diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -305,8 +305,9 @@ [CXXPre17CompatPedantic]>; def CXXPre20Compat : DiagGroup<"pre-c++20-compat">; def : DiagGroup<"c++98-c++11-c++14-c++17-compat", [CXXPre20Compat]>; +def CXXPre20CompatCoroutines : DiagGroup<"pre-c++20-compat-coroutines">; def CXXPre20CompatPedantic : DiagGroup<"pre-c++20-compat-pedantic", - [CXXPre20Compat]>; + [CXXPre20Compat, CXXPre20CompatCoroutines]>; def : DiagGroup<"c++98-c++11-c++14-c++17-compat-pedantic", [CXXPre20CompatPedantic]>; def CXXPre23Compat : DiagGroup<"pre-c++23-compat">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -230,6 +230,11 @@ "brace elision for designated initializer is a C99 extension">, InGroup, SFINAEFailure; +// C++20 coroutines +def warn_cxx17_compat_coroutines : Warning< + "coroutines are incompatible with C++ standards before C++20">, + InGroup, DefaultIgnore; + // Declarations. def ext_plain_complex : ExtWarn< "plain '_Complex' requires a type specifier; assuming '_Complex double'">; diff --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp --- a/clang/lib/Sema/SemaCoroutine.cpp +++ b/clang/lib/Sema/SemaCoroutine.cpp @@ -701,6 +701,8 @@ auto *Fn = cast(CurContext); SourceLocation Loc = Fn->getLocation(); + Diag(Loc, diag::warn_cxx17_compat_coroutines); + // Build the initial suspend point auto buildSuspends = [&](StringRef Name) mutable -> StmtResult { ExprResult Operand = buildPromiseCall(*this, ScopeInfo->CoroutinePromise, diff --git a/clang/test/SemaCXX/cxx20-coroutines-warning.cpp b/clang/test/SemaCXX/cxx20-coroutines-warning.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaCXX/cxx20-coroutines-warning.cpp @@ -0,0 +1,105 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 -I%S/Inputs -Wpre-c++20-compat-pedantic %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 -I%S/Inputs -Wpre-c++20-compat-coroutines %s + +#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) { // expected-warning {{coroutines are incompatible with C++ standards before C++20}} + co_await (i = 0, std::suspend_always{}); + co_await (i = 0, std::suspend_always{}); +} + +generator range(int start, int end) // expected-warning {{coroutines are incompatible with C++ standards before C++20}} +{ + while (start < end) + co_yield start++; +} + +Task go(int const& val); +Task go1(int x) { // expected-warning {{coroutines are incompatible with C++ standards before C++20}} + co_return co_await go(++x); +}