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 } 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 %val, %struct.MoveOnly* %[[MoParam:.+]], %struct.MoveAndCopy* %[[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* dereferenceable(4) %[[MoParam]]) 73 // CHECK-NEXT: call void @_ZN11MoveAndCopyC1EOS_(%struct.MoveAndCopy* %[[McCopy]], %struct.MoveAndCopy* dereferenceable(4) %[[McParam]]) # 74 // CHECK-NEXT: invoke void @_ZNSt12experimental16coroutine_traitsIJvi8MoveOnly11MoveAndCopyEE12promise_typeC1Ev( 75 76 // CHECK: call void @_ZN14suspend_always12await_resumeEv( 77 // CHECK: %[[IntParam:.+]] = load i32, i32* %val1 78 // CHECK: %[[MoGep:.+]] = getelementptr inbounds %struct.MoveOnly, %struct.MoveOnly* %[[MoCopy]], i32 0, i32 0 79 // CHECK: %[[MoVal:.+]] = load i32, i32* %[[MoGep]] 80 // CHECK: %[[McGep:.+]] = getelementptr inbounds %struct.MoveAndCopy, %struct.MoveAndCopy* %[[McCopy]], i32 0, i32 0 81 // CHECK: %[[McVal:.+]] = load i32, i32* %[[McGep]] 82 // CHECK: call void @_Z7consumeiii(i32 %[[IntParam]], i32 %[[MoVal]], i32 %[[McVal]]) 83 84 consume(val, moParam.val, mcParam.val); 85 co_return; 86 87 // Skip to final suspend: 88 // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJvi8MoveOnly11MoveAndCopyEE12promise_type13final_suspendEv( 89 // CHECK: call void @_ZN14suspend_always12await_resumeEv( 90 91 // Destroy promise, then parameter copies: 92 // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJvi8MoveOnly11MoveAndCopyEE12promise_typeD1Ev(%"struct.std::experimental::coroutine_traits<void, int, MoveOnly, MoveAndCopy>::promise_type"* %__promise) #2 93 // CHECK-NEXT: call void @_ZN11MoveAndCopyD1Ev(%struct.MoveAndCopy* %[[McCopy]]) 94 // CHECK-NEXT: call void @_ZN8MoveOnlyD1Ev(%struct.MoveOnly* %[[MoCopy]] 95 // CHECK-NEXT: call i8* @llvm.coro.free( 96 } 97 98 // CHECK-LABEL: void @_Z16dependent_paramsI1A1BEvT_T0_S3_(%struct.A* %x, %struct.B*, %struct.B* %y) 99 template <typename T, typename U> 100 void dependent_params(T x, U, U y) { 101 // CHECK: %[[x_copy:.+]] = alloca %struct.A 102 // CHECK-NEXT: %[[unnamed_copy:.+]] = alloca %struct.B 103 // CHECK-NEXT: %[[y_copy:.+]] = alloca %struct.B 104 105 // CHECK: call i8* @llvm.coro.begin 106 // CHECK-NEXT: call void @_ZN1AC1EOS_(%struct.A* %[[x_copy]], %struct.A* dereferenceable(512) %x) 107 // CHECK-NEXT: call void @_ZN1BC1EOS_(%struct.B* %[[unnamed_copy]], %struct.B* dereferenceable(512) %0) 108 // CHECK-NEXT: call void @_ZN1BC1EOS_(%struct.B* %[[y_copy]], %struct.B* dereferenceable(512) %y) 109 // CHECK-NEXT: invoke void @_ZNSt12experimental16coroutine_traitsIJv1A1BS2_EE12promise_typeC1Ev( 110 111 co_return; 112 } 113 114 struct A { 115 int WontFitIntoRegisterForSure[128]; 116 A(); 117 A(A&&) noexcept; 118 ~A(); 119 }; 120 121 struct B { 122 int WontFitIntoRegisterForSure[128]; 123 B(); 124 B(B&&) noexcept; 125 ~B(); 126 }; 127 128 void call_dependent_params() { 129 dependent_params(A{}, B{}, B{}); 130 } 131 132 // Test that, when the promise type has a constructor whose signature matches 133 // that of the coroutine function, that constructor is used. This is an 134 // experimental feature that will be proposed for the Coroutines TS. 135 136 struct promise_matching_constructor {}; 137 138 template<> 139 struct std::experimental::coroutine_traits<void, promise_matching_constructor, int, float, double> { 140 struct promise_type { 141 promise_type(promise_matching_constructor, int, float, double) {} 142 promise_type() = delete; 143 void get_return_object() {} 144 suspend_always initial_suspend() { return {}; } 145 suspend_always final_suspend() { return {}; } 146 void return_void() {} 147 void unhandled_exception() {} 148 }; 149 }; 150 151 // CHECK-LABEL: void @_Z38coroutine_matching_promise_constructor28promise_matching_constructorifd(i32, float, double) 152 void coroutine_matching_promise_constructor(promise_matching_constructor, int, float, double) { 153 // CHECK: %[[INT:.+]] = load i32, i32* %5, align 4 154 // CHECK: %[[FLOAT:.+]] = load float, float* %6, align 4 155 // CHECK: %[[DOUBLE:.+]] = load double, double* %7, align 8 156 // 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 %[[INT]], float %[[FLOAT]], double %[[DOUBLE]]) 157 co_return; 158 } 159 160 struct some_class; 161 162 struct method {}; 163 164 template <typename... Args> struct std::experimental::coroutine_traits<method, Args...> { 165 struct promise_type { 166 promise_type(some_class&, float); 167 method get_return_object(); 168 suspend_always initial_suspend(); 169 suspend_always final_suspend(); 170 void return_void(); 171 void unhandled_exception(); 172 }; 173 }; 174 175 struct some_class { 176 method good_coroutine_calls_custom_constructor(float); 177 }; 178 179 // CHECK-LABEL: define void @_ZN10some_class39good_coroutine_calls_custom_constructorEf(%struct.some_class* 180 method some_class::good_coroutine_calls_custom_constructor(float) { 181 // CHECK: invoke void @_ZNSt12experimental16coroutine_traitsIJ6methodR10some_classfEE12promise_typeC1ES3_f(%"struct.std::experimental::coroutine_traits<method, some_class &, float>::promise_type"* %__promise, %struct.some_class* dereferenceable(1) %{{.+}}, float 182 co_return; 183 } 184