1 // Verifies that parameters are copied with move constructors 2 // Verifies that parameter copies are destroyed 3 // Vefifies that parameter copies are used in the body of the coroutine 4 // Verifies that parameter copies are used to construct the promise type, if that type has a matching constructor 5 // RUN: %clang_cc1 -no-opaque-pointers -std=c++20 -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s -disable-llvm-passes -fexceptions | FileCheck %s 6 7 namespace std { 8 template <typename... T> struct coroutine_traits; 9 10 template <class Promise = void> struct coroutine_handle { 11 coroutine_handle() = default; 12 static coroutine_handle from_address(void *) noexcept; 13 }; 14 template <> struct coroutine_handle<void> { 15 static coroutine_handle from_address(void *) noexcept; 16 coroutine_handle() = default; 17 template <class PromiseType> 18 coroutine_handle(coroutine_handle<PromiseType>) noexcept; 19 }; 20 } // namespace std 21 22 struct suspend_always { 23 bool await_ready() noexcept; 24 void await_suspend(std::coroutine_handle<>) noexcept; 25 void await_resume() noexcept; 26 }; 27 28 template <typename... Args> struct std::coroutine_traits<void, Args...> { 29 struct promise_type { 30 void get_return_object() noexcept; 31 suspend_always initial_suspend() noexcept; 32 suspend_always final_suspend() noexcept; 33 void return_void() noexcept; 34 promise_type(); 35 ~promise_type() noexcept; 36 void unhandled_exception() noexcept; 37 }; 38 }; 39 40 // TODO: Not supported yet 41 struct CopyOnly { 42 int val; 43 CopyOnly(const CopyOnly&) noexcept; 44 CopyOnly(CopyOnly&&) = delete; 45 ~CopyOnly(); 46 }; 47 48 struct MoveOnly { 49 int val; 50 MoveOnly(const MoveOnly&) = delete; 51 MoveOnly(MoveOnly&&) noexcept; 52 ~MoveOnly(); 53 }; 54 55 struct MoveAndCopy { 56 int val; 57 MoveAndCopy(const MoveAndCopy&)noexcept; 58 MoveAndCopy(MoveAndCopy&&) noexcept; 59 ~MoveAndCopy(); 60 }; 61 62 void consume(int,int,int) noexcept; 63 64 // TODO: Add support for CopyOnly params 65 // CHECK: define{{.*}} void @_Z1fi8MoveOnly11MoveAndCopy(i32 noundef %val, %struct.MoveOnly* noundef %[[MoParam:.+]], %struct.MoveAndCopy* noundef %[[McParam:.+]]) #0 personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8* 66 void f(int val, MoveOnly moParam, MoveAndCopy mcParam) { 67 // CHECK: %[[MoCopy:.+]] = alloca %struct.MoveOnly 68 // CHECK: %[[McCopy:.+]] = alloca %struct.MoveAndCopy 69 // CHECK: store i32 %val, i32* %[[ValAddr:.+]] 70 71 // CHECK: call i8* @llvm.coro.begin( 72 // CHECK: call void @_ZN8MoveOnlyC1EOS_(%struct.MoveOnly* {{[^,]*}} %[[MoCopy]], %struct.MoveOnly* noundef nonnull align 4 dereferenceable(4) %[[MoParam]]) 73 // CHECK-NEXT: bitcast %struct.MoveAndCopy* %[[McCopy]] to i8* 74 // CHECK-NEXT: call void @llvm.lifetime.start.p0i8( 75 // CHECK-NEXT: call void @_ZN11MoveAndCopyC1EOS_(%struct.MoveAndCopy* {{[^,]*}} %[[McCopy]], %struct.MoveAndCopy* noundef nonnull align 4 dereferenceable(4) %[[McParam]]) # 76 // CHECK-NEXT: bitcast %"struct.std::coroutine_traits<void, int, MoveOnly, MoveAndCopy>::promise_type"* %__promise to i8* 77 // CHECK-NEXT: call void @llvm.lifetime.start.p0i8( 78 // CHECK-NEXT: invoke void @_ZNSt16coroutine_traitsIJvi8MoveOnly11MoveAndCopyEE12promise_typeC1Ev( 79 80 // CHECK: call void @_ZN14suspend_always12await_resumeEv( 81 // CHECK: %[[IntParam:.+]] = load i32, i32* %{{.*}} 82 // CHECK: %[[MoGep:.+]] = getelementptr inbounds %struct.MoveOnly, %struct.MoveOnly* %[[MoCopy]], i32 0, i32 0 83 // CHECK: %[[MoVal:.+]] = load i32, i32* %[[MoGep]] 84 // CHECK: %[[McGep:.+]] = getelementptr inbounds %struct.MoveAndCopy, %struct.MoveAndCopy* %[[McCopy]], i32 0, i32 0 85 // CHECK: %[[McVal:.+]] = load i32, i32* %[[McGep]] 86 // CHECK: call void @_Z7consumeiii(i32 noundef %[[IntParam]], i32 noundef %[[MoVal]], i32 noundef %[[McVal]]) 87 88 consume(val, moParam.val, mcParam.val); 89 co_return; 90 91 // Skip to final suspend: 92 // CHECK: call void @_ZNSt16coroutine_traitsIJvi8MoveOnly11MoveAndCopyEE12promise_type13final_suspendEv( 93 // CHECK: call void @_ZN14suspend_always12await_resumeEv( 94 95 // Destroy promise, then parameter copies: 96 // CHECK: call void @_ZNSt16coroutine_traitsIJvi8MoveOnly11MoveAndCopyEE12promise_typeD1Ev(%"struct.std::coroutine_traits<void, int, MoveOnly, MoveAndCopy>::promise_type"* {{[^,]*}} %__promise) 97 // CHECK-NEXT: bitcast %"struct.std::coroutine_traits<void, int, MoveOnly, MoveAndCopy>::promise_type"* %__promise to i8* 98 // CHECK-NEXT: call void @llvm.lifetime.end.p0i8( 99 // CHECK-NEXT: call void @_ZN11MoveAndCopyD1Ev(%struct.MoveAndCopy* {{[^,]*}} %[[McCopy]]) 100 // CHECK-NEXT: bitcast %struct.MoveAndCopy* %[[McCopy]] to i8* 101 // CHECK-NEXT: call void @llvm.lifetime.end.p0i8( 102 // CHECK-NEXT: call void @_ZN8MoveOnlyD1Ev(%struct.MoveOnly* {{[^,]*}} %[[MoCopy]] 103 // CHECK-NEXT: bitcast %struct.MoveOnly* %[[MoCopy]] to i8* 104 // CHECK-NEXT: call void @llvm.lifetime.end.p0i8( 105 // CHECK-NEXT: bitcast i32* %{{.+}} to i8* 106 // CHECK-NEXT: call void @llvm.lifetime.end.p0i8( 107 // CHECK-NEXT: call i8* @llvm.coro.free( 108 } 109 110 // CHECK-LABEL: void @_Z16dependent_paramsI1A1BEvT_T0_S3_(%struct.A* noundef %x, %struct.B* noundef %0, %struct.B* noundef %y) 111 template <typename T, typename U> 112 void dependent_params(T x, U, U y) { 113 // CHECK: %[[x_copy:.+]] = alloca %struct.A 114 // CHECK-NEXT: %[[unnamed_copy:.+]] = alloca %struct.B 115 // CHECK-NEXT: %[[y_copy:.+]] = alloca %struct.B 116 117 // CHECK: call i8* @llvm.coro.begin 118 // CHECK-NEXT: bitcast %struct.A* %[[x_copy]] to i8* 119 // CHECK-NEXT: call void @llvm.lifetime.start.p0i8( 120 // CHECK-NEXT: call void @_ZN1AC1EOS_(%struct.A* {{[^,]*}} %[[x_copy]], %struct.A* noundef nonnull align 4 dereferenceable(512) %x) 121 // CHECK-NEXT: bitcast %struct.B* %[[unnamed_copy]] to i8* 122 // CHECK-NEXT: call void @llvm.lifetime.start.p0i8( 123 // CHECK-NEXT: call void @_ZN1BC1EOS_(%struct.B* {{[^,]*}} %[[unnamed_copy]], %struct.B* noundef nonnull align 4 dereferenceable(512) %0) 124 // CHECK-NEXT: bitcast %struct.B* %[[y_copy]] to i8* 125 // CHECK-NEXT: call void @llvm.lifetime.start.p0i8( 126 // CHECK-NEXT: call void @_ZN1BC1EOS_(%struct.B* {{[^,]*}} %[[y_copy]], %struct.B* noundef nonnull align 4 dereferenceable(512) %y) 127 // CHECK-NEXT: bitcast %"struct.std::coroutine_traits<void, A, B, B>::promise_type"* %__promise to i8* 128 // CHECK-NEXT: call void @llvm.lifetime.start.p0i8( 129 // CHECK-NEXT: invoke void @_ZNSt16coroutine_traitsIJv1A1BS1_EE12promise_typeC1Ev( 130 131 co_return; 132 } 133 134 struct A { 135 int WontFitIntoRegisterForSure[128]; 136 A(); 137 A(A&&) noexcept; 138 ~A(); 139 }; 140 141 struct B { 142 int WontFitIntoRegisterForSure[128]; 143 B(); 144 B(B&&) noexcept; 145 ~B(); 146 }; 147 148 void call_dependent_params() { 149 dependent_params(A{}, B{}, B{}); 150 } 151 152 // Test that, when the promise type has a constructor whose signature matches 153 // that of the coroutine function, that constructor is used. This is an 154 // experimental feature that will be proposed for the Coroutines TS. 155 156 struct promise_matching_constructor {}; 157 158 template <> 159 struct std::coroutine_traits<void, promise_matching_constructor, int, float, double> { 160 struct promise_type { 161 promise_type(promise_matching_constructor, int, float, double) {} 162 promise_type() = delete; 163 void get_return_object() {} 164 suspend_always initial_suspend() { return {}; } 165 suspend_always final_suspend() noexcept { return {}; } 166 void return_void() {} 167 void unhandled_exception() {} 168 }; 169 }; 170 171 // CHECK-LABEL: void @_Z38coroutine_matching_promise_constructor28promise_matching_constructorifd(i32 noundef %0, float noundef %1, double noundef %2) 172 void coroutine_matching_promise_constructor(promise_matching_constructor, int, float, double) { 173 // CHECK: %[[INT:.+]] = load i32, i32* %5, align 4 174 // CHECK: %[[FLOAT:.+]] = load float, float* %6, align 4 175 // CHECK: %[[DOUBLE:.+]] = load double, double* %7, align 8 176 // CHECK: invoke void @_ZNSt16coroutine_traitsIJv28promise_matching_constructorifdEE12promise_typeC1ES0_ifd(%"struct.std::coroutine_traits<void, promise_matching_constructor, int, float, double>::promise_type"* {{[^,]*}} %__promise, i32 noundef %[[INT]], float noundef %[[FLOAT]], double noundef %[[DOUBLE]]) 177 co_return; 178 } 179 180 struct some_class; 181 182 struct method {}; 183 184 template <typename... Args> struct std::coroutine_traits<method, Args...> { 185 struct promise_type { 186 promise_type(some_class&, float); 187 method get_return_object(); 188 suspend_always initial_suspend(); 189 suspend_always final_suspend() noexcept; 190 void return_void(); 191 void unhandled_exception(); 192 }; 193 }; 194 195 struct some_class { 196 method good_coroutine_calls_custom_constructor(float); 197 }; 198 199 // CHECK-LABEL: define{{.*}} void @_ZN10some_class39good_coroutine_calls_custom_constructorEf(%struct.some_class* 200 method some_class::good_coroutine_calls_custom_constructor(float) { 201 // CHECK: invoke void @_ZNSt16coroutine_traitsIJ6methodR10some_classfEE12promise_typeC1ES2_f(%"struct.std::coroutine_traits<method, some_class &, float>::promise_type"* {{[^,]*}} %__promise, %struct.some_class* noundef nonnull align 1 dereferenceable(1) %{{.+}}, float 202 co_return; 203 } 204