Skip to content

codegen(async): await inside finally of an async generator block-waits (residual raw Await in the linearized finally state) #8715

Description

@proggeramlug

Summary

An await inside a finally block of a real async generator (async function*) is lowered to a raw Expr::Await inside its linearized finally state instead of an async suspend, so codegen (fs_await.rs, !ctx.is_async_fn) emits the blocking busy-wait (js_wait_for_event). This is the same class of deadlock as #8681 (await-in-catch), but in a different path: the #4438 B2-finally lowering in crates/perry-transform/src/generator/lower/abrupt.rs, not the was_plain_async catch path fixed by #8707.

Scope

  • The was_plain_async closure/function path (build_async_catch_route_body_direct) is not affected — the closure test async_closure_rewrite_leaves_no_residual_await covers in-finally and passes.
  • Only the real async-generator finally path leaves the residual. Minimal repro (transform-level):
async function* g() {
  try { yield 0; }
  finally { await Promise.resolve(); }   // <- becomes raw Expr::Await in a finally state
}

After transform_async_to_generator + transform_generators, the finally state contains LocalSet(__sent, Await(..)) (2 residual awaits — normal + abrupt completion copies) rather than an async suspend.

Likely fix direction

Analogous to #8707's catch fix: route the finally through its linearized FinallyRoute.finally_entry_state and stop materializing the finally body's suspends as raw awaits (the finally-state emission / build_finally_run_stmts interaction for the async path). This touches the heavily-patched #4438/#4374 abrupt-completion routing, so it wants its own change + behavioral validation (a try { await } finally { await } battery vs Node), separate from the catch fix.

Detection

The regression guard already exists: async_generator_linearizes_every_await_position in async_to_generator.rs had an await-in-finally case (removed from that test in #8707 and pointed here). Re-adding it is the acceptance test for this issue.

Found while landing #8681/#8707 (natively-compiled Claude Code -p streaming deadlock).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions