200 lines · cpp
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-attributes2 3#include "Inputs/std-coroutine.h"4 5using std::suspend_always;6using std::suspend_never;7 8template <typename T> struct [[clang::coro_lifetimebound, clang::coro_return_type]] Co {9 struct promise_type {10 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>19 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 30Co<int> foo_coro(const int& b) {31 if (b > 0)32 co_return 1;33 co_return 2;34}35 36int getInt() { return 0; }37 38Co<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 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 53[[clang::coro_wrapper]] Co<int> safe_forwarding(const int& b) {54 return foo_coro(b);55}56 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 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// Lambdas69// =============================================================================70namespace lambdas {71void 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 92Co<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 lambdas112 113// =============================================================================114// Member coroutines115// =============================================================================116namespace member_coroutines{117struct S {118 Co<int> member(const int& a) { co_return a; }119};120 121Co<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 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_coroutines135 136// =============================================================================137// Safe usage when parameters are value138// =============================================================================139namespace by_value {140Co<int> value_coro(int b) { co_return co_await foo_coro(b); }141[[clang::coro_wrapper]] Co<int> wrapper1(int b) { return value_coro(b); }142[[clang::coro_wrapper]] Co<int> wrapper2(const int& b) { return value_coro(b); }143} // namespace by_value144 145// =============================================================================146// Lifetime bound but not a Coroutine Return Type: No analysis.147// =============================================================================148namespace not_a_crt {149template <typename T> struct [[clang::coro_lifetimebound]] CoNoCRT {150 struct promise_type {151 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 161CoNoCRT<int> foo_coro(const int& a) { co_return a; }162CoNoCRT<int> bar(int a) { 163 auto x = foo_coro(a);164 co_return 1;165}166} // namespace not_a_crt167 168// =============================================================================169// Not lifetime bound coroutine wrappers: [[clang::coro_disable_lifetimebound]].170// =============================================================================171namespace disable_lifetimebound {172Co<int> foo(int x) { co_return x; }173 174[[clang::coro_wrapper, clang::coro_disable_lifetimebound]]175Co<int> foo_wrapper(const int& x) { return foo(x); }176 177[[clang::coro_wrapper]] Co<int> caller() {178 // The call to foo_wrapper is wrapper is safe.179 return foo_wrapper(1);180}181 182struct S{183[[clang::coro_wrapper, clang::coro_disable_lifetimebound]]184Co<int> member(const int& x) { return foo(x); }185};186 187Co<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 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_lifetimebound200