Repository navigation
rc: a value stored into a local that owns nothing is not freed under it - #652
Merged
Merged
Conversation
A local declared with no initializer (`let n: N;`), or in a catch or
finally clause, owns nothing (localTakesOwnership): it takes no count
for what it is given. A call's result stored into one was still a
discarded temporary, released at the end of its own block - before the
local's scope ends when the store sits in a nested block:
`let n: N; if (c) { n = mk(); } use(n)`, or a loop in a finally that
assigns a local declared outside it. The local then read a freed block.
releaseDiscardedTemporaries now leaves such a value alone when the
local is declared outside the value's block: a leak, the side this arc
keeps the uncertain case on. Fuzzer (#646) R9; the no-initializer
shape was found while reducing it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…store # Conflicts: # tslang/test/tester/CMakeLists.txt
This was referenced Oct 23, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes fuzzer bug R9 (#646), plus a broader shape found while reducing it.
Why
A local declared with no initializer, or inside a catch or finally clause, owns nothing (
localTakesOwnership, by design: no retain on store, no release at scope exit). A call's result stored into such a local was still treated as a discarded temporary, so it was released at the end of its own block. When that block is nested inside the local's scope (anif, a loop body), the release came while the local still held the value, and the next read used a freed block. When the store sits in the local's own block, that release happens at the local's scope end and is fine; that's whylet n = mk(); n = mk();in a catch worked.Fix (rc)
releaseDiscardedTemporariesleaves a value alone when it is stored into an unowned local declared outside the value's block. That value now leaks instead of being freed early, which is the direction this design already takes when it can't tell. Values stored into an unowned local in their own block are still released.Tests
tests/00unowned_local_store.tscovers:letassigned in anifand in a loopifIt fails under rc on Linux without the fix. The ownership verifier reports no errors on it. The test runs compiled, under JIT, and in the rc/none corpus.
Verification
ctest -C Release: Windows 4982/4982, WSL Linux 4964/4964.mm_fuzz.py --jobs 10 run --count 500 --seed 1: only the 3 known own failures (R1, O4).🤖 Generated with Claude Code