Commit Graph
6829 Commits
Author SHA1 Message Date
Chris Tate d978570cc3 Merge main 2026-10-30 09:45:28 -05:00
Malte UblandClaude Opus 5.5 0260725265 Fold the optional-call guard on a never-nullish library function
`rmSync?.(…)`: an imported node:fs function has a call lowering but no
value form, so lowering the guarded callee failed with "library
functions as values". The checker types the callee never-nullish, so the
guard can never short-circuit and the step is the plain call. When the
callee is never-nullish (any/unknown excluded) and has no value
lowering, mark the call's `?.` node chainHandled and re-dispatch the
plain call; it reports its own diagnostics when it has no lowering
either.

Also drops the unused isSafeToDiscard import left behind in
optional-chains.ts. Corpus: tests/corpus/optional-call-library-function.ts
(Node vs native, byte-identical).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 06:45:46 -07:00
Malte UblandClaude Opus 5.5 69f3ae8dd9 Discard a child-process listener's return value through the void adapter
`child.on("close", async (code) => …)` lowers to a closure returning
Promise<void>, and an annotated `(): number => 5` returns a number. Node
drops every listener's return value: EventEmitter ignores it, and a
returned promise still surfaces its rejection through the runtime's
unhandled-rejection tracking, which fires at settlement, not at the drop.
The listener slot is void, so the closure now adopts it with
coerceToExpected(cb, funcOf(params, VOID)) — the adapter calls the
listener and discards the answer, the shape every other void callback
slot already takes. A returned value that cannot be discarded still
refuses with SC1090.

tests/diagnostics/child-process.ts drops the annotated-listener fence
(Node drops that value too) and its snapshot is re-recorded; the new
corpus program pins the inferred, annotated and async listener shapes
against Node.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 06:45:45 -07:00
Malte UblandClaude Opus 5.5 62094b0832 Answer undefined for a member read on a stored primitive union arm
Reading a literal key off a union whose STORED value still carries a
scalar primitive arm answers undefined: JS boxes the primitive, finds no
such property, and returns undefined, the answer the unit and
missing-field arms already give. Lowering erases flow narrowing, so a
read the checker proved unreachable (`{ url?: string } | "pending"` read
after a typeof guard as `x?.url`) still dispatches over the string arm
and turned the whole read into an SC1090 refusal.

lowerUnionKeyedRead now asks the checker whether the primitive type
declares the property; a name it does declare (`length`, `toUpperCase`)
keeps the read's own lowering or refusal. The backend's unionKeyGet
stores the join's interned undefined arm for such an arm, and the IR
validator accepts it under a literal key when the result carries an
undefined arm.

A JavaScript-source read on a dyn-convertible receiver keeps its dynamic
fallback (`dynKeyGet`): every call site already answers that way, so a
program that compiles today keeps byte-identical IR
(tests/corpus/6331-jose-root-entry reads `jws.payload` off a
`{ … } | boolean` union). The static answer only replaces a refusal.

