Clang Project

clang_source_code/test/CodeGenCoroutines/coro-params.cpp
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
7namespace std::experimental {
8template <typename... T> struct coroutine_traits;
9
10template <class Promise = void> struct coroutine_handle {
11  coroutine_handle() = default;
12  static coroutine_handle from_address(void *) noexcept;
13};
14template <> 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
22struct suspend_always {
23  bool await_ready() noexcept;
24  void await_suspend(std::experimental::coroutine_handle<>) noexcept;
25  void await_resume() noexcept;
26};
27
28template <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
41struct CopyOnly {
42  int val;
43  CopyOnly(const CopyOnly&) noexcept;
44  CopyOnly(CopyOnly&&) = delete;
45  ~CopyOnly();
46};
47
48struct MoveOnly {
49  int val;
50  MoveOnly(const MoveOnly&) = delete;
51  MoveOnly(MoveOnly&&) noexcept;
52  ~MoveOnly();
53};
54
55struct MoveAndCopy {
56  int val;
57  MoveAndCopy(const MoveAndCopy&)noexcept;
58  MoveAndCopy(MoveAndCopy&&) noexcept;
59  ~MoveAndCopy();
60};
61
62void 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*
66void 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)
99template <typename T, typename U>
100void 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
114struct A {
115  int WontFitIntoRegisterForSure[128];
116  A();
117  A(A&&) noexcept;
118  ~A();
119};
120
121struct B {
122  int WontFitIntoRegisterForSure[128];
123  B();
124  B(B&&) noexcept;
125  ~B();
126};
127
128void 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
136struct promise_matching_constructor {};
137
138template<>
139struct 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)
152void 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
160struct some_class;
161
162struct method {};
163
164template <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
175struct 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*
180method 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