Repository navigation
Fix main: island objects as weak keys after #3135, and the runtime refusal baseline after #3137 - #3371
Conversation
#3135 builds scr_json.c once: no build of it defines SCR_NODE_ISLANDS any more, and its island paths call through ScrNiDynOps. #3325 was cut before that and keeps its paths for weak collections keyed by island objects under the define, so on main they are compiled into no program: a WeakMap, WeakSet or FinalizationRegistry given an island object refuses it with SC2020 again. The blocks are unconditional now and call three entries appended to the end of the table (weak_get, weak_test, weak_set), which the island handle unit installs. A value with the island tag exists only in a program that links that unit, so no site tests the table for NULL. The collection's `island` member is always there and NULL without islands. runtime-pack-matrix.test.ts also checks that every entry of the table is installed, in the struct's order, and that every call through the table names an installed entry. Co-Authored-By: Claude Code <noreply@anthropic.com>
The one-line wrapper sat at column 0 above scr_weak_set, and the refusal scan (scripts/runtime-refusals.mjs) reads a definition up to the next closing brace in column 0: it attributed scr_weak_set's refusal to the wrapper, a site the baseline does not list. The three callers test scr_ni_dyn_handle directly. Co-Authored-By: Claude Code <noreply@anthropic.com>
pnpm lint fails on main since #3137 (038fff5): its baseline of refusal sites was written before six sites that merged beside it (console methods through a stored value, Object.defineProperties with an unsupported property map, two fs path values, two Sign/Verify stream members). Written by node scripts/runtime-refusals.mjs --write: those six, the second symbol refusal of scr_finalization_key, and three scr_webstreams.c sites that no longer exist leave the list. Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Results that were still running when the PR was opened. Zero-cost gate,
This is the one-time cost of a call through an installed table in an always-linked unit, the exception recorded for #3135. Runtime benchmarks: 69 workloads, geomean -0.2%, 68 neutral, 1 faster ( Island conformance lane (check mode) on this branch: 408 pass, 9 fail. The failure shown is a signature that now ends in a diagnostic code, Not run in a sandbox: the native bootstraps (the compiler's own source creates no weak collection; CI's bootstrap job is the check) and the full test lanes. |
…ys after #3135 and its refusal baseline) into train/pi-1 Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
No conflict. Main now holds #3371, whose branch the train had merged: the same commits. Co-Authored-By: Claude Code <noreply@anthropic.com>
The fix for main's red tests since #3325 met #3135 (the island's weak collections leave scr_json.c for the installed table; the runtime refusal baseline is regenerated), merged as its own commits. One conflict, in runtime-pack-matrix.test.ts: the train's two tests and the fix's one were added at the same place; all three kept. Co-Authored-By: Claude Code <noreply@anthropic.com>
Main's fix for island objects as weak keys (vercel-labs#3371) adds weak_get, weak_test and weak_set to ScrNiDynOps. They keep main's place after report_uncaught; the train's error_has, promise_get, promise_has and promise_set follow. Co-Authored-By: Claude Code <noreply@anthropic.com>
The lane's list pins what each program prints. vercel-labs#3137 changed what every runtime refusal prints without touching a path that turns the lane on, so its own pull request did not run it, pushes to main never run it, and the stale list failed in the next pull request that touched island code instead (vercel-labs#3371). On a push the lane fails on the commit that moved the entries. Pushes to main and merge queue groups now select every lane except macos. Cost: the lane's four shards, 27 runner-minutes per push as measured on vercel-labs#3371's run (5.2, 9.0, 6.9 and 5.9 minutes), after the Linux native build a push runs anyway. Co-Authored-By: Claude Code <noreply@anthropic.com>
Main is red since 246041d (the merge of #3325) in six CI jobs, and its "lint and format" job is red since 038fff5 (the merge of #3137). Every PR that takes main inherits both. This PR turns both green; each cause has its own commit.
tests/harness/node-island-weak-keys.test.tsfails 3 of 3, the corpus programs8754-node-island-weak-keysand8850-node-island-weak-cachefail indifferential.test.tsandllvm-differential.test.ts, and two guard tests fail (runtime-pack-matrix.test.ts"a unit that tests SCR_NODE_ISLANDS has a build that defines it",runtime-define-sites.test.ts).pnpm lintfails innode scripts/runtime-refusals.mjs --strictwith seven refusal sites the baseline does not list. Because lint fails first, CI has skippedformat:check, the policy contracts and the generated IR checks behind it on every PR since 21:41 UTC.1. Island objects as weak keys
Cause
Two PRs that each passed their own CI and merged without a conflict:
scr_json.conce per flavor. No build of that unit definesSCR_NODE_ISLANDSany more; its island paths call through the table the island handle unit installs (ScrNiDynOps).scr_json.cunder#if defined(SCR_NODE_ISLANDS).On main those blocks are compiled into no program, so a
WeakMap,WeakSetorFinalizationRegistrythat is given an island object falls through to the older refusal:Weak collection keys of this native reference type have no weak lifetime lowering [SC2020]. This is the third crossing of this kind against #3135 (after #3161 and #3234); the two guard tests exist for it and namescr_json.c, but #3325's CI never ran with #3135 in its base.Fix
The same as for the earlier two: the blocks leave the
#ifdefand call through the table.ScrNiDynOpsgets three entries at the end,weak_get,weak_testandweak_set;scr_node_island_handle.cinstallsscr_ni_weak_get,scr_ni_weak_testandscr_ni_weak_setthere. New entries go last because the runtime every program links reads the table by offset.scr_json.c: the weak-collection and FinalizationRegistry paths test the key's island tag and call the entries. A value with that tag exists only in a program that links the island units, so the sites do not test the table for NULL.ScrWeakMap.islandis always a member and stays NULL in a program without islands.scr_json.ckeeps one build, so #3135's guard tests pass again without changing what they expect.Test that pins it
runtime-pack-matrix.test.ts, "the installed island table has every entry the shared units call through it": every entry ofScrNiDynOpsis installed bynh_dyn_opsin the struct's order, and everyscr_ni_dyn_ops->x(call in a runtime unit names an installed entry (the weak entries among them). It needs no libnode pack. A missing initializer line would otherwise be a NULL call that only an island program reaches.No test can catch this kind of crossing in the second PR's own CI when that CI ran on an older base: the guards fail on the merged tree, which is where main's CI showed them.
Other island paths
After this change
scr_async.cis the only runtime unit that testsSCR_NODE_ISLANDS, and it has builds that define it. No other island block on main is compiled out.Open PRs that are cut before #3135 and keep island blocks of
scr_json.cunder the define will meet the same guards when they take main: #3124 (15 conditionals in that unit at 9cdb212) and #3043 (13 at a132a9a). Each has to move its own blocks to the table and append its entries (json_query;iterator_method) afterweak_set, with the initializer lines in the same order.2. The runtime refusal baseline
#3137 made
pnpm lintcheck that every runtime refusal names a diagnostic code and is listed inpackages/runtime/refusal-sites.json. Its list was written before seven sites that merged while it was open. All 390 sites on main already name a code (390 name a diagnostic code, 0 do not), so no site needed one; only the list was behind.scr_console_native.c:70,scr_dyn_invoke.c:2797,scr_fs_path_value.c:29and:44,scr_pki.c:3693and:3695.scr_json.c:21367, is not a new refusal. It isscr_weak_set's, which the list has. Keep weak-collection entries keyed by Node island objects inside the island #3325 put a one-line function at column 0 abovescr_weak_set, and the scan reads a definition up to the next closing brace in column 0, so it attributed the refusal to that wrapper. The second commit removes the wrapper (its three callers testscr_ni_dyn_handledirectly), and the site isscr_weak_set's again.The third commit is the output of
node scripts/runtime-refusals.mjs --writeon the final tree. Its diff is those six sites and three things the strict check does not report, because it compares keys and only looks for unlisted ones:scr_console_native.csite replaces the one it had before (same site, new wording);scr_finalization_keyinscr_json.cis listed twice: Keep weak-collection entries keyed by Node island objects inside the island #3325 added a second raise of the same message in that function (an island symbol as a target or token);scr_webstreams.centries leave the list: their sites no longer exist.Evidence
All in Vercel Sandboxes, Linux x64,
CI=1, with a libnode pack assembled from the tree under test.Control, plain main 0d142e0:
node-island-weak-keys.test.tsfails 3 of 3 with the SC2020 refusal;runtime-pack-matrix.test.tsfails withexpected [ 'scr_json.c' ] to deeply equal [].node scripts/runtime-refusals.mjs --stricton main 511f1e8:7 are not in the baseline, exit 1.On this branch's head (main 511f1e8 merged in):
pnpm lint:390 name a diagnostic code, 0 do not; 0 are not in the baseline, oxlint 0 warnings and 0 errors.pnpm format:check: clean.code-quality.test.tsandci-stack-scope.test.ts39 of 39;pnpm generate:ts7-ast --check,generate:ir-fields --checkandgenerate:ir-codec --checkexit 0.node-island-weak-keys.test.ts3 of 3;runtime-pack-matrix.test.ts13 of 13 (the new test among them);runtime-define-sites.test.ts1 of 1.differential.test.tsandllvm-differential.test.ts(release and dev) for8754-node-island-weak-keys,8850-node-island-weak-cacheand9241-node-island-weak-import: pass.On the commits before the merge of main (base 0d142e0):
SCRIPTC_SAN=1:node-island-weak-keys.test.ts3 of 3, and the three corpus programs pass.tests/harness/node-island*.test.ts,island-value-matrix.test.ts): 11 files, 380 tests, all pass; no matrix entry moves.islandin its name,differential.test.ts: 131 pass.scr_json.obuilds of the release, library-release and speed flavors are byte-identical before and after it (the eight dev builds differ, they carry debug information).Still running when this PR was opened, results follow in a comment: the island conformance lane in check mode, and the zero-cost gate with
--gate-corpus=allplus the runtime benchmarks.Zero cost
scr_json.cis linked by every program, and this change is a call through an installed table in it: the one-time exception recorded for #3135 (one build per runtime unit; DECISIONS.md, 2026-11-07). Executables that link the weak-collection functions ofscr_json.ochange once: a tag test per weak operation, a NULL test in the collection's trace and release, and 8 bytes per collection. IR and LLVM do not change (no compiler source is touched). The row count and sizes come with the gate's result.🤖 Generated with Claude Code