codegen(async): await inside finally of an async generator block-waits (residual raw Await in the linearized finally state) #8715
Description
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_asyncclosure/function path (build_async_catch_route_body_direct) is not affected — the closure testasync_closure_rewrite_leaves_no_residual_awaitcoversin-finallyand 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).
Activity
fix(async): linearize `await` inside `catch` for async fns/closures (P…
fix(async): linearize await inside an async-generator finally; fix al…