Skip to content

Fix #672: reject top-level / non-async for await...of in the type checker#713

Merged
nickna merged 1 commit into
mainfrom
wrk/objective-lovelace-bf0dc5
Jun 16, 2026
Merged

Fix #672: reject top-level / non-async for await...of in the type checker#713
nickna merged 1 commit into
mainfrom
wrk/objective-lovelace-bf0dc5

Conversation

@nickna

@nickna nickna commented Jun 16, 2026

Copy link
Copy Markdown
Owner

What

A for await...of drives the async-iterator protocol and is only valid in an async context — exactly like an await expression or await using. The type checker flagged those two (CheckAwait / CheckUsingDeclaration) but never checked the await modifier on Stmt.ForOf, so a top-level for await (or one inside a plain function) slipped through type checking and was then executed as an ordinary synchronous for...of. Iterating an async generator that way failed at runtime in both modes with a misleading error:

Before
Interpreted Runtime Error: for...of requires an iterable (array, Map, Set, or iterator).
Compiled System.Exception: Runtime Error: Value is not iterable... at $Runtime.IterateToList

Fix

Reject it in VisitForOf with the same 'await' is only valid inside an async function. diagnostic (TS1308) the await-expression and await using paths already emit. SharpTS does not support top-level await, so this is consistent with the existing up-front rejection of top-level await expressions, rather than silently degrading to a sync loop:

Type Error: 'await' is only valid inside an async function.

Now rejected up front in both modes (and also inside a non-async function, matching TS).

Tests

Adds both-mode regression tests in AsyncGeneratorTests:

  • ForAwaitOf_TopLevel_RejectedByTypeChecker
  • ForAwaitOf_InNonAsyncFunction_RejectedByTypeChecker

Full suite green (12,838 passed). Conformance suites are unaffected by construction: Test262 runs with type-checking off for .js (and no runtime path changed), and the TypeScriptConformance subset covers only assignmentCompatibility / conditional — no for await.

Note on #645

While picking up this branch I confirmed #645 (compiled for await inside an async arrow throwing InvalidCastException) is already fixed on main (commit 2b7fc82) and its repro now passes in both modes — closing that issue separately.

Closes #672

…hecker

A `for await...of` drives the async-iterator protocol and is only valid in
an async context, exactly like an `await` expression or `await using`. The
type checker flagged those two (CheckAwait / CheckUsingDeclaration) but never
checked the `await` modifier on `Stmt.ForOf`, so a top-level `for await` — or
one inside a plain function — slipped through type checking and was then run as
an ordinary synchronous `for...of`. Iterating an async generator that way fails
at runtime in both modes with a misleading "not iterable" error.

Reject it in VisitForOf with the same `'await' is only valid inside an async
function.` diagnostic (TS1308) the await-expression and `await using` paths
already use. SharpTS does not support top-level await, so this is consistent
with the existing up-front rejection of top-level `await` expressions, instead
of silently degrading to a sync loop.

Adds both-mode regression tests for the top-level and non-async-function cases.
@nickna nickna merged commit 62cf940 into main Jun 16, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Type checker: top-level for await...of is accepted then mis-run as sync for-of (top-level await is otherwise rejected)

1 participant