You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In the interpreter, a labeled iteration statement (label: for/while/... {}) that contains an await or yield fails, because labeled statements have no async execution handler: RegistryDispatch.DispatchStmtAsync falls back to the synchronous ExecuteLabeledStatement, which runs the loop body via the sync Execute/Evaluate path. That path cannot suspend, so an await inside throws "'await' can only be used inside async functions" and a yield inside throws a raw YieldException.
Repro
// await inside a labeled loop in a plain async functionasyncfunctionf(){
outer: for(leti=0;i<2;i++){for(letj=0;j<2;j++){constx=awaitPromise.resolve(i*10+j);console.log("v"+x);if(j===0)break outer;}}}f();// Interpreter: Runtime Error: 'await' can only be used inside async functions.// Node: v0
// yield inside a labeled loop in an async generatorasyncfunction*g(){
outer: for(leti=0;i<3;i++){for(letj=0;j<3;j++){try{yieldi*10+j;if(j===1)break outer;}finally{console.log("fin"+i+j);}}}}asyncfunctionmain(){forawait(constvofg())console.log("v"+v);}main();// Interpreter: fin00 then "Runtime Error: ...YieldException..."// Node: v0, fin00, v1, fin01
Compiled mode is correct for both (it has a full async-generator/await state machine). The plain (non-labeled) sibling loops work in the interpreter — only the labeled form falls back to sync.
Cause / fix direction
Two pieces are needed:
An async handler for Stmt.LabeledStatement (an ExecuteLabeledStatementAsync mirroring the sync ExecuteLabeledStatement but running the inner statement via ExecuteStatementAsync).
Labeled break/continue adoption in the async loop handlers (ExecuteForAsyncVT, ExecuteWhileAsyncVT, ExecuteDoWhileAsyncVT, ExecuteForOfAsync, ExecuteForInAsync). These currently only honor unlabeled break/continue (TargetLabel == null); they do not drain _pendingLoopLabels / carry LabelNames the way the sync loops do (Compiled: chained label on a for re-runs initializer on continue <outerLabel> (interpreter handles it) #580). Adding (1) without (2) would regress labeled loops that today work via the sync fallback (a labeled continue would propagate out of the loop instead of advancing it).
Notes
Pre-existing; surfaced while rewriting the interpreter async generator to a lazy coroutine (which runs the body through the real async execution path). The compiled-only AsyncGeneratorTryFinallyTests.LabeledBreakToOuterLoop_RunsInterveningFinally / LabeledContinueToOuterLoop_RunsInterveningFinally cover the compiled side; the interpreter side is this gap.
Summary
In the interpreter, a labeled iteration statement (
label: for/while/... {}) that contains anawaitoryieldfails, because labeled statements have no async execution handler:RegistryDispatch.DispatchStmtAsyncfalls back to the synchronousExecuteLabeledStatement, which runs the loop body via the syncExecute/Evaluatepath. That path cannot suspend, so anawaitinside throws "'await' can only be used inside async functions" and ayieldinside throws a rawYieldException.Repro
Compiled mode is correct for both (it has a full async-generator/await state machine). The plain (non-labeled) sibling loops work in the interpreter — only the labeled form falls back to sync.
Cause / fix direction
Two pieces are needed:
Stmt.LabeledStatement(anExecuteLabeledStatementAsyncmirroring the syncExecuteLabeledStatementbut running the inner statement viaExecuteStatementAsync).ExecuteForAsyncVT,ExecuteWhileAsyncVT,ExecuteDoWhileAsyncVT,ExecuteForOfAsync,ExecuteForInAsync). These currently only honor unlabeled break/continue (TargetLabel == null); they do not drain_pendingLoopLabels/ carryLabelNamesthe way the sync loops do (Compiled: chained label on aforre-runs initializer oncontinue <outerLabel>(interpreter handles it) #580). Adding (1) without (2) would regress labeled loops that today work via the sync fallback (a labeledcontinuewould propagate out of the loop instead of advancing it).Notes
AsyncGeneratorTryFinallyTests.LabeledBreakToOuterLoop_RunsInterveningFinally/LabeledContinueToOuterLoop_RunsInterveningFinallycover the compiled side; the interpreter side is this gap.