[clang-tools-extra] ec11715 - [Coroutines] [Frontend] Lookup in std namespace first
Chuanqi Xu via cfe-commits
cfe-commits at lists.llvm.org
Wed Nov 3 20:54:04 PDT 2021
Author: Chuanqi Xu
Date: 2021-11-04T11:53:47+08:00
New Revision: ec117158a390a0ebf64377caa5abd0c976df8f7a
URL: https://github.com/llvm/llvm-project/commit/ec117158a390a0ebf64377caa5abd0c976df8f7a
DIFF: https://github.com/llvm/llvm-project/commit/ec117158a390a0ebf64377caa5abd0c976df8f7a.diff
LOG: [Coroutines] [Frontend] Lookup in std namespace first
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.
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. So the existing codes wouldn't be break after update
compiler.
And in case the compiler found std::coroutine_traits and
std::experimental::coroutine_traits at the same time, it would emit an
error for it.
The support for looking up std::experimental::coroutine_traits would be
removed in Clang16.
Reviewed By: lxfind, Quuxplusone
Differential Revision: https://reviews.llvm.org/D108696
Added:
clang/test/AST/Inputs/std-coroutine-exp-namespace.h
clang/test/AST/coroutine-locals-cleanup-exp-namespace.cpp
clang/test/AST/coroutine-source-location-crash-exp-namespace.cpp
clang/test/CodeGenCoroutines/Inputs/coroutine-exp-namespace.h
clang/test/CodeGenCoroutines/coro-alloc-exp-namespace.cpp
clang/test/CodeGenCoroutines/coro-always-inline-exp-namespace.cpp
clang/test/CodeGenCoroutines/coro-await-domination-exp-namespace.cpp
clang/test/CodeGenCoroutines/coro-await-exp-namespace.cpp
clang/test/CodeGenCoroutines/coro-await-resume-eh-exp-namespace.cpp
clang/test/CodeGenCoroutines/coro-cleanup-exp-namespace.cpp
clang/test/CodeGenCoroutines/coro-dest-slot-exp-namespace.cpp
clang/test/CodeGenCoroutines/coro-dwarf-exp-namespace.cpp
clang/test/CodeGenCoroutines/coro-eh-cleanup-exp-namespace.cpp
clang/test/CodeGenCoroutines/coro-gro-exp-namespace.cpp
clang/test/CodeGenCoroutines/coro-gro-nrvo-exp-namespace.cpp
clang/test/CodeGenCoroutines/coro-lambda-exp-namespace.cpp
clang/test/CodeGenCoroutines/coro-newpm-pipeline-exp-namespace.cpp
clang/test/CodeGenCoroutines/coro-params-exp-namespace.cpp
clang/test/CodeGenCoroutines/coro-promise-dtor-exp-namespace.cpp
clang/test/CodeGenCoroutines/coro-ret-void-exp-namespace.cpp
clang/test/CodeGenCoroutines/coro-return-exp-namespace.cpp
clang/test/CodeGenCoroutines/coro-return-voidtype-initlist-exp-namespace.cpp
clang/test/CodeGenCoroutines/coro-symmetric-transfer-01-exp-namespace.cpp
clang/test/CodeGenCoroutines/coro-symmetric-transfer-02-exp-namespace.cpp
clang/test/CodeGenCoroutines/coro-unhandled-exception-exp-namespace.cpp
clang/test/CodeGenCoroutines/microsoft-abi-operator-coawait-exp-namespace.cpp
clang/test/SemaCXX/Inputs/std-coroutine-exp-namespace.h
clang/test/SemaCXX/co_await-range-for-exp-namespace.cpp
clang/test/SemaCXX/coreturn-eh-exp-namespace.cpp
clang/test/SemaCXX/coreturn-exp-namespace.cpp
clang/test/SemaCXX/coroutine-final-suspend-noexcept-exp-namespace.cpp
clang/test/SemaCXX/coroutine-mixed-exp-namespace.cpp
clang/test/SemaCXX/coroutine-mixed2-exp-namespace.cpp
clang/test/SemaCXX/coroutine-rvo-exp-namespace.cpp
clang/test/SemaCXX/coroutine-seh-exp-namespace.cpp
clang/test/SemaCXX/coroutine-traits-undefined-template-exp-namespace.cpp
clang/test/SemaCXX/coroutine-unhandled_exception-warning-exp-namespace.cpp
clang/test/SemaCXX/coroutine-uninitialized-warning-crash-exp-namespace.cpp
clang/test/SemaCXX/coroutine_handle-address-return-type-exp-namespace.cpp
clang/test/SemaCXX/coroutine_handle-address-return-type.cpp
clang/test/SemaCXX/coroutines-exp-namespace.cpp
Modified:
clang-tools-extra/test/clang-tidy/checkers/Inputs/readability-identifier-naming/system/coroutines.h
clang/docs/LanguageExtensions.rst
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/include/clang/Sema/Sema.h
clang/lib/Frontend/InitPreprocessor.cpp
clang/lib/Sema/SemaCoroutine.cpp
clang/test/AST/Inputs/std-coroutine.h
clang/test/AST/coroutine-locals-cleanup.cpp
clang/test/AST/coroutine-source-location-crash.cpp
clang/test/Analysis/more-dtors-cfg-output.cpp
clang/test/CodeGenCXX/ubsan-coroutines.cpp
clang/test/CodeGenCoroutines/Inputs/coroutine.h
clang/test/CodeGenCoroutines/coro-alloc.cpp
clang/test/CodeGenCoroutines/coro-always-inline.cpp
clang/test/CodeGenCoroutines/coro-await-domination.cpp
clang/test/CodeGenCoroutines/coro-await-resume-eh.cpp
clang/test/CodeGenCoroutines/coro-await.cpp
clang/test/CodeGenCoroutines/coro-cleanup.cpp
clang/test/CodeGenCoroutines/coro-dest-slot.cpp
clang/test/CodeGenCoroutines/coro-dwarf.cpp
clang/test/CodeGenCoroutines/coro-eh-cleanup.cpp
clang/test/CodeGenCoroutines/coro-gro-nrvo.cpp
clang/test/CodeGenCoroutines/coro-gro.cpp
clang/test/CodeGenCoroutines/coro-lambda.cpp
clang/test/CodeGenCoroutines/coro-newpm-pipeline.cpp
clang/test/CodeGenCoroutines/coro-params.cpp
clang/test/CodeGenCoroutines/coro-promise-dtor.cpp
clang/test/CodeGenCoroutines/coro-ret-void.cpp
clang/test/CodeGenCoroutines/coro-return-voidtype-initlist.cpp
clang/test/CodeGenCoroutines/coro-return.cpp
clang/test/CodeGenCoroutines/coro-symmetric-transfer-01.cpp
clang/test/CodeGenCoroutines/coro-symmetric-transfer-02.cpp
clang/test/CodeGenCoroutines/coro-unhandled-exception.cpp
clang/test/CodeGenCoroutines/microsoft-abi-operator-coawait.cpp
clang/test/CoverageMapping/coroutine.cpp
clang/test/Index/coroutines.cpp
clang/test/Lexer/cxx-features.cpp
clang/test/PCH/coroutines.cpp
clang/test/SemaCXX/Inputs/std-coroutine.h
clang/test/SemaCXX/co_await-range-for.cpp
clang/test/SemaCXX/coreturn-eh.cpp
clang/test/SemaCXX/coreturn.cpp
clang/test/SemaCXX/coroutine-builtins.cpp
clang/test/SemaCXX/coroutine-final-suspend-noexcept.cpp
clang/test/SemaCXX/coroutine-rvo.cpp
clang/test/SemaCXX/coroutine-seh.cpp
clang/test/SemaCXX/coroutine-traits-undefined-template.cpp
clang/test/SemaCXX/coroutine-unhandled_exception-warning.cpp
clang/test/SemaCXX/coroutine-uninitialized-warning-crash.cpp
clang/test/SemaCXX/coroutines.cpp
clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
Removed:
clang/test/SemaCXX/coroutine_handle-addres-return-type.cpp
################################################################################
diff --git a/clang-tools-extra/test/clang-tidy/checkers/Inputs/readability-identifier-naming/system/coroutines.h b/clang-tools-extra/test/clang-tidy/checkers/Inputs/readability-identifier-naming/system/coroutines.h
index b38da9999c52..ed4373394351 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/Inputs/readability-identifier-naming/system/coroutines.h
+++ b/clang-tools-extra/test/clang-tidy/checkers/Inputs/readability-identifier-naming/system/coroutines.h
@@ -1,7 +1,6 @@
#pragma once
namespace std {
-namespace experimental {
template <typename ret_t, typename... args_t>
struct coroutine_traits {
@@ -13,7 +12,6 @@ struct coroutine_handle {
static constexpr coroutine_handle from_address(void *addr) noexcept { return {}; };
};
-} // namespace experimental
} // namespace std
struct never_suspend {
diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index da2c90778ef4..e3df9fd09960 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -2947,7 +2947,7 @@ C++ Coroutines support builtins
Clang provides experimental builtins to support C++ Coroutines as defined by
https://wg21.link/P0057. The following four are intended to be used by the
-standard library to implement `std::experimental::coroutine_handle` type.
+standard library to implement the ``std::coroutine_handle`` type.
**Syntax**:
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 1f2f84bdee4b..1daea69e791c 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11003,19 +11003,25 @@ def err_coroutine_invalid_func_context : Error<
"|a function with a deduced return type|a varargs function"
"|a consteval function}0">;
def err_implied_coroutine_type_not_found : Error<
- "%0 type was not found; include <experimental/coroutine> before defining "
- "a coroutine">;
+ "%0 type was not found; include <coroutine> before defining "
+ "a coroutine; include <experimental/coroutine> if your version "
+ "of libcxx is less than 14.0">;
+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 err_implicit_coroutine_std_nothrow_type_not_found : Error<
"std::nothrow was not found; include <new> before defining a coroutine which "
"uses get_return_object_on_allocation_failure()">;
def err_malformed_std_nothrow : Error<
"std::nothrow must be a valid variable declaration">;
def err_malformed_std_coroutine_handle : Error<
- "std::experimental::coroutine_handle must be a class template">;
+ "std::coroutine_handle isn't a class template">;
def err_coroutine_handle_missing_member : Error<
- "std::experimental::coroutine_handle missing a member named '%0'">;
+ "std::coroutine_handle must have a member named '%0'">;
def err_malformed_std_coroutine_traits : Error<
- "'std::experimental::coroutine_traits' must be a class template">;
+ "std::coroutine_traits isn't a class template">;
def err_implied_std_coroutine_traits_promise_type_not_found : Error<
"this function cannot be a coroutine: %q0 has no member named 'promise_type'">;
def err_implied_std_coroutine_traits_promise_type_not_class : Error<
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 909328b164d5..62f6dcc8966b 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -1124,6 +1124,10 @@ class Sema final {
/// The C++ "std::coroutine_traits" template, which is defined in
/// \<coroutine_traits>
ClassTemplateDecl *StdCoroutineTraitsCache;
+ /// The namespace where coroutine components are defined. In standard,
+ /// they are defined in std namespace. And in the previous implementation,
+ /// they are defined in std::experimental namespace.
+ NamespaceDecl *CoroTraitsNamespaceCache;
/// The C++ "type_info" declaration, which is defined in \<typeinfo>.
RecordDecl *CXXTypeInfoDecl;
@@ -5696,6 +5700,7 @@ class Sema final {
NamespaceDecl *getOrCreateStdNamespace();
NamespaceDecl *lookupStdExperimentalNamespace();
+ NamespaceDecl *getCachedCoroNamespace() { return CoroTraitsNamespaceCache; }
CXXRecordDecl *getStdBadAlloc() const;
EnumDecl *getStdAlignValT() const;
@@ -10265,8 +10270,11 @@ class Sema final {
bool buildCoroutineParameterMoves(SourceLocation Loc);
VarDecl *buildCoroutinePromise(SourceLocation Loc);
void CheckCompletedCoroutineBody(FunctionDecl *FD, Stmt *&Body);
+ /// Lookup 'coroutine_traits' in std namespace and std::experimental
+ /// namespace. The namespace found is recorded in Namespace.
ClassTemplateDecl *lookupCoroutineTraits(SourceLocation KwLoc,
- SourceLocation FuncLoc);
+ SourceLocation FuncLoc,
+ NamespaceDecl *&Namespace);
/// Check that the expression co_await promise.final_suspend() shall not be
/// potentially-throwing.
bool checkFinalSuspendNoThrow(const Stmt *FinalSuspend);
diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp
index a3e1ca5d5226..002456867b0a 100644
--- a/clang/lib/Frontend/InitPreprocessor.cpp
+++ b/clang/lib/Frontend/InitPreprocessor.cpp
@@ -600,7 +600,7 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts,
//Builder.defineMacro("__cpp_consteval", "201811L");
Builder.defineMacro("__cpp_constexpr_dynamic_alloc", "201907L");
Builder.defineMacro("__cpp_constinit", "201907L");
- //Builder.defineMacro("__cpp_coroutines", "201902L");
+ Builder.defineMacro("__cpp_impl_coroutine", "201902L");
Builder.defineMacro("__cpp_designated_initializers", "201707L");
Builder.defineMacro("__cpp_impl_three_way_comparison", "201907L");
//Builder.defineMacro("__cpp_modules", "201907L");
diff --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp
index 3d1899a57c72..323e425585ce 100644
--- a/clang/lib/Sema/SemaCoroutine.cpp
+++ b/clang/lib/Sema/SemaCoroutine.cpp
@@ -53,15 +53,10 @@ static QualType lookupPromiseType(Sema &S, const FunctionDecl *FD,
SourceLocation KwLoc) {
const FunctionProtoType *FnType = FD->getType()->castAs<FunctionProtoType>();
const SourceLocation FuncLoc = FD->getLocation();
- // FIXME: Cache std::coroutine_traits once we've found it.
- NamespaceDecl *StdExp = S.lookupStdExperimentalNamespace();
- if (!StdExp) {
- S.Diag(KwLoc, diag::err_implied_coroutine_type_not_found)
- << "std::experimental::coroutine_traits";
- return QualType();
- }
- ClassTemplateDecl *CoroTraits = S.lookupCoroutineTraits(KwLoc, FuncLoc);
+ NamespaceDecl *CoroNamespace = nullptr;
+ ClassTemplateDecl *CoroTraits =
+ S.lookupCoroutineTraits(KwLoc, FuncLoc, CoroNamespace);
if (!CoroTraits) {
return QualType();
}
@@ -122,7 +117,7 @@ static QualType lookupPromiseType(Sema &S, const FunctionDecl *FD,
QualType PromiseType = S.Context.getTypeDeclType(Promise);
auto buildElaboratedType = [&]() {
- auto *NNS = NestedNameSpecifier::Create(S.Context, nullptr, StdExp);
+ auto *NNS = NestedNameSpecifier::Create(S.Context, nullptr, CoroNamespace);
NNS = NestedNameSpecifier::Create(S.Context, NNS, false,
CoroTrait.getTypePtr());
return S.Context.getElaboratedType(ETK_None, NNS, PromiseType);
@@ -141,20 +136,20 @@ static QualType lookupPromiseType(Sema &S, const FunctionDecl *FD,
return PromiseType;
}
-/// Look up the std::experimental::coroutine_handle<PromiseType>.
+/// Look up the std::coroutine_handle<PromiseType>.
static QualType lookupCoroutineHandleType(Sema &S, QualType PromiseType,
SourceLocation Loc) {
if (PromiseType.isNull())
return QualType();
- NamespaceDecl *StdExp = S.lookupStdExperimentalNamespace();
- assert(StdExp && "Should already be diagnosed");
+ NamespaceDecl *CoroNamespace = S.getCachedCoroNamespace();
+ assert(CoroNamespace && "Should already be diagnosed");
LookupResult Result(S, &S.PP.getIdentifierTable().get("coroutine_handle"),
Loc, Sema::LookupOrdinaryName);
- if (!S.LookupQualifiedName(Result, StdExp)) {
+ if (!S.LookupQualifiedName(Result, CoroNamespace)) {
S.Diag(Loc, diag::err_implied_coroutine_type_not_found)
- << "std::experimental::coroutine_handle";
+ << "std::coroutine_handle";
return QualType();
}
@@ -1000,7 +995,7 @@ static Expr *buildStdNoThrowDeclRef(Sema &S, SourceLocation Loc) {
LookupResult Result(S, &S.PP.getIdentifierTable().get("nothrow"), Loc,
Sema::LookupOrdinaryName);
if (!S.LookupQualifiedName(Result, Std)) {
- // FIXME: <experimental/coroutine> should have been included already.
+ // FIXME: <coroutine> should have been included already.
// If we require it to include <new> then this diagnostic is no longer
// needed.
S.Diag(Loc, diag::err_implicit_coroutine_std_nothrow_type_not_found);
@@ -1663,25 +1658,47 @@ StmtResult Sema::BuildCoroutineBodyStmt(CoroutineBodyStmt::CtorArgs Args) {
}
ClassTemplateDecl *Sema::lookupCoroutineTraits(SourceLocation KwLoc,
- SourceLocation FuncLoc) {
+ SourceLocation FuncLoc,
+ NamespaceDecl *&Namespace) {
if (!StdCoroutineTraitsCache) {
- if (auto StdExp = lookupStdExperimentalNamespace()) {
- LookupResult Result(*this,
- &PP.getIdentifierTable().get("coroutine_traits"),
- FuncLoc, LookupOrdinaryName);
- if (!LookupQualifiedName(Result, StdExp)) {
+ 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 <experimental/coroutine> is
+ /// removed.
+ CoroNamespace = lookupStdExperimentalNamespace();
+ if (!CoroNamespace || !LookupQualifiedName(Result, CoroNamespace)) {
Diag(KwLoc, diag::err_implied_coroutine_type_not_found)
- << "std::experimental::coroutine_traits";
+ << "std::coroutine_traits";
return nullptr;
}
- if (!(StdCoroutineTraitsCache =
- Result.getAsSingle<ClassTemplateDecl>())) {
- Result.suppressDiagnostics();
- NamedDecl *Found = *Result.begin();
- Diag(Found->getLocation(), diag::err_malformed_std_coroutine_traits);
+ /// TODO: Add a warning about not including <experimental/coroutine>
+ /// once we update libcxx.
+ } 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;
}
}
+
+ if (!(StdCoroutineTraitsCache = Result.getAsSingle<ClassTemplateDecl>())) {
+ Result.suppressDiagnostics();
+ NamedDecl *Found = *Result.begin();
+ Diag(Found->getLocation(), diag::err_malformed_std_coroutine_traits);
+ return nullptr;
+ }
+ CoroTraitsNamespaceCache = CoroNamespace;
}
+ Namespace = CoroTraitsNamespaceCache;
return StdCoroutineTraitsCache;
}
diff --git a/clang/test/AST/Inputs/std-coroutine-exp-namespace.h b/clang/test/AST/Inputs/std-coroutine-exp-namespace.h
new file mode 100644
index 000000000000..8234a19cba13
--- /dev/null
+++ b/clang/test/AST/Inputs/std-coroutine-exp-namespace.h
@@ -0,0 +1,71 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin9 %s -std=c++14 -fcoroutines-ts -fsyntax-only -Wignored-qualifiers -Wno-error=return-type -verify -fblocks -Wno-unreachable-code -Wno-unused-value
+#ifndef STD_COROUTINE_H
+#define STD_COROUTINE_H
+
+namespace std {
+namespace experimental {
+template <typename R, typename...> struct coroutine_traits {
+ using promise_type = typename R::promise_type;
+};
+
+template <typename Promise = void> struct coroutine_handle;
+
+template <> struct coroutine_handle<void> {
+ static coroutine_handle from_address(void *addr) noexcept {
+ coroutine_handle me;
+ me.ptr = addr;
+ return me;
+ }
+ void operator()() { resume(); }
+ void *address() const noexcept { return ptr; }
+ void resume() const { __builtin_coro_resume(ptr); }
+ void destroy() const { __builtin_coro_destroy(ptr); }
+ bool done() const { return __builtin_coro_done(ptr); }
+ coroutine_handle &operator=(decltype(nullptr)) {
+ ptr = nullptr;
+ return *this;
+ }
+ coroutine_handle(decltype(nullptr)) : ptr(nullptr) {}
+ coroutine_handle() : ptr(nullptr) {}
+ // void reset() { ptr = nullptr; } // add to P0057?
+ explicit operator bool() const { return ptr; }
+
+protected:
+ void *ptr;
+};
+
+template <typename Promise> struct coroutine_handle : coroutine_handle<> {
+ using coroutine_handle<>::operator=;
+
+ static coroutine_handle from_address(void *addr) noexcept {
+ coroutine_handle me;
+ me.ptr = addr;
+ return me;
+ }
+
+ Promise &promise() const {
+ return *reinterpret_cast<Promise *>(
+ __builtin_coro_promise(ptr, alignof(Promise), false));
+ }
+ static coroutine_handle from_promise(Promise &promise) {
+ coroutine_handle p;
+ p.ptr = __builtin_coro_promise(&promise, alignof(Promise), true);
+ return p;
+ }
+};
+
+struct suspend_always {
+ bool await_ready() { return false; }
+ void await_suspend(coroutine_handle<>) {}
+ void await_resume() {}
+};
+
+struct suspend_never {
+ bool await_ready() noexcept { return true; }
+ void await_suspend(coroutine_handle<>) noexcept {}
+ void await_resume() noexcept {}
+};
+} // namespace experimental
+} // namespace std
+
+#endif // STD_COROUTINE_H
diff --git a/clang/test/AST/Inputs/std-coroutine.h b/clang/test/AST/Inputs/std-coroutine.h
index 5a1498f00494..98ddb91a6008 100644
--- a/clang/test/AST/Inputs/std-coroutine.h
+++ b/clang/test/AST/Inputs/std-coroutine.h
@@ -1,9 +1,8 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin9 %s -std=c++14 -fcoroutines-ts -fsyntax-only -Wignored-qualifiers -Wno-error=return-type -verify -fblocks -Wno-unreachable-code -Wno-unused-value
+// RUN: %clang_cc1 -triple x86_64-apple-darwin9 %s -std=c++20 -fcoroutines-ts -fsyntax-only -Wignored-qualifiers -Wno-error=return-type -verify -fblocks -Wno-unreachable-code -Wno-unused-value
#ifndef STD_COROUTINE_H
#define STD_COROUTINE_H
namespace std {
-namespace experimental {
template <typename R, typename...> struct coroutine_traits {
using promise_type = typename R::promise_type;
@@ -67,7 +66,6 @@ struct suspend_never {
void await_resume() noexcept {}
};
-} // namespace experimental
} // namespace std
#endif // STD_COROUTINE_H
diff --git a/clang/test/AST/coroutine-locals-cleanup-exp-namespace.cpp b/clang/test/AST/coroutine-locals-cleanup-exp-namespace.cpp
new file mode 100644
index 000000000000..048c6778bd05
--- /dev/null
+++ b/clang/test/AST/coroutine-locals-cleanup-exp-namespace.cpp
@@ -0,0 +1,107 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fcoroutines-ts -std=c++14 -fsyntax-only -ast-dump %s | FileCheck %s
+
+#include "Inputs/std-coroutine-exp-namespace.h"
+
+using namespace std::experimental;
+
+struct Task {
+ struct promise_type {
+ Task get_return_object() noexcept {
+ return Task{coroutine_handle<promise_type>::from_promise(*this)};
+ }
+
+ void return_void() noexcept {}
+
+ struct final_awaiter {
+ bool await_ready() noexcept { return false; }
+ coroutine_handle<> await_suspend(coroutine_handle<promise_type> h) noexcept {
+ h.destroy();
+ return {};
+ }
+ void await_resume() noexcept {}
+ };
+
+ void unhandled_exception() noexcept {}
+
+ final_awaiter final_suspend() noexcept { return {}; }
+
+ suspend_always initial_suspend() noexcept { return {}; }
+
+ template <typename Awaitable>
+ auto await_transform(Awaitable &&awaitable) {
+ return awaitable.co_viaIfAsync();
+ }
+ };
+
+ using handle_t = coroutine_handle<promise_type>;
+
+ class Awaiter {
+ public:
+ explicit Awaiter(handle_t coro) noexcept;
+ Awaiter(Awaiter &&other) noexcept;
+ Awaiter(const Awaiter &) = delete;
+ ~Awaiter();
+
+ bool await_ready() noexcept { return false; }
+ handle_t await_suspend(coroutine_handle<> continuation) noexcept;
+ void await_resume();
+
+ private:
+ handle_t coro_;
+ };
+
+ Task(handle_t coro) noexcept : coro_(coro) {}
+
+ handle_t coro_;
+
+ Task(const Task &t) = delete;
+ Task(Task &&t) noexcept;
+ ~Task();
+ Task &operator=(Task t) noexcept;
+
+ Awaiter co_viaIfAsync();
+};
+
+static Task foo() {
+ co_return;
+}
+
+Task bar() {
+ auto mode = 2;
+ switch (mode) {
+ case 1:
+ co_await foo();
+ break;
+ case 2:
+ co_await foo();
+ break;
+ default:
+ break;
+ }
+}
+
+// CHECK-LABEL: FunctionDecl {{.*}} bar 'Task ()'
+// CHECK: SwitchStmt
+// CHECK: CaseStmt
+// CHECK: ExprWithCleanups {{.*}} 'void'
+// CHECK-NEXT: CoawaitExpr
+// CHECK-NEXT: MaterializeTemporaryExpr {{.*}} 'Task::Awaiter':'Task::Awaiter'
+// CHECK: ExprWithCleanups {{.*}} 'bool'
+// CHECK-NEXT: CXXMemberCallExpr {{.*}} 'bool'
+// CHECK-NEXT: MemberExpr {{.*}} .await_ready
+// CHECK: CallExpr {{.*}} 'void'
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(void *)'
+// CHECK-NEXT: DeclRefExpr {{.*}} '__builtin_coro_resume' 'void (void *)'
+// CHECK-NEXT: ExprWithCleanups {{.*}} 'void *'
+
+// CHECK: CaseStmt
+// CHECK: ExprWithCleanups {{.*}} 'void'
+// CHECK-NEXT: CoawaitExpr
+// CHECK-NEXT: MaterializeTemporaryExpr {{.*}} 'Task::Awaiter':'Task::Awaiter'
+// CHECK: ExprWithCleanups {{.*}} 'bool'
+// CHECK-NEXT: CXXMemberCallExpr {{.*}} 'bool'
+// CHECK-NEXT: MemberExpr {{.*}} .await_ready
+// CHECK: CallExpr {{.*}} 'void'
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(void *)'
+// CHECK-NEXT: DeclRefExpr {{.*}} '__builtin_coro_resume' 'void (void *)'
+// CHECK-NEXT: ExprWithCleanups {{.*}} 'void *'
diff --git a/clang/test/AST/coroutine-locals-cleanup.cpp b/clang/test/AST/coroutine-locals-cleanup.cpp
index 5e993ad323f8..4e2fe6275de7 100644
--- a/clang/test/AST/coroutine-locals-cleanup.cpp
+++ b/clang/test/AST/coroutine-locals-cleanup.cpp
@@ -1,8 +1,8 @@
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fcoroutines-ts -std=c++14 -fsyntax-only -ast-dump %s | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -fsyntax-only -ast-dump %s | FileCheck %s
#include "Inputs/std-coroutine.h"
-using namespace std::experimental;
+using namespace std;
struct Task {
struct promise_type {
diff --git a/clang/test/AST/coroutine-source-location-crash-exp-namespace.cpp b/clang/test/AST/coroutine-source-location-crash-exp-namespace.cpp
new file mode 100644
index 000000000000..9995dee542e9
--- /dev/null
+++ b/clang/test/AST/coroutine-source-location-crash-exp-namespace.cpp
@@ -0,0 +1,42 @@
+// Test without serialization:
+// RUN: %clang_cc1 -triple x86_64-apple-darwin9 %s -std=c++14 -fcoroutines-ts \
+// RUN: -fsyntax-only -ast-dump | FileCheck %s
+//
+// Test with serialization:
+// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -std=c++14 -fcoroutines-ts -emit-pch -o %t %s
+// RUN: %clang_cc1 -x c++ -triple x86_64-apple-darwin9 -std=c++14 -fcoroutines-ts -include-pch %t \
+// RUN: -ast-dump-all /dev/null \
+// RUN: | sed -e "s/ <undeserialized declarations>//" -e "s/ imported//" \
+// RUN: | FileCheck %s
+
+#include "Inputs/std-coroutine-exp-namespace.h"
+
+using namespace std::experimental;
+
+struct A {
+ bool await_ready();
+ void await_resume();
+ template <typename F>
+ void await_suspend(F);
+};
+
+struct coro_t {
+ struct promise_type {
+ coro_t get_return_object();
+ suspend_never initial_suspend();
+ suspend_never final_suspend() noexcept;
+ void return_void();
+ static void unhandled_exception();
+ };
+};
+
+// {{0x[0-9a-fA-F]+}} <line:[[@LINE+1]]:1, col:36>
+// CHECK-LABEL: FunctionDecl {{.*}} f 'coro_t (int)'
+coro_t f(int n) {
+ A a{};
+ // CHECK: CoawaitExpr {{0x[0-9a-fA-F]+}} <col:3, col:12>
+ // CHECK-NEXT: DeclRefExpr {{0x[0-9a-fA-F]+}} <col:12>
+ // CHECK-NEXT: CXXMemberCallExpr {{0x[0-9a-fA-F]+}} <col:12>
+ // CHECK-NEXT: MemberExpr {{0x[0-9a-fA-F]+}} <col:12>
+ co_await a;
+}
diff --git a/clang/test/AST/coroutine-source-location-crash.cpp b/clang/test/AST/coroutine-source-location-crash.cpp
index 6c0184d2076d..9b18dc817fb5 100644
--- a/clang/test/AST/coroutine-source-location-crash.cpp
+++ b/clang/test/AST/coroutine-source-location-crash.cpp
@@ -1,17 +1,17 @@
// Test without serialization:
-// RUN: %clang_cc1 -triple x86_64-apple-darwin9 %s -std=c++14 -fcoroutines-ts \
+// RUN: %clang_cc1 -triple x86_64-apple-darwin9 %s -std=c++20 \
// RUN: -fsyntax-only -ast-dump | FileCheck %s
//
// Test with serialization:
-// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -std=c++14 -fcoroutines-ts -emit-pch -o %t %s
-// RUN: %clang_cc1 -x c++ -triple x86_64-apple-darwin9 -std=c++14 -fcoroutines-ts -include-pch %t \
+// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -std=c++20 -emit-pch -o %t %s
+// RUN: %clang_cc1 -x c++ -triple x86_64-apple-darwin9 -std=c++20 -include-pch %t \
// RUN: -ast-dump-all /dev/null \
// RUN: | sed -e "s/ <undeserialized declarations>//" -e "s/ imported//" \
// RUN: | FileCheck %s
#include "Inputs/std-coroutine.h"
-using namespace std::experimental;
+using namespace std;
struct A {
bool await_ready();
diff --git a/clang/test/Analysis/more-dtors-cfg-output.cpp b/clang/test/Analysis/more-dtors-cfg-output.cpp
index 964c6d94d378..21824506c73c 100644
--- a/clang/test/Analysis/more-dtors-cfg-output.cpp
+++ b/clang/test/Analysis/more-dtors-cfg-output.cpp
@@ -275,32 +275,32 @@ void new_default_ctor_with_default_arg(long count) {
#if CXX2A
// Boilerplate needed to test co_return:
-namespace std::experimental {
- template <typename Promise>
- struct coroutine_handle {
- static coroutine_handle from_address(void *) noexcept;
- };
-}
+namespace std {
+template <typename Promise>
+struct coroutine_handle {
+ static coroutine_handle from_address(void *) noexcept;
+};
+} // namespace std
struct TestPromise {
TestPromise initial_suspend();
TestPromise final_suspend() noexcept;
bool await_ready() noexcept;
- void await_suspend(const std::experimental::coroutine_handle<TestPromise> &) noexcept;
+ void await_suspend(const std::coroutine_handle<TestPromise> &) noexcept;
void await_resume() noexcept;
Foo return_value(const Bar &);
Bar get_return_object();
void unhandled_exception();
};
-namespace std::experimental {
- template <typename Ret, typename... Args>
- struct coroutine_traits;
- template <>
- struct coroutine_traits<Bar> {
- using promise_type = TestPromise;
- };
-}
+namespace std {
+template <typename Ret, typename... Args>
+struct coroutine_traits;
+template <>
+struct coroutine_traits<Bar> {
+ using promise_type = TestPromise;
+};
+} // namespace std
Bar coreturn() {
co_return get_bar();
diff --git a/clang/test/CodeGenCXX/ubsan-coroutines.cpp b/clang/test/CodeGenCXX/ubsan-coroutines.cpp
index 8ce15ee6ae73..04ab0505f140 100644
--- a/clang/test/CodeGenCXX/ubsan-coroutines.cpp
+++ b/clang/test/CodeGenCXX/ubsan-coroutines.cpp
@@ -2,7 +2,7 @@
// crash when the LLVM coroutines passes are run.
// RUN: %clang_cc1 -emit-obj -std=c++2a -fsanitize=null %s -o %t.o
-namespace std::experimental {
+namespace std {
template <typename R, typename... T> struct coroutine_traits {
using promise_type = typename R::promise_type;
};
@@ -18,11 +18,11 @@ template <class Promise> struct coroutine_handle : coroutine_handle<void> {
coroutine_handle() = default;
static coroutine_handle from_address(void *) noexcept;
};
-}
+} // namespace std
struct suspend_always {
bool await_ready() noexcept;
- void await_suspend(std::experimental::coroutine_handle<>) noexcept;
+ void await_suspend(std::coroutine_handle<>) noexcept;
void await_resume() noexcept;
};
@@ -39,7 +39,7 @@ struct task {
struct awaitable {
task await() { (void)co_await *this; }
bool await_ready() { return false; }
- bool await_suspend(std::experimental::coroutine_handle<> awaiter) { return false; }
+ bool await_suspend(std::coroutine_handle<> awaiter) { return false; }
bool await_resume() { return false; }
};
diff --git a/clang/test/CodeGenCoroutines/Inputs/coroutine-exp-namespace.h b/clang/test/CodeGenCoroutines/Inputs/coroutine-exp-namespace.h
new file mode 100644
index 000000000000..2e8b949ca148
--- /dev/null
+++ b/clang/test/CodeGenCoroutines/Inputs/coroutine-exp-namespace.h
@@ -0,0 +1,82 @@
+#pragma once
+
+namespace std {
+namespace experimental {
+inline namespace coroutines_v1 {
+
+template <typename R, typename...> struct coroutine_traits {
+ using promise_type = typename R::promise_type;
+};
+
+template <typename Promise = void> struct coroutine_handle;
+
+template <> struct coroutine_handle<void> {
+ static coroutine_handle from_address(void *addr) noexcept {
+ coroutine_handle me;
+ me.ptr = addr;
+ return me;
+ }
+ void operator()() { resume(); }
+ void *address() const noexcept { return ptr; }
+ void resume() const { __builtin_coro_resume(ptr); }
+ void destroy() const { __builtin_coro_destroy(ptr); }
+ bool done() const { return __builtin_coro_done(ptr); }
+ coroutine_handle &operator=(decltype(nullptr)) {
+ ptr = nullptr;
+ return *this;
+ }
+ coroutine_handle(decltype(nullptr)) : ptr(nullptr) {}
+ coroutine_handle() : ptr(nullptr) {}
+ // void reset() { ptr = nullptr; } // add to P0057?
+ explicit operator bool() const { return ptr; }
+
+protected:
+ void *ptr;
+};
+
+template <typename Promise> struct coroutine_handle : coroutine_handle<> {
+ using coroutine_handle<>::operator=;
+
+ static coroutine_handle from_address(void *addr) noexcept {
+ coroutine_handle me;
+ me.ptr = addr;
+ return me;
+ }
+
+ Promise &promise() const {
+ return *reinterpret_cast<Promise *>(
+ __builtin_coro_promise(ptr, alignof(Promise), false));
+ }
+ static coroutine_handle from_promise(Promise &promise) {
+ coroutine_handle p;
+ p.ptr = __builtin_coro_promise(&promise, alignof(Promise), true);
+ return p;
+ }
+};
+
+template <typename _PromiseT>
+bool operator==(coroutine_handle<_PromiseT> const &_Left,
+ coroutine_handle<_PromiseT> const &_Right) noexcept {
+ return _Left.address() == _Right.address();
+}
+
+template <typename _PromiseT>
+bool operator!=(coroutine_handle<_PromiseT> const &_Left,
+ coroutine_handle<_PromiseT> const &_Right) noexcept {
+ return !(_Left == _Right);
+}
+
+struct suspend_always {
+ bool await_ready() { return false; }
+ void await_suspend(coroutine_handle<>) {}
+ void await_resume() {}
+};
+struct suspend_never {
+ bool await_ready() noexcept { return true; }
+ void await_suspend(coroutine_handle<>) noexcept {}
+ void await_resume() noexcept {}
+};
+
+} // namespace coroutines_v1
+} // namespace experimental
+} // namespace std
diff --git a/clang/test/CodeGenCoroutines/Inputs/coroutine.h b/clang/test/CodeGenCoroutines/Inputs/coroutine.h
index 2dd1ce7e9735..581c7166e842 100644
--- a/clang/test/CodeGenCoroutines/Inputs/coroutine.h
+++ b/clang/test/CodeGenCoroutines/Inputs/coroutine.h
@@ -1,6 +1,6 @@
#pragma once
-namespace std { namespace experimental { inline namespace coroutines_v1 {
+namespace std {
template <typename R, typename...> struct coroutine_traits {
using promise_type = typename R::promise_type;
@@ -77,4 +77,4 @@ struct suspend_never {
void await_resume() noexcept {}
};
-}}}
+} // namespace std
diff --git a/clang/test/CodeGenCoroutines/coro-alloc-exp-namespace.cpp b/clang/test/CodeGenCoroutines/coro-alloc-exp-namespace.cpp
new file mode 100644
index 000000000000..39223c38613a
--- /dev/null
+++ b/clang/test/CodeGenCoroutines/coro-alloc-exp-namespace.cpp
@@ -0,0 +1,255 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fcoroutines-ts -std=c++14 \
+// RUN: -Wno-coroutine-missing-unhandled-exception -emit-llvm %s -o - -disable-llvm-passes \
+// RUN: | FileCheck %s
+
+namespace std {
+namespace experimental {
+template <typename... T>
+struct coroutine_traits; // expected-note {{declared here}}
+
+template <class Promise = void>
+struct coroutine_handle {
+ coroutine_handle() = default;
+ static coroutine_handle from_address(void *) noexcept { return {}; }
+};
+
+template <>
+struct coroutine_handle<void> {
+ static coroutine_handle from_address(void *) { return {}; }
+ coroutine_handle() = default;
+ template <class PromiseType>
+ coroutine_handle(coroutine_handle<PromiseType>) noexcept {}
+};
+
+} // end namespace experimental
+
+struct nothrow_t {};
+constexpr nothrow_t nothrow = {};
+
+} // end namespace std
+
+// Required when get_return_object_on_allocation_failure() is defined by
+// the promise.
+using SizeT = decltype(sizeof(int));
+void *operator new(SizeT __sz, const std::nothrow_t &) noexcept;
+void operator delete(void *__p, const std::nothrow_t &)noexcept;
+
+struct suspend_always {
+ bool await_ready() noexcept { return false; }
+ void await_suspend(std::experimental::coroutine_handle<>) noexcept {}
+ void await_resume() noexcept {}
+};
+
+struct global_new_delete_tag {};
+
+template <>
+struct std::experimental::coroutine_traits<void, global_new_delete_tag> {
+ struct promise_type {
+ void get_return_object() {}
+ suspend_always initial_suspend() { return {}; }
+ suspend_always final_suspend() noexcept { return {}; }
+ void return_void() {}
+ };
+};
+
+// CHECK-LABEL: f0(
+extern "C" void f0(global_new_delete_tag) {
+ // CHECK: %[[ID:.+]] = call token @llvm.coro.id(i32 16
+ // CHECK: %[[NeedAlloc:.+]] = call i1 @llvm.coro.alloc(token %[[ID]])
+ // CHECK: br i1 %[[NeedAlloc]], label %[[AllocBB:.+]], label %[[InitBB:.+]]
+
+ // CHECK: [[AllocBB]]:
+ // CHECK: %[[SIZE:.+]] = call i64 @llvm.coro.size.i64()
+ // CHECK: %[[MEM:.+]] = call noalias nonnull i8* @_Znwm(i64 %[[SIZE]])
+ // CHECK: br label %[[InitBB]]
+
+ // CHECK: [[InitBB]]:
+ // CHECK: %[[PHI:.+]] = phi i8* [ null, %{{.+}} ], [ %call, %[[AllocBB]] ]
+ // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.begin(token %[[ID]], i8* %[[PHI]])
+
+ // CHECK: %[[MEM:.+]] = call i8* @llvm.coro.free(token %[[ID]], i8* %[[FRAME]])
+ // CHECK: %[[NeedDealloc:.+]] = icmp ne i8* %[[MEM]], null
+ // CHECK: br i1 %[[NeedDealloc]], label %[[FreeBB:.+]], label %[[Afterwards:.+]]
+
+ // CHECK: [[FreeBB]]:
+ // CHECK: call void @_ZdlPv(i8* %[[MEM]])
+ // CHECK: br label %[[Afterwards]]
+
+ // CHECK: [[Afterwards]]:
+ // CHECK: ret void
+ co_return;
+}
+
+struct promise_new_tag {};
+
+template <>
+struct std::experimental::coroutine_traits<void, promise_new_tag> {
+ struct promise_type {
+ void *operator new(unsigned long);
+ void get_return_object() {}
+ suspend_always initial_suspend() { return {}; }
+ suspend_always final_suspend() noexcept { return {}; }
+ void return_void() {}
+ };
+};
+
+// CHECK-LABEL: f1(
+extern "C" void f1(promise_new_tag) {
+ // CHECK: %[[ID:.+]] = call token @llvm.coro.id(i32 16
+ // CHECK: %[[SIZE:.+]] = call i64 @llvm.coro.size.i64()
+ // CHECK: call i8* @_ZNSt12experimental16coroutine_traitsIJv15promise_new_tagEE12promise_typenwEm(i64 %[[SIZE]])
+
+ // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.begin(
+ // CHECK: %[[MEM:.+]] = call i8* @llvm.coro.free(token %[[ID]], i8* %[[FRAME]])
+ // CHECK: call void @_ZdlPv(i8* %[[MEM]])
+ co_return;
+}
+
+struct promise_matching_placement_new_tag {};
+
+template <>
+struct std::experimental::coroutine_traits<void, promise_matching_placement_new_tag, int, float, double> {
+ struct promise_type {
+ void *operator new(unsigned long, promise_matching_placement_new_tag,
+ int, float, double);
+ void get_return_object() {}
+ suspend_always initial_suspend() { return {}; }
+ suspend_always final_suspend() noexcept { return {}; }
+ void return_void() {}
+ };
+};
+
+// CHECK-LABEL: f1a(
+extern "C" void f1a(promise_matching_placement_new_tag, int x, float y, double z) {
+ // CHECK: store i32 %x, i32* %x.addr, align 4
+ // CHECK: store float %y, float* %y.addr, align 4
+ // CHECK: store double %z, double* %z.addr, align 8
+ // CHECK: %[[ID:.+]] = call token @llvm.coro.id(i32 16
+ // CHECK: %[[SIZE:.+]] = call i64 @llvm.coro.size.i64()
+ // CHECK: %[[INT:.+]] = load i32, i32* %x.addr, align 4
+ // CHECK: %[[FLOAT:.+]] = load float, float* %y.addr, align 4
+ // CHECK: %[[DOUBLE:.+]] = load double, double* %z.addr, align 8
+ // CHECK: call i8* @_ZNSt12experimental16coroutine_traitsIJv34promise_matching_placement_new_tagifdEE12promise_typenwEmS1_ifd(i64 %[[SIZE]], i32 %[[INT]], float %[[FLOAT]], double %[[DOUBLE]])
+ co_return;
+}
+
+// Declare a placement form operator new, such as the one described in
+// C++ 18.6.1.3.1, which takes a void* argument.
+void *operator new(SizeT __sz, void *__p) noexcept;
+
+struct promise_matching_global_placement_new_tag {};
+struct dummy {};
+template <>
+struct std::experimental::coroutine_traits<void, promise_matching_global_placement_new_tag, dummy *> {
+ struct promise_type {
+ void get_return_object() {}
+ suspend_always initial_suspend() { return {}; }
+ suspend_always final_suspend() noexcept { return {}; }
+ void return_void() {}
+ };
+};
+
+// A coroutine that takes a single pointer argument should not invoke this
+// placement form operator. [dcl.fct.def.coroutine]/7 dictates that lookup for
+// allocation functions matching the coroutine function's signature be done
+// within the scope of the promise type's class.
+// CHECK-LABEL: f1b(
+extern "C" void f1b(promise_matching_global_placement_new_tag, dummy *) {
+ // CHECK: call noalias nonnull i8* @_Znwm(i64
+ co_return;
+}
+
+struct promise_delete_tag {};
+
+template <>
+struct std::experimental::coroutine_traits<void, promise_delete_tag> {
+ struct promise_type {
+ void operator delete(void *);
+ void get_return_object() {}
+ suspend_always initial_suspend() { return {}; }
+ suspend_always final_suspend() noexcept { return {}; }
+ void return_void() {}
+ };
+};
+
+// CHECK-LABEL: f2(
+extern "C" void f2(promise_delete_tag) {
+ // CHECK: %[[ID:.+]] = call token @llvm.coro.id(i32 16
+ // CHECK: %[[SIZE:.+]] = call i64 @llvm.coro.size.i64()
+ // CHECK: call noalias nonnull i8* @_Znwm(i64 %[[SIZE]])
+
+ // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.begin(
+ // CHECK: %[[MEM:.+]] = call i8* @llvm.coro.free(token %[[ID]], i8* %[[FRAME]])
+ // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJv18promise_delete_tagEE12promise_typedlEPv(i8* %[[MEM]])
+ co_return;
+}
+
+struct promise_sized_delete_tag {};
+
+template <>
+struct std::experimental::coroutine_traits<void, promise_sized_delete_tag> {
+ struct promise_type {
+ void operator delete(void *, unsigned long);
+ void get_return_object() {}
+ suspend_always initial_suspend() { return {}; }
+ suspend_always final_suspend() noexcept { return {}; }
+ void return_void() {}
+ };
+};
+
+// CHECK-LABEL: f3(
+extern "C" void f3(promise_sized_delete_tag) {
+ // CHECK: %[[ID:.+]] = call token @llvm.coro.id(i32 16
+ // CHECK: %[[SIZE:.+]] = call i64 @llvm.coro.size.i64()
+ // CHECK: call noalias nonnull i8* @_Znwm(i64 %[[SIZE]])
+
+ // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.begin(
+ // CHECK: %[[MEM:.+]] = call i8* @llvm.coro.free(token %[[ID]], i8* %[[FRAME]])
+ // CHECK: %[[SIZE2:.+]] = call i64 @llvm.coro.size.i64()
+ // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJv24promise_sized_delete_tagEE12promise_typedlEPvm(i8* %[[MEM]], i64 %[[SIZE2]])
+ co_return;
+}
+
+struct promise_on_alloc_failure_tag {};
+
+template <>
+struct std::experimental::coroutine_traits<int, promise_on_alloc_failure_tag> {
+ struct promise_type {
+ int get_return_object() { return 0; }
+ suspend_always initial_suspend() { return {}; }
+ suspend_always final_suspend() noexcept { return {}; }
+ void return_void() {}
+ static int get_return_object_on_allocation_failure() { return -1; }
+ };
+};
+
+// CHECK-LABEL: f4(
+extern "C" int f4(promise_on_alloc_failure_tag) {
+ // CHECK: %[[RetVal:.+]] = alloca i32
+ // CHECK: %[[Gro:.+]] = alloca i32
+ // CHECK: %[[ID:.+]] = call token @llvm.coro.id(i32 16
+ // CHECK: %[[SIZE:.+]] = call i64 @llvm.coro.size.i64()
+ // CHECK: %[[MEM:.+]] = call noalias i8* @_ZnwmRKSt9nothrow_t(i64 %[[SIZE]], %"struct.std::nothrow_t"* nonnull align 1 dereferenceable(1) @_ZStL7nothrow)
+ // CHECK: %[[OK:.+]] = icmp ne i8* %[[MEM]], null
+ // CHECK: br i1 %[[OK]], label %[[OKBB:.+]], label %[[ERRBB:.+]]
+
+ // CHECK: [[ERRBB]]:
+ // CHECK: %[[FailRet:.+]] = call i32 @_ZNSt12experimental16coroutine_traitsIJi28promise_on_alloc_failure_tagEE12promise_type39get_return_object_on_allocation_failureEv(
+ // CHECK: store i32 %[[FailRet]], i32* %[[RetVal]]
+ // CHECK: br label %[[RetBB:.+]]
+
+ // CHECK: [[OKBB]]:
+ // CHECK: %[[OkRet:.+]] = call i32 @_ZNSt12experimental16coroutine_traitsIJi28promise_on_alloc_failure_tagEE12promise_type17get_return_objectEv(
+ // CHECK: store i32 %[[OkRet]], i32* %[[Gro]]
+
+ // CHECK: %[[Tmp1:.*]] = load i32, i32* %[[Gro]]
+ // CHECK-NEXT: store i32 %[[Tmp1]], i32* %[[RetVal]]
+ // CHECK-NEXT: %[[Gro_CAST:.+]] = bitcast i32* %[[Gro]] to i8*
+ // CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 4, i8* %[[Gro_CAST]]) #2
+ // CHECK-NEXT: br label %[[RetBB]]
+
+ // CHECK: [[RetBB]]:
+ // CHECK: %[[LoadRet:.+]] = load i32, i32* %[[RetVal]], align 4
+ // CHECK: ret i32 %[[LoadRet]]
+ co_return;
+}
diff --git a/clang/test/CodeGenCoroutines/coro-alloc.cpp b/clang/test/CodeGenCoroutines/coro-alloc.cpp
index c60ca5a83d48..b1a2e290b4fa 100644
--- a/clang/test/CodeGenCoroutines/coro-alloc.cpp
+++ b/clang/test/CodeGenCoroutines/coro-alloc.cpp
@@ -1,9 +1,8 @@
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fcoroutines-ts -std=c++14 \
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 \
// RUN: -Wno-coroutine-missing-unhandled-exception -emit-llvm %s -o - -disable-llvm-passes \
// RUN: | FileCheck %s
namespace std {
-namespace experimental {
template <typename... T>
struct coroutine_traits; // expected-note {{declared here}}
@@ -21,8 +20,6 @@ struct coroutine_handle<void> {
coroutine_handle(coroutine_handle<PromiseType>) noexcept {}
};
-} // end namespace experimental
-
struct nothrow_t {};
constexpr nothrow_t nothrow = {};
@@ -37,14 +34,14 @@ void operator delete(void* __p, const std::nothrow_t&) noexcept;
struct suspend_always {
bool await_ready() noexcept { return false; }
- void await_suspend(std::experimental::coroutine_handle<>) noexcept {}
+ void await_suspend(std::coroutine_handle<>) noexcept {}
void await_resume() noexcept {}
};
struct global_new_delete_tag {};
-template<>
-struct std::experimental::coroutine_traits<void, global_new_delete_tag> {
+template <>
+struct std::coroutine_traits<void, global_new_delete_tag> {
struct promise_type {
void get_return_object() {}
suspend_always initial_suspend() { return {}; }
@@ -83,8 +80,8 @@ extern "C" void f0(global_new_delete_tag) {
struct promise_new_tag {};
-template<>
-struct std::experimental::coroutine_traits<void, promise_new_tag> {
+template <>
+struct std::coroutine_traits<void, promise_new_tag> {
struct promise_type {
void *operator new(unsigned long);
void get_return_object() {}
@@ -98,7 +95,7 @@ struct std::experimental::coroutine_traits<void, promise_new_tag> {
extern "C" void f1(promise_new_tag ) {
// CHECK: %[[ID:.+]] = call token @llvm.coro.id(i32 16
// CHECK: %[[SIZE:.+]] = call i64 @llvm.coro.size.i64()
- // CHECK: call i8* @_ZNSt12experimental16coroutine_traitsIJv15promise_new_tagEE12promise_typenwEm(i64 %[[SIZE]])
+ // CHECK: call i8* @_ZNSt16coroutine_traitsIJv15promise_new_tagEE12promise_typenwEm(i64 %[[SIZE]])
// CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.begin(
// CHECK: %[[MEM:.+]] = call i8* @llvm.coro.free(token %[[ID]], i8* %[[FRAME]])
@@ -108,8 +105,8 @@ extern "C" void f1(promise_new_tag ) {
struct promise_matching_placement_new_tag {};
-template<>
-struct std::experimental::coroutine_traits<void, promise_matching_placement_new_tag, int, float, double> {
+template <>
+struct std::coroutine_traits<void, promise_matching_placement_new_tag, int, float, double> {
struct promise_type {
void *operator new(unsigned long, promise_matching_placement_new_tag,
int, float, double);
@@ -130,7 +127,7 @@ extern "C" void f1a(promise_matching_placement_new_tag, int x, float y , double
// CHECK: %[[INT:.+]] = load i32, i32* %x.addr, align 4
// CHECK: %[[FLOAT:.+]] = load float, float* %y.addr, align 4
// CHECK: %[[DOUBLE:.+]] = load double, double* %z.addr, align 8
- // CHECK: call i8* @_ZNSt12experimental16coroutine_traitsIJv34promise_matching_placement_new_tagifdEE12promise_typenwEmS1_ifd(i64 %[[SIZE]], i32 %[[INT]], float %[[FLOAT]], double %[[DOUBLE]])
+ // CHECK: call i8* @_ZNSt16coroutine_traitsIJv34promise_matching_placement_new_tagifdEE12promise_typenwEmS0_ifd(i64 %[[SIZE]], i32 %[[INT]], float %[[FLOAT]], double %[[DOUBLE]])
co_return;
}
@@ -140,8 +137,8 @@ void* operator new(SizeT __sz, void *__p) noexcept;
struct promise_matching_global_placement_new_tag {};
struct dummy {};
-template<>
-struct std::experimental::coroutine_traits<void, promise_matching_global_placement_new_tag, dummy*> {
+template <>
+struct std::coroutine_traits<void, promise_matching_global_placement_new_tag, dummy *> {
struct promise_type {
void get_return_object() {}
suspend_always initial_suspend() { return {}; }
@@ -162,8 +159,8 @@ extern "C" void f1b(promise_matching_global_placement_new_tag, dummy *) {
struct promise_delete_tag {};
-template<>
-struct std::experimental::coroutine_traits<void, promise_delete_tag> {
+template <>
+struct std::coroutine_traits<void, promise_delete_tag> {
struct promise_type {
void operator delete(void*);
void get_return_object() {}
@@ -181,14 +178,14 @@ extern "C" void f2(promise_delete_tag) {
// CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.begin(
// CHECK: %[[MEM:.+]] = call i8* @llvm.coro.free(token %[[ID]], i8* %[[FRAME]])
- // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJv18promise_delete_tagEE12promise_typedlEPv(i8* %[[MEM]])
+ // CHECK: call void @_ZNSt16coroutine_traitsIJv18promise_delete_tagEE12promise_typedlEPv(i8* %[[MEM]])
co_return;
}
struct promise_sized_delete_tag {};
-template<>
-struct std::experimental::coroutine_traits<void, promise_sized_delete_tag> {
+template <>
+struct std::coroutine_traits<void, promise_sized_delete_tag> {
struct promise_type {
void operator delete(void*, unsigned long);
void get_return_object() {}
@@ -207,14 +204,14 @@ extern "C" void f3(promise_sized_delete_tag) {
// CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.begin(
// CHECK: %[[MEM:.+]] = call i8* @llvm.coro.free(token %[[ID]], i8* %[[FRAME]])
// CHECK: %[[SIZE2:.+]] = call i64 @llvm.coro.size.i64()
- // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJv24promise_sized_delete_tagEE12promise_typedlEPvm(i8* %[[MEM]], i64 %[[SIZE2]])
+ // CHECK: call void @_ZNSt16coroutine_traitsIJv24promise_sized_delete_tagEE12promise_typedlEPvm(i8* %[[MEM]], i64 %[[SIZE2]])
co_return;
}
struct promise_on_alloc_failure_tag {};
-template<>
-struct std::experimental::coroutine_traits<int, promise_on_alloc_failure_tag> {
+template <>
+struct std::coroutine_traits<int, promise_on_alloc_failure_tag> {
struct promise_type {
int get_return_object() { return 0; }
suspend_always initial_suspend() { return {}; }
@@ -235,12 +232,12 @@ extern "C" int f4(promise_on_alloc_failure_tag) {
// CHECK: br i1 %[[OK]], label %[[OKBB:.+]], label %[[ERRBB:.+]]
// CHECK: [[ERRBB]]:
- // CHECK: %[[FailRet:.+]] = call i32 @_ZNSt12experimental16coroutine_traitsIJi28promise_on_alloc_failure_tagEE12promise_type39get_return_object_on_allocation_failureEv(
+ // CHECK: %[[FailRet:.+]] = call i32 @_ZNSt16coroutine_traitsIJi28promise_on_alloc_failure_tagEE12promise_type39get_return_object_on_allocation_failureEv(
// CHECK: store i32 %[[FailRet]], i32* %[[RetVal]]
// CHECK: br label %[[RetBB:.+]]
// CHECK: [[OKBB]]:
- // CHECK: %[[OkRet:.+]] = call i32 @_ZNSt12experimental16coroutine_traitsIJi28promise_on_alloc_failure_tagEE12promise_type17get_return_objectEv(
+ // CHECK: %[[OkRet:.+]] = call i32 @_ZNSt16coroutine_traitsIJi28promise_on_alloc_failure_tagEE12promise_type17get_return_objectEv(
// CHECK: store i32 %[[OkRet]], i32* %[[Gro]]
// CHECK: %[[Tmp1:.*]] = load i32, i32* %[[Gro]]
diff --git a/clang/test/CodeGenCoroutines/coro-always-inline-exp-namespace.cpp b/clang/test/CodeGenCoroutines/coro-always-inline-exp-namespace.cpp
new file mode 100644
index 000000000000..ef7183b9642d
--- /dev/null
+++ b/clang/test/CodeGenCoroutines/coro-always-inline-exp-namespace.cpp
@@ -0,0 +1,52 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fcoroutines-ts \
+// RUN: -fexperimental-new-pass-manager -O0 %s -o - | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fcoroutines-ts \
+// RUN: -fexperimental-new-pass-manager -fno-inline -O0 %s -o - | FileCheck %s
+
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fcoroutines-ts \
+// RUN: -O0 %s -o - | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fcoroutines-ts \
+// RUN: -fno-inline -O0 %s -o - | FileCheck %s
+
+namespace std {
+namespace experimental {
+
+struct handle {};
+
+struct awaitable {
+ bool await_ready() noexcept { return true; }
+ // CHECK-NOT: await_suspend
+ inline void __attribute__((__always_inline__)) await_suspend(handle) noexcept {}
+ bool await_resume() noexcept { return true; }
+};
+
+template <typename T>
+struct coroutine_handle {
+ static handle from_address(void *address) noexcept { return {}; }
+};
+
+template <typename T = void>
+struct coroutine_traits {
+ struct promise_type {
+ awaitable initial_suspend() { return {}; }
+ awaitable final_suspend() noexcept { return {}; }
+ void return_void() {}
+ T get_return_object() { return T(); }
+ void unhandled_exception() {}
+ };
+};
+} // namespace experimental
+} // namespace std
+
+// CHECK-LABEL: @_Z3foov
+// CHECK-LABEL: entry:
+// CHECK: [[CAST0:%[0-9]+]] = bitcast %"struct.std::experimental::awaitable"* %ref.tmp{{.*}} to i8*
+// CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 1, i8* [[CAST0]])
+// CHECK: [[CAST1:%[0-9]+]] = bitcast %"struct.std::experimental::awaitable"* %ref.tmp{{.*}} to i8*
+// CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 1, i8* [[CAST1]])
+
+// CHECK: [[CAST2:%[0-9]+]] = bitcast %"struct.std::experimental::awaitable"* %ref.tmp{{.*}} to i8*
+// CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 1, i8* [[CAST2]])
+// CHECK: [[CAST3:%[0-9]+]] = bitcast %"struct.std::experimental::awaitable"* %ref.tmp{{.*}} to i8*
+// CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 1, i8* [[CAST3]])
+void foo() { co_return; }
diff --git a/clang/test/CodeGenCoroutines/coro-always-inline.cpp b/clang/test/CodeGenCoroutines/coro-always-inline.cpp
index ef7183b9642d..39a82960e8db 100644
--- a/clang/test/CodeGenCoroutines/coro-always-inline.cpp
+++ b/clang/test/CodeGenCoroutines/coro-always-inline.cpp
@@ -1,15 +1,14 @@
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fcoroutines-ts \
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -std=c++20 \
// RUN: -fexperimental-new-pass-manager -O0 %s -o - | FileCheck %s
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fcoroutines-ts \
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -std=c++20 \
// RUN: -fexperimental-new-pass-manager -fno-inline -O0 %s -o - | FileCheck %s
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fcoroutines-ts \
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -std=c++20 \
// RUN: -O0 %s -o - | FileCheck %s
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fcoroutines-ts \
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -std=c++20 \
// RUN: -fno-inline -O0 %s -o - | FileCheck %s
namespace std {
-namespace experimental {
struct handle {};
@@ -35,18 +34,17 @@ struct coroutine_traits {
void unhandled_exception() {}
};
};
-} // namespace experimental
} // namespace std
// CHECK-LABEL: @_Z3foov
// CHECK-LABEL: entry:
-// CHECK: [[CAST0:%[0-9]+]] = bitcast %"struct.std::experimental::awaitable"* %ref.tmp{{.*}} to i8*
+// CHECK: [[CAST0:%[0-9]+]] = bitcast %"struct.std::awaitable"* %ref.tmp{{.*}} to i8*
// CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 1, i8* [[CAST0]])
-// CHECK: [[CAST1:%[0-9]+]] = bitcast %"struct.std::experimental::awaitable"* %ref.tmp{{.*}} to i8*
+// CHECK: [[CAST1:%[0-9]+]] = bitcast %"struct.std::awaitable"* %ref.tmp{{.*}} to i8*
// CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 1, i8* [[CAST1]])
-// CHECK: [[CAST2:%[0-9]+]] = bitcast %"struct.std::experimental::awaitable"* %ref.tmp{{.*}} to i8*
+// CHECK: [[CAST2:%[0-9]+]] = bitcast %"struct.std::awaitable"* %ref.tmp{{.*}} to i8*
// CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 1, i8* [[CAST2]])
-// CHECK: [[CAST3:%[0-9]+]] = bitcast %"struct.std::experimental::awaitable"* %ref.tmp{{.*}} to i8*
+// CHECK: [[CAST3:%[0-9]+]] = bitcast %"struct.std::awaitable"* %ref.tmp{{.*}} to i8*
// CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 1, i8* [[CAST3]])
void foo() { co_return; }
diff --git a/clang/test/CodeGenCoroutines/coro-await-domination-exp-namespace.cpp b/clang/test/CodeGenCoroutines/coro-await-domination-exp-namespace.cpp
new file mode 100644
index 000000000000..008867eb4bcf
--- /dev/null
+++ b/clang/test/CodeGenCoroutines/coro-await-domination-exp-namespace.cpp
@@ -0,0 +1,36 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fcoroutines-ts -std=c++14 -emit-llvm %s -o - | FileCheck %s
+#include "Inputs/coroutine-exp-namespace.h"
+
+using namespace std::experimental;
+
+struct coro {
+ struct promise_type {
+ coro get_return_object();
+ suspend_never initial_suspend();
+ suspend_never final_suspend() noexcept;
+ void return_void();
+ static void unhandled_exception();
+ };
+};
+
+struct A {
+ ~A();
+ bool await_ready();
+ int await_resume() { return 8; }
+ template <typename F> void await_suspend(F);
+};
+
+extern "C" void consume(int);
+
+// Verifies that domination is properly built during cleanup.
+// Without CGCleanup.cpp fix verifier was reporting:
+// Instruction does not dominate all uses!
+// %tmp.exprcleanup = alloca i32*, align 8
+// store i32* %x, i32** %tmp.exprcleanup, align 8
+
+// CHECK-LABEL: f(
+extern "C" coro f(int) {
+ int x = 42;
+ x = co_await A{};
+ consume(x);
+}
diff --git a/clang/test/CodeGenCoroutines/coro-await-domination.cpp b/clang/test/CodeGenCoroutines/coro-await-domination.cpp
index 3ce8cd7a6227..61082170fc5a 100644
--- a/clang/test/CodeGenCoroutines/coro-await-domination.cpp
+++ b/clang/test/CodeGenCoroutines/coro-await-domination.cpp
@@ -1,7 +1,7 @@
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fcoroutines-ts -std=c++14 -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -emit-llvm %s -o - | FileCheck %s
#include "Inputs/coroutine.h"
-using namespace std::experimental;
+using namespace std;
struct coro {
struct promise_type {
diff --git a/clang/test/CodeGenCoroutines/coro-await-exp-namespace.cpp b/clang/test/CodeGenCoroutines/coro-await-exp-namespace.cpp
new file mode 100644
index 000000000000..c7a8c7eafc03
--- /dev/null
+++ b/clang/test/CodeGenCoroutines/coro-await-exp-namespace.cpp
@@ -0,0 +1,355 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fcoroutines-ts -std=c++14 \
+// RUN: -emit-llvm %s -o - -disable-llvm-passes -Wno-coroutine -Wno-unused | FileCheck %s
+
+namespace std {
+namespace experimental {
+template <typename... T>
+struct coroutine_traits;
+
+template <typename Promise = void> struct coroutine_handle;
+
+template <>
+struct coroutine_handle<void> {
+ void *ptr;
+ static coroutine_handle from_address(void *);
+ void *address();
+};
+
+template <typename Promise>
+struct coroutine_handle : coroutine_handle<> {
+ static coroutine_handle from_address(void *) noexcept;
+};
+
+} // namespace experimental
+} // namespace std
+
+struct init_susp {
+ bool await_ready();
+ void await_suspend(std::experimental::coroutine_handle<>);
+ void await_resume();
+};
+struct final_susp {
+ bool await_ready() noexcept;
+ void await_suspend(std::experimental::coroutine_handle<>) noexcept;
+ void await_resume() noexcept;
+};
+
+struct suspend_always {
+ int stuff;
+ bool await_ready();
+ void await_suspend(std::experimental::coroutine_handle<>);
+ void await_resume();
+};
+
+template <>
+struct std::experimental::coroutine_traits<void> {
+ struct promise_type {
+ void get_return_object();
+ init_susp initial_suspend();
+ final_susp final_suspend() noexcept;
+ void return_void();
+ };
+};
+
+// CHECK-LABEL: f0(
+extern "C" void f0() {
+ // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.begin(
+
+ // See if initial_suspend was issued:
+ // ----------------------------------
+ // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJvEE12promise_type15initial_suspendEv(
+ // CHECK-NEXT: call zeroext i1 @_ZN9init_susp11await_readyEv(%struct.init_susp*
+ // CHECK: %[[INITSP_ID:.+]] = call token @llvm.coro.save(
+ // CHECK: call i8 @llvm.coro.suspend(token %[[INITSP_ID]], i1 false)
+
+ co_await suspend_always{};
+ // See if we need to suspend:
+ // --------------------------
+ // CHECK: %[[READY:.+]] = call zeroext i1 @_ZN14suspend_always11await_readyEv(%struct.suspend_always* {{[^,]*}} %[[AWAITABLE:.+]])
+ // CHECK: br i1 %[[READY]], label %[[READY_BB:.+]], label %[[SUSPEND_BB:.+]]
+
+ // If we are suspending:
+ // ---------------------
+ // CHECK: [[SUSPEND_BB]]:
+ // CHECK: %[[SUSPEND_ID:.+]] = call token @llvm.coro.save(
+ // ---------------------------
+ // Build the coroutine handle and pass it to await_suspend
+ // ---------------------------
+ // CHECK: call i8* @_ZNSt12experimental16coroutine_handleINS_16coroutine_traitsIJvEE12promise_typeEE12from_addressEPv(i8* %[[FRAME]])
+ // ... many lines of code to coerce coroutine_handle into an i8* scalar
+ // CHECK: %[[CH:.+]] = load i8*, i8** %{{.+}}
+ // CHECK: call void @_ZN14suspend_always13await_suspendENSt12experimental16coroutine_handleIvEE(%struct.suspend_always* {{[^,]*}} %[[AWAITABLE]], i8* %[[CH]])
+ // -------------------------
+ // Generate a suspend point:
+ // -------------------------
+ // CHECK: %[[OUTCOME:.+]] = call i8 @llvm.coro.suspend(token %[[SUSPEND_ID]], i1 false)
+ // CHECK: switch i8 %[[OUTCOME]], label %[[RET_BB:.+]] [
+ // CHECK: i8 0, label %[[READY_BB]]
+ // CHECK: i8 1, label %[[CLEANUP_BB:.+]]
+ // CHECK: ]
+
+ // Cleanup code goes here:
+ // -----------------------
+ // CHECK: [[CLEANUP_BB]]:
+
+ // When coroutine is resumed, call await_resume
+ // --------------------------
+ // CHECK: [[READY_BB]]:
+ // CHECK: call void @_ZN14suspend_always12await_resumeEv(%struct.suspend_always* {{[^,]*}} %[[AWAITABLE]])
+
+ // See if final_suspend was issued:
+ // ----------------------------------
+ // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJvEE12promise_type13final_suspendEv(
+ // CHECK-NEXT: call zeroext i1 @_ZN10final_susp11await_readyEv(%struct.final_susp*
+ // CHECK: %[[FINALSP_ID:.+]] = call token @llvm.coro.save(
+ // CHECK: call i8 @llvm.coro.suspend(token %[[FINALSP_ID]], i1 true)
+}
+
+struct suspend_maybe {
+ float stuff;
+ ~suspend_maybe();
+ bool await_ready();
+ bool await_suspend(std::experimental::coroutine_handle<>);
+ void await_resume();
+};
+
+template <>
+struct std::experimental::coroutine_traits<void, int> {
+ struct promise_type {
+ void get_return_object();
+ init_susp initial_suspend();
+ final_susp final_suspend() noexcept;
+ void return_void();
+ suspend_maybe yield_value(int);
+ };
+};
+
+// CHECK-LABEL: f1(
+extern "C" void f1(int) {
+ // CHECK: %[[PROMISE:.+]] = alloca %"struct.std::experimental::coroutine_traits<void, int>::promise_type"
+ // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.begin(
+ co_yield 42;
+ // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJviEE12promise_type11yield_valueEi(%struct.suspend_maybe* sret(%struct.suspend_maybe) align 4 %[[AWAITER:.+]], %"struct.std::experimental::coroutine_traits<void, int>::promise_type"* {{[^,]*}} %[[PROMISE]], i32 42)
+
+ // See if we need to suspend:
+ // --------------------------
+ // CHECK: %[[READY:.+]] = call zeroext i1 @_ZN13suspend_maybe11await_readyEv(%struct.suspend_maybe* {{[^,]*}} %[[AWAITABLE]])
+ // CHECK: br i1 %[[READY]], label %[[READY_BB:.+]], label %[[SUSPEND_BB:.+]]
+
+ // If we are suspending:
+ // ---------------------
+ // CHECK: [[SUSPEND_BB]]:
+ // CHECK: %[[SUSPEND_ID:.+]] = call token @llvm.coro.save(
+ // ---------------------------
+ // Build the coroutine handle and pass it to await_suspend
+ // ---------------------------
+ // CHECK: call i8* @_ZNSt12experimental16coroutine_handleINS_16coroutine_traitsIJviEE12promise_typeEE12from_addressEPv(i8* %[[FRAME]])
+ // ... many lines of code to coerce coroutine_handle into an i8* scalar
+ // CHECK: %[[CH:.+]] = load i8*, i8** %{{.+}}
+ // CHECK: %[[YES:.+]] = call zeroext i1 @_ZN13suspend_maybe13await_suspendENSt12experimental16coroutine_handleIvEE(%struct.suspend_maybe* {{[^,]*}} %[[AWAITABLE]], i8* %[[CH]])
+ // -------------------------------------------
+ // See if await_suspend decided not to suspend
+ // -------------------------------------------
+ // CHECK: br i1 %[[YES]], label %[[SUSPEND_PLEASE:.+]], label %[[READY_BB]]
+
+ // CHECK: [[SUSPEND_PLEASE]]:
+ // CHECK: call i8 @llvm.coro.suspend(token %[[SUSPEND_ID]], i1 false)
+
+ // CHECK: [[READY_BB]]:
+ // CHECK: call void @_ZN13suspend_maybe12await_resumeEv(%struct.suspend_maybe* {{[^,]*}} %[[AWAITABLE]])
+}
+
+struct ComplexAwaiter {
+ template <typename F> void await_suspend(F);
+ bool await_ready();
+ _Complex float await_resume();
+};
+extern "C" void UseComplex(_Complex float);
+
+// CHECK-LABEL: @TestComplex(
+extern "C" void TestComplex() {
+ UseComplex(co_await ComplexAwaiter{});
+ // CHECK: call <2 x float> @_ZN14ComplexAwaiter12await_resumeEv(%struct.ComplexAwaiter*
+ // CHECK: call void @UseComplex(<2 x float> %{{.+}})
+
+ co_await ComplexAwaiter{};
+ // CHECK: call <2 x float> @_ZN14ComplexAwaiter12await_resumeEv(%struct.ComplexAwaiter*
+
+ _Complex float Val = co_await ComplexAwaiter{};
+ // CHECK: call <2 x float> @_ZN14ComplexAwaiter12await_resumeEv(%struct.ComplexAwaiter*
+}
+
+struct Aggr {
+ int X, Y, Z;
+ ~Aggr();
+};
+struct AggrAwaiter {
+ template <typename F> void await_suspend(F);
+ bool await_ready();
+ Aggr await_resume();
+};
+
+extern "C" void Whatever();
+extern "C" void UseAggr(Aggr &&);
+
+// FIXME: Once the cleanup code is in, add testing that destructors for Aggr
+// are invoked properly on the cleanup branches.
+
+// CHECK-LABEL: @TestAggr(
+extern "C" void TestAggr() {
+ UseAggr(co_await AggrAwaiter{});
+ Whatever();
+ // CHECK: call void @_ZN11AggrAwaiter12await_resumeEv(%struct.Aggr* sret(%struct.Aggr) align 4 %[[AwaitResume:.+]],
+ // CHECK: call void @UseAggr(%struct.Aggr* nonnull align 4 dereferenceable(12) %[[AwaitResume]])
+ // CHECK: call void @_ZN4AggrD1Ev(%struct.Aggr* {{[^,]*}} %[[AwaitResume]])
+ // CHECK: call void @Whatever()
+
+ co_await AggrAwaiter{};
+ Whatever();
+ // CHECK: call void @_ZN11AggrAwaiter12await_resumeEv(%struct.Aggr* sret(%struct.Aggr) align 4 %[[AwaitResume2:.+]],
+ // CHECK: call void @_ZN4AggrD1Ev(%struct.Aggr* {{[^,]*}} %[[AwaitResume2]])
+ // CHECK: call void @Whatever()
+
+ Aggr Val = co_await AggrAwaiter{};
+ Whatever();
+ // CHECK: call void @_ZN11AggrAwaiter12await_resumeEv(%struct.Aggr* sret(%struct.Aggr) align 4 %[[AwaitResume3:.+]],
+ // CHECK: call void @Whatever()
+ // CHECK: call void @_ZN4AggrD1Ev(%struct.Aggr* {{[^,]*}} %[[AwaitResume3]])
+}
+
+struct ScalarAwaiter {
+ template <typename F> void await_suspend(F);
+ bool await_ready();
+ int await_resume();
+};
+
+extern "C" void UseScalar(int);
+
+// CHECK-LABEL: @TestScalar(
+extern "C" void TestScalar() {
+ UseScalar(co_await ScalarAwaiter{});
+ // CHECK: %[[Result:.+]] = call i32 @_ZN13ScalarAwaiter12await_resumeEv(%struct.ScalarAwaiter*
+ // CHECK: call void @UseScalar(i32 %[[Result]])
+
+ int Val = co_await ScalarAwaiter{};
+ // CHECK: %[[Result2:.+]] = call i32 @_ZN13ScalarAwaiter12await_resumeEv(%struct.ScalarAwaiter*
+ // CHECK: store i32 %[[Result2]], i32* %[[TMP_EXPRCLEANUP:.+]],
+ // CHECK: %[[TMP:.+]] = load i32, i32* %[[TMP_EXPRCLEANUP]],
+ // CHECK: store i32 %[[TMP]], i32* %Val,
+
+ co_await ScalarAwaiter{};
+ // CHECK: call i32 @_ZN13ScalarAwaiter12await_resumeEv(%struct.ScalarAwaiter*
+}
+
+// Test operator co_await codegen.
+enum class MyInt : int {};
+ScalarAwaiter operator co_await(MyInt);
+
+struct MyAgg {
+ AggrAwaiter operator co_await();
+};
+
+// CHECK-LABEL: @TestOpAwait(
+extern "C" void TestOpAwait() {
+ co_await MyInt(42);
+ // CHECK: call void @_Zaw5MyInt(i32 42)
+ // CHECK: call i32 @_ZN13ScalarAwaiter12await_resumeEv(%struct.ScalarAwaiter* {{[^,]*}} %
+
+ co_await MyAgg{};
+ // CHECK: call void @_ZN5MyAggawEv(%struct.MyAgg* {{[^,]*}} %
+ // CHECK: call void @_ZN11AggrAwaiter12await_resumeEv(%struct.Aggr* sret(%struct.Aggr) align 4 %
+}
+
+// CHECK-LABEL: EndlessLoop(
+extern "C" void EndlessLoop() {
+ // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.begin(
+
+ // See if initial_suspend was issued:
+ // ----------------------------------
+ // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJvEE12promise_type15initial_suspendEv(
+ // CHECK-NEXT: call zeroext i1 @_ZN9init_susp11await_readyEv(%struct.init_susp*
+
+ for (;;)
+ co_await suspend_always{};
+
+ // Verify that final_suspend was NOT issued:
+ // ----------------------------------
+ // CHECK-NOT: call void @_ZNSt12experimental16coroutine_traitsIJvEE12promise_type13final_suspendEv(
+ // CHECK-NOT: call zeroext i1 @_ZN10final_susp11await_readyEv(%struct.final_susp*
+}
+
+// Verifies that we don't crash when awaiting on an lvalue.
+// CHECK-LABEL: @_Z11AwaitLValuev(
+void AwaitLValue() {
+ suspend_always lval;
+ co_await lval;
+}
+
+struct RefTag {};
+
+struct AwaitResumeReturnsLValue {
+ bool await_ready();
+ void await_suspend(std::experimental::coroutine_handle<>);
+ RefTag &await_resume();
+};
+
+template <>
+struct std::experimental::coroutine_traits<void, double> {
+ struct promise_type {
+ void get_return_object();
+ init_susp initial_suspend();
+ final_susp final_suspend() noexcept;
+ void return_void();
+ AwaitResumeReturnsLValue yield_value(int);
+ };
+};
+
+// Verifies that we don't crash when returning an lvalue from an await_resume()
+// expression.
+// CHECK-LABEL: define{{.*}} void @_Z18AwaitReturnsLValued(double %0)
+void AwaitReturnsLValue(double) {
+ AwaitResumeReturnsLValue a;
+ // CHECK: %[[AVAR:.+]] = alloca %struct.AwaitResumeReturnsLValue,
+ // CHECK: %[[XVAR:.+]] = alloca %struct.RefTag*,
+
+ // CHECK: %[[YVAR:.+]] = alloca %struct.RefTag*,
+ // CHECK-NEXT: %[[TMP1:.+]] = alloca %struct.AwaitResumeReturnsLValue,
+
+ // CHECK: %[[TMP_EXPRCLEANUP1:.+]] = alloca %struct.RefTag*,
+ // CHECK: %[[ZVAR:.+]] = alloca %struct.RefTag*,
+ // CHECK-NEXT: %[[TMP2:.+]] = alloca %struct.AwaitResumeReturnsLValue,
+ // CHECK: %[[TMP_EXPRCLEANUP2:.+]] = alloca %struct.RefTag*,
+
+ // CHECK: %[[RES1:.+]] = call nonnull align 1 dereferenceable({{.*}}) %struct.RefTag* @_ZN24AwaitResumeReturnsLValue12await_resumeEv(%struct.AwaitResumeReturnsLValue* {{[^,]*}} %[[AVAR]])
+ // CHECK-NEXT: store %struct.RefTag* %[[RES1]], %struct.RefTag** %[[XVAR]],
+ RefTag &x = co_await a;
+
+ // CHECK: %[[RES2:.+]] = call nonnull align 1 dereferenceable({{.*}}) %struct.RefTag* @_ZN24AwaitResumeReturnsLValue12await_resumeEv(%struct.AwaitResumeReturnsLValue* {{[^,]*}} %[[TMP1]])
+ // CHECK-NEXT: store %struct.RefTag* %[[RES2]], %struct.RefTag** %[[TMP_EXPRCLEANUP1]],
+ // CHECK: %[[LOAD_TMP1:.+]] = load %struct.RefTag*, %struct.RefTag** %[[TMP_EXPRCLEANUP1]],
+ // CHECK: store %struct.RefTag* %[[LOAD_TMP1]], %struct.RefTag** %[[YVAR]],
+
+ RefTag &y = co_await AwaitResumeReturnsLValue{};
+ // CHECK: %[[RES3:.+]] = call nonnull align 1 dereferenceable({{.*}}) %struct.RefTag* @_ZN24AwaitResumeReturnsLValue12await_resumeEv(%struct.AwaitResumeReturnsLValue* {{[^,]*}} %[[TMP2]])
+ // CHECK-NEXT: store %struct.RefTag* %[[RES3]], %struct.RefTag** %[[TMP_EXPRCLEANUP2]],
+ // CHECK: %[[LOAD_TMP2:.+]] = load %struct.RefTag*, %struct.RefTag** %[[TMP_EXPRCLEANUP2]],
+ // CHECK: store %struct.RefTag* %[[LOAD_TMP2]], %struct.RefTag** %[[ZVAR]],
+ RefTag &z = co_yield 42;
+}
+
+struct TailCallAwait {
+ bool await_ready();
+ std::experimental::coroutine_handle<> await_suspend(std::experimental::coroutine_handle<>);
+ void await_resume();
+};
+
+// CHECK-LABEL: @TestTailcall(
+extern "C" void TestTailcall() {
+ co_await TailCallAwait{};
+
+ // CHECK: %[[RESULT:.+]] = call i8* @_ZN13TailCallAwait13await_suspendENSt12experimental16coroutine_handleIvEE(%struct.TailCallAwait*
+ // CHECK: %[[COERCE:.+]] = getelementptr inbounds %"struct.std::experimental::coroutine_handle", %"struct.std::experimental::coroutine_handle"* %[[TMP:.+]], i32 0, i32 0
+ // CHECK: store i8* %[[RESULT]], i8** %[[COERCE]]
+ // CHECK: %[[ADDR:.+]] = call i8* @_ZNSt12experimental16coroutine_handleIvE7addressEv(%"struct.std::experimental::coroutine_handle"* {{[^,]*}} %[[TMP]])
+ // CHECK: call void @llvm.coro.resume(i8* %[[ADDR]])
+}
diff --git a/clang/test/CodeGenCoroutines/coro-await-resume-eh-exp-namespace.cpp b/clang/test/CodeGenCoroutines/coro-await-resume-eh-exp-namespace.cpp
new file mode 100644
index 000000000000..87c021ff2d05
--- /dev/null
+++ b/clang/test/CodeGenCoroutines/coro-await-resume-eh-exp-namespace.cpp
@@ -0,0 +1,114 @@
+// Test the behavior of http://wg21.link/P0664, a proposal to catch any
+// exceptions thrown after the initial suspend point of a coroutine by
+// executing the handler specified by the promise type's 'unhandled_exception'
+// member function.
+//
+// RUN: %clang_cc1 -std=c++14 -fcoroutines-ts \
+// RUN: -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s \
+// RUN: -fexceptions -fcxx-exceptions -disable-llvm-passes \
+// RUN: | FileCheck %s
+
+#include "Inputs/coroutine-exp-namespace.h"
+
+namespace coro = std::experimental::coroutines_v1;
+
+struct throwing_awaitable {
+ bool await_ready() { return true; }
+ void await_suspend(coro::coroutine_handle<>) {}
+ void await_resume() { throw 42; }
+};
+
+struct throwing_task {
+ struct promise_type {
+ auto get_return_object() { return throwing_task{}; }
+ auto initial_suspend() { return throwing_awaitable{}; }
+ auto final_suspend() noexcept { return coro::suspend_never{}; }
+ void return_void() {}
+ void unhandled_exception() {}
+ };
+};
+
+// CHECK-LABEL: define{{.*}} void @_Z1fv()
+throwing_task f() {
+ // A variable RESUMETHREW is used to keep track of whether the body
+ // of 'await_resume' threw an exception. Exceptions thrown in
+ // 'await_resume' are unwound to RESUMELPAD.
+ // CHECK: init.ready:
+ // CHECK-NEXT: store i1 true, i1* %[[RESUMETHREW:.+]], align 1
+ // CHECK-NEXT: invoke void @_ZN18throwing_awaitable12await_resumeEv
+ // CHECK-NEXT: to label %[[RESUMECONT:.+]] unwind label %[[RESUMELPAD:.+]]
+
+ // If 'await_resume' does not throw an exception, 'false' is stored in
+ // variable RESUMETHREW.
+ // CHECK: [[RESUMECONT]]:
+ // CHECK-NEXT: store i1 false, i1* %[[RESUMETHREW]]
+ // CHECK-NEXT: br label %[[RESUMETRYCONT:.+]]
+
+ // 'unhandled_exception' is called for the exception thrown in
+ // 'await_resume'. The variable RESUMETHREW is never set to false,
+ // and a jump is made to RESUMETRYCONT.
+ // CHECK: [[RESUMELPAD]]:
+ // CHECK: br label %[[RESUMECATCH:.+]]
+ // CHECK: [[RESUMECATCH]]:
+ // CHECK: invoke void @_ZN13throwing_task12promise_type19unhandled_exceptionEv
+ // CHECK-NEXT: to label %[[RESUMEENDCATCH:.+]] unwind label
+ // CHECK: [[RESUMEENDCATCH]]:
+ // CHECK-NEXT: invoke void @__cxa_end_catch()
+ // CHECK-NEXT: to label %[[RESUMEENDCATCHCONT:.+]] unwind label
+ // CHECK: [[RESUMEENDCATCHCONT]]:
+ // CHECK-NEXT: br label %[[RESUMETRYCONT]]
+ // CHECK: [[RESUMETRYCONT]]:
+ // CHECK-NEXT: br label %[[CLEANUP:.+]]
+ // CHECK: [[CLEANUP]]:
+ // CHECK: switch i32 %{{.+}}, label %{{.+}} [
+ // CHECK-NEXT: i32 0, label %[[CLEANUPCONT:.+]]
+ // CHECK-NEXT: ]
+
+ // The variable RESUMETHREW is loaded and if true, then 'await_resume'
+ // threw an exception and the coroutine body is skipped, and the final
+ // suspend is executed immediately. Otherwise, the coroutine body is
+ // executed, and then the final suspend.
+ // CHECK: [[CLEANUPCONT]]:
+ // CHECK-NEXT: %[[RESUMETHREWLOAD:.+]] = load i1, i1* %[[RESUMETHREW]]
+ // CHECK-NEXT: br i1 %[[RESUMETHREWLOAD]], label %[[RESUMEDCONT:.+]], label %[[RESUMEDBODY:.+]]
+
+ // CHECK: [[RESUMEDBODY]]:
+ // CHECK: invoke void @_ZN13throwing_task12promise_type11return_voidEv
+ // CHECK-NEXT: to label %[[REDUMEDBODYCONT:.+]] unwind label
+ // CHECK: [[REDUMEDBODYCONT]]:
+ // CHECK-NEXT: br label %[[COROFINAL:.+]]
+
+ // CHECK: [[RESUMEDCONT]]:
+ // CHECK-NEXT: br label %[[COROFINAL]]
+
+ // CHECK: [[COROFINAL]]:
+ // CHECK: call void @_ZN13throwing_task12promise_type13final_suspendEv
+ co_return;
+}
+
+struct noexcept_awaitable {
+ bool await_ready() { return true; }
+ void await_suspend(coro::coroutine_handle<>) {}
+ void await_resume() noexcept {}
+};
+
+struct noexcept_task {
+ struct promise_type {
+ auto get_return_object() { return noexcept_task{}; }
+ auto initial_suspend() { return noexcept_awaitable{}; }
+ auto final_suspend() noexcept { return coro::suspend_never{}; }
+ void return_void() {}
+ void unhandled_exception() {}
+ };
+};
+
+// CHECK-LABEL: define{{.*}} void @_Z1gv()
+noexcept_task g() {
+ // If the await_resume function is marked as noexcept, none of the additional
+ // conditions that are present in f() above are added to the IR.
+ // This means that no i1 are stored before or after calling await_resume:
+ // CHECK: init.ready:
+ // CHECK-NEXT: call void @_ZN18noexcept_awaitable12await_resumeEv
+ // CHECK-NOT: store i1 false, i1*
+ co_return;
+}
diff --git a/clang/test/CodeGenCoroutines/coro-await-resume-eh.cpp b/clang/test/CodeGenCoroutines/coro-await-resume-eh.cpp
index 1b0c3a1c5c57..25616dcc62d9 100644
--- a/clang/test/CodeGenCoroutines/coro-await-resume-eh.cpp
+++ b/clang/test/CodeGenCoroutines/coro-await-resume-eh.cpp
@@ -3,18 +3,16 @@
// executing the handler specified by the promise type's 'unhandled_exception'
// member function.
//
-// RUN: %clang_cc1 -std=c++14 -fcoroutines-ts \
+// RUN: %clang_cc1 -std=c++20 \
// RUN: -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s \
// RUN: -fexceptions -fcxx-exceptions -disable-llvm-passes \
// RUN: | FileCheck %s
#include "Inputs/coroutine.h"
-namespace coro = std::experimental::coroutines_v1;
-
struct throwing_awaitable {
bool await_ready() { return true; }
- void await_suspend(coro::coroutine_handle<>) {}
+ void await_suspend(std::coroutine_handle<>) {}
void await_resume() { throw 42; }
};
@@ -22,7 +20,7 @@ struct throwing_task {
struct promise_type {
auto get_return_object() { return throwing_task{}; }
auto initial_suspend() { return throwing_awaitable{}; }
- auto final_suspend() noexcept { return coro::suspend_never{}; }
+ auto final_suspend() noexcept { return std::suspend_never{}; }
void return_void() {}
void unhandled_exception() {}
};
@@ -88,7 +86,7 @@ throwing_task f() {
struct noexcept_awaitable {
bool await_ready() { return true; }
- void await_suspend(coro::coroutine_handle<>) {}
+ void await_suspend(std::coroutine_handle<>) {}
void await_resume() noexcept {}
};
@@ -96,7 +94,7 @@ struct noexcept_task {
struct promise_type {
auto get_return_object() { return noexcept_task{}; }
auto initial_suspend() { return noexcept_awaitable{}; }
- auto final_suspend() noexcept { return coro::suspend_never{}; }
+ auto final_suspend() noexcept { return std::suspend_never{}; }
void return_void() {}
void unhandled_exception() {}
};
diff --git a/clang/test/CodeGenCoroutines/coro-await.cpp b/clang/test/CodeGenCoroutines/coro-await.cpp
index 3fa45d5f9ab6..3bcb3603d0a7 100644
--- a/clang/test/CodeGenCoroutines/coro-await.cpp
+++ b/clang/test/CodeGenCoroutines/coro-await.cpp
@@ -1,8 +1,7 @@
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fcoroutines-ts -std=c++14 \
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 \
// RUN: -emit-llvm %s -o - -disable-llvm-passes -Wno-coroutine -Wno-unused | FileCheck %s
namespace std {
-namespace experimental {
template <typename... T>
struct coroutine_traits;
@@ -20,29 +19,28 @@ struct coroutine_handle : coroutine_handle<> {
static coroutine_handle from_address(void *) noexcept;
};
-}
-}
+} // namespace std
struct init_susp {
bool await_ready();
- void await_suspend(std::experimental::coroutine_handle<>);
+ void await_suspend(std::coroutine_handle<>);
void await_resume();
};
struct final_susp {
bool await_ready() noexcept;
- void await_suspend(std::experimental::coroutine_handle<>) noexcept;
+ void await_suspend(std::coroutine_handle<>) noexcept;
void await_resume() noexcept;
};
struct suspend_always {
int stuff;
bool await_ready();
- void await_suspend(std::experimental::coroutine_handle<>);
+ void await_suspend(std::coroutine_handle<>);
void await_resume();
};
-template<>
-struct std::experimental::coroutine_traits<void> {
+template <>
+struct std::coroutine_traits<void> {
struct promise_type {
void get_return_object();
init_susp initial_suspend();
@@ -57,7 +55,7 @@ extern "C" void f0() {
// See if initial_suspend was issued:
// ----------------------------------
- // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJvEE12promise_type15initial_suspendEv(
+ // CHECK: call void @_ZNSt16coroutine_traitsIJvEE12promise_type15initial_suspendEv(
// CHECK-NEXT: call zeroext i1 @_ZN9init_susp11await_readyEv(%struct.init_susp*
// CHECK: %[[INITSP_ID:.+]] = call token @llvm.coro.save(
// CHECK: call i8 @llvm.coro.suspend(token %[[INITSP_ID]], i1 false)
@@ -75,10 +73,10 @@ extern "C" void f0() {
// ---------------------------
// Build the coroutine handle and pass it to await_suspend
// ---------------------------
- // CHECK: call i8* @_ZNSt12experimental16coroutine_handleINS_16coroutine_traitsIJvEE12promise_typeEE12from_addressEPv(i8* %[[FRAME]])
+ // CHECK: call i8* @_ZNSt16coroutine_handleINSt16coroutine_traitsIJvEE12promise_typeEE12from_addressEPv(i8* %[[FRAME]])
// ... many lines of code to coerce coroutine_handle into an i8* scalar
// CHECK: %[[CH:.+]] = load i8*, i8** %{{.+}}
- // CHECK: call void @_ZN14suspend_always13await_suspendENSt12experimental16coroutine_handleIvEE(%struct.suspend_always* {{[^,]*}} %[[AWAITABLE]], i8* %[[CH]])
+ // CHECK: call void @_ZN14suspend_always13await_suspendESt16coroutine_handleIvE(%struct.suspend_always* {{[^,]*}} %[[AWAITABLE]], i8* %[[CH]])
// -------------------------
// Generate a suspend point:
// -------------------------
@@ -99,7 +97,7 @@ extern "C" void f0() {
// See if final_suspend was issued:
// ----------------------------------
- // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJvEE12promise_type13final_suspendEv(
+ // CHECK: call void @_ZNSt16coroutine_traitsIJvEE12promise_type13final_suspendEv(
// CHECK-NEXT: call zeroext i1 @_ZN10final_susp11await_readyEv(%struct.final_susp*
// CHECK: %[[FINALSP_ID:.+]] = call token @llvm.coro.save(
// CHECK: call i8 @llvm.coro.suspend(token %[[FINALSP_ID]], i1 true)
@@ -109,13 +107,12 @@ struct suspend_maybe {
float stuff;
~suspend_maybe();
bool await_ready();
- bool await_suspend(std::experimental::coroutine_handle<>);
+ bool await_suspend(std::coroutine_handle<>);
void await_resume();
};
-
-template<>
-struct std::experimental::coroutine_traits<void,int> {
+template <>
+struct std::coroutine_traits<void, int> {
struct promise_type {
void get_return_object();
init_susp initial_suspend();
@@ -127,10 +124,10 @@ struct std::experimental::coroutine_traits<void,int> {
// CHECK-LABEL: f1(
extern "C" void f1(int) {
- // CHECK: %[[PROMISE:.+]] = alloca %"struct.std::experimental::coroutine_traits<void, int>::promise_type"
+ // CHECK: %[[PROMISE:.+]] = alloca %"struct.std::coroutine_traits<void, int>::promise_type"
// CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.begin(
co_yield 42;
- // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJviEE12promise_type11yield_valueEi(%struct.suspend_maybe* sret(%struct.suspend_maybe) align 4 %[[AWAITER:.+]], %"struct.std::experimental::coroutine_traits<void, int>::promise_type"* {{[^,]*}} %[[PROMISE]], i32 42)
+ // CHECK: call void @_ZNSt16coroutine_traitsIJviEE12promise_type11yield_valueEi(%struct.suspend_maybe* sret(%struct.suspend_maybe) align 4 %[[AWAITER:.+]], %"struct.std::coroutine_traits<void, int>::promise_type"* {{[^,]*}} %[[PROMISE]], i32 42)
// See if we need to suspend:
// --------------------------
@@ -144,10 +141,10 @@ extern "C" void f1(int) {
// ---------------------------
// Build the coroutine handle and pass it to await_suspend
// ---------------------------
- // CHECK: call i8* @_ZNSt12experimental16coroutine_handleINS_16coroutine_traitsIJviEE12promise_typeEE12from_addressEPv(i8* %[[FRAME]])
+ // CHECK: call i8* @_ZNSt16coroutine_handleINSt16coroutine_traitsIJviEE12promise_typeEE12from_addressEPv(i8* %[[FRAME]])
// ... many lines of code to coerce coroutine_handle into an i8* scalar
// CHECK: %[[CH:.+]] = load i8*, i8** %{{.+}}
- // CHECK: %[[YES:.+]] = call zeroext i1 @_ZN13suspend_maybe13await_suspendENSt12experimental16coroutine_handleIvEE(%struct.suspend_maybe* {{[^,]*}} %[[AWAITABLE]], i8* %[[CH]])
+ // CHECK: %[[YES:.+]] = call zeroext i1 @_ZN13suspend_maybe13await_suspendESt16coroutine_handleIvE(%struct.suspend_maybe* {{[^,]*}} %[[AWAITABLE]], i8* %[[CH]])
// -------------------------------------------
// See if await_suspend decided not to suspend
// -------------------------------------------
@@ -264,7 +261,7 @@ extern "C" void EndlessLoop() {
// See if initial_suspend was issued:
// ----------------------------------
- // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJvEE12promise_type15initial_suspendEv(
+ // CHECK: call void @_ZNSt16coroutine_traitsIJvEE12promise_type15initial_suspendEv(
// CHECK-NEXT: call zeroext i1 @_ZN9init_susp11await_readyEv(%struct.init_susp*
for (;;)
@@ -272,7 +269,7 @@ extern "C" void EndlessLoop() {
// Verify that final_suspend was NOT issued:
// ----------------------------------
- // CHECK-NOT: call void @_ZNSt12experimental16coroutine_traitsIJvEE12promise_type13final_suspendEv(
+ // CHECK-NOT: call void @_ZNSt16coroutine_traitsIJvEE12promise_type13final_suspendEv(
// CHECK-NOT: call zeroext i1 @_ZN10final_susp11await_readyEv(%struct.final_susp*
}
@@ -287,13 +284,12 @@ struct RefTag { };
struct AwaitResumeReturnsLValue {
bool await_ready();
- void await_suspend(std::experimental::coroutine_handle<>);
+ void await_suspend(std::coroutine_handle<>);
RefTag& await_resume();
};
-
-template<>
-struct std::experimental::coroutine_traits<void,double> {
+template <>
+struct std::coroutine_traits<void, double> {
struct promise_type {
void get_return_object();
init_susp initial_suspend();
@@ -338,7 +334,7 @@ void AwaitReturnsLValue(double) {
struct TailCallAwait {
bool await_ready();
- std::experimental::coroutine_handle<> await_suspend(std::experimental::coroutine_handle<>);
+ std::coroutine_handle<> await_suspend(std::coroutine_handle<>);
void await_resume();
};
@@ -346,9 +342,9 @@ struct TailCallAwait {
extern "C" void TestTailcall() {
co_await TailCallAwait{};
- // CHECK: %[[RESULT:.+]] = call i8* @_ZN13TailCallAwait13await_suspendENSt12experimental16coroutine_handleIvEE(%struct.TailCallAwait*
- // CHECK: %[[COERCE:.+]] = getelementptr inbounds %"struct.std::experimental::coroutine_handle", %"struct.std::experimental::coroutine_handle"* %[[TMP:.+]], i32 0, i32 0
+ // CHECK: %[[RESULT:.+]] = call i8* @_ZN13TailCallAwait13await_suspendESt16coroutine_handleIvE(%struct.TailCallAwait*
+ // CHECK: %[[COERCE:.+]] = getelementptr inbounds %"struct.std::coroutine_handle", %"struct.std::coroutine_handle"* %[[TMP:.+]], i32 0, i32 0
// CHECK: store i8* %[[RESULT]], i8** %[[COERCE]]
- // CHECK: %[[ADDR:.+]] = call i8* @_ZNSt12experimental16coroutine_handleIvE7addressEv(%"struct.std::experimental::coroutine_handle"* {{[^,]*}} %[[TMP]])
+ // CHECK: %[[ADDR:.+]] = call i8* @_ZNSt16coroutine_handleIvE7addressEv(%"struct.std::coroutine_handle"* {{[^,]*}} %[[TMP]])
// CHECK: call void @llvm.coro.resume(i8* %[[ADDR]])
}
diff --git a/clang/test/CodeGenCoroutines/coro-cleanup-exp-namespace.cpp b/clang/test/CodeGenCoroutines/coro-cleanup-exp-namespace.cpp
new file mode 100644
index 000000000000..0f80a9827314
--- /dev/null
+++ b/clang/test/CodeGenCoroutines/coro-cleanup-exp-namespace.cpp
@@ -0,0 +1,99 @@
+// Verify that coroutine promise and allocated memory are freed up on exception.
+// RUN: %clang_cc1 -std=c++1z -fcoroutines-ts -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s -fexceptions -fcxx-exceptions -disable-llvm-passes | FileCheck %s
+
+namespace std::experimental {
+template <typename... T> struct coroutine_traits;
+
+template <class Promise = void> struct coroutine_handle {
+ coroutine_handle() = default;
+ static coroutine_handle from_address(void *) noexcept;
+};
+template <> struct coroutine_handle<void> {
+ static coroutine_handle from_address(void *) noexcept;
+ coroutine_handle() = default;
+ template <class PromiseType>
+ coroutine_handle(coroutine_handle<PromiseType>) noexcept;
+};
+} // namespace std::experimental
+
+struct suspend_always {
+ bool await_ready() noexcept;
+ void await_suspend(std::experimental::coroutine_handle<>) noexcept;
+ void await_resume() noexcept;
+};
+
+template <> struct std::experimental::coroutine_traits<void> {
+ struct promise_type {
+ void get_return_object() noexcept;
+ suspend_always initial_suspend() noexcept;
+ suspend_always final_suspend() noexcept;
+ void return_void() noexcept;
+ promise_type();
+ ~promise_type();
+ void unhandled_exception() noexcept;
+ };
+};
+
+struct Cleanup {
+ ~Cleanup();
+};
+void may_throw();
+
+// CHECK-LABEL: define{{.*}} void @_Z1fv(
+void f() {
+ // CHECK: call noalias nonnull i8* @_Znwm(i64
+
+ // If promise constructor throws, check that we free the memory.
+
+ // CHECK: invoke void @_ZNSt12experimental16coroutine_traitsIJvEE12promise_typeC1Ev(
+ // CHECK-NEXT: to label %{{.+}} unwind label %[[DeallocPad:.+]]
+
+ // CHECK: [[DeallocPad]]:
+ // CHECK-NEXT: landingpad
+ // CHECK-NEXT: cleanup
+ // CHECK: br label %[[Dealloc:.+]]
+
+ Cleanup cleanup;
+ may_throw();
+
+ // if may_throw throws, check that we destroy the promise and free the memory.
+
+ // CHECK: invoke void @_Z9may_throwv(
+ // CHECK-NEXT: to label %{{.+}} unwind label %[[CatchPad:.+]]
+
+ // CHECK: [[CatchPad]]:
+ // CHECK-NEXT: landingpad
+ // CHECK-NEXT: catch i8* null
+ // CHECK: call void @_ZN7CleanupD1Ev(
+ // CHECK: br label %[[Catch:.+]]
+
+ // CHECK: [[Catch]]:
+ // CHECK: call i8* @__cxa_begin_catch(
+ // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJvEE12promise_type19unhandled_exceptionEv(
+ // CHECK: invoke void @__cxa_end_catch()
+ // CHECK-NEXT: to label %[[Cont:.+]] unwind
+
+ // CHECK: [[Cont]]:
+ // CHECK-NEXT: br label %[[Cont2:.+]]
+ // CHECK: [[Cont2]]:
+ // CHECK-NEXT: br label %[[Cleanup:.+]]
+
+ // CHECK: [[Cleanup]]:
+ // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJvEE12promise_typeD1Ev(
+ // CHECK: %[[Mem0:.+]] = call i8* @llvm.coro.free(
+ // CHECK: call void @_ZdlPv(i8* %[[Mem0]]
+
+ // CHECK: [[Dealloc]]:
+ // CHECK: %[[Mem:.+]] = call i8* @llvm.coro.free(
+ // CHECK: call void @_ZdlPv(i8* %[[Mem]])
+
+ co_return;
+}
+
+// CHECK-LABEL: define{{.*}} void @_Z1gv(
+void g() {
+ for (;;)
+ co_await suspend_always{};
+ // Since this is the endless loop there should be no fallthrough handler (call to 'return_void').
+ // CHECK-NOT: call void @_ZNSt12experimental16coroutine_traitsIJvEE12promise_type11return_voidEv
+}
diff --git a/clang/test/CodeGenCoroutines/coro-cleanup.cpp b/clang/test/CodeGenCoroutines/coro-cleanup.cpp
index 7ef614e817d2..8535d5105171 100644
--- a/clang/test/CodeGenCoroutines/coro-cleanup.cpp
+++ b/clang/test/CodeGenCoroutines/coro-cleanup.cpp
@@ -1,7 +1,7 @@
// Verify that coroutine promise and allocated memory are freed up on exception.
-// RUN: %clang_cc1 -std=c++1z -fcoroutines-ts -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s -fexceptions -fcxx-exceptions -disable-llvm-passes | FileCheck %s
+// RUN: %clang_cc1 -std=c++20 -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s -fexceptions -fcxx-exceptions -disable-llvm-passes | FileCheck %s
-namespace std::experimental {
+namespace std {
template <typename... T> struct coroutine_traits;
template <class Promise = void> struct coroutine_handle {
@@ -14,15 +14,15 @@ template <> struct coroutine_handle<void> {
template <class PromiseType>
coroutine_handle(coroutine_handle<PromiseType>) noexcept;
};
-}
+} // namespace std
struct suspend_always {
bool await_ready() noexcept;
- void await_suspend(std::experimental::coroutine_handle<>) noexcept;
+ void await_suspend(std::coroutine_handle<>) noexcept;
void await_resume() noexcept;
};
-template <> struct std::experimental::coroutine_traits<void> {
+template <> struct std::coroutine_traits<void> {
struct promise_type {
void get_return_object() noexcept;
suspend_always initial_suspend() noexcept;
@@ -43,7 +43,7 @@ void f() {
// If promise constructor throws, check that we free the memory.
- // CHECK: invoke void @_ZNSt12experimental16coroutine_traitsIJvEE12promise_typeC1Ev(
+ // CHECK: invoke void @_ZNSt16coroutine_traitsIJvEE12promise_typeC1Ev(
// CHECK-NEXT: to label %{{.+}} unwind label %[[DeallocPad:.+]]
// CHECK: [[DeallocPad]]:
@@ -67,7 +67,7 @@ void f() {
// CHECK: [[Catch]]:
// CHECK: call i8* @__cxa_begin_catch(
- // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJvEE12promise_type19unhandled_exceptionEv(
+ // CHECK: call void @_ZNSt16coroutine_traitsIJvEE12promise_type19unhandled_exceptionEv(
// CHECK: invoke void @__cxa_end_catch()
// CHECK-NEXT: to label %[[Cont:.+]] unwind
@@ -77,7 +77,7 @@ void f() {
// CHECK-NEXT: br label %[[Cleanup:.+]]
// CHECK: [[Cleanup]]:
- // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJvEE12promise_typeD1Ev(
+ // CHECK: call void @_ZNSt16coroutine_traitsIJvEE12promise_typeD1Ev(
// CHECK: %[[Mem0:.+]] = call i8* @llvm.coro.free(
// CHECK: call void @_ZdlPv(i8* %[[Mem0]]
@@ -93,5 +93,5 @@ void g() {
for (;;)
co_await suspend_always{};
// Since this is the endless loop there should be no fallthrough handler (call to 'return_void').
- // CHECK-NOT: call void @_ZNSt12experimental16coroutine_traitsIJvEE12promise_type11return_voidEv
+ // CHECK-NOT: call void @_ZNSt16coroutine_traitsIJvEE12promise_type11return_voidEv
}
diff --git a/clang/test/CodeGenCoroutines/coro-dest-slot-exp-namespace.cpp b/clang/test/CodeGenCoroutines/coro-dest-slot-exp-namespace.cpp
new file mode 100644
index 000000000000..6ea24b0be1a5
--- /dev/null
+++ b/clang/test/CodeGenCoroutines/coro-dest-slot-exp-namespace.cpp
@@ -0,0 +1,40 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fcoroutines-ts -std=c++14 -emit-llvm %s -o - -disable-llvm-passes | FileCheck %s
+
+#include "Inputs/coroutine-exp-namespace.h"
+
+using namespace std::experimental;
+
+struct coro {
+ struct promise_type {
+ coro get_return_object();
+ suspend_always initial_suspend();
+ suspend_never final_suspend() noexcept;
+ void return_void();
+ static void unhandled_exception();
+ };
+};
+
+extern "C" coro f(int) { co_return; }
+// Verify that cleanup.dest.slot is eliminated in a coroutine.
+// CHECK-LABEL: f(
+// CHECK: %[[INIT_SUSPEND:.+]] = call i8 @llvm.coro.suspend(
+// CHECK-NEXT: switch i8 %[[INIT_SUSPEND]], label
+// CHECK-NEXT: i8 0, label %[[INIT_READY:.+]]
+// CHECK-NEXT: i8 1, label %[[INIT_CLEANUP:.+]]
+// CHECK-NEXT: ]
+// CHECK: %[[CLEANUP_DEST0:.+]] = phi i32 [ 0, %[[INIT_READY]] ], [ 2, %[[INIT_CLEANUP]] ]
+
+// CHECK: %[[FINAL_SUSPEND:.+]] = call i8 @llvm.coro.suspend(
+// CHECK-NEXT: switch i8 %{{.*}}, label %coro.ret [
+// CHECK-NEXT: i8 0, label %[[FINAL_READY:.+]]
+// CHECK-NEXT: i8 1, label %[[FINAL_CLEANUP:.+]]
+// CHECK-NEXT: ]
+
+// CHECK: call void @_ZNSt12experimental13coroutines_v113suspend_never12await_resumeEv(
+// CHECK: %[[CLEANUP_DEST1:.+]] = phi i32 [ 0, %[[FINAL_READY]] ], [ 2, %[[FINAL_CLEANUP]] ]
+// CHECK: %[[CLEANUP_DEST2:.+]] = phi i32 [ %[[CLEANUP_DEST0]], %{{.+}} ], [ %[[CLEANUP_DEST1]], %{{.+}} ], [ 0, %{{.+}} ]
+// CHECK: call i8* @llvm.coro.free(
+// CHECK: switch i32 %[[CLEANUP_DEST2]], label %{{.+}} [
+// CHECK-NEXT: i32 0
+// CHECK-NEXT: i32 2
+// CHECK-NEXT: ]
diff --git a/clang/test/CodeGenCoroutines/coro-dest-slot.cpp b/clang/test/CodeGenCoroutines/coro-dest-slot.cpp
index c7129df11526..c986130bc3ba 100644
--- a/clang/test/CodeGenCoroutines/coro-dest-slot.cpp
+++ b/clang/test/CodeGenCoroutines/coro-dest-slot.cpp
@@ -1,8 +1,8 @@
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fcoroutines-ts -std=c++14 -emit-llvm %s -o - -disable-llvm-passes | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -emit-llvm %s -o - -disable-llvm-passes | FileCheck %s
#include "Inputs/coroutine.h"
-using namespace std::experimental;
+using namespace std;
struct coro {
struct promise_type {
@@ -30,7 +30,7 @@ extern "C" coro f(int) { co_return; }
// CHECK-NEXT: i8 1, label %[[FINAL_CLEANUP:.+]]
// CHECK-NEXT: ]
-// CHECK: call void @_ZNSt12experimental13coroutines_v113suspend_never12await_resumeEv(
+// CHECK: call void @_ZNSt13suspend_never12await_resumeEv(
// CHECK: %[[CLEANUP_DEST1:.+]] = phi i32 [ 0, %[[FINAL_READY]] ], [ 2, %[[FINAL_CLEANUP]] ]
// CHECK: %[[CLEANUP_DEST2:.+]] = phi i32 [ %[[CLEANUP_DEST0]], %{{.+}} ], [ %[[CLEANUP_DEST1]], %{{.+}} ], [ 0, %{{.+}} ]
// CHECK: call i8* @llvm.coro.free(
diff --git a/clang/test/CodeGenCoroutines/coro-dwarf-exp-namespace.cpp b/clang/test/CodeGenCoroutines/coro-dwarf-exp-namespace.cpp
new file mode 100644
index 000000000000..2849f8d80f79
--- /dev/null
+++ b/clang/test/CodeGenCoroutines/coro-dwarf-exp-namespace.cpp
@@ -0,0 +1,72 @@
+// RUN: %clang_cc1 -disable-llvm-optzns -std=c++2a -fcoroutines-ts \
+// RUN: -triple=x86_64 -dwarf-version=4 -debug-info-kind=limited \
+// RUN: -emit-llvm -o - %s | \
+// RUN: FileCheck %s --implicit-check-not=DILocalVariable
+
+namespace std::experimental {
+template <typename... T> struct coroutine_traits;
+
+template <class Promise = void> struct coroutine_handle {
+ coroutine_handle() = default;
+ static coroutine_handle from_address(void *) noexcept;
+};
+template <> struct coroutine_handle<void> {
+ static coroutine_handle from_address(void *) noexcept;
+ coroutine_handle() = default;
+ template <class PromiseType>
+ coroutine_handle(coroutine_handle<PromiseType>) noexcept;
+};
+} // namespace std::experimental
+
+struct suspend_always {
+ bool await_ready() noexcept;
+ void await_suspend(std::experimental::coroutine_handle<>) noexcept;
+ void await_resume() noexcept;
+};
+
+template <typename... Args> struct std::experimental::coroutine_traits<void, Args...> {
+ struct promise_type {
+ void get_return_object() noexcept;
+ suspend_always initial_suspend() noexcept;
+ suspend_always final_suspend() noexcept;
+ void return_void() noexcept;
+ promise_type();
+ ~promise_type() noexcept;
+ void unhandled_exception() noexcept;
+ };
+};
+
+// TODO: Not supported yet
+struct CopyOnly {
+ int val;
+ CopyOnly(const CopyOnly &) noexcept;
+ CopyOnly(CopyOnly &&) = delete;
+ ~CopyOnly();
+};
+
+struct MoveOnly {
+ int val;
+ MoveOnly(const MoveOnly &) = delete;
+ MoveOnly(MoveOnly &&) noexcept;
+ ~MoveOnly();
+};
+
+struct MoveAndCopy {
+ int val;
+ MoveAndCopy(const MoveAndCopy &) noexcept;
+ MoveAndCopy(MoveAndCopy &&) noexcept;
+ ~MoveAndCopy();
+};
+
+void consume(int, int, int) noexcept;
+
+void f_coro(int val, MoveOnly moParam, MoveAndCopy mcParam) {
+ consume(val, moParam.val, mcParam.val);
+ co_return;
+}
+
+// CHECK: ![[SP:[0-9]+]] = distinct !DISubprogram(name: "f_coro", linkageName: "_Z6f_coroi8MoveOnly11MoveAndCopy"
+// CHECK: !{{[0-9]+}} = !DILocalVariable(name: "val", arg: 1, scope: ![[SP]], file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}})
+// CHECK: !{{[0-9]+}} = !DILocalVariable(name: "moParam", arg: 2, scope: ![[SP]], file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}})
+// CHECK: !{{[0-9]+}} = !DILocalVariable(name: "mcParam", arg: 3, scope: ![[SP]], file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}})
+// CHECK: !{{[0-9]+}} = !DILocalVariable(name: "__promise",
diff --git a/clang/test/CodeGenCoroutines/coro-dwarf.cpp b/clang/test/CodeGenCoroutines/coro-dwarf.cpp
index 2849f8d80f79..7914babe5483 100644
--- a/clang/test/CodeGenCoroutines/coro-dwarf.cpp
+++ b/clang/test/CodeGenCoroutines/coro-dwarf.cpp
@@ -1,9 +1,9 @@
-// RUN: %clang_cc1 -disable-llvm-optzns -std=c++2a -fcoroutines-ts \
+// RUN: %clang_cc1 -disable-llvm-optzns -std=c++20 \
// RUN: -triple=x86_64 -dwarf-version=4 -debug-info-kind=limited \
// RUN: -emit-llvm -o - %s | \
// RUN: FileCheck %s --implicit-check-not=DILocalVariable
-namespace std::experimental {
+namespace std {
template <typename... T> struct coroutine_traits;
template <class Promise = void> struct coroutine_handle {
@@ -16,15 +16,15 @@ template <> struct coroutine_handle<void> {
template <class PromiseType>
coroutine_handle(coroutine_handle<PromiseType>) noexcept;
};
-} // namespace std::experimental
+} // namespace std
struct suspend_always {
bool await_ready() noexcept;
- void await_suspend(std::experimental::coroutine_handle<>) noexcept;
+ void await_suspend(std::coroutine_handle<>) noexcept;
void await_resume() noexcept;
};
-template <typename... Args> struct std::experimental::coroutine_traits<void, Args...> {
+template <typename... Args> struct std::coroutine_traits<void, Args...> {
struct promise_type {
void get_return_object() noexcept;
suspend_always initial_suspend() noexcept;
diff --git a/clang/test/CodeGenCoroutines/coro-eh-cleanup-exp-namespace.cpp b/clang/test/CodeGenCoroutines/coro-eh-cleanup-exp-namespace.cpp
new file mode 100644
index 000000000000..4cd81929f547
--- /dev/null
+++ b/clang/test/CodeGenCoroutines/coro-eh-cleanup-exp-namespace.cpp
@@ -0,0 +1,88 @@
+// RUN: %clang_cc1 -std=c++1z -fcoroutines-ts -triple=x86_64-pc-windows-msvc18.0.0 -emit-llvm %s -o - -fexceptions -fcxx-exceptions -disable-llvm-passes | FileCheck %s
+// RUN: %clang_cc1 -std=c++1z -fcoroutines-ts -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s -fexceptions -fcxx-exceptions -disable-llvm-passes | FileCheck --check-prefix=CHECK-LPAD %s
+
+namespace std::experimental {
+template <typename R, typename... T> struct coroutine_traits {
+ using promise_type = typename R::promise_type;
+};
+
+template <class Promise = void> struct coroutine_handle;
+
+template <> struct coroutine_handle<void> {
+ static coroutine_handle from_address(void *) noexcept;
+ coroutine_handle() = default;
+ template <class PromiseType>
+ coroutine_handle(coroutine_handle<PromiseType>) noexcept;
+};
+template <class Promise> struct coroutine_handle : coroutine_handle<void> {
+ coroutine_handle() = default;
+ static coroutine_handle from_address(void *) noexcept;
+};
+} // namespace std::experimental
+
+struct suspend_always {
+ bool await_ready() noexcept;
+ void await_suspend(std::experimental::coroutine_handle<>) noexcept;
+ void await_resume() noexcept;
+};
+
+struct coro_t {
+ struct promise_type {
+ coro_t get_return_object() noexcept;
+ suspend_always initial_suspend() noexcept;
+ suspend_always final_suspend() noexcept;
+ void return_void() noexcept;
+ void unhandled_exception() noexcept;
+ };
+};
+
+struct Cleanup {
+ ~Cleanup();
+};
+void may_throw();
+
+coro_t f() {
+ Cleanup x;
+ may_throw();
+ co_return;
+}
+
+// CHECK: @"?f@@YA?AUcoro_t@@XZ"(
+// CHECK: invoke void @"?may_throw@@YAXXZ"()
+// CHECK: to label %[[CONT:.+]] unwind label %[[EHCLEANUP:.+]]
+// CHECK: [[EHCLEANUP]]:
+// CHECK: %[[INNERPAD:.+]] = cleanuppad within none []
+// CHECK: call void @"??1Cleanup@@QEAA at XZ"(
+// CHECK: cleanupret from %{{.+}} unwind label %[[CATCHDISPATCH:.+]]
+
+// CHECK: [[CATCHDISPATCH]]:
+// CHECK: catchswitch within none [label %[[CATCHPAD:.+]]] unwind label %[[COROENDBB:.+]]
+// CHECK: [[CATCHPAD]]:
+// CHECK: call void @"?unhandled_exception at promise_type@coro_t@@QEAAXXZ"
+
+// CHECK: [[COROENDBB]]:
+// CHECK-NEXT: %[[CLPAD:.+]] = cleanuppad within none
+// CHECK-NEXT: call i1 @llvm.coro.end(i8* null, i1 true) [ "funclet"(token %[[CLPAD]]) ]
+// CHECK-NEXT: cleanupret from %[[CLPAD]] unwind label
+
+// CHECK-LPAD: @_Z1fv(
+// CHECK-LPAD: invoke void @_Z9may_throwv()
+// CHECK-LPAD: to label %[[CONT:.+]] unwind label %[[EHCLEANUP:.+]]
+// CHECK-LPAD: [[EHCLEANUP]]:
+// CHECK-LPAD: landingpad { i8*, i32 }
+// CHECK-LPAD: catch
+// CHECK-LPAD: call void @_ZN7CleanupD1Ev(
+// CHECK-LPAD: call i8* @__cxa_begin_catch
+// CHECK-LPAD: call void @_ZN6coro_t12promise_type19unhandled_exceptionEv
+// CHECK-LPAD: invoke void @__cxa_end_catch()
+// CHECK-LPAD: to label %{{.+}} unwind label %[[UNWINDBB:.+]]
+
+// CHECK-LPAD: [[UNWINDBB]]:
+// CHECK-LPAD: %[[I1RESUME:.+]] = call i1 @llvm.coro.end(i8* null, i1 true)
+// CHECK-LPAD: br i1 %[[I1RESUME]], label %[[EHRESUME:.+]], label
+// CHECK-LPAD: [[EHRESUME]]:
+// CHECK-LPAD-NEXT: %[[exn:.+]] = load i8*, i8** %exn.slot, align 8
+// CHECK-LPAD-NEXT: %[[sel:.+]] = load i32, i32* %ehselector.slot, align 4
+// CHECK-LPAD-NEXT: %[[val1:.+]] = insertvalue { i8*, i32 } undef, i8* %[[exn]], 0
+// CHECK-LPAD-NEXT: %[[val2:.+]] = insertvalue { i8*, i32 } %[[val1]], i32 %[[sel]], 1
+// CHECK-LPAD-NEXT: resume { i8*, i32 } %[[val2]]
diff --git a/clang/test/CodeGenCoroutines/coro-eh-cleanup.cpp b/clang/test/CodeGenCoroutines/coro-eh-cleanup.cpp
index 9801151b4f14..85777bb88fad 100644
--- a/clang/test/CodeGenCoroutines/coro-eh-cleanup.cpp
+++ b/clang/test/CodeGenCoroutines/coro-eh-cleanup.cpp
@@ -1,7 +1,7 @@
-// RUN: %clang_cc1 -std=c++1z -fcoroutines-ts -triple=x86_64-pc-windows-msvc18.0.0 -emit-llvm %s -o - -fexceptions -fcxx-exceptions -disable-llvm-passes | FileCheck %s
-// RUN: %clang_cc1 -std=c++1z -fcoroutines-ts -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s -fexceptions -fcxx-exceptions -disable-llvm-passes | FileCheck --check-prefix=CHECK-LPAD %s
+// RUN: %clang_cc1 -std=c++20 -triple=x86_64-pc-windows-msvc18.0.0 -emit-llvm %s -o - -fexceptions -fcxx-exceptions -disable-llvm-passes | FileCheck %s
+// RUN: %clang_cc1 -std=c++20 -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s -fexceptions -fcxx-exceptions -disable-llvm-passes | FileCheck --check-prefix=CHECK-LPAD %s
-namespace std::experimental {
+namespace std {
template <typename R, typename... T> struct coroutine_traits {
using promise_type = typename R::promise_type;
};
@@ -18,11 +18,11 @@ template <class Promise> struct coroutine_handle: coroutine_handle<void> {
coroutine_handle() = default;
static coroutine_handle from_address(void *) noexcept;
};
-}
+} // namespace std
struct suspend_always {
bool await_ready() noexcept;
- void await_suspend(std::experimental::coroutine_handle<>) noexcept;
+ void await_suspend(std::coroutine_handle<>) noexcept;
void await_resume() noexcept;
};
diff --git a/clang/test/CodeGenCoroutines/coro-gro-exp-namespace.cpp b/clang/test/CodeGenCoroutines/coro-gro-exp-namespace.cpp
new file mode 100644
index 000000000000..90ab5fe35004
--- /dev/null
+++ b/clang/test/CodeGenCoroutines/coro-gro-exp-namespace.cpp
@@ -0,0 +1,88 @@
+// Verifies lifetime of __gro local variable
+// Verify that coroutine promise and allocated memory are freed up on exception.
+// RUN: %clang_cc1 -std=c++1z -fcoroutines-ts -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s -disable-llvm-passes | FileCheck %s
+
+namespace std::experimental {
+template <typename... T> struct coroutine_traits;
+
+template <class Promise = void> struct coroutine_handle {
+ coroutine_handle() = default;
+ static coroutine_handle from_address(void *) noexcept;
+};
+template <> struct coroutine_handle<void> {
+ static coroutine_handle from_address(void *) noexcept;
+ coroutine_handle() = default;
+ template <class PromiseType>
+ coroutine_handle(coroutine_handle<PromiseType>) noexcept;
+};
+} // namespace std::experimental
+
+struct suspend_always {
+ bool await_ready() noexcept;
+ void await_suspend(std::experimental::coroutine_handle<>) noexcept;
+ void await_resume() noexcept;
+};
+
+struct GroType {
+ ~GroType();
+ operator int() noexcept;
+};
+
+template <> struct std::experimental::coroutine_traits<int> {
+ struct promise_type {
+ GroType get_return_object() noexcept;
+ suspend_always initial_suspend() noexcept;
+ suspend_always final_suspend() noexcept;
+ void return_void() noexcept;
+ promise_type();
+ ~promise_type();
+ void unhandled_exception() noexcept;
+ };
+};
+
+struct Cleanup {
+ ~Cleanup();
+};
+void doSomething() noexcept;
+
+// CHECK: define{{.*}} i32 @_Z1fv(
+int f() {
+ // CHECK: %[[RetVal:.+]] = alloca i32
+ // CHECK: %[[GroActive:.+]] = alloca i1
+
+ // CHECK: %[[Size:.+]] = call i64 @llvm.coro.size.i64()
+ // CHECK: call noalias nonnull i8* @_Znwm(i64 %[[Size]])
+ // CHECK: store i1 false, i1* %[[GroActive]]
+ // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJiEE12promise_typeC1Ev(
+ // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJiEE12promise_type17get_return_objectEv(
+ // CHECK: store i1 true, i1* %[[GroActive]]
+
+ Cleanup cleanup;
+ doSomething();
+ co_return;
+
+ // CHECK: call void @_Z11doSomethingv(
+ // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJiEE12promise_type11return_voidEv(
+ // CHECK: call void @_ZN7CleanupD1Ev(
+
+ // Destroy promise and free the memory.
+
+ // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJiEE12promise_typeD1Ev(
+ // CHECK: %[[Mem:.+]] = call i8* @llvm.coro.free(
+ // CHECK: call void @_ZdlPv(i8* %[[Mem]])
+
+ // Initialize retval from Gro and destroy Gro
+
+ // CHECK: %[[Conv:.+]] = call i32 @_ZN7GroTypecviEv(
+ // CHECK: store i32 %[[Conv]], i32* %[[RetVal]]
+ // CHECK: %[[IsActive:.+]] = load i1, i1* %[[GroActive]]
+ // CHECK: br i1 %[[IsActive]], label %[[CleanupGro:.+]], label %[[Done:.+]]
+
+ // CHECK: [[CleanupGro]]:
+ // CHECK: call void @_ZN7GroTypeD1Ev(
+ // CHECK: br label %[[Done]]
+
+ // CHECK: [[Done]]:
+ // CHECK: %[[LoadRet:.+]] = load i32, i32* %[[RetVal]]
+ // CHECK: ret i32 %[[LoadRet]]
+}
diff --git a/clang/test/CodeGenCoroutines/coro-gro-nrvo-exp-namespace.cpp b/clang/test/CodeGenCoroutines/coro-gro-nrvo-exp-namespace.cpp
new file mode 100644
index 000000000000..32e95a652811
--- /dev/null
+++ b/clang/test/CodeGenCoroutines/coro-gro-nrvo-exp-namespace.cpp
@@ -0,0 +1,85 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fcoroutines-ts -std=c++14 -emit-llvm %s -o - -disable-llvm-passes | FileCheck %s
+
+#include "Inputs/coroutine-exp-namespace.h"
+
+using namespace std::experimental;
+
+namespace std {
+
+struct nothrow_t {};
+constexpr nothrow_t nothrow = {};
+
+} // end namespace std
+
+// Required when get_return_object_on_allocation_failure() is defined by
+// the promise.
+void *operator new(__SIZE_TYPE__ __sz, const std::nothrow_t &) noexcept;
+void operator delete(void *__p, const std::nothrow_t &)noexcept;
+
+template <class RetObject>
+struct promise_type {
+ RetObject get_return_object();
+ suspend_always initial_suspend();
+ suspend_never final_suspend() noexcept;
+ void return_void();
+ static void unhandled_exception();
+};
+
+struct coro {
+ using promise_type = promise_type<coro>;
+ coro(coro const &);
+ struct Impl;
+ Impl *impl;
+};
+
+// Verify that the NRVO is applied to the Gro object.
+// CHECK-LABEL: define{{.*}} void @_Z1fi(%struct.coro* noalias sret(%struct.coro) align 8 %agg.result, i32 %0)
+coro f(int) {
+ // CHECK: %call = call noalias nonnull i8* @_Znwm(
+ // CHECK-NEXT: br label %[[CoroInit:.*]]
+
+ // CHECK: {{.*}}[[CoroInit]]:
+ // CHECK: store i1 false, i1* %gro.active
+ // CHECK: call void @{{.*get_return_objectEv}}(%struct.coro* sret(%struct.coro) align 8 %agg.result
+ // CHECK-NEXT: store i1 true, i1* %gro.active
+ co_return;
+}
+
+template <class RetObject>
+struct promise_type_with_on_alloc_failure {
+ static RetObject get_return_object_on_allocation_failure();
+ RetObject get_return_object();
+ suspend_always initial_suspend();
+ suspend_never final_suspend() noexcept;
+ void return_void();
+ static void unhandled_exception();
+};
+
+struct coro_two {
+ using promise_type = promise_type_with_on_alloc_failure<coro_two>;
+ coro_two(coro_two const &);
+ struct Impl;
+ Impl *impl;
+};
+
+// Verify that the NRVO is applied to the Gro object.
+// CHECK-LABEL: define{{.*}} void @_Z1hi(%struct.coro_two* noalias sret(%struct.coro_two) align 8 %agg.result, i32 %0)
+coro_two h(int) {
+
+ // CHECK: %call = call noalias i8* @_ZnwmRKSt9nothrow_t
+ // CHECK-NEXT: %[[CheckNull:.*]] = icmp ne i8* %call, null
+ // CHECK-NEXT: br i1 %[[CheckNull]], label %[[InitOnSuccess:.*]], label %[[InitOnFailure:.*]]
+
+ // CHECK: {{.*}}[[InitOnFailure]]:
+ // CHECK-NEXT: call void @{{.*get_return_object_on_allocation_failureEv}}(%struct.coro_two* sret(%struct.coro_two) align 8 %agg.result
+ // CHECK-NEXT: br label %[[RetLabel:.*]]
+
+ // CHECK: {{.*}}[[InitOnSuccess]]:
+ // CHECK: store i1 false, i1* %gro.active
+ // CHECK: call void @{{.*get_return_objectEv}}(%struct.coro_two* sret(%struct.coro_two) align 8 %agg.result
+ // CHECK-NEXT: store i1 true, i1* %gro.active
+
+ // CHECK: [[RetLabel]]:
+ // CHECK-NEXT: ret void
+ co_return;
+}
diff --git a/clang/test/CodeGenCoroutines/coro-gro-nrvo.cpp b/clang/test/CodeGenCoroutines/coro-gro-nrvo.cpp
index 940617197bc9..0a8366bf45b9 100644
--- a/clang/test/CodeGenCoroutines/coro-gro-nrvo.cpp
+++ b/clang/test/CodeGenCoroutines/coro-gro-nrvo.cpp
@@ -1,8 +1,8 @@
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fcoroutines-ts -std=c++14 -emit-llvm %s -o - -disable-llvm-passes | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fcoroutines-ts -std=c++20 -emit-llvm %s -o - -disable-llvm-passes | FileCheck %s
#include "Inputs/coroutine.h"
-using namespace std::experimental;
+using namespace std;
namespace std {
diff --git a/clang/test/CodeGenCoroutines/coro-gro.cpp b/clang/test/CodeGenCoroutines/coro-gro.cpp
index dd07185e3f84..bcf474cc22c3 100644
--- a/clang/test/CodeGenCoroutines/coro-gro.cpp
+++ b/clang/test/CodeGenCoroutines/coro-gro.cpp
@@ -1,8 +1,8 @@
// Verifies lifetime of __gro local variable
// Verify that coroutine promise and allocated memory are freed up on exception.
-// RUN: %clang_cc1 -std=c++1z -fcoroutines-ts -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s -disable-llvm-passes | FileCheck %s
+// RUN: %clang_cc1 -std=c++20 -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s -disable-llvm-passes | FileCheck %s
-namespace std::experimental {
+namespace std {
template <typename... T> struct coroutine_traits;
template <class Promise = void> struct coroutine_handle {
@@ -15,11 +15,11 @@ template <> struct coroutine_handle<void> {
template <class PromiseType>
coroutine_handle(coroutine_handle<PromiseType>) noexcept;
};
-}
+} // namespace std
struct suspend_always {
bool await_ready() noexcept;
- void await_suspend(std::experimental::coroutine_handle<>) noexcept;
+ void await_suspend(std::coroutine_handle<>) noexcept;
void await_resume() noexcept;
};
@@ -28,7 +28,7 @@ struct GroType {
operator int() noexcept;
};
-template <> struct std::experimental::coroutine_traits<int> {
+template <> struct std::coroutine_traits<int> {
struct promise_type {
GroType get_return_object() noexcept;
suspend_always initial_suspend() noexcept;
@@ -51,8 +51,8 @@ int f() {
// CHECK: %[[Size:.+]] = call i64 @llvm.coro.size.i64()
// CHECK: call noalias nonnull i8* @_Znwm(i64 %[[Size]])
// CHECK: store i1 false, i1* %[[GroActive]]
- // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJiEE12promise_typeC1Ev(
- // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJiEE12promise_type17get_return_objectEv(
+ // CHECK: call void @_ZNSt16coroutine_traitsIJiEE12promise_typeC1Ev(
+ // CHECK: call void @_ZNSt16coroutine_traitsIJiEE12promise_type17get_return_objectEv(
// CHECK: store i1 true, i1* %[[GroActive]]
Cleanup cleanup;
@@ -60,12 +60,12 @@ int f() {
co_return;
// CHECK: call void @_Z11doSomethingv(
- // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJiEE12promise_type11return_voidEv(
+ // CHECK: call void @_ZNSt16coroutine_traitsIJiEE12promise_type11return_voidEv(
// CHECK: call void @_ZN7CleanupD1Ev(
// Destroy promise and free the memory.
- // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJiEE12promise_typeD1Ev(
+ // CHECK: call void @_ZNSt16coroutine_traitsIJiEE12promise_typeD1Ev(
// CHECK: %[[Mem:.+]] = call i8* @llvm.coro.free(
// CHECK: call void @_ZdlPv(i8* %[[Mem]])
diff --git a/clang/test/CodeGenCoroutines/coro-lambda-exp-namespace.cpp b/clang/test/CodeGenCoroutines/coro-lambda-exp-namespace.cpp
new file mode 100644
index 000000000000..2c9c446be080
--- /dev/null
+++ b/clang/test/CodeGenCoroutines/coro-lambda-exp-namespace.cpp
@@ -0,0 +1,61 @@
+// Verify that we synthesized the coroutine for a lambda inside of a function template.
+// RUN: %clang_cc1 -std=c++1z -fcoroutines-ts -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s -fexceptions -fcxx-exceptions -disable-llvm-passes | FileCheck %s
+
+namespace std::experimental {
+template <typename R, typename... T> struct coroutine_traits {
+ using promise_type = typename R::promise_type;
+};
+
+template <class Promise = void> struct coroutine_handle;
+template <> struct coroutine_handle<void> {
+ static coroutine_handle from_address(void *) noexcept;
+ coroutine_handle() = default;
+ template <class PromiseType>
+ coroutine_handle(coroutine_handle<PromiseType>) noexcept;
+};
+template <class Promise> struct coroutine_handle : coroutine_handle<void> {
+ coroutine_handle() = default;
+ static coroutine_handle from_address(void *) noexcept;
+};
+} // namespace std::experimental
+
+struct suspend_always {
+ bool await_ready() noexcept;
+ void await_suspend(std::experimental::coroutine_handle<>) noexcept;
+ void await_resume() noexcept;
+};
+
+struct Task {
+ struct promise_type {
+ Task get_return_object();
+ void return_void() {}
+ suspend_always initial_suspend() noexcept;
+ suspend_always final_suspend() noexcept;
+ void unhandled_exception() noexcept;
+ };
+};
+
+template <typename _AwrT> auto SyncAwait(_AwrT &&A) {
+ if (!A.await_ready()) {
+ auto AwaitAsync = [&]() -> Task {
+ try {
+ (void)(co_await A);
+ } catch (...) {
+ }
+ };
+ Task t = AwaitAsync();
+ }
+ return A.await_resume();
+}
+
+void f() {
+ suspend_always test;
+ SyncAwait(test);
+}
+
+// Verify that we synthesized the coroutine for a lambda inside SyncAwait
+// CHECK-LABEL: define linkonce_odr void @_ZZ9SyncAwaitIR14suspend_alwaysEDaOT_ENKUlvE_clEv(
+// CHECK: alloca %"struct.Task::promise_type"
+// CHECK: call token @llvm.coro.id(
+// CHECK: call i8 @llvm.coro.suspend(
+// CHECK: call i1 @llvm.coro.end(
diff --git a/clang/test/CodeGenCoroutines/coro-lambda.cpp b/clang/test/CodeGenCoroutines/coro-lambda.cpp
index cd3256dc07ef..26c51070f9e2 100644
--- a/clang/test/CodeGenCoroutines/coro-lambda.cpp
+++ b/clang/test/CodeGenCoroutines/coro-lambda.cpp
@@ -1,7 +1,7 @@
// Verify that we synthesized the coroutine for a lambda inside of a function template.
-// RUN: %clang_cc1 -std=c++1z -fcoroutines-ts -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s -fexceptions -fcxx-exceptions -disable-llvm-passes | FileCheck %s
+// RUN: %clang_cc1 -std=c++20 -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s -fexceptions -fcxx-exceptions -disable-llvm-passes | FileCheck %s
-namespace std::experimental {
+namespace std {
template <typename R, typename... T> struct coroutine_traits {
using promise_type = typename R::promise_type;
};
@@ -17,11 +17,11 @@ template <class Promise> struct coroutine_handle : coroutine_handle<void> {
coroutine_handle() = default;
static coroutine_handle from_address(void *) noexcept;
};
-}
+} // namespace std
struct suspend_always {
bool await_ready() noexcept;
- void await_suspend(std::experimental::coroutine_handle<>) noexcept;
+ void await_suspend(std::coroutine_handle<>) noexcept;
void await_resume() noexcept;
};
diff --git a/clang/test/CodeGenCoroutines/coro-newpm-pipeline-exp-namespace.cpp b/clang/test/CodeGenCoroutines/coro-newpm-pipeline-exp-namespace.cpp
new file mode 100644
index 000000000000..869e98ecdb9e
--- /dev/null
+++ b/clang/test/CodeGenCoroutines/coro-newpm-pipeline-exp-namespace.cpp
@@ -0,0 +1,45 @@
+// Tests that coroutine passes are added to and run by the new pass manager
+// pipeline, at -O0 and above.
+
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm-bc -o /dev/null \
+// RUN: -fexperimental-new-pass-manager -fdebug-pass-manager -fcoroutines-ts \
+// RUN: -O0 %s 2>&1 | FileCheck %s --check-prefixes=CHECK-ALL
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm-bc -o /dev/null \
+// RUN: -fexperimental-new-pass-manager -fdebug-pass-manager -fcoroutines-ts \
+// RUN: -O1 %s 2>&1 | FileCheck %s --check-prefixes=CHECK-ALL,CHECK-OPT
+//
+// CHECK-ALL: Running pass:{{.*}}CoroEarlyPass
+//
+// CHECK-ALL: Running pass: CoroSplitPass on (_Z3foov)
+// CHECK-OPT: Running pass:{{.*}}CoroElidePass{{.*}} on {{.*}}_Z3foov{{.*}}
+//
+// CHECK-ALL: Running pass:{{.*}}CoroCleanupPass
+
+namespace std {
+namespace experimental {
+
+struct handle {};
+
+struct awaitable {
+ bool await_ready() noexcept { return false; }
+ void await_suspend(handle) noexcept {}
+ bool await_resume() noexcept { return true; }
+};
+
+template <typename T> struct coroutine_handle {
+ static handle from_address(void *address) noexcept { return {}; }
+};
+
+template <typename T = void> struct coroutine_traits {
+ struct promise_type {
+ awaitable initial_suspend() { return {}; }
+ awaitable final_suspend() noexcept { return {}; }
+ void return_void() {}
+ T get_return_object() { return T(); }
+ void unhandled_exception() {}
+ };
+};
+} // namespace experimental
+} // namespace std
+
+void foo() { co_return; }
diff --git a/clang/test/CodeGenCoroutines/coro-newpm-pipeline.cpp b/clang/test/CodeGenCoroutines/coro-newpm-pipeline.cpp
index 869e98ecdb9e..0aad12e5af4b 100644
--- a/clang/test/CodeGenCoroutines/coro-newpm-pipeline.cpp
+++ b/clang/test/CodeGenCoroutines/coro-newpm-pipeline.cpp
@@ -2,10 +2,10 @@
// pipeline, at -O0 and above.
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm-bc -o /dev/null \
-// RUN: -fexperimental-new-pass-manager -fdebug-pass-manager -fcoroutines-ts \
+// RUN: -fexperimental-new-pass-manager -fdebug-pass-manager -std=c++20 \
// RUN: -O0 %s 2>&1 | FileCheck %s --check-prefixes=CHECK-ALL
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm-bc -o /dev/null \
-// RUN: -fexperimental-new-pass-manager -fdebug-pass-manager -fcoroutines-ts \
+// RUN: -fexperimental-new-pass-manager -fdebug-pass-manager -std=c++20 \
// RUN: -O1 %s 2>&1 | FileCheck %s --check-prefixes=CHECK-ALL,CHECK-OPT
//
// CHECK-ALL: Running pass:{{.*}}CoroEarlyPass
@@ -16,7 +16,6 @@
// CHECK-ALL: Running pass:{{.*}}CoroCleanupPass
namespace std {
-namespace experimental {
struct handle {};
@@ -39,7 +38,6 @@ template <typename T = void> struct coroutine_traits {
void unhandled_exception() {}
};
};
-} // namespace experimental
} // namespace std
void foo() { co_return; }
diff --git a/clang/test/CodeGenCoroutines/coro-params-exp-namespace.cpp b/clang/test/CodeGenCoroutines/coro-params-exp-namespace.cpp
new file mode 100644
index 000000000000..d8aa8472a83b
--- /dev/null
+++ b/clang/test/CodeGenCoroutines/coro-params-exp-namespace.cpp
@@ -0,0 +1,205 @@
+// Verifies that parameters are copied with move constructors
+// Verifies that parameter copies are destroyed
+// Vefifies that parameter copies are used in the body of the coroutine
+// Verifies that parameter copies are used to construct the promise type, if that type has a matching constructor
+// RUN: %clang_cc1 -std=c++1z -fcoroutines-ts -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s -disable-llvm-passes -fexceptions | FileCheck %s
+
+namespace std::experimental {
+template <typename... T> struct coroutine_traits;
+
+template <class Promise = void> struct coroutine_handle {
+ coroutine_handle() = default;
+ static coroutine_handle from_address(void *) noexcept;
+};
+template <> struct coroutine_handle<void> {
+ static coroutine_handle from_address(void *) noexcept;
+ coroutine_handle() = default;
+ template <class PromiseType>
+ coroutine_handle(coroutine_handle<PromiseType>) noexcept;
+};
+} // namespace std::experimental
+
+struct suspend_always {
+ bool await_ready() noexcept;
+ void await_suspend(std::experimental::coroutine_handle<>) noexcept;
+ void await_resume() noexcept;
+};
+
+template <typename... Args> struct std::experimental::coroutine_traits<void, Args...> {
+ struct promise_type {
+ void get_return_object() noexcept;
+ suspend_always initial_suspend() noexcept;
+ suspend_always final_suspend() noexcept;
+ void return_void() noexcept;
+ promise_type();
+ ~promise_type() noexcept;
+ void unhandled_exception() noexcept;
+ };
+};
+
+// TODO: Not supported yet
+struct CopyOnly {
+ int val;
+ CopyOnly(const CopyOnly &) noexcept;
+ CopyOnly(CopyOnly &&) = delete;
+ ~CopyOnly();
+};
+
+struct MoveOnly {
+ int val;
+ MoveOnly(const MoveOnly &) = delete;
+ MoveOnly(MoveOnly &&) noexcept;
+ ~MoveOnly();
+};
+
+struct MoveAndCopy {
+ int val;
+ MoveAndCopy(const MoveAndCopy &) noexcept;
+ MoveAndCopy(MoveAndCopy &&) noexcept;
+ ~MoveAndCopy();
+};
+
+void consume(int, int, int) noexcept;
+
+// TODO: Add support for CopyOnly params
+// CHECK: define{{.*}} void @_Z1fi8MoveOnly11MoveAndCopy(i32 %val, %struct.MoveOnly* %[[MoParam:.+]], %struct.MoveAndCopy* %[[McParam:.+]]) #0 personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*
+void f(int val, MoveOnly moParam, MoveAndCopy mcParam) {
+ // CHECK: %[[MoCopy:.+]] = alloca %struct.MoveOnly
+ // CHECK: %[[McCopy:.+]] = alloca %struct.MoveAndCopy
+ // CHECK: store i32 %val, i32* %[[ValAddr:.+]]
+
+ // CHECK: call i8* @llvm.coro.begin(
+ // CHECK: call void @_ZN8MoveOnlyC1EOS_(%struct.MoveOnly* {{[^,]*}} %[[MoCopy]], %struct.MoveOnly* nonnull align 4 dereferenceable(4) %[[MoParam]])
+ // CHECK-NEXT: bitcast %struct.MoveAndCopy* %[[McCopy]] to i8*
+ // CHECK-NEXT: call void @llvm.lifetime.start.p0i8(
+ // CHECK-NEXT: call void @_ZN11MoveAndCopyC1EOS_(%struct.MoveAndCopy* {{[^,]*}} %[[McCopy]], %struct.MoveAndCopy* nonnull align 4 dereferenceable(4) %[[McParam]]) #
+ // CHECK-NEXT: bitcast %"struct.std::experimental::coroutine_traits<void, int, MoveOnly, MoveAndCopy>::promise_type"* %__promise to i8*
+ // CHECK-NEXT: call void @llvm.lifetime.start.p0i8(
+ // CHECK-NEXT: invoke void @_ZNSt12experimental16coroutine_traitsIJvi8MoveOnly11MoveAndCopyEE12promise_typeC1Ev(
+
+ // CHECK: call void @_ZN14suspend_always12await_resumeEv(
+ // CHECK: %[[IntParam:.+]] = load i32, i32* %{{.*}}
+ // CHECK: %[[MoGep:.+]] = getelementptr inbounds %struct.MoveOnly, %struct.MoveOnly* %[[MoCopy]], i32 0, i32 0
+ // CHECK: %[[MoVal:.+]] = load i32, i32* %[[MoGep]]
+ // CHECK: %[[McGep:.+]] = getelementptr inbounds %struct.MoveAndCopy, %struct.MoveAndCopy* %[[McCopy]], i32 0, i32 0
+ // CHECK: %[[McVal:.+]] = load i32, i32* %[[McGep]]
+ // CHECK: call void @_Z7consumeiii(i32 %[[IntParam]], i32 %[[MoVal]], i32 %[[McVal]])
+
+ consume(val, moParam.val, mcParam.val);
+ co_return;
+
+ // Skip to final suspend:
+ // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJvi8MoveOnly11MoveAndCopyEE12promise_type13final_suspendEv(
+ // CHECK: call void @_ZN14suspend_always12await_resumeEv(
+
+ // Destroy promise, then parameter copies:
+ // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJvi8MoveOnly11MoveAndCopyEE12promise_typeD1Ev(%"struct.std::experimental::coroutine_traits<void, int, MoveOnly, MoveAndCopy>::promise_type"* {{[^,]*}} %__promise)
+ // CHECK-NEXT: bitcast %"struct.std::experimental::coroutine_traits<void, int, MoveOnly, MoveAndCopy>::promise_type"* %__promise to i8*
+ // CHECK-NEXT: call void @llvm.lifetime.end.p0i8(
+ // CHECK-NEXT: call void @_ZN11MoveAndCopyD1Ev(%struct.MoveAndCopy* {{[^,]*}} %[[McCopy]])
+ // CHECK-NEXT: bitcast %struct.MoveAndCopy* %[[McCopy]] to i8*
+ // CHECK-NEXT: call void @llvm.lifetime.end.p0i8(
+ // CHECK-NEXT: call void @_ZN8MoveOnlyD1Ev(%struct.MoveOnly* {{[^,]*}} %[[MoCopy]]
+ // CHECK-NEXT: bitcast %struct.MoveOnly* %[[MoCopy]] to i8*
+ // CHECK-NEXT: call void @llvm.lifetime.end.p0i8(
+ // CHECK-NEXT: bitcast i32* %{{.+}} to i8*
+ // CHECK-NEXT: call void @llvm.lifetime.end.p0i8(
+ // CHECK-NEXT: call i8* @llvm.coro.free(
+}
+
+// CHECK-LABEL: void @_Z16dependent_paramsI1A1BEvT_T0_S3_(%struct.A* %x, %struct.B* %0, %struct.B* %y)
+template <typename T, typename U>
+void dependent_params(T x, U, U y) {
+ // CHECK: %[[x_copy:.+]] = alloca %struct.A
+ // CHECK-NEXT: %[[unnamed_copy:.+]] = alloca %struct.B
+ // CHECK-NEXT: %[[y_copy:.+]] = alloca %struct.B
+
+ // CHECK: call i8* @llvm.coro.begin
+ // CHECK-NEXT: bitcast %struct.A* %[[x_copy]] to i8*
+ // CHECK-NEXT: call void @llvm.lifetime.start.p0i8(
+ // CHECK-NEXT: call void @_ZN1AC1EOS_(%struct.A* {{[^,]*}} %[[x_copy]], %struct.A* nonnull align 4 dereferenceable(512) %x)
+ // CHECK-NEXT: bitcast %struct.B* %[[unnamed_copy]] to i8*
+ // CHECK-NEXT: call void @llvm.lifetime.start.p0i8(
+ // CHECK-NEXT: call void @_ZN1BC1EOS_(%struct.B* {{[^,]*}} %[[unnamed_copy]], %struct.B* nonnull align 4 dereferenceable(512) %0)
+ // CHECK-NEXT: bitcast %struct.B* %[[y_copy]] to i8*
+ // CHECK-NEXT: call void @llvm.lifetime.start.p0i8(
+ // CHECK-NEXT: call void @_ZN1BC1EOS_(%struct.B* {{[^,]*}} %[[y_copy]], %struct.B* nonnull align 4 dereferenceable(512) %y)
+ // CHECK-NEXT: bitcast %"struct.std::experimental::coroutine_traits<void, A, B, B>::promise_type"* %__promise to i8*
+ // CHECK-NEXT: call void @llvm.lifetime.start.p0i8(
+ // CHECK-NEXT: invoke void @_ZNSt12experimental16coroutine_traitsIJv1A1BS2_EE12promise_typeC1Ev(
+
+ co_return;
+}
+
+struct A {
+ int WontFitIntoRegisterForSure[128];
+ A();
+ A(A &&)
+ noexcept;
+ ~A();
+};
+
+struct B {
+ int WontFitIntoRegisterForSure[128];
+ B();
+ B(B &&)
+ noexcept;
+ ~B();
+};
+
+void call_dependent_params() {
+ dependent_params(A{}, B{}, B{});
+}
+
+// Test that, when the promise type has a constructor whose signature matches
+// that of the coroutine function, that constructor is used. This is an
+// experimental feature that will be proposed for the Coroutines TS.
+
+struct promise_matching_constructor {};
+
+template <>
+struct std::experimental::coroutine_traits<void, promise_matching_constructor, int, float, double> {
+ struct promise_type {
+ promise_type(promise_matching_constructor, int, float, double) {}
+ promise_type() = delete;
+ void get_return_object() {}
+ suspend_always initial_suspend() { return {}; }
+ suspend_always final_suspend() noexcept { return {}; }
+ void return_void() {}
+ void unhandled_exception() {}
+ };
+};
+
+// CHECK-LABEL: void @_Z38coroutine_matching_promise_constructor28promise_matching_constructorifd(i32 %0, float %1, double %2)
+void coroutine_matching_promise_constructor(promise_matching_constructor, int, float, double) {
+ // CHECK: %[[INT:.+]] = load i32, i32* %5, align 4
+ // CHECK: %[[FLOAT:.+]] = load float, float* %6, align 4
+ // CHECK: %[[DOUBLE:.+]] = load double, double* %7, align 8
+ // CHECK: invoke void @_ZNSt12experimental16coroutine_traitsIJv28promise_matching_constructorifdEE12promise_typeC1ES1_ifd(%"struct.std::experimental::coroutine_traits<void, promise_matching_constructor, int, float, double>::promise_type"* {{[^,]*}} %__promise, i32 %[[INT]], float %[[FLOAT]], double %[[DOUBLE]])
+ co_return;
+}
+
+struct some_class;
+
+struct method {};
+
+template <typename... Args> struct std::experimental::coroutine_traits<method, Args...> {
+ struct promise_type {
+ promise_type(some_class &, float);
+ method get_return_object();
+ suspend_always initial_suspend();
+ suspend_always final_suspend() noexcept;
+ void return_void();
+ void unhandled_exception();
+ };
+};
+
+struct some_class {
+ method good_coroutine_calls_custom_constructor(float);
+};
+
+// CHECK-LABEL: define{{.*}} void @_ZN10some_class39good_coroutine_calls_custom_constructorEf(%struct.some_class*
+method some_class::good_coroutine_calls_custom_constructor(float) {
+ // CHECK: invoke void @_ZNSt12experimental16coroutine_traitsIJ6methodR10some_classfEE12promise_typeC1ES3_f(%"struct.std::experimental::coroutine_traits<method, some_class &, float>::promise_type"* {{[^,]*}} %__promise, %struct.some_class* nonnull align 1 dereferenceable(1) %{{.+}}, float
+ co_return;
+}
diff --git a/clang/test/CodeGenCoroutines/coro-params.cpp b/clang/test/CodeGenCoroutines/coro-params.cpp
index 28753d524df2..b5f23245bd58 100644
--- a/clang/test/CodeGenCoroutines/coro-params.cpp
+++ b/clang/test/CodeGenCoroutines/coro-params.cpp
@@ -2,9 +2,9 @@
// Verifies that parameter copies are destroyed
// Vefifies that parameter copies are used in the body of the coroutine
// Verifies that parameter copies are used to construct the promise type, if that type has a matching constructor
-// RUN: %clang_cc1 -std=c++1z -fcoroutines-ts -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s -disable-llvm-passes -fexceptions | FileCheck %s
+// RUN: %clang_cc1 -std=c++20 -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s -disable-llvm-passes -fexceptions | FileCheck %s
-namespace std::experimental {
+namespace std {
template <typename... T> struct coroutine_traits;
template <class Promise = void> struct coroutine_handle {
@@ -17,15 +17,15 @@ template <> struct coroutine_handle<void> {
template <class PromiseType>
coroutine_handle(coroutine_handle<PromiseType>) noexcept;
};
-}
+} // namespace std
struct suspend_always {
bool await_ready() noexcept;
- void await_suspend(std::experimental::coroutine_handle<>) noexcept;
+ void await_suspend(std::coroutine_handle<>) noexcept;
void await_resume() noexcept;
};
-template <typename... Args> struct std::experimental::coroutine_traits<void, Args...> {
+template <typename... Args> struct std::coroutine_traits<void, Args...> {
struct promise_type {
void get_return_object() noexcept;
suspend_always initial_suspend() noexcept;
@@ -73,9 +73,9 @@ void f(int val, MoveOnly moParam, MoveAndCopy mcParam) {
// CHECK-NEXT: bitcast %struct.MoveAndCopy* %[[McCopy]] to i8*
// CHECK-NEXT: call void @llvm.lifetime.start.p0i8(
// CHECK-NEXT: call void @_ZN11MoveAndCopyC1EOS_(%struct.MoveAndCopy* {{[^,]*}} %[[McCopy]], %struct.MoveAndCopy* nonnull align 4 dereferenceable(4) %[[McParam]]) #
- // CHECK-NEXT: bitcast %"struct.std::experimental::coroutine_traits<void, int, MoveOnly, MoveAndCopy>::promise_type"* %__promise to i8*
+ // CHECK-NEXT: bitcast %"struct.std::coroutine_traits<void, int, MoveOnly, MoveAndCopy>::promise_type"* %__promise to i8*
// CHECK-NEXT: call void @llvm.lifetime.start.p0i8(
- // CHECK-NEXT: invoke void @_ZNSt12experimental16coroutine_traitsIJvi8MoveOnly11MoveAndCopyEE12promise_typeC1Ev(
+ // CHECK-NEXT: invoke void @_ZNSt16coroutine_traitsIJvi8MoveOnly11MoveAndCopyEE12promise_typeC1Ev(
// CHECK: call void @_ZN14suspend_always12await_resumeEv(
// CHECK: %[[IntParam:.+]] = load i32, i32* %{{.*}}
@@ -89,12 +89,12 @@ void f(int val, MoveOnly moParam, MoveAndCopy mcParam) {
co_return;
// Skip to final suspend:
- // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJvi8MoveOnly11MoveAndCopyEE12promise_type13final_suspendEv(
+ // CHECK: call void @_ZNSt16coroutine_traitsIJvi8MoveOnly11MoveAndCopyEE12promise_type13final_suspendEv(
// CHECK: call void @_ZN14suspend_always12await_resumeEv(
// Destroy promise, then parameter copies:
- // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJvi8MoveOnly11MoveAndCopyEE12promise_typeD1Ev(%"struct.std::experimental::coroutine_traits<void, int, MoveOnly, MoveAndCopy>::promise_type"* {{[^,]*}} %__promise)
- // CHECK-NEXT: bitcast %"struct.std::experimental::coroutine_traits<void, int, MoveOnly, MoveAndCopy>::promise_type"* %__promise to i8*
+ // CHECK: call void @_ZNSt16coroutine_traitsIJvi8MoveOnly11MoveAndCopyEE12promise_typeD1Ev(%"struct.std::coroutine_traits<void, int, MoveOnly, MoveAndCopy>::promise_type"* {{[^,]*}} %__promise)
+ // CHECK-NEXT: bitcast %"struct.std::coroutine_traits<void, int, MoveOnly, MoveAndCopy>::promise_type"* %__promise to i8*
// CHECK-NEXT: call void @llvm.lifetime.end.p0i8(
// CHECK-NEXT: call void @_ZN11MoveAndCopyD1Ev(%struct.MoveAndCopy* {{[^,]*}} %[[McCopy]])
// CHECK-NEXT: bitcast %struct.MoveAndCopy* %[[McCopy]] to i8*
@@ -124,9 +124,9 @@ void dependent_params(T x, U, U y) {
// CHECK-NEXT: bitcast %struct.B* %[[y_copy]] to i8*
// CHECK-NEXT: call void @llvm.lifetime.start.p0i8(
// CHECK-NEXT: call void @_ZN1BC1EOS_(%struct.B* {{[^,]*}} %[[y_copy]], %struct.B* nonnull align 4 dereferenceable(512) %y)
- // CHECK-NEXT: bitcast %"struct.std::experimental::coroutine_traits<void, A, B, B>::promise_type"* %__promise to i8*
+ // CHECK-NEXT: bitcast %"struct.std::coroutine_traits<void, A, B, B>::promise_type"* %__promise to i8*
// CHECK-NEXT: call void @llvm.lifetime.start.p0i8(
- // CHECK-NEXT: invoke void @_ZNSt12experimental16coroutine_traitsIJv1A1BS2_EE12promise_typeC1Ev(
+ // CHECK-NEXT: invoke void @_ZNSt16coroutine_traitsIJv1A1BS1_EE12promise_typeC1Ev(
co_return;
}
@@ -155,8 +155,8 @@ void call_dependent_params() {
struct promise_matching_constructor {};
-template<>
-struct std::experimental::coroutine_traits<void, promise_matching_constructor, int, float, double> {
+template <>
+struct std::coroutine_traits<void, promise_matching_constructor, int, float, double> {
struct promise_type {
promise_type(promise_matching_constructor, int, float, double) {}
promise_type() = delete;
@@ -173,7 +173,7 @@ void coroutine_matching_promise_constructor(promise_matching_constructor, int, f
// CHECK: %[[INT:.+]] = load i32, i32* %5, align 4
// CHECK: %[[FLOAT:.+]] = load float, float* %6, align 4
// CHECK: %[[DOUBLE:.+]] = load double, double* %7, align 8
- // CHECK: invoke void @_ZNSt12experimental16coroutine_traitsIJv28promise_matching_constructorifdEE12promise_typeC1ES1_ifd(%"struct.std::experimental::coroutine_traits<void, promise_matching_constructor, int, float, double>::promise_type"* {{[^,]*}} %__promise, i32 %[[INT]], float %[[FLOAT]], double %[[DOUBLE]])
+ // CHECK: invoke void @_ZNSt16coroutine_traitsIJv28promise_matching_constructorifdEE12promise_typeC1ES0_ifd(%"struct.std::coroutine_traits<void, promise_matching_constructor, int, float, double>::promise_type"* {{[^,]*}} %__promise, i32 %[[INT]], float %[[FLOAT]], double %[[DOUBLE]])
co_return;
}
@@ -181,7 +181,7 @@ struct some_class;
struct method {};
-template <typename... Args> struct std::experimental::coroutine_traits<method, Args...> {
+template <typename... Args> struct std::coroutine_traits<method, Args...> {
struct promise_type {
promise_type(some_class&, float);
method get_return_object();
@@ -198,6 +198,6 @@ struct some_class {
// CHECK-LABEL: define{{.*}} void @_ZN10some_class39good_coroutine_calls_custom_constructorEf(%struct.some_class*
method some_class::good_coroutine_calls_custom_constructor(float) {
- // CHECK: invoke void @_ZNSt12experimental16coroutine_traitsIJ6methodR10some_classfEE12promise_typeC1ES3_f(%"struct.std::experimental::coroutine_traits<method, some_class &, float>::promise_type"* {{[^,]*}} %__promise, %struct.some_class* nonnull align 1 dereferenceable(1) %{{.+}}, float
+ // CHECK: invoke void @_ZNSt16coroutine_traitsIJ6methodR10some_classfEE12promise_typeC1ES2_f(%"struct.std::coroutine_traits<method, some_class &, float>::promise_type"* {{[^,]*}} %__promise, %struct.some_class* nonnull align 1 dereferenceable(1) %{{.+}}, float
co_return;
}
diff --git a/clang/test/CodeGenCoroutines/coro-promise-dtor-exp-namespace.cpp b/clang/test/CodeGenCoroutines/coro-promise-dtor-exp-namespace.cpp
new file mode 100644
index 000000000000..72831a77d3e0
--- /dev/null
+++ b/clang/test/CodeGenCoroutines/coro-promise-dtor-exp-namespace.cpp
@@ -0,0 +1,49 @@
+// RUN: %clang_cc1 -std=c++14 -fcoroutines-ts -triple=x86_64-pc-windows-msvc18.0.0 -emit-llvm -o - %s -fexceptions -fcxx-exceptions -disable-llvm-passes | FileCheck %s
+// -triple=x86_64-unknown-linux-gnu
+
+#include "Inputs/coroutine-exp-namespace.h"
+
+namespace coro = std::experimental::coroutines_v1;
+
+struct coro_t {
+ void *p;
+ ~coro_t();
+ struct promise_type {
+ coro_t get_return_object();
+ coro::suspend_never initial_suspend();
+ coro::suspend_never final_suspend() noexcept;
+ void return_void();
+ promise_type();
+ ~promise_type();
+ void unhandled_exception();
+ };
+};
+
+struct Cleanup {
+ ~Cleanup();
+};
+void may_throw();
+
+coro_t f() {
+ Cleanup cleanup;
+ may_throw();
+ co_return;
+}
+
+// CHECK-LABEL: define dso_local void @"?f@@YA?AUcoro_t@@XZ"(
+// CHECK: %gro.active = alloca i1
+// CHECK: store i1 false, i1* %gro.active
+
+// CHECK: invoke %"struct.coro_t::promise_type"* @"??0promise_type at coro_t@@QEAA at XZ"(
+// CHECK: invoke void @"?get_return_object at promise_type@coro_t@@QEAA?AU2 at XZ"(
+// CHECK: store i1 true, i1* %gro.active
+
+// CHECK: %[[IS_ACTIVE:.+]] = load i1, i1* %gro.active
+// CHECK: br i1 %[[IS_ACTIVE]], label %[[CLEANUP1:.+]], label
+
+// CHECK: [[CLEANUP1]]:
+// CHECK: %[[NRVO:.+]] = load i1, i1* %nrvo
+// CHECK: br i1 %[[NRVO]], label %{{.+}}, label %[[DTOR:.+]]
+
+// CHECK: [[DTOR]]:
+// CHECK: call void @"??1coro_t@@QEAA at XZ"(
diff --git a/clang/test/CodeGenCoroutines/coro-promise-dtor.cpp b/clang/test/CodeGenCoroutines/coro-promise-dtor.cpp
index 4fb0f0fef794..41ffd1a47097 100644
--- a/clang/test/CodeGenCoroutines/coro-promise-dtor.cpp
+++ b/clang/test/CodeGenCoroutines/coro-promise-dtor.cpp
@@ -1,17 +1,15 @@
-// RUN: %clang_cc1 -std=c++14 -fcoroutines-ts -triple=x86_64-pc-windows-msvc18.0.0 -emit-llvm -o - %s -fexceptions -fcxx-exceptions -disable-llvm-passes | FileCheck %s
+// RUN: %clang_cc1 -std=c++20 -triple=x86_64-pc-windows-msvc18.0.0 -emit-llvm -o - %s -fexceptions -fcxx-exceptions -disable-llvm-passes | FileCheck %s
// -triple=x86_64-unknown-linux-gnu
#include "Inputs/coroutine.h"
-namespace coro = std::experimental::coroutines_v1;
-
struct coro_t {
void* p;
~coro_t();
struct promise_type {
coro_t get_return_object();
- coro::suspend_never initial_suspend();
- coro::suspend_never final_suspend() noexcept;
+ std::suspend_never initial_suspend();
+ std::suspend_never final_suspend() noexcept;
void return_void();
promise_type();
~promise_type();
diff --git a/clang/test/CodeGenCoroutines/coro-ret-void-exp-namespace.cpp b/clang/test/CodeGenCoroutines/coro-ret-void-exp-namespace.cpp
new file mode 100644
index 000000000000..7438e7f7019e
--- /dev/null
+++ b/clang/test/CodeGenCoroutines/coro-ret-void-exp-namespace.cpp
@@ -0,0 +1,53 @@
+// RUN: %clang_cc1 -std=c++14 -fcoroutines-ts -triple=x86_64-unknown-linux-gnu -emit-llvm %s -o - -disable-llvm-passes | FileCheck %s
+
+#include "Inputs/coroutine-exp-namespace.h"
+
+namespace coro = std::experimental::coroutines_v1;
+
+struct coro1 {
+ struct promise_type {
+ coro1 get_return_object();
+ coro::suspend_never initial_suspend();
+ coro::suspend_never final_suspend() noexcept;
+ void return_void();
+ };
+};
+
+coro1 f() {
+ co_await coro::suspend_never{};
+}
+
+// CHECK-LABEL: define{{.*}} void @_Z1fv(
+// CHECK: call void @_ZNSt12experimental13coroutines_v113suspend_never12await_resumeEv(%"struct.std::experimental::coroutines_v1::suspend_never"*
+// CHECK: call void @_ZN5coro112promise_type11return_voidEv(%"struct.coro1::promise_type"* {{[^,]*}} %__promise)
+
+struct A {
+ A();
+ ~A();
+};
+
+coro1 f2() {
+ co_return(void) A{};
+}
+
+// CHECK-LABEL: define{{.*}} void @_Z2f2v(
+// CHECK: call void @_ZN1AC1Ev(%struct.A* {{[^,]*}} %[[AVar:.*]])
+// CHECK-NEXT: call void @_ZN1AD1Ev(%struct.A* {{[^,]*}} %[[AVar]])
+// CHECK-NEXT: call void @_ZN5coro112promise_type11return_voidEv(%"struct.coro1::promise_type"*
+
+struct coro2 {
+ struct promise_type {
+ coro2 get_return_object();
+ coro::suspend_never initial_suspend();
+ coro::suspend_never final_suspend() noexcept;
+ void return_value(int);
+ };
+};
+
+coro2 g() {
+ co_return 42;
+}
+
+// CHECK-LABEL: define{{.*}} void @_Z1gv(
+// CHECK: call void @_ZNSt12experimental13coroutines_v113suspend_never12await_resumeEv(%"struct.std::experimental::coroutines_v1::suspend_never"*
+// CHECK: call void @_ZN5coro212promise_type12return_valueEi(%"struct.coro2::promise_type"* {{[^,]*}} %__promise, i32 42)
diff --git a/clang/test/CodeGenCoroutines/coro-ret-void.cpp b/clang/test/CodeGenCoroutines/coro-ret-void.cpp
index 1ef2950dd020..9501532f5d08 100644
--- a/clang/test/CodeGenCoroutines/coro-ret-void.cpp
+++ b/clang/test/CodeGenCoroutines/coro-ret-void.cpp
@@ -1,24 +1,22 @@
-// RUN: %clang_cc1 -std=c++14 -fcoroutines-ts -triple=x86_64-unknown-linux-gnu -emit-llvm %s -o - -disable-llvm-passes | FileCheck %s
+// RUN: %clang_cc1 -std=c++20 -triple=x86_64-unknown-linux-gnu -emit-llvm %s -o - -disable-llvm-passes | FileCheck %s
#include "Inputs/coroutine.h"
-namespace coro = std::experimental::coroutines_v1;
-
struct coro1 {
struct promise_type {
coro1 get_return_object();
- coro::suspend_never initial_suspend();
- coro::suspend_never final_suspend() noexcept;
+ std::suspend_never initial_suspend();
+ std::suspend_never final_suspend() noexcept;
void return_void();
};
};
coro1 f() {
- co_await coro::suspend_never{};
+ co_await std::suspend_never{};
}
// CHECK-LABEL: define{{.*}} void @_Z1fv(
-// CHECK: call void @_ZNSt12experimental13coroutines_v113suspend_never12await_resumeEv(%"struct.std::experimental::coroutines_v1::suspend_never"*
+// CHECK: call void @_ZNSt13suspend_never12await_resumeEv(%"struct.std::suspend_never"*
// CHECK: call void @_ZN5coro112promise_type11return_voidEv(%"struct.coro1::promise_type"* {{[^,]*}} %__promise)
struct A {
@@ -38,8 +36,8 @@ coro1 f2() {
struct coro2 {
struct promise_type {
coro2 get_return_object();
- coro::suspend_never initial_suspend();
- coro::suspend_never final_suspend() noexcept;
+ std::suspend_never initial_suspend();
+ std::suspend_never final_suspend() noexcept;
void return_value(int);
};
};
@@ -49,5 +47,5 @@ coro2 g() {
}
// CHECK-LABEL: define{{.*}} void @_Z1gv(
-// CHECK: call void @_ZNSt12experimental13coroutines_v113suspend_never12await_resumeEv(%"struct.std::experimental::coroutines_v1::suspend_never"*
+// CHECK: call void @_ZNSt13suspend_never12await_resumeEv(%"struct.std::suspend_never"*
// CHECK: call void @_ZN5coro212promise_type12return_valueEi(%"struct.coro2::promise_type"* {{[^,]*}} %__promise, i32 42)
diff --git a/clang/test/CodeGenCoroutines/coro-return-exp-namespace.cpp b/clang/test/CodeGenCoroutines/coro-return-exp-namespace.cpp
new file mode 100644
index 000000000000..5a5b56f327d1
--- /dev/null
+++ b/clang/test/CodeGenCoroutines/coro-return-exp-namespace.cpp
@@ -0,0 +1,59 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fcoroutines-ts -std=c++1z -emit-llvm %s -o - -disable-llvm-passes | FileCheck %s
+
+namespace std::experimental {
+template <typename... T> struct coroutine_traits;
+
+template <class Promise = void> struct coroutine_handle {
+ coroutine_handle() = default;
+ static coroutine_handle from_address(void *) noexcept { return {}; }
+};
+template <> struct coroutine_handle<void> {
+ static coroutine_handle from_address(void *) { return {}; }
+ coroutine_handle() = default;
+ template <class PromiseType>
+ coroutine_handle(coroutine_handle<PromiseType>) noexcept {}
+};
+} // namespace std::experimental
+
+struct suspend_always {
+ bool await_ready() noexcept;
+ void await_suspend(std::experimental::coroutine_handle<>) noexcept;
+ void await_resume() noexcept;
+};
+
+template <> struct std::experimental::coroutine_traits<void> {
+ struct promise_type {
+ void get_return_object();
+ suspend_always initial_suspend();
+ suspend_always final_suspend() noexcept;
+ void return_void();
+ };
+};
+
+// CHECK-LABEL: f0(
+extern "C" void f0() {
+ // CHECK: %__promise = alloca %"struct.std::experimental::coroutine_traits<void>::promise_type"
+ // CHECK: %call = call noalias nonnull i8* @_Znwm(
+ // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJvEE12promise_type11return_voidEv(%"struct.std::experimental::coroutine_traits<void>::promise_type"* {{[^,]*}} %__promise)
+ // CHECK: call void @_ZdlPv
+ co_return;
+}
+
+template <>
+struct std::experimental::coroutine_traits<int> {
+ struct promise_type {
+ int get_return_object();
+ suspend_always initial_suspend();
+ suspend_always final_suspend() noexcept;
+ void return_value(int);
+ };
+};
+
+// CHECK-LABEL: f1(
+extern "C" int f1() {
+ // CHECK: %__promise = alloca %"struct.std::experimental::coroutine_traits<int>::promise_type"
+ // CHECK: %call = call noalias nonnull i8* @_Znwm(
+ // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJiEE12promise_type12return_valueEi(%"struct.std::experimental::coroutine_traits<int>::promise_type"* {{[^,]*}} %__promise, i32 42)
+ // CHECK: call void @_ZdlPv
+ co_return 42;
+}
diff --git a/clang/test/CodeGenCoroutines/coro-return-voidtype-initlist-exp-namespace.cpp b/clang/test/CodeGenCoroutines/coro-return-voidtype-initlist-exp-namespace.cpp
new file mode 100644
index 000000000000..4057358ef7ab
--- /dev/null
+++ b/clang/test/CodeGenCoroutines/coro-return-voidtype-initlist-exp-namespace.cpp
@@ -0,0 +1,81 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fcoroutines-ts -std=c++1z -emit-llvm %s -o - -disable-llvm-passes | FileCheck %s
+
+namespace std {
+template <typename a>
+struct b { b(int, a); };
+template <typename, typename = int>
+struct c {};
+namespace experimental {
+template <typename d>
+struct coroutine_traits : d {};
+template <typename = void>
+struct coroutine_handle;
+template <>
+struct coroutine_handle<> {};
+template <typename>
+struct coroutine_handle : coroutine_handle<> {
+ static coroutine_handle from_address(void *) noexcept;
+};
+struct e {
+ int await_ready();
+ void await_suspend(coroutine_handle<>);
+ void await_resume();
+};
+} // namespace experimental
+} // namespace std
+template <typename ag>
+auto ah(ag) { return ag().ah(0); }
+template <typename>
+struct f;
+struct g {
+ struct h {
+ int await_ready() noexcept;
+ template <typename al>
+ void await_suspend(std::experimental::coroutine_handle<al>) noexcept;
+ void await_resume() noexcept;
+ };
+ std::experimental::e initial_suspend();
+ h final_suspend() noexcept;
+ template <typename ag>
+ auto await_transform(ag) { return ah(ag()); }
+};
+struct j : g {
+ f<std::b<std::c<int, int>>> get_return_object();
+ void return_value(std::b<std::c<int, int>>);
+ void unhandled_exception();
+};
+struct k {
+ k(std::experimental::coroutine_handle<>);
+ int await_ready();
+};
+template <typename am>
+struct f {
+ using promise_type = j;
+ std::experimental::coroutine_handle<> ar;
+ struct l : k {
+ using at = k;
+ l(std::experimental::coroutine_handle<> m) : at(m) {}
+ void await_suspend(std::experimental::coroutine_handle<>);
+ };
+ struct n : l {
+ n(std::experimental::coroutine_handle<> m) : l(m) {}
+ am await_resume();
+ };
+ auto ah(int) { return n(ar); }
+};
+template <typename am, typename av, typename aw>
+auto ax(std::c<am, av>, aw) -> f<std::c<int, aw>>;
+template <typename>
+struct J { static f<std::b<std::c<int, int>>> bo(); };
+// CHECK-LABEL: _ZN1JIiE2boEv(
+template <typename bc>
+f<std::b<std::c<int, int>>> J<bc>::bo() {
+ std::c<int> bu;
+ int bw(0);
+ // CHECK: void @_ZN1j12return_valueESt1bISt1cIiiEE(%struct.j* {{[^,]*}} %__promise)
+ co_return {0, co_await ax(bu, bw)};
+}
+void bh() {
+ auto cn = [] { J<int>::bo; };
+ cn();
+}
diff --git a/clang/test/CodeGenCoroutines/coro-return-voidtype-initlist.cpp b/clang/test/CodeGenCoroutines/coro-return-voidtype-initlist.cpp
index 5f638644291e..4058afa93cdf 100644
--- a/clang/test/CodeGenCoroutines/coro-return-voidtype-initlist.cpp
+++ b/clang/test/CodeGenCoroutines/coro-return-voidtype-initlist.cpp
@@ -1,11 +1,10 @@
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fcoroutines-ts -std=c++1z -emit-llvm %s -o - -disable-llvm-passes | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -emit-llvm %s -o - -disable-llvm-passes | FileCheck %s
namespace std {
template <typename a>
struct b { b(int, a); };
template <typename, typename = int>
struct c {};
-namespace experimental {
template <typename d>
struct coroutine_traits : d {};
template <typename = void>
@@ -21,7 +20,6 @@ struct e {
void await_suspend(coroutine_handle<>);
void await_resume();
};
-} // namespace experimental
} // namespace std
template <typename ag>
auto ah(ag) { return ag().ah(0); }
@@ -31,10 +29,10 @@ struct g {
struct h {
int await_ready() noexcept;
template <typename al>
- void await_suspend(std::experimental::coroutine_handle<al>) noexcept;
+ void await_suspend(std::coroutine_handle<al>) noexcept;
void await_resume() noexcept;
};
- std::experimental::e initial_suspend();
+ std::e initial_suspend();
h final_suspend() noexcept;
template <typename ag>
auto await_transform(ag) { return ah(ag()); }
@@ -45,20 +43,20 @@ struct j : g {
void unhandled_exception();
};
struct k {
- k(std::experimental::coroutine_handle<>);
+ k(std::coroutine_handle<>);
int await_ready();
};
template <typename am>
struct f {
using promise_type = j;
- std::experimental::coroutine_handle<> ar;
+ std::coroutine_handle<> ar;
struct l : k {
using at = k;
- l(std::experimental::coroutine_handle<> m) : at(m) {}
- void await_suspend(std::experimental::coroutine_handle<>);
+ l(std::coroutine_handle<> m) : at(m) {}
+ void await_suspend(std::coroutine_handle<>);
};
struct n : l {
- n(std::experimental::coroutine_handle<> m) : l(m) {}
+ n(std::coroutine_handle<> m) : l(m) {}
am await_resume();
};
auto ah(int) { return n(ar); }
diff --git a/clang/test/CodeGenCoroutines/coro-return.cpp b/clang/test/CodeGenCoroutines/coro-return.cpp
index d35954ed7a86..0ce426ad59cf 100644
--- a/clang/test/CodeGenCoroutines/coro-return.cpp
+++ b/clang/test/CodeGenCoroutines/coro-return.cpp
@@ -1,6 +1,6 @@
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fcoroutines-ts -std=c++1z -emit-llvm %s -o - -disable-llvm-passes | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -emit-llvm %s -o - -disable-llvm-passes | FileCheck %s
-namespace std::experimental {
+namespace std {
template <typename... T> struct coroutine_traits;
template <class Promise = void> struct coroutine_handle {
@@ -13,15 +13,15 @@ template <> struct coroutine_handle<void> {
template <class PromiseType>
coroutine_handle(coroutine_handle<PromiseType>) noexcept {}
};
-}
+} // namespace std
struct suspend_always {
bool await_ready() noexcept;
- void await_suspend(std::experimental::coroutine_handle<>) noexcept;
+ void await_suspend(std::coroutine_handle<>) noexcept;
void await_resume() noexcept;
};
-template <> struct std::experimental::coroutine_traits<void> {
+template <> struct std::coroutine_traits<void> {
struct promise_type {
void get_return_object();
suspend_always initial_suspend();
@@ -32,15 +32,15 @@ template <> struct std::experimental::coroutine_traits<void> {
// CHECK-LABEL: f0(
extern "C" void f0() {
- // CHECK: %__promise = alloca %"struct.std::experimental::coroutine_traits<void>::promise_type"
+ // CHECK: %__promise = alloca %"struct.std::coroutine_traits<void>::promise_type"
// CHECK: %call = call noalias nonnull i8* @_Znwm(
- // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJvEE12promise_type11return_voidEv(%"struct.std::experimental::coroutine_traits<void>::promise_type"* {{[^,]*}} %__promise)
+ // CHECK: call void @_ZNSt16coroutine_traitsIJvEE12promise_type11return_voidEv(%"struct.std::coroutine_traits<void>::promise_type"* {{[^,]*}} %__promise)
// CHECK: call void @_ZdlPv
co_return;
}
-template<>
-struct std::experimental::coroutine_traits<int> {
+template <>
+struct std::coroutine_traits<int> {
struct promise_type {
int get_return_object();
suspend_always initial_suspend();
@@ -51,9 +51,9 @@ struct std::experimental::coroutine_traits<int> {
// CHECK-LABEL: f1(
extern "C" int f1() {
- // CHECK: %__promise = alloca %"struct.std::experimental::coroutine_traits<int>::promise_type"
+ // CHECK: %__promise = alloca %"struct.std::coroutine_traits<int>::promise_type"
// CHECK: %call = call noalias nonnull i8* @_Znwm(
- // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJiEE12promise_type12return_valueEi(%"struct.std::experimental::coroutine_traits<int>::promise_type"* {{[^,]*}} %__promise, i32 42)
+ // CHECK: call void @_ZNSt16coroutine_traitsIJiEE12promise_type12return_valueEi(%"struct.std::coroutine_traits<int>::promise_type"* {{[^,]*}} %__promise, i32 42)
// CHECK: call void @_ZdlPv
co_return 42;
}
diff --git a/clang/test/CodeGenCoroutines/coro-symmetric-transfer-01-exp-namespace.cpp b/clang/test/CodeGenCoroutines/coro-symmetric-transfer-01-exp-namespace.cpp
new file mode 100644
index 000000000000..1b6f8ad2a161
--- /dev/null
+++ b/clang/test/CodeGenCoroutines/coro-symmetric-transfer-01-exp-namespace.cpp
@@ -0,0 +1,63 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fcoroutines-ts -std=c++14 -O0 -emit-llvm %s -o - -disable-llvm-passes | FileCheck %s
+// RUN: %clang -fcoroutines-ts -std=c++14 -O0 -emit-llvm -c %s -o %t -Xclang -disable-llvm-passes && %clang -c %t
+
+#include "Inputs/coroutine-exp-namespace.h"
+
+namespace coro = std::experimental::coroutines_v1;
+
+struct detached_task {
+ struct promise_type {
+ detached_task get_return_object() noexcept {
+ return detached_task{coro::coroutine_handle<promise_type>::from_promise(*this)};
+ }
+
+ void return_void() noexcept {}
+
+ struct final_awaiter {
+ bool await_ready() noexcept { return false; }
+ coro::coroutine_handle<> await_suspend(coro::coroutine_handle<promise_type> h) noexcept {
+ h.destroy();
+ return {};
+ }
+ void await_resume() noexcept {}
+ };
+
+ void unhandled_exception() noexcept {}
+
+ final_awaiter final_suspend() noexcept { return {}; }
+
+ coro::suspend_always initial_suspend() noexcept { return {}; }
+ };
+
+ ~detached_task() {
+ if (coro_) {
+ coro_.destroy();
+ coro_ = {};
+ }
+ }
+
+ void start() && {
+ auto tmp = coro_;
+ coro_ = {};
+ tmp.resume();
+ }
+
+ coro::coroutine_handle<promise_type> coro_;
+};
+
+detached_task foo() {
+ co_return;
+}
+
+// check that the lifetime of the coroutine handle used to obtain the address is contained within single basic block, and hence does not live across suspension points.
+// CHECK-LABEL: final.suspend:
+// CHECK: %{{.+}} = call token @llvm.coro.save(i8* null)
+// CHECK: %[[HDL_CAST1:.+]] = bitcast %"struct.std::experimental::coroutines_v1::coroutine_handle.0"* %[[HDL:.+]] to i8*
+// CHECK: call void @llvm.lifetime.start.p0i8(i64 8, i8* %[[HDL_CAST1]])
+// CHECK: %[[CALL:.+]] = call i8* @_ZN13detached_task12promise_type13final_awaiter13await_suspendENSt12experimental13coroutines_v116coroutine_handleIS0_EE(
+// CHECK: %[[HDL_CAST2:.+]] = getelementptr inbounds %"struct.std::experimental::coroutines_v1::coroutine_handle.0", %"struct.std::experimental::coroutines_v1::coroutine_handle.0"* %[[HDL]], i32 0, i32 0
+// CHECK: store i8* %[[CALL]], i8** %[[HDL_CAST2]], align 8
+// CHECK: %[[HDL_TRANSFER:.+]] = call i8* @_ZNKSt12experimental13coroutines_v116coroutine_handleIvE7addressEv(%"struct.std::experimental::coroutines_v1::coroutine_handle.0"* nonnull align 8 dereferenceable(8) %[[HDL]])
+// CHECK: %[[HDL_CAST3:.+]] = bitcast %"struct.std::experimental::coroutines_v1::coroutine_handle.0"* %[[HDL]] to i8*
+// CHECK: call void @llvm.lifetime.end.p0i8(i64 8, i8* %[[HDL_CAST3]])
+// CHECK: call void @llvm.coro.resume(i8* %[[HDL_TRANSFER]])
diff --git a/clang/test/CodeGenCoroutines/coro-symmetric-transfer-01.cpp b/clang/test/CodeGenCoroutines/coro-symmetric-transfer-01.cpp
index e9f09c8da038..b5a1bb05343b 100644
--- a/clang/test/CodeGenCoroutines/coro-symmetric-transfer-01.cpp
+++ b/clang/test/CodeGenCoroutines/coro-symmetric-transfer-01.cpp
@@ -1,21 +1,19 @@
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fcoroutines-ts -std=c++14 -O0 -emit-llvm %s -o - -disable-llvm-passes | FileCheck %s
-// RUN: %clang -fcoroutines-ts -std=c++14 -O0 -emit-llvm -c %s -o %t -Xclang -disable-llvm-passes && %clang -c %t
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -O0 -emit-llvm %s -o - -disable-llvm-passes | FileCheck %s
+// RUN: %clang -std=c++20 -O0 -emit-llvm -c %s -o %t -Xclang -disable-llvm-passes && %clang -c %t
#include "Inputs/coroutine.h"
-namespace coro = std::experimental::coroutines_v1;
-
struct detached_task {
struct promise_type {
detached_task get_return_object() noexcept {
- return detached_task{coro::coroutine_handle<promise_type>::from_promise(*this)};
+ return detached_task{std::coroutine_handle<promise_type>::from_promise(*this)};
}
void return_void() noexcept {}
struct final_awaiter {
bool await_ready() noexcept { return false; }
- coro::coroutine_handle<> await_suspend(coro::coroutine_handle<promise_type> h) noexcept {
+ std::coroutine_handle<> await_suspend(std::coroutine_handle<promise_type> h) noexcept {
h.destroy();
return {};
}
@@ -26,7 +24,7 @@ struct detached_task {
final_awaiter final_suspend() noexcept { return {}; }
- coro::suspend_always initial_suspend() noexcept { return {}; }
+ std::suspend_always initial_suspend() noexcept { return {}; }
};
~detached_task() {
@@ -42,7 +40,7 @@ struct detached_task {
tmp.resume();
}
- coro::coroutine_handle<promise_type> coro_;
+ std::coroutine_handle<promise_type> coro_;
};
detached_task foo() {
@@ -52,12 +50,12 @@ detached_task foo() {
// check that the lifetime of the coroutine handle used to obtain the address is contained within single basic block, and hence does not live across suspension points.
// CHECK-LABEL: final.suspend:
// CHECK: %{{.+}} = call token @llvm.coro.save(i8* null)
-// CHECK: %[[HDL_CAST1:.+]] = bitcast %"struct.std::experimental::coroutines_v1::coroutine_handle.0"* %[[HDL:.+]] to i8*
+// CHECK: %[[HDL_CAST1:.+]] = bitcast %"struct.std::coroutine_handle.0"* %[[HDL:.+]] to i8*
// CHECK: call void @llvm.lifetime.start.p0i8(i64 8, i8* %[[HDL_CAST1]])
-// CHECK: %[[CALL:.+]] = call i8* @_ZN13detached_task12promise_type13final_awaiter13await_suspendENSt12experimental13coroutines_v116coroutine_handleIS0_EE(
-// CHECK: %[[HDL_CAST2:.+]] = getelementptr inbounds %"struct.std::experimental::coroutines_v1::coroutine_handle.0", %"struct.std::experimental::coroutines_v1::coroutine_handle.0"* %[[HDL]], i32 0, i32 0
+// CHECK: %[[CALL:.+]] = call i8* @_ZN13detached_task12promise_type13final_awaiter13await_suspendESt16coroutine_handleIS0_E(
+// CHECK: %[[HDL_CAST2:.+]] = getelementptr inbounds %"struct.std::coroutine_handle.0", %"struct.std::coroutine_handle.0"* %[[HDL]], i32 0, i32 0
// CHECK: store i8* %[[CALL]], i8** %[[HDL_CAST2]], align 8
-// CHECK: %[[HDL_TRANSFER:.+]] = call i8* @_ZNKSt12experimental13coroutines_v116coroutine_handleIvE7addressEv(%"struct.std::experimental::coroutines_v1::coroutine_handle.0"* nonnull align 8 dereferenceable(8) %[[HDL]])
-// CHECK: %[[HDL_CAST3:.+]] = bitcast %"struct.std::experimental::coroutines_v1::coroutine_handle.0"* %[[HDL]] to i8*
+// CHECK: %[[HDL_TRANSFER:.+]] = call i8* @_ZNKSt16coroutine_handleIvE7addressEv(%"struct.std::coroutine_handle.0"* {{.*}}%[[HDL]])
+// CHECK: %[[HDL_CAST3:.+]] = bitcast %"struct.std::coroutine_handle.0"* %[[HDL]] to i8*
// CHECK: call void @llvm.lifetime.end.p0i8(i64 8, i8* %[[HDL_CAST3]])
// CHECK: call void @llvm.coro.resume(i8* %[[HDL_TRANSFER]])
diff --git a/clang/test/CodeGenCoroutines/coro-symmetric-transfer-02-exp-namespace.cpp b/clang/test/CodeGenCoroutines/coro-symmetric-transfer-02-exp-namespace.cpp
new file mode 100644
index 000000000000..c3e745d659d4
--- /dev/null
+++ b/clang/test/CodeGenCoroutines/coro-symmetric-transfer-02-exp-namespace.cpp
@@ -0,0 +1,126 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fcoroutines-ts -std=c++14 -O1 -emit-llvm %s -o - -disable-llvm-passes | FileCheck %s
+
+#include "Inputs/coroutine-exp-namespace.h"
+
+namespace coro = std::experimental::coroutines_v1;
+
+struct Task {
+ struct promise_type {
+ Task get_return_object() noexcept {
+ return Task{coro::coroutine_handle<promise_type>::from_promise(*this)};
+ }
+
+ void return_void() noexcept {}
+
+ struct final_awaiter {
+ bool await_ready() noexcept { return false; }
+ coro::coroutine_handle<> await_suspend(coro::coroutine_handle<promise_type> h) noexcept {
+ h.destroy();
+ return {};
+ }
+ void await_resume() noexcept {}
+ };
+
+ void unhandled_exception() noexcept {}
+
+ final_awaiter final_suspend() noexcept { return {}; }
+
+ coro::suspend_always initial_suspend() noexcept { return {}; }
+
+ template <typename Awaitable>
+ auto await_transform(Awaitable &&awaitable) {
+ return awaitable.co_viaIfAsync();
+ }
+ };
+
+ using handle_t = coro::coroutine_handle<promise_type>;
+
+ class Awaiter {
+ public:
+ explicit Awaiter(handle_t coro) noexcept;
+ Awaiter(Awaiter &&other) noexcept;
+ Awaiter(const Awaiter &) = delete;
+ ~Awaiter();
+
+ bool await_ready() noexcept { return false; }
+ handle_t await_suspend(coro::coroutine_handle<> continuation) noexcept;
+ void await_resume();
+
+ private:
+ handle_t coro_;
+ };
+
+ Task(handle_t coro) noexcept : coro_(coro) {}
+
+ handle_t coro_;
+
+ Task(const Task &t) = delete;
+ Task(Task &&t) noexcept;
+ ~Task();
+ Task &operator=(Task t) noexcept;
+
+ Awaiter co_viaIfAsync();
+};
+
+static Task foo() {
+ co_return;
+}
+
+Task bar() {
+ auto mode = 2;
+ switch (mode) {
+ case 1:
+ co_await foo();
+ break;
+ case 2:
+ co_await foo();
+ break;
+ default:
+ break;
+ }
+}
+
+// CHECK-LABEL: define{{.*}} void @_Z3barv
+// CHECK: %[[MODE:.+]] = load i32, i32* %mode
+// CHECK-NEXT: switch i32 %[[MODE]], label %{{.+}} [
+// CHECK-NEXT: i32 1, label %[[CASE1:.+]]
+// CHECK-NEXT: i32 2, label %[[CASE2:.+]]
+// CHECK-NEXT: ]
+
+// CHECK: [[CASE1]]:
+// CHECK: br i1 %{{.+}}, label %[[CASE1_AWAIT_READY:.+]], label %[[CASE1_AWAIT_SUSPEND:.+]]
+// CHECK: [[CASE1_AWAIT_SUSPEND]]:
+// CHECK-NEXT: %{{.+}} = call token @llvm.coro.save(i8* null)
+// CHECK-NEXT: %[[HANDLE11:.+]] = bitcast %"struct.std::experimental::coroutines_v1::coroutine_handle"* %[[TMP1:.+]] to i8*
+// CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 8, i8* %[[HANDLE11]])
+
+// CHECK: %[[HANDLE12:.+]] = bitcast %"struct.std::experimental::coroutines_v1::coroutine_handle"* %[[TMP1]] to i8*
+// CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 8, i8* %[[HANDLE12]])
+// CHECK-NEXT: call void @llvm.coro.resume
+// CHECK-NEXT: %{{.+}} = call i8 @llvm.coro.suspend
+// CHECK-NEXT: switch i8 %{{.+}}, label %coro.ret [
+// CHECK-NEXT: i8 0, label %[[CASE1_AWAIT_READY]]
+// CHECK-NEXT: i8 1, label %[[CASE1_AWAIT_CLEANUP:.+]]
+// CHECK-NEXT: ]
+// CHECK: [[CASE1_AWAIT_CLEANUP]]:
+// make sure that the awaiter eventually gets cleaned up.
+// CHECK: call void @{{.+Awaiter.+}}
+
+// CHECK: [[CASE2]]:
+// CHECK: br i1 %{{.+}}, label %[[CASE2_AWAIT_READY:.+]], label %[[CASE2_AWAIT_SUSPEND:.+]]
+// CHECK: [[CASE2_AWAIT_SUSPEND]]:
+// CHECK-NEXT: %{{.+}} = call token @llvm.coro.save(i8* null)
+// CHECK-NEXT: %[[HANDLE21:.+]] = bitcast %"struct.std::experimental::coroutines_v1::coroutine_handle"* %[[TMP2:.+]] to i8*
+// CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 8, i8* %[[HANDLE21]])
+
+// CHECK: %[[HANDLE22:.+]] = bitcast %"struct.std::experimental::coroutines_v1::coroutine_handle"* %[[TMP2]] to i8*
+// CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 8, i8* %[[HANDLE22]])
+// CHECK-NEXT: call void @llvm.coro.resume
+// CHECK-NEXT: %{{.+}} = call i8 @llvm.coro.suspend
+// CHECK-NEXT: switch i8 %{{.+}}, label %coro.ret [
+// CHECK-NEXT: i8 0, label %[[CASE2_AWAIT_READY]]
+// CHECK-NEXT: i8 1, label %[[CASE2_AWAIT_CLEANUP:.+]]
+// CHECK-NEXT: ]
+// CHECK: [[CASE2_AWAIT_CLEANUP]]:
+// make sure that the awaiter eventually gets cleaned up.
+// CHECK: call void @{{.+Awaiter.+}}
diff --git a/clang/test/CodeGenCoroutines/coro-symmetric-transfer-02.cpp b/clang/test/CodeGenCoroutines/coro-symmetric-transfer-02.cpp
index 885c2db3fe82..de02765fc0c8 100644
--- a/clang/test/CodeGenCoroutines/coro-symmetric-transfer-02.cpp
+++ b/clang/test/CodeGenCoroutines/coro-symmetric-transfer-02.cpp
@@ -1,20 +1,18 @@
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fcoroutines-ts -std=c++14 -O1 -emit-llvm %s -o - -disable-llvm-passes | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -O1 -emit-llvm %s -o - -disable-llvm-passes | FileCheck %s
#include "Inputs/coroutine.h"
-namespace coro = std::experimental::coroutines_v1;
-
struct Task {
struct promise_type {
Task get_return_object() noexcept {
- return Task{coro::coroutine_handle<promise_type>::from_promise(*this)};
+ return Task{std::coroutine_handle<promise_type>::from_promise(*this)};
}
void return_void() noexcept {}
struct final_awaiter {
bool await_ready() noexcept { return false; }
- coro::coroutine_handle<> await_suspend(coro::coroutine_handle<promise_type> h) noexcept {
+ std::coroutine_handle<> await_suspend(std::coroutine_handle<promise_type> h) noexcept {
h.destroy();
return {};
}
@@ -25,7 +23,7 @@ struct Task {
final_awaiter final_suspend() noexcept { return {}; }
- coro::suspend_always initial_suspend() noexcept { return {}; }
+ std::suspend_always initial_suspend() noexcept { return {}; }
template <typename Awaitable>
auto await_transform(Awaitable &&awaitable) {
@@ -33,7 +31,7 @@ struct Task {
}
};
- using handle_t = coro::coroutine_handle<promise_type>;
+ using handle_t = std::coroutine_handle<promise_type>;
class Awaiter {
public:
@@ -43,7 +41,7 @@ struct Task {
~Awaiter();
bool await_ready() noexcept { return false; }
- handle_t await_suspend(coro::coroutine_handle<> continuation) noexcept;
+ handle_t await_suspend(std::coroutine_handle<> continuation) noexcept;
void await_resume();
private:
@@ -91,10 +89,10 @@ Task bar() {
// CHECK: br i1 %{{.+}}, label %[[CASE1_AWAIT_READY:.+]], label %[[CASE1_AWAIT_SUSPEND:.+]]
// CHECK: [[CASE1_AWAIT_SUSPEND]]:
// CHECK-NEXT: %{{.+}} = call token @llvm.coro.save(i8* null)
-// CHECK-NEXT: %[[HANDLE11:.+]] = bitcast %"struct.std::experimental::coroutines_v1::coroutine_handle"* %[[TMP1:.+]] to i8*
+// CHECK-NEXT: %[[HANDLE11:.+]] = bitcast %"struct.std::coroutine_handle"* %[[TMP1:.+]] to i8*
// CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 8, i8* %[[HANDLE11]])
-// CHECK: %[[HANDLE12:.+]] = bitcast %"struct.std::experimental::coroutines_v1::coroutine_handle"* %[[TMP1]] to i8*
+// CHECK: %[[HANDLE12:.+]] = bitcast %"struct.std::coroutine_handle"* %[[TMP1]] to i8*
// CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 8, i8* %[[HANDLE12]])
// CHECK-NEXT: call void @llvm.coro.resume
// CHECK-NEXT: %{{.+}} = call i8 @llvm.coro.suspend
@@ -110,10 +108,10 @@ Task bar() {
// CHECK: br i1 %{{.+}}, label %[[CASE2_AWAIT_READY:.+]], label %[[CASE2_AWAIT_SUSPEND:.+]]
// CHECK: [[CASE2_AWAIT_SUSPEND]]:
// CHECK-NEXT: %{{.+}} = call token @llvm.coro.save(i8* null)
-// CHECK-NEXT: %[[HANDLE21:.+]] = bitcast %"struct.std::experimental::coroutines_v1::coroutine_handle"* %[[TMP2:.+]] to i8*
+// CHECK-NEXT: %[[HANDLE21:.+]] = bitcast %"struct.std::coroutine_handle"* %[[TMP2:.+]] to i8*
// CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 8, i8* %[[HANDLE21]])
-// CHECK: %[[HANDLE22:.+]] = bitcast %"struct.std::experimental::coroutines_v1::coroutine_handle"* %[[TMP2]] to i8*
+// CHECK: %[[HANDLE22:.+]] = bitcast %"struct.std::coroutine_handle"* %[[TMP2]] to i8*
// CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 8, i8* %[[HANDLE22]])
// CHECK-NEXT: call void @llvm.coro.resume
// CHECK-NEXT: %{{.+}} = call i8 @llvm.coro.suspend
diff --git a/clang/test/CodeGenCoroutines/coro-unhandled-exception-exp-namespace.cpp b/clang/test/CodeGenCoroutines/coro-unhandled-exception-exp-namespace.cpp
new file mode 100644
index 000000000000..96d7211b99b8
--- /dev/null
+++ b/clang/test/CodeGenCoroutines/coro-unhandled-exception-exp-namespace.cpp
@@ -0,0 +1,78 @@
+// RUN: %clang_cc1 -std=c++14 -fcoroutines-ts -triple=x86_64-pc-windows-msvc18.0.0 -emit-llvm %s -o - -fexceptions -fcxx-exceptions -disable-llvm-passes | FileCheck %s
+// RUN: %clang_cc1 -std=c++14 -fcoroutines-ts -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s -fexceptions -fcxx-exceptions -disable-llvm-passes | FileCheck --check-prefix=CHECK-LPAD %s
+
+#include "Inputs/coroutine-exp-namespace.h"
+
+namespace coro = std::experimental::coroutines_v1;
+
+namespace std {
+using exception_ptr = int;
+exception_ptr current_exception();
+} // namespace std
+
+struct coro_t {
+ struct promise_type {
+ coro_t get_return_object() {
+ coro::coroutine_handle<promise_type>{};
+ return {};
+ }
+ coro::suspend_never initial_suspend() { return {}; }
+ coro::suspend_never final_suspend() noexcept { return {}; }
+ void return_void() {}
+ void unhandled_exception() noexcept;
+ };
+};
+
+struct Cleanup {
+ ~Cleanup();
+};
+void may_throw();
+
+coro_t f() {
+ Cleanup x;
+ may_throw();
+ co_return;
+}
+
+// CHECK: @"?f@@YA?AUcoro_t@@XZ"(
+// CHECK: invoke void @"?may_throw@@YAXXZ"()
+// CHECK: to label %{{.+}} unwind label %[[EHCLEANUP:.+]]
+// CHECK: [[EHCLEANUP]]:
+// CHECK: %[[INNERPAD:.+]] = cleanuppad within none []
+// CHECK: call void @"??1Cleanup@@QEAA at XZ"(
+// CHECK: cleanupret from %[[INNERPAD]] unwind label %[[CATCHSW:.+]]
+// CHECK: [[CATCHSW]]:
+// CHECK: %[[CATCHSWTOK:.+]] = catchswitch within none [label %[[CATCH:.+]]] unwind label
+// CHECK: [[CATCH]]:
+// CHECK: %[[CATCHTOK:.+]] = catchpad within [[CATCHSWTOK:.+]]
+// CHECK: call void @"?unhandled_exception at promise_type@coro_t@@QEAAXXZ"
+// CHECK: catchret from %[[CATCHTOK]] to label %[[CATCHRETDEST:.+]]
+// CHECK: [[CATCHRETDEST]]:
+// CHECK-NEXT: br label %[[TRYCONT:.+]]
+// CHECK: [[TRYCONT]]:
+// CHECK-NEXT: br label %[[COROFIN:.+]]
+// CHECK: [[COROFIN]]:
+// CHECK-NEXT: bitcast %"struct.std::experimental::coroutines_v1::suspend_never"* %{{.+}} to i8*
+// CHECK-NEXT: call void @llvm.lifetime.start.p0i8(
+// CHECK-NEXT: call void @"?final_suspend at promise_type@coro_t@@QEAA?AUsuspend_never at coroutines_v1@experimental at std@@XZ"(
+
+// CHECK-LPAD: @_Z1fv(
+// CHECK-LPAD: invoke void @_Z9may_throwv()
+// CHECK-LPAD: to label %[[CONT:.+]] unwind label %[[CLEANUP:.+]]
+// CHECK-LPAD: [[CLEANUP]]:
+// CHECK-LPAD: call void @_ZN7CleanupD1Ev(%struct.Cleanup* {{[^,]*}} %x) #2
+// CHECK-LPAD: br label %[[CATCH:.+]]
+
+// CHECK-LPAD: [[CATCH]]:
+// CHECK-LPAD: call i8* @__cxa_begin_catch
+// CHECK-LPAD: call void @_ZN6coro_t12promise_type19unhandled_exceptionEv(%"struct.coro_t::promise_type"* {{[^,]*}} %__promise) #2
+// CHECK-LPAD: invoke void @__cxa_end_catch()
+// CHECK-LPAD-NEXT: to label %[[CATCHRETDEST:.+]] unwind label
+// CHECK-LPAD: [[CATCHRETDEST]]:
+// CHECK-LPAD-NEXT: br label %[[TRYCONT:.+]]
+// CHECK-LPAD: [[TRYCONT]]:
+// CHECK-LPAD: br label %[[COROFIN:.+]]
+// CHECK-LPAD: [[COROFIN]]:
+// CHECK-LPAD-NEXT: bitcast %"struct.std::experimental::coroutines_v1::suspend_never"* %{{.+}} to i8*
+// CHECK-LPAD-NEXT: call void @llvm.lifetime.start.p0i8(
+// CHECK-LPAD-NEXT: call void @_ZN6coro_t12promise_type13final_suspendEv(
diff --git a/clang/test/CodeGenCoroutines/coro-unhandled-exception.cpp b/clang/test/CodeGenCoroutines/coro-unhandled-exception.cpp
index f038c5b3a913..8ad0e6bc4379 100644
--- a/clang/test/CodeGenCoroutines/coro-unhandled-exception.cpp
+++ b/clang/test/CodeGenCoroutines/coro-unhandled-exception.cpp
@@ -1,10 +1,8 @@
-// RUN: %clang_cc1 -std=c++14 -fcoroutines-ts -triple=x86_64-pc-windows-msvc18.0.0 -emit-llvm %s -o - -fexceptions -fcxx-exceptions -disable-llvm-passes | FileCheck %s
-// RUN: %clang_cc1 -std=c++14 -fcoroutines-ts -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s -fexceptions -fcxx-exceptions -disable-llvm-passes | FileCheck --check-prefix=CHECK-LPAD %s
+// RUN: %clang_cc1 -std=c++20 -triple=x86_64-pc-windows-msvc18.0.0 -emit-llvm %s -o - -fexceptions -fcxx-exceptions -disable-llvm-passes | FileCheck %s
+// RUN: %clang_cc1 -std=c++20 -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s -fexceptions -fcxx-exceptions -disable-llvm-passes | FileCheck --check-prefix=CHECK-LPAD %s
#include "Inputs/coroutine.h"
-namespace coro = std::experimental::coroutines_v1;
-
namespace std {
using exception_ptr = int;
exception_ptr current_exception();
@@ -13,11 +11,11 @@ namespace std {
struct coro_t {
struct promise_type {
coro_t get_return_object() {
- coro::coroutine_handle<promise_type>{};
+ std::coroutine_handle<promise_type>{};
return {};
}
- coro::suspend_never initial_suspend() { return {}; }
- coro::suspend_never final_suspend() noexcept { return {}; }
+ std::suspend_never initial_suspend() { return {}; }
+ std::suspend_never final_suspend() noexcept { return {}; }
void return_void(){}
void unhandled_exception() noexcept;
};
@@ -50,9 +48,9 @@ coro_t f() {
// CHECK: [[TRYCONT]]:
// CHECK-NEXT: br label %[[COROFIN:.+]]
// CHECK: [[COROFIN]]:
-// CHECK-NEXT: bitcast %"struct.std::experimental::coroutines_v1::suspend_never"* %{{.+}} to i8*
+// CHECK-NEXT: bitcast %"struct.std::suspend_never"* %{{.+}} to i8*
// CHECK-NEXT: call void @llvm.lifetime.start.p0i8(
-// CHECK-NEXT: call void @"?final_suspend at promise_type@coro_t@@QEAA?AUsuspend_never at coroutines_v1@experimental at std@@XZ"(
+// CHECK-NEXT: call void @"?final_suspend at promise_type@coro_t@@QEAA?AUsuspend_never at std@@XZ"(
// CHECK-LPAD: @_Z1fv(
// CHECK-LPAD: invoke void @_Z9may_throwv()
@@ -71,6 +69,6 @@ coro_t f() {
// CHECK-LPAD: [[TRYCONT]]:
// CHECK-LPAD: br label %[[COROFIN:.+]]
// CHECK-LPAD: [[COROFIN]]:
-// CHECK-LPAD-NEXT: bitcast %"struct.std::experimental::coroutines_v1::suspend_never"* %{{.+}} to i8*
+// CHECK-LPAD-NEXT: bitcast %"struct.std::suspend_never"* %{{.+}} to i8*
// CHECK-LPAD-NEXT: call void @llvm.lifetime.start.p0i8(
// CHECK-LPAD-NEXT: call void @_ZN6coro_t12promise_type13final_suspendEv(
diff --git a/clang/test/CodeGenCoroutines/microsoft-abi-operator-coawait-exp-namespace.cpp b/clang/test/CodeGenCoroutines/microsoft-abi-operator-coawait-exp-namespace.cpp
new file mode 100644
index 000000000000..33818c5d2fd3
--- /dev/null
+++ b/clang/test/CodeGenCoroutines/microsoft-abi-operator-coawait-exp-namespace.cpp
@@ -0,0 +1,24 @@
+// RUN: %clang_cc1 -triple x86_64-pc-windows-msvc18.0.0 -fcoroutines-ts -emit-llvm %s -o - -std=c++14 -disable-llvm-passes | FileCheck %s
+struct no_suspend {
+ bool await_ready() { return true; }
+ template <typename F> void await_suspend(F) {}
+ void await_resume() {}
+};
+
+struct A {
+ no_suspend operator co_await() { return {}; }
+};
+
+struct B {};
+
+no_suspend operator co_await(B const &) { return {}; }
+
+// CHECK-LABEL: f(
+extern "C" void f() {
+ A a;
+ B b;
+ // CHECK: call void @"??__LA@@QEAA?AUno_suspend@@XZ"(
+ a.operator co_await();
+ // CHECK-NEXT: call i8 @"??__L at YA?AUno_suspend@@AEBUB@@@Z"(
+ operator co_await(b);
+}
diff --git a/clang/test/CodeGenCoroutines/microsoft-abi-operator-coawait.cpp b/clang/test/CodeGenCoroutines/microsoft-abi-operator-coawait.cpp
index 26e379493088..6abb5334bcc9 100644
--- a/clang/test/CodeGenCoroutines/microsoft-abi-operator-coawait.cpp
+++ b/clang/test/CodeGenCoroutines/microsoft-abi-operator-coawait.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple x86_64-pc-windows-msvc18.0.0 -fcoroutines-ts -emit-llvm %s -o - -std=c++14 -disable-llvm-passes | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-pc-windows-msvc18.0.0 -emit-llvm %s -o - -std=c++20 -disable-llvm-passes | FileCheck %s
struct no_suspend {
bool await_ready() { return true; }
template <typename F> void await_suspend(F) {}
diff --git a/clang/test/CoverageMapping/coroutine.cpp b/clang/test/CoverageMapping/coroutine.cpp
index 0f7559849fb1..c9de301f8175 100644
--- a/clang/test/CoverageMapping/coroutine.cpp
+++ b/clang/test/CoverageMapping/coroutine.cpp
@@ -1,6 +1,6 @@
// RUN: %clang_cc1 -mllvm -emptyline-comment-coverage=false -triple x86_64-unknown-linux-gnu -fcoroutines-ts -std=c++14 -emit-llvm -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping %s -o - | FileCheck %s
-namespace std::experimental {
+namespace std {
template <typename... T>
struct coroutine_traits;
@@ -16,16 +16,16 @@ struct coroutine_handle<void> {
template <class PromiseType>
coroutine_handle(coroutine_handle<PromiseType>) noexcept {}
};
-} // namespace std::experimental
+} // namespace std
struct suspend_always {
bool await_ready() noexcept;
- void await_suspend(std::experimental::coroutine_handle<>) noexcept;
+ void await_suspend(std::coroutine_handle<>) noexcept;
void await_resume() noexcept;
};
template <>
-struct std::experimental::coroutine_traits<int, int> {
+struct std::coroutine_traits<int, int> {
struct promise_type {
int get_return_object();
suspend_always initial_suspend();
diff --git a/clang/test/Index/coroutines.cpp b/clang/test/Index/coroutines.cpp
index 000327ffaec4..9adda03ebc18 100644
--- a/clang/test/Index/coroutines.cpp
+++ b/clang/test/Index/coroutines.cpp
@@ -1,8 +1,8 @@
// RUN: c-index-test -test-load-source all -c %s -fsyntax-only -target x86_64-apple-darwin9 -fcoroutines-ts -std=c++1z -I%S/../SemaCXX/Inputs | FileCheck %s
#include "std-coroutine.h"
-using std::experimental::suspend_always;
-using std::experimental::suspend_never;
+using std::suspend_always;
+using std::suspend_never;
struct promise_void {
void get_return_object();
@@ -13,7 +13,7 @@ struct promise_void {
};
template <>
-struct std::experimental::coroutine_traits<void> { using promise_type = promise_void; };
+struct std::coroutine_traits<void> { using promise_type = promise_void; };
void CoroutineTestRet() {
co_return;
diff --git a/clang/test/Lexer/cxx-features.cpp b/clang/test/Lexer/cxx-features.cpp
index 40c73f601942..2847273d1d3a 100644
--- a/clang/test/Lexer/cxx-features.cpp
+++ b/clang/test/Lexer/cxx-features.cpp
@@ -95,6 +95,10 @@
#error "wrong value for __cpp_impl_three_way_comparison"
#endif
+#if check(impl_coroutine, 0, 0, 0, 0, 201902L, 201902L)
+#error "wrong value for __cpp_impl_coroutine"
+#endif
+
// init_captures checked below
#if check(modules, 0, 0, 0, 0, 0, 0)
diff --git a/clang/test/PCH/coroutines.cpp b/clang/test/PCH/coroutines.cpp
index f907d1213731..e5e84d01a7de 100644
--- a/clang/test/PCH/coroutines.cpp
+++ b/clang/test/PCH/coroutines.cpp
@@ -8,7 +8,7 @@
#ifndef HEADER
#define HEADER
-namespace std::experimental {
+namespace std {
template <typename... T> struct coroutine_traits;
template <class Promise = void> struct coroutine_handle {
@@ -21,15 +21,15 @@ template <> struct coroutine_handle<void> {
template <class PromiseType>
coroutine_handle(coroutine_handle<PromiseType>) noexcept;
};
-}
+} // namespace std
struct suspend_always {
bool await_ready() noexcept;
- void await_suspend(std::experimental::coroutine_handle<>) noexcept;
+ void await_suspend(std::coroutine_handle<>) noexcept;
void await_resume() noexcept;
};
-template <typename... Args> struct std::experimental::coroutine_traits<void, Args...> {
+template <typename... Args> struct std::coroutine_traits<void, Args...> {
struct promise_type {
void get_return_object() noexcept;
suspend_always initial_suspend() noexcept;
@@ -42,7 +42,7 @@ template <typename... Args> struct std::experimental::coroutine_traits<void, Arg
};
};
-template <typename... Args> struct std::experimental::coroutine_traits<int, Args...> {
+template <typename... Args> struct std::coroutine_traits<int, Args...> {
struct promise_type {
int get_return_object() noexcept;
suspend_always initial_suspend() noexcept;
diff --git a/clang/test/SemaCXX/Inputs/std-coroutine-exp-namespace.h b/clang/test/SemaCXX/Inputs/std-coroutine-exp-namespace.h
new file mode 100644
index 000000000000..27702b516faf
--- /dev/null
+++ b/clang/test/SemaCXX/Inputs/std-coroutine-exp-namespace.h
@@ -0,0 +1,35 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin9 %s -std=c++14 -fcoroutines-ts -fsyntax-only -Wignored-qualifiers -Wno-error=return-type -verify -fblocks -Wno-unreachable-code -Wno-unused-value
+#ifndef STD_COROUTINE_EXPERIMENTAL_H
+#define STD_COROUTINE_EXPERIMENTAL_H
+
+namespace std {
+namespace experimental {
+template <class Ret, typename... T>
+struct coroutine_traits { using promise_type = typename Ret::promise_type; };
+
+template <class Promise = void>
+struct coroutine_handle {
+ static coroutine_handle from_address(void *) noexcept;
+};
+template <>
+struct coroutine_handle<void> {
+ template <class PromiseType>
+ coroutine_handle(coroutine_handle<PromiseType>) 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 {}
+};
+} // namespace experimental
+} // namespace std
+
+#endif // STD_COROUTINE_EXPERIMENTAL_H
diff --git a/clang/test/SemaCXX/Inputs/std-coroutine.h b/clang/test/SemaCXX/Inputs/std-coroutine.h
index e9af21aa5194..9809cd7cde7d 100644
--- a/clang/test/SemaCXX/Inputs/std-coroutine.h
+++ b/clang/test/SemaCXX/Inputs/std-coroutine.h
@@ -1,9 +1,8 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin9 %s -std=c++14 -fcoroutines-ts -fsyntax-only -Wignored-qualifiers -Wno-error=return-type -verify -fblocks -Wno-unreachable-code -Wno-unused-value
+// RUN: %clang_cc1 -triple x86_64-apple-darwin9 %s -std=c++20 -fsyntax-only -Wignored-qualifiers -Wno-error=return-type -verify -fblocks -Wno-unreachable-code -Wno-unused-value
#ifndef STD_COROUTINE_H
#define STD_COROUTINE_H
namespace std {
-namespace experimental {
template <class Ret, typename... T>
struct coroutine_traits { using promise_type = typename Ret::promise_type; };
@@ -31,7 +30,6 @@ struct suspend_never {
void await_resume() noexcept {}
};
-} // namespace experimental
} // namespace std
#endif // STD_COROUTINE_H
diff --git a/clang/test/SemaCXX/co_await-range-for-exp-namespace.cpp b/clang/test/SemaCXX/co_await-range-for-exp-namespace.cpp
new file mode 100644
index 000000000000..daf78b02ca87
--- /dev/null
+++ b/clang/test/SemaCXX/co_await-range-for-exp-namespace.cpp
@@ -0,0 +1,171 @@
+// 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
+#include "Inputs/std-coroutine-exp-namespace.h"
+
+using namespace std::experimental;
+
+template <class Begin>
+struct Awaiter {
+ bool await_ready();
+ void await_suspend(coroutine_handle<>);
+ Begin await_resume();
+};
+
+template <class Iter> struct BeginTag { BeginTag() = delete; };
+template <class Iter> struct IncTag { IncTag() = delete; };
+
+template <class Iter, bool Delete = false>
+struct CoawaitTag { CoawaitTag() = delete; };
+
+template <class T>
+struct Iter {
+ using value_type = T;
+ using reference = T &;
+ using pointer = T *;
+
+ IncTag<Iter> operator++();
+ reference operator*();
+ pointer operator->();
+};
+template <class T> bool operator==(Iter<T>, Iter<T>);
+template <class T> bool operator!=(Iter<T>, Iter<T>);
+
+template <class T>
+struct Range {
+ BeginTag<Iter<T>> begin();
+ Iter<T> end();
+};
+
+struct MyForLoopArrayAwaiter {
+ struct promise_type {
+ MyForLoopArrayAwaiter get_return_object() { return {}; }
+ void return_void();
+ void unhandled_exception();
+ suspend_never initial_suspend();
+ suspend_never final_suspend() noexcept;
+ template <class T>
+ Awaiter<T *> await_transform(T *) = delete; // expected-note {{explicitly deleted}}
+ };
+};
+MyForLoopArrayAwaiter g() {
+ int arr[10] = {0};
+ for
+ co_await(auto i : arr) {}
+ // expected-error at -1 {{call to deleted member function 'await_transform'}}
+ // expected-note at -2 {{'await_transform' implicitly required by 'co_await' here}}
+}
+
+struct ForLoopAwaiterBadBeginTransform {
+ struct promise_type {
+ ForLoopAwaiterBadBeginTransform get_return_object();
+ void return_void();
+ void unhandled_exception();
+ suspend_never initial_suspend();
+ suspend_never final_suspend() noexcept;
+
+ template <class T>
+ Awaiter<T> await_transform(BeginTag<T>) = delete; // expected-note 1+ {{explicitly deleted}}
+
+ template <class T>
+ CoawaitTag<T> await_transform(IncTag<T>); // expected-note 1+ {{candidate}}
+ };
+};
+ForLoopAwaiterBadBeginTransform bad_begin() {
+ Range<int> R;
+ for
+ co_await(auto i : R) {}
+ // expected-error at -1 {{call to deleted member function 'await_transform'}}
+ // expected-note at -2 {{'await_transform' implicitly required by 'co_await' here}}
+}
+template <class Dummy>
+ForLoopAwaiterBadBeginTransform bad_begin_template(Dummy) {
+ Range<Dummy> R;
+ for
+ co_await(auto i : R) {}
+ // expected-error at -1 {{call to deleted member function 'await_transform'}}
+ // expected-note at -2 {{'await_transform' implicitly required by 'co_await' here}}
+}
+template ForLoopAwaiterBadBeginTransform bad_begin_template(int); // expected-note {{requested here}}
+
+template <class Iter>
+Awaiter<Iter> operator co_await(CoawaitTag<Iter, true>) = delete;
+// expected-note at -1 1+ {{explicitly deleted}}
+
+struct ForLoopAwaiterBadIncTransform {
+ struct promise_type {
+ ForLoopAwaiterBadIncTransform get_return_object();
+ void return_void();
+ void unhandled_exception();
+ suspend_never initial_suspend();
+ suspend_never final_suspend() noexcept;
+
+ template <class T>
+ Awaiter<T> await_transform(BeginTag<T> e);
+
+ template <class T>
+ CoawaitTag<T, true> await_transform(IncTag<T>);
+ };
+};
+ForLoopAwaiterBadIncTransform bad_inc_transform() {
+ Range<float> R;
+ for
+ co_await(auto i : R) {}
+ // expected-error at -1 {{overload resolution selected deleted operator 'co_await'}}
+ // expected-note at -2 {{in implicit call to 'operator++' for iterator of type 'Range<float>'}}
+}
+
+template <class Dummy>
+ForLoopAwaiterBadIncTransform bad_inc_transform_template(Dummy) {
+ Range<Dummy> R;
+ for
+ co_await(auto i : R) {}
+ // expected-error at -1 {{overload resolution selected deleted operator 'co_await'}}
+ // expected-note at -2 {{in implicit call to 'operator++' for iterator of type 'Range<long>'}}
+}
+template ForLoopAwaiterBadIncTransform bad_inc_transform_template(long); // expected-note {{requested here}}
+
+// Ensure we mark and check the function as a coroutine even if it's
+// never instantiated.
+template <class T>
+constexpr void never_instant(T) {
+ static_assert(sizeof(T) != sizeof(T), "function should not be instantiated");
+ for
+ co_await(auto i : foo(T{})) {}
+ // expected-error at -1 {{'co_await' cannot be used in a constexpr function}}
+}
+
+namespace NS {
+struct ForLoopAwaiterCoawaitLookup {
+ struct promise_type {
+ ForLoopAwaiterCoawaitLookup get_return_object();
+ void return_void();
+ void unhandled_exception();
+ suspend_never initial_suspend();
+ suspend_never final_suspend() noexcept;
+ template <class T>
+ CoawaitTag<T, false> await_transform(BeginTag<T> e);
+ template <class T>
+ Awaiter<T> await_transform(IncTag<T>);
+ };
+};
+} // namespace NS
+using NS::ForLoopAwaiterCoawaitLookup;
+
+template <class T>
+ForLoopAwaiterCoawaitLookup test_coawait_lookup(T) {
+ Range<T> R;
+ for
+ co_await(auto i : R) {}
+ // expected-error at -1 {{no member named 'await_ready' in 'CoawaitTag<Iter<int>, false>'}}
+}
+template ForLoopAwaiterCoawaitLookup test_coawait_lookup(int); // expected-note {{requested here}}
+
+// FIXME: This test should fail as well since the newly declared operator co_await
+// should not be found by lookup.
+namespace NS2 {
+template <class Iter>
+Awaiter<Iter> operator co_await(CoawaitTag<Iter, false>);
+}
+using NS2::operator co_await;
+template ForLoopAwaiterCoawaitLookup test_coawait_lookup(long);
diff --git a/clang/test/SemaCXX/co_await-range-for.cpp b/clang/test/SemaCXX/co_await-range-for.cpp
index b6c6e6c40f97..e43f55a3434d 100644
--- a/clang/test/SemaCXX/co_await-range-for.cpp
+++ b/clang/test/SemaCXX/co_await-range-for.cpp
@@ -1,10 +1,9 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin9 %s -std=c++14 -fcoroutines-ts \
+// RUN: %clang_cc1 -triple x86_64-apple-darwin9 %s -std=c++20 \
// RUN: -fsyntax-only -Wignored-qualifiers -Wno-error=return-type -verify \
// RUN: -fblocks
#include "Inputs/std-coroutine.h"
-using namespace std::experimental;
-
+using namespace std;
template <class Begin>
struct Awaiter {
diff --git a/clang/test/SemaCXX/coreturn-eh-exp-namespace.cpp b/clang/test/SemaCXX/coreturn-eh-exp-namespace.cpp
new file mode 100644
index 000000000000..d49e23ad31e2
--- /dev/null
+++ b/clang/test/SemaCXX/coreturn-eh-exp-namespace.cpp
@@ -0,0 +1,47 @@
+// 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
+// expected-no-diagnostics
+
+#include "Inputs/std-coroutine-exp-namespace.h"
+
+using std::experimental::suspend_always;
+using std::experimental::suspend_never;
+
+struct awaitable {
+ bool await_ready();
+ void await_suspend(std::experimental::coroutine_handle<>); // FIXME: coroutine_handle
+ void await_resume();
+} a;
+
+struct object {
+ ~object() {}
+};
+
+struct promise_void_return_value {
+ void get_return_object();
+ suspend_always initial_suspend();
+ suspend_always final_suspend() noexcept;
+ void unhandled_exception();
+ void return_value(object);
+};
+
+struct VoidTagReturnValue {
+ struct promise_type {
+ VoidTagReturnValue get_return_object();
+ suspend_always initial_suspend();
+ suspend_always final_suspend() noexcept;
+ void unhandled_exception();
+ void return_value(object);
+ };
+};
+
+template <typename T1>
+struct std::experimental::coroutine_traits<void, T1> { using promise_type = promise_void_return_value; };
+
+VoidTagReturnValue test() {
+ object x = {};
+ try {
+ co_return {};
+ } catch (...) {
+ throw;
+ }
+}
diff --git a/clang/test/SemaCXX/coreturn-eh.cpp b/clang/test/SemaCXX/coreturn-eh.cpp
index 591ab8ec5c5e..0d409b9b99bb 100644
--- a/clang/test/SemaCXX/coreturn-eh.cpp
+++ b/clang/test/SemaCXX/coreturn-eh.cpp
@@ -1,14 +1,14 @@
-// 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
+// RUN: %clang_cc1 -triple x86_64-apple-darwin9 %s -std=c++20 -fcxx-exceptions -fsyntax-only -Wignored-qualifiers -Wno-error=return-type -verify -fblocks -Wall -Wextra -Wno-error=unreachable-code
// expected-no-diagnostics
#include "Inputs/std-coroutine.h"
-using std::experimental::suspend_always;
-using std::experimental::suspend_never;
+using std::suspend_always;
+using std::suspend_never;
struct awaitable {
bool await_ready();
- void await_suspend(std::experimental::coroutine_handle<>); // FIXME: coroutine_handle
+ void await_suspend(std::coroutine_handle<>); // FIXME: coroutine_handle
void await_resume();
} a;
@@ -33,7 +33,7 @@ struct VoidTagReturnValue {
};
template <typename T1>
-struct std::experimental::coroutine_traits<void, T1> { using promise_type = promise_void_return_value; };
+struct std::coroutine_traits<void, T1> { using promise_type = promise_void_return_value; };
VoidTagReturnValue test() {
object x = {};
diff --git a/clang/test/SemaCXX/coreturn-exp-namespace.cpp b/clang/test/SemaCXX/coreturn-exp-namespace.cpp
new file mode 100644
index 000000000000..c7fe212daed3
--- /dev/null
+++ b/clang/test/SemaCXX/coreturn-exp-namespace.cpp
@@ -0,0 +1,140 @@
+// 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
+#include "Inputs/std-coroutine-exp-namespace.h"
+
+using std::experimental::suspend_always;
+using std::experimental::suspend_never;
+
+struct awaitable {
+ bool await_ready();
+ void await_suspend(std::experimental::coroutine_handle<>); // FIXME: coroutine_handle
+ void await_resume();
+} a;
+
+struct promise_void {
+ void get_return_object();
+ suspend_always initial_suspend();
+ suspend_always final_suspend() noexcept;
+ void return_void();
+ void unhandled_exception();
+};
+
+struct promise_void_return_value {
+ void get_return_object();
+ suspend_always initial_suspend();
+ suspend_always final_suspend() noexcept;
+ void unhandled_exception();
+ void return_value(int);
+};
+
+struct VoidTagNoReturn {
+ struct promise_type {
+ VoidTagNoReturn get_return_object();
+ suspend_always initial_suspend();
+ suspend_always final_suspend() noexcept;
+ void unhandled_exception();
+ };
+};
+
+struct VoidTagReturnValue {
+ struct promise_type {
+ VoidTagReturnValue get_return_object();
+ suspend_always initial_suspend();
+ suspend_always final_suspend() noexcept;
+ void unhandled_exception();
+ void return_value(int);
+ };
+};
+
+struct VoidTagReturnVoid {
+ struct promise_type {
+ VoidTagReturnVoid get_return_object();
+ suspend_always initial_suspend();
+ suspend_always final_suspend() noexcept;
+ void unhandled_exception();
+ void return_void();
+ };
+};
+
+struct promise_float {
+ float get_return_object();
+ suspend_always initial_suspend();
+ suspend_always final_suspend() noexcept;
+ void return_void();
+ void unhandled_exception();
+};
+
+struct promise_int {
+ int get_return_object();
+ suspend_always initial_suspend();
+ suspend_always final_suspend() noexcept;
+ void return_value(int);
+ void unhandled_exception();
+};
+
+template <>
+struct std::experimental::coroutine_traits<void> { using promise_type = promise_void; };
+
+template <typename T1>
+struct std::experimental::coroutine_traits<void, T1> { using promise_type = promise_void_return_value; };
+
+template <typename... T>
+struct std::experimental::coroutine_traits<float, T...> { using promise_type = promise_float; };
+
+template <typename... T>
+struct std::experimental::coroutine_traits<int, T...> { using promise_type = promise_int; };
+
+void test0() { co_await a; }
+float test1() { co_await a; }
+
+int test2() {
+ co_await a;
+} // expected-warning {{non-void coroutine does not return a value}}
+
+int test2a(bool b) {
+ if (b)
+ co_return 42;
+} // expected-warning {{non-void coroutine does not return a value in all control paths}}
+
+int test3() {
+ co_await a;
+b:
+ goto b;
+}
+
+int test4() {
+ co_return 42;
+}
+
+void test5(int) {
+ co_await a;
+} // expected-warning {{non-void coroutine does not return a value}}
+
+void test6(int x) {
+ if (x)
+ co_return 42;
+} // expected-warning {{non-void coroutine does not return a value in all control paths}}
+
+void test7(int y) {
+ if (y)
+ co_return 42;
+ else
+ co_return 101;
+}
+
+VoidTagReturnVoid test8() {
+ co_await a;
+}
+
+VoidTagReturnVoid test9(bool b) {
+ if (b)
+ co_return;
+}
+
+VoidTagReturnValue test10() {
+ co_await a;
+} // expected-warning {{non-void coroutine does not return a value}}
+
+VoidTagReturnValue test11(bool b) {
+ if (b)
+ co_return 42;
+} // expected-warning {{non-void coroutine does not return a value in all control paths}}
diff --git a/clang/test/SemaCXX/coreturn.cpp b/clang/test/SemaCXX/coreturn.cpp
index eaa462016de5..7069a1040db2 100644
--- a/clang/test/SemaCXX/coreturn.cpp
+++ b/clang/test/SemaCXX/coreturn.cpp
@@ -1,12 +1,12 @@
-// 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
+// RUN: %clang_cc1 -triple x86_64-apple-darwin9 %s -std=c++20 -fsyntax-only -Wignored-qualifiers -Wno-error=return-type -verify -fblocks -Wall -Wextra -Wno-error=unreachable-code
#include "Inputs/std-coroutine.h"
-using std::experimental::suspend_always;
-using std::experimental::suspend_never;
+using std::suspend_always;
+using std::suspend_never;
struct awaitable {
bool await_ready();
- void await_suspend(std::experimental::coroutine_handle<>); // FIXME: coroutine_handle
+ void await_suspend(std::coroutine_handle<>); // FIXME: coroutine_handle
void await_resume();
} a;
@@ -72,16 +72,16 @@ struct promise_int {
};
template <>
-struct std::experimental::coroutine_traits<void> { using promise_type = promise_void; };
+struct std::coroutine_traits<void> { using promise_type = promise_void; };
template <typename T1>
-struct std::experimental::coroutine_traits<void, T1> { using promise_type = promise_void_return_value; };
+struct std::coroutine_traits<void, T1> { using promise_type = promise_void_return_value; };
template <typename... T>
-struct std::experimental::coroutine_traits<float, T...> { using promise_type = promise_float; };
+struct std::coroutine_traits<float, T...> { using promise_type = promise_float; };
template <typename... T>
-struct std::experimental::coroutine_traits<int, T...> { using promise_type = promise_int; };
+struct std::coroutine_traits<int, T...> { using promise_type = promise_int; };
void test0() { co_await a; }
float test1() { co_await a; }
diff --git a/clang/test/SemaCXX/coroutine-builtins.cpp b/clang/test/SemaCXX/coroutine-builtins.cpp
index 927faf6974b9..7fa1b49172c2 100644
--- a/clang/test/SemaCXX/coroutine-builtins.cpp
+++ b/clang/test/SemaCXX/coroutine-builtins.cpp
@@ -1,7 +1,8 @@
// RUN: %clang_cc1 -fsyntax-only -verify -fcoroutines-ts %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 %s
// RUN: %clang_cc1 -fsyntax-only -verify -DERRORS %s
-// Check that we don't crash when using __builtin_coro_* without the fcoroutine-ts option
+// Check that we don't crash when using __builtin_coro_* without the fcoroutine-ts or -std=c++20 option
#ifdef ERRORS
// expected-error@#A{{use of undeclared identifier '__builtin_coro_done'}}
diff --git a/clang/test/SemaCXX/coroutine-final-suspend-noexcept-exp-namespace.cpp b/clang/test/SemaCXX/coroutine-final-suspend-noexcept-exp-namespace.cpp
new file mode 100644
index 000000000000..92bb91be93a1
--- /dev/null
+++ b/clang/test/SemaCXX/coroutine-final-suspend-noexcept-exp-namespace.cpp
@@ -0,0 +1,70 @@
+// This file contains references to sections of the Coroutines TS, which can be
+// found at http://wg21.link/coroutines.
+
+// RUN: %clang_cc1 -std=c++14 -fcoroutines-ts -verify %s -fcxx-exceptions -fexceptions -Wunused-result
+
+namespace std {
+namespace experimental {
+template <class Ret, typename... T>
+struct coroutine_traits { using promise_type = typename Ret::promise_type; };
+
+template <class Promise = void>
+struct coroutine_handle {
+ static coroutine_handle from_address(void *); // expected-note 2 {{must be declared with 'noexcept'}}
+};
+template <>
+struct coroutine_handle<void> {
+ template <class PromiseType>
+ coroutine_handle(coroutine_handle<PromiseType>); // expected-note 2 {{must be declared with 'noexcept'}}
+};
+
+struct suspend_never {
+ bool await_ready() { return true; } // expected-note 2 {{must be declared with 'noexcept'}}
+ void await_suspend(coroutine_handle<>) {} // expected-note 2 {{must be declared with 'noexcept'}}
+ void await_resume() {} // expected-note 2 {{must be declared with 'noexcept'}}
+ ~suspend_never() noexcept(false); // expected-note 2 {{must be declared with 'noexcept'}}
+};
+
+struct suspend_always {
+ bool await_ready() { return false; }
+ void await_suspend(coroutine_handle<>) {}
+ void await_resume() {}
+ suspend_never operator co_await(); // expected-note 2 {{must be declared with 'noexcept'}}
+ ~suspend_always() noexcept(false); // expected-note 2 {{must be declared with 'noexcept'}}
+};
+} // namespace experimental
+} // namespace std
+
+using namespace std;
+
+struct A {
+ bool await_ready();
+ void await_resume();
+ template <typename F>
+ void await_suspend(F);
+};
+
+struct coro_t {
+ struct promise_type {
+ coro_t get_return_object();
+ std::experimental::suspend_never initial_suspend();
+ std::experimental::suspend_always final_suspend(); // expected-note 2 {{must be declared with 'noexcept'}}
+ void return_void();
+ static void unhandled_exception();
+ };
+};
+
+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;
+}
+
+template <typename T>
+coro_t f_dep(T n) { // expected-error {{the expression 'co_await __promise.final_suspend()' is required to be non-throwing}}
+ A a{};
+ co_await a;
+}
+
+void foo() {
+ f_dep<int>(5); // expected-note {{in instantiation of function template specialization 'f_dep<int>' requested here}}
+}
diff --git a/clang/test/SemaCXX/coroutine-final-suspend-noexcept.cpp b/clang/test/SemaCXX/coroutine-final-suspend-noexcept.cpp
index 48c65f8afb95..8635e4156a41 100644
--- a/clang/test/SemaCXX/coroutine-final-suspend-noexcept.cpp
+++ b/clang/test/SemaCXX/coroutine-final-suspend-noexcept.cpp
@@ -1,10 +1,9 @@
// This file contains references to sections of the Coroutines TS, which can be
// found at http://wg21.link/coroutines.
-// RUN: %clang_cc1 -std=c++14 -fcoroutines-ts -verify %s -fcxx-exceptions -fexceptions -Wunused-result
+// RUN: %clang_cc1 -std=c++20 -verify %s -fcxx-exceptions -fexceptions -Wunused-result
namespace std {
-namespace experimental {
template <class Ret, typename... T>
struct coroutine_traits { using promise_type = typename Ret::promise_type; };
@@ -34,10 +33,9 @@ struct suspend_always {
~suspend_always() noexcept(false); // expected-note 2 {{must be declared with 'noexcept'}}
};
-} // namespace experimental
} // namespace std
-using namespace std::experimental;
+using namespace std;
struct A {
bool await_ready();
diff --git a/clang/test/SemaCXX/coroutine-mixed-exp-namespace.cpp b/clang/test/SemaCXX/coroutine-mixed-exp-namespace.cpp
new file mode 100644
index 000000000000..5d1e687d5bd8
--- /dev/null
+++ b/clang/test/SemaCXX/coroutine-mixed-exp-namespace.cpp
@@ -0,0 +1,27 @@
+// 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
+
+#include "Inputs/std-coroutine-exp-namespace.h"
+#include "Inputs/std-coroutine.h"
+
+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<void> { 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.}}
+}
diff --git a/clang/test/SemaCXX/coroutine-mixed2-exp-namespace.cpp b/clang/test/SemaCXX/coroutine-mixed2-exp-namespace.cpp
new file mode 100644
index 000000000000..dcccd2b62e0c
--- /dev/null
+++ b/clang/test/SemaCXX/coroutine-mixed2-exp-namespace.cpp
@@ -0,0 +1,28 @@
+// This file is to test the mixed use of `std::experimental::coroutine*` and `std::coroutine*`
+// which is similar to coroutine-mixed-exp-namesapce. This file tests the relative order of
+// included header wouldn't affect the diagnostic messages.
+// RUN: %clang_cc1 -verify -std=c++20 -fsyntax-only %s
+
+#include "Inputs/std-coroutine-exp-namespace.h"
+#include "Inputs/std-coroutine.h"
+
+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<void> { 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.}}
+}
diff --git a/clang/test/SemaCXX/coroutine-rvo-exp-namespace.cpp b/clang/test/SemaCXX/coroutine-rvo-exp-namespace.cpp
new file mode 100644
index 000000000000..c65094c2e513
--- /dev/null
+++ b/clang/test/SemaCXX/coroutine-rvo-exp-namespace.cpp
@@ -0,0 +1,148 @@
+// RUN: %clang_cc1 -verify -std=c++17 -fcoroutines-ts -fsyntax-only %s
+
+namespace std::experimental {
+template <class Promise = void> struct coroutine_handle {
+ coroutine_handle() = default;
+ static coroutine_handle from_address(void *) noexcept;
+};
+
+template <> struct coroutine_handle<void> {
+ static coroutine_handle from_address(void *) noexcept;
+ coroutine_handle() = default;
+ template <class PromiseType>
+ coroutine_handle(coroutine_handle<PromiseType>) noexcept;
+};
+
+template <class... Args>
+struct void_t_imp {
+ using type = void;
+};
+template <class... Args>
+using void_t = typename void_t_imp<Args...>::type;
+
+template <class T, class = void>
+struct traits_sfinae_base {};
+
+template <class T>
+struct traits_sfinae_base<T, void_t<typename T::promise_type>> {
+ using promise_type = typename T::promise_type;
+};
+
+template <class Ret, class... Args>
+struct coroutine_traits : public traits_sfinae_base<Ret> {};
+} // namespace std::experimental
+
+struct suspend_never {
+ bool await_ready() noexcept;
+ void await_suspend(std::experimental::coroutine_handle<>) noexcept;
+ void await_resume() noexcept;
+};
+
+struct MoveOnly {
+ MoveOnly() = default;
+ MoveOnly(const MoveOnly &) = delete;
+ MoveOnly(MoveOnly &&) = default;
+};
+
+struct NoCopyNoMove {
+ NoCopyNoMove() = default;
+ NoCopyNoMove(const NoCopyNoMove &) = delete;
+};
+
+template <typename T>
+struct task {
+ struct promise_type {
+ auto initial_suspend() { return suspend_never{}; }
+ auto final_suspend() noexcept { return suspend_never{}; }
+ auto get_return_object() { return task{}; }
+ static void unhandled_exception() {}
+ void return_value(T &&value) {} // expected-note 4{{passing argument}}
+ };
+};
+
+task<NoCopyNoMove> local2val() {
+ NoCopyNoMove value;
+ co_return value;
+}
+
+task<NoCopyNoMove &> local2ref() {
+ NoCopyNoMove value;
+ co_return value; // expected-error {{non-const lvalue reference to type 'NoCopyNoMove' cannot bind to a temporary of type 'NoCopyNoMove'}}
+}
+
+// We need the move constructor for construction of the coroutine.
+task<MoveOnly> param2val(MoveOnly value) {
+ co_return value;
+}
+
+task<NoCopyNoMove> lvalue2val(NoCopyNoMove &value) {
+ co_return value; // expected-error {{rvalue reference to type 'NoCopyNoMove' cannot bind to lvalue of type 'NoCopyNoMove'}}
+}
+
+task<NoCopyNoMove> rvalue2val(NoCopyNoMove &&value) {
+ co_return value;
+}
+
+task<NoCopyNoMove &> lvalue2ref(NoCopyNoMove &value) {
+ co_return value;
+}
+
+task<NoCopyNoMove &> rvalue2ref(NoCopyNoMove &&value) {
+ co_return value; // expected-error {{non-const lvalue reference to type 'NoCopyNoMove' cannot bind to a temporary of type 'NoCopyNoMove'}}
+}
+
+struct To {
+ operator MoveOnly() &&;
+};
+task<MoveOnly> conversion_operator() {
+ To t;
+ co_return t;
+}
+
+struct Construct {
+ Construct(MoveOnly);
+};
+task<Construct> converting_constructor() {
+ MoveOnly w;
+ co_return w;
+}
+
+struct Derived : MoveOnly {};
+task<MoveOnly> derived2base() {
+ Derived result;
+ co_return result;
+}
+
+struct RetThis {
+ task<RetThis> foo() && {
+ co_return *this; // expected-error {{rvalue reference to type 'RetThis' cannot bind to lvalue of type 'RetThis'}}
+ }
+};
+
+template <typename, typename>
+struct is_same { static constexpr bool value = false; };
+
+template <typename T>
+struct is_same<T, T> { static constexpr bool value = true; };
+
+template <typename T>
+struct generic_task {
+ struct promise_type {
+ auto initial_suspend() { return suspend_never{}; }
+ auto final_suspend() noexcept { return suspend_never{}; }
+ auto get_return_object() { return generic_task{}; }
+ static void unhandled_exception();
+ template <typename U>
+ void return_value(U &&value) {
+ static_assert(is_same<T, U>::value);
+ }
+ };
+};
+
+generic_task<MoveOnly> param2template(MoveOnly value) {
+ co_return value; // We should deduce U = MoveOnly.
+}
+
+generic_task<NoCopyNoMove &> lvalue2template(NoCopyNoMove &value) {
+ co_return value; // We should deduce U = NoCopyNoMove&.
+}
diff --git a/clang/test/SemaCXX/coroutine-rvo.cpp b/clang/test/SemaCXX/coroutine-rvo.cpp
index 2c4bb0792cea..6bf1dee67557 100644
--- a/clang/test/SemaCXX/coroutine-rvo.cpp
+++ b/clang/test/SemaCXX/coroutine-rvo.cpp
@@ -1,6 +1,6 @@
-// RUN: %clang_cc1 -verify -std=c++17 -fcoroutines-ts -fsyntax-only %s
+// RUN: %clang_cc1 -verify -std=c++20 -fsyntax-only %s
-namespace std::experimental {
+namespace std {
template <class Promise = void> struct coroutine_handle {
coroutine_handle() = default;
static coroutine_handle from_address(void *) noexcept;
@@ -30,11 +30,11 @@ struct traits_sfinae_base<T, void_t<typename T::promise_type>> {
template <class Ret, class... Args>
struct coroutine_traits : public traits_sfinae_base<Ret> {};
-}
+} // namespace std
struct suspend_never {
bool await_ready() noexcept;
- void await_suspend(std::experimental::coroutine_handle<>) noexcept;
+ void await_suspend(std::coroutine_handle<>) noexcept;
void await_resume() noexcept;
};
diff --git a/clang/test/SemaCXX/coroutine-seh-exp-namespace.cpp b/clang/test/SemaCXX/coroutine-seh-exp-namespace.cpp
new file mode 100644
index 000000000000..cc5fce9fae7f
--- /dev/null
+++ b/clang/test/SemaCXX/coroutine-seh-exp-namespace.cpp
@@ -0,0 +1,38 @@
+// RUN: %clang_cc1 -std=c++1z -fcoroutines-ts -verify %s -fcxx-exceptions -fexceptions -triple x86_64-windows-msvc -fms-extensions
+namespace std::experimental {
+template <typename... T> struct coroutine_traits;
+
+template <class Promise = void> struct coroutine_handle {
+ coroutine_handle() = default;
+ static coroutine_handle from_address(void *) noexcept;
+};
+template <> struct coroutine_handle<void> {
+ static coroutine_handle from_address(void *) noexcept;
+ coroutine_handle() = default;
+ template <class PromiseType>
+ coroutine_handle(coroutine_handle<PromiseType>) noexcept;
+};
+} // namespace std::experimental
+
+struct suspend_always {
+ bool await_ready() noexcept;
+ void await_suspend(std::experimental::coroutine_handle<>) noexcept;
+ void await_resume() noexcept;
+};
+
+template <> struct std::experimental::coroutine_traits<void> {
+ struct promise_type {
+ void get_return_object() noexcept;
+ suspend_always initial_suspend() noexcept;
+ suspend_always final_suspend() noexcept;
+ void return_void() noexcept;
+ void unhandled_exception() noexcept;
+ };
+};
+
+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}}
+ } __except (0) {
+ }
+}
diff --git a/clang/test/SemaCXX/coroutine-seh.cpp b/clang/test/SemaCXX/coroutine-seh.cpp
index 647bb68b3185..6e778d998881 100644
--- a/clang/test/SemaCXX/coroutine-seh.cpp
+++ b/clang/test/SemaCXX/coroutine-seh.cpp
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -std=c++1z -fcoroutines-ts -verify %s -fcxx-exceptions -fexceptions -triple x86_64-windows-msvc -fms-extensions
-namespace std::experimental {
+// RUN: %clang_cc1 -std=c++20 -verify %s -fcxx-exceptions -fexceptions -triple x86_64-windows-msvc -fms-extensions
+namespace std {
template <typename... T> struct coroutine_traits;
template <class Promise = void> struct coroutine_handle {
@@ -12,15 +12,15 @@ template <> struct coroutine_handle<void> {
template <class PromiseType>
coroutine_handle(coroutine_handle<PromiseType>) noexcept;
};
-}
+} // namespace std
struct suspend_always {
bool await_ready() noexcept;
- void await_suspend(std::experimental::coroutine_handle<>) noexcept;
+ void await_suspend(std::coroutine_handle<>) noexcept;
void await_resume() noexcept;
};
-template <> struct std::experimental::coroutine_traits<void> {
+template <> struct std::coroutine_traits<void> {
struct promise_type {
void get_return_object() noexcept;
suspend_always initial_suspend() noexcept;
diff --git a/clang/test/SemaCXX/coroutine-traits-undefined-template-exp-namespace.cpp b/clang/test/SemaCXX/coroutine-traits-undefined-template-exp-namespace.cpp
new file mode 100644
index 000000000000..4179d20f4491
--- /dev/null
+++ b/clang/test/SemaCXX/coroutine-traits-undefined-template-exp-namespace.cpp
@@ -0,0 +1,19 @@
+// This file contains references to sections of the Coroutines TS, which can be
+// found at http://wg21.link/coroutines.
+
+// RUN: %clang_cc1 -std=c++14 -fcoroutines-ts -verify %s -fcxx-exceptions -fexceptions -Wunused-result
+
+namespace std {
+namespace experimental {
+template <typename... T>
+struct coroutine_traits {
+ struct promise_type {};
+};
+
+template <> struct coroutine_traits<void>; // expected-note {{forward declaration of 'std::experimental::coroutine_traits<void>'}}
+} // namespace experimental
+} // namespace std
+
+void uses_forward_declaration() {
+ co_return; // expected-error {{this function cannot be a coroutine: missing definition of specialization 'coroutine_traits<void>'}}
+}
diff --git a/clang/test/SemaCXX/coroutine-traits-undefined-template.cpp b/clang/test/SemaCXX/coroutine-traits-undefined-template.cpp
index 4687ed245ac8..ea25cea365b9 100644
--- a/clang/test/SemaCXX/coroutine-traits-undefined-template.cpp
+++ b/clang/test/SemaCXX/coroutine-traits-undefined-template.cpp
@@ -1,18 +1,17 @@
// This file contains references to sections of the Coroutines TS, which can be
// found at http://wg21.link/coroutines.
-// RUN: %clang_cc1 -std=c++14 -fcoroutines-ts -verify %s -fcxx-exceptions -fexceptions -Wunused-result
+// RUN: %clang_cc1 -std=c++20 -verify %s -fcxx-exceptions -fexceptions -Wunused-result
namespace std {
-namespace experimental {
template<typename ...T>
struct coroutine_traits {
struct promise_type {};
};
-template<> struct coroutine_traits<void>; // expected-note {{forward declaration of 'std::experimental::coroutine_traits<void>'}}
-}} // namespace std::experimental
+template <> struct coroutine_traits<void>; // expected-note {{forward declaration of 'std::coroutine_traits<void>'}}
+} // namespace std
void uses_forward_declaration() {
co_return; // expected-error {{this function cannot be a coroutine: missing definition of specialization 'coroutine_traits<void>'}}
diff --git a/clang/test/SemaCXX/coroutine-unhandled_exception-warning-exp-namespace.cpp b/clang/test/SemaCXX/coroutine-unhandled_exception-warning-exp-namespace.cpp
new file mode 100644
index 000000000000..5394ae78d098
--- /dev/null
+++ b/clang/test/SemaCXX/coroutine-unhandled_exception-warning-exp-namespace.cpp
@@ -0,0 +1,41 @@
+// 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 -Wno-unreachable-code -Wno-unused-value
+
+// 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 -Wno-unreachable-code -Wno-unused-value \
+// RUN: -DDISABLE_WARNING -Wno-coroutine-missing-unhandled-exception
+
+#if __has_feature(cxx_exceptions)
+#error This test requires exceptions be disabled
+#endif
+
+#include "Inputs/std-coroutine-exp-namespace.h"
+
+using std::experimental::suspend_always;
+using std::experimental::suspend_never;
+
+#ifndef DISABLE_WARNING
+struct promise_void { // expected-note {{defined here}}
+#else
+struct promise_void {
+#endif
+ void get_return_object();
+ suspend_always initial_suspend();
+ suspend_always final_suspend() noexcept;
+ void return_void();
+};
+
+template <typename... T>
+struct std::experimental::coroutine_traits<void, T...> { using promise_type = promise_void; };
+
+#ifndef DISABLE_WARNING
+void test0() { // expected-warning {{'promise_void' is required to declare the member 'unhandled_exception()' when exceptions are enabled}}
+ co_return;
+}
+#else
+void test0() { // expected-no-diagnostics
+ co_return;
+}
+#endif
diff --git a/clang/test/SemaCXX/coroutine-unhandled_exception-warning.cpp b/clang/test/SemaCXX/coroutine-unhandled_exception-warning.cpp
index 88fae2e8acb2..5ea1e5d67244 100644
--- a/clang/test/SemaCXX/coroutine-unhandled_exception-warning.cpp
+++ b/clang/test/SemaCXX/coroutine-unhandled_exception-warning.cpp
@@ -1,8 +1,8 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin9 %s -std=c++14 -fcoroutines-ts \
+// RUN: %clang_cc1 -triple x86_64-apple-darwin9 %s -std=c++20 \
// RUN: -fsyntax-only -Wignored-qualifiers -Wno-error=return-type -verify \
// RUN: -fblocks -Wno-unreachable-code -Wno-unused-value
-// RUN: %clang_cc1 -triple x86_64-apple-darwin9 %s -std=c++14 -fcoroutines-ts \
+// RUN: %clang_cc1 -triple x86_64-apple-darwin9 %s -std=c++20 \
// RUN: -fsyntax-only -Wignored-qualifiers -Wno-error=return-type -verify \
// RUN: -fblocks -Wno-unreachable-code -Wno-unused-value \
// RUN: -DDISABLE_WARNING -Wno-coroutine-missing-unhandled-exception
@@ -13,8 +13,8 @@
#include "Inputs/std-coroutine.h"
-using std::experimental::suspend_always;
-using std::experimental::suspend_never;
+using std::suspend_always;
+using std::suspend_never;
#ifndef DISABLE_WARNING
struct promise_void { // expected-note {{defined here}}
@@ -28,7 +28,7 @@ struct promise_void {
};
template <typename... T>
-struct std::experimental::coroutine_traits<void, T...> { using promise_type = promise_void; };
+struct std::coroutine_traits<void, T...> { using promise_type = promise_void; };
#ifndef DISABLE_WARNING
void test0() { // expected-warning {{'promise_void' is required to declare the member 'unhandled_exception()' when exceptions are enabled}}
diff --git a/clang/test/SemaCXX/coroutine-uninitialized-warning-crash-exp-namespace.cpp b/clang/test/SemaCXX/coroutine-uninitialized-warning-crash-exp-namespace.cpp
new file mode 100644
index 000000000000..3374084edda9
--- /dev/null
+++ b/clang/test/SemaCXX/coroutine-uninitialized-warning-crash-exp-namespace.cpp
@@ -0,0 +1,42 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin9 %s -std=c++14 -fcoroutines-ts -fsyntax-only -Wall -Wextra -Wuninitialized -fblocks
+#include "Inputs/std-coroutine-exp-namespace.h"
+
+using namespace std::experimental;
+
+struct A {
+ bool await_ready() { return true; }
+ int await_resume() { return 42; }
+ template <typename F>
+ void await_suspend(F) {}
+};
+
+struct coro_t {
+ struct promise_type {
+ coro_t get_return_object() { return {}; }
+ suspend_never initial_suspend() { return {}; }
+ suspend_never final_suspend() noexcept { return {}; }
+ A yield_value(int) { return {}; }
+ void return_void() {}
+ static void unhandled_exception() {}
+ };
+};
+
+coro_t f(int n) {
+ if (n == 0)
+ co_return;
+ co_yield 42;
+ int x = co_await A{};
+}
+
+template <class Await>
+coro_t g(int n) {
+ if (n == 0)
+ co_return;
+ co_yield 42;
+ int x = co_await Await{};
+}
+
+int main() {
+ f(0);
+ g<A>(0);
+}
diff --git a/clang/test/SemaCXX/coroutine-uninitialized-warning-crash.cpp b/clang/test/SemaCXX/coroutine-uninitialized-warning-crash.cpp
index 391f64d37e5b..8b471206d09f 100644
--- a/clang/test/SemaCXX/coroutine-uninitialized-warning-crash.cpp
+++ b/clang/test/SemaCXX/coroutine-uninitialized-warning-crash.cpp
@@ -1,8 +1,7 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin9 %s -std=c++14 -fcoroutines-ts -fsyntax-only -Wall -Wextra -Wuninitialized -fblocks
+// RUN: %clang_cc1 -triple x86_64-apple-darwin9 %s -std=c++20 -fsyntax-only -Wall -Wextra -Wuninitialized -fblocks
#include "Inputs/std-coroutine.h"
-using namespace std::experimental;
-
+using namespace std;
struct A {
bool await_ready() { return true; }
diff --git a/clang/test/SemaCXX/coroutine_handle-addres-return-type.cpp b/clang/test/SemaCXX/coroutine_handle-address-return-type-exp-namespace.cpp
similarity index 100%
rename from clang/test/SemaCXX/coroutine_handle-addres-return-type.cpp
rename to clang/test/SemaCXX/coroutine_handle-address-return-type-exp-namespace.cpp
diff --git a/clang/test/SemaCXX/coroutine_handle-address-return-type.cpp b/clang/test/SemaCXX/coroutine_handle-address-return-type.cpp
new file mode 100644
index 000000000000..884ff3680e1a
--- /dev/null
+++ b/clang/test/SemaCXX/coroutine_handle-address-return-type.cpp
@@ -0,0 +1,75 @@
+// RUN: %clang_cc1 -verify %s -stdlib=libc++ -std=c++20 -fsyntax-only
+
+namespace std {
+template <class Promise = void>
+struct coroutine_handle;
+
+template <>
+struct coroutine_handle<void> {
+ coroutine_handle() = default;
+ static coroutine_handle from_address(void *) noexcept;
+ void *address() const;
+};
+
+template <class Promise>
+struct coroutine_handle : public coroutine_handle<> {
+};
+
+template <class... Args>
+struct void_t_imp {
+ using type = void;
+};
+template <class... Args>
+using void_t = typename void_t_imp<Args...>::type;
+
+template <class T, class = void>
+struct traits_sfinae_base {};
+
+template <class T>
+struct traits_sfinae_base<T, void_t<typename T::promise_type>> {
+ using promise_type = typename T::promise_type;
+};
+
+template <class Ret, class... Args>
+struct coroutine_traits : public traits_sfinae_base<Ret> {};
+} // namespace std
+
+struct suspend_never {
+ bool await_ready() noexcept;
+ void await_suspend(std::coroutine_handle<>) noexcept;
+ void await_resume() noexcept;
+};
+
+struct task {
+ struct promise_type {
+ auto initial_suspend() { return suspend_never{}; }
+ auto final_suspend() noexcept { return suspend_never{}; }
+ auto get_return_object() { return task{}; }
+ static void unhandled_exception() {}
+ void return_void() {}
+ };
+};
+
+namespace std {
+template <>
+struct coroutine_handle<task::promise_type> : public coroutine_handle<> {
+ coroutine_handle<task::promise_type> *address() const; // expected-warning {{return type of 'coroutine_handle<>::address should be 'void*'}}
+};
+} // namespace std
+
+struct awaitable {
+ bool await_ready();
+
+ std::coroutine_handle<task::promise_type>
+ await_suspend(std::coroutine_handle<> handle);
+ void await_resume();
+} a;
+
+task f() {
+ co_await a;
+}
+
+int main() {
+ f();
+ return 0;
+}
diff --git a/clang/test/SemaCXX/coroutines-exp-namespace.cpp b/clang/test/SemaCXX/coroutines-exp-namespace.cpp
new file mode 100644
index 000000000000..4ea0c66c5b34
--- /dev/null
+++ b/clang/test/SemaCXX/coroutines-exp-namespace.cpp
@@ -0,0 +1,1450 @@
+// This file is the same as coroutines.cpp, except the components are defined in namespace std::experimental.
+// The intent of this test is to make sure the std::experimental implementation still works.
+// TODO: Remove this test once we drop support for <experimental/coroutine>.
+
+// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected,cxx20_2b,cxx2b %s -fcxx-exceptions -fexceptions -Wunused-result
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx14_20,cxx20_2b %s -fcxx-exceptions -fexceptions -Wunused-result
+// RUN: %clang_cc1 -std=c++14 -fcoroutines-ts -fsyntax-only -verify=expected,cxx14_20 %s -fcxx-exceptions -fexceptions -Wunused-result
+
+void no_coroutine_traits_bad_arg_await() {
+ co_await a; // expected-error {{include <experimental/coroutine>}}
+ // expected-error at -1 {{use of undeclared identifier 'a'}}
+}
+
+void no_coroutine_traits_bad_arg_yield() {
+ co_yield a; // expected-error {{include <experimental/coroutine>}}
+ // expected-error at -1 {{use of undeclared identifier 'a'}}
+}
+
+void no_coroutine_traits_bad_arg_return() {
+ co_return a; // expected-error {{include <experimental/coroutine>}}
+ // expected-error at -1 {{use of undeclared identifier 'a'}}
+}
+
+void no_coroutine_traits() {
+ co_await 4; // expected-error {{std::coroutine_traits type was not found; include <coroutine> before defining a coroutine; include <experimental/coroutine> if your version of libcxx is less than 14.0}}
+}
+
+namespace std {
+namespace experimental {
+
+template <class... Args>
+struct void_t_imp {
+ using type = void;
+};
+template <class... Args>
+using void_t = typename void_t_imp<Args...>::type;
+
+template <class T, class = void>
+struct traits_sfinae_base {};
+
+template <class T>
+struct traits_sfinae_base<T, void_t<typename T::promise_type>> {
+ using promise_type = typename T::promise_type;
+};
+
+template <class Ret, class... Args>
+struct coroutine_traits : public traits_sfinae_base<Ret> {};
+} // namespace experimental
+} // namespace std
+
+template <typename Promise> struct coro {};
+template <typename Promise, typename... Ps>
+struct std::experimental::coroutine_traits<coro<Promise>, Ps...> {
+ using promise_type = Promise;
+};
+
+struct awaitable {
+ bool await_ready() noexcept;
+ template <typename F>
+ void await_suspend(F) noexcept;
+ void await_resume() noexcept;
+} a;
+
+struct suspend_always {
+ bool await_ready() noexcept { return false; }
+ template <typename F>
+ void await_suspend(F) noexcept;
+ void await_resume() noexcept {}
+};
+
+struct suspend_never {
+ bool await_ready() noexcept { return true; }
+ template <typename F>
+ void await_suspend(F) noexcept;
+ void await_resume() noexcept {}
+};
+
+struct auto_await_suspend {
+ bool await_ready();
+ template <typename F> auto await_suspend(F) {}
+ void await_resume();
+};
+
+struct DummyVoidTag {};
+DummyVoidTag no_specialization() { // expected-error {{this function cannot be a coroutine: 'std::experimental::coroutine_traits<DummyVoidTag>' has no member named 'promise_type'}}
+ co_await a;
+}
+
+template <typename... T>
+struct std::experimental::coroutine_traits<int, T...> {};
+
+int no_promise_type() { // expected-error {{this function cannot be a coroutine: 'std::experimental::coroutine_traits<int>' has no member named 'promise_type'}}
+ co_await a;
+}
+
+int no_promise_type_multiple_awaits(int) { // expected-error {{this function cannot be a coroutine: 'std::experimental::coroutine_traits<int, int>' has no member named 'promise_type'}}
+ co_await a;
+ co_await a;
+}
+
+template <>
+struct std::experimental::coroutine_traits<double, double> { typedef int promise_type; };
+double bad_promise_type(double) { // expected-error {{this function cannot be a coroutine: 'experimental::coroutine_traits<double, double>::promise_type' (aka 'int') is not a class}}
+ co_await a;
+}
+
+template <>
+struct std::experimental::coroutine_traits<double, int> {
+ struct promise_type {};
+};
+double bad_promise_type_2(int) { // expected-error {{no member named 'initial_suspend'}}
+ co_yield 0; // expected-error {{no member named 'yield_value' in 'std::experimental::coroutine_traits<double, int>::promise_type'}}
+}
+
+struct promise; // expected-note {{forward declaration}}
+struct promise_void;
+struct void_tag {};
+template <typename... T>
+struct std::experimental::coroutine_traits<void, T...> { using promise_type = promise; };
+template <typename... T>
+struct std::experimental::coroutine_traits<void, void_tag, T...> { using promise_type = promise_void; };
+
+// FIXME: This diagnostic is terrible.
+void undefined_promise() { // expected-error {{this function cannot be a coroutine: 'experimental::coroutine_traits<void>::promise_type' (aka 'promise') is an incomplete type}}
+ co_await a;
+}
+
+struct yielded_thing {
+ const char *p;
+ short a, b;
+};
+
+struct not_awaitable {};
+
+struct promise {
+ void get_return_object();
+ suspend_always initial_suspend();
+ suspend_always final_suspend() noexcept;
+ awaitable yield_value(int); // expected-note 2{{candidate}}
+ awaitable yield_value(yielded_thing); // expected-note 2{{candidate}}
+ not_awaitable yield_value(void()); // expected-note 2{{candidate}}
+ void return_value(int); // expected-note 2{{here}}
+ void unhandled_exception();
+};
+
+struct promise_void {
+ void get_return_object();
+ suspend_always initial_suspend();
+ suspend_always final_suspend() noexcept;
+ void return_void();
+ void unhandled_exception();
+};
+
+void no_coroutine_handle() { // expected-error {{std::coroutine_handle type was not found; include <coroutine> before defining a coroutine; include <experimental/coroutine> if your version of libcxx is less than 14.0}}
+ //expected-note at -1 {{call to 'initial_suspend' implicitly required by the initial suspend point}}
+ co_return 5; //expected-note {{function is a coroutine due to use of 'co_return' here}}
+}
+
+namespace std {
+namespace experimental {
+template <class PromiseType = void>
+struct coroutine_handle {
+ static coroutine_handle from_address(void *) noexcept;
+};
+template <>
+struct coroutine_handle<void> {
+ template <class PromiseType>
+ coroutine_handle(coroutine_handle<PromiseType>) noexcept;
+ static coroutine_handle from_address(void *) noexcept;
+};
+} // namespace experimental
+} // namespace std
+
+void yield() {
+ co_yield 0;
+ co_yield {"foo", 1, 2};
+ co_yield {1e100}; // expected-error {{cannot be narrowed}} expected-note {{explicit cast}} expected-warning {{implicit conversion}} expected-warning {{braces around scalar}}
+ co_yield {"foo", __LONG_LONG_MAX__}; // expected-error {{cannot be narrowed}} expected-note {{explicit cast}} expected-warning {{changes value}}
+ co_yield {"foo"};
+ co_yield "foo"; // expected-error {{no matching}}
+ co_yield 1.0;
+ co_yield yield; // expected-error {{no member named 'await_ready' in 'not_awaitable'}}
+}
+
+void check_auto_await_suspend() {
+ co_await auto_await_suspend{}; // Should compile successfully.
+}
+
+void coreturn(int n) {
+ co_await a;
+ if (n == 0)
+ co_return 3;
+ if (n == 1)
+ co_return {4}; // expected-warning {{braces around scalar initializer}}
+ if (n == 2)
+ co_return "foo"; // expected-error {{cannot initialize a parameter of type 'int' with an lvalue of type 'const char[4]'}}
+ co_return 42;
+}
+
+template <class T>
+void co_await_non_dependent_arg(T) {
+ co_await a;
+}
+template void co_await_non_dependent_arg(int);
+
+void mixed_yield() {
+ co_yield 0; // expected-note {{use of 'co_yield'}}
+ return; // expected-error {{not allowed in coroutine}}
+}
+
+void mixed_yield_invalid() {
+ co_yield blah; // expected-error {{use of undeclared identifier}}
+ // expected-note at -1 {{function is a coroutine due to use of 'co_yield'}}
+ return; // expected-error {{return statement not allowed in coroutine}}
+}
+
+template <class T>
+void mixed_yield_template(T) {
+ co_yield blah; // expected-error {{use of undeclared identifier}}
+ // expected-note at -1 {{function is a coroutine due to use of 'co_yield'}}
+ return; // expected-error {{return statement not allowed in coroutine}}
+}
+
+template <class T>
+void mixed_yield_template2(T) {
+ co_yield 42;
+ // expected-note at -1 {{function is a coroutine due to use of 'co_yield'}}
+ return; // expected-error {{return statement not allowed in coroutine}}
+}
+
+template <class T>
+void mixed_yield_template3(T v) {
+ co_yield blah(v);
+ // expected-note at -1 {{function is a coroutine due to use of 'co_yield'}}
+ return; // expected-error {{return statement not allowed in coroutine}}
+}
+
+void mixed_await() {
+ co_await a; // expected-note {{use of 'co_await'}}
+ return; // expected-error {{not allowed in coroutine}}
+}
+
+void mixed_await_invalid() {
+ co_await 42; // expected-error {{'int' is not a structure or union}}
+ // expected-note at -1 {{function is a coroutine due to use of 'co_await'}}
+ return; // expected-error {{not allowed in coroutine}}
+}
+
+template <class T>
+void mixed_await_template(T) {
+ co_await 42;
+ // expected-note at -1 {{function is a coroutine due to use of 'co_await'}}
+ return; // expected-error {{not allowed in coroutine}}
+}
+
+template <class T>
+void mixed_await_template2(T v) {
+ co_await v; // expected-error {{'long' is not a structure or union}}
+ // expected-note at -1 {{function is a coroutine due to use of 'co_await'}}
+ return; // expected-error {{not allowed in coroutine}}
+}
+template void mixed_await_template2(long); // expected-note {{requested here}}
+
+void only_coreturn(void_tag) {
+ co_return; // OK
+}
+
+void mixed_coreturn(void_tag, bool b) {
+ if (b)
+ co_return; // expected-note {{use of 'co_return'}}
+ else
+ return; // expected-error {{not allowed in coroutine}}
+}
+
+void mixed_coreturn_invalid(bool b) {
+ if (b)
+ co_return; // expected-note {{use of 'co_return'}}
+ // expected-error at -1 {{no member named 'return_void' in 'promise'}}
+ else
+ return; // expected-error {{not allowed in coroutine}}
+}
+
+template <class T>
+void mixed_coreturn_template(void_tag, bool b, T v) {
+ if (b)
+ co_return v; // expected-note {{use of 'co_return'}}
+ // expected-error at -1 {{no member named 'return_value' in 'promise_void'}}
+ else
+ return; // expected-error {{not allowed in coroutine}}
+}
+template void mixed_coreturn_template(void_tag, bool, int); // expected-note {{requested here}}
+
+template <class T>
+void mixed_coreturn_template2(bool b, T) {
+ if (b)
+ co_return v; // expected-note {{use of 'co_return'}}
+ // expected-error at -1 {{use of undeclared identifier 'v'}}
+ else
+ return; // expected-error {{not allowed in coroutine}}
+}
+
+struct CtorDtor {
+ CtorDtor() {
+ co_yield 0; // expected-error {{'co_yield' cannot be used in a constructor}}
+ }
+ CtorDtor(awaitable a) {
+ // The spec doesn't say this is ill-formed, but it must be.
+ co_await a; // expected-error {{'co_await' cannot be used in a constructor}}
+ }
+ ~CtorDtor() {
+ co_return 0; // expected-error {{'co_return' cannot be used in a destructor}}
+ }
+ void operator=(CtorDtor &) {
+ co_yield 0; // OK.
+ }
+ void operator=(CtorDtor const &) {
+ co_yield 0; // OK.
+ }
+ void operator=(CtorDtor &&) {
+ co_await a; // OK.
+ }
+ void operator=(CtorDtor const &&) {
+ co_await a; // OK.
+ }
+ void operator=(int) {
+ co_await a; // OK. Not a special member
+ }
+};
+
+namespace std {
+class type_info;
+}
+
+void unevaluated() {
+ decltype(co_await a); // expected-error {{'co_await' cannot be used in an unevaluated context}}
+ // expected-warning at -1 {{declaration does not declare anything}}
+ sizeof(co_await a); // expected-error {{'co_await' cannot be used in an unevaluated context}}
+ // expected-error at -1 {{invalid application of 'sizeof' to an incomplete type 'void'}}
+ // expected-warning at -2 {{expression with side effects has no effect in an unevaluated context}}
+ typeid(co_await a); // expected-error {{'co_await' cannot be used in an unevaluated context}}
+ // expected-warning at -1 {{expression with side effects has no effect in an unevaluated context}}
+ // expected-warning at -2 {{expression result unused}}
+ decltype(co_yield 1); // expected-error {{'co_yield' cannot be used in an unevaluated context}}
+ // expected-warning at -1 {{declaration does not declare anything}}
+ sizeof(co_yield 2); // expected-error {{'co_yield' cannot be used in an unevaluated context}}
+ // expected-error at -1 {{invalid application of 'sizeof' to an incomplete type 'void'}}
+ // expected-warning at -2 {{expression with side effects has no effect in an unevaluated context}}
+ typeid(co_yield 3); // expected-error {{'co_yield' cannot be used in an unevaluated context}}
+ // expected-warning at -1 {{expression with side effects has no effect in an unevaluated context}}
+ // expected-warning at -2 {{expression result unused}}
+}
+
+// [expr.await]p2: "An await-expression shall not appear in a default argument."
+// FIXME: A better diagnostic would explicitly state that default arguments are
+// not allowed. A user may not understand that this is "outside a function."
+void default_argument(int arg = co_await 0) {} // expected-error {{'co_await' cannot be used outside a function}}
+
+void await_in_catch_coroutine() {
+ try {
+ } catch (...) { // FIXME: Emit a note diagnostic pointing out the try handler on this line.
+ []() -> void { co_await a; }(); // OK
+ co_await a; // expected-error {{'co_await' cannot be used in the handler of a try block}}
+ }
+}
+
+void await_nested_in_catch_coroutine() {
+ try {
+ } catch (...) { // FIXME: Emit a note diagnostic pointing out the try handler on this line.
+ try {
+ co_await a; // expected-error {{'co_await' cannot be used in the handler of a try block}}
+ []() -> void { co_await a; }(); // OK
+ } catch (...) {
+ co_return 123;
+ }
+ }
+}
+
+void await_in_lambda_in_catch_coroutine() {
+ try {
+ } catch (...) {
+ []() -> void { co_await a; }(); // OK
+ }
+}
+
+void yield_in_catch_coroutine() {
+ try {
+ } catch (...) {
+ co_yield 1; // expected-error {{'co_yield' cannot be used in the handler of a try block}}
+ }
+}
+
+void return_in_catch_coroutine() {
+ try {
+ } catch (...) {
+ co_return 123; // OK
+ }
+}
+
+constexpr auto constexpr_deduced_return_coroutine() {
+ co_yield 0; // expected-error {{'co_yield' cannot be used in a constexpr function}}
+ // expected-error at -1 {{'co_yield' cannot be used in a function with a deduced return type}}
+}
+
+void varargs_coroutine(const char *, ...) {
+ co_await a; // expected-error {{'co_await' cannot be used in a varargs function}}
+}
+
+auto deduced_return_coroutine() {
+ co_await a; // expected-error {{'co_await' cannot be used in a function with a deduced return type}}
+}
+
+struct outer {};
+struct await_arg_1 {};
+struct await_arg_2 {};
+
+namespace adl_ns {
+struct coawait_arg_type {};
+awaitable operator co_await(coawait_arg_type) noexcept;
+} // namespace adl_ns
+
+namespace dependent_operator_co_await_lookup {
+template <typename T> void await_template(T t) {
+ // no unqualified lookup results
+ co_await t; // expected-error {{no member named 'await_ready' in 'dependent_operator_co_await_lookup::not_awaitable'}}
+ // expected-error at -1 {{call to function 'operator co_await' that is neither visible in the template definition nor found by argument-dependent lookup}}
+};
+template void await_template(awaitable);
+
+struct indirectly_awaitable {
+ indirectly_awaitable(outer);
+};
+awaitable operator co_await(indirectly_awaitable); // expected-note {{should be declared prior to}}
+template void await_template(indirectly_awaitable);
+
+struct not_awaitable {};
+template void await_template(not_awaitable); // expected-note {{instantiation}}
+
+template <typename T> void await_template_2(T t) {
+ // one unqualified lookup result
+ co_await t;
+};
+template void await_template(outer); // expected-note {{instantiation}}
+template void await_template_2(outer);
+
+struct transform_awaitable {};
+struct transformed {};
+
+struct transform_promise {
+ typedef transform_awaitable await_arg;
+ coro<transform_promise> get_return_object();
+ transformed initial_suspend();
+ ::adl_ns::coawait_arg_type final_suspend() noexcept;
+ transformed await_transform(transform_awaitable);
+ void unhandled_exception();
+ void return_void();
+};
+template <class AwaitArg>
+struct basic_promise {
+ typedef AwaitArg await_arg;
+ coro<basic_promise> get_return_object();
+ awaitable initial_suspend();
+ awaitable final_suspend() noexcept;
+ void unhandled_exception();
+ void return_void();
+};
+
+awaitable operator co_await(await_arg_1);
+
+template <typename T, typename U>
+coro<T> await_template_3(U t) {
+ co_await t;
+}
+
+template coro<basic_promise<await_arg_1>> await_template_3<basic_promise<await_arg_1>>(await_arg_1);
+
+template <class T, int I = 0>
+struct dependent_member {
+ coro<T> mem_fn() const {
+ co_await typename T::await_arg{}; // expected-error {{call to function 'operator co_await'}}}
+ }
+ template <class U>
+ coro<T> dep_mem_fn(U t) {
+ co_await t;
+ }
+};
+
+template <>
+struct dependent_member<long> {
+ // FIXME this diagnostic is terrible
+ coro<transform_promise> mem_fn() const { // expected-error {{no member named 'await_ready' in 'dependent_operator_co_await_lookup::transformed'}}
+ // expected-note at -1 {{call to 'initial_suspend' implicitly required by the initial suspend point}}
+ // expected-note at +1 {{function is a coroutine due to use of 'co_await' here}}
+ co_await transform_awaitable{};
+ // expected-error at -1 {{no member named 'await_ready'}}
+ }
+ template <class R, class U>
+ coro<R> dep_mem_fn(U u) { co_await u; }
+};
+
+awaitable operator co_await(await_arg_2); // expected-note {{'operator co_await' should be declared prior to the call site}}
+
+template struct dependent_member<basic_promise<await_arg_1>, 0>;
+template struct dependent_member<basic_promise<await_arg_2>, 0>; // expected-note {{in instantiation}}
+
+template <>
+coro<transform_promise>
+// FIXME this diagnostic is terrible
+dependent_member<long>::dep_mem_fn<transform_promise>(int) { // expected-error {{no member named 'await_ready' in 'dependent_operator_co_await_lookup::transformed'}}
+ //expected-note at -1 {{call to 'initial_suspend' implicitly required by the initial suspend point}}
+ //expected-note at +1 {{function is a coroutine due to use of 'co_await' here}}
+ co_await transform_awaitable{};
+ // expected-error at -1 {{no member named 'await_ready'}}
+}
+
+void operator co_await(transform_awaitable) = delete;
+awaitable operator co_await(transformed);
+
+template coro<transform_promise>
+ dependent_member<long>::dep_mem_fn<transform_promise>(transform_awaitable);
+
+template <>
+coro<transform_promise> dependent_member<long>::dep_mem_fn<transform_promise>(long) {
+ co_await transform_awaitable{};
+}
+
+template <>
+struct dependent_member<int> {
+ coro<transform_promise> mem_fn() const {
+ co_await transform_awaitable{};
+ }
+};
+
+template coro<transform_promise> await_template_3<transform_promise>(transform_awaitable);
+template struct dependent_member<transform_promise>;
+template coro<transform_promise> dependent_member<transform_promise>::dep_mem_fn(transform_awaitable);
+} // namespace dependent_operator_co_await_lookup
+
+struct yield_fn_tag {};
+template <>
+struct std::experimental::coroutine_traits<void, yield_fn_tag> {
+ struct promise_type {
+ // FIXME: add an await_transform overload for functions
+ awaitable yield_value(int());
+ void return_value(int());
+
+ suspend_never initial_suspend();
+ suspend_never final_suspend() noexcept;
+ void get_return_object();
+ void unhandled_exception();
+ };
+};
+
+namespace placeholder {
+awaitable f(), f(int); // expected-note 4{{possible target}}
+int g(), g(int); // expected-note 2{{candidate}}
+void x() {
+ co_await f; // expected-error {{reference to overloaded function}}
+}
+void y() {
+ co_yield g; // expected-error {{no matching member function for call to 'yield_value'}}
+}
+void z() {
+ co_await a;
+ co_return g; // expected-error {{address of overloaded function 'g' does not match required type 'int'}}
+}
+
+void x(yield_fn_tag) {
+ co_await f; // expected-error {{reference to overloaded function}}
+}
+void y(yield_fn_tag) {
+ co_yield g;
+}
+void z(yield_fn_tag) {
+ co_await a;
+ co_return g;
+}
+} // namespace placeholder
+
+struct bad_promise_1 {
+ suspend_always initial_suspend();
+ suspend_always final_suspend() noexcept;
+ void unhandled_exception();
+ void return_void();
+};
+coro<bad_promise_1> missing_get_return_object() { // expected-error {{no member named 'get_return_object' in 'bad_promise_1'}}
+ co_await a;
+}
+
+struct bad_promise_2 {
+ coro<bad_promise_2> get_return_object();
+ suspend_always final_suspend() noexcept;
+ void unhandled_exception();
+ void return_void();
+};
+// FIXME: This shouldn't happen twice
+coro<bad_promise_2> missing_initial_suspend() { // expected-error {{no member named 'initial_suspend' in 'bad_promise_2'}}
+ co_await a;
+}
+
+struct bad_promise_3 {
+ coro<bad_promise_3> get_return_object();
+ suspend_always initial_suspend();
+ void unhandled_exception();
+ void return_void();
+};
+coro<bad_promise_3> missing_final_suspend() noexcept { // expected-error {{no member named 'final_suspend' in 'bad_promise_3'}}
+ co_await a;
+}
+
+struct bad_promise_4 {
+ coro<bad_promise_4> get_return_object();
+ not_awaitable initial_suspend();
+ suspend_always final_suspend() noexcept;
+ void return_void();
+};
+// FIXME: This diagnostic is terrible.
+coro<bad_promise_4> bad_initial_suspend() { // expected-error {{no member named 'await_ready' in 'not_awaitable'}}
+ // expected-note at -1 {{call to 'initial_suspend' implicitly required by the initial suspend point}}
+ co_await a; // expected-note {{function is a coroutine due to use of 'co_await' here}}
+}
+
+struct bad_promise_5 {
+ coro<bad_promise_5> get_return_object();
+ suspend_always initial_suspend();
+ not_awaitable final_suspend() noexcept;
+ void return_void();
+};
+// FIXME: This diagnostic is terrible.
+coro<bad_promise_5> bad_final_suspend() { // expected-error {{no member named 'await_ready' in 'not_awaitable'}}
+ // expected-note at -1 {{call to 'final_suspend' implicitly required by the final suspend point}}
+ co_await a; // expected-note {{function is a coroutine due to use of 'co_await' here}}
+}
+
+struct bad_promise_6 {
+ coro<bad_promise_6> get_return_object();
+ suspend_always initial_suspend();
+ suspend_always final_suspend() noexcept;
+ void unhandled_exception();
+ void return_void(); // expected-note 2 {{member 'return_void' first declared here}}
+ void return_value(int) const; // expected-note 2 {{member 'return_value' first declared here}}
+ void return_value(int);
+};
+coro<bad_promise_6> bad_implicit_return() { // expected-error {{'bad_promise_6' declares both 'return_value' and 'return_void'}}
+ co_await a;
+}
+
+template <class T>
+coro<T> bad_implicit_return_dependent(T) { // expected-error {{'bad_promise_6' declares both 'return_value' and 'return_void'}}
+ co_await a;
+}
+template coro<bad_promise_6> bad_implicit_return_dependent(bad_promise_6); // expected-note {{in instantiation}}
+
+struct bad_promise_7 { // expected-note 2 {{defined here}}
+ coro<bad_promise_7> get_return_object();
+ suspend_always initial_suspend();
+ suspend_always final_suspend() noexcept;
+ void return_void();
+};
+coro<bad_promise_7> no_unhandled_exception() { // expected-error {{'bad_promise_7' is required to declare the member 'unhandled_exception()'}}
+ co_await a;
+}
+
+template <class T>
+coro<T> no_unhandled_exception_dependent(T) { // expected-error {{'bad_promise_7' is required to declare the member 'unhandled_exception()'}}
+ co_await a;
+}
+template coro<bad_promise_7> no_unhandled_exception_dependent(bad_promise_7); // expected-note {{in instantiation}}
+
+struct bad_promise_base {
+private:
+ void return_void(); // expected-note 2 {{declared private here}}
+};
+struct bad_promise_8 : bad_promise_base {
+ coro<bad_promise_8> get_return_object();
+ suspend_always initial_suspend();
+ suspend_always final_suspend() noexcept;
+ void unhandled_exception() __attribute__((unavailable)); // expected-note 2 {{marked unavailable here}}
+ void unhandled_exception() const;
+ void unhandled_exception(void *) const;
+};
+coro<bad_promise_8> calls_unhandled_exception() {
+ // expected-error at -1 {{'unhandled_exception' is unavailable}}
+ // expected-error at -2 {{'return_void' is a private member}}
+ co_await a;
+}
+
+template <class T>
+coro<T> calls_unhandled_exception_dependent(T) {
+ // expected-error at -1 {{'unhandled_exception' is unavailable}}
+ // expected-error at -2 {{'return_void' is a private member}}
+ co_await a;
+}
+template coro<bad_promise_8> calls_unhandled_exception_dependent(bad_promise_8); // expected-note {{in instantiation}}
+
+struct bad_promise_9 {
+ coro<bad_promise_9> get_return_object();
+ suspend_always initial_suspend();
+ suspend_always final_suspend() noexcept;
+ void await_transform(void *);
+ awaitable await_transform(int) __attribute__((unavailable)); // expected-note {{explicitly marked unavailable}}
+ void return_void();
+ void unhandled_exception();
+};
+coro<bad_promise_9> calls_await_transform() {
+ co_await 42; // expected-error {{'await_transform' is unavailable}}
+}
+
+struct bad_promise_10 {
+ coro<bad_promise_10> get_return_object();
+ suspend_always initial_suspend();
+ suspend_always final_suspend() noexcept;
+ int await_transform;
+ void return_void();
+ void unhandled_exception();
+};
+coro<bad_promise_10> bad_coawait() {
+ // FIXME this diagnostic is terrible
+ co_await 42; // expected-error {{called object type 'int' is not a function or function pointer}}
+ // expected-note at -1 {{call to 'await_transform' implicitly required by 'co_await' here}}
+}
+
+struct call_operator {
+ template <class... Args>
+ awaitable operator()(Args...) const { return a; }
+};
+void ret_void();
+struct good_promise_1 {
+ coro<good_promise_1> get_return_object();
+ suspend_always initial_suspend();
+ suspend_always final_suspend() noexcept;
+ void unhandled_exception();
+ static const call_operator await_transform;
+ using Fn = void (*)();
+ Fn return_void = ret_void;
+};
+const call_operator good_promise_1::await_transform;
+coro<good_promise_1> ok_static_coawait() {
+ // FIXME this diagnostic is terrible
+ co_await 42;
+}
+
+template <typename T> void ok_generic_lambda_coawait_PR41909() {
+ [](auto &arg) -> coro<good_promise_1> { // expected-warning {{expression result unused}}
+ co_await 12;
+ };
+ [](auto &arg) -> coro<good_promise_1> {
+ co_await 24;
+ }("argument");
+ [](auto &arg) -> coro<good_promise_1> { // expected-warning {{expression result unused}}
+ []() -> coro<good_promise_1> {
+ co_await 36;
+ };
+ co_await 48;
+ };
+}
+template void ok_generic_lambda_coawait_PR41909<int>(); // expected-note {{in instantiation of function template specialization 'ok_generic_lambda_coawait_PR41909<int>' requested here}}
+
+template <> struct std::experimental::coroutine_traits<int, int, const char **> { using promise_type = promise; };
+
+int main(int, const char **) {
+ co_await a; // expected-error {{'co_await' cannot be used in the 'main' function}}
+}
+
+struct good_promise_2 {
+ float get_return_object();
+ suspend_always initial_suspend();
+ suspend_always final_suspend() noexcept;
+ void return_void();
+ void unhandled_exception();
+};
+template <> struct std::experimental::coroutine_handle<good_promise_2> {};
+
+template <> struct std::experimental::coroutine_traits<float> { using promise_type = good_promise_2; };
+
+float badly_specialized_coro_handle() { // expected-error {{std::coroutine_handle must have a member named 'from_address'}}
+ //expected-note at -1 {{call to 'initial_suspend' implicitly required by the initial suspend point}}
+ co_return; //expected-note {{function is a coroutine due to use of 'co_return' here}}
+}
+
+namespace std {
+struct nothrow_t {};
+constexpr nothrow_t nothrow = {};
+} // namespace std
+
+using SizeT = decltype(sizeof(int));
+
+void *operator new(SizeT __sz, const std::nothrow_t &) noexcept;
+void operator delete(void *__p, const std::nothrow_t &)noexcept;
+
+struct promise_on_alloc_failure_tag {};
+
+template <>
+struct std::experimental::coroutine_traits<int, promise_on_alloc_failure_tag> {
+ struct promise_type {
+ int get_return_object() {}
+ suspend_always initial_suspend() { return {}; }
+ suspend_always final_suspend() noexcept { return {}; }
+ void return_void() {}
+ int get_return_object_on_allocation_failure(); // expected-error{{'promise_type': 'get_return_object_on_allocation_failure()' must be a static member function}}
+ void unhandled_exception();
+ };
+};
+
+extern "C" int f(promise_on_alloc_failure_tag) {
+ co_return; //expected-note {{function is a coroutine due to use of 'co_return' here}}
+}
+
+struct bad_promise_11 {
+ coro<bad_promise_11> get_return_object();
+ suspend_always initial_suspend();
+ suspend_always final_suspend() noexcept;
+ void unhandled_exception();
+ void return_void();
+
+private:
+ static coro<bad_promise_11> get_return_object_on_allocation_failure(); // expected-note 2 {{declared private here}}
+};
+coro<bad_promise_11> private_alloc_failure_handler() {
+ // expected-error at -1 {{'get_return_object_on_allocation_failure' is a private member of 'bad_promise_11'}}
+ co_return; // FIXME: Add a "declared coroutine here" note.
+}
+
+template <class T>
+coro<T> dependent_private_alloc_failure_handler(T) {
+ // expected-error at -1 {{'get_return_object_on_allocation_failure' is a private member of 'bad_promise_11'}}
+ co_return; // FIXME: Add a "declared coroutine here" note.
+}
+template coro<bad_promise_11> dependent_private_alloc_failure_handler(bad_promise_11);
+// expected-note at -1 {{requested here}}
+
+struct bad_promise_12 {
+ coro<bad_promise_12> get_return_object();
+ suspend_always initial_suspend();
+ suspend_always final_suspend() noexcept;
+ void unhandled_exception();
+ void return_void();
+ static coro<bad_promise_12> get_return_object_on_allocation_failure();
+
+ static void *operator new(SizeT);
+ // expected-error at -1 2 {{'operator new' is required to have a non-throwing noexcept specification when the promise type declares 'get_return_object_on_allocation_failure()'}}
+};
+coro<bad_promise_12> throwing_in_class_new() { // expected-note {{call to 'operator new' implicitly required by coroutine function here}}
+ co_return;
+}
+
+template <class T>
+coro<T> dependent_throwing_in_class_new(T) { // expected-note {{call to 'operator new' implicitly required by coroutine function here}}
+ co_return;
+}
+template coro<bad_promise_12> dependent_throwing_in_class_new(bad_promise_12); // expected-note {{requested here}}
+
+struct good_promise_13 {
+ coro<good_promise_13> get_return_object();
+ suspend_always initial_suspend();
+ suspend_always final_suspend() noexcept;
+ void unhandled_exception();
+ void return_void();
+ static coro<good_promise_13> get_return_object_on_allocation_failure();
+};
+coro<good_promise_13> uses_nothrow_new() {
+ co_return;
+}
+
+template <class T>
+coro<T> dependent_uses_nothrow_new(T) {
+ co_return;
+}
+template coro<good_promise_13> dependent_uses_nothrow_new(good_promise_13);
+
+struct good_promise_custom_new_operator {
+ coro<good_promise_custom_new_operator> get_return_object();
+ suspend_always initial_suspend();
+ suspend_always final_suspend() noexcept;
+ void return_void();
+ void unhandled_exception();
+ void *operator new(SizeT, double, float, int);
+};
+
+coro<good_promise_custom_new_operator>
+good_coroutine_calls_custom_new_operator(double, float, int) {
+ co_return;
+}
+
+struct coroutine_nonstatic_member_struct;
+
+struct good_promise_nonstatic_member_custom_new_operator {
+ coro<good_promise_nonstatic_member_custom_new_operator> get_return_object();
+ suspend_always initial_suspend();
+ suspend_always final_suspend() noexcept;
+ void return_void();
+ void unhandled_exception();
+ void *operator new(SizeT, coroutine_nonstatic_member_struct &, double);
+};
+
+struct good_promise_noexcept_custom_new_operator {
+ static coro<good_promise_noexcept_custom_new_operator> get_return_object_on_allocation_failure();
+ coro<good_promise_noexcept_custom_new_operator> get_return_object();
+ suspend_always initial_suspend();
+ suspend_always final_suspend() noexcept;
+ void return_void();
+ void unhandled_exception();
+ void *operator new(SizeT, double, float, int) noexcept;
+};
+
+coro<good_promise_noexcept_custom_new_operator>
+good_coroutine_calls_noexcept_custom_new_operator(double, float, int) {
+ co_return;
+}
+
+struct mismatch_gro_type_tag1 {};
+template <>
+struct std::experimental::coroutine_traits<int, mismatch_gro_type_tag1> {
+ struct promise_type {
+ void get_return_object() {} //expected-note {{member 'get_return_object' declared here}}
+ suspend_always initial_suspend() { return {}; }
+ suspend_always final_suspend() noexcept { return {}; }
+ void return_void() {}
+ void unhandled_exception();
+ };
+};
+
+extern "C" int f(mismatch_gro_type_tag1) {
+ // expected-error at -1 {{cannot initialize return object of type 'int' with an rvalue of type 'void'}}
+ co_return; //expected-note {{function is a coroutine due to use of 'co_return' here}}
+}
+
+struct mismatch_gro_type_tag2 {};
+template <>
+struct std::experimental::coroutine_traits<int, mismatch_gro_type_tag2> {
+ struct promise_type {
+ void *get_return_object() {} //expected-note {{member 'get_return_object' declared here}}
+ suspend_always initial_suspend() { return {}; }
+ suspend_always final_suspend() noexcept { return {}; }
+ void return_void() {}
+ void unhandled_exception();
+ };
+};
+
+extern "C" int f(mismatch_gro_type_tag2) {
+ // cxx2b-error at -1 {{cannot initialize return object of type 'int' with an rvalue of type 'void *'}}
+ // cxx14_20-error at -2 {{cannot initialize return object of type 'int' with an lvalue of type 'void *'}}
+ co_return; //expected-note {{function is a coroutine due to use of 'co_return' here}}
+}
+
+struct mismatch_gro_type_tag3 {};
+template <>
+struct std::experimental::coroutine_traits<int, mismatch_gro_type_tag3> {
+ struct promise_type {
+ int get_return_object() {}
+ static void get_return_object_on_allocation_failure() {} //expected-note {{member 'get_return_object_on_allocation_failure' declared here}}
+ suspend_always initial_suspend() { return {}; }
+ suspend_always final_suspend() noexcept { return {}; }
+ void return_void() {}
+ void unhandled_exception();
+ };
+};
+
+extern "C" int f(mismatch_gro_type_tag3) {
+ // expected-error at -1 {{cannot initialize return object of type 'int' with an rvalue of type 'void'}}
+ co_return; //expected-note {{function is a coroutine due to use of 'co_return' here}}
+}
+
+struct mismatch_gro_type_tag4 {};
+template <>
+struct std::experimental::coroutine_traits<int, mismatch_gro_type_tag4> {
+ struct promise_type {
+ int get_return_object() {}
+ static char *get_return_object_on_allocation_failure() {} //expected-note {{member 'get_return_object_on_allocation_failure' declared}}
+ suspend_always initial_suspend() { return {}; }
+ suspend_always final_suspend() noexcept { return {}; }
+ void return_void() {}
+ void unhandled_exception();
+ };
+};
+
+extern "C" int f(mismatch_gro_type_tag4) {
+ // expected-error at -1 {{cannot initialize return object of type 'int' with an rvalue of type 'char *'}}
+ co_return; //expected-note {{function is a coroutine due to use of 'co_return' here}}
+}
+
+struct bad_promise_no_return_func { // expected-note {{'bad_promise_no_return_func' defined here}}
+ coro<bad_promise_no_return_func> get_return_object();
+ suspend_always initial_suspend();
+ suspend_always final_suspend() noexcept;
+ void unhandled_exception();
+};
+// FIXME: The PDTS currently specifies this as UB, technically forbidding a
+// diagnostic.
+coro<bad_promise_no_return_func> no_return_value_or_return_void() {
+ // expected-error at -1 {{'bad_promise_no_return_func' must declare either 'return_value' or 'return_void'}}
+ co_await a;
+}
+
+struct bad_await_suspend_return {
+ bool await_ready();
+ // expected-error at +1 {{return type of 'await_suspend' is required to be 'void' or 'bool' (have 'char')}}
+ char await_suspend(std::experimental::coroutine_handle<>);
+ void await_resume();
+};
+struct bad_await_ready_return {
+ // expected-note at +1 {{return type of 'await_ready' is required to be contextually convertible to 'bool'}}
+ void await_ready();
+ bool await_suspend(std::experimental::coroutine_handle<>);
+ void await_resume();
+};
+struct await_ready_explicit_bool {
+ struct BoolT {
+ explicit operator bool() const;
+ };
+ BoolT await_ready();
+ void await_suspend(std::experimental::coroutine_handle<>);
+ void await_resume();
+};
+template <class SuspendTy>
+struct await_suspend_type_test {
+ bool await_ready();
+ // expected-error at +2 {{return type of 'await_suspend' is required to be 'void' or 'bool' (have 'bool &')}}
+ // expected-error at +1 {{return type of 'await_suspend' is required to be 'void' or 'bool' (have 'bool &&')}}
+ SuspendTy await_suspend(std::experimental::coroutine_handle<>);
+ // cxx20_2b-warning at -1 {{volatile-qualified return type 'const volatile bool' is deprecated}}
+ void await_resume();
+};
+void test_bad_suspend() {
+ {
+ // FIXME: The actual error emitted here is terrible, and no number of notes can save it.
+ bad_await_ready_return a;
+ // expected-error at +1 {{value of type 'void' is not contextually convertible to 'bool'}}
+ co_await a; // expected-note {{call to 'await_ready' implicitly required by coroutine function here}}
+ }
+ {
+ bad_await_suspend_return b;
+ co_await b; // expected-note {{call to 'await_suspend' implicitly required by coroutine function here}}
+ }
+ {
+ await_ready_explicit_bool c;
+ co_await c; // OK
+ }
+ {
+ await_suspend_type_test<bool &&> a;
+ await_suspend_type_test<bool &> b;
+ await_suspend_type_test<const void> c;
+ await_suspend_type_test<const volatile bool> d; // cxx20_2b-note {{in instantiation of template class}}
+ co_await a; // expected-note {{call to 'await_suspend' implicitly required by coroutine function here}}
+ co_await b; // expected-note {{call to 'await_suspend' implicitly required by coroutine function here}}
+ co_await c; // OK
+ co_await d; // OK
+ }
+}
+
+template <int ID = 0>
+struct NoCopy {
+ NoCopy(NoCopy const &) = delete; // expected-note 2 {{deleted here}}
+};
+template <class T, class U>
+void test_dependent_param(T t, U) {
+ // expected-error at -1 {{call to deleted constructor of 'NoCopy<0>'}}
+ // expected-error at -2 {{call to deleted constructor of 'NoCopy<1>'}}
+ ((void)t);
+ co_return 42;
+}
+template void test_dependent_param(NoCopy<0>, NoCopy<1>); // expected-note {{requested here}}
+
+namespace CoroHandleMemberFunctionTest {
+struct CoroMemberTag {};
+struct BadCoroMemberTag {};
+
+template <class T, class U>
+constexpr bool IsSameV = false;
+template <class T>
+constexpr bool IsSameV<T, T> = true;
+
+template <class T>
+struct TypeTest {
+ template <class U>
+ static constexpr bool IsSame = IsSameV<T, U>;
+
+ template <class... Args>
+ static constexpr bool MatchesArgs = IsSameV<T,
+ std::experimental::coroutine_traits<CoroMemberTag, Args...>>;
+};
+
+template <class T>
+struct AwaitReturnsType {
+ bool await_ready() const;
+ void await_suspend(...) const;
+ T await_resume() const;
+};
+
+template <class... CoroTraitsArgs>
+struct CoroMemberPromise {
+ using TraitsT = std::experimental::coroutine_traits<CoroTraitsArgs...>;
+ using TypeTestT = TypeTest<TraitsT>;
+ using AwaitTestT = AwaitReturnsType<TypeTestT>;
+
+ CoroMemberTag get_return_object();
+ suspend_always initial_suspend();
+ suspend_always final_suspend() noexcept;
+
+ AwaitTestT yield_value(int);
+
+ void return_void();
+ void unhandled_exception();
+};
+
+} // namespace CoroHandleMemberFunctionTest
+
+template <class... Args>
+struct ::std::experimental::coroutine_traits<CoroHandleMemberFunctionTest::CoroMemberTag, Args...> {
+ using promise_type = CoroHandleMemberFunctionTest::CoroMemberPromise<CoroHandleMemberFunctionTest::CoroMemberTag, Args...>;
+};
+
+namespace CoroHandleMemberFunctionTest {
+struct TestType {
+
+ CoroMemberTag test_qual() {
+ auto TC = co_yield 0;
+ static_assert(TC.MatchesArgs<TestType &>, "");
+ static_assert(!TC.MatchesArgs<TestType>, "");
+ static_assert(!TC.MatchesArgs<TestType *>, "");
+ }
+
+ CoroMemberTag test_sanity(int *) const {
+ auto TC = co_yield 0;
+ static_assert(TC.MatchesArgs<const TestType &>, ""); // expected-error {{static_assert failed}}
+ static_assert(TC.MatchesArgs<const TestType &>, ""); // expected-error {{static_assert failed}}
+ static_assert(TC.MatchesArgs<const TestType &, int *>, "");
+ }
+
+ CoroMemberTag test_qual(int *, const float &&, volatile void *volatile) const {
+ // cxx20_2b-warning at -1 {{volatile-qualified parameter type}}
+ auto TC = co_yield 0;
+ static_assert(TC.MatchesArgs<const TestType &, int *, const float &&, volatile void *volatile>, "");
+ }
+
+ CoroMemberTag test_qual() const volatile {
+ auto TC = co_yield 0;
+ static_assert(TC.MatchesArgs<const volatile TestType &>, "");
+ }
+
+ CoroMemberTag test_ref_qual() & {
+ auto TC = co_yield 0;
+ static_assert(TC.MatchesArgs<TestType &>, "");
+ }
+ CoroMemberTag test_ref_qual() const & {
+ auto TC = co_yield 0;
+ static_assert(TC.MatchesArgs<TestType const &>, "");
+ }
+ CoroMemberTag test_ref_qual() && {
+ auto TC = co_yield 0;
+ static_assert(TC.MatchesArgs<TestType &&>, "");
+ }
+ CoroMemberTag test_ref_qual(const char *&) const volatile && {
+ auto TC = co_yield 0;
+ static_assert(TC.MatchesArgs<TestType const volatile &&, const char *&>, "");
+ }
+
+ CoroMemberTag test_args(int) {
+ auto TC = co_yield 0;
+ static_assert(TC.MatchesArgs<TestType &, int>, "");
+ }
+ CoroMemberTag test_args(int, long &, void *) const {
+ auto TC = co_yield 0;
+ static_assert(TC.MatchesArgs<TestType const &, int, long &, void *>, "");
+ }
+
+ template <class... Args>
+ CoroMemberTag test_member_template(Args...) const && {
+ auto TC = co_yield 0;
+ static_assert(TC.template MatchesArgs<TestType const &&, Args...>, "");
+ }
+
+ static CoroMemberTag test_static() {
+ auto TC = co_yield 0;
+ static_assert(TC.MatchesArgs<>, "");
+ static_assert(!TC.MatchesArgs<TestType>, "");
+ static_assert(!TC.MatchesArgs<TestType &>, "");
+ static_assert(!TC.MatchesArgs<TestType *>, "");
+ }
+
+ static CoroMemberTag test_static(volatile void *const, char &&) {
+ auto TC = co_yield 0;
+ static_assert(TC.MatchesArgs<volatile void *const, char &&>, "");
+ }
+
+ template <class Dummy>
+ static CoroMemberTag test_static_template(const char *volatile &, unsigned) {
+ auto TC = co_yield 0;
+ using TCT = decltype(TC);
+ static_assert(TCT::MatchesArgs<const char *volatile &, unsigned>, "");
+ static_assert(!TCT::MatchesArgs<TestType &, const char *volatile &, unsigned>, "");
+ }
+
+ BadCoroMemberTag test_diagnostics() {
+ // expected-error at -1 {{this function cannot be a coroutine: 'std::experimental::coroutine_traits<CoroHandleMemberFunctionTest::BadCoroMemberTag, CoroHandleMemberFunctionTest::TestType &>' has no member named 'promise_type'}}
+ co_return;
+ }
+ BadCoroMemberTag test_diagnostics(int) const && {
+ // expected-error at -1 {{this function cannot be a coroutine: 'std::experimental::coroutine_traits<CoroHandleMemberFunctionTest::BadCoroMemberTag, const CoroHandleMemberFunctionTest::TestType &&, int>' has no member named 'promise_type'}}
+ co_return;
+ }
+
+ static BadCoroMemberTag test_static_diagnostics(long *) {
+ // expected-error at -1 {{this function cannot be a coroutine: 'std::experimental::coroutine_traits<CoroHandleMemberFunctionTest::BadCoroMemberTag, long *>' has no member named 'promise_type'}}
+ co_return;
+ }
+};
+
+template CoroMemberTag TestType::test_member_template(long, const char *) const &&;
+template CoroMemberTag TestType::test_static_template<void>(const char *volatile &, unsigned);
+
+template <class... Args>
+struct DepTestType {
+
+ CoroMemberTag test_sanity(int *) const {
+ auto TC = co_yield 0;
+ static_assert(TC.template MatchesArgs<const DepTestType &>, ""); // expected-error {{static_assert failed}}
+ static_assert(TC.template MatchesArgs<>, ""); // expected-error {{static_assert failed}}
+ static_assert(TC.template MatchesArgs<const DepTestType &, int *>, "");
+ }
+
+ CoroMemberTag test_qual() {
+ auto TC = co_yield 0;
+ static_assert(TC.template MatchesArgs<DepTestType &>, "");
+ static_assert(!TC.template MatchesArgs<DepTestType>, "");
+ static_assert(!TC.template MatchesArgs<DepTestType *>, "");
+ }
+
+ CoroMemberTag test_qual(int *, const float &&, volatile void *volatile) const {
+ // cxx20_2b-warning at -1 {{volatile-qualified parameter type}}
+ auto TC = co_yield 0;
+ static_assert(TC.template MatchesArgs<const DepTestType &, int *, const float &&, volatile void *volatile>, "");
+ }
+
+ CoroMemberTag test_qual() const volatile {
+ auto TC = co_yield 0;
+ static_assert(TC.template MatchesArgs<const volatile DepTestType &>, "");
+ }
+
+ CoroMemberTag test_ref_qual() & {
+ auto TC = co_yield 0;
+ static_assert(TC.template MatchesArgs<DepTestType &>, "");
+ }
+ CoroMemberTag test_ref_qual() const & {
+ auto TC = co_yield 0;
+ static_assert(TC.template MatchesArgs<DepTestType const &>, "");
+ }
+ CoroMemberTag test_ref_qual() && {
+ auto TC = co_yield 0;
+ static_assert(TC.template MatchesArgs<DepTestType &&>, "");
+ }
+ CoroMemberTag test_ref_qual(const char *&) const volatile && {
+ auto TC = co_yield 0;
+ static_assert(TC.template MatchesArgs<DepTestType const volatile &&, const char *&>, "");
+ }
+
+ CoroMemberTag test_args(int) {
+ auto TC = co_yield 0;
+ static_assert(TC.template MatchesArgs<DepTestType &, int>, "");
+ }
+ CoroMemberTag test_args(int, long &, void *) const {
+ auto TC = co_yield 0;
+ static_assert(TC.template MatchesArgs<DepTestType const &, int, long &, void *>, "");
+ }
+
+ template <class... UArgs>
+ CoroMemberTag test_member_template(UArgs...) const && {
+ auto TC = co_yield 0;
+ static_assert(TC.template MatchesArgs<DepTestType const &&, UArgs...>, "");
+ }
+
+ static CoroMemberTag test_static() {
+ auto TC = co_yield 0;
+ using TCT = decltype(TC);
+ static_assert(TCT::MatchesArgs<>, "");
+ static_assert(!TCT::MatchesArgs<DepTestType>, "");
+ static_assert(!TCT::MatchesArgs<DepTestType &>, "");
+ static_assert(!TCT::MatchesArgs<DepTestType *>, "");
+
+ // Ensure diagnostics are actually being generated here
+ static_assert(TCT::MatchesArgs<int>, ""); // expected-error {{static_assert failed}}
+ }
+
+ static CoroMemberTag test_static(volatile void *const, char &&) {
+ auto TC = co_yield 0;
+ using TCT = decltype(TC);
+ static_assert(TCT::MatchesArgs<volatile void *const, char &&>, "");
+ }
+
+ template <class Dummy>
+ static CoroMemberTag test_static_template(const char *volatile &, unsigned) {
+ auto TC = co_yield 0;
+ using TCT = decltype(TC);
+ static_assert(TCT::MatchesArgs<const char *volatile &, unsigned>, "");
+ static_assert(!TCT::MatchesArgs<DepTestType &, const char *volatile &, unsigned>, "");
+ }
+};
+
+template struct DepTestType<int>; // expected-note {{requested here}}
+template CoroMemberTag DepTestType<int>::test_member_template(long, const char *) const &&;
+
+template CoroMemberTag DepTestType<int>::test_static_template<void>(const char *volatile &, unsigned);
+
+struct bad_promise_deleted_constructor {
+ // expected-note at +1 {{'bad_promise_deleted_constructor' has been explicitly marked deleted here}}
+ bad_promise_deleted_constructor() = delete;
+ coro<bad_promise_deleted_constructor> get_return_object();
+ suspend_always initial_suspend();
+ suspend_always final_suspend() noexcept;
+ void return_void();
+ void unhandled_exception();
+};
+
+coro<bad_promise_deleted_constructor>
+bad_coroutine_calls_deleted_promise_constructor() {
+ // expected-error at -1 {{call to deleted constructor of 'std::experimental::coroutine_traits<coro<CoroHandleMemberFunctionTest::bad_promise_deleted_constructor>>::promise_type' (aka 'CoroHandleMemberFunctionTest::bad_promise_deleted_constructor')}}
+ co_return;
+}
+
+// Test that, when the promise type has a constructor whose signature matches
+// that of the coroutine function, that constructor is used. If no matching
+// constructor exists, the default constructor is used as a fallback. If no
+// matching constructors exist at all, an error is emitted. This is an
+// experimental feature that will be proposed for the Coroutines TS.
+
+struct good_promise_default_constructor {
+ good_promise_default_constructor(double, float, int);
+ good_promise_default_constructor() = default;
+ coro<good_promise_default_constructor> get_return_object();
+ suspend_always initial_suspend();
+ suspend_always final_suspend() noexcept;
+ void return_void();
+ void unhandled_exception();
+};
+
+coro<good_promise_default_constructor>
+good_coroutine_calls_default_constructor() {
+ co_return;
+}
+
+struct some_class;
+
+struct good_promise_custom_constructor {
+ good_promise_custom_constructor(some_class &, float, int);
+ good_promise_custom_constructor(double, float, int);
+ good_promise_custom_constructor() = delete;
+ coro<good_promise_custom_constructor> get_return_object();
+ suspend_always initial_suspend();
+ suspend_always final_suspend() noexcept;
+ void return_void();
+ void unhandled_exception();
+};
+
+coro<good_promise_custom_constructor>
+good_coroutine_calls_custom_constructor(double, float, int) {
+ co_return;
+}
+
+struct some_class {
+ coro<good_promise_custom_constructor>
+ good_coroutine_calls_custom_constructor(float, int) {
+ co_return;
+ }
+ coro<good_promise_custom_constructor> static good_coroutine_calls_custom_constructor(double, float, int) {
+ co_return;
+ }
+};
+
+struct bad_promise_no_matching_constructor {
+ bad_promise_no_matching_constructor(int, int, int);
+ // expected-note at +1 2 {{'bad_promise_no_matching_constructor' has been explicitly marked deleted here}}
+ bad_promise_no_matching_constructor() = delete;
+ coro<bad_promise_no_matching_constructor> get_return_object();
+ suspend_always initial_suspend();
+ suspend_always final_suspend() noexcept;
+ void return_void();
+ void unhandled_exception();
+};
+
+coro<bad_promise_no_matching_constructor>
+bad_coroutine_calls_with_no_matching_constructor(int, int) {
+ // expected-error at -1 {{call to deleted constructor of 'std::experimental::coroutine_traits<coro<CoroHandleMemberFunctionTest::bad_promise_no_matching_constructor>, int, int>::promise_type' (aka 'CoroHandleMemberFunctionTest::bad_promise_no_matching_constructor')}}
+ co_return;
+}
+
+struct some_class2 {
+ coro<bad_promise_no_matching_constructor>
+ bad_coroutine_calls_with_no_matching_constructor(int, int, int) {
+ // expected-error at -1 {{call to deleted constructor}}
+ co_return;
+ }
+};
+
+} // namespace CoroHandleMemberFunctionTest
+
+class awaitable_no_unused_warn {
+public:
+ using handle_type = std::experimental::coroutine_handle<>;
+ constexpr bool await_ready() noexcept { return false; }
+ void await_suspend(handle_type) noexcept {}
+ int await_resume() noexcept { return 1; }
+};
+
+class awaitable_unused_warn {
+public:
+ using handle_type = std::experimental::coroutine_handle<>;
+ constexpr bool await_ready() noexcept { return false; }
+ void await_suspend(handle_type) noexcept {}
+ [[nodiscard]] int await_resume() noexcept { return 1; }
+};
+
+template <class Await>
+struct check_warning_promise {
+ coro<check_warning_promise> get_return_object();
+ Await initial_suspend();
+ Await final_suspend() noexcept;
+ Await yield_value(int);
+ void return_void();
+ void unhandled_exception();
+};
+
+coro<check_warning_promise<awaitable_no_unused_warn>>
+test_no_unused_warning() {
+ co_await awaitable_no_unused_warn();
+ co_yield 42;
+}
+
+coro<check_warning_promise<awaitable_unused_warn>>
+test_unused_warning() {
+ co_await awaitable_unused_warn(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+ co_yield 42; // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+}
+
+struct missing_await_ready {
+ void await_suspend(std::experimental::coroutine_handle<>);
+ void await_resume();
+};
+struct missing_await_suspend {
+ bool await_ready();
+ void await_resume();
+};
+struct missing_await_resume {
+ bool await_ready();
+ void await_suspend(std::experimental::coroutine_handle<>);
+};
+
+void test_missing_awaitable_members() {
+ co_await missing_await_ready{}; // expected-error {{no member named 'await_ready' in 'missing_await_ready'}}
+ co_await missing_await_suspend{}; // expected-error {{no member named 'await_suspend' in 'missing_await_suspend'}}
+ co_await missing_await_resume{}; // expected-error {{no member named 'await_resume' in 'missing_await_resume'}}
+}
diff --git a/clang/test/SemaCXX/coroutines.cpp b/clang/test/SemaCXX/coroutines.cpp
index 9222066e8793..26415be80060 100644
--- a/clang/test/SemaCXX/coroutines.cpp
+++ b/clang/test/SemaCXX/coroutines.cpp
@@ -1,32 +1,30 @@
// This file contains references to sections of the Coroutines TS, which can be
// found at http://wg21.link/coroutines.
-// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected,cxx20_2b,cxx2b %s -fcxx-exceptions -fexceptions -Wunused-result
-// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx14_20,cxx20_2b %s -fcxx-exceptions -fexceptions -Wunused-result
-// RUN: %clang_cc1 -std=c++14 -fcoroutines-ts -fsyntax-only -verify=expected,cxx14_20 %s -fcxx-exceptions -fexceptions -Wunused-result
+// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected,cxx20_2b,cxx2b %s -fcxx-exceptions -fexceptions -Wunused-result
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx14_20,cxx20_2b %s -fcxx-exceptions -fexceptions -Wunused-result
void no_coroutine_traits_bad_arg_await() {
- co_await a; // expected-error {{include <experimental/coroutine>}}
+ co_await a; // expected-error {{include <coroutine>}}
// expected-error at -1 {{use of undeclared identifier 'a'}}
}
void no_coroutine_traits_bad_arg_yield() {
- co_yield a; // expected-error {{include <experimental/coroutine>}}
+ co_yield a; // expected-error {{include <coroutine>}}
// expected-error at -1 {{use of undeclared identifier 'a'}}
}
void no_coroutine_traits_bad_arg_return() {
- co_return a; // expected-error {{include <experimental/coroutine>}}
+ co_return a; // expected-error {{include <coroutine>}}
// expected-error at -1 {{use of undeclared identifier 'a'}}
}
void no_coroutine_traits() {
- co_await 4; // expected-error {{std::experimental::coroutine_traits type was not found; include <experimental/coroutine>}}
+ co_await 4; // expected-error {{std::coroutine_traits type was not found; include <coroutine>}}
}
namespace std {
-namespace experimental {
template <class... Args>
struct void_t_imp {
@@ -45,11 +43,11 @@ struct traits_sfinae_base<T, void_t<typename T::promise_type>> {
template <class Ret, class... Args>
struct coroutine_traits : public traits_sfinae_base<Ret> {};
-}} // namespace std::experimental
+} // end of namespace std
template<typename Promise> struct coro {};
template <typename Promise, typename... Ps>
-struct std::experimental::coroutine_traits<coro<Promise>, Ps...> {
+struct std::coroutine_traits<coro<Promise>, Ps...> {
using promise_type = Promise;
};
@@ -81,47 +79,46 @@ struct auto_await_suspend {
};
struct DummyVoidTag {};
-DummyVoidTag no_specialization() { // expected-error {{this function cannot be a coroutine: 'std::experimental::coroutine_traits<DummyVoidTag>' has no member named 'promise_type'}}
+DummyVoidTag no_specialization() { // expected-error {{this function cannot be a coroutine: 'std::coroutine_traits<DummyVoidTag>' has no member named 'promise_type'}}
co_await a;
}
template <typename... T>
-struct std::experimental::coroutine_traits<int, T...> {};
+struct std::coroutine_traits<int, T...> {};
-int no_promise_type() { // expected-error {{this function cannot be a coroutine: 'std::experimental::coroutine_traits<int>' has no member named 'promise_type'}}
+int no_promise_type() { // expected-error {{this function cannot be a coroutine: 'std::coroutine_traits<int>' has no member named 'promise_type'}}
co_await a;
}
-int no_promise_type_multiple_awaits(int) { // expected-error {{this function cannot be a coroutine: 'std::experimental::coroutine_traits<int, int>' has no member named 'promise_type'}}
+int no_promise_type_multiple_awaits(int) { // expected-error {{this function cannot be a coroutine: 'std::coroutine_traits<int, int>' has no member named 'promise_type'}}
co_await a;
co_await a;
}
template <>
-struct std::experimental::coroutine_traits<double, double> { typedef int promise_type; };
-double bad_promise_type(double) { // expected-error {{this function cannot be a coroutine: 'experimental::coroutine_traits<double, double>::promise_type' (aka 'int') is not a class}}
+struct std::coroutine_traits<double, double> { typedef int promise_type; };
+double bad_promise_type(double) { // expected-error {{this function cannot be a coroutine: 'std::coroutine_traits<double, double>::promise_type' (aka 'int') is not a class}}
co_await a;
}
template <>
-struct std::experimental::coroutine_traits<double, int> {
+struct std::coroutine_traits<double, int> {
struct promise_type {};
};
double bad_promise_type_2(int) { // expected-error {{no member named 'initial_suspend'}}
- co_yield 0; // expected-error {{no member named 'yield_value' in 'std::experimental::coroutine_traits<double, int>::promise_type'}}
+ co_yield 0; // expected-error {{no member named 'yield_value' in 'std::coroutine_traits<double, int>::promise_type'}}
}
struct promise; // expected-note {{forward declaration}}
struct promise_void;
struct void_tag {};
template <typename... T>
-struct std::experimental::coroutine_traits<void, T...> { using promise_type = promise; };
+struct std::coroutine_traits<void, T...> { using promise_type = promise; };
template <typename... T>
-struct std::experimental::coroutine_traits<void, void_tag, T...>
-{ using promise_type = promise_void; };
+struct std::coroutine_traits<void, void_tag, T...> { using promise_type = promise_void; };
// FIXME: This diagnostic is terrible.
-void undefined_promise() { // expected-error {{this function cannot be a coroutine: 'experimental::coroutine_traits<void>::promise_type' (aka 'promise') is an incomplete type}}
+void undefined_promise() { // expected-error {{this function cannot be a coroutine: 'std::coroutine_traits<void>::promise_type' (aka 'promise') is an incomplete type}}
co_await a;
}
@@ -148,13 +145,12 @@ struct promise_void {
void unhandled_exception();
};
-void no_coroutine_handle() { // expected-error {{std::experimental::coroutine_handle type was not found; include <experimental/coroutine> before defining a coroutine}}
+void no_coroutine_handle() { // expected-error {{std::coroutine_handle type was not found; include <coroutine> before defining a coroutine}}
//expected-note at -1 {{call to 'initial_suspend' implicitly required by the initial suspend point}}
co_return 5; //expected-note {{function is a coroutine due to use of 'co_return' here}}
}
namespace std {
-namespace experimental {
template <class PromiseType = void>
struct coroutine_handle {
static coroutine_handle from_address(void *) noexcept;
@@ -165,7 +161,7 @@ struct coroutine_handle<void> {
coroutine_handle(coroutine_handle<PromiseType>) noexcept;
static coroutine_handle from_address(void *) noexcept;
};
-}} // namespace std::experimental
+} // namespace std
void yield() {
co_yield 0;
@@ -529,7 +525,7 @@ namespace dependent_operator_co_await_lookup {
struct yield_fn_tag {};
template <>
-struct std::experimental::coroutine_traits<void, yield_fn_tag> {
+struct std::coroutine_traits<void, yield_fn_tag> {
struct promise_type {
// FIXME: add an await_transform overload for functions
awaitable yield_value(int());
@@ -747,8 +743,7 @@ template<typename T> void ok_generic_lambda_coawait_PR41909() {
}
template void ok_generic_lambda_coawait_PR41909<int>(); // expected-note {{in instantiation of function template specialization 'ok_generic_lambda_coawait_PR41909<int>' requested here}}
-template<> struct std::experimental::coroutine_traits<int, int, const char**>
-{ using promise_type = promise; };
+template <> struct std::coroutine_traits<int, int, const char **> { using promise_type = promise; };
int main(int, const char**) {
co_await a; // expected-error {{'co_await' cannot be used in the 'main' function}}
@@ -761,12 +756,11 @@ struct good_promise_2 {
void return_void();
void unhandled_exception();
};
-template<> struct std::experimental::coroutine_handle<good_promise_2> {};
+template <> struct std::coroutine_handle<good_promise_2> {};
-template<> struct std::experimental::coroutine_traits<float>
-{ using promise_type = good_promise_2; };
+template <> struct std::coroutine_traits<float> { using promise_type = good_promise_2; };
-float badly_specialized_coro_handle() { // expected-error {{std::experimental::coroutine_handle missing a member named 'from_address'}}
+float badly_specialized_coro_handle() { // expected-error {{std::coroutine_handle must have a member named 'from_address'}}
//expected-note at -1 {{call to 'initial_suspend' implicitly required by the initial suspend point}}
co_return; //expected-note {{function is a coroutine due to use of 'co_return' here}}
}
@@ -785,8 +779,8 @@ void operator delete(void* __p, const std::nothrow_t&) noexcept;
struct promise_on_alloc_failure_tag {};
-template<>
-struct std::experimental::coroutine_traits<int, promise_on_alloc_failure_tag> {
+template <>
+struct std::coroutine_traits<int, promise_on_alloc_failure_tag> {
struct promise_type {
int get_return_object() {}
suspend_always initial_suspend() { return {}; }
@@ -905,8 +899,8 @@ good_coroutine_calls_noexcept_custom_new_operator(double, float, int) {
}
struct mismatch_gro_type_tag1 {};
-template<>
-struct std::experimental::coroutine_traits<int, mismatch_gro_type_tag1> {
+template <>
+struct std::coroutine_traits<int, mismatch_gro_type_tag1> {
struct promise_type {
void get_return_object() {} //expected-note {{member 'get_return_object' declared here}}
suspend_always initial_suspend() { return {}; }
@@ -922,8 +916,8 @@ extern "C" int f(mismatch_gro_type_tag1) {
}
struct mismatch_gro_type_tag2 {};
-template<>
-struct std::experimental::coroutine_traits<int, mismatch_gro_type_tag2> {
+template <>
+struct std::coroutine_traits<int, mismatch_gro_type_tag2> {
struct promise_type {
void *get_return_object() {} //expected-note {{member 'get_return_object' declared here}}
suspend_always initial_suspend() { return {}; }
@@ -940,8 +934,8 @@ extern "C" int f(mismatch_gro_type_tag2) {
}
struct mismatch_gro_type_tag3 {};
-template<>
-struct std::experimental::coroutine_traits<int, mismatch_gro_type_tag3> {
+template <>
+struct std::coroutine_traits<int, mismatch_gro_type_tag3> {
struct promise_type {
int get_return_object() {}
static void get_return_object_on_allocation_failure() {} //expected-note {{member 'get_return_object_on_allocation_failure' declared here}}
@@ -959,8 +953,8 @@ extern "C" int f(mismatch_gro_type_tag3) {
struct mismatch_gro_type_tag4 {};
-template<>
-struct std::experimental::coroutine_traits<int, mismatch_gro_type_tag4> {
+template <>
+struct std::coroutine_traits<int, mismatch_gro_type_tag4> {
struct promise_type {
int get_return_object() {}
static char *get_return_object_on_allocation_failure() {} //expected-note {{member 'get_return_object_on_allocation_failure' declared}}
@@ -992,13 +986,13 @@ coro<bad_promise_no_return_func> no_return_value_or_return_void() {
struct bad_await_suspend_return {
bool await_ready();
// expected-error at +1 {{return type of 'await_suspend' is required to be 'void' or 'bool' (have 'char')}}
- char await_suspend(std::experimental::coroutine_handle<>);
+ char await_suspend(std::coroutine_handle<>);
void await_resume();
};
struct bad_await_ready_return {
// expected-note at +1 {{return type of 'await_ready' is required to be contextually convertible to 'bool'}}
void await_ready();
- bool await_suspend(std::experimental::coroutine_handle<>);
+ bool await_suspend(std::coroutine_handle<>);
void await_resume();
};
struct await_ready_explicit_bool {
@@ -1006,7 +1000,7 @@ struct await_ready_explicit_bool {
explicit operator bool() const;
};
BoolT await_ready();
- void await_suspend(std::experimental::coroutine_handle<>);
+ void await_suspend(std::coroutine_handle<>);
void await_resume();
};
template <class SuspendTy>
@@ -1014,7 +1008,7 @@ struct await_suspend_type_test {
bool await_ready();
// expected-error at +2 {{return type of 'await_suspend' is required to be 'void' or 'bool' (have 'bool &')}}
// expected-error at +1 {{return type of 'await_suspend' is required to be 'void' or 'bool' (have 'bool &&')}}
- SuspendTy await_suspend(std::experimental::coroutine_handle<>);
+ SuspendTy await_suspend(std::coroutine_handle<>);
// cxx20_2b-warning at -1 {{volatile-qualified return type 'const volatile bool' is deprecated}}
void await_resume();
};
@@ -1074,7 +1068,7 @@ struct TypeTest {
template <class... Args>
static constexpr bool MatchesArgs = IsSameV<T,
- std::experimental::coroutine_traits<CoroMemberTag, Args...>>;
+ std::coroutine_traits<CoroMemberTag, Args...>>;
};
template <class T>
@@ -1086,7 +1080,7 @@ struct AwaitReturnsType {
template <class... CoroTraitsArgs>
struct CoroMemberPromise {
- using TraitsT = std::experimental::coroutine_traits<CoroTraitsArgs...>;
+ using TraitsT = std::coroutine_traits<CoroTraitsArgs...>;
using TypeTestT = TypeTest<TraitsT>;
using AwaitTestT = AwaitReturnsType<TypeTestT>;
@@ -1103,7 +1097,7 @@ struct CoroMemberPromise {
} // namespace CoroHandleMemberFunctionTest
template <class... Args>
-struct ::std::experimental::coroutine_traits<CoroHandleMemberFunctionTest::CoroMemberTag, Args...> {
+struct ::std::coroutine_traits<CoroHandleMemberFunctionTest::CoroMemberTag, Args...> {
using promise_type = CoroHandleMemberFunctionTest::CoroMemberPromise<CoroHandleMemberFunctionTest::CoroMemberTag, Args...>;
};
@@ -1189,16 +1183,16 @@ struct TestType {
}
BadCoroMemberTag test_diagnostics() {
- // expected-error at -1 {{this function cannot be a coroutine: 'std::experimental::coroutine_traits<CoroHandleMemberFunctionTest::BadCoroMemberTag, CoroHandleMemberFunctionTest::TestType &>' has no member named 'promise_type'}}
+ // expected-error at -1 {{this function cannot be a coroutine: 'std::coroutine_traits<CoroHandleMemberFunctionTest::BadCoroMemberTag, CoroHandleMemberFunctionTest::TestType &>' has no member named 'promise_type'}}
co_return;
}
BadCoroMemberTag test_diagnostics(int) const && {
- // expected-error at -1 {{this function cannot be a coroutine: 'std::experimental::coroutine_traits<CoroHandleMemberFunctionTest::BadCoroMemberTag, const CoroHandleMemberFunctionTest::TestType &&, int>' has no member named 'promise_type'}}
+ // expected-error at -1 {{this function cannot be a coroutine: 'std::coroutine_traits<CoroHandleMemberFunctionTest::BadCoroMemberTag, const CoroHandleMemberFunctionTest::TestType &&, int>' has no member named 'promise_type'}}
co_return;
}
static BadCoroMemberTag test_static_diagnostics(long *) {
- // expected-error at -1 {{this function cannot be a coroutine: 'std::experimental::coroutine_traits<CoroHandleMemberFunctionTest::BadCoroMemberTag, long *>' has no member named 'promise_type'}}
+ // expected-error at -1 {{this function cannot be a coroutine: 'std::coroutine_traits<CoroHandleMemberFunctionTest::BadCoroMemberTag, long *>' has no member named 'promise_type'}}
co_return;
}
};
@@ -1310,7 +1304,7 @@ struct bad_promise_deleted_constructor {
coro<bad_promise_deleted_constructor>
bad_coroutine_calls_deleted_promise_constructor() {
- // expected-error at -1 {{call to deleted constructor of 'std::experimental::coroutine_traits<coro<CoroHandleMemberFunctionTest::bad_promise_deleted_constructor>>::promise_type' (aka 'CoroHandleMemberFunctionTest::bad_promise_deleted_constructor')}}
+ // expected-error at -1 {{call to deleted constructor of 'std::coroutine_traits<coro<CoroHandleMemberFunctionTest::bad_promise_deleted_constructor>>::promise_type' (aka 'CoroHandleMemberFunctionTest::bad_promise_deleted_constructor')}}
co_return;
}
@@ -1377,7 +1371,7 @@ struct bad_promise_no_matching_constructor {
coro<bad_promise_no_matching_constructor>
bad_coroutine_calls_with_no_matching_constructor(int, int) {
- // expected-error at -1 {{call to deleted constructor of 'std::experimental::coroutine_traits<coro<CoroHandleMemberFunctionTest::bad_promise_no_matching_constructor>, int, int>::promise_type' (aka 'CoroHandleMemberFunctionTest::bad_promise_no_matching_constructor')}}
+ // expected-error at -1 {{call to deleted constructor of 'std::coroutine_traits<coro<CoroHandleMemberFunctionTest::bad_promise_no_matching_constructor>, int, int>::promise_type' (aka 'CoroHandleMemberFunctionTest::bad_promise_no_matching_constructor')}}
co_return;
}
@@ -1393,7 +1387,7 @@ bad_coroutine_calls_with_no_matching_constructor(int, int, int) {
class awaitable_no_unused_warn {
public:
- using handle_type = std::experimental::coroutine_handle<>;
+ using handle_type = std::coroutine_handle<>;
constexpr bool await_ready() noexcept { return false; }
void await_suspend(handle_type) noexcept {}
int await_resume() noexcept { return 1; }
@@ -1402,7 +1396,7 @@ class awaitable_no_unused_warn {
class awaitable_unused_warn {
public:
- using handle_type = std::experimental::coroutine_handle<>;
+ using handle_type = std::coroutine_handle<>;
constexpr bool await_ready() noexcept { return false; }
void await_suspend(handle_type) noexcept {}
[[nodiscard]] int await_resume() noexcept { return 1; }
@@ -1432,7 +1426,7 @@ test_unused_warning() {
}
struct missing_await_ready {
- void await_suspend(std::experimental::coroutine_handle<>);
+ void await_suspend(std::coroutine_handle<>);
void await_resume();
};
struct missing_await_suspend {
@@ -1441,7 +1435,7 @@ struct missing_await_suspend {
};
struct missing_await_resume {
bool await_ready();
- void await_suspend(std::experimental::coroutine_handle<>);
+ void await_suspend(std::coroutine_handle<>);
};
void test_missing_awaitable_members() {
diff --git a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
index f39d5ecbb095..af15572a573b 100644
--- a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
+++ b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
@@ -623,7 +623,6 @@ TEST(Matcher, MatchesCoroutine) {
FileContentMappings M;
M.push_back(std::make_pair("/coro_header", R"cpp(
namespace std {
-namespace experimental {
template <class... Args>
struct void_t_imp {
@@ -642,7 +641,7 @@ struct traits_sfinae_base<T, void_t<typename T::promise_type>> {
template <class Ret, class... Args>
struct coroutine_traits : public traits_sfinae_base<Ret> {};
-}} // namespace std::experimental
+} // namespace std
struct awaitable {
bool await_ready() noexcept;
template <typename F>
@@ -658,14 +657,13 @@ struct promise {
void unhandled_exception();
};
template <typename... T>
-struct std::experimental::coroutine_traits<void, T...> { using promise_type = promise; };
+struct std::coroutine_traits<void, T...> { using promise_type = promise; };
namespace std {
-namespace experimental {
template <class PromiseType = void>
struct coroutine_handle {
static coroutine_handle from_address(void *) noexcept;
};
-}} // namespace std::experimental
+} // namespace std
)cpp"));
StringRef CoReturnCode = R"cpp(
#include <coro_header>
More information about the cfe-commits
mailing list