Skip to content

A labelled break or continue disposes and releases up to the statement it names - #591

Merged
ASDAlexander77 merged 1 commit into
mainfrom
fix-labelled-scope-exit
Oct 17, 2026
Merged

ASDAlexander77 merged 1 commit into
mainfrom
fix-labelled-scope-exit

Conversation

@ASDAlexander77

Copy link
Copy Markdown
Owner

Follow-up to #590: the scope-exit bugs found there, all in the walk a break/continue makes outwards to dispose using declarations and release owned locals.

A labelled jump stopped at the wrong scope. scopeExitContinuesOutwards handed each enclosing scope an empty label, and every loop's context held an empty one. So a labelled jump stopped at the first loop body it met:

  • continue outer written inside an if in an inner loop never disposed the outer body's using.
  • A jump to a labelled block (a: { ... break a; }) matched nothing and ran on to the function's scope. It disposed the function's using there and then (and never at the function's end). Under -mm=rc it released the function's locals: a string built in the block was freed before it was returned. This is the failure Linux CI showed on continue a in a: b: for goes on to the next iteration; labelled for-of no longer crashes #590.

Now the walk carries the label all the way out:

  • A loop's body scope records the loop's label in a new loopBodyLabel. loopLabel alone can't do this, because a loop's context is a copy of the scope around it and stands in for that scope in the walk.
  • A labelled statement that isn't a loop gets a scope of its own. labelledScopeLabel marks where its jumps stop.
  • New scopes clear labelledScopeLabel so they don't inherit it.

An unlabelled break in a labelled block only left the block. The LabelOp lowering claimed unlabelled jumps; in JS they belong to the loop or switch around the block. LabelOp now claims only its own label (LowerToAffineLoops.cpp).

A loop body without braces had no body scope. In for (...) if (c) continue;, the jump disposed the scopes around the loop, here the function's using. Such a body is now made a scope through mlirGenBranchStatement, as a brace-less if branch already was.

New test 00labelled_scope_exit.ts (compile + JIT, and in TSLANG_CORPUS) checks dispose order for each case, plus the rc string cases. It passes under gc, rc and none, and --verify-ownership is quiet on it.

Found, not fixed here

Any using in a case clause crashes the compiler, even with no break. Case clauses are generated in the enclosing scope's context, so the using lands in the function's dispose list, but its symbol is gone by the function's end. A switch needs a scope of its own, and an unlabelled break needs to stop at it. That's a different change, which I'll propose separately.

Full ctest -C Release -j 8 --timeout 300: 4112/4112 passed.

🤖 Generated with Claude Code

…t it names

The scope-exit walk handed each enclosing scope an empty label, and every
loop's context held an empty one, so a labelled jump stopped at the first
loop body it met: `continue outer` in an `if` in an inner loop never
disposed the outer body's `using`. A jump to a labelled block matched
nothing and ran on to the function's scope, disposing the function's
`using` there and then and, under rc, releasing its locals (a string built
in the block was freed before it was returned).

The walk now carries the label. A loop's body scope records the loop's
label in `loopBodyLabel`, apart from `loopLabel`: a loop's context is a copy
of the scope around it and stands in for that scope in the walk. A labelled
statement that is not a loop gets a scope of its own, `labelledScopeLabel`,
where its jumps stop.

An unlabelled break in a labelled block left only the block: LabelOp
lowering claimed unlabelled jumps, which in JS belong to the loop or switch
around it. And a loop body written without braces had no body scope, so a
break or continue in it disposed the scopes around the loop; it is now made
one as a brace-less if branch is.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ASDAlexander77
ASDAlexander77 merged commit ad6b9ef into main Oct 17, 2026
2 checks passed
@ASDAlexander77
ASDAlexander77 deleted the fix-labelled-scope-exit branch October 17, 2026 16:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant