Author: Morris Hafner Date: 2026-10-07T12:33:09Z New Revision: 5215171a44273c2b24e6d6266f39ee8f5b04c30e
URL: https://github.com/llvm/llvm-project/commit/5215171a44273c2b24e6d6266f39ee8f5b04c30e DIFF: https://github.com/llvm/llvm-project/commit/5215171a44273c2b24e6d6266f39ee8f5b04c30e.diff LOG: [CIR] Destroy non-trivially destructible co_await results after the cir.await (#229585) Follow-up to #225412. When await_resume() returns a class with a non-trivial destructor and the result is discarded the destructor was pushed inside the cir.await resume region. The associated cir.cleanup.scope captured the region's terminator which led to a "block with no terminator" verification error. Push the destructor after the await instead such that the result is destroyed at the end of the full expression. Co-authored-by: Claude Opus 5.5 <[email protected]> Added: Modified: clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp clang/test/CIR/CodeGenCoroutines/coro-agg.cpp Removed: ################################################################################ diff --git a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp index d92c65dc43c5f..c2a85aab2c19d 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp @@ -654,6 +654,31 @@ emitSuspendExpression(CIRGenFunction &cgf, CGCoroData &coro, CIRGenFunction::OpaqueValueMapping binder = CIRGenFunction::OpaqueValueMapping(cgf, s.getOpaqueValue()); CIRGenBuilderTy &builder = cgf.getBuilder(); + + // Exception handling requires additional IR. We avoid generating it when + // the resume expression is a direct call to a 'noexcept' member function. + const bool resumeInTry = coro.exceptionHandler && + kind == cir::AwaitKind::Init && + memberCallExpressionCanThrow(s.getResumeExpr()); + + // If the await_resume() result needs a destructor, take over its + // destruction, unless the destination owns it. The try/catch path destroys + // the result itself. + const CXXBindTemporaryExpr *resultBind = nullptr; + if (!resumeInTry && !aggSlot.isExternallyDestructed()) + resultBind = dyn_cast<CXXBindTemporaryExpr>(s.getResumeExpr()); + if (resultBind) { + // Emit the result into a slot created outside of the cir.await, so that it + // is still available after it. + if (aggSlot.isIgnored()) + aggSlot = cgf.createAggTemp(resultBind->getType(), + cgf.getLoc(resultBind->getSourceRange()), + "agg.tmp.ensured"); + // Don't push the destructor from within the resume region, where its + // cir.cleanup.scope would capture the region's terminator. + aggSlot.setExternallyDestructed(); + } + [[maybe_unused]] cir::AwaitOp awaitOp = cir::AwaitOp::create( builder, cgf.getLoc(s.getSourceRange()), kind, /*readyBuilder=*/ @@ -680,11 +705,7 @@ emitSuspendExpression(CIRGenFunction &cgf, CGCoroData &coro, }, /*resumeBuilder=*/ [&](mlir::OpBuilder &b, mlir::Location loc) { - // Exception handling requires additional IR. If the 'await_resume' - // function is marked as 'noexcept', we avoid generating this additional - // IR. - if (coro.exceptionHandler && kind == cir::AwaitKind::Init && - memberCallExpressionCanThrow(s.getResumeExpr())) { + if (resumeInTry) { // we are basically just emitting: // resumeEh = false; // try { @@ -764,6 +785,14 @@ emitSuspendExpression(CIRGenFunction &cgf, CGCoroData &coro, cir::YieldOp::create(builder, loc); }); + // Push the destructor right after the cir.await, where the result starts to + // exist, and not around it: destroying the coroutine at the suspend point + // would destroy the not yet constructed result. The cleanup runs at the end + // of the full-expression. + if (resultBind) + cgf.emitCXXTemporary(resultBind->getTemporary(), resultBind->getType(), + aggSlot.getAddress()); + assert(awaitBuild.succeeded() && "Should know how to codegen"); return awaitRes; } diff --git a/clang/test/CIR/CodeGenCoroutines/coro-agg.cpp b/clang/test/CIR/CodeGenCoroutines/coro-agg.cpp index 816b647c98ff2..a966537abf918 100644 --- a/clang/test/CIR/CodeGenCoroutines/coro-agg.cpp +++ b/clang/test/CIR/CodeGenCoroutines/coro-agg.cpp @@ -1,5 +1,7 @@ // RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -Wno-coroutine-missing-unhandled-exception -emit-cir %s -o %t.cir // RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR +// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -fcxx-exceptions -fexceptions -Wno-coroutine-missing-unhandled-exception -emit-cir %s -o %t.eh.cir +// RUN: FileCheck --input-file=%t.eh.cir %s -check-prefix=CIR-EH // RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -emit-llvm -disable-llvm-passes -Wno-coroutine-missing-unhandled-exception %s -o %t.ll // RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG @@ -99,3 +101,361 @@ coro_t aggregate_coyield_expr_unused() { // OGCG: store i64 %[[RES]], ptr %{{.*}}, align 4 co_yield 42; } + +struct NT { + int x; + ~NT(); +}; + +struct AwaitNT { + bool await_ready() { return true; } + NT await_resume(); + template <typename F> void await_suspend(F) {} +}; + +void use(int); +void consume(NT); +void consume2(NT, NT); + +struct coro_nt_t { + struct promise_type { + coro_nt_t get_return_object() { return {}; } + std::suspend_never initial_suspend() { return {}; } + std::suspend_never final_suspend() noexcept { return {}; } + void return_value(NT) {} + static void unhandled_exception() {} + AwaitNT yield_value(int) { return {}; } + }; +}; + +// The cleanup for a result with a non-trivial destructor starts right after +// the cir.await and ends with the full-expression. + +// CIR-LABEL: cir.func coroutine {{.*}} @_Z18discard_nontrivialv +// CIR: %[[TMP:.*]] = cir.alloca "agg.tmp.ensured" align(4) : !cir.ptr<!rec_NT> +// CIR: cir.await(user, ready : { +// CIR: }, resume : { +// CIR-NEXT: cir.call @_ZN7AwaitNT12await_resumeEv(%[[TMP]], %{{.*}}) +// CIR-NEXT: cir.yield +// CIR-NEXT: },) +// CIR-NEXT: cir.cleanup.scope { +// CIR-NEXT: cir.yield +// CIR-NEXT: } cleanup normal { +// CIR-NEXT: cir.call @_ZN2NTD1Ev(%[[TMP]]) +// CIR-NEXT: cir.yield +// CIR-NEXT: } + +// With exceptions the cleanup is also an EH cleanup, and await_resume() itself +// stays outside of it. +// CIR-EH-LABEL: cir.func coroutine {{.*}} @_Z18discard_nontrivialv +// CIR-EH: cir.await(user, ready : { +// CIR-EH: }, resume : { +// CIR-EH-NEXT: cir.call @_ZN7AwaitNT12await_resumeEv(%[[TMP:.*]], %{{.*}}) +// CIR-EH-NEXT: cir.yield +// CIR-EH-NEXT: },) +// CIR-EH-NEXT: cir.cleanup.scope { +// CIR-EH-NEXT: cir.yield +// CIR-EH-NEXT: } cleanup all { +// CIR-EH-NEXT: cir.call @_ZN2NTD1Ev(%[[TMP]]) + +// OGCG-LABEL: define dso_local void @_Z18discard_nontrivialv +// OGCG: await.cleanup: +// OGCG-NEXT: br label %[[CLEANUP:.*]] +// OGCG: await.ready: +// OGCG-NEXT: call void @_ZN7AwaitNT12await_resumeEv(ptr {{.*}} sret(%struct.NT) {{.*}} %[[TMP:.*]], ptr +// OGCG-NEXT: call void @_ZN2NTD1Ev(ptr {{.*}} %[[TMP]]) +// OGCG-NEXT: br label %[[CLEANUP]] +coro_t discard_nontrivial() { co_await AwaitNT{}; } + +// A discarded result is destroyed at the end of the full-expression. +// CIR-LABEL: cir.func coroutine {{.*}} @_Z16comma_nontrivialv +// CIR: cir.await(user, ready : { +// CIR: }, resume : { +// CIR-NEXT: cir.call @_ZN7AwaitNT12await_resumeEv(%[[TMP:.*]], %{{.*}}) +// CIR-NEXT: cir.yield +// CIR-NEXT: },) +// CIR-NEXT: cir.cleanup.scope { +// CIR-NEXT: %[[ONE:.*]] = cir.const #cir.int<1> : !s32i +// CIR-NEXT: cir.call @_Z3usei(%[[ONE]]) +// CIR-NEXT: cir.yield +// CIR-NEXT: } cleanup normal { +// CIR-NEXT: cir.call @_ZN2NTD1Ev(%[[TMP]]) + +// OGCG-LABEL: define dso_local void @_Z16comma_nontrivialv +// OGCG: await.ready: +// OGCG-NEXT: call void @_ZN7AwaitNT12await_resumeEv(ptr {{.*}} %[[TMP:.*]], ptr +// OGCG-NEXT: call void @_Z3usei(i32 noundef 1) +// OGCG-NEXT: call void @_ZN2NTD1Ev(ptr {{.*}} %[[TMP]]) +coro_t comma_nontrivial() { (co_await AwaitNT{}, use(1)); } + +// CIR-LABEL: cir.func coroutine {{.*}} @_Z16byval_nontrivialv +// CIR: cir.await(user, ready : { +// CIR: }, resume : { +// CIR-NEXT: cir.call @_ZN7AwaitNT12await_resumeEv(%[[ARG:.*]], %{{.*}}) +// CIR-NEXT: cir.yield +// CIR-NEXT: },) +// CIR-NEXT: cir.cleanup.scope { +// CIR-NEXT: cir.call @_Z7consume2NT(%[[ARG]]) +// CIR-NEXT: cir.yield +// CIR-NEXT: } cleanup normal { +// CIR-NEXT: cir.call @_ZN2NTD1Ev(%[[ARG]]) + +// OGCG-LABEL: define dso_local void @_Z16byval_nontrivialv +// OGCG: await.ready: +// OGCG-NEXT: call void @_ZN7AwaitNT12await_resumeEv(ptr {{.*}} %[[ARG:.*]], ptr +// OGCG-NEXT: call void @_Z7consume2NT(ptr {{.*}} %[[ARG]]) +// OGCG-NEXT: call void @_ZN2NTD1Ev(ptr {{.*}} %[[ARG]]) +coro_t byval_nontrivial() { consume(co_await AwaitNT{}); } + +// The first argument is alive while the second co_await is suspended, so its +// cleanup encloses the second cir.await; the second one's does not. They are +// destroyed in reverse order. +// CIR-LABEL: cir.func coroutine {{.*}} @_Z19two_args_nontrivialv +// CIR: cir.await(user, ready : { +// CIR: }, resume : { +// CIR-NEXT: cir.call @_ZN7AwaitNT12await_resumeEv(%[[A0:.*]], %{{.*}}) +// CIR-NEXT: cir.yield +// CIR-NEXT: },) +// CIR-NEXT: cir.cleanup.scope { +// CIR: cir.await(user, ready : { +// CIR: }, resume : { +// CIR-NEXT: cir.call @_ZN7AwaitNT12await_resumeEv(%[[A1:.*]], %{{.*}}) +// CIR-NEXT: cir.yield +// CIR-NEXT: },) +// CIR-NEXT: cir.cleanup.scope { +// CIR-NEXT: cir.call @_Z8consume22NTS_(%[[A0]], %[[A1]]) +// CIR-NEXT: cir.yield +// CIR-NEXT: } cleanup normal { +// CIR-NEXT: cir.call @_ZN2NTD1Ev(%[[A1]]) +// CIR-NEXT: cir.yield +// CIR-NEXT: } +// CIR-NEXT: cir.yield +// CIR-NEXT: } cleanup normal { +// CIR-NEXT: cir.call @_ZN2NTD1Ev(%[[A0]]) + +// CIR-EH-LABEL: cir.func coroutine {{.*}} @_Z19two_args_nontrivialv +// CIR-EH: cir.call @_ZN7AwaitNT12await_resumeEv(%[[A0:[0-9]+]], %{{.*}}) +// CIR-EH-NEXT: cir.yield +// CIR-EH-NEXT: },) +// CIR-EH-NEXT: cir.cleanup.scope { +// CIR-EH-NEXT: cir.await(user, ready : { +// CIR-EH: cir.call @_ZN7AwaitNT12await_resumeEv(%[[A1:[0-9]+]], %{{.*}}) +// CIR-EH-NEXT: cir.yield +// CIR-EH-NEXT: },) +// CIR-EH-NEXT: cir.cleanup.scope { +// CIR-EH-NEXT: cir.call @_Z8consume22NTS_(%[[A0]], %[[A1]]) +// CIR-EH-NEXT: cir.yield +// CIR-EH-NEXT: } cleanup all { +// CIR-EH-NEXT: cir.call @_ZN2NTD1Ev(%[[A1]]) +// CIR-EH-NEXT: cir.yield +// CIR-EH-NEXT: } +// CIR-EH-NEXT: cir.yield +// CIR-EH-NEXT: } cleanup all { +// CIR-EH-NEXT: cir.call @_ZN2NTD1Ev(%[[A0]]) + +// OGCG-LABEL: define dso_local void @_Z19two_args_nontrivialv +// OGCG: await.cleanup: +// OGCG-NEXT: br label %[[OUTER:.*]] +// OGCG: await2.cleanup: +// OGCG-NEXT: br label %[[INNER:.*]] +// OGCG: await2.ready: +// OGCG: call void @_Z8consume22NTS_(ptr {{.*}} %[[A0:.*]], ptr {{.*}} %[[A1:.*]]) +// OGCG-NEXT: call void @_ZN2NTD1Ev(ptr {{.*}} %[[A1]]) +// OGCG-NEXT: br label %[[INNER]] +// OGCG: [[INNER]]: +// OGCG-NOT: br +// OGCG: call void @_ZN2NTD1Ev(ptr {{.*}} %[[A0]]) +// OGCG-NEXT: br label %[[OUTER]] +coro_t two_args_nontrivial() { consume2(co_await AwaitNT{}, co_await AwaitNT{}); } + +// co_yield, and a promise call taking the result by value. +// CIR-LABEL: cir.func coroutine {{.*}} @_Z27coreturn_coyield_nontrivialv +// CIR: cir.await(yield, ready : { +// CIR: }, resume : { +// CIR-NEXT: cir.call @_ZN7AwaitNT12await_resumeEv(%[[ARG:.*]], %{{.*}}) +// CIR-NEXT: cir.yield +// CIR-NEXT: },) +// CIR-NEXT: cir.cleanup.scope { +// CIR-NEXT: cir.call @_ZN9coro_nt_t12promise_type12return_valueE2NT(%{{.*}}, %[[ARG]]) +// CIR-NEXT: cir.yield +// CIR-NEXT: } cleanup normal { +// CIR-NEXT: cir.call @_ZN2NTD1Ev(%[[ARG]]) + +// OGCG-LABEL: define dso_local void @_Z27coreturn_coyield_nontrivialv +// OGCG: yield.ready: +// OGCG-NEXT: call void @_ZN7AwaitNT12await_resumeEv(ptr {{.*}} %[[ARG:.*]], ptr +// OGCG-NEXT: call void @_ZN9coro_nt_t12promise_type12return_valueE2NT(ptr {{.*}}, ptr {{.*}} %[[ARG]]) +// OGCG-NEXT: call void @_ZN2NTD1Ev(ptr {{.*}} %[[ARG]]) +coro_nt_t coreturn_coyield_nontrivial() { co_return co_yield 42; } + +// Inside a conditional the destructor is guarded by a flag, which is set once +// the cir.await has completed. +// CIR-LABEL: cir.func coroutine {{.*}} @_Z23cond_discard_nontrivialb +// CIR: %[[FLAG:.*]] = cir.alloca "cleanup.cond" {{.*}} : !cir.ptr<!cir.bool> +// CIR: cir.cleanup.scope { +// CIR-NEXT: %[[FALSE:.*]] = cir.const #false +// CIR-NEXT: cir.store %[[FALSE]], %[[FLAG]] : !cir.bool +// CIR: cir.ternary(%{{.*}}, true { +// CIR: cir.await(user, ready : { +// CIR: }, resume : { +// CIR-NEXT: cir.call @_ZN7AwaitNT12await_resumeEv(%[[TMP:.*]], %{{.*}}) +// CIR-NEXT: cir.yield +// CIR-NEXT: },) +// CIR-NEXT: %[[TRUE:.*]] = cir.const #true +// CIR-NEXT: cir.store %[[TRUE]], %[[FLAG]] : !cir.bool +// CIR: } cleanup normal { +// CIR-NEXT: %[[ACTIVE:.*]] = cir.load {{.*}} %[[FLAG]] : +// CIR-NEXT: cir.if %[[ACTIVE]] { +// CIR-NEXT: cir.call @_ZN2NTD1Ev(%[[TMP]]) + +// OGCG-LABEL: define dso_local void @_Z23cond_discard_nontrivialb +// OGCG: store i1 false, ptr %[[FLAG:cleanup.cond]] +// OGCG: await.cleanup: +// OGCG-NEXT: br label %[[CLEANUP:.*]] +// OGCG: await.ready: +// OGCG-NEXT: call void @_ZN7AwaitNT12await_resumeEv(ptr {{.*}} %[[TMP:.*]], ptr +// OGCG-NEXT: store i1 true, ptr %[[FLAG]] +// OGCG: cleanup.action: +// OGCG-NEXT: call void @_ZN2NTD1Ev(ptr {{.*}} %[[TMP]]) +coro_t cond_discard_nontrivial(bool c) { c ? (void)co_await AwaitNT{} : (void)0; } + +// A destination that is destroyed by its owner gets no extra temporary or +// cleanup: the variable's own cleanup is the only destructor call. +// CIR-LABEL: cir.func coroutine {{.*}} @_Z19var_init_nontrivialv +// CIR-NOT: agg.tmp.ensured +// CIR: %[[V:.*]] = cir.alloca "v" align(4) init : !cir.ptr<!rec_NT> +// CIR-NOT: agg.tmp.ensured +// CIR: cir.await(user, ready : { +// CIR: }, resume : { +// CIR-NEXT: cir.call @_ZN7AwaitNT12await_resumeEv(%[[V]], %{{.*}}) +// CIR-NEXT: cir.yield +// CIR-NEXT: },) +// CIR-NEXT: cir.cleanup.scope { +// CIR-NOT: @_ZN2NTD1Ev +// CIR: } cleanup normal { +// CIR-NEXT: cir.call @_ZN2NTD1Ev(%[[V]]) +// CIR-NOT: @_ZN2NTD1Ev +// CIR: }, finalSuspend : { + +// OGCG-LABEL: define dso_local void @_Z19var_init_nontrivialv +// OGCG-NOT: agg.tmp.ensured +// OGCG: await.ready: +// OGCG-NEXT: call void @_ZN7AwaitNT12await_resumeEv(ptr {{.*}} %[[V:v]], ptr +// OGCG-NOT: @_ZN2NTD1Ev +// OGCG: call void @_Z3usei( +// OGCG-NEXT: call void @_ZN2NTD1Ev(ptr {{.*}} %[[V]]) +// OGCG-NOT: @_ZN2NTD1Ev +// OGCG: coro.final: +coro_t var_init_nontrivial() { NT v = co_await AwaitNT{}; use(v.x); } + +bool check(NT); + +// In a loop condition the full-expression's cleanups are done before the +// cir.condition. +// CIR-LABEL: cir.func coroutine {{.*}} @_Z20loop_cond_nontrivialv +// CIR: cir.while { +// CIR: cir.await(user, ready : { +// CIR: }, resume : { +// CIR-NEXT: cir.call @_ZN7AwaitNT12await_resumeEv(%[[ARG:.*]], %{{.*}}) +// CIR-NEXT: cir.yield +// CIR-NEXT: },) +// CIR-NEXT: cir.cleanup.scope { +// CIR-NEXT: %[[CHECK:.*]] = cir.call @_Z5check2NT(%[[ARG]]) +// CIR-NEXT: cir.store {{.*}} %[[CHECK]], %[[SPILL:.*]] : !cir.bool, !cir.ptr<!cir.bool> +// CIR-NEXT: cir.yield +// CIR-NEXT: } cleanup normal { +// CIR-NEXT: cir.call @_ZN2NTD1Ev(%[[ARG]]) +// CIR-NEXT: cir.yield +// CIR-NEXT: } +// CIR-NEXT: %[[COND:.*]] = cir.load {{.*}} %[[SPILL]] : !cir.ptr<!cir.bool>, !cir.bool +// CIR-NEXT: cir.condition(%[[COND]]) + +// OGCG-LABEL: define dso_local void @_Z20loop_cond_nontrivialv +// OGCG: await.ready: +// OGCG-NEXT: call void @_ZN7AwaitNT12await_resumeEv(ptr {{.*}} %[[ARG:.*]], ptr +// OGCG-NEXT: %[[CHECK:.*]] = call {{.*}} i1 @_Z5check2NT(ptr {{.*}} %[[ARG]]) +// OGCG-NEXT: store i1 %[[CHECK]], ptr %[[SPILL:.*]], align 1 +// OGCG-NEXT: call void @_ZN2NTD1Ev(ptr {{.*}} %[[ARG]]) +coro_t loop_cond_nontrivial() { + while (check(co_await AwaitNT{})) { + } +} + +struct InitAwaitNT { + bool await_ready() { return true; } + void await_suspend(std::coroutine_handle<>) {} + NT await_resume(); +}; + +struct FinalAwaitNT { + bool await_ready() noexcept { return true; } + void await_suspend(std::coroutine_handle<>) noexcept {} + NT await_resume() noexcept; +}; + +struct coro_init_final_nt_t { + struct promise_type { + coro_init_final_nt_t get_return_object() { return {}; } + InitAwaitNT initial_suspend() { return {}; } + FinalAwaitNT final_suspend() noexcept { return {}; } + void return_void() {} + static void unhandled_exception() {} + }; +}; + +// CIR-LABEL: cir.func coroutine {{.*}} @_Z21init_final_nontrivialv +// CIR: cir.coroutine initialSuspend : { +// CIR: cir.await(init, ready : { +// CIR: }, resume : { +// CIR-NEXT: cir.call @_ZN11InitAwaitNT12await_resumeEv(%[[INIT:.*]], %{{.*}}) +// CIR-NEXT: cir.yield +// CIR-NEXT: },) +// CIR-NEXT: cir.cleanup.scope { +// CIR-NEXT: cir.yield +// CIR-NEXT: } cleanup normal { +// CIR-NEXT: cir.call @_ZN2NTD1Ev(%[[INIT]]) +// CIR: }, finalSuspend : { +// CIR: cir.await(final, ready : { +// CIR: }, resume : { +// CIR-NEXT: cir.call @_ZN12FinalAwaitNT12await_resumeEv(%[[FINAL:.*]], %{{.*}}) +// CIR-NEXT: cir.yield +// CIR-NEXT: },) +// CIR-NEXT: cir.cleanup.scope { +// CIR-NEXT: cir.yield +// CIR-NEXT: } cleanup normal { +// CIR-NEXT: cir.call @_ZN2NTD1Ev(%[[FINAL]]) + +// With exceptions, the initial await_resume() is emitted in a try/catch that +// owns and destroys the result; nothing is pushed after the cir.await. +// CIR-EH-LABEL: cir.func coroutine {{.*}} @_Z21init_final_nontrivialv +// CIR-EH: cir.coroutine initialSuspend : { +// CIR-EH: cir.await(init, ready : { +// CIR-EH: }, resume : { +// CIR-EH: cir.try { +// CIR-EH-NEXT: cir.call @_ZN11InitAwaitNT12await_resumeEv(%[[INIT:.*]], %{{.*}}) +// CIR-EH-NEXT: cir.cleanup.scope { +// CIR-EH: } cleanup all { +// CIR-EH-NEXT: cir.call @_ZN2NTD1Ev(%[[INIT]]) +// CIR-EH: } catch all +// CIR-EH: },) +// CIR-EH-NEXT: cir.yield +// CIR-EH-NEXT: }, body : { +// CIR-EH: }, finalSuspend : { +// CIR-EH: cir.await(final, ready : { +// CIR-EH: }, resume : { +// CIR-EH-NEXT: cir.call @_ZN12FinalAwaitNT12await_resumeEv(%[[FINAL:.*]], %{{.*}}) +// CIR-EH-NEXT: cir.yield +// CIR-EH-NEXT: },) +// CIR-EH-NEXT: cir.cleanup.scope { +// CIR-EH-NEXT: cir.yield +// CIR-EH-NEXT: } cleanup all { +// CIR-EH-NEXT: cir.call @_ZN2NTD1Ev(%[[FINAL]]) + +// OGCG-LABEL: define dso_local void @_Z21init_final_nontrivialv +// OGCG: init.ready: +// OGCG-NEXT: call void @_ZN11InitAwaitNT12await_resumeEv(ptr {{.*}} %[[INIT:.*]], ptr +// OGCG-NEXT: call void @_ZN2NTD1Ev(ptr {{.*}} %[[INIT]]) +// OGCG: final.ready: +// OGCG-NEXT: call void @_ZN12FinalAwaitNT12await_resumeEv(ptr {{.*}} %[[FINAL:.*]], ptr +// OGCG-NEXT: call void @_ZN2NTD1Ev(ptr {{.*}} %[[FINAL]]) +coro_init_final_nt_t init_final_nontrivial() { co_return; } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
