`.filter` on `A[] | B[]` refused with SC2020: `lowerArrayUnionHofCall`
admitted only `map` and `forEach`. Under TS 5.2's callable-union rule the
predicate runs over `A | B`, and the result element is the one the
predicate names — `(b): b is TextContent` over a union of block arrays
answers `TextContent[]` — which no single arm's element need equal.
`filter` joins the same arm dispatch: each arm filters its own concrete
array through the existing per-arm helper, and its survivors then pass
through the checked conversion to the checker's result element, so a
predicate that kept an element it does not describe throws instead of
reinterpreting a value. An arm with no such conversion (a boolean
predicate that names no narrower element) leaves the SC2020 fence in place.
Typed programs are untouched: the gate only widens, and `map`/`forEach`
keep their exact helper key, so the helpers a program already emitted stay
byte-identical.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A checked-dynamic JSON stringify (any record or array carrying an `unknown`
field, so the runtime walker serializes it) answers `string | undefined` in
the IR — its live walker can return undefined for a dyn root — while the
checker types the call `string`. Reading a string member of that result
(`JSON.stringify(toolsAdded).length` in a token estimator) then met the
lib-boundary pass, which refused every union at a concrete slot with SC2003
"expected 'string', got 'string | undefined'".
The slot's own type IS the site's proven type: tsc approved the call, so the
stored union is the declaration's wider one (an absence arm the compiler
itself carries, a stale capture, a widened inferred return). Coerce it with
coerceToExpected's checked arm rule — the proven arm's payload comes out and
any other arm throws the existing catchable TypeError — and keep the refusal
for a union that does not carry the slot type as an arm. No compiling program
changes IR: every program on this path refused before.
Corpus: tests/corpus/lib-slot-union-arm-extraction.ts pins string members
(length, slice, toUpperCase, indexOf, template interpolation, comparison and
concatenation) of dyn-root JSON stringify results over arrays, nested
records and empty collections.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`''.match(re) ?? []` typed the literal uninhabited (`never[]`), whose element
still mapped through never's f64 value representation, so the whole
expression was `RegExpMatchArray | number[]` — a result no single array slot
can receive. A union return such as `"all" | readonly string[]` refused with
SC2003 "union conversion has no unambiguous layout mapping" (`getScriptReach`
in a coding agent's tool-discovery path).
An EMPTY array-literal default has no element of its own: it adopts the
left's present array arm, exactly the rule the union slot and the ternary arm
already state for an empty literal. The left's nullish test and the lazy
default are unchanged, and a default whose own type already matches the left
keeps the identical lowering it had.
Corpus: tests/corpus/nullish-empty-array-default.ts pins the union return,
string element reads through the default, the declared optional-array slot
with a fresh empty value per evaluation, and a parenthesized/template default.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A partial record assertion's literal now keeps the asserted type's
optional fields it omits, in their absent state, as an ordinary record
of that type does: a read yields undefined, in answers false and a write
adds the property, with no guard. Only an omitted required field is
missing from the view, and only its writes are guarded, so a write to an
absent optional field of an ordinary record of the same type no longer
throws. The receiver of a guarded write is saved whenever its value is,
so a right side that reassigns the receiver's variable no longer
redirects the check and the store.
A call rewritten for a getter-only name records the classes it tests. When a later wave reaches another class with the same getter, the fallback call is rewritten again for that class, instead of being skipped, so its instances also read the getter before the arguments. Before, `target.step(arg)` on such an instance threw "target.step is not a function".
A write completion callback that a JavaScript module returned reaches
the stream lowering as an untyped value. Since the error-argument form
of write callbacks, the lowering refused it with SC2020, so
tests/corpus/8532-stream-saved-callback-receiver failed to compile.
Such a callback is now converted to a function of the error argument.
The stream calls it unbound with null after a successful write or with
the write's error, and ignores what it returns, as Node does.
The event loop slept to its next timer with nanosleep. macOS applies the
timer slack of the thread's QoS class to nanosleep: a 5 ms sleep takes
about 38 ms at utility QoS and 180 ms at background QoS, where launchd
services such as CI jobs run. Timers then fired out of order against
Node, whose libuv loop waits with a kevent timeout that keeps its
deadline at every QoS. On macOS the loop now sleeps with a kevent timeout
on a kqueue with no events, shared by every thread's loop.
Intervals also re-arm from the time their callback started, as Node's
listOnTimeout does, instead of from the clock after the callback: a
callback that takes 30 ms of a 50 ms interval keeps the ticks 50 ms
apart, and one slower than the interval makes the next tick due on the
next turn. interval-slow-callback.ts checks both against Node.
Node sets SIGPIPE and SIGXFSZ to SIG_IGN at startup, so a write to a
closed pipe or past RLIMIT_FSIZE fails with EPIPE or EFBIG, and libuv's
spawns start children with every signal at its default action. The
runtime instead blocked both signals in the writing thread. On Linux the
signal a write raises goes to that thread, but on macOS it goes to the
process, so a write to a closed pipe from the fs pool's threads ended the
program with SIGPIPE.
- scr_init ignores SIGPIPE and SIGXFSZ. A process.on listener replaces the
disposition while it is registered, as before.
- scr_fs_write_once writes directly in executables, and the fs pool's
threads no longer block the two signals. Library builds keep the
per-thread blocking, since their host owns the dispositions.
- scr_spawn_command starts children with SIGPIPE and SIGXFSZ at their
default actions (POSIX_SPAWN_SETSIGDEF), for spawnSync, execFileSync
and spawn alike.
write-signals-ignored.ts and write-signals-listener.mjs check the ignored
signals, a listener, and each spawn path's child against Node.
A JavaScript object pattern over an untyped source (the result of an untyped function, a parameter, a property of an untyped object) now checks the source first when V8 can spell it, as the `any` path in TypeScript files does: a null or undefined source throws "Cannot destructure property 'size' of 'lookup(...)' as it is null." instead of "Cannot read properties of null (reading 'size')". When the first element has a default and a static key (`{ size = 0 }`), the source is not checked first and the read throws, as in V8; this also applies to `any` sources in TypeScript files and to nullable union sources.
The musl lane links zig's libc, whose memset, memcmp, bcmp and strlen are
byte loops, and whose memcpy and memmove copy without string
instructions. An x86_64 musl executable now defines these six routines
(scr_musl_mem.h): SSE2 for short and medium lengths, and rep stosb or rep
movsb from 2 KiB for memset, memcpy and forward memmove. zig's
definitions are weak, so these replace them. Library artifacts keep the
routines of the program they link into.
Adds a test that checks every length up to 600 and longer ones, every
alignment in a 32-byte window, overlapping moves in both directions,
memcmp's sign at each position, and strlen up to the end of a readable
page, optimized and under ASan and UBSan.
A call through a function-valued private static field (this.#fn(...))
completed its arguments against the field's fixed parameter list, so a
rest-parameter function lost the arguments for its rest parameter, and a
function that reads arguments was refused. The call now takes the
ordinary call-through-value lanes (typed rests, runtime-arity closures,
checked-dynamic values) and binds this to the checked receiver.