diff --git a/tslang/lib/TypeScript/MLIRGenStatements.cpp b/tslang/lib/TypeScript/MLIRGenStatements.cpp index d2a156754..38d6de5c7 100644 --- a/tslang/lib/TypeScript/MLIRGenStatements.cpp +++ b/tslang/lib/TypeScript/MLIRGenStatements.cpp @@ -788,8 +788,6 @@ namespace mlirgen auto location = loc(forStatementAST); - auto hasAwait = InternalFlags::ForAwait == (forStatementAST->internalFlags & InternalFlags::ForAwait); - // initializer // TODO: why do we have ForInitialier if (isExpression(forStatementAST->initializer)) @@ -851,63 +849,12 @@ namespace mlirgen // body builder.setInsertionPointToStart(&forOp.getBody().front()); - if (hasAwait) - { - if (forStatementAST->statement == SyntaxKind::Block) - { - auto firstStatement = forStatementAST->statement.as()->statements.front(); - auto result = mlirGen(firstStatement, loopGenContext); - EXIT_IF_FAILED(result) - } - - // TODO: we need to strip metadata to fix issue with debug info - // async body - auto isFailed = false; - auto asyncExecOp = builder.create( - stripMetadata(location), mlir::TypeRange{}, mlir::ValueRange{}, mlir::ValueRange{}, - [&](mlir::OpBuilder &builder, mlir::Location location, mlir::ValueRange values) { - GenContext execOpBodyGenContext(loopGenContext); - DITableScopeT debugAsyncCodeScope(debugScope); - MLIRDebugInfoHelper mdi(builder, debugScope); - - // TODO: temp hack to break wrong chain on scopes because 'await' create extra function wrap - mdi.clearDebugScope(); - mdi.setLexicalBlock(location); - - if (forStatementAST->statement == SyntaxKind::Block) - { - if (mlir::failed(mlirGen(forStatementAST->statement.as(), execOpBodyGenContext, 1))) - { - isFailed = true; - } - } - else - { - if (mlir::failed(mlirGen(forStatementAST->statement, execOpBodyGenContext))) - { - isFailed = true; - } - } - - builder.create(location, mlir::ValueRange{}); - }); - - if (isFailed) - { - return mlir::failure(); - } - - // One iteration at a time, as `for await` runs its body: the next value is asked for only - // once this body is done. Each body was added to a group awaited after the loop, so the - // bodies ran as concurrent tasks - interleaved, losing each other's writes (#498). - builder.create(location, asyncExecOp.getToken()); - } - else - { - // default - auto result = mlirGen(forStatementAST->statement, loopGenContext); - EXIT_IF_FAILED(result) - } + // A `for await` body is generated in the loop as any other: an `await` in it makes its own + // async.execute and waits for it, so one iteration runs at a time (#498). Generated inside an + // async.execute of its own, a `break` or `continue` in it had no loop in its region, and a + // nested `for await` crashed the compiler (#502). + auto bodyResult = mlirGen(forStatementAST->statement, loopGenContext); + EXIT_IF_FAILED(bodyResult) builder.create(location); diff --git a/tslang/test/tester/CMakeLists.txt b/tslang/test/tester/CMakeLists.txt index 1af2da335..10b0fa007 100644 --- a/tslang/test/tester/CMakeLists.txt +++ b/tslang/test/tester/CMakeLists.txt @@ -354,6 +354,7 @@ tslang_add_test(NAME test-compile-00-record-variable-to-class COMMAND test-runne tslang_add_test(NAME test-compile-00-array-insert-functions COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00array_insert_functions.ts") tslang_add_test(NAME test-compile-00-await-void COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00await_void.ts") tslang_add_test(NAME test-compile-00-for-await-sequential COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00for_await_sequential.ts") +tslang_add_test(NAME test-compile-00-for-await-control COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00for_await_control.ts") tslang_add_test(NAME test-compile-00-const-record-owned-fields COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00const_record_owned_fields.ts") tslang_add_test(NAME test-compile-00-array-length-valid COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00array_length_valid.ts") tslang_add_test(NAME test-compile-00-string-view-copy COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00string_view_copy.ts") @@ -865,6 +866,7 @@ tslang_add_test(NAME test-jit-00-record-variable-to-class COMMAND test-runner -j tslang_add_test(NAME test-jit-00-array-insert-functions COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00array_insert_functions.ts") tslang_add_test(NAME test-jit-00-await-void COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00await_void.ts") tslang_add_test(NAME test-jit-00-for-await-sequential COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00for_await_sequential.ts") +tslang_add_test(NAME test-jit-00-for-await-control COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00for_await_control.ts") tslang_add_test(NAME test-jit-00-const-record-owned-fields COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00const_record_owned_fields.ts") tslang_add_test(NAME test-jit-00-array-length-valid COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00array_length_valid.ts") tslang_add_test(NAME test-jit-00-string-view-copy COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00string_view_copy.ts") @@ -1705,6 +1707,7 @@ set(TSLANG_CORPUS 00array_insert_functions.ts 00await_void.ts 00for_await_sequential.ts + 00for_await_control.ts 00const_record_owned_fields.ts 00array_length_valid.ts 00string_view_copy.ts diff --git a/tslang/test/tester/tests/00for_await_control.ts b/tslang/test/tester/tests/00for_await_control.ts new file mode 100644 index 000000000..f1e0600e2 --- /dev/null +++ b/tslang/test/tester/tests/00for_await_control.ts @@ -0,0 +1,50 @@ +// `break` and `continue` in a `for await` body, and a `for await` nested in another (#502): the +// body was generated in an async.execute region of its own, where a `break` had no loop to leave, +// and the compiler crashed. +let log = ""; +async function* numbers() { + yield 1; + yield 2; + yield 3; + yield 4; +} +async function* letters() { + yield "a"; + yield "b"; +} +async function note(s: string) { + log += s; +} +async function main() { + for await (const x of numbers()) { + if (x == 3) break; + log += x; + } + assert(log == "12", "break"); + + log = ""; + for await (const x of numbers()) { + if (x == 2) continue; + log += x; + } + assert(log == "134", "continue"); + + log = ""; + for await (const x of numbers()) { + for await (const y of letters()) { + log += x + y; + } + } + assert(log == "1a1b2a2b3a3b4a4b", "nested"); + + log = ""; + for await (const x of numbers()) { + await note("<" + x); + if (x == 2) continue; + if (x == 4) break; + await note(">"); + } + assert(log == "<1><2<3><4", "awaits, continue and break"); + + print("done."); +}