[clang] Implement lifetime analysis for lifetime_capture_by(X) (#115921)
[llvm-project.git] / clang / test / CodeGenCoroutines / pr56919.cpp
blobbaa8c27ce6649b00595c07bc3be2635d67105439
1 // Test for PR56919. Tests the destroy function contains the call to delete function only.
2 //
3 // REQUIRES: x86-registered-target
4 //
5 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 %s -O3 -S -o - | FileCheck %s
7 #include "Inputs/coroutine.h"
9 namespace std {
11 template <typename T> struct remove_reference { using type = T; };
12 template <typename T> struct remove_reference<T &> { using type = T; };
13 template <typename T> struct remove_reference<T &&> { using type = T; };
15 template <typename T>
16 constexpr typename std::remove_reference<T>::type&& move(T &&t) noexcept {
17 return static_cast<typename std::remove_reference<T>::type &&>(t);
22 template <typename T>
23 class Task final {
24 public:
25 using value_type = T;
27 class promise_type final {
28 public:
29 Task<void> get_return_object() { return Task<void>(std::coroutine_handle<promise_type>::from_promise(*this)); }
31 void unhandled_exception() {}
33 std::suspend_always initial_suspend() { return {}; }
35 auto await_transform(Task<void> co) {
36 return await_transform(std::move(co.handle_.promise()));
39 auto await_transform(promise_type&& awaited) {
40 struct Awaitable {
41 promise_type&& awaited;
43 bool await_ready() { return false; }
45 std::coroutine_handle<> await_suspend(
46 const std::coroutine_handle<> handle) {
47 // Register our handle to be resumed once the awaited promise's coroutine
48 // finishes, and then resume that coroutine.
49 awaited.registered_handle_ = handle;
50 return std::coroutine_handle<promise_type>::from_promise(awaited);
53 void await_resume() {}
55 private:
58 return Awaitable{std::move(awaited)};
61 void return_void() {}
63 // At final suspend resume our registered handle.
64 auto final_suspend() noexcept {
65 struct FinalSuspendAwaitable final {
66 bool await_ready() noexcept { return false; }
68 std::coroutine_handle<> await_suspend(
69 std::coroutine_handle<> h) noexcept {
70 return to_resume;
73 void await_resume() noexcept {}
75 std::coroutine_handle<> to_resume;
78 return FinalSuspendAwaitable{registered_handle_};
81 private:
82 std::coroutine_handle<promise_type> my_handle() {
83 return std::coroutine_handle<promise_type>::from_promise(*this);
86 std::coroutine_handle<> registered_handle_;
89 ~Task() {
90 // Teach llvm that we are only ever destroyed when the coroutine body is done,
91 // so there is no need for the jump table in the destroy function. Our coroutine
92 // library doesn't expose handles to the user, so we know this constraint isn't
93 // violated.
94 if (!handle_.done()) {
95 __builtin_unreachable();
98 handle_.destroy();
101 private:
102 explicit Task(const std::coroutine_handle<promise_type> handle)
103 : handle_(handle) {}
105 const std::coroutine_handle<promise_type> handle_;
108 Task<void> Qux() { co_return; }
109 Task<void> Baz() { co_await Qux(); }
110 Task<void> Bar() { co_await Baz(); }
112 // CHECK: _Z3Quxv.destroy:{{.*}}
113 // CHECK-NEXT: #
114 // CHECK-NEXT: movl $40, %esi
115 // CHECK-NEXT: jmp _ZdlPvm@PLT
117 // CHECK: _Z3Bazv.destroy:{{.*}}
118 // CHECK-NEXT: #
119 // CHECK-NEXT: movl $80, %esi
120 // CHECK-NEXT: jmp _ZdlPvm
122 // CHECK: _Z3Barv.destroy:{{.*}}
123 // CHECK-NEXT: #
124 // CHECK-NEXT: movl $120, %esi
125 // CHECK-NEXT: jmp _ZdlPvm