| // 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 |
| |
| #include "Inputs/std-coroutine.h" |
| |
| using std::suspend_always; |
| using std::suspend_never; |
| |
| template <typename T> struct [[clang::coro_lifetimebound, clang::coro_return_type]] Co { |
| struct promise_type { |
| Co<T> get_return_object() { |
| return {}; |
| } |
| suspend_always initial_suspend(); |
| suspend_always final_suspend() noexcept; |
| void unhandled_exception(); |
| void return_value(const T &t); |
| |
| template <typename U> |
| auto await_transform(const Co<U> &) { |
| struct awaitable { |
| bool await_ready() noexcept { return false; } |
| void await_suspend(std::coroutine_handle<>) noexcept {} |
| U await_resume() noexcept { return {}; } |
| }; |
| return awaitable{}; |
| } |
| }; |
| }; |
| |
| Co<int> foo_coro(const int& b) { |
| if (b > 0) |
| co_return 1; |
| co_return 2; |
| } |
| |
| int getInt() { return 0; } |
| |
| Co<int> bar_coro(const int &b, int c) { |
| int x = co_await foo_coro(b); |
| int y = co_await foo_coro(1); |
| int z = co_await foo_coro(getInt()); |
| auto unsafe1 = foo_coro(1); // expected-warning {{temporary whose address is used as value of local variable}} |
| auto unsafe2 = foo_coro(getInt()); // expected-warning {{temporary whose address is used as value of local variable}} |
| auto safe1 = foo_coro(b); |
| auto safe2 = foo_coro(c); |
| co_return co_await foo_coro(co_await foo_coro(1)); |
| } |
| |
| [[clang::coro_wrapper]] Co<int> plain_return_co(int b) { |
| return foo_coro(b); // expected-warning {{address of stack memory associated with parameter}} |
| } |
| |
| [[clang::coro_wrapper]] Co<int> safe_forwarding(const int& b) { |
| return foo_coro(b); |
| } |
| |
| [[clang::coro_wrapper]] Co<int> unsafe_wrapper(int b) { |
| return safe_forwarding(b); // expected-warning {{address of stack memory associated with parameter}} |
| } |
| |
| [[clang::coro_wrapper]] Co<int> complex_plain_return(int b) { |
| return b > 0 |
| ? foo_coro(1) // expected-warning {{returning address of local temporary object}} |
| : bar_coro(0, 1); // expected-warning {{returning address of local temporary object}} |
| } |
| |
| // ============================================================================= |
| // Lambdas |
| // ============================================================================= |
| namespace lambdas { |
| void lambdas() { |
| auto unsafe_lambda = [] [[clang::coro_wrapper]] (int b) { |
| return foo_coro(b); // expected-warning {{address of stack memory associated with parameter}} |
| }; |
| auto coro_lambda = [] (const int&) -> Co<int> { |
| co_return 0; |
| }; |
| auto unsafe_coro_lambda = [&] (const int& b) -> Co<int> { |
| int x = co_await coro_lambda(b); |
| auto safe = coro_lambda(b); |
| auto unsafe1 = coro_lambda(1); // expected-warning {{temporary whose address is used as value of local variable}} |
| auto unsafe2 = coro_lambda(getInt()); // expected-warning {{temporary whose address is used as value of local variable}} |
| auto unsafe3 = coro_lambda(co_await coro_lambda(b)); // expected-warning {{temporary whose address is used as value of local variable}} |
| co_return co_await safe; |
| }; |
| auto safe_lambda = [](int b) -> Co<int> { |
| int x = co_await foo_coro(1); |
| co_return x + co_await foo_coro(b); |
| }; |
| } |
| |
| Co<int> lambda_captures() { |
| int a = 1; |
| // Temporary lambda object dies. |
| auto lamb = [a](int x, const int& y) -> Co<int> { // expected-warning {{temporary whose address is used as value of local variable 'lamb'}} |
| co_return x + y + a; |
| }(1, a); |
| // Object dies but it has no capture. |
| auto no_capture = []() -> Co<int> { co_return 1; }(); |
| auto bad_no_capture = [](const int& a) -> Co<int> { co_return a; }(1); // expected-warning {{temporary}} |
| // Temporary lambda object with lifetime extension under co_await. |
| int res = co_await [a](int x, const int& y) -> Co<int> { |
| co_return x + y + a; |
| }(1, a); |
| // Lambda object on stack should be fine. |
| auto lamb2 = [a]() -> Co<int> { co_return a; }; |
| auto on_stack = lamb2(); |
| auto res2 = co_await on_stack; |
| co_return 1; |
| } |
| } // namespace lambdas |
| |
| // ============================================================================= |
| // Member coroutines |
| // ============================================================================= |
| namespace member_coroutines{ |
| struct S { |
| Co<int> member(const int& a) { co_return a; } |
| }; |
| |
| Co<int> use() { |
| S s; |
| int a = 1; |
| auto test1 = s.member(1); // expected-warning {{temporary whose address is used as value of local variable}} |
| auto test2 = s.member(a); |
| auto test3 = S{}.member(a); // expected-warning {{temporary whose address is used as value of local variable}} |
| co_return 1; |
| } |
| |
| [[clang::coro_wrapper]] Co<int> wrapper(const int& a) { |
| S s; |
| return s.member(a); // expected-warning {{address of stack memory}} |
| } |
| } // member_coroutines |
| |
| // ============================================================================= |
| // Safe usage when parameters are value |
| // ============================================================================= |
| namespace by_value { |
| Co<int> value_coro(int b) { co_return co_await foo_coro(b); } |
| [[clang::coro_wrapper]] Co<int> wrapper1(int b) { return value_coro(b); } |
| [[clang::coro_wrapper]] Co<int> wrapper2(const int& b) { return value_coro(b); } |
| } // namespace by_value |
| |
| // ============================================================================= |
| // Lifetime bound but not a Coroutine Return Type: No analysis. |
| // ============================================================================= |
| namespace not_a_crt { |
| template <typename T> struct [[clang::coro_lifetimebound]] CoNoCRT { |
| struct promise_type { |
| CoNoCRT<T> get_return_object() { |
| return {}; |
| } |
| suspend_always initial_suspend(); |
| suspend_always final_suspend() noexcept; |
| void unhandled_exception(); |
| void return_value(const T &t); |
| }; |
| }; |
| |
| CoNoCRT<int> foo_coro(const int& a) { co_return a; } |
| CoNoCRT<int> bar(int a) { |
| auto x = foo_coro(a); |
| co_return 1; |
| } |
| } // namespace not_a_crt |
| |
| // ============================================================================= |
| // Not lifetime bound coroutine wrappers: [[clang::coro_disable_lifetimebound]]. |
| // ============================================================================= |
| namespace disable_lifetimebound { |
| Co<int> foo(int x) { co_return x; } |
| |
| [[clang::coro_wrapper, clang::coro_disable_lifetimebound]] |
| Co<int> foo_wrapper(const int& x) { return foo(x); } |
| |
| [[clang::coro_wrapper]] Co<int> caller() { |
| // The call to foo_wrapper is wrapper is safe. |
| return foo_wrapper(1); |
| } |
| |
| struct S{ |
| [[clang::coro_wrapper, clang::coro_disable_lifetimebound]] |
| Co<int> member(const int& x) { return foo(x); } |
| }; |
| |
| Co<int> use() { |
| S s; |
| int a = 1; |
| auto test1 = s.member(1); // param is not flagged. |
| auto test2 = S{}.member(a); // 'this' is not flagged. |
| co_return 1; |
| } |
| |
| [[clang::coro_wrapper]] Co<int> return_stack_addr(const int& a) { |
| S s; |
| return s.member(a); // return of stack addr is not flagged. |
| } |
| } // namespace disable_lifetimebound |