Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -8828,8 +8828,13 @@ "|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 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_implied_coroutine_type_not_found : Error< + "%0 type was not found; include before defining " + "a coroutine">; 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 @@ "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< Index: lib/Sema/SemaCoroutine.cpp =================================================================== --- lib/Sema/SemaCoroutine.cpp +++ lib/Sema/SemaCoroutine.cpp @@ -39,14 +39,16 @@ // 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 @@ 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 @@ 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(); + 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. + 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 @@ 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 @@ /// 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 @@ 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 @@ 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 @@ 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(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(First) ? "co_await" : - isa(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(First) ? "co_await" : + isa(First) ? "co_yield" : "co_return"); + } } SubStmtBuilder Builder(*this, *FD, *Fn, Body); if (Builder.isInvalid()) Index: lib/Sema/SemaExprMember.cpp =================================================================== --- lib/Sema/SemaExprMember.cpp +++ lib/Sema/SemaExprMember.cpp @@ -973,7 +973,7 @@ // 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(); Index: test/CodeGenCoroutines/coro-alloc.cpp =================================================================== --- test/CodeGenCoroutines/coro-alloc.cpp +++ test/CodeGenCoroutines/coro-alloc.cpp @@ -4,12 +4,27 @@ namespace experimental { template struct coroutine_traits; // expected-note {{declared here}} + +template +struct coroutine_handle { + coroutine_handle() = default; + static coroutine_handle from_address(void *) { return {}; } +}; + +template <> +struct coroutine_handle { + static coroutine_handle from_address(void *) { return {}; } + coroutine_handle() = default; + template + coroutine_handle(coroutine_handle) {} +}; + } } struct suspend_always { bool await_ready() { return false; } - void await_suspend() {} + void await_suspend(std::experimental::coroutine_handle<>) {} void await_resume() {} }; Index: test/CodeGenCoroutines/coro-return.cpp =================================================================== --- test/CodeGenCoroutines/coro-return.cpp +++ test/CodeGenCoroutines/coro-return.cpp @@ -4,12 +4,27 @@ namespace experimental { template struct coroutine_traits; + +template +struct coroutine_handle { + coroutine_handle() = default; + static coroutine_handle from_address(void *) { return {}; } +}; + +template <> +struct coroutine_handle { + static coroutine_handle from_address(void *) { return {}; } + coroutine_handle() = default; + template + coroutine_handle(coroutine_handle) {} +}; + } } struct suspend_always { bool await_ready(); - void await_suspend(); + void await_suspend(std::experimental::coroutine_handle<>); void await_resume(); }; Index: test/SemaCXX/coreturn.cpp =================================================================== --- test/SemaCXX/coreturn.cpp +++ test/SemaCXX/coreturn.cpp @@ -25,7 +25,9 @@ struct coroutine_traits { using promise_type = typename Ret::promise_type; }; template -struct coroutine_handle {}; +struct coroutine_handle { + static coroutine_handle from_address(void *); +}; } } Index: test/SemaCXX/coroutines.cpp =================================================================== --- test/SemaCXX/coroutines.cpp +++ test/SemaCXX/coroutines.cpp @@ -16,42 +16,42 @@ // 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 }} +} + +namespace std { +namespace experimental { +template +struct coroutine_traits; // expected-note {{declared here}} +}} // namespace std::experimental + +template struct coro {}; +template +struct std::experimental::coroutine_traits, Ps...> { + using promise_type = Promise; +}; struct awaitable { bool await_ready(); - void await_suspend(); // FIXME: coroutine_handle + template void await_suspend(F); void await_resume(); } a; struct suspend_always { bool await_ready() { return false; } - void await_suspend() {} + template void await_suspend(F); +// void await_suspend(std::experimental::coroutine_handle<>) {} void await_resume() {} }; struct suspend_never { bool await_ready() { return true; } - void await_suspend() {} + template void await_suspend(F); +// void await_suspend(std::experimental::coroutine_handle<>) {} void await_resume() {} }; -void no_coroutine_traits() { - co_await a; // expected-error {{need to include }} -} - -namespace std { -namespace experimental { -template -struct coroutine_traits; // expected-note {{declared here}} -} -} - -template struct coro {}; -template -struct std::experimental::coroutine_traits, Ps...> { - using promise_type = Promise; -}; - void no_specialization() { co_await a; // expected-error {{implicit instantiation of undefined template 'std::experimental::coroutine_traits'}} } @@ -86,13 +86,6 @@ struct std::experimental::coroutine_traits { using promise_type = promise_void; }; -namespace std { -namespace experimental { -template -struct coroutine_handle; -} -} - // FIXME: This diagnostic is terrible. void undefined_promise() { // expected-error {{this function cannot be a coroutine: 'experimental::coroutine_traits::promise_type' (aka 'promise') is an incomplete type}} co_await a; @@ -119,6 +112,25 @@ void return_void(); }; +void no_coroutine_handle() { // expected-error {{std::experimental::coroutine_handle type was not found; include 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 +struct coroutine_handle { + static coroutine_handle from_address(void *); +}; +template <> +struct coroutine_handle { + template + coroutine_handle(coroutine_handle); + static coroutine_handle from_address(void *); +}; +}} // namespace std::experimental + void yield() { co_yield 0; co_yield {"foo", 1, 2}; @@ -520,3 +532,19 @@ 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 {}; + +template<> struct std::experimental::coroutine_traits +{ 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}} +}