blob: e72cd850bf4eb8996d31d0f279d9bb2de48fd6b6 [file]
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fcxx-exceptions -fexceptions -fclangir -emit-cir %s -o %t.cir
// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
// FIXME: we currently don't have flatten-cfg for coroutines or lower to LLVM-IR
// implemented correctly, so this tests only the CIR output.
namespace std {
template <class Ret, typename... T>
struct coroutine_traits { using promise_type = typename Ret::promise_type; };
template <class Promise = void> struct coroutine_handle {
static coroutine_handle from_address(void *) noexcept;
};
template <> struct coroutine_handle<void> {
template <class P> coroutine_handle(coroutine_handle<P>) noexcept;
static coroutine_handle from_address(void *);
};
// Awaitable whose await_resume CAN throw.
struct suspend_maybe_throw {
bool await_ready() noexcept;
void await_suspend(coroutine_handle<>) noexcept;
void await_resume(); // not noexcept
};
// Awaitable whose await_resume is noexcept.
struct suspend_nothrow {
bool await_ready() noexcept;
void await_suspend(coroutine_handle<>) noexcept;
void await_resume() noexcept;
};
} // namespace std
struct TaskWithEH {
struct promise_type {
TaskWithEH get_return_object();
std::suspend_nothrow initial_suspend() noexcept;
std::suspend_nothrow final_suspend() noexcept;
void return_void();
void unhandled_exception();
};
};
TaskWithEH simple_eh_body() {
co_return;
}
// CIR-LABEL: cir.func{{.*}} @_Z14simple_eh_bodyv
// CIR: }, resume : {
// Note no 'try'/'catch' here.
// CIR-NEXT: cir.call @_ZNSt15suspend_nothrow12await_resumeEv
// CIR-NEXT: cir.yield
// CIR-NEXT: }
// CIR: cir.coro.body {
// CIR-NOT: cir.if
// CIR: cir.scope {
// CIR: cir.try {
// CIR: cir.call @_ZN10TaskWithEH12promise_type11return_voidEv
// CIR: cir.co_return
// CIR: } catch all {{.*}} {
// CIR: cir.cleanup.scope {
// CIR: cir.call @_ZN10TaskWithEH12promise_type19unhandled_exceptionEv
// CIR: cir.yield
// CIR: } cleanup all {
// CIR: cir.end_catch
// CIR: cir.yield
// CIR: }
// CIR: cir.yield
// CIR: }
// CIR: }
// CIR: cir.yield
// CIR: }
// Make sure that 'final' is outside of the above try/catch/etc.
// CIR: cir.call @_ZN10TaskWithEH12promise_type13final_suspendEv
struct TaskThrowingInit {
struct promise_type {
TaskThrowingInit get_return_object();
std::suspend_maybe_throw initial_suspend() noexcept; // resume CAN throw
std::suspend_nothrow final_suspend() noexcept;
void return_void();
void unhandled_exception();
};
};
TaskThrowingInit throwing_init_suspend() {
co_return;
}
// CIR-LABEL: cir.func{{.*}} @_Z21throwing_init_suspendv
// CIR: %[[RESUME_FLAG:.*]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["resume.eh"] {alignment = 1 : i64}
// CIR: }, resume : {
// CIR: %[[FALSE:.*]] = cir.const #false
// CIR: cir.store %[[FALSE]], %[[RESUME_FLAG]] : !cir.bool, !cir.ptr<!cir.bool>
// CIR: cir.scope {
// CIR: cir.try {
// CIR: cir.call @_ZNSt19suspend_maybe_throw12await_resumeEv
// CIR: %[[TRUE:.*]] = cir.const #true
// CIR: cir.store %[[TRUE]], %[[RESUME_FLAG]] : !cir.bool, !cir.ptr<!cir.bool>
// CIR: cir.yield
// CIR: } catch all {{.*}} {
// CIR: cir.begin_catch
// CIR: cir.cleanup.scope {
// CIR: cir.call @_ZN16TaskThrowingInit12promise_type19unhandled_exceptionEv
// CIR: cir.yield
// CIR: } cleanup all {
// CIR: cir.end_catch
// CIR: cir.yield
// CIR: }
// CIR: cir.yield
// CIR: }
// CIR: }
// CIR: cir.yield
// CIR: }
// CIR: cir.coro.body {
// CIR: %[[LOAD_RESUME_FLAG:.*]] = cir.load align(1) %[[RESUME_FLAG]] : !cir.ptr<!cir.bool>, !cir.bool
// CIR: cir.if %[[LOAD_RESUME_FLAG]] {
// CIR: cir.scope {
// CIR: cir.try {
// CIR: cir.call @_ZN16TaskThrowingInit12promise_type11return_voidEv
// CIR: cir.co_return
// CIR: } catch all {{.*}} {
// CIR: cir.cleanup.scope {
// CIR: cir.call @_ZN16TaskThrowingInit12promise_type19unhandled_exceptionEv
// CIR: cir.yield
// CIR: } cleanup all {
// CIR: cir.end_catch
// CIR: cir.yield
// CIR: }
// CIR: cir.yield
// CIR: }
// CIR: }
// CIR: }
// CIR: cir.yield
// CIR: }
// CIR: cir.call @_ZN16TaskThrowingInit12promise_type13final_suspendEv