Each call of a function makes a new function object for a generic arrow
or function expression bound inside it, so `make() !== make()` when make
returns the binding, and such values are distinct Map and Set keys. The
compiled instances are shared module functions, which cannot give that
identity, so a value use of such a binding keeps an SC1090 fence that
names the reason. Calls of the binding still compile.
A typed array or Buffer in a compiled program is always a built-in kind
(programs cannot subclass them, add own properties or change their
prototypes), so Node 24's prototype chains for them are known exactly.
- A read of a member the type does not declare (in JavaScript, or under
@ts-ignore) answers undefined when that chain lacks the name, as Node's
GetV does: the base64 and hex feature probes (`typeof bytes.toHex`) that
libraries such as @noble/hashes make. Names Node has, including the
Buffer prototype's members on a Uint8Array that may hold a Buffer, keep
the fence.
- `"name" in bytes` with a literal member name answers from the same
chains, and `bytes.BYTES_PER_ELEMENT` is the element size.
- Array destructuring assignment into typed-array elements
(`[high[i], low[i]] = [h, l]`) evaluates the receiver, the index and the
value in order and writes the element as `b[i] = v` does.
Refs #1437
Co-authored-by: saboursepideh <25549001+saboursepideh@users.noreply.github.com>
Math.max and Math.min with a spread and other arguments (`Math.max(...xs,
5)`) pack their arguments, in order and with each spread flattened, into
one number[] that the runtime folds, as Math.hypot already does. The fold
keeps NaN poisoning, -0 below +0 and the empty fold's infinities, and the
arguments evaluate left to right before it. The refusal hint now names
the supported forms.
Fixes#1621
Co-authored-by: ejc3 <1694850+ejc3@users.noreply.github.com>
A generic arrow or function expression bound inside a function or method
compiles when it uses nothing from the enclosing function's frame: no
`this`, `super`, `arguments` or `new.target`, and no local, parameter or
type parameter of an enclosing function. Its instantiations are module
functions, as for a module-scope binding, and may call the binding itself
and read module-scope bindings. Instances of bindings in functions and
blocks take the declaration's position in their names, so two functions
may declare bindings of the same name. Bindings that use the enclosing
frame keep their SC1090 fence.
Fixes#1624
Co-authored-by: ejc3 <1694850+ejc3@users.noreply.github.com>
Another module can import an exported const (`export const` or an
`export { name }` specifier) and reach its elements, so a spread of it
is not their only reference. Those spreads keep the element-layout
refusal instead of copying the elements.
A const initialized by an array literal and spread once can still run
that spread more than once: in a loop body, loop test or update, a
function, or a class field initializer. Each run inserts the same
elements in JavaScript, so copying them into the destination's layout
would break identity. Those spreads keep the element-layout refusal.
Object.fromEntries(...) as T passed the entries through the
checked-dynamic bridge, which copies native arrays and records: an array
shared by two entries, or held elsewhere, became separate copies, and
writes through the asserted object no longer reached it. The bridge now
applies only when the entries' values are scalars or checked-dynamic
values, which pass by value or by identity; other entries keep the
exact-shape fence (SC2002). Corpus 6467 checks that entries held as
unknown keep their identity; diagnostics fixture
fromentries-aliased-entries.ts pins the fence.
The bakeability check tracks the unions it is inside as well as the
records, so a union reachable from itself through an array
(`type Tree = number | Tree[]`) reports SC1090 instead of recursing without
end. The message names both recursive records and recursive unions.
- Merge origin/main (e9fb01603); the import lines at the top of
lowerer.ts conflicted and both are kept.
- Corpus 6385 gains a wrapper whose block-local `args` alias shadows an
outer one in the same function; each call forwards its own arguments.
Externrefs a guest holds are borrowed pointers. An import's result was
kept only until the outermost call returned, which was before the export
call converted its results to JavaScript, so a result created by an
import was freed before conversion. A table or global slot released the
reference it replaced at once, although the guest may still hold it in a
local after reading the slot. The kept references are now released after
the outermost caller has converted its results (scr_wasm_leave), and a
table or global store during a call keeps the reference it replaces until
then.
Checked memories (explicit bounds checks: sanitized builds, Windows)
grew with realloc, which can move the storage that memory.buffer and its
views alias. They now reserve the address space for their largest size
(mmap on POSIX hosts, VirtualAlloc on Windows) and commit pages as they
grow, so the base never moves. Hosts without address-space reservation
grow in place only until a buffer has been handed out; after that,
growth that would move the storage fails.
Tests: webassembly-externref-lifetimes.mjs (import results through an
export, table and global references replaced by the guest and by
JavaScript), webassembly-memory-growth.test.ts (views written after
growth from JavaScript and from the guest), and translate.test.ts cases
for memargs naming memory 0 explicitly and for local.tee operand types,
which b3bcbaaf7 made consistent with validation. Under SCRIPTC_SAN=1 the
two runtime tests report heap-use-after-free without these changes.
Merges main; internal/compatibility/generated/node-v24-backlog.json is
regenerated with pnpm node-compat.
The fallback declaration of events.once takes NodeJS.ReadableStream and
NodeJS.WritableStream emitters like @types/node does. The diagnostics
fixture now pins the forms that stay refused (options that are not an
object literal, a computed name, a stream's 'data' event), and the
limitations page states the supported events.once forms.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- matchInferSignature reads a fixed-tuple rest as its positions, so
(...args: [string, number]) needs two arguments, and a positional
any[] pattern parameter matches only when the function's parameter
there accepts an array, as TypeScript's assignability does
(corpus 6398).
- Merge origin/main (200fe8d22); node-v24-backlog.json regenerated.
- Merge origin/main (e1c27176a).
- matchInferSignature answers false when a function needs more arguments
than a fixed-parameter pattern passes, as TypeScript's assignability
does (corpus 6398).
- Record corpus costs for the programs this change adds.
The refusal of callback returns that the inferred result type would cut
down found the return by walking up through parentheses and await only,
so a return inside a conditional's branch or a `satisfies` expression
converted silently and lost the extended members. It now walks through
every parent whose value can be the expression's own: parentheses,
await, satisfies, non-null assertions, conditional branches, the
operands of ??, || and &&, and the right operand of a comma.
An untyped child-stream 'data' listener takes strings when a setEncoding
call on the same stream precedes it. A const alias of an access path
(`const out = child.stdout`) now resolves to that path, so a setEncoding
through one alias and a listener through another, or through the stream
itself, agree. A setEncoding on another receiver that can hold the same
stream (a let copy, a parameter, a call result) leaves the state unclear
and reports the existing SC1090. Receivers that name different members
of one root, or two const roots each initialized by a call or new, are
treated as different streams.
- `s.match(re) ?? []` / `|| []` made typescript-go's API panic whenever the
union's members were serialized (the empty literal is a tuple clone
without tuple data), poisoning the statement with SC0004. The members
of the union's widened type are readable and describe the same values,
so constituent reads answer with them; the empty tuple beside a match
array is one of its values. `checker-panic-tuple.ts` compiles and moves
to the corpus.
- Spreading a fixed tuple into an array literal copies its positions
into the literal's element type.
- fs.Stats values are an array element kind, so `Promise.all` over
`stat()` promises and `Stats[].reduce` compile.
- `.then(String)`, `.then(Number)` and `.then(Boolean)` over a number or
boolean settled value convert it through an interned closure.
- Inspecting (util.format, %s, util.inspect) an Error-typed value whose
runtime class declares fields prints the stackless base with Node's
constructor-name header (`[Bailable [Error]: m]`) and the own
properties, `[Bailable [Error]: m] { bail: true }`; it was refused. A
subclass whose own fields cannot render (functions, or a field Node's
error inspector treats specially) throws a named refusal on its own
instances only.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@types/node types the transform/flush completion callback's data as any
and a child stream 'data' chunk as any, and ChildProcess accepts
(code: number, signal: string) exit and close listeners through its
catch-all overload. None of these compiled: the any-typed completion
callback crashed the LLVM emitter, and the child listeners were refused.
The completion callback's data parameter is typed Buffer | string |
undefined while the option callback lowers. Child stream 'data' listeners
with an unknown or any chunk box it as a checked-dynamic value, taking
strings when a setEncoding statement on the same stream precedes the
listener and Buffers when the function never calls setEncoding on it;
other placements are refused. Exit and close listener parameters declared
number or string bind as checked-dynamic values, so the body sees the
null Node passes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A call that resolves to an overload signature of a generic function or
method with an implementation (`load<T>(k, map: Mapper<T>): Promise<T>;
load(k): Promise<Reply>; load<T>(k, map?) {...}`) monomorphizes the
implementation under the bindings the overload's resolved signature
proves; a type parameter the overload never mentions is unknown, and
parameters the overload omits must be optional in the implementation.
It was refused.
- `view.m(...)` where `view` is a `Pick`/`Omit` view of a program class
and `m` is a generic (or generically overloaded) method extracts the
instance from the view's live receiver and calls the method statically,
instead of a runtime refusal reading the method through the untyped
view.
- Binding inference pairs the arms of `T | Promise<T>` with an
instantiated `R | Promise<R>`, and `unknown | Promise<unknown>` maps to
the checked-dynamic value the checker reduces it to.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A callback passed to a generic call without a return annotation can
return values the checker unifies into a narrower result type ('return
items' beside 'return items.map((i) => ({ ...i, extra }))' infers
Item[]). Converting the extended objects into that type copied them
and dropped the added members, which Node keeps and a later wider view
reads back. Such a return is now refused by name, pointing at the
annotation that keeps the members.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#1490 also took records with accessor slots out of the dyn-convertible
domain, which refused passing such a literal where `unknown` is
expected. Native APIs read these values through their accessor slots:
fileURLToPath, fileURLToPathBuffer and pathToFileURL call the `windows`
getter of an options object, so tests/corpus/node24-file-url-options.ts
stopped compiling.
Records with accessor slots convert to checked-dynamic values again.
JSON.stringify of their typed values stays refused, since the typed
serializer cannot invoke the getter.