Index: clang/lib/Sema/SemaChecking.cpp =================================================================== --- clang/lib/Sema/SemaChecking.cpp +++ clang/lib/Sema/SemaChecking.cpp @@ -15241,6 +15241,23 @@ Base::VisitStmt(E); } + void VisitCoroutineSuspendExpr(const CoroutineSuspendExpr *CSE) { + for (auto *Sub : CSE->children()) { + const Expr *ChildExpr = dyn_cast_or_null(Sub); + if (!ChildExpr) + continue; + + if (ChildExpr == CSE->getOperand()) + // Do not recurse over a CoroutineSuspendExpr's operand. + // The operand is also a subexpression of getCommonExpr(), and + // recursing into it directly could confuse object management + // for the sake of sequence tracking. + continue; + + Visit(Sub); + } + } + void VisitCastExpr(const CastExpr *E) { Object O = Object(); if (E->getCastKind() == CK_LValueToRValue) Index: clang/test/SemaCXX/Inputs/std-coroutine.h =================================================================== --- clang/test/SemaCXX/Inputs/std-coroutine.h +++ clang/test/SemaCXX/Inputs/std-coroutine.h @@ -4,12 +4,23 @@ namespace std { +template struct remove_reference { typedef T type; }; +template struct remove_reference { typedef T type; }; +template struct remove_reference { typedef T type; }; + +template +typename remove_reference::type &&move(T &&t) noexcept; + +struct input_iterator_tag {}; +struct forward_iterator_tag : public input_iterator_tag {}; + template struct coroutine_traits { using promise_type = typename Ret::promise_type; }; template struct coroutine_handle { static coroutine_handle from_address(void *) noexcept; + static coroutine_handle from_promise(Promise &promise); constexpr void* address() const noexcept; }; template <> Index: clang/test/SemaCXX/coroutine-mixed-exp-namespace.cpp =================================================================== --- clang/test/SemaCXX/coroutine-mixed-exp-namespace.cpp +++ clang/test/SemaCXX/coroutine-mixed-exp-namespace.cpp @@ -26,5 +26,5 @@ co_return; // expected-error {{mixed use of std and std::experimental namespaces for coroutine components}} // expected-warning@-1{{support for 'std::experimental::coroutine_traits' will be removed}} // expected-note@Inputs/std-coroutine-exp-namespace.h:8 {{'coroutine_traits' declared here}} - // expected-note@Inputs/std-coroutine.h:8 {{'coroutine_traits' declared here}} + // expected-note@Inputs/std-coroutine.h:18 {{'coroutine_traits' declared here}} } Index: clang/test/SemaCXX/coroutine-mixed2-exp-namespace.cpp =================================================================== --- clang/test/SemaCXX/coroutine-mixed2-exp-namespace.cpp +++ clang/test/SemaCXX/coroutine-mixed2-exp-namespace.cpp @@ -27,5 +27,5 @@ co_return; // expected-error {{mixed use of std and std::experimental namespaces for coroutine components}} // expected-warning@-1{{support for 'std::experimental::coroutine_traits' will be removed}} // expected-note@Inputs/std-coroutine-exp-namespace.h:8 {{'coroutine_traits' declared here}} - // expected-note@Inputs/std-coroutine.h:8 {{'coroutine_traits' declared here}} + // expected-note@Inputs/std-coroutine.h:18 {{'coroutine_traits' declared here}} } Index: clang/test/SemaCXX/coroutine-mixed4-exp-namespace.cpp =================================================================== --- clang/test/SemaCXX/coroutine-mixed4-exp-namespace.cpp +++ clang/test/SemaCXX/coroutine-mixed4-exp-namespace.cpp @@ -28,5 +28,5 @@ void test() { co_return; // expected-error {{mixed use of std and std::experimental namespaces for coroutine components}} // expected-warning@-1{{support for 'std::experimental::coroutine_traits' will be removed}} - // expected-note@Inputs/std-coroutine.h:8 {{'coroutine_traits' declared here}} + // expected-note@Inputs/std-coroutine.h:18 {{'coroutine_traits' declared here}} } Index: clang/test/SemaCXX/warn-unsequenced-coro.cpp =================================================================== --- /dev/null +++ clang/test/SemaCXX/warn-unsequenced-coro.cpp @@ -0,0 +1,105 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fsyntax-only -verify -std=c++20 -I%S/Inputs -Wno-unused -Wno-uninitialized -Wunsequenced %s + +// expected-no-diagnostics + +#include "std-coroutine.h" + +typedef __PTRDIFF_TYPE__ ptrdiff_t; + +using namespace std; + +template +struct Task { + struct promise_type { + Task get_return_object() noexcept; + suspend_always initial_suspend() noexcept; + suspend_always final_suspend() noexcept; + void return_value(T); + void unhandled_exception(); + auto yield_value(Task) noexcept { return final_suspend(); } + }; + bool await_ready() noexcept { return false; } + void await_suspend(coroutine_handle<>) noexcept {} + T await_resume(); +}; + +template<> +struct Task { + struct promise_type { + Task get_return_object() noexcept; + suspend_always initial_suspend() noexcept; + suspend_always final_suspend() noexcept; + void return_void() noexcept; + void unhandled_exception() noexcept; + auto yield_value(Task) noexcept { return final_suspend(); } + }; + bool await_ready() noexcept { return false; } + void await_suspend(coroutine_handle<>) noexcept {} + void await_resume() noexcept {} +}; + +template +class generator +{ + struct Promise + { + auto get_return_object() { return generator{*this}; } + auto initial_suspend() { return suspend_never{}; } + auto final_suspend() noexcept { return suspend_always{}; } + void unhandled_exception() {} + void return_void() {} + + auto yield_value(T value) + { + value_ = std::move(value); + return suspend_always{}; + } + + T value_; + }; + + using Handle = coroutine_handle; + + struct sentinel{}; + struct iterator + { + using iterator_category = input_iterator_tag; + using value_type = T; + using difference_type = ptrdiff_t; + using reference = T &; + using const_reference = const T &; + using pointer = T *; + + iterator &operator++() + { + h_.resume(); + return *this; + } + const_reference &operator*() const { return h_.promise().value_; } + bool operator!=(sentinel) { return !h_.done(); } + + Handle h_; + }; + + explicit generator(Promise &p) : h_(Handle::from_promise(p)) {} + Handle h_; +public: + using promise_type = Promise; + auto begin() { return iterator{h_}; } + auto end() { return sentinel{}; } +}; + +Task c(int i) { + co_await (i = 0, std::suspend_always{}); +} + +generator range(int start, int end) +{ + while (start < end) + co_yield start++; +} + +Task go(int const& val); +Task go1(int x) { + co_return co_await go(++x); +} \ No newline at end of file