ReviewOS

zig-utils/zig-js

Parser: nested labels overwrite outer label-list storage across function and static-block boundaries

#950
Closed chris opened this 10 days ago ·
10 days ago

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.

boundaryprogram shapenodezig-js
ordinary functionvar n=0; outer: for(;;){ function f(){ inner:{ break inner; } } n=1; break outer; } n1UnexpectedToken
class static blockvar n=0; outer: for(;;){ class C { static { inner:{ break inner; } } } n=1; break outer; } n1UnexpectedToken

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.

10 days ago

Fixed on main in 9e4ae588.

Function bodies, arrow bodies, and class static blocks now move the complete three-list label context aside and install fresh lists. Nested labels can no longer overwrite retained outer slots. Both executed repros changed from UnexpectedToken to 1, the arrow-boundary variant also returns 1, and relabeling inside a nested arrow remains valid.

Covered by the 226/226 parser module run, the 20-case focused frontend matrix, and the exact-parent 9,409-case corpus comparison reported on #945.

Sign in to comment on this issue.