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 -std=c++1z -fcoroutines-ts -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s -disable-llvm-passes -fexceptions | FileCheck %s 6 7 namespace std::experimental { 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::experimental 21 22 struct suspend_always { 23 bool await_ready() noexcept; 24 void await_suspend(std::experimental::coroutine_handle<>) noexcept; 25 void await_resume() noexcept; 26 }; 27 28 template <typename... Args> struct std::experimental::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::experimental::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 @_ZNSt12experimental16coroutine_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 @_ZNSt12experimental16coroutine_traitsIJvi8MoveOnly11MoveAndCopyEE12promise_type13final_suspendEv( 93 // CHECK: call void @_ZN14suspend_always12await_resumeEv( 94 95 // Destroy promise, then parameter copies: 96 // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJvi8MoveOnly11MoveAndCopyEE12promise_typeD1Ev(%"struct.std::experimental::coroutine_traits<void, int, MoveOnly, MoveAndCopy>::promise_type"* {{[^,]*}} %__promise) 97 // CHECK-NEXT: bitcast %"struct.std::experimental::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::experimental::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 @_ZNSt12experimental16coroutine_traitsIJv1A1BS2_EE12promise_typeC1Ev( 130 131 co_return; 132 } 133 134 struct A { 135 int WontFitIntoRegisterForSure[128]; 136 A(); 137 A(A &&) 138 noexcept; 139 ~A(); 140 }; 141 142 struct B { 143 int WontFitIntoRegisterForSure[128]; 144 B(); 145 B(B &&) 146 noexcept; 147 ~B(); 148 }; 149 150 void call_dependent_params() { 151 dependent_params(A{}, B{}, B{}); 152 } 153 154 // Test that, when the promise type has a constructor whose signature matches 155 // that of the coroutine function, that constructor is used. This is an 156 // experimental feature that will be proposed for the Coroutines TS. 157 158 struct promise_matching_constructor {}; 159 160 template <> 161 struct std::experimental::coroutine_traits<void, promise_matching_constructor, int, float, double> { 162 struct promise_type { 163 promise_type(promise_matching_constructor, int, float, double) {} 164 promise_type() = delete; 165 void get_return_object() {} 166 suspend_always initial_suspend() { return {}; } 167 suspend_always final_suspend() noexcept { return {}; } 168 void return_void() {} 169 void unhandled_exception() {} 170 }; 171 }; 172 173 // CHECK-LABEL: void @_Z38coroutine_matching_promise_constructor28promise_matching_constructorifd(i32 noundef %0, float noundef %1, double noundef %2) 174 void coroutine_matching_promise_constructor(promise_matching_constructor, int, float, double) { 175 // CHECK: %[[INT:.+]] = load i32, i32* %5, align 4 176 // CHECK: %[[FLOAT:.+]] = load float, float* %6, align 4 177 // CHECK: %[[DOUBLE:.+]] = load double, double* %7, align 8 178 // CHECK: invoke void @_ZNSt12experimental16coroutine_traitsIJv28promise_matching_constructorifdEE12promise_typeC1ES1_ifd(%"struct.std::experimental::coroutine_traits<void, promise_matching_constructor, int, float, double>::promise_type"* {{[^,]*}} %__promise, i32 noundef %[[INT]], float noundef %[[FLOAT]], double noundef %[[DOUBLE]]) 179 co_return; 180 } 181 182 struct some_class; 183 184 struct method {}; 185 186 template <typename... Args> struct std::experimental::coroutine_traits<method, Args...> { 187 struct promise_type { 188 promise_type(some_class &, float); 189 method get_return_object(); 190 suspend_always initial_suspend(); 191 suspend_always final_suspend() noexcept; 192 void return_void(); 193 void unhandled_exception(); 194 }; 195 }; 196 197 struct some_class { 198 method good_coroutine_calls_custom_constructor(float); 199 }; 200 201 // CHECK-LABEL: define{{.*}} void @_ZN10some_class39good_coroutine_calls_custom_constructorEf(%struct.some_class* 202 method some_class::good_coroutine_calls_custom_constructor(float) { 203 // CHECK: invoke void @_ZNSt12experimental16coroutine_traitsIJ6methodR10some_classfEE12promise_typeC1ES3_f(%"struct.std::experimental::coroutine_traits<method, some_class &, float>::promise_type"* {{[^,]*}} %__promise, %struct.some_class* noundef nonnull align 1 dereferenceable(1) %{{.+}}, float 204 co_return; 205 } 206