diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -15176,6 +15176,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) diff --git a/clang/test/SemaCXX/warn-unsequenced-coro.cpp b/clang/test/SemaCXX/warn-unsequenced-coro.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaCXX/warn-unsequenced-coro.cpp @@ -0,0 +1,144 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fsyntax-only -verify -std=c++20 -Wno-unused -Wno-uninitialized -Wunsequenced %s + +// expected-no-diagnostics + +typedef __PTRDIFF_TYPE__ ptrdiff_t; + +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; + +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); +}; +template <> +struct coroutine_handle { + template + coroutine_handle(coroutine_handle) noexcept; + static coroutine_handle from_address(void *); +}; + +struct suspend_always { + bool await_ready() noexcept { return false; } + void await_suspend(coroutine_handle<>) noexcept {} + void await_resume() noexcept {} +}; + +struct suspend_never { + bool await_ready() noexcept { return true; } + void await_suspend(coroutine_handle<>) noexcept {} + void await_resume() noexcept {} +}; + +struct input_iterator_tag {}; +struct forward_iterator_tag : public input_iterator_tag {}; + +} // namespace std + +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