[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
This commit is contained in:
parent
553a872465
commit
ec117158a3
|
@ -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 {
|
||||
|
|
|
@ -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**:
|
||||
|
||||
|
|
|
@ -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<
|
||||
|
|
|
@ -1124,6 +1124,10 @@ public:
|
|||
/// 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 @@ public:
|
|||
NamespaceDecl *getOrCreateStdNamespace();
|
||||
|
||||
NamespaceDecl *lookupStdExperimentalNamespace();
|
||||
NamespaceDecl *getCachedCoroNamespace() { return CoroTraitsNamespaceCache; }
|
||||
|
||||
CXXRecordDecl *getStdBadAlloc() const;
|
||||
EnumDecl *getStdAlignValT() const;
|
||||
|
@ -10265,8 +10270,11 @@ public:
|
|||
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);
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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 *'
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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; }
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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]]
|
||||
|
|
|
@ -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; }
|
|
@ -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; }
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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]])
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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() {}
|
||||
};
|
||||
|
|
|
@ -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]])
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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: ]
|
|
@ -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(
|
||||
|
|
|
@ -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",
|
|
@ -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;
|
||||
|
|
|
@ -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@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@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]]
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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]]
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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 {
|
||||
|
||||
|
|
|
@ -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]])
|
||||
|
||||
|
|
|
@ -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(
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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; }
|
|
@ -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; }
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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@coro_t@@QEAA@XZ"(
|
||||
// CHECK: invoke void @"?get_return_object@promise_type@coro_t@@QEAA?AU2@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@XZ"(
|
|
@ -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();
|
||||
|
|
|
@ -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)
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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); }
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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]])
|
|
@ -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]])
|
||||
|
|
|
@ -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.+}}
|
|
@ -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
|
||||
|
|
|
@ -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@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@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@promise_type@coro_t@@QEAA?AUsuspend_never@coroutines_v1@experimental@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(
|
|
@ -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@promise_type@coro_t@@QEAA?AUsuspend_never@coroutines_v1@experimental@std@@XZ"(
|
||||
// CHECK-NEXT: call void @"?final_suspend@promise_type@coro_t@@QEAA?AUsuspend_never@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(
|
||||
|
|
|
@ -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@YA?AUno_suspend@@AEBUB@@@Z"(
|
||||
operator co_await(b);
|
||||
}
|
|
@ -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) {}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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@-1 {{call to deleted member function 'await_transform'}}
|
||||
// expected-note@-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@-1 {{call to deleted member function 'await_transform'}}
|
||||
// expected-note@-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@-1 {{call to deleted member function 'await_transform'}}
|
||||
// expected-note@-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@-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@-1 {{overload resolution selected deleted operator 'co_await'}}
|
||||
// expected-note@-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@-1 {{overload resolution selected deleted operator 'co_await'}}
|
||||
// expected-note@-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@-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@-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);
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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 = {};
|
||||
|
|
|
@ -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}}
|
|
@ -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; }
|
||||
|
|
|
@ -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'}}
|
||||
|
|
|
@ -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}}
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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.}}
|
||||
}
|
|
@ -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.}}
|
||||
}
|
|
@ -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&.
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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) {
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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>'}}
|
||||
}
|
|
@ -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>'}}
|
||||
|
|
|
@ -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
|
|
@ -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}}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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; }
|
||||
|
|
|
@ -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;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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@-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@-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@-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@-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@-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@+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@+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@+2 {{return type of 'await_suspend' is required to be 'void' or 'bool' (have 'bool &')}}
|
||||
// expected-error@+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@-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@-1 {{this function cannot be a coroutine: 'std::experimental::coroutine_traits<CoroHandleMemberFunctionTest::BadCoroMemberTag, CoroHandleMemberFunctionTest::TestType &>' has no member named 'promise_type'}}
|
||||
// expected-error@-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@-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@-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@-1 {{this function cannot be a coroutine: 'std::experimental::coroutine_traits<CoroHandleMemberFunctionTest::BadCoroMemberTag, long *>' has no member named 'promise_type'}}
|
||||
// expected-error@-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@-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@-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@-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@-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 @@ public:
|
|||
|
||||
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() {
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue