// 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();
}
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();
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.
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;
/// 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();
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();
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();
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())
// 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>}}
+}
+
+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>
+struct std::experimental::coroutine_traits<coro<Promise>, Ps...> {
+ using promise_type = Promise;
+};
struct awaitable {
bool await_ready();
- void await_suspend(); // FIXME: coroutine_handle
+ template <typename F> void await_suspend(F);
void await_resume();
} a;
struct suspend_always {
bool await_ready() { return false; }
- void await_suspend() {}
+ template <typename F> void await_suspend(F);
void await_resume() {}
};
struct suspend_never {
bool await_ready() { return true; }
- void await_suspend() {}
+ template <typename F> void await_suspend(F);
void await_resume() {}
};
-void no_coroutine_traits() {
- co_await a; // expected-error {{need to include <experimental/coroutine>}}
-}
-
-namespace std {
-namespace experimental {
-template <typename... T>
-struct coroutine_traits; // expected-note {{declared here}}
-}
-}
-
-template<typename Promise> struct coro {};
-template <typename Promise, typename... Ps>
-struct std::experimental::coroutine_traits<coro<Promise>, Ps...> {
- using promise_type = Promise;
-};
-
void no_specialization() {
co_await a; // expected-error {{implicit instantiation of undefined template 'std::experimental::coroutine_traits<void>'}}
}
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;
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};
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}}
+}