You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
gc: a loop-free program never drains its deferred nursery trigger, so it now performs zero collections (test_gap_specabi_reassign went inert on 9 matrix cells) #7059
Found while landing #7024 (PR #7057), which made the alloc-point deferral reachable under an explicit PERRY_GC_HEAP_LIMIT. It is a coverage loss, not a correctness bug, and it is worth a number of its own because it is the one place the matrix got less live.
The row does not collect at all any more on any arm that used to take the allocation-point fallback. It stays byte-exact against the oracle; the harness correctly reports UNVER rather than green.
Mechanism
test-files/test_gap_specabi_reassign.ts is a 20-line program with no loop. Codegen only emits the back-edge poll (js_gc_loop_safepoint) for allocating loop bodies, so this program contains no poll at all, and it does not run long enough to reach the microtask-pump safepoint.
Before #7024 the deferral was unreachable under the pressure knob (the defer cap and the trigger ceiling shared a formula), so a due nursery trigger fell through to the alloc-point non-moving minor and the row collected once. Now the trigger is correctly deferred to the next precise-root safepoint — and the program exits before reaching one. The safety valve (one gc_moving_defer_slack_dyn_bytes() of further growth) never fires either, because the program never allocates that much.
Nothing leaks: growth is bounded by the slack, and the process is exiting.
Why file it
One corpus row went inert, and the matrix's whole premise is that an inert arm is not a green one. Nine cells that used to be PASS are now UNVER for this reason.
It is a real property of the shipped collector, not just of the corpus: a loop-free program that reaches its first nursery trigger near exit now performs no collection at all where it previously performed one. For a short-lived process that is free; for anything holding a finalizer-bearing resource it is a behavioural difference worth knowing about.
Candidate directions (not prescriptive)
Extend poll coverage so the deferral has a drain in more shapes — the emit_gc_loop_safepoint doc already lists the specialized/versioned for lowerings, for-of and for-in as not yet emitting it (Phase 2 codegen work). That does not help a genuinely loop-free program.
Drain a pending deferral at a function-return safepoint, not only at loop back-edges and the microtask pump.
Or accept it and give the corpus row a shape that reaches a safepoint, so the matrix keeps its liveness on this representation. That is the cheapest, and the least honest, so it should be a deliberate choice rather than a drive-by edit.
The mechanism is gone. gc_scavenge_enabled() is now unconditionally default-ON (gc/mod.rs, tagged #7056, via #7377), and the defer-to-safepoint block is entered only when loop-polls are on — so with today's default the code falls straight through to an immediate gc_collect_minor_with_trigger.
This was a side effect rather than a fix filed against this issue, so if the test_gap_specabi_reassign matrix row still shows the symptom, please reopen.
Activity
proggeramlug commentedon Aug 5, 2026
The mechanism is gone.
gc_scavenge_enabled()is now unconditionally default-ON (gc/mod.rs, tagged #7056, via #7377), and the defer-to-safepoint block is entered only when loop-polls are on — so with today's default the code falls straight through to an immediategc_collect_minor_with_trigger.This was a side effect rather than a fix filed against this issue, so if the
test_gap_specabi_reassignmatrix row still shows the symptom, please reopen.