// Test for PR56919. Tests the destroy function contains the call to delete function only. // // REQUIRES: x86-registered-target // // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 %s -O3 -S -o - | FileCheck %s #include "Inputs/coroutine.h" namespace std { template struct remove_reference { using type = T; }; template struct remove_reference { using type = T; }; template struct remove_reference { using type = T; }; template constexpr typename std::remove_reference::type&& move(T &&t) noexcept { return static_cast::type &&>(t); } } template class Task final { public: using value_type = T; class promise_type final { public: Task get_return_object() { return Task(std::coroutine_handle::from_promise(*this)); } void unhandled_exception() {} std::suspend_always initial_suspend() { return {}; } auto await_transform(Task co) { return await_transform(std::move(co.handle_.promise())); } auto await_transform(promise_type&& awaited) { struct Awaitable { promise_type&& awaited; bool await_ready() { return false; } std::coroutine_handle<> await_suspend( const std::coroutine_handle<> handle) { // Register our handle to be resumed once the awaited promise's coroutine // finishes, and then resume that coroutine. awaited.registered_handle_ = handle; return std::coroutine_handle::from_promise(awaited); } void await_resume() {} private: }; return Awaitable{std::move(awaited)}; } void return_void() {} // At final suspend resume our registered handle. auto final_suspend() noexcept { struct FinalSuspendAwaitable final { bool await_ready() noexcept { return false; } std::coroutine_handle<> await_suspend( std::coroutine_handle<> h) noexcept { return to_resume; } void await_resume() noexcept {} std::coroutine_handle<> to_resume; }; return FinalSuspendAwaitable{registered_handle_}; } private: std::coroutine_handle my_handle() { return std::coroutine_handle::from_promise(*this); } std::coroutine_handle<> registered_handle_; }; ~Task() { // Teach llvm that we are only ever destroyed when the coroutine body is done, // so there is no need for the jump table in the destroy function. Our coroutine // library doesn't expose handles to the user, so we know this constraint isn't // violated. if (!handle_.done()) { __builtin_unreachable(); } handle_.destroy(); } private: explicit Task(const std::coroutine_handle handle) : handle_(handle) {} const std::coroutine_handle handle_; }; Task Qux() { co_return; } Task Baz() { co_await Qux(); } Task Bar() { co_await Baz(); } // CHECK: _Z3Quxv.destroy:{{.*}} // CHECK-NEXT: # // CHECK-NEXT: jmp _ZdlPv // CHECK: _Z3Bazv.destroy:{{.*}} // CHECK-NEXT: # // CHECK-NEXT: jmp _ZdlPv // CHECK: _Z3Barv.destroy:{{.*}} // CHECK-NEXT: # // CHECK-NEXT: jmp _ZdlPv