Found while implementing #945 and executed against node v24.4.1 and zig-js main (70234924). Fresh control-flow boundaries save the lengths of active_labels, pending_labels, and continue_labels, truncate the lists to zero, then restore the old lengths. A nested label reuses and overwrites the retained backing slots, so restoring the length resurrects the nested label in place of the outer one.
| boundary | program shape | node | zig-js |
|---|---|---|---|
| ordinary function | var n=0; outer: for(;;){ function f(){ inner:{ break inner; } } n=1; break outer; } n | 1 | UnexpectedToken |
| class static block | var n=0; outer: for(;;){ class C { static { inner:{ break inner; } } } n=1; break outer; } n | 1 | UnexpectedToken |
The nested body itself is valid. The later break outer fails because slot zero now contains inner.
Fix the boundary operation by moving each complete ArrayListUnmanaged value aside and installing an empty list for the nested context, then restoring the original list. Apply the same operation to ordinary functions, arrows (#945), and static blocks. The inner list remains arena-owned, so no ownership transfer or cleanup is needed.
Done when both probes return 1, nested relabeling remains valid, and focused parser tests cover restoration after each boundary.