Fix #435, #669, #625, #641: arrow/closure capture in generator & async-arrow state machines#699
Merged
Merged
Conversation
…c-arrow state machines Compiled-mode arrows that capture variables inside generator and async-arrow state machines were miscompiled. Each issue's primary case is now fixed and passes --verify; deeper sub-cases fail-fast with a clear compile error and are tracked by follow-up issues rather than silently miscompiling. #435/#669 - arrows inside sync generator bodies: - Add the missing Expr.Yield case to CollectArrowsFromExpr; arrows inside a yielded expression were never collected, so the callback compiled to ldnull ("Array.prototype.map callback is not callable"). - Teach the base EmitArrowFunction to build the display class for capturing arrows via the existing GetThisField/GetHoistedVariableField hooks. GeneratorMoveNextEmitter is the only emitter using the base default, so this fixes read/loop-var/this captures without adding a 4th per-emitter duplicate. - Fix GeneratorStateAnalyzer.UsesThis to see `this` used only inside an arrow, so an instance generator method materializes its receiver (else the captured `this` snapshot was null -> NRE). - Reject an arrow that WRITES a captured generator variable (the generator SM has no function display class to share storage) -> tracked by #674. #625 - async arrow writing a captured variable emitted unverifiable IL: - A boxed value-type state machine cannot be mutated in place by verifiable IL (unbox yields a readonly managed pointer; stfld through it fails ILVerify and can drop the write). Promote direct-child written captures into the reference-type function display class and have the arrow read/write outer.functionDC.field. Async-method and top-level-arrow variants -> #682. #641 - capturing async arrow nested in a top-level async arrow was unsupported: - Thread the single capture through the standalone stub's $TSFunction prepend slot, loaded from the enclosing arrow's frame. Resolve standalone captures in the emitter at LOW priority (below module globals) so a top-level variable a standalone arrow closes over still reads live from its static field rather than the by-value snapshot. Write-back and multi-capture -> #684. Shared CapturedWriteAnalysis.CollectImmediateWrites backs the write-capture guards. ILVerifier now surfaces the failing IL offset/found/expected. Verification: full xUnit suite 12,713/12,713; TypeScriptConformance 31/31; 29 new regression tests (generator arrow bodies, async captured writes, nested async captures). Test262 corpus could not run here due to the pre-existing 0x80131506 test-host crash (independent of these compiled-only changes).
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.
Compiled-mode arrows that capture variables inside generator and async-arrow state machines were miscompiled. Each issue's primary case is now fixed and passes
--verify; deeper sub-cases fail-fast with a clear compile error (never a silent miscompile) and are tracked by follow-up issues.What & why
#435 / #669 — arrows inside sync generator bodies
CollectArrowsFromExprhad noExpr.Yieldcase, so an arrow inside a yielded expression (yield arr.map(x => …)) was never registered → the callback compiled toldnull→ "Array.prototype.map callback is not callable". Added the case.GeneratorMoveNextEmitteris the only emitter that uses the baseExpressionEmitterBase.EmitArrowFunction, so I taught the base to build the display class for capturing arrows via the existingGetThisField/GetHoistedVariableFieldhooks. This fixes read / loop-var /thiscaptures without adding a 4th per-emitter duplicate (it actually reduces the triple-sync surface).thiscapture:GeneratorStateAnalyzer.UsesThisdidn't descend into arrows, sothisused only inside an arrow left the receiver field (<>4__this) undefined → NRE. Fixed with a focused sub-visitor.forEach(n => s += n)) need a function display class the generator SM lacks → clear compile error, tracked by Compiled: arrow inside a generator body that writes a captured variable needs a function-level display class (mutation lost) #674.#625 — async arrow writing a captured variable emitted unverifiable IL (
StackUnexpected)A boxed value-type state machine cannot be verifiably mutated in place —
unboxyields a readonly (controlled-mutability) managed pointer, sostfldthrough it fails ILVerify and can drop the write in complex state machines. Fix promotes direct-child written captures into the reference-type function display class; the arrow reads/writesouter.functionDC.field(a class reference → verifiable). Async-method and top-level-arrow variants → #682.#641 — capturing async arrow nested in a top-level async arrow was unsupported
Threads the single capture through the standalone stub's
$TSFunction"target" prepend slot, loaded from the enclosing arrow's frame. Standalone captures are resolved in the emitter at low priority (below module globals) so a top-level variable a standalone arrow closes over still reads live from its static field rather than the by-value snapshot. Write-back and multi-capture → #684.Verification
--verifyon every repro0x80131506test-host crash (crashes before executing any test; independent of these compiled-only changes)A mid-work regression (6
*_CapturedTopLevel_AsyncArrowtests) was caught and fixed: the standalone-capture read had to move to low priority in the emitter, since a top-level var is also registered as a standalone capture but must read live from its static field.ILVerifiernow surfaces the failing IL offset / found / expected in its error messages (it previously reported only "Unexpected type on the stack" with no location) — this is what made #625 diagnosable.Reviewer notes
origin/mainadvanced ~6 commits during development (adjacent async-generator try/catch + for-await rework). No file-level conflicts (they touch sibling partial files), and Compiler: array methods with arrow/function callbacks inside a (sync) generator body are not callable #435/Compiled generator body: arrow functions are miscompiled (map/forEach callback not callable; captured closures crash) #669 remain unaddressed there — but please rebase and re-run the suite before merging since that work is nearby.