Skip to content

Interpreter: async generator with a C-style for loop whose condition reads a parameter yields nothing #788

@nickna

Description

@nickna

Summary

In the interpreter, an async function* (or async *m() method) whose body contains a C-style for loop with a condition that reads a parameter (or other enclosing-scope variable) produces no values — the loop body never runs. The equivalent while loop works, the equivalent sync generator works, and compiled mode is correct in all cases. The failure is silent (no error, exit 0), which makes it especially dangerous.

Repro

async function* g(n: number) { for (let i = 0; i < n; i++) yield i; }
async function main() { for await (const x of g(3)) console.log(x); }
main();
// interpreter: (no output)
// compiled:    0 1 2   (correct)

Minimal diff matrix (interpreter)

Variant Result
async gen, for (let i=0;i<n;i++) reading param n ❌ no output
async gen, for (let i=0;i<3;i++) literal bound ✅ works
async gen, while (i<n) reading param n ✅ works
async gen, reads param into a local then yield (no loop) ✅ works
sync gen, for (let i=0;i<n;i++) reading param n ✅ works
async gen, bare yield a; yield a*2 reading param a ✅ works

Compiled mode is correct for every row.

Likely cause

The parser desugars a C-style for into a block-scoped while ({ let i = 0; while (i < n) { … i++; } }). In the interpreter's lazy async-generator coroutine, that extra block scope around the desugared while apparently does not chain back to the function's parameter/closure environment, so the condition reads n as undefinedi < undefined is false → zero iterations. A hand-written while (no wrapping block) works, and the sync generator for works, which points at the async-generator coroutine's environment handling of the for-desugaring block specifically (cf. the lazy-coroutine rewrite around #690/#717/#752).

Scope

Interpreter-only; compiled mode (IL) is unaffected and correct. Discovered while implementing compiled static/async generator methods (#778).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions