GorNishanov updated this revision to Diff 90861. GorNishanov added a comment.
Per's @rsmith feedback in Kona - Added diagnostic if from_address is missing from coroutine_handle - Switch to using BuildDeclarationNameExpr in buildCoroutineHandle https://reviews.llvm.org/D26316 Files: include/clang/Basic/DiagnosticSemaKinds.td lib/Sema/SemaCoroutine.cpp lib/Sema/SemaExprMember.cpp test/CodeGenCoroutines/coro-alloc.cpp test/CodeGenCoroutines/coro-return.cpp test/SemaCXX/coroutines.cpp
Index: test/SemaCXX/coroutines.cpp =================================================================== --- test/SemaCXX/coroutines.cpp +++ test/SemaCXX/coroutines.cpp @@ -16,42 +16,52 @@ // expected-error@-1 {{use of undeclared identifier 'a'}} } +void no_coroutine_traits() { + co_await 4; // expected-error {{need to include <experimental/coroutine>}} +} + +namespace std { +namespace experimental { +template <typename... T> +struct coroutine_traits; // expected-note {{declared here}} + +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 + +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 + void await_suspend(std::experimental::coroutine_handle<>); void await_resume(); } a; struct suspend_always { bool await_ready() { return false; } - void await_suspend() {} + void await_suspend(std::experimental::coroutine_handle<>) {} void await_resume() {} }; struct suspend_never { bool await_ready() { return true; } - void await_suspend() {} + void await_suspend(std::experimental::coroutine_handle<>) {} 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>'}} } @@ -86,13 +96,6 @@ 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; @@ -520,3 +523,20 @@ 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}} + //expected-note@+1 {{function is a coroutine due to use of 'co_return' here}} + co_return; +} Index: test/CodeGenCoroutines/coro-return.cpp =================================================================== --- test/CodeGenCoroutines/coro-return.cpp +++ test/CodeGenCoroutines/coro-return.cpp @@ -4,12 +4,27 @@ 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(); }; Index: test/CodeGenCoroutines/coro-alloc.cpp =================================================================== --- test/CodeGenCoroutines/coro-alloc.cpp +++ test/CodeGenCoroutines/coro-alloc.cpp @@ -4,12 +4,27 @@ 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() {} }; 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: lib/Sema/SemaCoroutine.cpp =================================================================== --- lib/Sema/SemaCoroutine.cpp +++ lib/Sema/SemaCoroutine.cpp @@ -116,6 +116,50 @@ 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_std_coroutine_traits_not_found); + 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_traits_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 +281,35 @@ return Call.get(); } +static ExprResult buildCoroutineHandle(Sema &S, QualType PromiseType, + SourceLocation Loc) { + QualType CoroHandleType = lookupCoroutineHandleType(S, PromiseType, Loc); + if (CoroHandleType.isNull()) + return ExprError(); + + if (S.RequireCompleteType(Loc, CoroHandleType, diag::err_incomplete_type)) + 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(); + } + + CXXScopeSpec SS; + ExprResult FromAddr = + S.BuildDeclarationNameExpr(SS, Found, /*NeedsADL=*/false); + if (FromAddr.isInvalid()) + return ExprError(); + + Expr *FramePtr = + buildBuiltinCall(S, Loc, Builtin::BI__builtin_coro_frame, {}); + + 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(); @@ -409,7 +487,7 @@ } ExprResult Sema::BuildUnresolvedCoawaitExpr(SourceLocation Loc, Expr *E, - UnresolvedLookupExpr *Lookup) { + UnresolvedLookupExpr *Lookup) { auto *FSI = checkCoroutineContext(*this, Loc, "co_await"); if (!FSI) return ExprError(); @@ -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<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()) Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -8828,6 +8828,10 @@ "|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_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_std_coroutine_traits_not_found : Error< "you need to include <experimental/coroutine> before defining a coroutine">; def err_malformed_std_coroutine_traits : Error<
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits