Index: include/clang/AST/ExprCXX.h =================================================================== --- include/clang/AST/ExprCXX.h +++ include/clang/AST/ExprCXX.h @@ -4251,6 +4251,49 @@ } }; +/// \brief Represents a 'co_await' expression while the type of the promise +/// is dependent. +class CoawaitDependentExpr : public Expr { + SourceLocation KeywordLoc; + Stmt *Operand; + UnresolvedSet<16> CoawaitOperatorCandidates; + + friend class ASTStmtReader; + +public: + CoawaitDependentExpr(SourceLocation KeywordLoc, QualType Ty, Expr *Op, + UnresolvedSet<16> OperatorCandidates) + : Expr(CoawaitDependentExprClass, Ty, VK_RValue, OK_Ordinary, true, true, + true, Op->containsUnexpandedParameterPack()), + KeywordLoc(KeywordLoc), Operand(Op), + CoawaitOperatorCandidates(OperatorCandidates) { + assert(Op->isTypeDependent() && Ty->isDependentType() && + "wrong constructor for non-dependent co_await/co_yield expression"); + } + + CoawaitDependentExpr(EmptyShell Empty) + : Expr(CoawaitDependentExprClass, Empty) {} + + Expr *getOperand() const { return static_cast(Operand); } + + const UnresolvedSet<16> &getOperatorCandidates() const { + return CoawaitOperatorCandidates; + } + + SourceLocation getKeywordLoc() const { return KeywordLoc; } + + SourceLocation getLocStart() const LLVM_READONLY { return KeywordLoc; } + SourceLocation getLocEnd() const LLVM_READONLY { + return getOperand()->getLocEnd(); + } + + child_range children() { return child_range(&Operand, &Operand + 1); } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == CoawaitDependentExprClass; + } +}; + /// \brief Represents a 'co_yield' expression. class CoyieldExpr : public CoroutineSuspendExpr { friend class ASTStmtReader; Index: include/clang/AST/RecursiveASTVisitor.h =================================================================== --- include/clang/AST/RecursiveASTVisitor.h +++ include/clang/AST/RecursiveASTVisitor.h @@ -2471,6 +2471,12 @@ ShouldVisitChildren = false; } }) +DEF_TRAVERSE_STMT(CoawaitDependentExpr, { + if (!getDerived().shouldVisitImplicitCode()) { + TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getOperand()); + ShouldVisitChildren = false; + } +}) DEF_TRAVERSE_STMT(CoyieldExpr, { if (!getDerived().shouldVisitImplicitCode()) { TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getOperand()); Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -8645,6 +8645,8 @@ "this function cannot be a coroutine: %q0 has no member named 'promise_type'">; 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< "this function cannot be a coroutine: missing definition of " "specialization %q0">; @@ -8655,6 +8657,8 @@ "'std::current_exception' must be a function">; def err_coroutine_promise_return_ill_formed : Error< "%0 declares both 'return_value' and 'return_void'">; +def note_coroutine_promise_implicit_await_transform_required_here : Note< + "call to 'await_transform' implicitly required by 'co_await' here">; } let CategoryName = "Documentation Issue" in { Index: include/clang/Basic/StmtNodes.td =================================================================== --- include/clang/Basic/StmtNodes.td +++ include/clang/Basic/StmtNodes.td @@ -148,6 +148,7 @@ // C++ Coroutines TS expressions def CoroutineSuspendExpr : DStmt; def CoawaitExpr : DStmt; +def CoawaitDependentExpr : DStmt; def CoyieldExpr : DStmt; // Obj-C Expressions. Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -8035,6 +8035,9 @@ StmtResult ActOnCoreturnStmt(SourceLocation KwLoc, Expr *E); ExprResult BuildCoawaitExpr(SourceLocation KwLoc, Expr *E); + ExprResult + BuildCoawaitDependentExpr(SourceLocation KwLoc, Expr *E, + const UnresolvedSet<16> &CoawaitOperatorCandidates); ExprResult BuildCoyieldExpr(SourceLocation KwLoc, Expr *E); StmtResult BuildCoreturnStmt(SourceLocation KwLoc, Expr *E); Index: lib/AST/Expr.cpp =================================================================== --- lib/AST/Expr.cpp +++ lib/AST/Expr.cpp @@ -2897,6 +2897,7 @@ case CXXNewExprClass: case CXXDeleteExprClass: case CoawaitExprClass: + case CoawaitDependentExprClass: case CoyieldExprClass: // These always have a side-effect. return true; Index: lib/AST/ExprClassification.cpp =================================================================== --- lib/AST/ExprClassification.cpp +++ lib/AST/ExprClassification.cpp @@ -188,6 +188,9 @@ case Expr::CXXFoldExprClass: case Expr::NoInitExprClass: case Expr::DesignatedInitUpdateExprClass: + // FIXME How should we classify co_await expressions while they're still + // dependent? + case Expr::CoawaitDependentExprClass: case Expr::CoyieldExprClass: return Cl::CL_PRValue; Index: lib/AST/ExprConstant.cpp =================================================================== --- lib/AST/ExprConstant.cpp +++ lib/AST/ExprConstant.cpp @@ -9442,6 +9442,7 @@ case Expr::LambdaExprClass: case Expr::CXXFoldExprClass: case Expr::CoawaitExprClass: + case Expr::CoawaitDependentExprClass: case Expr::CoyieldExprClass: return ICEDiag(IK_NotICE, E->getLocStart()); Index: lib/AST/ItaniumMangle.cpp =================================================================== --- lib/AST/ItaniumMangle.cpp +++ lib/AST/ItaniumMangle.cpp @@ -3281,6 +3281,8 @@ // These all can only appear in local or variable-initialization // contexts and so should never appear in a mangling. case Expr::AddrLabelExprClass: + // This should no longer exist in the AST by now + case Expr::CoawaitDependentExprClass: case Expr::DesignatedInitUpdateExprClass: case Expr::ImplicitValueInitExprClass: case Expr::NoInitExprClass: Index: lib/AST/StmtPrinter.cpp =================================================================== --- lib/AST/StmtPrinter.cpp +++ lib/AST/StmtPrinter.cpp @@ -2422,6 +2422,11 @@ PrintExpr(S->getOperand()); } +void StmtPrinter::VisitCoawaitDependentExpr(CoawaitDependentExpr *S) { + OS << "co_await "; + PrintExpr(S->getOperand()); +} + void StmtPrinter::VisitCoyieldExpr(CoyieldExpr *S) { OS << "co_yield "; PrintExpr(S->getOperand()); Index: lib/AST/StmtProfile.cpp =================================================================== --- lib/AST/StmtProfile.cpp +++ lib/AST/StmtProfile.cpp @@ -1552,6 +1552,10 @@ VisitExpr(S); } +void StmtProfiler::VisitCoawaitDependentExpr(const CoawaitDependentExpr *S) { + VisitExpr(S); +} + void StmtProfiler::VisitCoyieldExpr(const CoyieldExpr *S) { VisitExpr(S); } Index: lib/Sema/SemaCoroutine.cpp =================================================================== --- lib/Sema/SemaCoroutine.cpp +++ lib/Sema/SemaCoroutine.cpp @@ -21,21 +21,32 @@ using namespace clang; using namespace sema; +static bool lookupMember(Sema &S, const char *Name, CXXRecordDecl *RD, + SourceLocation Loc) { + DeclarationName DN = S.PP.getIdentifierInfo(Name); + LookupResult LR(S, DN, Loc, Sema::LookupMemberName); + // Suppress diagnostics when a private member is selected. The same warnings + // will be produced again when building the call. + LR.suppressDiagnostics(); + return S.LookupQualifiedName(LR, RD); +} + /// Look up the std::coroutine_traits<...>::promise_type for the given /// function type. static QualType lookupPromiseType(Sema &S, const FunctionProtoType *FnType, - SourceLocation Loc) { + SourceLocation KwLoc, + SourceLocation FuncLoc) { // FIXME: Cache std::coroutine_traits once we've found it. NamespaceDecl *StdExp = S.lookupStdExperimentalNamespace(); if (!StdExp) { - S.Diag(Loc, diag::err_implied_std_coroutine_traits_not_found); + S.Diag(KwLoc, diag::err_implied_std_coroutine_traits_not_found); return QualType(); } LookupResult Result(S, &S.PP.getIdentifierTable().get("coroutine_traits"), - Loc, Sema::LookupOrdinaryName); + FuncLoc, Sema::LookupOrdinaryName); if (!S.LookupQualifiedName(Result, StdExp)) { - S.Diag(Loc, diag::err_implied_std_coroutine_traits_not_found); + S.Diag(KwLoc, diag::err_implied_std_coroutine_traits_not_found); return QualType(); } @@ -49,22 +60,22 @@ } // Form template argument list for coroutine_traits. - TemplateArgumentListInfo Args(Loc, Loc); + TemplateArgumentListInfo Args(KwLoc, KwLoc); Args.addArgument(TemplateArgumentLoc( TemplateArgument(FnType->getReturnType()), - S.Context.getTrivialTypeSourceInfo(FnType->getReturnType(), Loc))); + S.Context.getTrivialTypeSourceInfo(FnType->getReturnType(), KwLoc))); // FIXME: If the function is a non-static member function, add the type // of the implicit object parameter before the formal parameters. for (QualType T : FnType->getParamTypes()) Args.addArgument(TemplateArgumentLoc( - TemplateArgument(T), S.Context.getTrivialTypeSourceInfo(T, Loc))); + TemplateArgument(T), S.Context.getTrivialTypeSourceInfo(T, KwLoc))); // Build the template-id. QualType CoroTrait = - S.CheckTemplateIdType(TemplateName(CoroTraits), Loc, Args); + S.CheckTemplateIdType(TemplateName(CoroTraits), KwLoc, Args); if (CoroTrait.isNull()) return QualType(); - if (S.RequireCompleteType(Loc, CoroTrait, + if (S.RequireCompleteType(KwLoc, CoroTrait, diag::err_coroutine_traits_missing_specialization)) return QualType(); @@ -72,29 +83,36 @@ assert(RD && "specialization of class template is not a class?"); // Look up the ::promise_type member. - LookupResult R(S, &S.PP.getIdentifierTable().get("promise_type"), Loc, + LookupResult R(S, &S.PP.getIdentifierTable().get("promise_type"), KwLoc, Sema::LookupOrdinaryName); S.LookupQualifiedName(R, RD); auto *Promise = R.getAsSingle(); if (!Promise) { - S.Diag(Loc, diag::err_implied_std_coroutine_traits_promise_type_not_found) + S.Diag(FuncLoc, + diag::err_implied_std_coroutine_traits_promise_type_not_found) << RD; return QualType(); } - // The promise type is required to be a class type. QualType PromiseType = S.Context.getTypeDeclType(Promise); - if (!PromiseType->getAsCXXRecordDecl()) { - // Use the fully-qualified name of the type. + + auto buildNNS = [&]() { auto *NNS = NestedNameSpecifier::Create(S.Context, nullptr, StdExp); NNS = NestedNameSpecifier::Create(S.Context, NNS, false, CoroTrait.getTypePtr()); - PromiseType = S.Context.getElaboratedType(ETK_None, NNS, PromiseType); + return S.Context.getElaboratedType(ETK_None, NNS, PromiseType); + }; - S.Diag(Loc, diag::err_implied_std_coroutine_traits_promise_type_not_class) - << PromiseType; + RD = PromiseType->getAsCXXRecordDecl(); + if (!RD) { + S.Diag(FuncLoc, + diag::err_implied_std_coroutine_traits_promise_type_not_class) + << buildNNS(); return QualType(); } + if (S.RequireCompleteType(FuncLoc, buildNNS(), + diag::err_coroutine_promise_type_incomplete)) + return QualType(); return PromiseType; } @@ -173,10 +191,11 @@ // If we don't have a promise variable, build one now. if (!ScopeInfo->CoroutinePromise) { - QualType T = FD->getType()->isDependentType() - ? S.Context.DependentTy - : lookupPromiseType( - S, FD->getType()->castAs(), Loc); + QualType T = + FD->getType()->isDependentType() + ? S.Context.DependentTy + : lookupPromiseType(S, FD->getType()->castAs(), + Loc, FD->getLocation()); if (T.isNull()) return nullptr; @@ -215,11 +234,20 @@ /// Build a call to 'operator co_await' if there is a suitable operator for /// the given expression. -static ExprResult buildOperatorCoawaitCall(Sema &SemaRef, Scope *S, - SourceLocation Loc, Expr *E) { +static UnresolvedSet<16> lookupOperatorCoawaitCall(Sema &SemaRef, Scope *S, + SourceLocation Loc, + Expr *E) { UnresolvedSet<16> Functions; SemaRef.LookupOverloadedOperatorName(OO_Coawait, S, E->getType(), QualType(), Functions); + return Functions; +} + +/// Build a call to 'operator co_await' if there is a suitable operator for +/// the given expression. +static ExprResult buildOperatorCoawaitCall(Sema &SemaRef, SourceLocation Loc, + Expr *E, + const UnresolvedSet<16> &Functions) { return SemaRef.CreateOverloadedUnaryOp(Loc, UO_Coawait, Functions, E); } @@ -268,6 +296,22 @@ return Calls; } +static ExprResult buildPromiseCall(Sema &S, FunctionScopeInfo *Coroutine, + SourceLocation Loc, StringRef Name, + MutableArrayRef Args) { + assert(Coroutine->CoroutinePromise && "no promise for coroutine"); + + // Form a reference to the promise. + auto *Promise = Coroutine->CoroutinePromise; + ExprResult PromiseRef = S.BuildDeclRefExpr( + Promise, Promise->getType().getNonReferenceType(), VK_LValue, Loc); + if (PromiseRef.isInvalid()) + return ExprError(); + + // Call 'yield_value', passing in E. + return buildMemberCall(S, PromiseRef.get(), Loc, Name, Args); +} + ExprResult Sema::ActOnCoawaitExpr(Scope *S, SourceLocation Loc, Expr *E) { auto *Coroutine = checkCoroutineContext(*this, Loc, "co_await"); if (!Coroutine) { @@ -279,13 +323,51 @@ if (R.isInvalid()) return ExprError(); E = R.get(); } + return BuildCoawaitDependentExpr(Loc, E, + lookupOperatorCoawaitCall(*this, S, Loc, E)); +} + +ExprResult +Sema::BuildCoawaitDependentExpr(SourceLocation Loc, Expr *E, + const UnresolvedSet<16> &Candidates) { + auto *Coroutine = checkCoroutineContext(*this, Loc, "co_await"); + if (!Coroutine) + return ExprError(); - ExprResult Awaitable = buildOperatorCoawaitCall(*this, S, Loc, E); + if (E->getType()->isPlaceholderType()) { + ExprResult R = CheckPlaceholderExpr(E); + if (R.isInvalid()) + return ExprError(); + E = R.get(); + } + + auto *Promise = Coroutine->CoroutinePromise; + if (Promise->getType()->isDependentType()) { + Expr *Res = new (Context) + CoawaitDependentExpr(Loc, Context.DependentTy, E, Candidates); + Coroutine->CoroutineStmts.push_back(Res); + return Res; + } + + CXXRecordDecl *RD = Promise->getType()->getAsCXXRecordDecl(); + if (lookupMember(*this, "await_transform", RD, Loc)) { + ExprResult R = + buildPromiseCall(*this, Coroutine, Loc, "await_transform", E); + if (R.isInvalid()) { + Diag(Loc, + diag::note_coroutine_promise_implicit_await_transform_required_here) + << E->getSourceRange(); + return ExprError(); + } + E = R.get(); + } + ExprResult Awaitable = buildOperatorCoawaitCall(*this, Loc, E, Candidates); if (Awaitable.isInvalid()) return ExprError(); return BuildCoawaitExpr(Loc, Awaitable.get()); } + ExprResult Sema::BuildCoawaitExpr(SourceLocation Loc, Expr *E) { auto *Coroutine = checkCoroutineContext(*this, Loc, "co_await"); if (!Coroutine) @@ -319,22 +401,6 @@ return Res; } -static ExprResult buildPromiseCall(Sema &S, FunctionScopeInfo *Coroutine, - SourceLocation Loc, StringRef Name, - MutableArrayRef Args) { - assert(Coroutine->CoroutinePromise && "no promise for coroutine"); - - // Form a reference to the promise. - auto *Promise = Coroutine->CoroutinePromise; - ExprResult PromiseRef = S.BuildDeclRefExpr( - Promise, Promise->getType().getNonReferenceType(), VK_LValue, Loc); - if (PromiseRef.isInvalid()) - return ExprError(); - - // Call 'yield_value', passing in E. - return buildMemberCall(S, PromiseRef.get(), Loc, Name, Args); -} - ExprResult Sema::ActOnCoyieldExpr(Scope *S, SourceLocation Loc, Expr *E) { auto *Coroutine = checkCoroutineContext(*this, Loc, "co_yield"); if (!Coroutine) { @@ -349,7 +415,8 @@ return ExprError(); // Build 'operator co_await' call. - Awaitable = buildOperatorCoawaitCall(*this, S, Loc, Awaitable.get()); + auto Functions = lookupOperatorCoawaitCall(*this, S, Loc, Awaitable.get()); + Awaitable = buildOperatorCoawaitCall(*this, Loc, Awaitable.get(), Functions); if (Awaitable.isInvalid()) return ExprError(); @@ -579,16 +646,22 @@ } bool AnyCoawaits = false; + bool AnyDependentCoawaits = false; bool AnyCoyields = false; for (auto *CoroutineStmt : Fn->CoroutineStmts) { AnyCoawaits |= isa(CoroutineStmt); + AnyDependentCoawaits |= isa(CoroutineStmt); AnyCoyields |= isa(CoroutineStmt); } - if (!AnyCoawaits && !AnyCoyields) + if (!AnyCoawaits && !AnyCoyields && !AnyDependentCoawaits) Diag(Fn->CoroutineStmts.front()->getLocStart(), diag::ext_coroutine_without_co_await_co_yield); + assert((!AnyDependentCoawaits || + Fn->CoroutinePromise->getType()->isDependentType()) && + "All dependent coawait expressions should already be resolved"); + SourceLocation Loc = FD->getLocation(); // Form a declaration statement for the promise declaration, so that AST @@ -633,17 +706,12 @@ !Fn->CoroutinePromise->getType()->isDependentType()) { CXXRecordDecl *RD = Fn->CoroutinePromise->getType()->getAsCXXRecordDecl(); assert(RD && "Type should have already been checked"); + // [dcl.fct.def.coroutine]/4 // The unqualified-ids 'return_void' and 'return_value' are looked up in // the scope of class P. If both are found, the program is ill-formed. - DeclarationName RVoidDN = PP.getIdentifierInfo("return_void"); - LookupResult RVoidResult(*this, RVoidDN, Loc, Sema::LookupMemberName); - const bool HasRVoid = LookupQualifiedName(RVoidResult, RD); - - DeclarationName RValueDN = PP.getIdentifierInfo("return_value"); - LookupResult RValueResult(*this, RValueDN, Loc, Sema::LookupMemberName); - const bool HasRValue = LookupQualifiedName(RValueResult, RD); - + const bool HasRVoid = lookupMember(*this, "return_void", RD, Loc); + const bool HasRValue = lookupMember(*this, "return_value", RD, Loc); if (HasRVoid && HasRValue) { // FIXME Improve this diagnostic Diag(FD->getLocation(), diag::err_coroutine_promise_return_ill_formed) @@ -662,9 +730,8 @@ // [dcl.fct.def.coroutine]/3 // The unqualified-id set_exception is found in the scope of P by class // member access lookup (3.4.5). - DeclarationName SetExDN = PP.getIdentifierInfo("set_exception"); - LookupResult SetExResult(*this, SetExDN, Loc, Sema::LookupMemberName); - if (LookupQualifiedName(SetExResult, RD)) { + + if (lookupMember(*this, "set_exception", RD, Loc)) { // Form the call 'p.set_exception(std::current_exception())' SetException = buildStdCurrentExceptionCall(*this, Loc); if (SetException.isInvalid()) Index: lib/Sema/SemaExceptionSpec.cpp =================================================================== --- lib/Sema/SemaExceptionSpec.cpp +++ lib/Sema/SemaExceptionSpec.cpp @@ -1146,6 +1146,7 @@ case Expr::ArraySubscriptExprClass: case Expr::OMPArraySectionExprClass: case Expr::BinaryOperatorClass: + case Expr::CoawaitDependentExprClass: case Expr::CompoundAssignOperatorClass: case Expr::CStyleCastExprClass: case Expr::CXXStaticCastExprClass: Index: lib/Sema/TreeTransform.h =================================================================== --- lib/Sema/TreeTransform.h +++ lib/Sema/TreeTransform.h @@ -1318,6 +1318,16 @@ return getSema().BuildCoawaitExpr(CoawaitLoc, Result); } + /// \brief Build a new co_await expression. + /// + /// By default, performs semantic analysis to build the new expression. + /// Subclasses may override this routine to provide different behavior. + ExprResult RebuildCoawaitDependentExpr(SourceLocation CoawaitLoc, + Expr *Result, + const UnresolvedSet<16> &Candidates) { + return getSema().BuildCoawaitDependentExpr(CoawaitLoc, Result, Candidates); + } + /// \brief Build a new co_yield expression. /// /// By default, performs semantic analysis to build the new expression. @@ -6684,9 +6694,22 @@ return getDerived().RebuildCoawaitExpr(E->getKeywordLoc(), Result.get()); } -template +template ExprResult -TreeTransform::TransformCoyieldExpr(CoyieldExpr *E) { +TreeTransform::TransformCoawaitDependentExpr(CoawaitDependentExpr *E) { + ExprResult Result = getDerived().TransformInitializer(E->getOperand(), + /*NotCopyInit*/ false); + if (Result.isInvalid()) + return ExprError(); + + // Always rebuild; we don't know if this needs to be injected into a new + // context or if the promise type has changed. + return getDerived().RebuildCoawaitDependentExpr( + E->getKeywordLoc(), Result.get(), E->getOperatorCandidates()); +} + +template +ExprResult TreeTransform::TransformCoyieldExpr(CoyieldExpr *E) { ExprResult Result = getDerived().TransformInitializer(E->getOperand(), /*NotCopyInit*/false); if (Result.isInvalid()) Index: lib/Serialization/ASTReaderStmt.cpp =================================================================== --- lib/Serialization/ASTReaderStmt.cpp +++ lib/Serialization/ASTReaderStmt.cpp @@ -400,6 +400,11 @@ llvm_unreachable("unimplemented"); } +void ASTStmtReader::VisitCoawaitDependentExpr(CoawaitDependentExpr *S) { + // FIXME: Implement coroutine serialization. + llvm_unreachable("unimplemented"); +} + void ASTStmtReader::VisitCoyieldExpr(CoyieldExpr *S) { // FIXME: Implement coroutine serialization. llvm_unreachable("unimplemented"); Index: lib/Serialization/ASTWriterStmt.cpp =================================================================== --- lib/Serialization/ASTWriterStmt.cpp +++ lib/Serialization/ASTWriterStmt.cpp @@ -315,6 +315,11 @@ llvm_unreachable("unimplemented"); } +void ASTStmtWriter::VisitCoawaitDependentExpr(CoawaitDependentExpr *S) { + // FIXME: Implement coroutine serialization. + llvm_unreachable("unimplemented"); +} + void ASTStmtWriter::VisitCoyieldExpr(CoyieldExpr *S) { // FIXME: Implement coroutine serialization. llvm_unreachable("unimplemented"); Index: lib/StaticAnalyzer/Core/ExprEngine.cpp =================================================================== --- lib/StaticAnalyzer/Core/ExprEngine.cpp +++ lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -774,6 +774,7 @@ case Stmt::FunctionParmPackExprClass: case Stmt::CoroutineBodyStmtClass: case Stmt::CoawaitExprClass: + case Stmt::CoawaitDependentExprClass: case Stmt::CoreturnStmtClass: case Stmt::CoyieldExprClass: case Stmt::SEHTryStmtClass: Index: test/SemaCXX/coroutines.cpp =================================================================== --- test/SemaCXX/coroutines.cpp +++ test/SemaCXX/coroutines.cpp @@ -59,14 +59,14 @@ template struct std::experimental::coroutine_traits {}; -int no_promise_type() { - co_await a; // expected-error {{this function cannot be a coroutine: 'std::experimental::coroutine_traits' has no member named 'promise_type'}} +int no_promise_type() { // expected-error {{this function cannot be a coroutine: 'std::experimental::coroutine_traits' has no member named 'promise_type'}} + co_await a; } template <> struct std::experimental::coroutine_traits { typedef int promise_type; }; -double bad_promise_type(double) { - co_await a; // expected-error {{this function cannot be a coroutine: 'experimental::coroutine_traits::promise_type' (aka 'int') is not a class}} +double bad_promise_type(double) { // expected-error {{this function cannot be a coroutine: 'experimental::coroutine_traits::promise_type' (aka 'int') is not a class}} + co_await a; } template <> @@ -77,7 +77,7 @@ co_yield 0; // expected-error {{no member named 'yield_value' in 'std::experimental::coroutine_traits::promise_type'}} } -struct promise; // expected-note 2{{forward declaration}} +struct promise; // expected-note {{forward declaration}} struct promise_void; struct void_tag {}; template @@ -94,9 +94,7 @@ } // FIXME: This diagnostic is terrible. -void undefined_promise() { // expected-error {{variable has incomplete type 'promise_type'}} - // FIXME: This diagnostic doesn't make any sense. - // expected-error@-2 {{incomplete definition of type 'promise'}} +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; } @@ -239,6 +237,21 @@ }; template void await_template(outer); // expected-note {{instantiation}} template void await_template_2(outer); + + template coro await_template_3(U t) { + co_await t; + } + struct transform_awaitable {}; + struct transformed {}; + struct transform_promise { + coro get_return_object(); + suspend_always initial_suspend(); + suspend_always final_suspend(); + transformed await_transform(transform_awaitable); + }; + void operator co_await(transform_awaitable) = delete; + awaitable operator co_await(transformed); + template coro await_template_3(transform_awaitable); } struct yield_fn_tag {}; @@ -355,20 +368,68 @@ int *current_exception(); } -struct bad_promise_8 { +struct bad_promise_base { +private: + void return_void(); // expected-note {{declared private here}} +}; +struct bad_promise_8 : bad_promise_base { coro get_return_object(); suspend_always initial_suspend(); suspend_always final_suspend(); - void return_void(); void set_exception(); // expected-note {{function not viable}} void set_exception(int *) __attribute__((unavailable)); // expected-note {{explicitly made unavailable}} void set_exception(void *); // expected-note {{candidate function}} }; coro calls_set_exception() { // expected-error@-1 {{call to unavailable member function 'set_exception'}} + // expected-error@-2 {{'return_void' is a private member of 'bad_promise_base'}} co_await a; } +struct bad_promise_9 { + coro get_return_object(); + suspend_always initial_suspend(); + suspend_always final_suspend(); + void await_transform(void *); // expected-note {{candidate}} + awaitable await_transform(int) __attribute__((unavailable)); // expected-note {{explicitly made unavailable}} + void return_void(); +}; +coro calls_await_transform() { + co_await 42; // expected-error {{call to unavailable member function 'await_transform'}} + // expected-note@-1 {{call to 'await_transform' implicitly required by 'co_await' here}} +} + +struct bad_promise_10 { + coro get_return_object(); + suspend_always initial_suspend(); + suspend_always final_suspend(); + int await_transform; + void return_void(); +}; +coro bad_coawait() { + // FIXME this diagnostic is terrible + co_await 42; // expected-error {{called object type 'int' is not a function or function pointer}} + // expected-note@-1 {{call to 'await_transform' implicitly required by 'co_await' here}} +} + +struct call_operator { + template awaitable operator()(Args...) const { return a; } +}; +void ret_void(); +struct good_promise_1 { + coro get_return_object(); + suspend_always initial_suspend(); + suspend_always final_suspend(); + static const call_operator await_transform; + using Fn = void(*)(); + Fn return_void = ret_void; +}; +const call_operator good_promise_1::await_transform; +coro ok_static_coawait() { + // FIXME this diagnostic is terrible + co_await 42; +} + template<> struct std::experimental::coroutine_traits { using promise_type = promise; }; Index: tools/libclang/CXCursor.cpp =================================================================== --- tools/libclang/CXCursor.cpp +++ tools/libclang/CXCursor.cpp @@ -231,6 +231,7 @@ case Stmt::TypeTraitExprClass: case Stmt::CoroutineBodyStmtClass: case Stmt::CoawaitExprClass: + case Stmt::CoawaitDependentExprClass: case Stmt::CoreturnStmtClass: case Stmt::CoyieldExprClass: case Stmt::CXXBindTemporaryExprClass: