Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11040,14 +11040,13 @@ "a coroutine; include if your version " "of libcxx is less than 14.0">; def warn_deprecated_coroutine_namespace : Warning< - "Please move from std::experimental::%0 to std::%0. " + "please move from std::experimental::%0 to std::%0. " "Support for std::experimental::%0 will be removed in LLVM 15.">, InGroup; -def err_mixed_use_std_and_experimental_namespace_for_coroutine : Error < - "Found mixed use of std namespace and std::experimental namespace for " - "coroutine, which is disallowed. The coroutine components in " - "std::experimental namespace is deprecated. Please use coroutine components " - "under std namespace.">; +def warn_mixed_use_std_and_experimental_namespace_for_coroutine : Warning< + "conflicting mixed use of std namespace and std::experimental namespace for " + "coroutine. Use coroutine components in std namespace.">, + InGroup; def err_implicit_coroutine_std_nothrow_type_not_found : Error< "std::nothrow was not found; include before defining a coroutine which " "uses get_return_object_on_allocation_failure()">; Index: clang/lib/Sema/SemaCoroutine.cpp =================================================================== --- clang/lib/Sema/SemaCoroutine.cpp +++ clang/lib/Sema/SemaCoroutine.cpp @@ -1661,44 +1661,62 @@ SourceLocation FuncLoc, NamespaceDecl *&Namespace) { if (!StdCoroutineTraitsCache) { - NamespaceDecl *CoroNamespace = getStdNamespace(); - LookupResult Result(*this, &PP.getIdentifierTable().get("coroutine_traits"), - FuncLoc, LookupOrdinaryName); - - if (!CoroNamespace || !LookupQualifiedName(Result, CoroNamespace)) { - /// Look up in namespace std::experimental, for compatibility. - /// TODO: Remove this extra lookup when is - /// removed. - CoroNamespace = lookupStdExperimentalNamespace(); - if (!CoroNamespace || !LookupQualifiedName(Result, CoroNamespace)) { - Diag(KwLoc, diag::err_implied_coroutine_type_not_found) - << "std::coroutine_traits"; - return nullptr; - } - Diag(KwLoc, diag::warn_deprecated_coroutine_namespace) - << "coroutine_traits"; - } else { - /// When we found coroutine_traits in std namespace. Make sure there is no - /// misleading definition in std::experimental namespace. - NamespaceDecl *ExpNamespace = lookupStdExperimentalNamespace(); - LookupResult ExpResult(*this, - &PP.getIdentifierTable().get("coroutine_traits"), - FuncLoc, LookupOrdinaryName); - if (ExpNamespace && LookupQualifiedName(ExpResult, ExpNamespace)) { - Diag(KwLoc, - diag::err_mixed_use_std_and_experimental_namespace_for_coroutine); - return nullptr; - } + auto const &TraitIdent = PP.getIdentifierTable().get("coroutine_traits"); + + // Look in ::std + NamespaceDecl *StdSpace = getStdNamespace(); + LookupResult ResStd(*this, &TraitIdent, FuncLoc, LookupOrdinaryName); + bool InStd = StdSpace && LookupQualifiedName(ResStd, StdSpace); + + /// FIXME: 2021-12-16, remove std::experimental handling for clang 15. + // Look in ::std::experimental + NamespaceDecl *ExpSpace = lookupStdExperimentalNamespace(); + LookupResult ResExp(*this, &TraitIdent, FuncLoc, LookupOrdinaryName); + bool InExp = ExpSpace && LookupQualifiedName(ResExp, ExpSpace); + + if (!InStd && !InExp) { + Diag(KwLoc, diag::err_implied_coroutine_type_not_found) + << "std::coroutine_traits"; + return nullptr; } - if (!(StdCoroutineTraitsCache = Result.getAsSingle())) { + // Prefer ::std's lookup, if it exists + auto &Result = InStd ? ResStd : ResExp; + CoroTraitsNamespaceCache = InStd ? StdSpace : ExpSpace; + + // Check we found a TemplateDecl + StdCoroutineTraitsCache = Result.getAsSingle(); + if (!StdCoroutineTraitsCache) { Result.suppressDiagnostics(); - NamedDecl *Found = *Result.begin(); - Diag(Found->getLocation(), diag::err_malformed_std_coroutine_traits); + auto *Decl = *Result.begin(); + Diag(Decl->getLocation(), diag::err_malformed_std_coroutine_traits); return nullptr; } - CoroTraitsNamespaceCache = CoroNamespace; + + if (!InStd) { + // Found only in std::experimental, warn + Diag(KwLoc, diag::warn_deprecated_coroutine_namespace) + << "coroutine_traits"; + Diag(StdCoroutineTraitsCache->getLocation(), + diag::note_entity_declared_at) + << StdCoroutineTraitsCache; + + CoroTraitsNamespaceCache = ExpSpace; + } else if (InExp && + ResExp.getAsSingle() != StdCoroutineTraitsCache) { + // Found in both, and they are different. We only warn, as this is not + // ill-formed, just confused. + Diag(KwLoc, + diag::warn_mixed_use_std_and_experimental_namespace_for_coroutine); + Diag(StdCoroutineTraitsCache->getLocation(), + diag::note_entity_declared_at) + << StdCoroutineTraitsCache; + ResExp.suppressDiagnostics(); + auto *DeclExp = *ResExp.begin(); + Diag(DeclExp->getLocation(), diag::note_entity_declared_at) << DeclExp; + } } + Namespace = CoroTraitsNamespaceCache; return StdCoroutineTraitsCache; } Index: clang/test/SemaCXX/Inputs/std-coroutine-exp-namespace.h =================================================================== --- clang/test/SemaCXX/Inputs/std-coroutine-exp-namespace.h +++ clang/test/SemaCXX/Inputs/std-coroutine-exp-namespace.h @@ -6,13 +6,20 @@ namespace experimental { template struct coroutine_traits { using promise_type = typename Ret::promise_type; }; - +#if EXPERIMENTAL_DEPRECATED != 0 +// expected-note@-2{{declared here}} +#endif template struct coroutine_handle { static coroutine_handle from_address(void *) noexcept; }; template <> struct coroutine_handle { +#if EXPERIMENTAL_DEPRECATED == 2 + // expected-note@-2{{candidate constructor}} + // expected-note@-3{{candidate constructor}} + // expected-note@+3{{candidate template ignored}} +#endif template coroutine_handle(coroutine_handle) noexcept; static coroutine_handle from_address(void *); Index: clang/test/SemaCXX/Inputs/std-coroutine.h =================================================================== --- clang/test/SemaCXX/Inputs/std-coroutine.h +++ clang/test/SemaCXX/Inputs/std-coroutine.h @@ -6,6 +6,9 @@ template struct coroutine_traits { using promise_type = typename Ret::promise_type; }; +#if EXPERIMENTAL_DEPRECATED == 2 +// expected-note@-2{{declared here}} +#endif template struct coroutine_handle { Index: clang/test/SemaCXX/co_await-range-for-exp-namespace.cpp =================================================================== --- clang/test/SemaCXX/co_await-range-for-exp-namespace.cpp +++ clang/test/SemaCXX/co_await-range-for-exp-namespace.cpp @@ -1,6 +1,7 @@ // RUN: %clang_cc1 -triple x86_64-apple-darwin9 %s -std=c++14 -fcoroutines-ts \ // RUN: -fsyntax-only -Wignored-qualifiers -Wno-error=return-type -verify \ // RUN: -fblocks +#define EXPERIMENTAL_DEPRECATED 1 #include "Inputs/std-coroutine-exp-namespace.h" using namespace std::experimental; @@ -50,7 +51,9 @@ }; MyForLoopArrayAwaiter g() { int arr[10] = {0}; - for co_await (auto i : arr) {} // expected-warning {{Please move from std::experimental::coroutine_traits to std::coroutine_traits}} + for + co_await(auto i // expected-warning {{move from std::experimental::coroutine_traits to std::coroutine_traits}} + : arr) {} // expected-error@-1 {{call to deleted member function 'await_transform'}} // expected-note@-2 {{'await_transform' implicitly required by 'co_await' here}} } Index: clang/test/SemaCXX/coreturn-eh-exp-namespace.cpp =================================================================== --- clang/test/SemaCXX/coreturn-eh-exp-namespace.cpp +++ clang/test/SemaCXX/coreturn-eh-exp-namespace.cpp @@ -1,5 +1,6 @@ // RUN: %clang_cc1 -triple x86_64-apple-darwin9 %s -std=c++14 -fcoroutines-ts -fcxx-exceptions -fsyntax-only -Wignored-qualifiers -Wno-error=return-type -verify -fblocks -Wall -Wextra -Wno-error=unreachable-code +#define EXPERIMENTAL_DEPRECATED 1 #include "Inputs/std-coroutine-exp-namespace.h" using std::experimental::suspend_always; @@ -39,7 +40,7 @@ VoidTagReturnValue test() { object x = {}; try { - co_return {}; // expected-warning {{Please move from std::experimental::coroutine_traits to std::coroutine_traits}} + co_return {}; // expected-warning {{move from std::experimental::coroutine_traits to std::coroutine_traits}} } catch (...) { throw; } Index: clang/test/SemaCXX/coreturn-exp-namespace.cpp =================================================================== --- clang/test/SemaCXX/coreturn-exp-namespace.cpp +++ clang/test/SemaCXX/coreturn-exp-namespace.cpp @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -triple x86_64-apple-darwin9 %s -std=c++14 -fcoroutines-ts -fsyntax-only -Wignored-qualifiers -Wno-error=return-type -verify -fblocks -Wall -Wextra -Wno-error=unreachable-code +#define EXPERIMENTAL_DEPRECATED 1 #include "Inputs/std-coroutine-exp-namespace.h" using std::experimental::suspend_always; @@ -83,7 +84,7 @@ template struct std::experimental::coroutine_traits { using promise_type = promise_int; }; -void test0() { co_await a; } // expected-warning {{Please move from std::experimental::coroutine_traits to std::coroutine_traits}} +void test0() { co_await a; } // expected-warning {{move from std::experimental::coroutine_traits to std::coroutine_traits}} float test1() { co_await a; } int test2() { Index: clang/test/SemaCXX/coroutine-final-suspend-noexcept-exp-namespace.cpp =================================================================== --- clang/test/SemaCXX/coroutine-final-suspend-noexcept-exp-namespace.cpp +++ clang/test/SemaCXX/coroutine-final-suspend-noexcept-exp-namespace.cpp @@ -7,6 +7,7 @@ namespace experimental { template struct coroutine_traits { using promise_type = typename Ret::promise_type; }; +// expected-note@-1{{declared here}} template struct coroutine_handle { @@ -56,7 +57,7 @@ coro_t f(int n) { // expected-error {{the expression 'co_await __promise.final_suspend()' is required to be non-throwing}} A a{}; - co_await a; // expected-warning {{Please move from std::experimental::coroutine_traits to std::coroutine_traits}} + co_await a; // expected-warning {{move from std::experimental::coroutine_traits to std::coroutine_traits}} } 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 @@ -2,11 +2,13 @@ // wouldn't make the compliler to crash and emit the diagnostic message correctly. // RUN: %clang_cc1 -verify -std=c++20 -fsyntax-only %s +#define EXPERIMENTAL_DEPRECATED 2 #include "Inputs/std-coroutine-exp-namespace.h" #include "Inputs/std-coroutine.h" struct my_awaitable { bool await_ready() noexcept; + // expected-note@+1{{passing argument}} void await_suspend(std::experimental::coroutine_handle<> coro) noexcept; void await_resume() noexcept; }; @@ -23,5 +25,9 @@ struct std::coroutine_traits { using promise_type = promise_void; }; void test() { - co_return; // expected-error {{Found mixed use of std namespace and std::experimental namespace for coroutine, which is disallowed. The coroutine components in std::experimental namespace is deprecated. Please use coroutine components under std namespace.}} + // expected-error@-1{{no viable conversion}} + // expected-note@-2{{required by the initial suspend point}} + co_return; + // expected-note@-1{{function is a coroutine}} + // expected-warning@-2{{conflicting mixed use of std namespace and std::experimental namespace for coroutine}} } 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 @@ -3,11 +3,13 @@ // included header wouldn't affect the diagnostic messages. // RUN: %clang_cc1 -verify -std=c++20 -fsyntax-only %s +#define EXPERIMENTAL_DEPRECATED 2 +#include "Inputs/std-coroutine.h" // First #include "Inputs/std-coroutine-exp-namespace.h" -#include "Inputs/std-coroutine.h" struct my_awaitable { bool await_ready() noexcept; + // expected-note@+1{{passing argument}} void await_suspend(std::experimental::coroutine_handle<> coro) noexcept; void await_resume() noexcept; }; @@ -24,5 +26,9 @@ struct std::coroutine_traits { using promise_type = promise_void; }; void test() { - co_return; // expected-error {{Found mixed use of std namespace and std::experimental namespace for coroutine, which is disallowed. The coroutine components in std::experimental namespace is deprecated. Please use coroutine components under std namespace.}} + // expected-error@-1{{no viable conversion}} + // expected-note@-2{{required by the initial suspend point}} + co_return; + // expected-note@-1{{function is a coroutine}} + // expected-warning@-2{{conflicting mixed use of std namespace and std::experimental namespace for coroutine}} } Index: clang/test/SemaCXX/coroutine-mixed3-exp-namespace.cpp =================================================================== --- /dev/null +++ clang/test/SemaCXX/coroutine-mixed3-exp-namespace.cpp @@ -0,0 +1,33 @@ +// This file is to test the mixed use of `std::experimental::coroutine*` and `std::coroutine*` +// wouldn't make the compliler to crash and emit the diagnostic message correctly. +// RUN: %clang_cc1 -verify -std=c++20 -fsyntax-only %s +// expected-no-diagnostics + +#include "Inputs/std-coroutine.h" + +namespace std::experimental { +// Perfectly fine placement in std::experimental +using std::coroutine_handle; +using std::coroutine_traits; +} // namespace std::experimental + +struct my_awaitable { + bool await_ready() noexcept; + void await_suspend(std::experimental::coroutine_handle<> coro) noexcept; + void await_resume() noexcept; +}; + +struct promise_void { + void get_return_object(); + my_awaitable initial_suspend(); + my_awaitable final_suspend() noexcept; + void return_void(); + void unhandled_exception(); +}; + +template <> +struct std::coroutine_traits { using promise_type = promise_void; }; + +void test() { + co_return; +} Index: clang/test/SemaCXX/coroutine-rvo-exp-namespace.cpp =================================================================== --- clang/test/SemaCXX/coroutine-rvo-exp-namespace.cpp +++ clang/test/SemaCXX/coroutine-rvo-exp-namespace.cpp @@ -30,6 +30,7 @@ template struct coroutine_traits : public traits_sfinae_base {}; +// expected-note@-1{{declared here}} } // namespace std::experimental struct suspend_never { @@ -62,7 +63,7 @@ task local2val() { NoCopyNoMove value; - co_return value; // expected-warning {{Please move from std::experimental::coroutine_traits to std::coroutine_traits}} + co_return value; // expected-warning {{move from std::experimental::coroutine_traits to std::coroutine_traits}} } task local2ref() { Index: clang/test/SemaCXX/coroutine-seh-exp-namespace.cpp =================================================================== --- clang/test/SemaCXX/coroutine-seh-exp-namespace.cpp +++ clang/test/SemaCXX/coroutine-seh-exp-namespace.cpp @@ -1,6 +1,7 @@ // RUN: %clang_cc1 -std=c++1z -fcoroutines-ts -verify %s -fcxx-exceptions -fexceptions -triple x86_64-windows-msvc -fms-extensions namespace std::experimental { template struct coroutine_traits; +// expected-note@-1{{declared here}} template struct coroutine_handle { coroutine_handle() = default; @@ -33,7 +34,7 @@ void SEH_used() { __try { // expected-error {{cannot use SEH '__try' in a coroutine when C++ exceptions are enabled}} co_return; // expected-note {{function is a coroutine due to use of 'co_return' here}} - // expected-warning@-1 {{Please move from std::experimental::coroutine_traits to std::coroutine_traits}} + // expected-warning@-1 {{move from std::experimental::coroutine_traits to std::coroutine_traits}} } __except (0) { } } Index: clang/test/SemaCXX/coroutine-traits-undefined-template-exp-namespace.cpp =================================================================== --- clang/test/SemaCXX/coroutine-traits-undefined-template-exp-namespace.cpp +++ clang/test/SemaCXX/coroutine-traits-undefined-template-exp-namespace.cpp @@ -6,7 +6,7 @@ namespace std { namespace experimental { template -struct coroutine_traits { +struct coroutine_traits { // expected-note{{declared here}} struct promise_type {}; }; @@ -16,5 +16,5 @@ void uses_forward_declaration() { co_return; // expected-error {{this function cannot be a coroutine: missing definition of specialization 'coroutine_traits'}} - // expected-warning@-1 {{Please move from std::experimental::coroutine_traits to std::coroutine_traits}} + // expected-warning@-1 {{move from std::experimental::coroutine_traits to std::coroutine_traits}} } Index: clang/test/SemaCXX/coroutine-unhandled_exception-warning-exp-namespace.cpp =================================================================== --- clang/test/SemaCXX/coroutine-unhandled_exception-warning-exp-namespace.cpp +++ clang/test/SemaCXX/coroutine-unhandled_exception-warning-exp-namespace.cpp @@ -11,6 +11,9 @@ #error This test requires exceptions be disabled #endif +#if !DISABLE_WARNING +#define EXPERIMENTAL_DEPRECATED 1 +#endif #include "Inputs/std-coroutine-exp-namespace.h" using std::experimental::suspend_always; @@ -32,7 +35,7 @@ #ifndef DISABLE_WARNING void test0() { // expected-warning {{'promise_void' is required to declare the member 'unhandled_exception()' when exceptions are enabled}} - co_return; // expected-warning {{Please move from std::experimental::coroutine_traits to std::coroutine_traits}} + co_return; // expected-warning {{move from std::experimental::coroutine_traits to std::coroutine_traits}} } #else void test0() { // expected-no-diagnostics Index: clang/test/SemaCXX/coroutine_handle-address-return-type-exp-namespace.cpp =================================================================== --- clang/test/SemaCXX/coroutine_handle-address-return-type-exp-namespace.cpp +++ clang/test/SemaCXX/coroutine_handle-address-return-type-exp-namespace.cpp @@ -31,6 +31,7 @@ }; template +// expected-note@+1{{declared here}} struct coroutine_traits : public traits_sfinae_base {}; } // namespace std::experimental @@ -66,7 +67,7 @@ } a; task f() { - co_await a; // expected-warning {{Please move from std::experimental::coroutine_traits to std::coroutine_traits}} + co_await a; // expected-warning {{move from std::experimental::coroutine_traits to std::coroutine_traits}} } int main() { Index: clang/test/SemaCXX/coroutines-exp-namespace.cpp =================================================================== --- clang/test/SemaCXX/coroutines-exp-namespace.cpp +++ clang/test/SemaCXX/coroutines-exp-namespace.cpp @@ -45,6 +45,7 @@ template struct coroutine_traits : public traits_sfinae_base {}; +// expected-note@-1{{declared here}} } // namespace experimental } // namespace std @@ -83,7 +84,7 @@ struct DummyVoidTag {}; DummyVoidTag no_specialization() { // expected-error {{this function cannot be a coroutine: 'std::experimental::coroutine_traits' has no member named 'promise_type'}} - co_await a; // expected-warning {{Please move from std::experimental::coroutine_traits to std::coroutine_traits}} + co_await a; // expected-warning {{move from std::experimental::coroutine_traits to std::coroutine_traits}} } template