v3 VM: Closures and GC
- Trace lexical environments through returned closures.
- Derive GC edges from this VM’s actual object kinds.
- Diagnose cycles, root retention, and repeated-collection failures.
1 / 6
Suppose global m points to map M, whose value at key "saved" is array A. A holds closure C; C captures environment E containing back→M. Unrooted array U was allocated and popped:
global.m → M → A → C → E.back → M
U has no reachable incoming edge
| Step | Newly marked objects | Why traversal continues or stops |
|---|---|---|
| Root global.m | M | Visit map values |
| M["saved"] | A | Visit array elements |
| A[0] | C | Visit captured environment E |
| E.back | none | M already marked; cycle terminates |
| Sweep | remove U | U was never reached from a root |
HeapCount changes from four to three. E and the string key do not add to that count. If every root into the cycle is removed, all three objects become collectible; references inside an unreachable cycle do not preserve it.
A collector passes “allocate array, pop, collect” but loses an array stored inside a rooted map. A later patch survives one GC and fails after adding a new child before the second. Identify both missing invariants.
Draw a rooted closure/environment cycle containing two arrays and an unreachable sibling array. Predict HeapCount before GC, after GC, and after removing the final root. Explain why host test references do not change these answers.
Carry a closure-lifetime trace and a repeated-GC fixture into BC2 and the capstone. Successful arithmetic execution alone is not evidence that heap reachability is correct.