Corpus: tests/corpus/union-primitive-arm-members.ts (Node vs native,
byte-identical). Unit: packages/compiler/test/union-primitive-arm-member-emission.test.ts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 06:45:44 -07:00
Chris Tate a37abcce48 frontend: lower entries().filter in generic bodies, name superset filter refusals (#2623) 2026-10-30 08:39:24 -05:00
Chris Tate 903c7e7c8e Keep the names of functions boxed inside records and arrays (#2622) 2026-10-30 08:30:04 -05:00
Chris Tate f3a0858a5a Use the runtime's rounding and libm definitions in musl executables (#2614) 2026-10-30 08:04:48 -05:00
Chris Tate 717ddfdfe3 Keep the names of functions boxed inside records and arrays
A function value converted to unknown on its own takes its name from the
conversion site, but one held in a record, an array, a map or another
converted value had no name there, so util.inspect printed
`[Function (anonymous)]` where Node prints `[Function: toString]`.

The function box now looks a nameless function up in a per-module table
of (entry point, name) pairs, built from the functions the module makes
closures of: each declared or lifted function's sourceName, which follows
NamedEvaluation, and for an object literal's method its key
(IrFunction.displayName). An adapter is looked up through the function it
adapts. displayName is set only in modules that box a value that can hold
a function, and the table exists only in modules that box functions.
2026-10-30 07:40:27 -05:00
Chris Tate 2ca0ca75e9 Run sloppy-mode modules and builtin-patching modules in Node islands automatically (#2591) 2026-10-30 07:39:37 -05:00
Malte UblandClaude 5.5 c38a8035b3 frontend: lower entries().filter in generic bodies, name superset filter refusals
`Object.entries(value).filter(([, item]) => ...)` inside a generic body
spells the entry element as a deferred conditional over the lib overload's
own type parameter (`[string, keyof T extends never ? unknown : T[keyof
T]]`), which no bound-type-parameter resolver can decide. The array-method
entry then found no mapping for the receiver type and fenced the call with
SC2020, even though the `Object.entries` lowering answers the concrete
`[string, <value union>][]` the value really has. When the checker's
receiver type is an array that the tables cannot map, fall back to the
lowered value's array type, as the checker-untyped probe above does
(re-lowering is pure IR construction). The stdlib member gate still runs,
because the checker does type this receiver, so only calls that were
previously refused can change.

A `.filter` type predicate whose record target declares fields the
receiver element type has no slot for stays refused, but the SC1090
message now names the cause instead of a generic missing conversion: each
element was narrowed when it was STORED, so those fields do not exist at
runtime and the predicate cannot make them real. The sound paths already
work and match Node: a checked-dynamic (`unknown[]`) receiver verifies
every target field at the narrowed element, and an index-signature
receiver reads the target's fields out of its overflow.

Corpus programs:

- 8741-mixed-tuple-array-slots: `["one"] | string[]` through a
  `Record<string, Mixed>` read, a `string[][]` literal, spread, a
  `readonly string[]` binding, a ternary, and parameter position — every
  shape Node accepts stays an array and never traps.
- 8753-filter-predicate-superset-guard: the superset guard as an inline
  annotation on a checked-dynamic receiver (honest per-field tests; a
  lying predicate throws the catchable TypeError instead of handing out
  invented fields) and as a guard function on an index-signature receiver.
- 8768-entries-filter-generic-instantiation: `entries().filter` inside
  `compact<T>`, a `const` binding with `.map`/`.some`, and a written
  predicate over the same receiver.

`tests/diagnostics/filter-predicate-superset-record.ts` pins the refusal
and its new message.

Co-Authored-By: Claude 5.5 <noreply@anthropic.com>
2026-10-30 05:32:14 -07:00
Chris Tate d404610842 Read SharedArrayBuffer, DataView, BigInt64Array, BigUint64Array, Proxy and Intl as values (#2620) 2026-10-30 07:23:58 -05:00
Chris Tate 353d8533ea Validate untyped listen ports and give net errors Node's system properties (#2619) 2026-10-30 07:18:37 -05:00
Chris Tate ce2e6624ff Check http.request options as Node does (#2606) 2026-10-30 07:16:38 -05:00
Chris Tate a119816d9b Keep the runtime's libm definitions in musl links without lld
A Linux link that does not use lld leaves out scr_glibc_math.o, because
GNU ld keeps glibc's libm.so needed when the unit's unused code is linked.
A link through SCRIPTC_LINKER counts as such a link, so a musl executable
linked through a wrapper (bench:runtime --layouts among them) took the
libc's floor, ceil and trunc instead of the runtime's. A musl executable
is static and has no libm.so, so its links now keep the unit.
2026-10-30 06:33:07 -05:00
Chris Tate 4610fe32be Read SharedArrayBuffer, DataView, BigInt64Array, BigUint64Array, Proxy and Intl as values
SharedArrayBuffer, DataView, BigInt64Array, BigUint64Array and Proxy read
as values are interned constructor values with Node's name and length.
Calling one throws Node's TypeError "Constructor X requires 'new'".
instanceof through a stored value answers as Node does: SharedArrayBuffer
and DataView test the value's kind, BigInt64Array and BigUint64Array answer
false (neither is constructed), and Proxy answers false for a primitive and
throws Node's TypeError for an object.

Intl read as a value is a namespace object with Node's 12 members in
Node's order. Its members are function values that throw the SC1090 fence
when called; direct calls such as new Intl.NumberFormat("en-US") keep
their lowerings.

TypeScript sources read the same values into any and unknown slots, so
the diagnostics program uses Atomics for a builtin without a value.
2026-10-30 06:30:40 -05:00
Chris Tate f1e4311881 Check the request path and method before a socket is opened
http.request runs the ClientRequest constructor's path and method checks
before it calls a createConnection option, takes a pooled socket or dials
a socketPath, as Node does. Before, the createConnection callback had
already run, and a socketPath server had seen a connection, when the
check threw ERR_UNESCAPED_CHARACTERS or ERR_INVALID_HTTP_TOKEN.
2026-10-30 06:16:58 -05:00
Chris Tate a54dd09e04 Validate untyped listen ports and give net errors Node's system properties 2026-10-30 06:14:57 -05:00
Chris Tate 10c1f11c8e Remove the close race in evolving-let-socket-optional-reads (#2617) 2026-10-30 06:04:27 -05:00
Chris Tate c4cd63108c pi: island references and crossings (typebox re-export, records into and out of islands) (#2593) 2026-10-30 05:54:09 -05:00
Chris Tate 1716899871 pi decisions: per-arm union spreads, checked-dynamic union merges, EventEmitter with any event name, optional reads of always-defined globals (#2596) 2026-10-30 05:53:18 -05:00
Chris Tate 953293e9e4 Close the socket corpus program's connection without a reset
evolving-let-socket-optional-reads.cjs ended the server's side of the
connection as soon as it was accepted and closed the server while the
client was still ending its own side. On macOS the client could then read
ECONNRESET, under Node and the compiled program alike, and each reports the
unhandled 'error' event in its own form.

The server now ends its side once the client's end arrives, and the server
closes when the client socket has closed. The program's output is unchanged.
2026-10-30 05:21:56 -05:00
Chris Tate 787098c087 Type a forward-captured island handle binding checked-dynamic
A closure declared before the island array binding it reads, such as
`const show = () => describe(local); const local = list();`, made the
lowering predeclare the binding's slot with its declared native array type.
The island lowering then retyped the binding checked-dynamic at its first
use, so the closure's capture and the binding disagreed (SC9001). The
predeclared slot of an island handle binding is now checked-dynamic from
the start, as the binding's own declaration lowers.

Test: 6897-node-island-declared-arrays gains arrows and a function
declaration that capture an island array declared after them.
2026-10-30 05:04:49 -05:00
Chris Tate 7ae0a0b322 Merge main 2026-10-30 05:03:19 -05:00
Chris Tate 82fbc6a983 Report import source as unsupported instead of failing to decode it (#2612) 2026-10-30 04:47:34 -05:00
Chris Tate d9c9843fd0 Compile scr_intl.c with the ICU headers in the COMMON-symbol test (#2611) 2026-10-30 04:40:42 -05:00
Chris Tate c052e4fc78 Keep records cast from unknown[] elements to dictionary types as live views (#2613) 2026-10-30 04:40:20 -05:00
Chris Tate 32d4ad0072 Compare generic declarations by identity across pinned signatures (#2565) 2026-10-30 04:34:26 -05:00
Chris Tate 50b9976483 Use the runtime's rounding and libm definitions in musl executables
floor, ceil and trunc in a musl executable came from zig's libc as weak
bit-manipulation routines, and the runtime's date arithmetic and the
compiler's rounding intrinsics call them. scr_glibc_math.c now applies to
musl executables too: floor, ceil, trunc, round, sqrt, fmod, scalbn,
ldexp and frexp are the same exactly specified definitions as on glibc.
A static musl executable has no ifunc, so on x86_64 floor, ceil and trunc
call through a pointer that their first call sets to the SSE4.1 rounding
instruction when the CPU has it. Library artifacts keep the embedder's
libm.

Adds a test that builds the same program against glibc's libm and, for
x86_64-linux-musl, against these definitions, and compares every result.
2026-10-30 04:28:03 -05:00
Chris Tate 895dd44bd7 Keep evaluation order and listener signatures with any-name emitters and union spreads
A distributed union spread evaluates its source before the dispatch, so
the properties written before the spread run after it. When the source
may have effects (a call that changes a value an earlier property reads),
those properties must now be constants; otherwise the literal takes the
checked-dynamic merge, which keeps Node's order.

A listener registered under an unrestricted event name was not recorded
in the program's event scan. A prefixed or suffixed emit with no matching
pattern listener was then lowered to `false` without dispatching, so the
any-name listener never ran. The scan now records such registrations, and
computed-name emits dispatch whenever one exists.

The same registrations can place a listener of any signature in the
bucket of a literal event. listeners(), rawListeners() and
events.getEventListeners() of a literal event returned that bucket typed
by the event's tuple, so calling an element passed the listener arguments
of other types. These calls are now refused once the program registers a
listener under an unrestricted name, as they already are for pattern
registrations.
2026-10-30 04:09:40 -05:00
Chris Tate 834bccdcd0 Keep records cast from unknown[] elements to dictionary types as live views 2026-10-30 04:01:39 -05:00
Chris Tate 76a44bfedd Report import source as unsupported instead of failing to decode it
TypeScript 7.1's protocol 9 marks an `import source` clause with phase
bits 3, which the decoder rejected, so a 7.1 checker failed the whole
build with "TypeScript AST: `import source` is not supported".

The decoder now keeps the phase, `phaseModifier` reports it as
SOURCE_PHASE_MODIFIER, and preflight reports SC1090 at the declaration.
2026-10-30 03:49:17 -05:00
Chris Tate 5a95e8a076 Compile scr_intl.c with the ICU headers in the COMMON-symbol test
common-symbols.test.ts compiles every runtime source with the vendored
include paths only. scr_intl.c includes ICU's C API headers, which the
runtime pack build takes from the fetched ICU release, so on a host
without system ICU headers both of the test's compiles failed with
"'unicode/ubrk.h' file not found", and the aarch64-macos compile failed
on every host. The test now passes the same source/common and
source/i18n include paths for scr_intl.c, through icu-data.mjs's
ensureIcuSources.
2026-10-30 03:43:48 -05:00
Chris Tate c889ba300b Read inherited keys in assert.throws with a shared expected object (#2610) 2026-10-30 03:40:57 -05:00
Chris Tate 2bc82f97dc Merge main 2026-10-30 03:40:32 -05:00
Chris Tate 87c0f7f3e2 Alias held process.env values to the live environment; keep a returned arrow's result in value slots (#2554) 2026-10-30 02:56:44 -05:00
Chris Tate 5a70967a0e Read inherited keys in assert.throws with a shared expected object
assert.throws and assert.rejects with an expected object held in a
variable compare each key with the thrown error's own or inherited value,
as Node's `key in actual` check does. An error's name is on its
prototype, so { name, message, code } failed against every error.
2026-10-30 02:55:44 -05:00
Chris Tate cf18a84a2d Call methods of class instances inline at untyped call sites (#2605) 2026-10-30 02:32:48 -05:00
Chris Tate 6f5abbf0d1 Convert sums built on unsigned shifts from 32 bits (#2604) 2026-10-30 02:09:50 -05:00
Chris Tate 933d2c5e17 Merge main 2026-10-30 02:02:22 -05:00
Chris Tate 70e967862e Pass strings to an untyped 'data' listener on a child stream after setEncoding (#2602) 2026-10-30 01:54:58 -05:00
Chris Tate 9818484abb Simplify optimized partitions at O1 before the thin link (#2603) 2026-10-30 01:53:57 -05:00
Chris Tate 0ad917929e Check http.request options as Node does 2026-10-30 01:51:42 -05:00
Chris Tate 14de5ce992 Call methods of class instances inline at untyped call sites
Untyped code called a method of a compiled class instance through the
site's dispatch helper: the callback that resolves own function-valued
properties, then the helper, which normalizes the capsule, tests each
class key and unwraps the instance before calling the method.

A call of a method dispatch helper from a dynamic-tier function now
tests the receiver's capsule key against the helper's plain class
branches (a direct call of one method with the helper's arguments) and
calls that branch's method with the capsule's instance. A branch
qualifies when every subclass the normalization can rebox its key to
calls the same method, and when the callback has no branch for the key
or those subclasses, so it answers undefined for the capsule. Every
other receiver calls the callback before the arguments and then the
helper, as before. Module initializers call this way only inside loops.
2026-10-30 01:40:20 -05:00
Chris Tate 1ec321ed77 Keep a probe load eager when it catches the refusal of a program file
A package that catches the probe's refusal to load a program file
evaluated a fallback, and the probe recorded that load as clean and
snapshotted the fallback's exports, which the executable never has. A
load in which the resolve hook refused a program file is now recorded as
throwing the refusal whether or not the load caught it, and so is a later
load that reaches one of its files through the module cache.
2026-10-30 01:30:27 -05:00
Chris Tate 4a507a817d Refresh the runtime profile (#2538) 2026-10-30 01:25:18 -05:00
Chris Tate 3eed91a29b Convert sums built on unsigned shifts from 32 bits
An integer `+`, `-` or `*` whose operands and result are proven within
the uint32 range, and whose operand is a `>>>` result or arithmetic on
one, such as `((i + j) * (i + j + 1) >>> 1) + i + 1`, is now 32-bit
arithmetic without wrap, and its double converts from the 32 bits
(`uitofp i32`). Vectorized, that conversion takes three instructions on
x86-64 without AVX-512, where the 64-bit sum's unsigned conversion took
six. Sums of counters and literals, such as `i + 1`, keep the 64-bit
form, whose conversion LLVM folds with their uses as indexes.
2026-10-30 01:20:14 -05:00
Chris Tate e4bc919aec Refuse to evaluate program source in the island deferral probe
The build-time probe loads island package modules to decide their
deferral. A package can require a file outside node_modules (a program
module it shares, or any project file), and that file evaluated on the
build machine. The probe's resolve hook now throws for a file outside
every node_modules directory before it evaluates, so the module that
asked for it stays eager. This also covers a program module passed to the
probe directly.
2026-10-30 01:18:35 -05:00
Chris Tate 422a02e225 Compile three Node test shapes: a top-level return before declarations, nullable strictEqual operands, and a branch-assigned let (#2590) 2026-10-30 01:04:18 -05:00
Chris Tate 13c5fc6748 pi wave 5: node process APIs, stdlib singletons, misc lowering (#2548) 2026-10-30 01:00:51 -05:00
Chris Tate 0933548b8d Simplify optimized partitions at O1 before the thin link
An optimized program divided into partitions was simplified at O2 before
the thin link and optimized again at O2 after importing, so the work on
each partition's largest functions ran twice. The pre-link now runs the
O1 simplification, which still annotates the profile and inlines and
simplifies enough for the summaries the import decisions read; the
backend's O2 optimization is unchanged. A program optimized as one module
is unchanged.

The O1 simplification has no correlated value propagation. Without it,
SimplifyCFG folds a jammed loop's early exit and its trip-count exit into
one branch, the loop's latch no longer exits, and the backend's unroller
keeps a trip-count test in every unrolled iteration. The pre-link runs the
pass once per function after simplifying it.
2026-10-30 00:51:09 -05:00