|  | // 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 <typename T> struct remove_reference { using type = T; }; | 
|  | template <typename T> struct remove_reference<T &> { using type = T; }; | 
|  | template <typename T> struct remove_reference<T &&> { using type = T; }; | 
|  |  | 
|  | template <typename T> | 
|  | constexpr typename std::remove_reference<T>::type&& move(T &&t) noexcept { | 
|  | return static_cast<typename std::remove_reference<T>::type &&>(t); | 
|  | } | 
|  |  | 
|  | } | 
|  |  | 
|  | template <typename T> | 
|  | class Task final { | 
|  | public: | 
|  | using value_type = T; | 
|  |  | 
|  | class promise_type final { | 
|  | public: | 
|  | Task<void> get_return_object() { return Task<void>(std::coroutine_handle<promise_type>::from_promise(*this)); } | 
|  |  | 
|  | void unhandled_exception() {} | 
|  |  | 
|  | std::suspend_always initial_suspend() { return {}; } | 
|  |  | 
|  | auto await_transform(Task<void> 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<promise_type>::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<promise_type> my_handle() { | 
|  | return std::coroutine_handle<promise_type>::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<promise_type> handle) | 
|  | : handle_(handle) {} | 
|  |  | 
|  | const std::coroutine_handle<promise_type> handle_; | 
|  | }; | 
|  |  | 
|  | Task<void> Qux() { co_return; } | 
|  | Task<void> Baz() { co_await Qux(); } | 
|  | Task<void> Bar() { co_await Baz(); } | 
|  |  | 
|  | // CHECK: _Z3Quxv.destroy:{{.*}} | 
|  | // CHECK-NEXT: # | 
|  | // CHECK-NEXT: movl	$40, %esi | 
|  | // CHECK-NEXT: jmp	_ZdlPvm@PLT | 
|  |  | 
|  | // CHECK: _Z3Bazv.destroy:{{.*}} | 
|  | // CHECK-NEXT: # | 
|  | // CHECK-NEXT: movl	$80, %esi | 
|  | // CHECK-NEXT: jmp	_ZdlPvm | 
|  |  | 
|  | // CHECK: _Z3Barv.destroy:{{.*}} | 
|  | // CHECK-NEXT: # | 
|  | // CHECK-NEXT: movl	$120, %esi | 
|  | // CHECK-NEXT: jmp	_ZdlPvm |