Skip to content

The usings of a scope are disposed in reverse declaration order - #593

Merged
ASDAlexander77 merged 1 commit into
mainfrom
fix-using-dispose-order
Oct 17, 2026
Merged

ASDAlexander77 merged 1 commit into
mainfrom
fix-using-dispose-order

Conversation

@ASDAlexander77

Copy link
Copy Markdown
Owner

{ using a = new D("a"); using b = new D("b"); } disposed a then b. JS disposes the last one declared first, so a later resource can still use an earlier one while it is disposed.

mlirGenDisposable (MLIRGenImpl.h) walked the scope's usingVars forward. It now walks them in reverse, as mlirGenReleaseOwned beside it already does for owned locals. It is the only place that iterates the list, and every scope exit goes through it: the end of a block, return, break, continue, the unwind cleanup region of a block or try body, and the switch's TryOp wrap from #592. So they all changed together. The order across scopes was already right (inner scope first).

Because of the dispose guard, a [Symbol.dispose]() that throws still leaves the earlier usings for the cleanup to dispose: body;b;a;caught;, as in JS.

Tests

  • New 00using_dispose_order.ts: two and three usings in one scope, through falling off a block and a function body, a return out of a nested block, a throw out of a block and out of a try body, a throwing dispose, continue/break out of a loop body, and break out of a switch case. It is registered for compile and JIT, and through the corpus for rc and none.
  • 00switch_case_scope_exit.ts gets a using in each of two cases. The cases share the switch's scope, and A using in a switch case is disposed where the switch ends; break in an if in a case compiles #592 had avoided this shape because of this bug.
  • No existing test depended on the old order: 00-04disposable, 00labelled_scope_exit, 00switch_case_scope_exit and the try_catch_return_dispose tests pass with no change to their expectations. Each of them has at most one using per scope.

Verification

  • JIT under gc, rc and none prints the reverse order for both files.
  • --emit=mlir-affine --verify-ownership (rc, own) reports nothing on either file.
  • Full ctest -C Release -j 8 --timeout 300: 100% passed, 0 failed out of 4124. The two test files were edited after that run; I reran them and the ownership-verifier shards afterwards, and all 20 passed.

Not in scope (pre-existing, still open)

  • The ownership verifier flags let s = ...; { using a = new D(); return s; }: a function-level owned local has no unwind release when a nested using-block's cleanup unwinds out of the function.

🤖 Generated with Claude Code

`{ using a = new D("a"); using b = new D("b"); }` disposed `a` then `b`; JS disposes the last
one declared first, so a later resource can still use an earlier one while it is disposed.
mlirGenDisposable walked the scope's usingVars forward; it now walks them in reverse, as
mlirGenReleaseOwned beside it already did for owned locals.

Every scope exit goes through it - the end of a block, `return`, `break`, `continue`, the
unwind cleanup region of a block or try body, and the switch's wrap - so they all change
together. With the dispose guard, a `[Symbol.dispose]()` that throws in the body still leaves
the earlier `using`s for the cleanup to dispose.

New test 00using_dispose_order.ts: two and three `using`s in one scope, through falling off
a block and a function body, a `return` out of a nested block, a throw out of a block and out
of a try body, a `[Symbol.dispose]()` that throws, `continue`/`break` out of a loop body, and `break` out of a switch case.
00switch_case_scope_exit.ts gains a `using` in each of two cases, which share the switch's
scope. No existing test depended on the old order (each had at most one `using` per scope,
and nested scopes were already disposed inner first), so none of their expectations changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ASDAlexander77
ASDAlexander77 merged commit bafd853 into main Oct 17, 2026
2 checks passed
@ASDAlexander77
ASDAlexander77 deleted the fix-using-dispose-order branch October 17, 2026 17: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