[coroutines] Build and pass coroutine_handle to await_suspend
Summary: This patch adds passing a coroutine_handle object to await_suspend calls. It builds the coroutine_handle using coroutine_handle<PromiseType>::from_address(__builtin_coro_frame()). (a revision of https://reviews.llvm.org/D26316 that for some reason refuses to apply via arc patch) Reviewers: GorNishanov Subscribers: mehdi_amini, cfe-commits, EricWF Differential Revision: https://reviews.llvm.org/D30769 llvm-svn: 297356
This commit is contained in:
parent
84a2dadcee
commit
6dcb0eb301
|
@ -8828,8 +8828,13 @@ def err_coroutine_invalid_func_context : Error<
|
|||
"|a copy assignment operator|a move assignment operator|the 'main' function"
|
||||
"|a constexpr function|a function with a deduced return type"
|
||||
"|a varargs function}0">;
|
||||
def err_implied_std_coroutine_traits_not_found : Error<
|
||||
"you need to include <experimental/coroutine> before defining a coroutine">;
|
||||
def err_implied_coroutine_type_not_found : Error<
|
||||
"%0 type was not found; include <experimental/coroutine> before defining "
|
||||
"a coroutine">;
|
||||
def err_malformed_std_coroutine_handle : Error<
|
||||
"std::experimental::coroutine_handle must be a class template">;
|
||||
def err_coroutine_handle_missing_member : Error<
|
||||
"std::experimental::coroutine_handle missing a member named '%0'">;
|
||||
def err_malformed_std_coroutine_traits : Error<
|
||||
"'std::experimental::coroutine_traits' must be a class template">;
|
||||
def err_implied_std_coroutine_traits_promise_type_not_found : Error<
|
||||
|
@ -8838,7 +8843,7 @@ def err_implied_std_coroutine_traits_promise_type_not_class : Error<
|
|||
"this function cannot be a coroutine: %0 is not a class">;
|
||||
def err_coroutine_promise_type_incomplete : Error<
|
||||
"this function cannot be a coroutine: %0 is an incomplete type">;
|
||||
def err_coroutine_traits_missing_specialization : Error<
|
||||
def err_coroutine_type_missing_specialization : Error<
|
||||
"this function cannot be a coroutine: missing definition of "
|
||||
"specialization %q0">;
|
||||
def err_implied_std_current_exception_not_found : Error<
|
||||
|
|
|
@ -39,14 +39,16 @@ static QualType lookupPromiseType(Sema &S, const FunctionProtoType *FnType,
|
|||
// FIXME: Cache std::coroutine_traits once we've found it.
|
||||
NamespaceDecl *StdExp = S.lookupStdExperimentalNamespace();
|
||||
if (!StdExp) {
|
||||
S.Diag(KwLoc, diag::err_implied_std_coroutine_traits_not_found);
|
||||
S.Diag(KwLoc, diag::err_implied_coroutine_type_not_found)
|
||||
<< "std::experimental::coroutine_traits";
|
||||
return QualType();
|
||||
}
|
||||
|
||||
LookupResult Result(S, &S.PP.getIdentifierTable().get("coroutine_traits"),
|
||||
FuncLoc, Sema::LookupOrdinaryName);
|
||||
if (!S.LookupQualifiedName(Result, StdExp)) {
|
||||
S.Diag(KwLoc, diag::err_implied_std_coroutine_traits_not_found);
|
||||
S.Diag(KwLoc, diag::err_implied_coroutine_type_not_found)
|
||||
<< "std::experimental::coroutine_traits";
|
||||
return QualType();
|
||||
}
|
||||
|
||||
|
@ -76,7 +78,7 @@ static QualType lookupPromiseType(Sema &S, const FunctionProtoType *FnType,
|
|||
if (CoroTrait.isNull())
|
||||
return QualType();
|
||||
if (S.RequireCompleteType(KwLoc, CoroTrait,
|
||||
diag::err_coroutine_traits_missing_specialization))
|
||||
diag::err_coroutine_type_missing_specialization))
|
||||
return QualType();
|
||||
|
||||
auto *RD = CoroTrait->getAsCXXRecordDecl();
|
||||
|
@ -116,6 +118,51 @@ static QualType lookupPromiseType(Sema &S, const FunctionProtoType *FnType,
|
|||
return PromiseType;
|
||||
}
|
||||
|
||||
/// Look up the std::coroutine_traits<...>::promise_type for the given
|
||||
/// function type.
|
||||
static QualType lookupCoroutineHandleType(Sema &S, QualType PromiseType,
|
||||
SourceLocation Loc) {
|
||||
if (PromiseType.isNull())
|
||||
return QualType();
|
||||
|
||||
NamespaceDecl *StdExp = S.lookupStdExperimentalNamespace();
|
||||
assert(StdExp && "Should already be diagnosed");
|
||||
|
||||
LookupResult Result(S, &S.PP.getIdentifierTable().get("coroutine_handle"),
|
||||
Loc, Sema::LookupOrdinaryName);
|
||||
if (!S.LookupQualifiedName(Result, StdExp)) {
|
||||
S.Diag(Loc, diag::err_implied_coroutine_type_not_found)
|
||||
<< "std::experimental::coroutine_handle";
|
||||
return QualType();
|
||||
}
|
||||
|
||||
ClassTemplateDecl *CoroHandle = Result.getAsSingle<ClassTemplateDecl>();
|
||||
if (!CoroHandle) {
|
||||
Result.suppressDiagnostics();
|
||||
// We found something weird. Complain about the first thing we found.
|
||||
NamedDecl *Found = *Result.begin();
|
||||
S.Diag(Found->getLocation(), diag::err_malformed_std_coroutine_handle);
|
||||
return QualType();
|
||||
}
|
||||
|
||||
// Form template argument list for coroutine_handle<Promise>.
|
||||
TemplateArgumentListInfo Args(Loc, Loc);
|
||||
Args.addArgument(TemplateArgumentLoc(
|
||||
TemplateArgument(PromiseType),
|
||||
S.Context.getTrivialTypeSourceInfo(PromiseType, Loc)));
|
||||
|
||||
// Build the template-id.
|
||||
QualType CoroHandleType =
|
||||
S.CheckTemplateIdType(TemplateName(CoroHandle), Loc, Args);
|
||||
if (CoroHandleType.isNull())
|
||||
return QualType();
|
||||
if (S.RequireCompleteType(Loc, CoroHandleType,
|
||||
diag::err_coroutine_type_missing_specialization))
|
||||
return QualType();
|
||||
|
||||
return CoroHandleType;
|
||||
}
|
||||
|
||||
static bool isValidCoroutineContext(Sema &S, SourceLocation Loc,
|
||||
StringRef Keyword) {
|
||||
// 'co_await' and 'co_yield' are not permitted in unevaluated operands.
|
||||
|
@ -237,6 +284,32 @@ static Expr *buildBuiltinCall(Sema &S, SourceLocation Loc, Builtin::ID Id,
|
|||
return Call.get();
|
||||
}
|
||||
|
||||
static ExprResult buildCoroutineHandle(Sema &S, QualType PromiseType,
|
||||
SourceLocation Loc) {
|
||||
QualType CoroHandleType = lookupCoroutineHandleType(S, PromiseType, Loc);
|
||||
if (CoroHandleType.isNull())
|
||||
return ExprError();
|
||||
|
||||
DeclContext *LookupCtx = S.computeDeclContext(CoroHandleType);
|
||||
LookupResult Found(S, &S.PP.getIdentifierTable().get("from_address"), Loc,
|
||||
Sema::LookupOrdinaryName);
|
||||
if (!S.LookupQualifiedName(Found, LookupCtx)) {
|
||||
S.Diag(Loc, diag::err_coroutine_handle_missing_member)
|
||||
<< "from_address";
|
||||
return ExprError();
|
||||
}
|
||||
|
||||
Expr *FramePtr =
|
||||
buildBuiltinCall(S, Loc, Builtin::BI__builtin_coro_frame, {});
|
||||
|
||||
CXXScopeSpec SS;
|
||||
ExprResult FromAddr =
|
||||
S.BuildDeclarationNameExpr(SS, Found, /*NeedsADL=*/false);
|
||||
if (FromAddr.isInvalid())
|
||||
return ExprError();
|
||||
|
||||
return S.ActOnCallExpr(nullptr, FromAddr.get(), Loc, FramePtr, Loc);
|
||||
}
|
||||
|
||||
struct ReadySuspendResumeResult {
|
||||
bool IsInvalid;
|
||||
|
@ -261,18 +334,23 @@ static ExprResult buildMemberCall(Sema &S, Expr *Base, SourceLocation Loc,
|
|||
|
||||
/// Build calls to await_ready, await_suspend, and await_resume for a co_await
|
||||
/// expression.
|
||||
static ReadySuspendResumeResult buildCoawaitCalls(Sema &S, SourceLocation Loc,
|
||||
Expr *E) {
|
||||
static ReadySuspendResumeResult buildCoawaitCalls(Sema &S, VarDecl *CoroPromise,
|
||||
SourceLocation Loc, Expr *E) {
|
||||
// Assume invalid until we see otherwise.
|
||||
ReadySuspendResumeResult Calls = {true, {}};
|
||||
|
||||
ExprResult CoroHandleRes = buildCoroutineHandle(S, CoroPromise->getType(), Loc);
|
||||
if (CoroHandleRes.isInvalid())
|
||||
return Calls;
|
||||
Expr *CoroHandle = CoroHandleRes.get();
|
||||
|
||||
const StringRef Funcs[] = {"await_ready", "await_suspend", "await_resume"};
|
||||
MultiExprArg Args[] = {None, CoroHandle, None};
|
||||
for (size_t I = 0, N = llvm::array_lengthof(Funcs); I != N; ++I) {
|
||||
Expr *Operand = new (S.Context) OpaqueValueExpr(
|
||||
Loc, E->getType(), VK_LValue, E->getObjectKind(), E);
|
||||
|
||||
// FIXME: Pass coroutine handle to await_suspend.
|
||||
ExprResult Result = buildMemberCall(S, Operand, Loc, Funcs[I], None);
|
||||
ExprResult Result = buildMemberCall(S, Operand, Loc, Funcs[I], Args[I]);
|
||||
if (Result.isInvalid())
|
||||
return Calls;
|
||||
Calls.Results[I] = Result.get();
|
||||
|
@ -473,7 +551,8 @@ ExprResult Sema::BuildResolvedCoawaitExpr(SourceLocation Loc, Expr *E,
|
|||
E = CreateMaterializeTemporaryExpr(E->getType(), E, true);
|
||||
|
||||
// Build the await_ready, await_suspend, await_resume calls.
|
||||
ReadySuspendResumeResult RSS = buildCoawaitCalls(*this, Loc, E);
|
||||
ReadySuspendResumeResult RSS =
|
||||
buildCoawaitCalls(*this, Coroutine->CoroutinePromise, Loc, E);
|
||||
if (RSS.IsInvalid)
|
||||
return ExprError();
|
||||
|
||||
|
@ -526,7 +605,8 @@ ExprResult Sema::BuildCoyieldExpr(SourceLocation Loc, Expr *E) {
|
|||
E = CreateMaterializeTemporaryExpr(E->getType(), E, true);
|
||||
|
||||
// Build the await_ready, await_suspend, await_resume calls.
|
||||
ReadySuspendResumeResult RSS = buildCoawaitCalls(*this, Loc, E);
|
||||
ReadySuspendResumeResult RSS =
|
||||
buildCoawaitCalls(*this, Coroutine->CoroutinePromise, Loc, E);
|
||||
if (RSS.IsInvalid)
|
||||
return ExprError();
|
||||
|
||||
|
@ -677,14 +757,30 @@ void Sema::CheckCompletedCoroutineBody(FunctionDecl *FD, Stmt *&Body) {
|
|||
FunctionScopeInfo *Fn = getCurFunction();
|
||||
assert(Fn && Fn->CoroutinePromise && "not a coroutine");
|
||||
|
||||
if (!Body) {
|
||||
assert(FD->isInvalidDecl() &&
|
||||
"a null body is only allowed for invalid declarations");
|
||||
return;
|
||||
}
|
||||
|
||||
if (isa<CoroutineBodyStmt>(Body)) {
|
||||
// FIXME(EricWF): Nothing todo. the body is already a transformed coroutine
|
||||
// body statement.
|
||||
return;
|
||||
}
|
||||
|
||||
// Coroutines [stmt.return]p1:
|
||||
// A return statement shall not appear in a coroutine.
|
||||
if (Fn->FirstReturnLoc.isValid()) {
|
||||
Diag(Fn->FirstReturnLoc, diag::err_return_in_coroutine);
|
||||
auto *First = Fn->CoroutineStmts[0];
|
||||
Diag(First->getLocStart(), diag::note_declared_coroutine_here)
|
||||
<< (isa<CoawaitExpr>(First) ? "co_await" :
|
||||
isa<CoyieldExpr>(First) ? "co_yield" : "co_return");
|
||||
// FIXME: Every Coroutine statement may be invalid and therefore not added
|
||||
// to CoroutineStmts. Find another way to provide location information.
|
||||
if (!Fn->CoroutineStmts.empty()) {
|
||||
auto *First = Fn->CoroutineStmts[0];
|
||||
Diag(First->getLocStart(), diag::note_declared_coroutine_here)
|
||||
<< (isa<CoawaitExpr>(First) ? "co_await" :
|
||||
isa<CoyieldExpr>(First) ? "co_yield" : "co_return");
|
||||
}
|
||||
}
|
||||
SubStmtBuilder Builder(*this, *FD, *Fn, Body);
|
||||
if (Builder.isInvalid())
|
||||
|
|
|
@ -973,7 +973,7 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType,
|
|||
|
||||
// C++1z [expr.ref]p2:
|
||||
// For the first option (dot) the first expression shall be a glvalue [...]
|
||||
if (!IsArrow && BaseExpr->isRValue()) {
|
||||
if (!IsArrow && BaseExpr && BaseExpr->isRValue()) {
|
||||
ExprResult Converted = TemporaryMaterializationConversion(BaseExpr);
|
||||
if (Converted.isInvalid())
|
||||
return ExprError();
|
||||
|
|
|
@ -4,12 +4,27 @@ 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 *) { return {}; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct coroutine_handle<void> {
|
||||
static coroutine_handle from_address(void *) { return {}; }
|
||||
coroutine_handle() = default;
|
||||
template <class PromiseType>
|
||||
coroutine_handle(coroutine_handle<PromiseType>) {}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
struct suspend_always {
|
||||
bool await_ready() { return false; }
|
||||
void await_suspend() {}
|
||||
void await_suspend(std::experimental::coroutine_handle<>) {}
|
||||
void await_resume() {}
|
||||
};
|
||||
|
||||
|
|
|
@ -4,12 +4,27 @@ namespace std {
|
|||
namespace experimental {
|
||||
template <typename... T>
|
||||
struct coroutine_traits;
|
||||
|
||||
template <class Promise = void>
|
||||
struct coroutine_handle {
|
||||
coroutine_handle() = default;
|
||||
static coroutine_handle from_address(void *) { return {}; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct coroutine_handle<void> {
|
||||
static coroutine_handle from_address(void *) { return {}; }
|
||||
coroutine_handle() = default;
|
||||
template <class PromiseType>
|
||||
coroutine_handle(coroutine_handle<PromiseType>) {}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
struct suspend_always {
|
||||
bool await_ready();
|
||||
void await_suspend();
|
||||
void await_suspend(std::experimental::coroutine_handle<>);
|
||||
void await_resume();
|
||||
};
|
||||
|
||||
|
|
|
@ -1,23 +1,5 @@
|
|||
// RUN: %clang_cc1 -triple x86_64-apple-darwin9 %s -std=c++14 -fcoroutines-ts -fsyntax-only -Wignored-qualifiers -Wno-error=return-type -verify -fblocks -Wno-unreachable-code -Wno-unused-value
|
||||
|
||||
struct awaitable {
|
||||
bool await_ready();
|
||||
void await_suspend(); // FIXME: coroutine_handle
|
||||
void await_resume();
|
||||
} a;
|
||||
|
||||
struct suspend_always {
|
||||
bool await_ready() { return false; }
|
||||
void await_suspend() {}
|
||||
void await_resume() {}
|
||||
};
|
||||
|
||||
struct suspend_never {
|
||||
bool await_ready() { return true; }
|
||||
void await_suspend() {}
|
||||
void await_resume() {}
|
||||
};
|
||||
|
||||
namespace std {
|
||||
namespace experimental {
|
||||
|
||||
|
@ -25,10 +7,37 @@ template <class Ret, typename... T>
|
|||
struct coroutine_traits { using promise_type = typename Ret::promise_type; };
|
||||
|
||||
template <class Promise = void>
|
||||
struct coroutine_handle {};
|
||||
struct coroutine_handle {
|
||||
static coroutine_handle from_address(void *);
|
||||
};
|
||||
template <>
|
||||
struct coroutine_handle<void> {
|
||||
template <class PromiseType>
|
||||
coroutine_handle(coroutine_handle<PromiseType>);
|
||||
static coroutine_handle from_address(void *);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
struct awaitable {
|
||||
bool await_ready();
|
||||
void await_suspend(std::experimental::coroutine_handle<>); // FIXME: coroutine_handle
|
||||
void await_resume();
|
||||
} a;
|
||||
|
||||
struct suspend_always {
|
||||
bool await_ready() { return false; }
|
||||
void await_suspend(std::experimental::coroutine_handle<>) {}
|
||||
void await_resume() {}
|
||||
};
|
||||
|
||||
struct suspend_never {
|
||||
bool await_ready() { return true; }
|
||||
void await_suspend(std::experimental::coroutine_handle<>) {}
|
||||
void await_resume() {}
|
||||
};
|
||||
|
||||
struct promise_void {
|
||||
void get_return_object();
|
||||
suspend_always initial_suspend();
|
||||
|
|
|
@ -16,35 +16,15 @@ void no_coroutine_traits_bad_arg_return() {
|
|||
// expected-error@-1 {{use of undeclared identifier 'a'}}
|
||||
}
|
||||
|
||||
|
||||
struct awaitable {
|
||||
bool await_ready();
|
||||
void await_suspend(); // FIXME: coroutine_handle
|
||||
void await_resume();
|
||||
} a;
|
||||
|
||||
struct suspend_always {
|
||||
bool await_ready() { return false; }
|
||||
void await_suspend() {}
|
||||
void await_resume() {}
|
||||
};
|
||||
|
||||
struct suspend_never {
|
||||
bool await_ready() { return true; }
|
||||
void await_suspend() {}
|
||||
void await_resume() {}
|
||||
};
|
||||
|
||||
void no_coroutine_traits() {
|
||||
co_await a; // expected-error {{need to include <experimental/coroutine>}}
|
||||
co_await 4; // expected-error {{std::experimental::coroutine_traits type was not found; include <experimental/coroutine>}}
|
||||
}
|
||||
|
||||
namespace std {
|
||||
namespace experimental {
|
||||
template <typename... T>
|
||||
struct coroutine_traits; // expected-note {{declared here}}
|
||||
}
|
||||
}
|
||||
}} // namespace std::experimental
|
||||
|
||||
template<typename Promise> struct coro {};
|
||||
template <typename Promise, typename... Ps>
|
||||
|
@ -52,6 +32,24 @@ struct std::experimental::coroutine_traits<coro<Promise>, Ps...> {
|
|||
using promise_type = Promise;
|
||||
};
|
||||
|
||||
struct awaitable {
|
||||
bool await_ready();
|
||||
template <typename F> void await_suspend(F);
|
||||
void await_resume();
|
||||
} a;
|
||||
|
||||
struct suspend_always {
|
||||
bool await_ready() { return false; }
|
||||
template <typename F> void await_suspend(F);
|
||||
void await_resume() {}
|
||||
};
|
||||
|
||||
struct suspend_never {
|
||||
bool await_ready() { return true; }
|
||||
template <typename F> void await_suspend(F);
|
||||
void await_resume() {}
|
||||
};
|
||||
|
||||
void no_specialization() {
|
||||
co_await a; // expected-error {{implicit instantiation of undefined template 'std::experimental::coroutine_traits<void>'}}
|
||||
}
|
||||
|
@ -86,13 +84,6 @@ template <typename... T>
|
|||
struct std::experimental::coroutine_traits<void, void_tag, T...>
|
||||
{ using promise_type = promise_void; };
|
||||
|
||||
namespace std {
|
||||
namespace experimental {
|
||||
template <typename Promise = void>
|
||||
struct coroutine_handle;
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: This diagnostic is terrible.
|
||||
void undefined_promise() { // expected-error {{this function cannot be a coroutine: 'experimental::coroutine_traits<void>::promise_type' (aka 'promise') is an incomplete type}}
|
||||
co_await a;
|
||||
|
@ -119,6 +110,25 @@ struct promise_void {
|
|||
void return_void();
|
||||
};
|
||||
|
||||
void no_coroutine_handle() { // expected-error {{std::experimental::coroutine_handle type was not found; include <experimental/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 *);
|
||||
};
|
||||
template <>
|
||||
struct coroutine_handle<void> {
|
||||
template <class PromiseType>
|
||||
coroutine_handle(coroutine_handle<PromiseType>);
|
||||
static coroutine_handle from_address(void *);
|
||||
};
|
||||
}} // namespace std::experimental
|
||||
|
||||
void yield() {
|
||||
co_yield 0;
|
||||
co_yield {"foo", 1, 2};
|
||||
|
@ -520,3 +530,19 @@ template<> struct std::experimental::coroutine_traits<int, int, const char**>
|
|||
int main(int, const char**) {
|
||||
co_await a; // expected-error {{'co_await' cannot be used in the 'main' function}}
|
||||
}
|
||||
|
||||
struct good_promise_2 {
|
||||
float get_return_object();
|
||||
suspend_always initial_suspend();
|
||||
suspend_always final_suspend();
|
||||
void return_void();
|
||||
};
|
||||
template<> struct std::experimental::coroutine_handle<good_promise_2> {};
|
||||
|
||||
template<> struct std::experimental::coroutine_traits<float>
|
||||
{ using promise_type = good_promise_2; };
|
||||
|
||||
float badly_specialized_coro_handle() { // expected-error {{std::experimental::coroutine_handle missing 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}}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue