Skip to content

GC backlog re-verification sweep, 2026-08-02 #7262

Description

@proggeramlug

Re-verification sweep of the open GC/repsel/lint backlog against current main, prompted by the #7194 precedent: a real, deterministic GC bug that was already fixed by #7249 and sat open only because nobody re-tested it. The backlog grew faster than verification could retire it largely because gc-stress reported skipped on 12/12 nightly main runs until #7253 fixed its if:.

10 issues are fixed. 17 still reproduce. 2 cannot be concluded either way, and that is the most important finding here.

Method, and the bar every FIXED claim had to clear

  • Base: main = d4ac65f21 (refactor: split the 16 files over the 2000-line lint gate #7256), built --profile perry-dev -p perry -p perry-runtime-static -p perry-stdlib-static in an isolated target dir, PERRY_RUNTIME_DIR pinned to it, PERRY_NO_AUTO_OPTIMIZE=1. Oracle: node 26.5.1, matching .node-version.
  • Acceptance is the allocation-point arm: compile with no compile-time GC env, run with PERRY_GC_HEAP_LIMIT=8 PERRY_GC_INCREMENTAL=0 PERRY_CONSERVATIVE_STACK_SCAN=off. fix(gc): run the globalThis bootstrap in a no-move window (#7217) #7249 established that PERRY_GC_MOVING_LOOP_POLLS=1 cannot expose an unrooted local in runtime code that does not re-enter user JS, so a loop-polls-green result was not accepted as evidence for anything.
  • 10 runs per cell, byte-compared against the oracle.
  • Movement confirmed per issue. Every green cell reports its own copy-minor count and summed copied_objects from PERRY_GC_DIAG=1 on the same binary and the same arm. A green cell with copied_objects=0 was not accepted as a fix — see the two NOT VERIFIABLE rows, which is exactly the CLAUDE.md hazard "the gate runs but its subject never did".

Results

# bucket evidence reason
#7200 FIXED test_gap_gc_spread_accessor_rooting 10/10 byte-exact; 5 copy-minors, 17 861 copied Was exit=139 10/10. Fixed by #7207 (js_object_assign_one rooting) + #7249 (bootstrap no-move window).
#7201 FIXED test_gap_gc_static_block_this_rooting 10/10; 5 copy-minors, 17 364 copied Was bad 1 10/10. Fixed by #7207 (the PutValueSet key, not the static-this cell) + #7249.
#7202 FIXED lower_call/new.rs:1164 now root_entry_alloca(ctx, &this_slot); --unrooted-allocas --moving-only 0 violations over 138 modules / 5 779 gc-capable allocas Both named instances closed: inline-ctor this_slot by #7207, closure this/new.target by #7216. Residual staging array is #7210 §2.
#7208 FIXED test_gap_gc_closure_this_capture_rooting 10/10; 5 copy-minors, 17 325 copied Was exit=139 3/3. Fixed by #7216closure.rs now reserves and binds the +1 slot method.rs always had.
#7209 FIXED test_gap_gc_catch_param_rooting 10/10; 6 copy-minors, 1 614 copied Was message 8 field 8 of 400. Fixed by #7216 — the reserved catch-param index is now bound.
#7211 FIXED gc_root_dominance_check.py --moving-only: 5 hits → 0, over 2 282 functions / 138 modules / 2 950 root stores, --seeded-violations 4040 planted, 40 caught, 0 MISSED Fixed by #7226 (7f29b804c): protect_handle now includes !named_statics.is_empty(). The seeded arm proves the checker still parses the IR, so the 0 is a verdict, not a parse failure.
#7215 FIXED object/alloc.rs:1171 let owned: String = s.to_string(); before the loop Fixed by #7216. No runtime witness exists and the issue says so; the code change is the claim and it is present.
#6991 FIXED test_gap_repsel_ptr_shape_locals 10/10 on alloc-point, +force_evac, +force+verify (6 076 copied); zeal 3/3 at 6 copy-minors / 15 724 copied vs the 4 640 cited in the report Was SIGSEGV in a compiled ctor across the globalThis-population collection — literally #7249's root cause. Green at 3.4× the cited movement.
#6992 FIXED test_gap_repsel_proven_this_frozen 10/10 on the same three arms (6 049 copied); zeal 3/3 at 5 copy-minors / 15 623 copied vs the 4 614 cited Was TypeError: bump is not a function. Same cause, same fix. Green at 3.4× the cited movement.
#7066 FIXED (duplicate) same witness as #7208 Textually the same defect as #7208 (closure captured this in an unbound alloca), fixed by the same commit #7216.
#7246 STILL OPEN B 2 10/10 (oracle B 0); 88 copy-minors, 15 504 copied. Also on +PERRY_GC_FORCE_EVACUATE=1, 10/10 Survives #7243 as the issue predicted. alloc_symbol still gc_malloc(..., GC_TYPE_STRING); no GC_TYPE_SYMBOL exists in the tree, so nothing can trace the description pointer.
#7247 STILL OPEN test_gap_gc_regexp_receiver_rooting exit=139 10/10 Unchanged by #7249. js_regexp_new still opens with string_as_str(pattern)/string_as_str(flags).
#7248 STILL OPEN — and its numbers are now wrong bad char 2 count 2 6/10, bad char 3 count 3 4/10 See the correction comment on the issue: it is neither 1 nor deterministic on current main.
#7251 STILL OPEN no GcSuppressScope in global_this/generator.rs or global_this/typed_array.rs Deliberately not shipped with #7249; the two tower builders are still ungated and unwindowed.
#7254 STILL OPEN 3/3 rc=134, gc evacuation verification failed: stale forwarded pointer in shadow stack roots Reproduces exactly as written on d4ac65f21.
#7255 STILL OPEN default and verify_evac arms: 0 copy-minors on 5/5 files tried; evac_minor on the same binaries: 1, 2, 5, 5, 8, 88 The requires=scavenge arms are inert. This is the root blocker for two other rows below.
#7245 STILL OPEN cargo test -p perry-codegen --test native_proof_regressions: 235 passed, 16 failed — the same 15 invalidation::* plus typed_f64_receiver_method_clone_raw_loads_after_composed_guards Byte-identical failure list.
#7257 STILL OPEN benchmarks/ci_public_baseline_check.pypublic artifact benchmark inputs changed Byte-identical. Still gating steps 4–10 of lint.
#7258 STILL OPEN scripts/gc_store_site_inventory.py exit 1, 19 findings Byte-identical finding list.
#7259 STILL OPEN scripts/addr_class_inventory.py exit 1 Byte-identical: both ratchet regressions (child_process/value_util.rs 3/1, fs/dirent.rs 3/2) plus the O_SYMLINK allowlist substring miss.
#7183 STILL OPEN PERRY_GC_MOVING_LOOP_POLLS present in object_cache.rs:908, absent from build_cache.rs's key list (lines 24–53) Exactly the state the issue describes.
#7210 STILL OPEN codegen/helpers.rs:1275 still emit_raw("alloca [N x double]"), still filled interleaved §1's class-keys bullet is now a known false-positive class — see the correction comment.
#7231 PARTIALLY FIXED test_gap_gc_process_env_cache_rooting 10/10, 5 copy-minors / 16 750 copied; scan_process_env_cache_roots_mut registered at gc/mod.rs:639 #7239 rooted the three independently-verified caches and seven more. The unverified tail is still unrooted: no gc/ reference for SHAPE_CACHE, BUILTIN_MAP_SET_VALUE_BITS, MODULE_PATH_REGISTRY, RECOVERABLE_ERRORS, GLOB_ITERATORS, internally_handled.
#7261 STILL OPEN strip_dedup.rs removes extract_dir at 10 sites, never tmp_base The perry_strip_<pid> dir itself is never removed.
#6984 STILL OPEN PERRY_PTR_SHAPE_LOCALS=0TypeError: Cannot read properties of undefined (reading 'area'), rc=1, exact 4-line diff (p5a-iface/named/inherit/collide) Unchanged. This is also #6976 item 2.
#7016 STILL OPEN test_gap_repsel_p4a3_numarray_growth: 0 gc-diag lines at HEAP_LIMIT=8, +INCREMENTAL=0, and HEAP_LIMIT=2 Matches the issue's table exactly.
#6978 STILL OPEN test_gap_repsel_canonical_i32: 0 cycles at HEAP_LIMIT=8 alone, 1 with +INCREMENTAL=0 The issue's own acceptance signal (cycles > 0 with no other GC env) is still unmet.
#6982 NOT VERIFIABLE 10/10 byte-exact — but 0 copy-minors, and still 0 with PERRY_GC_FORCE_EVACUATE=1 added ⚠️ Not closed. The arm the issue specifies (conservative scan ON, INCREMENTAL=0) no longer runs a copying minor at all, so a green run says nothing about a crash inside a copying minor. Blocked on #7255.
#7018 NOT VERIFIABLE PERRY_GC_TRACE=1 5/5 rc=0, +force_evac 5/5 rc=0 — but 0 copy-minors with AND without the flag ⚠️ Not closed. The issue cites copied_objects of 261 251–379 072 for this file; this build reaches zero. The subject never ran. Blocked on #7255.

The two NOT VERIFIABLE rows are the finding

#6982 and #7018 both go green, 10/10 and 5/5, byte-exact against the oracle — and both are worthless as evidence, because copied_objects=0. Closing them on those runs would have been the single most damaging outcome of this sweep: two real crash reports retired on the strength of an arm that had gone inert underneath them.

That inertness is #7255, and it is now load-bearing for more than a stale header comment. Until the requires=scavenge arms relocate again, any "fixed under the shipped configuration" claim about the copying minor is unfalsifiable. #7255 should be treated as the top of this queue, ahead of the individual rooting bugs.

What genuinely remains

Bugs with a live, reproduced witness#7246, #7247, #7248, #7254, #6984 (= #6976 §2), #7251, #7210, #7231 (tail), #7261

Gate problems — a gate that cannot fail, or cannot run#7255 (four PR arms inert), #7257#7258#7259 (lint red at step 3, so steps 4–10 including the new gc_gate_wiring_check.py are unreachable), #7245 (16 red, not per-PR gated), #7016 (a corpus row that asserts nothing), #6978 (shipped config performs zero collections), #7183 (build-cache key)

Blocked on a gate before they can be judged#6982, #7018

What was NOT tested, explicitly

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions