Summary
Under the full-evacuation stress mode PERRY_GC_FORCE_EVACUATE=1, a strong, module-level array that is populated from inside a function (closure/IIFE) loses all its elements after allocation churn + gc(). No weak references are involved. Distinct from #5459 (which was a closure array-push write-back codegen bug, fixed by #5461 and correct under default GC) — this reproduces only under forced evacuation.
Repro
declare function gc(): void;
function churn(n: number): void {
let j: any[] = [];
for (let i = 0; i < n; i++) { j.push({ i, p: "z".repeat(40) + i }); if (j.length > 128) j = []; }
}
const strong: any[] = [];
(function setup() { for (let n = 0; n < 40; n++) strong.push({ id: n }); })(); // populate from a fn
for (let c = 0; c < 12; c++) { churn(80000); gc(); }
let alive = 0;
for (let n = 0; n < 40; n++) if (strong[n] && strong[n].id === n) alive++;
console.log("strong-array alive:", alive, "/ 40");
- Default GC:
40 / 40 ✓
PERRY_GC_FORCE_EVACUATE=1 PERRY_GC_VERIFY_EVACUATION=1: 0 / 40 (5/5 runs). VERIFY does not panic, so no dangling-forwarded-ref is detected — the elements are simply collected despite the live strong reference from the module-global array. Downstream, accessing the freed elements can SIGSEGV (heap-layout dependent).
Notes
Impact
PERRY_GC_FORCE_EVACUATE is a debug/bisection mode (gc-stress CI, tag-gated), so no production impact — but it currently makes that stress mode unsound for an ordinary code shape, which limits its usefulness as a correctness gate.
Summary
Under the full-evacuation stress mode
PERRY_GC_FORCE_EVACUATE=1, a strong, module-level array that is populated from inside a function (closure/IIFE) loses all its elements after allocation churn +gc(). No weak references are involved. Distinct from #5459 (which was a closure array-push write-back codegen bug, fixed by #5461 and correct under default GC) — this reproduces only under forced evacuation.Repro
40 / 40✓PERRY_GC_FORCE_EVACUATE=1 PERRY_GC_VERIFY_EVACUATION=1:0 / 40(5/5 runs).VERIFYdoes not panic, so no dangling-forwarded-ref is detected — the elements are simply collected despite the live strong reference from the module-global array. Downstream, accessing the freed elements can SIGSEGV (heap-layout dependent).Notes
gc/copying.rs, root/remembered handling for module-global array roots).Impact
PERRY_GC_FORCE_EVACUATEis a debug/bisection mode (gc-stress CI, tag-gated), so no production impact — but it currently makes that stress mode unsound for an ordinary code shape, which limits its usefulness as a correctness gate.