Commit Graph
6750 Commits
Author SHA1 Message Date
Chris Tate 27954618c7 Merge base branch 2026-10-25 16:31:56 -05:00
Chris Tate 474edacb4f Prefer the union arm that keeps every field of a converted record (#2259) 2026-10-25 16:26:24 -05:00
Chris Tate a9ea71df7d Compare unions that hold promises as untyped values
A promise converted into a union's promise arm is a new promise, so two
optional slots set to one promise each hold their own conversion, and a
value narrowed out of such a slot may be one too. Union equality compared
those arm payloads by pointer and answered false where Node answers true.

Any comparison between a union with a promise arm and a promise or another
such union now compares both sides as untyped values, where a converted
promise answers with its origin's identity.
2026-10-25 16:22:25 -05:00
Chris Tate c50ddd3c91 Merge base branch and mark p12 as a race 2026-10-25 16:21:09 -05:00
Chris Tate 34401a7d7b Merge base branch 2026-10-25 16:16:47 -05:00
Chris Tate d4a677525d Build the sanitized three.js nodes fixture in its own CI job (#2210) 2026-10-25 16:16:34 -05:00
Chris Tate ef14f0ae8a Merge main 2026-10-25 16:15:54 -05:00
Chris Tate 0ee74478fd Run paired row sums two columns per step (#2247) 2026-10-25 16:15:20 -05:00
Chris Tate a96fbcfb64 Refuse static member reads of MIMEType and MIMEParams in JavaScript
JavaScript sources hold node:util's MIMEType and MIMEParams as identity
tokens, so a read such as MIMEType.prototype or MIMEType.name answered
undefined where Node returns the class's member. Those reads now report
SC2020, as the TypeScript spelling does at compile time; in JavaScript
the refusal throws when the read runs.

The node24-mime-errors fixture reads MIMEType.prototype to take the
getters' descriptors, so its expected refusal is now SC2020.
2026-10-25 16:14:48 -05:00
Chris Tate 062d04668d Batch the program's identifier symbols before collection (#2254) 2026-10-25 16:08:26 -05:00
Chris Tate 110396ee2d Replace a package's declaration files or supply its declarations with type patches (#2245) 2026-10-25 16:03:41 -05:00
Chris Tate 4217aa12bf Remove the committed node_modules symlink 2026-10-25 16:03:13 -05:00
Chris Tate ac003fdf07 Remove the committed node_modules symlink 2026-10-25 16:01:45 -05:00
Chris Tate 8a87cf015c Remove the committed node_modules symlink 2026-10-25 15:58:45 -05:00
Chris Tate d1f0113e49 Check only the files whose diagnostics the tsc gate keeps
The tsc gate (preflight's errorsOf, and the original program of a JSX or
declaration-projection build) asked for every file's semantic
diagnostics, then dropped all of those in an opted-in package's files, in
unselected package JavaScript and in package shims. With --npm-static
that is nearly the whole check: on three.js's nodes fixture the 82
package files took 1.17 s and reported 13,022 diagnostics, the program's
own files 0.1 s.

getPreEmitDiagnostics takes the files whose diagnostics its caller keeps.
When some file is left out, the server checks only the kept files
(getDiagnosticsOfCheckedFiles), and their semantic diagnostics come in
the full check's order. When a stored answer for the supplied
declaration files applies to the program, it stands in for checking
those files, as in the full check; the storage rule is unchanged. A
global diagnostic can come from checking any file, so when the partial
check reports one, the full check answers as before. At load time,
before workspace packages are known, the gate leaves out only package
and shim files.

The kept diagnostics are unchanged.
2026-10-25 15:58:14 -05:00
Chris Tate e5778cafbb Remove the committed node_modules symlink 2026-10-25 15:57:51 -05:00
Chris Tate 6f1efbf42b Remove the committed node_modules symlink 2026-10-25 15:56:53 -05:00
Chris Tate 829f35bf85 Pass sanitizeAttributes where the serialized-IR test emits directly
The test emits each corpus program from its serialized IR with its own
emitter options and compares the text with compile()'s. compile() now
marks functions sanitize_address only in sanitized builds, so the test
passes the same setting as objectAudit.
2026-10-25 15:55:57 -05:00
Chris Tate 9cfeb2408f Count shorthand property reads of an initializer-less JavaScript let (#2252) 2026-10-25 15:54:08 -05:00
Chris Tate 7be83d8e11 Fix the merge and address review feedback 2026-10-25 15:47:57 -05:00
Malte Ubl a5512e5939 Merge remote-tracking branch 'origin/perf/shape-overlay-soundness' into HEAD
# Conflicts:
#	packages/compiler/src/frontend/shape-inference/static-objects.ts
2026-10-25 13:47:20 -07:00
Chris Tate 11de08b931 Skip worker cancellation polls in short loops inside another loop
In a worker program, a loop inside another loop of the same function
needs less than its own budget, because the enclosing loop polls at
least once every 64 of its iterations:

- A loop whose counters' proven ranges bound its runs, and those of the
  loops in its body, to 64 x 64 emits no poll. This covers loops with
  constant bounds, such as mandelbrot's lane and iteration loops. The
  bound uses the counted and two-counter loop ranges, or the range of a
  counter that the update alone steps by a constant where the condition
  reads it. Loops whose counted form needs a run-time guard don't count.
- An innermost counted loop is strip-mined like a counted loop over
  typed arrays, whatever its body. A run of at most 64 iterations never
  polls, and the budget's countdown leaves the body. A run of at most
  16384 iterations is one block and polls once as it leaves. A longer
  run polls between blocks of 64, but not after its last block.

Between two polls there are then at most 64 iterations of the enclosing
loop, and in each of them at most 16384 unpolled iterations of each
strip-mined inner loop and 64 x 64 runs of each bounded nest. A loop
that no other loop of its function encloses polls as before.

New corpus program: native-worker-terminate-bounded-loops.ts.
2026-10-25 15:46:27 -05:00
Malte Ubl b04fea812d Merge remote-tracking branch 'origin/fix/js-dynamic-lowering' into HEAD
# Conflicts:
#	packages/compiler/src/frontend/pipeline.ts
#	packages/compiler/src/frontend/shape-inference/overlay.ts
#	packages/compiler/src/frontend/shape-inference/static-objects.ts
#	packages/compiler/test/shape-overlay-render.test.ts
2026-10-25 13:46:26 -07:00
Malte UblandClaude Opus 5.5 8b38aa5d8f Merge perf/shape-overlay-cost; keep the compiler self-hosting; record the new npm cases' order baselines
- array-concat.ts: a boolean predicate (the self-hosted compiler refused the
  truthiness test on an IrType | null | "dyn" value, SC2003).
- TypeScript 7 order baselines for concat-dynamic-args and
  evolving-let-index.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 13:44:32 -07:00
Malte Ubl d4e83259bf Merge remote-tracking branch 'origin/perf/shape-overlay-cost' into HEAD 2026-10-25 13:44:15 -07:00
Malte Ubl 6c1275bb34 Merge remote-tracking branch 'origin/perf/shape-site-clones' into HEAD
# Conflicts:
#	packages/compiler/src/frontend/shape-inference/overlay.ts
2026-10-25 13:44:08 -07:00
Chris Tate 94446a086a Answer Object.prototype's methods on typed records (#2244) 2026-10-25 15:43:30 -05:00
Malte UblandClaude Opus 5.5 411852656c Merge perf/shape-analysis-budget; keep argsFrom in a Map the analyzer owns
Compiler sources keep caches on their owners, not in weak collections
(compiler-weak-collections.test.ts).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 13:42:14 -07:00
Malte Ubl 477751ce38 Merge remote-tracking branch 'origin/perf/shape-table-dispatch' into HEAD 2026-10-25 13:40:54 -07:00
Malte Ubl 922a53fa0c Merge remote-tracking branch 'origin/perf/shape-overlay-fallback' into HEAD
# Conflicts:
#	packages/compiler/src/frontend/shape-inference/static-objects.ts
2026-10-25 13:40:45 -07:00
Malte Ubl cbede2e5fd Merge remote-tracking branch 'origin/perf/static-objects' into HEAD
# Conflicts:
#	packages/compiler/src/frontend/pipeline.ts
#	packages/compiler/src/frontend/shape-inference/overlay.ts
#	tests/harness/npm.test.ts
2026-10-25 13:38:56 -07:00
Malte UblandClaude Opus 5.5 ee63d4a607 Distribute conditional members over a generic body's bound parameter
A generic body reading a member typed by a distributive conditional over
its type parameter (`opts?: T extends "alpha" ? AlphaOpts : T extends
"beta" ? BetaOpts : never`) refused it with SC2009 when the instance bound
T to a union of literals (`"alpha" | "gamma"`, `"a" | (string & {})`), and
mapped it as `number | undefined` (never's placeholder) when the binding
selected only never, while the concrete instantiation maps that member as
`null | undefined`; values crossing between the two failed with SC2003.

The conditional now distributes over the binding as the checker does: each
member evaluates, the non-never results form the union, an open `string &
{}` member checks as `string`, and an all-never result leaves an optional
member that holds only undefined.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 13:37:14 -07:00
Chris Tate f0f91ac1ff Load a Node module's fallback declarations only when the program names it
Without @types/node, every program loaded scriptc-node-fallback.d.ts whole:
the console and process globals and 47 Node modules, about 8,100 lines,
parsed, bound and checked in every cold build whether or not the program
imported them.

- The fallback file keeps the globals and the modules they name (console,
  readline, readline/promises, perf_hooks, tty). Every other module, under
  both spellings, has its own file in ambient/node-fallback/, which
  references the files of the modules its declarations name.
- A module's file joins the program when one of the program's files names
  the module in a string literal: imports, requires, import types and JSDoc
  types all name modules that way. The entry is read before the first
  program; the program-root discovery passes add modules that other files
  name. Files are read as tsgo reads them, and the TypeScript libraries and
  scriptc's own declarations are skipped.
- The checks that recognized the fallback file by its path recognize every
  fallback file.
2026-10-25 15:36:08 -05:00
Chris Tate 224e040aac Map generic alias casts inside generic bodies with the instance's arguments (#2227) 2026-10-25 15:32:14 -05:00
Chris Tate 677c550fdb Merge main 2026-10-25 15:28:35 -05:00
Chris Tate e842da2ceb Batch the program's identifier symbols before collection 2026-10-25 15:24:38 -05:00
Chris Tate cbe4a9387b Merge main 2026-10-25 15:24:25 -05:00
Chris Tate c3880c89b0 Skip module-pass work that changes nothing in the functions it walks (#2250) 2026-10-25 15:20:10 -05:00
Chris Tate 20574b1932 Merge main 2026-10-25 15:18:27 -05:00
Chris Tate a4ae5f9e27 Run the linker job directly for host Linux links with lld
A Linux executable link runs `clang -fuse-ld=lld ...`, and the driver
only starts ld.lld with a job it derives from its arguments, its
environment and the installed toolchain. Starting the driver costs
about 10 ms of a small program's 29 ms link.

LinkerJobs (native/linker-job.ts) records that job in the native cache,
with the link's own inputs and output as positions. A traced link (the
Linux executable cache's `-v -Wl,-t` link) records it from the job line
the driver prints with -v; a link the executable cache does not trace
lists it with -v -### first. The entry also keeps the identities of the
files and directories the job and the driver's -v lines name (ld.lld,
startup objects, library directories, the GCC installation, the
driver's directory): the set the Linux executable cache records from its
traced link. It is keyed by the driver's identity, the link's
arguments, the toolchain environment, PATH and the working directory. A
later link with the same key whose recorded paths still have those
identities runs ld.lld with the job directly, and the executable cache
traces it with the job's -t output and the recorded identities.

Every other link runs through the driver as before: cross-target links,
links without lld, a driver that is not clang, SCRIPTC_LINKER, sanitizer
links, a toolchain environment that names mutable inputs, a listing
without exactly one ld.lld job naming each of the link's paths once, a
recorded path whose identity changed, and a job that fails (the driver
then reports the error). The linker gets the same arguments, so the
executables are identical.
2026-10-25 15:17:20 -05:00
Chris Tate f5c316fbc6 Cut the shape overlay's compile cost (#2249) 2026-10-25 15:12:53 -05:00
Chris Tate aead30fc26 Mark emitted functions sanitize_address only in sanitized builds
The sanitized lane's -fsanitize=address link needs the attribute to
instrument the emitted functions. Without a sanitizer pass it is not
inert: LLVM does not shrink-wrap functions that carry it, so every call
saved and restored all of a function's callee-saved registers on entry,
including on paths that return before any call, and it blocks
speculative loads. The LLVM helper already dropped it for builds that
import runtime bitcode (speed); release builds kept it.

- The new sanitizeAttributes option (default true for direct emission)
  is set from --sanitize wherever objectAudit is.
- Without it and without a target CPU level, attribute group #0 holds a
  string attribute LLVM ignores, since a group cannot be empty.
2026-10-25 15:10:32 -05:00
Malte UblandClaude Opus 5.5 e31e2ebe75 Dispatch a constant function table without a fallback when every caller passes one of its keys
When the key is a parameter of a function that never escapes, every call
passes a string literal key of the table, and the body neither assigns the
parameter nor uses arguments, the dispatch needs no arm for other keys. In
nsbench the fallback boxed the Vec probe into a dynamic call, so every Vec
carried the property bag and lived on the heap; without it the workload
inlines into one loop. SCRIPTC_SHAPE_EXHAUSTIVE_TABLES=0 opts out.

nsbench (n=20000, overlay on): 36 -> 8.2 ms (Node about 21 ms).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 13:10:25 -07:00
Malte UblandClaude Opus 5.5 484fc2cb13 Fold members stored after their namespace was created; skip the receiver read in JavaScript functions that never use this
- static-objects: a primitive member stored once by an unconditional
  top-level statement of the object's own file (app.math.EPSILON = 1e-9)
  reads as its literal where the read provably runs after the store: inside
  functions when the statements before the store run no code, and in later
  top-level statements of that file. SCRIPTC_SHAPE_FOLD_STORES=0 opts out.
- JavaScript functions read the ambient receiver at entry only when this
  occurs in their parameters or body outside nested non-arrow functions.
- npm case static-stores pins both, with the overlay on and off.

nsbench (n=20000, overlay on): 64.4 -> 36 ms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 13:10:12 -07:00
Malte UblandClaude Opus 5.5 439f973dd9 Format analyze.ts
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 13:10:08 -07:00
Chris Tate 8d5c85448f Close recursive record knots through erased generic method members (#2246) 2026-10-25 15:07:46 -05:00
Malte UblandClaude Opus 5.5 b3d4a3f341 Fix the stack's failing unit and snapshot tests
- analyze.ts keeps argsFrom in a Map the analyzer owns (compiler sources keep
  caches on their owners, not in weak collections).
- constant-graphs.test.ts: lib.math is stored after lib was created, so the
  report shows it as undefined|{...} with a graph of its own.
- maybe-promise-fences.ts: `record instanceof Promise` lowers to false since
  the base's neverPromise lowering; the snapshot no longer lists the refusal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 13:01:49 -07:00
Chris Tate da616a27cd Install the global lookup for function-local snapshots, and release navigator values read alone
A computed read through a snapshot of the global object taken in a
function (`const g = globalThis; return g[name];`) threw SC2020 for
builtin globals: the scan that decides whether a module installs the
builtin-global lookup runs before function bodies are lowered, when the
snapshot's alias is not registered yet. The scan now also recognizes a
variable whose only declaration initializes it with the global object.

navigator.language and navigator.languages cache their answers, but
only taking the navigator object registered their release at exit, so a
program that read just those members left them live and a sanitized
build exited with status 99. The first cached value now registers the
release.

Corpus 8161 reads through a function-local snapshot, and 8168 reads
navigator.language, navigator.languages and navigator.platform without
the navigator object.
2026-10-25 14:57:45 -05:00
Chris Tate c21fc01201 Key bound alias instances by the declared alias
The compiler compiles itself, and a Map keyed by another Map is outside
the key domains it compiles (SC2009 in the self-hosted frontend). The
Lowerer's table is now keyed by the declared alias reference: each entry
lists the instances that instantiate it with their ts-level bindings maps,
and the resolver picks the current instance's by identity.
2026-10-25 14:49:55 -05:00
Chris Tate 826519133f Merge main 2026-10-25 14:49:01 -05:00