You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 0142cd5
Browse filesBrowse the repository at this point in the historyBrowse files
An exception out of a try/finally, a nested block in a try, or a throw releases the scopes it leaves (#596)
Under -mm=rc, --verify-ownership still reported real leaks of an owned local on three unwind
shapes that #595 left open, plus a plain `throw`:
- `let s = a + b; try { h(); } finally { ... } return s;` - after the finally rethrows, `s` was
never given back. The release cannot go in the cleanup region, which runs before the finally
and so before it may read `s`. MLIRGen now puts the walk of #595 (mlirGenReleaseOwnedOnUnwind)
at the end of the finally, with each release marked UNWIND_ONLY_ATTR_NAME. TryOpLowering keeps
them in the copy of the finally the landing pad runs, and erases them from the copies normal
completion, `return` and `break` run (through each copy's mapping: the rewriter defers an
erasure, so an op erased from the region is still cloned into later copies).
- `try { { let s = a + b; h(); } } catch {}` - the TryOp's cleanup releases its body's scope and
no other. A block that declares a let/const inside a TryOp body (GenContext::isUnwindCleanupScope
somewhere up the chain) is now wrapped like a `using` block, so its cleanup releases it and the
scopes up to that body.
- `try { h(); } catch { throw 2; }` and `let s = a + b; throw 1;` - a throw statement now releases
the scopes it leaves up to the nearest TryOp body, as a `return` does, after taking a reference
for the thrown value (mlirGenRetainCaptured at last use) so `throw s` is not freed with `s`. The
thrown value is never given back, so it leaks rather than dangles. A catch clause of a try with a
finally stops that walk, since the finally's unwind copy releases those scopes.
New test 00try_unwind_outer_locals.ts (compile + JIT + TSLANG_CORPUS, so gc/rc/none) covers the
finally on throw/complete/return/break, a throw from a catch with and without a finally, a try in
a try body, nested scopes and a loop body in a try, and throws from a function body and of a local.
The compiler before this change reports 9 slots in it; the verifier sweep covers it.
Still open: a call that throws out of a catch with no finally, a typed catch that does not match
with no finally, and a throwing call with no try around it - none has a landing pad to release
from. Found on the way, all pre-existing: an empty `catch { }` does not catch; under rc a `return`
through a finally releases the frame before the finally runs; `return` inside a finally, and a
try/catch/finally nested in a finally, crash Win32ExceptionPass.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
0 commit comments