1 // RUN: %clang_cc1 -triple x86_64-apple-darwin9 %s -std=c++20 -fsyntax-only -verify -Wall -Wextra -Wno-error=unreachable-code -Wno-unused -Wno-c++23-lambda-attributes
2
3 #include "Inputs/std-coroutine.h"
4
5 using std::suspend_always;
6 using std::suspend_never;
7
8 template <typename T> struct [[clang::coro_lifetimebound, clang::coro_return_type]] Co {
9 struct promise_type {
get_return_objectCo::promise_type10 Co<T> get_return_object() {
11 return {};
12 }
13 suspend_always initial_suspend();
14 suspend_always final_suspend() noexcept;
15 void unhandled_exception();
16 void return_value(const T &t);
17
18 template <typename U>
await_transformCo::promise_type19 auto await_transform(const Co<U> &) {
20 struct awaitable {
21 bool await_ready() noexcept { return false; }
22 void await_suspend(std::coroutine_handle<>) noexcept {}
23 U await_resume() noexcept { return {}; }
24 };
25 return awaitable{};
26 }
27 };
28 };
29
foo_coro(const int & b)30 Co<int> foo_coro(const int& b) {
31 if (b > 0)
32 co_return 1;
33 co_return 2;
34 }
35
getInt()36 int getInt() { return 0; }
37
bar_coro(const int & b,int c)38 Co<int> bar_coro(const int &b, int c) {
39 int x = co_await foo_coro(b);
40 int y = co_await foo_coro(1);
41 int z = co_await foo_coro(getInt());
42 auto unsafe1 = foo_coro(1); // expected-warning {{temporary whose address is used as value of local variable}}
43 auto unsafe2 = foo_coro(getInt()); // expected-warning {{temporary whose address is used as value of local variable}}
44 auto safe1 = foo_coro(b);
45 auto safe2 = foo_coro(c);
46 co_return co_await foo_coro(co_await foo_coro(1));
47 }
48
plain_return_co(int b)49 [[clang::coro_wrapper]] Co<int> plain_return_co(int b) {
50 return foo_coro(b); // expected-warning {{address of stack memory associated with parameter}}
51 }
52
safe_forwarding(const int & b)53 [[clang::coro_wrapper]] Co<int> safe_forwarding(const int& b) {
54 return foo_coro(b);
55 }
56
unsafe_wrapper(int b)57 [[clang::coro_wrapper]] Co<int> unsafe_wrapper(int b) {
58 return safe_forwarding(b); // expected-warning {{address of stack memory associated with parameter}}
59 }
60
complex_plain_return(int b)61 [[clang::coro_wrapper]] Co<int> complex_plain_return(int b) {
62 return b > 0
63 ? foo_coro(1) // expected-warning {{returning address of local temporary object}}
64 : bar_coro(0, 1); // expected-warning {{returning address of local temporary object}}
65 }
66
67 // =============================================================================
68 // Lambdas
69 // =============================================================================
70 namespace lambdas {
lambdas()71 void lambdas() {
72 auto unsafe_lambda = [] [[clang::coro_wrapper]] (int b) {
73 return foo_coro(b); // expected-warning {{address of stack memory associated with parameter}}
74 };
75 auto coro_lambda = [] (const int&) -> Co<int> {
76 co_return 0;
77 };
78 auto unsafe_coro_lambda = [&] (const int& b) -> Co<int> {
79 int x = co_await coro_lambda(b);
80 auto safe = coro_lambda(b);
81 auto unsafe1 = coro_lambda(1); // expected-warning {{temporary whose address is used as value of local variable}}
82 auto unsafe2 = coro_lambda(getInt()); // expected-warning {{temporary whose address is used as value of local variable}}
83 auto unsafe3 = coro_lambda(co_await coro_lambda(b)); // expected-warning {{temporary whose address is used as value of local variable}}
84 co_return co_await safe;
85 };
86 auto safe_lambda = [](int b) -> Co<int> {
87 int x = co_await foo_coro(1);
88 co_return x + co_await foo_coro(b);
89 };
90 }
91
lambda_captures()92 Co<int> lambda_captures() {
93 int a = 1;
94 // Temporary lambda object dies.
95 auto lamb = [a](int x, const int& y) -> Co<int> { // expected-warning {{temporary whose address is used as value of local variable 'lamb'}}
96 co_return x + y + a;
97 }(1, a);
98 // Object dies but it has no capture.
99 auto no_capture = []() -> Co<int> { co_return 1; }();
100 auto bad_no_capture = [](const int& a) -> Co<int> { co_return a; }(1); // expected-warning {{temporary}}
101 // Temporary lambda object with lifetime extension under co_await.
102 int res = co_await [a](int x, const int& y) -> Co<int> {
103 co_return x + y + a;
104 }(1, a);
105 // Lambda object on stack should be fine.
106 auto lamb2 = [a]() -> Co<int> { co_return a; };
107 auto on_stack = lamb2();
108 auto res2 = co_await on_stack;
109 co_return 1;
110 }
111 } // namespace lambdas
112
113 // =============================================================================
114 // Member coroutines
115 // =============================================================================
116 namespace member_coroutines{
117 struct S {
membermember_coroutines::S118 Co<int> member(const int& a) { co_return a; }
119 };
120
use()121 Co<int> use() {
122 S s;
123 int a = 1;
124 auto test1 = s.member(1); // expected-warning {{temporary whose address is used as value of local variable}}
125 auto test2 = s.member(a);
126 auto test3 = S{}.member(a); // expected-warning {{temporary whose address is used as value of local variable}}
127 co_return 1;
128 }
129
wrapper(const int & a)130 [[clang::coro_wrapper]] Co<int> wrapper(const int& a) {
131 S s;
132 return s.member(a); // expected-warning {{address of stack memory}}
133 }
134 } // member_coroutines
135
136 // =============================================================================
137 // Safe usage when parameters are value
138 // =============================================================================
139 namespace by_value {
value_coro(int b)140 Co<int> value_coro(int b) { co_return co_await foo_coro(b); }
wrapper1(int b)141 [[clang::coro_wrapper]] Co<int> wrapper1(int b) { return value_coro(b); }
wrapper2(const int & b)142 [[clang::coro_wrapper]] Co<int> wrapper2(const int& b) { return value_coro(b); }
143 } // namespace by_value
144
145 // =============================================================================
146 // Lifetime bound but not a Coroutine Return Type: No analysis.
147 // =============================================================================
148 namespace not_a_crt {
149 template <typename T> struct [[clang::coro_lifetimebound]] CoNoCRT {
150 struct promise_type {
get_return_objectnot_a_crt::CoNoCRT::promise_type151 CoNoCRT<T> get_return_object() {
152 return {};
153 }
154 suspend_always initial_suspend();
155 suspend_always final_suspend() noexcept;
156 void unhandled_exception();
157 void return_value(const T &t);
158 };
159 };
160
foo_coro(const int & a)161 CoNoCRT<int> foo_coro(const int& a) { co_return a; }
bar(int a)162 CoNoCRT<int> bar(int a) {
163 auto x = foo_coro(a);
164 co_return 1;
165 }
166 } // namespace not_a_crt
167
168 // =============================================================================
169 // Not lifetime bound coroutine wrappers: [[clang::coro_disable_lifetimebound]].
170 // =============================================================================
171 namespace disable_lifetimebound {
foo(int x)172 Co<int> foo(int x) { co_return x; }
173
174 [[clang::coro_wrapper, clang::coro_disable_lifetimebound]]
foo_wrapper(const int & x)175 Co<int> foo_wrapper(const int& x) { return foo(x); }
176
caller()177 [[clang::coro_wrapper]] Co<int> caller() {
178 // The call to foo_wrapper is wrapper is safe.
179 return foo_wrapper(1);
180 }
181
182 struct S{
183 [[clang::coro_wrapper, clang::coro_disable_lifetimebound]]
memberdisable_lifetimebound::S184 Co<int> member(const int& x) { return foo(x); }
185 };
186
use()187 Co<int> use() {
188 S s;
189 int a = 1;
190 auto test1 = s.member(1); // param is not flagged.
191 auto test2 = S{}.member(a); // 'this' is not flagged.
192 co_return 1;
193 }
194
return_stack_addr(const int & a)195 [[clang::coro_wrapper]] Co<int> return_stack_addr(const int& a) {
196 S s;
197 return s.member(a); // return of stack addr is not flagged.
198 }
199 } // namespace disable_lifetimebound
200