Repository navigation
A generator's loop local gives back what the last turn stored - #618
Merged
Merged
Conversation
A local declared in a generator's loop is a field of the state object, and
its declaration stores into that field on every turn. Nothing released what
the previous turn stored there, so under rc and own every turn but the last
leaked (a const string[] in a while(true) grew by about 180 MB over 500000
runs). registerVariable now emits ts.ReleaseSlot on the field before the
store.
Under own that makes the generator's next() a call that may drop, and a
caller's for...of was rejected: walkBorrowed followed the {value, done}
record returned by it.next() as part of the method read out of `it`, so the
record's release counted as a use after the other next() call. A call marked
__own_fresh_result hands back a block of the caller's own, and the walk now
stops there.
Tests: own_generator_loop_local.ts under own, rc, gc and none (JIT and AOT),
and an IR check that each generator's next releases its loop local.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
What
A local declared in a generator's loop is a field of the generator's state object. Its declaration stores into that field again on every turn, and nothing released what the previous turn had stored there. Under
-mm=rcand-mm=own, every turn but the last leaked.registerVariable(allocateInContextThis branch): emitts.ReleaseSlot(storage)after the retain/consume and before the store. The field starts out null, so the first turn releases nothing.walkBorrowed: stop at a call marked__own_fresh_result. Without this, the release above made own reject a caller'sfor (const g of gen()):next()may drop, the checker looked at the method read out of the generator'snextfield (itsthisis the generator).{value, done}record thatnext()returns, so the record's final release counted as a use of that read, coming after the otherit.next()call.__own_fresh_result, and its result is the caller's own block.own_err_field_function_reassignedandown_err_generator_place_overwritten, so the rule is limited to fresh results.Measured (peak private bytes, AOT, 500000 runs)
const result: string[] = [label(i++), "x"]inwhile(true)(rc / own)"a" + i++(rc)The remaining growth in the original repro comes from the number->string temporary of
"a" + i++.OwnedReturnConsumptionPassskips whole generators (spec 18.5), so no temporary in a generator body is released.yield ("a" + i).lengthleaks the same way with no local at all. That is a separate change, being worked on separately.Tests
own/own_generator_loop_local.tsruns under own (it's in the own_test list, so it also gets the no-counting check), and under rc, gc and none, both JIT and AOT. It covers:constin aforand in awhile(true)withbreakfor...ofover a generator that yields fresh arraysnext()Each local is read after a
churn(), so a release of the wrong value reads garbage.test-rc-generator-loop-local-releasereads the IR: each generator'snexthas ats.ReleaseSlotof itsstring[]field. It fails with the old compiler.Full
ctest -C Release -j 16: 4758 passed, 0 failed.With the uncommitted
own-param-markerswork applied on top:-R "own|markers"passed 809/809, includingown_generator_locals.🤖 Generated with Claude Code