xref: /llvm-project/clang/test/SemaCXX/coro-lifetimebound.cpp (revision 667e58a72e0d81abe0ab3500b5d5563b6a598e7f)
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