Skip to content

Commit 2fbd254

Browse files
committed
[Coroutines] [Clang] Look up coroutine component in std namespace first
Summary: Now in libcxx and clang, all the coroutine components are defined in std::experimental namespace. And now the coroutine TS is merged into C++20. So in the working draft like N4892, we could find the coroutine components is defined in std namespace instead of std::experimental namespace. And the coroutine support in clang seems to be relatively stable. So I think it may be suitable to move the coroutine component into the experiment namespace now. But move the coroutine component into the std namespace may be an break change. So I planned to split this change into two patch. One in clang and other in libcxx. This patch would make clang lookup coroutine_traits in std namespace first. For the compatibility consideration, clang would lookup in std::experimental namespace if it can't find definitions in std namespace and emit a warning in this case. So the existing codes wouldn't be break after update compiler. Test Plan: check-clang, check-libcxx Reviewed By: lxfind Differential Revision: https://reviews.llvm.org/D108696
1 parent 6cd4b50 commit 2fbd254

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+1772
-350
lines changed

clang-tools-extra/test/clang-tidy/checkers/Inputs/readability-identifier-naming/system/coroutines.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
#pragma once
22

33
namespace std {
4-
namespace experimental {
54

65
template <typename ret_t, typename... args_t>
76
struct coroutine_traits {
@@ -13,7 +12,6 @@ struct coroutine_handle {
1312
static constexpr coroutine_handle from_address(void *addr) noexcept { return {}; };
1413
};
1514

16-
} // namespace experimental
1715
} // namespace std
1816

1917
struct never_suspend {

clang/docs/LanguageExtensions.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2872,7 +2872,7 @@ C++ Coroutines support builtins
28722872
28732873
Clang provides experimental builtins to support C++ Coroutines as defined by
28742874
https://wg21.link/P0057. The following four are intended to be used by the
2875-
standard library to implement `std::experimental::coroutine_handle` type.
2875+
standard library to implement `std::coroutine_handle` type.
28762876
28772877
**Syntax**:
28782878

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10957,19 +10957,20 @@ def err_coroutine_invalid_func_context : Error<
1095710957
"|a function with a deduced return type|a varargs function"
1095810958
"|a consteval function}0">;
1095910959
def err_implied_coroutine_type_not_found : Error<
10960-
"%0 type was not found; include <experimental/coroutine> before defining "
10961-
"a coroutine">;
10960+
"%0 type was not found; include <coroutine> before defining "
10961+
"a coroutine; include <experimental/coroutine> if your version "
10962+
"of libcxx is less than 14.0">;
1096210963
def err_implicit_coroutine_std_nothrow_type_not_found : Error<
1096310964
"std::nothrow was not found; include <new> before defining a coroutine which "
1096410965
"uses get_return_object_on_allocation_failure()">;
1096510966
def err_malformed_std_nothrow : Error<
1096610967
"std::nothrow must be a valid variable declaration">;
1096710968
def err_malformed_std_coroutine_handle : Error<
10968-
"std::experimental::coroutine_handle must be a class template">;
10969+
"std::coroutine_handle must be a class template">;
1096910970
def err_coroutine_handle_missing_member : Error<
10970-
"std::experimental::coroutine_handle missing a member named '%0'">;
10971+
"std::coroutine_handle missing a member named '%0'">;
1097110972
def err_malformed_std_coroutine_traits : Error<
10972-
"'std::experimental::coroutine_traits' must be a class template">;
10973+
"'std::coroutine_traits' must be a class template">;
1097310974
def err_implied_std_coroutine_traits_promise_type_not_found : Error<
1097410975
"this function cannot be a coroutine: %q0 has no member named 'promise_type'">;
1097510976
def err_implied_std_coroutine_traits_promise_type_not_class : Error<
@@ -11011,6 +11012,10 @@ def note_await_ready_no_bool_conversion : Note<
1101111012
def warn_coroutine_handle_address_invalid_return_type : Warning <
1101211013
"return type of 'coroutine_handle<>::address should be 'void*' (have %0) in order to get capability with existing async C API.">,
1101311014
InGroup<Coroutine>;
11015+
def warn_coroutine_in_legacy_experimental_space : Warning <
11016+
"coroutine_traits defined in std::experimental namespace is deprecated. "
11017+
"Consider updating libcxx and include <coroutine> instead of <experimental/coroutine>.">,
11018+
InGroup<Coroutine>;
1101411019
def err_coroutine_promise_final_suspend_requires_nothrow : Error<
1101511020
"the expression 'co_await __promise.final_suspend()' is required to be non-throwing"
1101611021
>;

clang/include/clang/Sema/Sema.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1128,6 +1128,10 @@ class Sema final {
11281128
/// The C++ "std::coroutine_traits" template, which is defined in
11291129
/// \<coroutine_traits>
11301130
ClassTemplateDecl *StdCoroutineTraitsCache;
1131+
/// The namespace where coroutine components are defined. In standard,
1132+
/// they are defined in std namespace. And in the previous implementation,
1133+
/// they are defined in std::experimental namespace.
1134+
NamespaceDecl *CoroTraitsNamespaceCache;
11311135

11321136
/// The C++ "type_info" declaration, which is defined in \<typeinfo>.
11331137
RecordDecl *CXXTypeInfoDecl;
@@ -5662,6 +5666,7 @@ class Sema final {
56625666
NamespaceDecl *getOrCreateStdNamespace();
56635667

56645668
NamespaceDecl *lookupStdExperimentalNamespace();
5669+
NamespaceDecl *getCachedCoroNamespace() { return CoroTraitsNamespaceCache; }
56655670

56665671
CXXRecordDecl *getStdBadAlloc() const;
56675672
EnumDecl *getStdAlignValT() const;
@@ -10218,8 +10223,11 @@ class Sema final {
1021810223
bool buildCoroutineParameterMoves(SourceLocation Loc);
1021910224
VarDecl *buildCoroutinePromise(SourceLocation Loc);
1022010225
void CheckCompletedCoroutineBody(FunctionDecl *FD, Stmt *&Body);
10226+
/// Lookup 'coroutine_traits' in std namespace and std::experimental
10227+
/// namespace. The namespace found would be recorded in Namespace.
1022110228
ClassTemplateDecl *lookupCoroutineTraits(SourceLocation KwLoc,
10222-
SourceLocation FuncLoc);
10229+
SourceLocation FuncLoc,
10230+
NamespaceDecl *&Namespace);
1022310231
/// Check that the expression co_await promise.final_suspend() shall not be
1022410232
/// potentially-throwing.
1022510233
bool checkFinalSuspendNoThrow(const Stmt *FinalSuspend);

clang/lib/Sema/SemaCoroutine.cpp

Lines changed: 31 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,10 @@ static QualType lookupPromiseType(Sema &S, const FunctionDecl *FD,
5353
SourceLocation KwLoc) {
5454
const FunctionProtoType *FnType = FD->getType()->castAs<FunctionProtoType>();
5555
const SourceLocation FuncLoc = FD->getLocation();
56-
// FIXME: Cache std::coroutine_traits once we've found it.
57-
NamespaceDecl *StdExp = S.lookupStdExperimentalNamespace();
58-
if (!StdExp) {
59-
S.Diag(KwLoc, diag::err_implied_coroutine_type_not_found)
60-
<< "std::experimental::coroutine_traits";
61-
return QualType();
62-
}
6356

64-
ClassTemplateDecl *CoroTraits = S.lookupCoroutineTraits(KwLoc, FuncLoc);
57+
NamespaceDecl *CoroNamespace = nullptr;
58+
ClassTemplateDecl *CoroTraits =
59+
S.lookupCoroutineTraits(KwLoc, FuncLoc, CoroNamespace);
6560
if (!CoroTraits) {
6661
return QualType();
6762
}
@@ -122,7 +117,7 @@ static QualType lookupPromiseType(Sema &S, const FunctionDecl *FD,
122117
QualType PromiseType = S.Context.getTypeDeclType(Promise);
123118

124119
auto buildElaboratedType = [&]() {
125-
auto *NNS = NestedNameSpecifier::Create(S.Context, nullptr, StdExp);
120+
auto *NNS = NestedNameSpecifier::Create(S.Context, nullptr, CoroNamespace);
126121
NNS = NestedNameSpecifier::Create(S.Context, NNS, false,
127122
CoroTrait.getTypePtr());
128123
return S.Context.getElaboratedType(ETK_None, NNS, PromiseType);
@@ -141,20 +136,20 @@ static QualType lookupPromiseType(Sema &S, const FunctionDecl *FD,
141136
return PromiseType;
142137
}
143138

144-
/// Look up the std::experimental::coroutine_handle<PromiseType>.
139+
/// Look up the std::coroutine_handle<PromiseType>.
145140
static QualType lookupCoroutineHandleType(Sema &S, QualType PromiseType,
146141
SourceLocation Loc) {
147142
if (PromiseType.isNull())
148143
return QualType();
149144

150-
NamespaceDecl *StdExp = S.lookupStdExperimentalNamespace();
151-
assert(StdExp && "Should already be diagnosed");
145+
NamespaceDecl *CoroNamespace = S.getCachedCoroNamespace();
146+
assert(CoroNamespace && "Should already be diagnosed");
152147

153148
LookupResult Result(S, &S.PP.getIdentifierTable().get("coroutine_handle"),
154149
Loc, Sema::LookupOrdinaryName);
155-
if (!S.LookupQualifiedName(Result, StdExp)) {
150+
if (!S.LookupQualifiedName(Result, CoroNamespace)) {
156151
S.Diag(Loc, diag::err_implied_coroutine_type_not_found)
157-
<< "std::experimental::coroutine_handle";
152+
<< "std::coroutine_handle";
158153
return QualType();
159154
}
160155

@@ -1000,7 +995,7 @@ static Expr *buildStdNoThrowDeclRef(Sema &S, SourceLocation Loc) {
1000995
LookupResult Result(S, &S.PP.getIdentifierTable().get("nothrow"), Loc,
1001996
Sema::LookupOrdinaryName);
1002997
if (!S.LookupQualifiedName(Result, Std)) {
1003-
// FIXME: <experimental/coroutine> should have been included already.
998+
// FIXME: <coroutine> should have been included already.
1004999
// If we require it to include <new> then this diagnostic is no longer
10051000
// needed.
10061001
S.Diag(Loc, diag::err_implicit_coroutine_std_nothrow_type_not_found);
@@ -1663,25 +1658,32 @@ StmtResult Sema::BuildCoroutineBodyStmt(CoroutineBodyStmt::CtorArgs Args) {
16631658
}
16641659

16651660
ClassTemplateDecl *Sema::lookupCoroutineTraits(SourceLocation KwLoc,
1666-
SourceLocation FuncLoc) {
1661+
SourceLocation FuncLoc,
1662+
NamespaceDecl *&Namespace) {
16671663
if (!StdCoroutineTraitsCache) {
1668-
if (auto StdExp = lookupStdExperimentalNamespace()) {
1669-
LookupResult Result(*this,
1670-
&PP.getIdentifierTable().get("coroutine_traits"),
1671-
FuncLoc, LookupOrdinaryName);
1672-
if (!LookupQualifiedName(Result, StdExp)) {
1664+
NamespaceDecl *CoroNamespace = getStdNamespace();
1665+
LookupResult Result(*this, &PP.getIdentifierTable().get("coroutine_traits"),
1666+
FuncLoc, LookupOrdinaryName);
1667+
if (!CoroNamespace || !LookupQualifiedName(Result, CoroNamespace)) {
1668+
/// TODO: lookup in std::expeirmental namespace for compability.
1669+
/// Remove this once users get familiar with coroutine under std
1670+
/// namespace.
1671+
CoroNamespace = lookupStdExperimentalNamespace();
1672+
if (!CoroNamespace || !LookupQualifiedName(Result, CoroNamespace)) {
16731673
Diag(KwLoc, diag::err_implied_coroutine_type_not_found)
1674-
<< "std::experimental::coroutine_traits";
1675-
return nullptr;
1676-
}
1677-
if (!(StdCoroutineTraitsCache =
1678-
Result.getAsSingle<ClassTemplateDecl>())) {
1679-
Result.suppressDiagnostics();
1680-
NamedDecl *Found = *Result.begin();
1681-
Diag(Found->getLocation(), diag::err_malformed_std_coroutine_traits);
1674+
<< "std::coroutine_traits";
16821675
return nullptr;
16831676
}
1677+
Diag(KwLoc, diag::warn_coroutine_in_legacy_experimental_space);
1678+
}
1679+
if (!(StdCoroutineTraitsCache = Result.getAsSingle<ClassTemplateDecl>())) {
1680+
Result.suppressDiagnostics();
1681+
NamedDecl *Found = *Result.begin();
1682+
Diag(Found->getLocation(), diag::err_malformed_std_coroutine_traits);
1683+
return nullptr;
16841684
}
1685+
CoroTraitsNamespaceCache = CoroNamespace;
16851686
}
1687+
Namespace = CoroTraitsNamespaceCache;
16861688
return StdCoroutineTraitsCache;
16871689
}

clang/test/AST/Inputs/std-coroutine.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
#define STD_COROUTINE_H
44

55
namespace std {
6-
namespace experimental {
76

87
template <typename R, typename...> struct coroutine_traits {
98
using promise_type = typename R::promise_type;
@@ -67,7 +66,6 @@ struct suspend_never {
6766
void await_resume() noexcept {}
6867
};
6968

70-
} // namespace experimental
7169
} // namespace std
7270

7371
#endif // STD_COROUTINE_H

clang/test/AST/coroutine-locals-cleanup.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
#include "Inputs/std-coroutine.h"
44

5-
using namespace std::experimental;
5+
using namespace std;
66

77
struct Task {
88
struct promise_type {

clang/test/AST/coroutine-source-location-crash.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
#include "Inputs/std-coroutine.h"
1313

14-
using namespace std::experimental;
14+
using namespace std;
1515

1616
struct A {
1717
bool await_ready();

clang/test/Analysis/more-dtors-cfg-output.cpp

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -275,32 +275,32 @@ void new_default_ctor_with_default_arg(long count) {
275275
#if CXX2A
276276
// Boilerplate needed to test co_return:
277277

278-
namespace std::experimental {
279-
template <typename Promise>
280-
struct coroutine_handle {
281-
static coroutine_handle from_address(void *) noexcept;
282-
};
283-
}
278+
namespace std {
279+
template <typename Promise>
280+
struct coroutine_handle {
281+
static coroutine_handle from_address(void *) noexcept;
282+
};
283+
} // namespace std
284284

285285
struct TestPromise {
286286
TestPromise initial_suspend();
287287
TestPromise final_suspend() noexcept;
288288
bool await_ready() noexcept;
289-
void await_suspend(const std::experimental::coroutine_handle<TestPromise> &) noexcept;
289+
void await_suspend(const std::coroutine_handle<TestPromise> &) noexcept;
290290
void await_resume() noexcept;
291291
Foo return_value(const Bar &);
292292
Bar get_return_object();
293293
void unhandled_exception();
294294
};
295295

296-
namespace std::experimental {
297-
template <typename Ret, typename... Args>
298-
struct coroutine_traits;
299-
template <>
300-
struct coroutine_traits<Bar> {
301-
using promise_type = TestPromise;
302-
};
303-
}
296+
namespace std {
297+
template <typename Ret, typename... Args>
298+
struct coroutine_traits;
299+
template <>
300+
struct coroutine_traits<Bar> {
301+
using promise_type = TestPromise;
302+
};
303+
} // namespace std
304304

305305
Bar coreturn() {
306306
co_return get_bar();

clang/test/CodeGenCXX/ubsan-coroutines.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
// llvm.coro.* intrinsics have not yet been ported.
55
// RUN: %clang_cc1 -fno-experimental-new-pass-manager -emit-obj -std=c++2a -fsanitize=null %s -o %t.o
66

7-
namespace std::experimental {
7+
namespace std {
88
template <typename R, typename... T> struct coroutine_traits {
99
using promise_type = typename R::promise_type;
1010
};
@@ -20,11 +20,11 @@ template <class Promise> struct coroutine_handle : coroutine_handle<void> {
2020
coroutine_handle() = default;
2121
static coroutine_handle from_address(void *) noexcept;
2222
};
23-
}
23+
} // namespace std
2424

2525
struct suspend_always {
2626
bool await_ready() noexcept;
27-
void await_suspend(std::experimental::coroutine_handle<>) noexcept;
27+
void await_suspend(std::coroutine_handle<>) noexcept;
2828
void await_resume() noexcept;
2929
};
3030

@@ -41,7 +41,7 @@ struct task {
4141
struct awaitable {
4242
task await() { (void)co_await *this; }
4343
bool await_ready() { return false; }
44-
bool await_suspend(std::experimental::coroutine_handle<> awaiter) { return false; }
44+
bool await_suspend(std::coroutine_handle<> awaiter) { return false; }
4545
bool await_resume() { return false; }
4646
};
4747

clang/test/CodeGenCoroutines/Inputs/coroutine.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#pragma once
22

3-
namespace std { namespace experimental { inline namespace coroutines_v1 {
3+
namespace std {
44

55
template <typename R, typename...> struct coroutine_traits {
66
using promise_type = typename R::promise_type;
@@ -77,4 +77,4 @@ struct suspend_never {
7777
void await_resume() noexcept {}
7878
};
7979

80-
}}}
80+
} // namespace std

0 commit comments

Comments
 (0)