Fix #488, #534, #634, #635: generator expressions in more positions; async function expressions#686
Merged
Merged
Conversation
…nc function expressions #635 — Parse async function expressions `async function () {}` / `async function* () {}` in expression position now parse. The parser previously only attempted an async arrow after `async`; added a `function` check that defers to FunctionExpression (now isAsync-aware), mirroring the statement-level async-function-declaration path. GeneratorArrowLifter — complete, identity-preserving AST traversal The lifter rewrites generator function expressions into declarations so the IL pipeline can lower them. Its traversal was incomplete, leaving generator expressions un-lifted in several positions (their `yield` then failed type-checking). #488 — descends through grouped / call-position callees, so an IIFE generator `(function*(){ yield 1 })()` is lifted and runs (both modes). #634 — descends into for / for-of / for-in / do-while / try-catch-finally / switch / labeled bodies (plus ternary, array/object literals, new, spread, await, etc.). #534 — a generator expression closing over an enclosing function's body-level local is lifted into that function (interpreter runs it natively; the compiler emits the existing #501 "Yield not supported" error, so the success path is interpreter-only). A conservative free-variable analysis over-approximates the bound set so a module-closing generator is never mis-relocated into a function — no regression to the #522/#1295 compiled module-closure cases. Tests: new AsyncFunctionExpressionTests; new GeneratorTests regions for #488/#634/#534. Full SharpTS.Tests suite green (12,716); TypeScript conformance green. Follow-ups filed: #678 (block-scoped/loop-var capture), #679 (named genexpr self-recursion), #681 (.call/.apply/.bind on async functions).
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.
Completes #488, #534, #634, #635.
#635 — Parse async function expressions
async function () {}andasync function* () {}in expression position now parse. The parser previously only attempted an async arrow afterasync(it unconditionally consumed(), so any async function expression was a parse error. Added afunction-keyword check that defers to the sharedFunctionExpressionparser (nowisAsync-aware, and it already handles*), mirroring the statement-levelasync functiondeclaration path.GeneratorArrowLifter — complete, identity-preserving traversal
GeneratorArrowLifterrewrites generator function expressions into top-levelStmt.Functiondeclarations so the mature generator-declaration IL pipeline can lower them. Its hand-rolled traversal covered only a subset of AST nodes, so a generator expression in an un-visited position was never lifted — and because the function-expression generator context is established only via the lift, the type checker then rejected itsyield. The rewriter now descends through every expression- and statement-bearing position (identity-preserving, so untouched subtrees keep their node identity for the type checker).(function* () { yield 1; })()is lifted and runs. ✅ both modesfor/for-of/for-in/do-while/try-catch-finally/switch/ labeled-statement bodies (plus ternary, array/object literals,new, spread,await, template literals, etc.). ✅ both modesTests
AsyncFunctionExpressionTests(anonymous/named/IIFE/awaited/callback async function expressions + async generator expressionfor await).GeneratorTestsregions for Generator function expression in IIFE/call position: 'yield' not recognized #488 (IIFE/spread), GeneratorArrowLifter does not recurse into for/for-of/for-in/do-while/try/switch/labeled statements (generator function expression in those contexts fails to type-check) #634 (each loop/try/switch/labeled body), and Generator function expression nested in a function can't close over the enclosing function's locals (lifted to module scope) #534 (closure over function local / parameter, by-reference capture — interpreted).SharpTS.Testssuite green (12,716 passed); TypeScript conformance green (31).Follow-up issues filed
letTDZ).function* foo(){ … foo() … })..call/.apply/.bindnot supported on async functions (interpreter: 'undefined is not a function') #681 —.call/.apply/.bindunsupported on async functions (pre-existing; surfaced via Parser: async function expressions (async function () {}) are not parsed #635).for await...ofover an async generator throws InvalidCastException (cast to IEnumerable) #680 was filed for the compiled async-generatorfor awaitcrash, then closed as a duplicate of Compiler: for await...of over an async generator inside an async arrow emits synchronous IEnumerable iteration (InvalidCastException) #430/Compiled: or await over an async generator inside an async ARROW function throws InvalidCastException (lowered as sync for-of) #645 — that fix landed onmainduring this work, soasync function*expressions now run in both modes.Note on Test262
async functionexpressions let the wholeArray/fromAsync/*Test262 suite parse (it usesasyncTest(async function () { … })); those ~76 tests moveParseError → Fail(a parser progression — they fail only becauseArray.fromAsyncis unimplemented). The Test262 baseline is not updated here: local regen is non-deterministic in this Windows environment (unrelated tests flipPass/Fail/RuntimeErrorrun-to-run, plus intermittent host crash0x80131506), so a regen would bake in noise. Test262 is not a CI gate; recommend regenerating its baseline in a stable environment.