xref: /llvm-project/clang/test/CodeGenCoroutines/pr56329.cpp (revision c5de4dd1eab00df76c1a68c5f397304ceacb71f2)
1 // Test for PR56919. Tests the we won't contain the resumption of final suspend point.
2 //
3 // RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 %s -O3 -emit-llvm -o - | FileCheck %s
4 // This test is expected to fail on PowerPC.
5 // XFAIL: target=powerpc{{.*}}
6 
7 #include "Inputs/coroutine.h"
8 
9 void _exit(int status) __attribute__ ((__noreturn__));
10 
11 class Promise;
12 
13 // An object that can be co_awaited, but we always resume immediately from
14 // await_suspend.
15 struct ResumeFromAwaitSuspend{};
16 
17 struct Task {
18   using promise_type = Promise;
19   Promise& promise;
20 };
21 
22 struct Promise {
GetHandlePromise23   static std::coroutine_handle<Promise> GetHandle(Promise& promise) {
24     return std::coroutine_handle<Promise>::from_promise(promise);
25   }
26 
unhandled_exceptionPromise27   void unhandled_exception() {}
get_return_objectPromise28   Task get_return_object() { return Task{*this}; }
return_voidPromise29   void return_void() {}
30 
31   // Always suspend before starting the coroutine body. We actually run the body
32   // when we are co_awaited.
initial_suspendPromise33   std::suspend_always initial_suspend() { return {}; }
34 
35   // We support awaiting tasks. We do so by configuring them to resume us when
36   // they are finished, and then resuming them from their initial suspend.
await_transformPromise37   auto await_transform(Task&& task) {
38     struct Awaiter {
39       bool await_ready() { return false; }
40 
41       std::coroutine_handle<> await_suspend(
42           const std::coroutine_handle<> handle) {
43         // Tell the child to resume the parent once it finishes.
44         child.resume_at_final_suspend = GetHandle(parent);
45 
46         // Run the child.
47         return GetHandle(child);
48       }
49 
50       void await_resume() {
51         // The child is now at its final suspend point, and can be destroyed.
52         return GetHandle(child).destroy();
53       }
54 
55       Promise& parent;
56       Promise& child;
57     };
58 
59     return Awaiter{
60         .parent = *this,
61         .child = task.promise,
62     };
63   }
64 
65   // Make evaluation of `co_await ResumeFromAwaitSuspend{}` go through the
66   // await_suspend path, but cause it to resume immediately by returning our own
67   // handle to resume.
await_transformPromise68   auto await_transform(ResumeFromAwaitSuspend) {
69     struct Awaiter {
70       bool await_ready() { return false; }
71 
72       std::coroutine_handle<> await_suspend(const std::coroutine_handle<> h) {
73         return h;
74       }
75 
76       void await_resume() {}
77     };
78 
79     return Awaiter{};
80   }
81 
82   // Always suspend at the final suspend point, transferring control back to our
83   // caller. We expect never to be resumed from the final suspend.
final_suspendPromise84   auto final_suspend() noexcept {
85     struct FinalSuspendAwaitable final {
86       bool await_ready() noexcept { return false; }
87 
88       std::coroutine_handle<> await_suspend(std::coroutine_handle<>) noexcept {
89         return promise.resume_at_final_suspend;
90       }
91 
92       void await_resume() noexcept {
93         _exit(1);
94       }
95 
96       Promise& promise;
97     };
98 
99     return FinalSuspendAwaitable{.promise = *this};
100   }
101 
102   // The handle we will resume once we hit final suspend.
103   std::coroutine_handle<> resume_at_final_suspend;
104 };
105 
106 Task Inner();
107 
Outer()108 Task Outer() {
109   co_await ResumeFromAwaitSuspend();
110   co_await Inner();
111 }
112 
113 // CHECK: define{{.*}}@_Z5Outerv.resume(
114 // CHECK-NOT: }
115 // CHECK-NOT: _exit
116 // CHECK: musttail call
117 // CHECK: musttail call
118 // CHECK: musttail call
119 // CHECK-NEXT: ret void
120 // CHECK-EMPTY:
121 // CHECK-NEXT: unreachable:
122 // CHECK-NEXT: unreachable
123 // CHECK-NEXT: }
124