From 708f13bd1a8b4b7e44ae7030068b97efdd8668f8 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Tue, 27 Oct 2015 06:02:45 +0000 Subject: [PATCH] [coroutines] Creation of promise object, lookup of operator co_await, building of await_* calls, and AST representation for same. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@251387 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/DataRecursiveASTVisitor.h | 8 +- include/clang/AST/ExprCXX.h | 123 +++++++++++ include/clang/AST/OperationKinds.h | 3 +- include/clang/AST/RecursiveASTVisitor.h | 30 ++- include/clang/AST/StmtCXX.h | 100 ++++++++- include/clang/AST/StmtVisitor.h | 3 +- include/clang/Basic/DiagnosticSemaKinds.td | 19 +- include/clang/Basic/StmtNodes.td | 8 + include/clang/Sema/ScopeInfo.h | 26 ++- include/clang/Sema/Sema.h | 21 +- lib/AST/ASTImporter.cpp | 5 +- lib/AST/Expr.cpp | 8 + lib/AST/ExprClassification.cpp | 4 + lib/AST/ExprConstant.cpp | 3 + lib/AST/ItaniumMangle.cpp | 12 + lib/AST/StmtCXX.cpp | 6 +- lib/AST/StmtPrinter.cpp | 25 +++ lib/AST/StmtProfile.cpp | 16 ++ lib/Analysis/ThreadSafetyCommon.cpp | 1 + lib/CodeGen/CGStmt.cpp | 4 + lib/Parse/ParseExpr.cpp | 2 +- lib/Parse/ParseExprCXX.cpp | 2 +- lib/Parse/ParseStmt.cpp | 11 +- lib/Sema/ScopeInfo.cpp | 2 + lib/Sema/SemaCoroutine.cpp | 231 ++++++++++++++++++-- lib/Sema/SemaExceptionSpec.cpp | 2 + lib/Sema/SemaExpr.cpp | 1 + lib/Sema/SemaOverload.cpp | 20 +- lib/Sema/SemaStmt.cpp | 83 ++++--- lib/Sema/TreeTransform.h | 78 ++++++- lib/Serialization/ASTReaderStmt.cpp | 27 ++- lib/Serialization/ASTWriterStmt.cpp | 21 ++ lib/StaticAnalyzer/Core/ExprEngine.cpp | 4 + test/Parser/cxx1z-coroutines.cpp | 2 +- test/SemaCXX/coroutines.cpp | 88 ++++++-- tools/libclang/CXCursor.cpp | 4 + 36 files changed, 871 insertions(+), 132 deletions(-) diff --git a/include/clang/AST/DataRecursiveASTVisitor.h b/include/clang/AST/DataRecursiveASTVisitor.h index 46dcb6ca7b..25e2c069ba 100644 --- a/include/clang/AST/DataRecursiveASTVisitor.h +++ b/include/clang/AST/DataRecursiveASTVisitor.h @@ -43,7 +43,7 @@ OPERATOR(PostInc) OPERATOR(PostDec) OPERATOR(PreInc) OPERATOR(PreDec) \ OPERATOR(AddrOf) OPERATOR(Deref) OPERATOR(Plus) OPERATOR(Minus) \ OPERATOR(Not) OPERATOR(LNot) OPERATOR(Real) OPERATOR(Imag) \ - OPERATOR(Extension) + OPERATOR(Extension) OPERATOR(Coawait) // All binary operators (excluding compound assign operators). #define BINOP_LIST() \ @@ -2306,6 +2306,12 @@ DEF_TRAVERSE_STMT(MaterializeTemporaryExpr, {}) DEF_TRAVERSE_STMT(CXXFoldExpr, {}) DEF_TRAVERSE_STMT(AtomicExpr, {}) +// Coroutine support. +DEF_TRAVERSE_STMT(CoroutineBodyStmt, {}) +DEF_TRAVERSE_STMT(CoreturnStmt, {}) +DEF_TRAVERSE_STMT(CoawaitExpr, {}) +DEF_TRAVERSE_STMT(CoyieldExpr, {}) + // These literals (all of them) do not need any action. DEF_TRAVERSE_STMT(IntegerLiteral, {}) DEF_TRAVERSE_STMT(CharacterLiteral, {}) diff --git a/include/clang/AST/ExprCXX.h b/include/clang/AST/ExprCXX.h index e8493f1933..055542650f 100644 --- a/include/clang/AST/ExprCXX.h +++ b/include/clang/AST/ExprCXX.h @@ -4008,6 +4008,129 @@ public: child_range children() { return child_range(SubExprs, SubExprs + 2); } }; +/// \brief Represents a 'co_await' expression. This expression checks whether its +/// operand is ready, and suspends the coroutine if not. Then (after the resume +/// if suspended) it resumes the coroutine and extracts the value from the +/// operand. This implies making four calls: +/// +/// .operator co_await() or operator co_await() +/// .await_ready() +/// .await_suspend(h) +/// .await_resume() +/// +/// where h is a handle to the coroutine, and is the result of calling +/// operator co_await() if it exists or the original operand otherwise. +/// +/// Note that the coroutine is prepared for suspension before the 'await_suspend' +/// call, but resumes after that call, which may cause parts of the +/// 'await_suspend' expression to occur much later than expected. +class CoawaitExpr : public Expr { + SourceLocation CoawaitLoc; + + enum SubExpr { Operand, Ready, Suspend, Resume, Count }; + Stmt *SubExprs[SubExpr::Count]; + + friend class ASTStmtReader; +public: + CoawaitExpr(SourceLocation CoawaitLoc, Expr *Operand, Expr *Ready, + Expr *Suspend, Expr *Resume) + : Expr(CoawaitExprClass, Resume->getType(), Resume->getValueKind(), + Resume->getObjectKind(), + Resume->isTypeDependent(), + Resume->isValueDependent(), + Operand->isInstantiationDependent(), + Operand->containsUnexpandedParameterPack()), + CoawaitLoc(CoawaitLoc), + SubExprs{Operand, Ready, Suspend, Resume} {} + CoawaitExpr(SourceLocation CoawaitLoc, QualType Ty, Expr *Operand) + : Expr(CoawaitExprClass, Ty, VK_RValue, OK_Ordinary, + true, true, true, Operand->containsUnexpandedParameterPack()), + CoawaitLoc(CoawaitLoc), SubExprs{Operand} { + assert(Operand->isTypeDependent() && Ty->isDependentType() && + "wrong constructor for non-dependent co_await expression"); + } + CoawaitExpr(EmptyShell Empty) : Expr(CoawaitExprClass, Empty) {} + + SourceLocation getKeywordLoc() const { return CoawaitLoc; } + Expr *getOperand() const { + return static_cast(SubExprs[SubExpr::Operand]); + } + + Expr *getReadyExpr() const { + return static_cast(SubExprs[SubExpr::Ready]); + } + Expr *getSuspendExpr() const { + return static_cast(SubExprs[SubExpr::Suspend]); + } + Expr *getResumeExpr() const { + return static_cast(SubExprs[SubExpr::Resume]); + } + + SourceLocation getLocStart() const LLVM_READONLY { + return CoawaitLoc; + } + SourceLocation getLocEnd() const LLVM_READONLY { + return getOperand()->getLocEnd(); + } + + child_range children() { + return child_range(SubExprs, SubExprs + SubExpr::Count); + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == CoawaitExprClass; + } +}; + +/// \brief Represents a 'co_yield' expression. This expression provides a value +/// to the coroutine promise and optionally suspends the coroutine. This implies +/// a making call to .yield_value(), which we name the "promise +/// call". +class CoyieldExpr : public Expr { + SourceLocation CoyieldLoc; + + /// The operand of the 'co_yield' expression. + Stmt *Operand; + /// The implied call to the promise object. May be null if the + /// coroutine has not yet been finalized. + Stmt *PromiseCall; + + friend class ASTStmtReader; +public: + CoyieldExpr(SourceLocation CoyieldLoc, QualType Void, Expr *Operand) + : Expr(CoyieldExprClass, Void, VK_RValue, OK_Ordinary, false, false, + Operand->isInstantiationDependent(), + Operand->containsUnexpandedParameterPack()), + CoyieldLoc(CoyieldLoc), Operand(Operand), PromiseCall(nullptr) {} + CoyieldExpr(EmptyShell Empty) : Expr(CoyieldExprClass, Empty) {} + + SourceLocation getKeywordLoc() const { return CoyieldLoc; } + Expr *getOperand() const { return static_cast(Operand); } + + /// \brief Get the call to the promise objet that is implied by an evaluation + /// of this expression. Will be nullptr if the coroutine has not yet been + /// finalized. + Expr *getPromiseCall() const { return static_cast(PromiseCall); } + + /// \brief Set the resolved promise call. This is delayed until the + /// complete coroutine body has been parsed and the promise type is known. + void finalize(Stmt *PC) { PromiseCall = PC; } + + SourceLocation getLocStart() const LLVM_READONLY { return CoyieldLoc; } + SourceLocation getLocEnd() const LLVM_READONLY { + return Operand->getLocEnd(); + } + + child_range children() { + Stmt **Which = PromiseCall ? &PromiseCall : &Operand; + return child_range(Which, Which + 1); + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == CoyieldExprClass; + } +}; + } // end namespace clang #endif diff --git a/include/clang/AST/OperationKinds.h b/include/clang/AST/OperationKinds.h index e3f0126677..2235c1012f 100644 --- a/include/clang/AST/OperationKinds.h +++ b/include/clang/AST/OperationKinds.h @@ -334,7 +334,8 @@ enum UnaryOperatorKind { UO_Plus, UO_Minus, // [C99 6.5.3.3] Unary arithmetic UO_Not, UO_LNot, // [C99 6.5.3.3] Unary arithmetic UO_Real, UO_Imag, // "__real expr"/"__imag expr" Extension. - UO_Extension // __extension__ marker. + UO_Extension, // __extension__ marker. + UO_Coawait // [C++ Coroutines] co_await operator }; /// \brief The kind of bridging performed by the Objective-C bridge cast. diff --git a/include/clang/AST/RecursiveASTVisitor.h b/include/clang/AST/RecursiveASTVisitor.h index 2fabfa988e..341acd99e0 100644 --- a/include/clang/AST/RecursiveASTVisitor.h +++ b/include/clang/AST/RecursiveASTVisitor.h @@ -43,7 +43,7 @@ OPERATOR(PostInc) OPERATOR(PostDec) OPERATOR(PreInc) OPERATOR(PreDec) \ OPERATOR(AddrOf) OPERATOR(Deref) OPERATOR(Plus) OPERATOR(Minus) \ OPERATOR(Not) OPERATOR(LNot) OPERATOR(Real) OPERATOR(Imag) \ - OPERATOR(Extension) + OPERATOR(Extension) OPERATOR(Coawait) // All binary operators (excluding compound assign operators). #define BINOP_LIST() \ @@ -2346,6 +2346,34 @@ DEF_TRAVERSE_STMT(MaterializeTemporaryExpr, {}) DEF_TRAVERSE_STMT(CXXFoldExpr, {}) DEF_TRAVERSE_STMT(AtomicExpr, {}) +// For coroutines expressions, traverse either the operand +// as written or the implied calls, depending on what the +// derived class requests. +DEF_TRAVERSE_STMT(CoroutineBodyStmt, { + if (!getDerived().shouldVisitImplicitCode()) { + TRY_TO(TraverseStmt(S->getBody())); + return true; + } +}) +DEF_TRAVERSE_STMT(CoreturnStmt, { + if (!getDerived().shouldVisitImplicitCode()) { + TRY_TO(TraverseStmt(S->getOperand())); + return true; + } +}) +DEF_TRAVERSE_STMT(CoawaitExpr, { + if (!getDerived().shouldVisitImplicitCode()) { + TRY_TO(TraverseStmt(S->getOperand())); + return true; + } +}) +DEF_TRAVERSE_STMT(CoyieldExpr, { + if (!getDerived().shouldVisitImplicitCode()) { + TRY_TO(TraverseStmt(S->getOperand())); + return true; + } +}) + // These literals (all of them) do not need any action. DEF_TRAVERSE_STMT(IntegerLiteral, {}) DEF_TRAVERSE_STMT(CharacterLiteral, {}) diff --git a/include/clang/AST/StmtCXX.h b/include/clang/AST/StmtCXX.h index 567a772884..1dfc74425e 100644 --- a/include/clang/AST/StmtCXX.h +++ b/include/clang/AST/StmtCXX.h @@ -131,12 +131,16 @@ class CXXForRangeStmt : public Stmt { // SubExprs[RANGE] is an expression or declstmt. // SubExprs[COND] and SubExprs[INC] are expressions. Stmt *SubExprs[END]; + SourceLocation CoawaitLoc; SourceLocation ColonLoc; SourceLocation RParenLoc; + + friend class ASTStmtReader; public: CXXForRangeStmt(DeclStmt *Range, DeclStmt *BeginEnd, Expr *Cond, Expr *Inc, DeclStmt *LoopVar, Stmt *Body, - SourceLocation FL, SourceLocation CL, SourceLocation RPL); + SourceLocation FL, SourceLocation CAL, SourceLocation CL, + SourceLocation RPL); CXXForRangeStmt(EmptyShell Empty) : Stmt(CXXForRangeStmtClass, Empty) { } @@ -181,13 +185,10 @@ public: void setLoopVarStmt(Stmt *S) { SubExprs[LOOPVAR] = S; } void setBody(Stmt *S) { SubExprs[BODY] = S; } - SourceLocation getForLoc() const { return ForLoc; } - void setForLoc(SourceLocation Loc) { ForLoc = Loc; } + SourceLocation getCoawaitLoc() const { return CoawaitLoc; } SourceLocation getColonLoc() const { return ColonLoc; } - void setColonLoc(SourceLocation Loc) { ColonLoc = Loc; } SourceLocation getRParenLoc() const { return RParenLoc; } - void setRParenLoc(SourceLocation Loc) { RParenLoc = Loc; } SourceLocation getLocStart() const LLVM_READONLY { return ForLoc; } SourceLocation getLocEnd() const LLVM_READONLY { @@ -287,6 +288,95 @@ public: } }; +/// \brief Represents the body of a coroutine. This wraps the normal function +/// body and holds the additional semantic context required to set up and tear +/// down the coroutine frame. +class CoroutineBodyStmt : public Stmt { + enum SubStmt { Body, Count }; + Stmt *SubStmts[SubStmt::Count]; + + friend class ASTStmtReader; +public: + CoroutineBodyStmt(Stmt *Body) + : Stmt(CoroutineBodyStmtClass), SubStmts{Body} {} + + /// \brief Retrieve the body of the coroutine as written. This will be either + /// a CompoundStmt or a TryStmt. + Stmt *getBody() const { + return SubStmts[SubStmt::Body]; + } + + SourceLocation getLocStart() const LLVM_READONLY { + return getBody()->getLocStart(); + } + SourceLocation getLocEnd() const LLVM_READONLY { + return getBody()->getLocEnd(); + } + + child_range children() { + return child_range(SubStmts, SubStmts + SubStmt::Count); + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == CoroutineBodyStmtClass; + } +}; + +/// \brief Represents a 'co_return' statement in the C++ Coroutines TS. +/// +/// This statament models the initialization of the coroutine promise +/// (encapsulating the eventual notional return value) from an expression +/// (or braced-init-list). +/// +/// This initialization is modeled by a call to one of: +/// .return_value() +/// .return_void() +/// which we name the "promise call". +class CoreturnStmt : public Stmt { + SourceLocation CoreturnLoc; + + /// The operand of the 'co_return' statement. + Stmt *Operand; + /// The implied call to the promise object. May be null if the + /// coroutine has not yet been finalized. + Stmt *PromiseCall; + + friend class ASTStmtReader; +public: + CoreturnStmt(SourceLocation CoreturnLoc, Stmt *Operand) + : Stmt(CoreturnStmtClass), CoreturnLoc(CoreturnLoc), + Operand(Operand), PromiseCall(nullptr) {} + + SourceLocation getKeywordLoc() const { return CoreturnLoc; } + + /// \brief Retrieve the operand of the 'co_return' statement. Will be nullptr + /// if none was specified. + Expr *getOperand() const { return static_cast(Operand); } + + /// \brief Retrieve the promise call that results from this 'co_return' + /// statement. Will be nullptr if either the coroutine has not yet been + /// finalized or the coroutine has no eventual return type. + Expr *getPromiseCall() const { return static_cast(PromiseCall); } + + /// \brief Set the resolved promise call. This is delayed until the + /// complete coroutine body has been parsed and the promise type is known. + void finalize(Stmt *PC) { PromiseCall = PC; } + + SourceLocation getLocStart() const LLVM_READONLY { return CoreturnLoc; } + SourceLocation getLocEnd() const LLVM_READONLY { + return Operand->getLocEnd(); + } + + child_range children() { + Stmt **Which = PromiseCall ? &PromiseCall : &Operand; + return child_range(Which, Which + 1); + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == CoreturnStmtClass; + } +}; + } // end namespace clang #endif diff --git a/include/clang/AST/StmtVisitor.h b/include/clang/AST/StmtVisitor.h index ac357bc667..df4a2d8bc3 100644 --- a/include/clang/AST/StmtVisitor.h +++ b/include/clang/AST/StmtVisitor.h @@ -94,6 +94,7 @@ public: case UO_Real: DISPATCH(UnaryReal, UnaryOperator); case UO_Imag: DISPATCH(UnaryImag, UnaryOperator); case UO_Extension: DISPATCH(UnaryExtension, UnaryOperator); + case UO_Coawait: DISPATCH(UnaryCoawait, UnaryOperator); } } @@ -158,7 +159,7 @@ public: UNARYOP_FALLBACK(Plus) UNARYOP_FALLBACK(Minus) UNARYOP_FALLBACK(Not) UNARYOP_FALLBACK(LNot) UNARYOP_FALLBACK(Real) UNARYOP_FALLBACK(Imag) - UNARYOP_FALLBACK(Extension) + UNARYOP_FALLBACK(Extension) UNARYOP_FALLBACK(Coawait) #undef UNARYOP_FALLBACK // Base case, ignore it. :) diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 9bf458d513..6e90942cc4 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -7839,7 +7839,8 @@ def err_module_import_in_implementation : Error< } let CategoryName = "Coroutines Issue" in { -def err_return_in_coroutine : Error<"return statement in coroutine">; +def err_return_in_coroutine : Error< + "return statement not allowed in coroutine; did you mean 'co_return'?">; def note_declared_coroutine_here : Note< "function is a coroutine due to use of " "'%select{co_await|co_yield|co_return}0' here">; @@ -7853,10 +7854,24 @@ def err_coroutine_constexpr : Error< "'%0' cannot be used in a constexpr function">; def err_coroutine_varargs : Error< "'%0' cannot be used in a varargs function">; -def ext_coroutine_without_coawait_coyield : ExtWarn< +def ext_coroutine_without_co_await_co_yield : ExtWarn< "'co_return' used in a function " "that uses neither 'co_await' nor 'co_yield'">, InGroup>; +def err_co_await_no_viable_function : Error< + "invalid co_await operand of type %0; " + "no viable '%1' function %select{|for awaited type %3 }2available">; +def err_implied_std_coroutine_traits_not_found : Error< + "you need to include before defining a coroutine">; +def err_malformed_std_coroutine_traits : Error< + "std::coroutine_traits must be a class template">; +def err_implied_std_coroutine_traits_promise_type_not_found : Error< + "this function cannot be a coroutine: %0 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_traits_missing_specialization : Error< + "this function cannot be a coroutine: missing definition of " + "specialization %0">; } let CategoryName = "Documentation Issue" in { diff --git a/include/clang/Basic/StmtNodes.td b/include/clang/Basic/StmtNodes.td index 7bf7537bc9..6e665322e2 100644 --- a/include/clang/Basic/StmtNodes.td +++ b/include/clang/Basic/StmtNodes.td @@ -48,6 +48,10 @@ def CXXCatchStmt : Stmt; def CXXTryStmt : Stmt; def CXXForRangeStmt : Stmt; +// C++ Coroutines TS statements +def CoroutineBodyStmt : Stmt; +def CoreturnStmt : Stmt; + // Expressions def Expr : Stmt<1>; def PredefinedExpr : DStmt; @@ -140,6 +144,10 @@ def MaterializeTemporaryExpr : DStmt; def LambdaExpr : DStmt; def CXXFoldExpr : DStmt; +// C++ Coroutines TS expressions +def CoawaitExpr : DStmt; +def CoyieldExpr : DStmt; + // Obj-C Expressions. def ObjCStringLiteral : DStmt; def ObjCBoxedExpr : DStmt; diff --git a/include/clang/Sema/ScopeInfo.h b/include/clang/Sema/ScopeInfo.h index bc0fd20195..8f29d7c34c 100644 --- a/include/clang/Sema/ScopeInfo.h +++ b/include/clang/Sema/ScopeInfo.h @@ -89,40 +89,43 @@ protected: public: /// \brief What kind of scope we are describing. /// - ScopeKind Kind; + ScopeKind Kind : 2; /// \brief Whether this function contains a VLA, \@try, try, C++ /// initializer, or anything else that can't be jumped past. - bool HasBranchProtectedScope; + bool HasBranchProtectedScope : 1; /// \brief Whether this function contains any switches or direct gotos. - bool HasBranchIntoScope; + bool HasBranchIntoScope : 1; /// \brief Whether this function contains any indirect gotos. - bool HasIndirectGoto; + bool HasIndirectGoto : 1; /// \brief Whether a statement was dropped because it was invalid. - bool HasDroppedStmt; + bool HasDroppedStmt : 1; /// A flag that is set when parsing a method that must call super's /// implementation, such as \c -dealloc, \c -finalize, or any method marked /// with \c __attribute__((objc_requires_super)). - bool ObjCShouldCallSuper; + bool ObjCShouldCallSuper : 1; /// True when this is a method marked as a designated initializer. - bool ObjCIsDesignatedInit; + bool ObjCIsDesignatedInit : 1; /// This starts true for a method marked as designated initializer and will /// be set to false if there is an invocation to a designated initializer of /// the super class. - bool ObjCWarnForNoDesignatedInitChain; + bool ObjCWarnForNoDesignatedInitChain : 1; /// True when this is an initializer method not marked as a designated /// initializer within a class that has at least one initializer marked as a /// designated initializer. - bool ObjCIsSecondaryInit; + bool ObjCIsSecondaryInit : 1; /// This starts true for a secondary initializer method and will be set to /// false if there is an invocation of an initializer on 'self'. - bool ObjCWarnForNoInitDelegation; + bool ObjCWarnForNoInitDelegation : 1; + + /// First 'return' statement in the current function. + SourceLocation FirstReturnLoc; /// First C++ 'try' statement in the current function. SourceLocation FirstCXXTryLoc; @@ -142,6 +145,9 @@ public: /// optimization, or if we need to infer a return type. SmallVector Returns; + /// \brief The promise object for this coroutine, if any. + VarDecl *CoroutinePromise; + /// \brief The list of coroutine control flow constructs (co_await, co_yield, /// co_return) that occur within the function or block. Empty if and only if /// this function or block is not (yet known to be) a coroutine. diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 4415e7b57a..3ee700efc3 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -2492,17 +2492,8 @@ public: FRS_DiagnosticIssued }; - // An enum to represent whether something is dealing with a call to begin() - // or a call to end() in a range-based for loop. - enum BeginEndFunction { - BEF_begin, - BEF_end - }; - - ForRangeStatus BuildForRangeBeginEndCall(Scope *S, SourceLocation Loc, + ForRangeStatus BuildForRangeBeginEndCall(SourceLocation Loc, SourceLocation RangeLoc, - VarDecl *Decl, - BeginEndFunction BEF, const DeclarationNameInfo &NameInfo, LookupResult &MemberLookup, OverloadCandidateSet *CandidateSet, @@ -3324,7 +3315,7 @@ public: BFRK_Check }; - StmtResult ActOnCXXForRangeStmt(SourceLocation ForLoc, + StmtResult ActOnCXXForRangeStmt(Scope *S, SourceLocation ForLoc, SourceLocation CoawaitLoc, Stmt *LoopVar, SourceLocation ColonLoc, Expr *Collection, @@ -7699,10 +7690,14 @@ public: //===--------------------------------------------------------------------===// // C++ Coroutines TS // - ExprResult ActOnCoawaitExpr(SourceLocation KwLoc, Expr *E); - ExprResult ActOnCoyieldExpr(SourceLocation KwLoc, Expr *E); + ExprResult ActOnCoawaitExpr(Scope *S, SourceLocation KwLoc, Expr *E); + ExprResult ActOnCoyieldExpr(Scope *S, SourceLocation KwLoc, Expr *E); StmtResult ActOnCoreturnStmt(SourceLocation KwLoc, Expr *E); + ExprResult BuildCoawaitExpr(SourceLocation KwLoc, Expr *E); + ExprResult BuildCoyieldExpr(SourceLocation KwLoc, Expr *E); + StmtResult BuildCoreturnStmt(SourceLocation KwLoc, Expr *E); + void CheckCompletedCoroutineBody(FunctionDecl *FD, Stmt *Body); //===--------------------------------------------------------------------===// diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp index 6816dd11d9..ccc6931a5f 100644 --- a/lib/AST/ASTImporter.cpp +++ b/lib/AST/ASTImporter.cpp @@ -4915,13 +4915,14 @@ Stmt *ASTNodeImporter::VisitCXXForRangeStmt(CXXForRangeStmt *S) { if (!ToBody && S->getBody()) return nullptr; SourceLocation ToForLoc = Importer.Import(S->getForLoc()); + SourceLocation ToCoawaitLoc = Importer.Import(S->getCoawaitLoc()); SourceLocation ToColonLoc = Importer.Import(S->getColonLoc()); SourceLocation ToRParenLoc = Importer.Import(S->getRParenLoc()); return new (Importer.getToContext()) CXXForRangeStmt(ToRange, ToBeginEnd, ToCond, ToInc, ToLoopVar, ToBody, - ToForLoc, ToColonLoc, - ToRParenLoc); + ToForLoc, ToCoawaitLoc, + ToColonLoc, ToRParenLoc); } Stmt *ASTNodeImporter::VisitObjCForCollectionStmt(ObjCForCollectionStmt *S) { diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 1dd54d293d..dbc890d292 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -1079,6 +1079,7 @@ StringRef UnaryOperator::getOpcodeStr(Opcode Op) { case UO_Real: return "__real"; case UO_Imag: return "__imag"; case UO_Extension: return "__extension__"; + case UO_Coawait: return "co_await"; } llvm_unreachable("Unknown unary operator"); } @@ -1095,6 +1096,7 @@ UnaryOperator::getOverloadedOpcode(OverloadedOperatorKind OO, bool Postfix) { case OO_Minus: return UO_Minus; case OO_Tilde: return UO_Not; case OO_Exclaim: return UO_LNot; + case OO_Coawait: return UO_Coawait; } } @@ -1108,6 +1110,7 @@ OverloadedOperatorKind UnaryOperator::getOverloadedOperator(Opcode Opc) { case UO_Minus: return OO_Minus; case UO_Not: return OO_Tilde; case UO_LNot: return OO_Exclaim; + case UO_Coawait: return OO_Coawait; default: return OO_None; } } @@ -2050,6 +2053,9 @@ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc, case UO_LNot: case UO_Deref: break; + case UO_Coawait: + // This is just the 'operator co_await' call inside the guts of a + // dependent co_await call. case UO_PostInc: case UO_PostDec: case UO_PreInc: @@ -3005,6 +3011,8 @@ bool Expr::HasSideEffects(const ASTContext &Ctx, case CXXNewExprClass: case CXXDeleteExprClass: case ExprWithCleanupsClass: + case CoawaitExprClass: + case CoyieldExprClass: // These always have a side-effect. return true; diff --git a/lib/AST/ExprClassification.cpp b/lib/AST/ExprClassification.cpp index 882c786c35..a5a44353a2 100644 --- a/lib/AST/ExprClassification.cpp +++ b/lib/AST/ExprClassification.cpp @@ -186,6 +186,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { case Expr::CXXFoldExprClass: case Expr::NoInitExprClass: case Expr::DesignatedInitUpdateExprClass: + case Expr::CoyieldExprClass: return Cl::CL_PRValue; // Next come the complicated cases. @@ -397,6 +398,9 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { assert(cast(E)->getNumInits() == 1 && "Only 1-element init lists can be glvalues."); return ClassifyInternal(Ctx, cast(E)->getInit(0)); + + case Expr::CoawaitExprClass: + return ClassifyInternal(Ctx, cast(E)->getResumeExpr()); } llvm_unreachable("unhandled expression kind in classification"); diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 08fcd260a3..e59902ddd8 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -9017,6 +9017,8 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) { case Expr::AtomicExprClass: case Expr::LambdaExprClass: case Expr::CXXFoldExprClass: + case Expr::CoawaitExprClass: + case Expr::CoyieldExprClass: return ICEDiag(IK_NotICE, E->getLocStart()); case Expr::InitListExprClass: { @@ -9102,6 +9104,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) { case UO_PreDec: case UO_AddrOf: case UO_Deref: + case UO_Coawait: // C99 6.6/3 allows increment and decrement within unevaluated // subexpressions of constant expressions, but they can never be ICEs // because an ICE cannot contain an lvalue operand. diff --git a/lib/AST/ItaniumMangle.cpp b/lib/AST/ItaniumMangle.cpp index 86bc8e6775..c3432839c2 100644 --- a/lib/AST/ItaniumMangle.cpp +++ b/lib/AST/ItaniumMangle.cpp @@ -3514,6 +3514,18 @@ recurse: case Expr::CXXThisExprClass: Out << "fpT"; break; + + case Expr::CoawaitExprClass: + // FIXME: Propose a non-vendor mangling. + Out << "v18co_await"; + mangleExpression(cast(E)->getOperand()); + break; + + case Expr::CoyieldExprClass: + // FIXME: Propose a non-vendor mangling. + Out << "v18co_yield"; + mangleExpression(cast(E)->getOperand()); + break; } } diff --git a/lib/AST/StmtCXX.cpp b/lib/AST/StmtCXX.cpp index aa721468e2..e39a01daf9 100644 --- a/lib/AST/StmtCXX.cpp +++ b/lib/AST/StmtCXX.cpp @@ -52,8 +52,10 @@ CXXTryStmt::CXXTryStmt(SourceLocation tryLoc, Stmt *tryBlock, CXXForRangeStmt::CXXForRangeStmt(DeclStmt *Range, DeclStmt *BeginEndStmt, Expr *Cond, Expr *Inc, DeclStmt *LoopVar, Stmt *Body, SourceLocation FL, - SourceLocation CL, SourceLocation RPL) - : Stmt(CXXForRangeStmtClass), ForLoc(FL), ColonLoc(CL), RParenLoc(RPL) { + SourceLocation CAL, SourceLocation CL, + SourceLocation RPL) + : Stmt(CXXForRangeStmtClass), ForLoc(FL), CoawaitLoc(CAL), ColonLoc(CL), + RParenLoc(RPL) { SubExprs[RANGE] = Range; SubExprs[BEGINEND] = BeginEndStmt; SubExprs[COND] = Cond; diff --git a/lib/AST/StmtPrinter.cpp b/lib/AST/StmtPrinter.cpp index bf155f5680..52aa59e252 100644 --- a/lib/AST/StmtPrinter.cpp +++ b/lib/AST/StmtPrinter.cpp @@ -2163,6 +2163,31 @@ void StmtPrinter::VisitCXXFoldExpr(CXXFoldExpr *E) { OS << ")"; } +// C++ Coroutines TS + +void StmtPrinter::VisitCoroutineBodyStmt(CoroutineBodyStmt *S) { + Visit(S->getBody()); +} + +void StmtPrinter::VisitCoreturnStmt(CoreturnStmt *S) { + OS << "co_return"; + if (S->getOperand()) { + OS << " "; + Visit(S->getOperand()); + } + OS << ";"; +} + +void StmtPrinter::VisitCoawaitExpr(CoawaitExpr *S) { + OS << "co_await "; + PrintExpr(S->getOperand()); +} + +void StmtPrinter::VisitCoyieldExpr(CoyieldExpr *S) { + OS << "co_yield "; + PrintExpr(S->getOperand()); +} + // Obj-C void StmtPrinter::VisitObjCStringLiteral(ObjCStringLiteral *Node) { diff --git a/lib/AST/StmtProfile.cpp b/lib/AST/StmtProfile.cpp index 93b80e5d99..28507c1e53 100644 --- a/lib/AST/StmtProfile.cpp +++ b/lib/AST/StmtProfile.cpp @@ -1362,6 +1362,22 @@ void StmtProfiler::VisitCXXFoldExpr(const CXXFoldExpr *S) { ID.AddInteger(S->getOperator()); } +void StmtProfiler::VisitCoroutineBodyStmt(const CoroutineBodyStmt *S) { + VisitStmt(S); +} + +void StmtProfiler::VisitCoreturnStmt(const CoreturnStmt *S) { + VisitStmt(S); +} + +void StmtProfiler::VisitCoawaitExpr(const CoawaitExpr *S) { + VisitExpr(S); +} + +void StmtProfiler::VisitCoyieldExpr(const CoyieldExpr *S) { + VisitExpr(S); +} + void StmtProfiler::VisitOpaqueValueExpr(const OpaqueValueExpr *E) { VisitExpr(E); } diff --git a/lib/Analysis/ThreadSafetyCommon.cpp b/lib/Analysis/ThreadSafetyCommon.cpp index 0b564398db..ffe95ea22a 100644 --- a/lib/Analysis/ThreadSafetyCommon.cpp +++ b/lib/Analysis/ThreadSafetyCommon.cpp @@ -445,6 +445,7 @@ til::SExpr *SExprBuilder::translateUnaryOperator(const UnaryOperator *UO, case UO_Real: case UO_Imag: case UO_Extension: + case UO_Coawait: return new (Arena) til::Undefined(UO); } return new (Arena) til::Undefined(UO); diff --git a/lib/CodeGen/CGStmt.cpp b/lib/CodeGen/CGStmt.cpp index f915518c26..b1cb395b8e 100644 --- a/lib/CodeGen/CGStmt.cpp +++ b/lib/CodeGen/CGStmt.cpp @@ -141,6 +141,10 @@ void CodeGenFunction::EmitStmt(const Stmt *S) { case Stmt::SwitchStmtClass: EmitSwitchStmt(cast(*S)); break; case Stmt::GCCAsmStmtClass: // Intentional fall-through. case Stmt::MSAsmStmtClass: EmitAsmStmt(cast(*S)); break; + case Stmt::CoroutineBodyStmtClass: + case Stmt::CoreturnStmtClass: + CGM.ErrorUnsupported(S, "coroutine"); + break; case Stmt::CapturedStmtClass: { const CapturedStmt *CS = cast(S); EmitCapturedStmt(*CS, CS->getCapturedRegionKind()); diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index f3316461ac..490bd5ada6 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -1048,7 +1048,7 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression, SourceLocation CoawaitLoc = ConsumeToken(); Res = ParseCastExpression(false); if (!Res.isInvalid()) - Res = Actions.ActOnCoawaitExpr(CoawaitLoc, Res.get()); + Res = Actions.ActOnCoawaitExpr(getCurScope(), CoawaitLoc, Res.get()); return Res; } diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp index bd9b9f9731..d5f188104e 100644 --- a/lib/Parse/ParseExprCXX.cpp +++ b/lib/Parse/ParseExprCXX.cpp @@ -1568,7 +1568,7 @@ ExprResult Parser::ParseCoyieldExpression() { SourceLocation Loc = ConsumeToken(); ExprResult Expr = ParseAssignmentExpression(); if (!Expr.isInvalid()) - Expr = Actions.ActOnCoyieldExpr(Loc, Expr.get()); + Expr = Actions.ActOnCoyieldExpr(getCurScope(), Loc, Expr.get()); return Expr; } diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index af7008f8e7..35cb0fef78 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -1691,13 +1691,10 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) { StmtResult ForEachStmt; if (ForRange) { - ForRangeStmt = Actions.ActOnCXXForRangeStmt(ForLoc, CoawaitLoc, - FirstPart.get(), - ForRangeInit.ColonLoc, - ForRangeInit.RangeExpr.get(), - T.getCloseLocation(), - Sema::BFRK_Build); - + ForRangeStmt = Actions.ActOnCXXForRangeStmt( + getCurScope(), ForLoc, CoawaitLoc, FirstPart.get(), + ForRangeInit.ColonLoc, ForRangeInit.RangeExpr.get(), + T.getCloseLocation(), Sema::BFRK_Build); // Similarly, we need to do the semantic analysis for a for-range // statement immediately in order to close over temporaries correctly. diff --git a/lib/Sema/ScopeInfo.cpp b/lib/Sema/ScopeInfo.cpp index ba8c3642bc..6a400a2efe 100644 --- a/lib/Sema/ScopeInfo.cpp +++ b/lib/Sema/ScopeInfo.cpp @@ -33,11 +33,13 @@ void FunctionScopeInfo::Clear() { ObjCWarnForNoDesignatedInitChain = false; ObjCIsSecondaryInit = false; ObjCWarnForNoInitDelegation = false; + FirstReturnLoc = SourceLocation(); FirstCXXTryLoc = SourceLocation(); FirstSEHTryLoc = SourceLocation(); SwitchStack.clear(); Returns.clear(); + CoroutineStmts.clear(); ErrorTrap.reset(); PossiblyUnreachableDiags.clear(); WeakObjectUses.clear(); diff --git a/lib/Sema/SemaCoroutine.cpp b/lib/Sema/SemaCoroutine.cpp index 6545b6709a..a2a79001f0 100644 --- a/lib/Sema/SemaCoroutine.cpp +++ b/lib/Sema/SemaCoroutine.cpp @@ -12,12 +12,89 @@ //===----------------------------------------------------------------------===// #include "clang/Sema/SemaInternal.h" +#include "clang/AST/Decl.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/StmtCXX.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Sema/Overload.h" using namespace clang; using namespace sema; +/// Look up the std::coroutine_traits<...>::promise_type for the given +/// function type. +static QualType lookupPromiseType(Sema &S, const FunctionProtoType *FnType, + SourceLocation Loc) { + // FIXME: Cache std::coroutine_traits once we've found it. + NamespaceDecl *Std = S.getStdNamespace(); + if (!Std) { + S.Diag(Loc, diag::err_implied_std_coroutine_traits_not_found); + return QualType(); + } + + LookupResult Result(S, &S.PP.getIdentifierTable().get("coroutine_traits"), + Loc, Sema::LookupOrdinaryName); + if (!S.LookupQualifiedName(Result, Std)) { + S.Diag(Loc, diag::err_implied_std_coroutine_traits_not_found); + return QualType(); + } + + ClassTemplateDecl *CoroTraits = Result.getAsSingle(); + if (!CoroTraits) { + 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_traits); + return QualType(); + } + + // Form template argument list for coroutine_traits. + TemplateArgumentListInfo Args(Loc, Loc); + Args.addArgument(TemplateArgumentLoc( + TemplateArgument(FnType->getReturnType()), + S.Context.getTrivialTypeSourceInfo(FnType->getReturnType(), Loc))); + for (QualType T : FnType->getParamTypes()) + Args.addArgument(TemplateArgumentLoc( + TemplateArgument(T), S.Context.getTrivialTypeSourceInfo(T, Loc))); + + // Build the template-id. + QualType CoroTrait = + S.CheckTemplateIdType(TemplateName(CoroTraits), Loc, Args); + if (CoroTrait.isNull()) + return QualType(); + if (S.RequireCompleteType(Loc, CoroTrait, + diag::err_coroutine_traits_missing_specialization)) + return QualType(); + + CXXRecordDecl *RD = CoroTrait->getAsCXXRecordDecl(); + 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, + 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) + << RD; + return QualType(); + } + + // The promise type is required to be a class type. + QualType PromiseType = S.Context.getTypeDeclType(Promise); + if (!PromiseType->getAsCXXRecordDecl()) { + S.Diag(Loc, diag::err_implied_std_coroutine_traits_promise_type_not_class) + << PromiseType; + return QualType(); + } + + return PromiseType; +} + +/// Check that this is a context in which a coroutine suspension can appear. static FunctionScopeInfo * checkCoroutineContext(Sema &S, SourceLocation Loc, StringRef Keyword) { // 'co_await' and 'co_yield' are permitted in unevaluated operands. + // FIXME: Not in 'noexcept'. if (S.isUnevaluatedContext()) return nullptr; @@ -42,36 +119,143 @@ checkCoroutineContext(Sema &S, SourceLocation Loc, StringRef Keyword) { } else { auto *ScopeInfo = S.getCurFunction(); assert(ScopeInfo && "missing function scope for function"); + + // If we don't have a promise variable, build one now. + if (!ScopeInfo->CoroutinePromise && !FD->getType()->isDependentType()) { + QualType T = + lookupPromiseType(S, FD->getType()->castAs(), Loc); + if (T.isNull()) + return nullptr; + + // Create and default-initialize the promise. + ScopeInfo->CoroutinePromise = + VarDecl::Create(S.Context, FD, FD->getLocation(), FD->getLocation(), + &S.PP.getIdentifierTable().get("__promise"), T, + S.Context.getTrivialTypeSourceInfo(T, Loc), SC_None); + S.CheckVariableDeclarationType(ScopeInfo->CoroutinePromise); + if (!ScopeInfo->CoroutinePromise->isInvalidDecl()) + S.ActOnUninitializedDecl(ScopeInfo->CoroutinePromise, false); + } + return ScopeInfo; } return nullptr; } -ExprResult Sema::ActOnCoawaitExpr(SourceLocation Loc, Expr *E) { - auto *Context = checkCoroutineContext(*this, Loc, "co_await"); - ExprResult Res = ExprError(); +/// 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) { + UnresolvedSet<16> Functions; + SemaRef.LookupOverloadedOperatorName(OO_Coawait, S, E->getType(), QualType(), + Functions); + return SemaRef.CreateOverloadedUnaryOp(Loc, UO_Coawait, Functions, E); +} + +struct ReadySuspendResumeResult { + bool IsInvalid; + Expr *Results[3]; +}; + +/// Build calls to await_ready, await_suspend, and await_resume for a co_await +/// expression. +static ReadySuspendResumeResult buildCoawaitCalls(Sema &S, SourceLocation Loc, + Expr *E) { + // Assume invalid until we see otherwise. + ReadySuspendResumeResult Calls = {true, {}}; + + const StringRef Funcs[] = {"await_ready", "await_suspend", "await_resume"}; + for (size_t I = 0, N = llvm::array_lengthof(Funcs); I != N; ++I) { + DeclarationNameInfo NameInfo(&S.PP.getIdentifierTable().get(Funcs[I]), Loc); + + Expr *Operand = new (S.Context) OpaqueValueExpr( + Loc, E->getType(), E->getValueKind(), E->getObjectKind(), E); + + // FIXME: Fix BuildMemberReferenceExpr to take a const CXXScopeSpec&. + CXXScopeSpec SS; + ExprResult Result = S.BuildMemberReferenceExpr( + Operand, Operand->getType(), Loc, /*IsPtr=*/false, SS, + SourceLocation(), nullptr, NameInfo, /*TemplateArgs=*/nullptr, + /*Scope=*/nullptr); + if (Result.isInvalid()) + return Calls; - if (Context && !Res.isInvalid()) - Context->CoroutineStmts.push_back(Res.get()); + // FIXME: Pass coroutine handle to await_suspend. + Result = S.ActOnCallExpr(nullptr, Result.get(), Loc, None, Loc, nullptr); + if (Result.isInvalid()) + return Calls; + Calls.Results[I] = Result.get(); + } + + Calls.IsInvalid = false; + return Calls; +} + +ExprResult Sema::ActOnCoawaitExpr(Scope *S, SourceLocation Loc, Expr *E) { + ExprResult Awaitable = buildOperatorCoawaitCall(*this, S, Loc, E); + 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 (E->getType()->isDependentType()) { + Expr *Res = new (Context) CoawaitExpr(Loc, Context.DependentTy, E); + if (Coroutine) + Coroutine->CoroutineStmts.push_back(Res); + return Res; + } + + if (E->getType()->isPlaceholderType()) { + ExprResult R = CheckPlaceholderExpr(E); + if (R.isInvalid()) return ExprError(); + E = R.get(); + } + + // FIXME: If E is a prvalue, create a temporary. + // FIXME: If E is an xvalue, convert to lvalue. + + // Build the await_ready, await_suspend, await_resume calls. + ReadySuspendResumeResult RSS = buildCoawaitCalls(*this, Loc, E); + if (RSS.IsInvalid) + return ExprError(); + + Expr *Res = new (Context) CoawaitExpr(Loc, E, RSS.Results[0], RSS.Results[1], + RSS.Results[2]); + if (Coroutine) + Coroutine->CoroutineStmts.push_back(Res); return Res; } -ExprResult Sema::ActOnCoyieldExpr(SourceLocation Loc, Expr *E) { - auto *Context = checkCoroutineContext(*this, Loc, "co_yield"); - ExprResult Res = ExprError(); +ExprResult Sema::ActOnCoyieldExpr(Scope *S, SourceLocation Loc, Expr *E) { + // FIXME: Build yield_value call. + ExprResult Awaitable = buildOperatorCoawaitCall(*this, S, Loc, E); + if (Awaitable.isInvalid()) + return ExprError(); + return BuildCoyieldExpr(Loc, Awaitable.get()); +} +ExprResult Sema::BuildCoyieldExpr(SourceLocation Loc, Expr *E) { + auto *Coroutine = checkCoroutineContext(*this, Loc, "co_yield"); - if (Context && !Res.isInvalid()) - Context->CoroutineStmts.push_back(Res.get()); + // FIXME: Build await_* calls. + Expr *Res = new (Context) CoyieldExpr(Loc, Context.VoidTy, E); + if (Coroutine) + Coroutine->CoroutineStmts.push_back(Res); return Res; } StmtResult Sema::ActOnCoreturnStmt(SourceLocation Loc, Expr *E) { - auto *Context = checkCoroutineContext(*this, Loc, "co_return"); - StmtResult Res = StmtError(); + return BuildCoreturnStmt(Loc, E); +} +StmtResult Sema::BuildCoreturnStmt(SourceLocation Loc, Expr *E) { + auto *Coroutine = checkCoroutineContext(*this, Loc, "co_return"); - if (Context && !Res.isInvalid()) - Context->CoroutineStmts.push_back(Res.get()); + // FIXME: Build return_* calls. + Stmt *Res = new (Context) CoreturnStmt(Loc, E); + if (Coroutine) + Coroutine->CoroutineStmts.push_back(Res); return Res; } @@ -81,26 +265,25 @@ void Sema::CheckCompletedCoroutineBody(FunctionDecl *FD, Stmt *Body) { // Coroutines [stmt.return]p1: // A return statement shall not appear in a coroutine. - if (!Fn->Returns.empty()) { - Diag(Fn->Returns.front()->getLocStart(), diag::err_return_in_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) - << 0; // FIXME: Indicate the kind here + << (isa(First) ? 0 : + isa(First) ? 1 : 2); } bool AnyCoawaits = false; bool AnyCoyields = false; for (auto *CoroutineStmt : Fn->CoroutineStmts) { - (void)CoroutineStmt; - AnyCoawaits = AnyCoyields = true; // FIXME + AnyCoawaits |= isa(CoroutineStmt); + AnyCoyields |= isa(CoroutineStmt); } if (!AnyCoawaits && !AnyCoyields) Diag(Fn->CoroutineStmts.front()->getLocStart(), - diag::ext_coroutine_without_coawait_coyield); + diag::ext_coroutine_without_co_await_co_yield); - // FIXME: If we have a deduced return type, resolve it now. - // FIXME: Compute the promise type. - // FIXME: Perform analysis of initial and final suspend, and set_exception call. - // FIXME: Complete the semantic analysis of the CoroutineStmts. + // FIXME: Perform analysis of initial and final suspend, + // and set_exception call. } diff --git a/lib/Sema/SemaExceptionSpec.cpp b/lib/Sema/SemaExceptionSpec.cpp index a18824f155..f993a28a13 100644 --- a/lib/Sema/SemaExceptionSpec.cpp +++ b/lib/Sema/SemaExceptionSpec.cpp @@ -1063,8 +1063,10 @@ CanThrowResult Sema::canThrow(const Expr *E) { // Many other things have subexpressions, so we have to test those. // Some are simple: + case Expr::CoawaitExprClass: case Expr::ConditionalOperatorClass: case Expr::CompoundLiteralExprClass: + case Expr::CoyieldExprClass: case Expr::CXXConstCastExprClass: case Expr::CXXReinterpretCastExprClass: case Expr::CXXStdInitializerListExprClass: diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index badb2ef118..857925da99 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -10901,6 +10901,7 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc, } break; case UO_Extension: + case UO_Coawait: resultType = Input.get()->getType(); VK = Input.get()->getValueKind(); OK = Input.get()->getObjectKind(); diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index d4a220037e..084e25a1cb 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -8228,15 +8228,16 @@ void Sema::AddBuiltinOperatorCandidates(OverloadedOperatorKind Op, case OO_Array_New: case OO_Array_Delete: case OO_Call: - case OO_Coawait: llvm_unreachable( "Special operators don't use AddBuiltinOperatorCandidates"); case OO_Comma: case OO_Arrow: + case OO_Coawait: // C++ [over.match.oper]p3: - // -- For the operator ',', the unary operator '&', or the - // operator '->', the built-in candidates set is empty. + // -- For the operator ',', the unary operator '&', the + // operator '->', or the operator 'co_await', the + // built-in candidates set is empty. break; case OO_Plus: // '+' is either unary or binary @@ -12481,13 +12482,14 @@ ExprResult Sema::BuildLiteralOperatorCall(LookupResult &R, /// otherwise CallExpr is set to ExprError() and some non-success value /// is returned. Sema::ForRangeStatus -Sema::BuildForRangeBeginEndCall(Scope *S, SourceLocation Loc, - SourceLocation RangeLoc, VarDecl *Decl, - BeginEndFunction BEF, +Sema::BuildForRangeBeginEndCall(SourceLocation Loc, + SourceLocation RangeLoc, const DeclarationNameInfo &NameInfo, LookupResult &MemberLookup, OverloadCandidateSet *CandidateSet, Expr *Range, ExprResult *CallExpr) { + Scope *S = nullptr; + CandidateSet->clear(); if (!MemberLookup.empty()) { ExprResult MemberRef = @@ -12499,15 +12501,11 @@ Sema::BuildForRangeBeginEndCall(Scope *S, SourceLocation Loc, /*TemplateArgs=*/nullptr, S); if (MemberRef.isInvalid()) { *CallExpr = ExprError(); - Diag(Range->getLocStart(), diag::note_in_for_range) - << RangeLoc << BEF << Range->getType(); return FRS_DiagnosticIssued; } *CallExpr = ActOnCallExpr(S, MemberRef.get(), Loc, None, Loc, nullptr); if (CallExpr->isInvalid()) { *CallExpr = ExprError(); - Diag(Range->getLocStart(), diag::note_in_for_range) - << RangeLoc << BEF << Range->getType(); return FRS_DiagnosticIssued; } } else { @@ -12538,8 +12536,6 @@ Sema::BuildForRangeBeginEndCall(Scope *S, SourceLocation Loc, /*AllowTypoCorrection=*/false); if (CallExpr->isInvalid() || OverloadResult != OR_Success) { *CallExpr = ExprError(); - Diag(Range->getLocStart(), diag::note_in_for_range) - << RangeLoc << BEF << Range->getType(); return FRS_DiagnosticIssued; } } diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp index fec1200095..f8668c3893 100644 --- a/lib/Sema/SemaStmt.cpp +++ b/lib/Sema/SemaStmt.cpp @@ -1859,13 +1859,19 @@ static bool FinishForRangeVarDecl(Sema &SemaRef, VarDecl *Decl, Expr *Init, } namespace { +// An enum to represent whether something is dealing with a call to begin() +// or a call to end() in a range-based for loop. +enum BeginEndFunction { + BEF_begin, + BEF_end +}; /// Produce a note indicating which begin/end function was implicitly called /// by a C++11 for-range statement. This is often not obvious from the code, /// nor from the diagnostics produced when analysing the implicit expressions /// required in a for-range statement. void NoteForRangeBeginEndFunction(Sema &SemaRef, Expr *E, - Sema::BeginEndFunction BEF) { + BeginEndFunction BEF) { CallExpr *CE = dyn_cast(E); if (!CE) return; @@ -1923,10 +1929,11 @@ static bool ObjCEnumerationCollection(Expr *Collection) { /// /// The body of the loop is not available yet, since it cannot be analysed until /// we have determined the type of the for-range-declaration. -StmtResult -Sema::ActOnCXXForRangeStmt(SourceLocation ForLoc, SourceLocation CoawaitLoc, - Stmt *First, SourceLocation ColonLoc, Expr *Range, - SourceLocation RParenLoc, BuildForRangeKind Kind) { +StmtResult Sema::ActOnCXXForRangeStmt(Scope *S, SourceLocation ForLoc, + SourceLocation CoawaitLoc, Stmt *First, + SourceLocation ColonLoc, Expr *Range, + SourceLocation RParenLoc, + BuildForRangeKind Kind) { if (!First) return StmtError(); @@ -1950,7 +1957,7 @@ Sema::ActOnCXXForRangeStmt(SourceLocation ForLoc, SourceLocation CoawaitLoc, // Coroutines: 'for co_await' implicitly co_awaits its range. if (CoawaitLoc.isValid()) { - ExprResult Coawait = ActOnCoawaitExpr(CoawaitLoc, Range); + ExprResult Coawait = ActOnCoawaitExpr(S, CoawaitLoc, Range); if (Coawait.isInvalid()) return StmtError(); Range = Coawait.get(); } @@ -1990,7 +1997,7 @@ Sema::ActOnCXXForRangeStmt(SourceLocation ForLoc, SourceLocation CoawaitLoc, /// BeginExpr and EndExpr are set and FRS_Success is returned on success; /// CandidateSet and BEF are set and some non-success value is returned on /// failure. -static Sema::ForRangeStatus BuildNonArrayForRange(Sema &SemaRef, Scope *S, +static Sema::ForRangeStatus BuildNonArrayForRange(Sema &SemaRef, Expr *BeginRange, Expr *EndRange, QualType RangeType, VarDecl *BeginVar, @@ -1999,7 +2006,7 @@ static Sema::ForRangeStatus BuildNonArrayForRange(Sema &SemaRef, Scope *S, OverloadCandidateSet *CandidateSet, ExprResult *BeginExpr, ExprResult *EndExpr, - Sema::BeginEndFunction *BEF) { + BeginEndFunction *BEF) { DeclarationNameInfo BeginNameInfo( &SemaRef.PP.getIdentifierTable().get("begin"), ColonLoc); DeclarationNameInfo EndNameInfo(&SemaRef.PP.getIdentifierTable().get("end"), @@ -2020,7 +2027,7 @@ static Sema::ForRangeStatus BuildNonArrayForRange(Sema &SemaRef, Scope *S, if (BeginMemberLookup.empty() != EndMemberLookup.empty()) { SourceLocation RangeLoc = BeginVar->getLocation(); - *BEF = BeginMemberLookup.empty() ? Sema::BEF_end : Sema::BEF_begin; + *BEF = BeginMemberLookup.empty() ? BEF_end : BEF_begin; SemaRef.Diag(RangeLoc, diag::err_for_range_member_begin_end_mismatch) << RangeLoc << BeginRange->getType() << *BEF; @@ -2034,29 +2041,35 @@ static Sema::ForRangeStatus BuildNonArrayForRange(Sema &SemaRef, Scope *S, } - *BEF = Sema::BEF_begin; + *BEF = BEF_begin; Sema::ForRangeStatus RangeStatus = - SemaRef.BuildForRangeBeginEndCall(S, ColonLoc, ColonLoc, BeginVar, - Sema::BEF_begin, BeginNameInfo, + SemaRef.BuildForRangeBeginEndCall(ColonLoc, ColonLoc, BeginNameInfo, BeginMemberLookup, CandidateSet, BeginRange, BeginExpr); - if (RangeStatus != Sema::FRS_Success) + if (RangeStatus != Sema::FRS_Success) { + if (RangeStatus == Sema::FRS_DiagnosticIssued) + SemaRef.Diag(BeginRange->getLocStart(), diag::note_in_for_range) + << ColonLoc << BEF_begin << BeginRange->getType(); return RangeStatus; + } if (FinishForRangeVarDecl(SemaRef, BeginVar, BeginExpr->get(), ColonLoc, diag::err_for_range_iter_deduction_failure)) { NoteForRangeBeginEndFunction(SemaRef, BeginExpr->get(), *BEF); return Sema::FRS_DiagnosticIssued; } - *BEF = Sema::BEF_end; + *BEF = BEF_end; RangeStatus = - SemaRef.BuildForRangeBeginEndCall(S, ColonLoc, ColonLoc, EndVar, - Sema::BEF_end, EndNameInfo, + SemaRef.BuildForRangeBeginEndCall(ColonLoc, ColonLoc, EndNameInfo, EndMemberLookup, CandidateSet, EndRange, EndExpr); - if (RangeStatus != Sema::FRS_Success) + if (RangeStatus != Sema::FRS_Success) { + if (RangeStatus == Sema::FRS_DiagnosticIssued) + SemaRef.Diag(EndRange->getLocStart(), diag::note_in_for_range) + << ColonLoc << BEF_end << EndRange->getType(); return RangeStatus; + } if (FinishForRangeVarDecl(SemaRef, EndVar, EndExpr->get(), ColonLoc, diag::err_for_range_iter_deduction_failure)) { NoteForRangeBeginEndFunction(SemaRef, EndExpr->get(), *BEF); @@ -2086,10 +2099,9 @@ static StmtResult RebuildForRangeWithDereference(Sema &SemaRef, Scope *S, if (AdjustedRange.isInvalid()) return StmtResult(); - StmtResult SR = - SemaRef.ActOnCXXForRangeStmt(ForLoc, CoawaitLoc, LoopVarDecl, ColonLoc, - AdjustedRange.get(), RParenLoc, - Sema::BFRK_Check); + StmtResult SR = SemaRef.ActOnCXXForRangeStmt( + S, ForLoc, CoawaitLoc, LoopVarDecl, ColonLoc, AdjustedRange.get(), + RParenLoc, Sema::BFRK_Check); if (SR.isInvalid()) return StmtResult(); } @@ -2099,8 +2111,8 @@ static StmtResult RebuildForRangeWithDereference(Sema &SemaRef, Scope *S, // case there are any other (non-fatal) problems with it. SemaRef.Diag(RangeLoc, diag::err_for_range_dereference) << Range->getType() << FixItHint::CreateInsertion(RangeLoc, "*"); - return SemaRef.ActOnCXXForRangeStmt(ForLoc, CoawaitLoc, LoopVarDecl, ColonLoc, - AdjustedRange.get(), RParenLoc, + return SemaRef.ActOnCXXForRangeStmt(S, ForLoc, CoawaitLoc, LoopVarDecl, + ColonLoc, AdjustedRange.get(), RParenLoc, Sema::BFRK_Rebuild); } @@ -2127,6 +2139,15 @@ Sema::BuildCXXForRangeStmt(SourceLocation ForLoc, SourceLocation CoawaitLoc, Stmt *RangeDecl, Stmt *BeginEnd, Expr *Cond, Expr *Inc, Stmt *LoopVarDecl, SourceLocation RParenLoc, BuildForRangeKind Kind) { + // FIXME: This should not be used during template instantiation. We should + // pick up the set of unqualified lookup results for the != and + operators + // in the initial parse. + // + // Testcase (accepts-invalid): + // template void f() { for (auto x : T()) {} } + // namespace N { struct X { X begin(); X end(); int operator*(); }; } + // bool operator!=(N::X, N::X); void operator++(N::X); + // void g() { f(); } Scope *S = getCurScope(); DeclStmt *RangeDS = cast(RangeDecl); @@ -2226,9 +2247,9 @@ Sema::BuildCXXForRangeStmt(SourceLocation ForLoc, SourceLocation CoawaitLoc, } else { OverloadCandidateSet CandidateSet(RangeLoc, OverloadCandidateSet::CSK_Normal); - Sema::BeginEndFunction BEFFailure; + BeginEndFunction BEFFailure; ForRangeStatus RangeStatus = - BuildNonArrayForRange(*this, S, BeginRangeRef.get(), + BuildNonArrayForRange(*this, BeginRangeRef.get(), EndRangeRef.get(), RangeType, BeginVar, EndVar, ColonLoc, &CandidateSet, &BeginExpr, &EndExpr, &BEFFailure); @@ -2325,7 +2346,7 @@ Sema::BuildCXXForRangeStmt(SourceLocation ForLoc, SourceLocation CoawaitLoc, IncrExpr = ActOnUnaryOp(S, ColonLoc, tok::plusplus, BeginRef.get()); if (!IncrExpr.isInvalid() && CoawaitLoc.isValid()) - IncrExpr = ActOnCoawaitExpr(CoawaitLoc, IncrExpr.get()); + IncrExpr = ActOnCoawaitExpr(S, CoawaitLoc, IncrExpr.get()); if (!IncrExpr.isInvalid()) IncrExpr = ActOnFinishFullExpr(IncrExpr.get()); if (IncrExpr.isInvalid()) { @@ -2364,10 +2385,10 @@ Sema::BuildCXXForRangeStmt(SourceLocation ForLoc, SourceLocation CoawaitLoc, if (Kind == BFRK_Check) return StmtResult(); - // FIXME: Pass in CoawaitLoc in the dependent case. return new (Context) CXXForRangeStmt( RangeDS, cast_or_null(BeginEndDecl.get()), NotEqExpr.get(), - IncrExpr.get(), LoopVarDS, /*Body=*/nullptr, ForLoc, ColonLoc, RParenLoc); + IncrExpr.get(), LoopVarDS, /*Body=*/nullptr, ForLoc, CoawaitLoc, + ColonLoc, RParenLoc); } /// FinishObjCForCollectionStmt - Attach the body to a objective-C foreach @@ -2925,6 +2946,9 @@ Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { if (CurCap->HasImplicitReturnType || NRVOCandidate) FunctionScopes.back()->Returns.push_back(Result); + if (FunctionScopes.back()->FirstReturnLoc.isInvalid()) + FunctionScopes.back()->FirstReturnLoc = ReturnLoc; + return Result; } @@ -3291,6 +3315,9 @@ StmtResult Sema::BuildReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { if (Result->getNRVOCandidate()) FunctionScopes.back()->Returns.push_back(Result); + if (FunctionScopes.back()->FirstReturnLoc.isInvalid()) + FunctionScopes.back()->FirstReturnLoc = ReturnLoc; + return Result; } diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index 117ae56969..176e57165f 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -1287,6 +1287,30 @@ public: Constraints, Clobbers, Exprs, EndLoc); } + /// \brief Build a new co_return statement. + /// + /// By default, performs semantic analysis to build the new statement. + /// Subclasses may override this routine to provide different behavior. + StmtResult RebuildCoreturnStmt(SourceLocation CoreturnLoc, Expr *Result) { + return getSema().BuildCoreturnStmt(CoreturnLoc, 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 RebuildCoawaitExpr(SourceLocation CoawaitLoc, Expr *Result) { + return getSema().BuildCoawaitExpr(CoawaitLoc, Result); + } + + /// \brief Build a new co_yield expression. + /// + /// By default, performs semantic analysis to build the new expression. + /// Subclasses may override this routine to provide different behavior. + ExprResult RebuildCoyieldExpr(SourceLocation CoyieldLoc, Expr *Result) { + return getSema().BuildCoyieldExpr(CoyieldLoc, Result); + } + /// \brief Build a new Objective-C \@try statement. /// /// By default, performs semantic analysis to build the new statement. @@ -1715,6 +1739,7 @@ public: /// By default, performs semantic analysis to build the new statement. /// Subclasses may override this routine to provide different behavior. StmtResult RebuildCXXForRangeStmt(SourceLocation ForLoc, + SourceLocation CoawaitLoc, SourceLocation ColonLoc, Stmt *Range, Stmt *BeginEnd, Expr *Cond, Expr *Inc, @@ -1737,7 +1762,6 @@ public: } } - SourceLocation CoawaitLoc; // FIXME return getSema().BuildCXXForRangeStmt(ForLoc, CoawaitLoc, ColonLoc, Range, BeginEnd, Cond, Inc, LoopVar, RParenLoc, @@ -6403,6 +6427,56 @@ TreeTransform::TransformMSAsmStmt(MSAsmStmt *S) { TransformedExprs, S->getEndLoc()); } +// C++ Coroutines TS + +template +StmtResult +TreeTransform::TransformCoroutineBodyStmt(CoroutineBodyStmt *S) { + // The coroutine body should be re-formed by the caller if necessary. + return getDerived().TransformStmt(S->getBody()); +} + +template +StmtResult +TreeTransform::TransformCoreturnStmt(CoreturnStmt *S) { + ExprResult Result = getDerived().TransformInitializer(S->getOperand(), + /*NotCopyInit*/false); + if (Result.isInvalid()) + return StmtError(); + + // 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().RebuildCoreturnStmt(S->getKeywordLoc(), Result.get()); +} + +template +ExprResult +TreeTransform::TransformCoawaitExpr(CoawaitExpr *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().RebuildCoawaitExpr(E->getKeywordLoc(), Result.get()); +} + +template +ExprResult +TreeTransform::TransformCoyieldExpr(CoyieldExpr *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().RebuildCoyieldExpr(E->getKeywordLoc(), Result.get()); +} + +// Objective-C Statements. + template StmtResult TreeTransform::TransformObjCAtTryStmt(ObjCAtTryStmt *S) { @@ -6692,6 +6766,7 @@ TreeTransform::TransformCXXForRangeStmt(CXXForRangeStmt *S) { Inc.get() != S->getInc() || LoopVar.get() != S->getLoopVarStmt()) { NewStmt = getDerived().RebuildCXXForRangeStmt(S->getForLoc(), + S->getCoawaitLoc(), S->getColonLoc(), Range.get(), BeginEnd.get(), Cond.get(), Inc.get(), LoopVar.get(), @@ -6708,6 +6783,7 @@ TreeTransform::TransformCXXForRangeStmt(CXXForRangeStmt *S) { // it now so we have a new statement to attach the body to. if (Body.get() != S->getBody() && NewStmt.get() == S) { NewStmt = getDerived().RebuildCXXForRangeStmt(S->getForLoc(), + S->getCoawaitLoc(), S->getColonLoc(), Range.get(), BeginEnd.get(), Cond.get(), Inc.get(), LoopVar.get(), diff --git a/lib/Serialization/ASTReaderStmt.cpp b/lib/Serialization/ASTReaderStmt.cpp index 5f9f052292..ab82eee10e 100644 --- a/lib/Serialization/ASTReaderStmt.cpp +++ b/lib/Serialization/ASTReaderStmt.cpp @@ -381,6 +381,26 @@ void ASTStmtReader::VisitMSAsmStmt(MSAsmStmt *S) { Constraints, Exprs, Clobbers); } +void ASTStmtReader::VisitCoroutineBodyStmt(CoroutineBodyStmt *S) { + // FIXME: Implement coroutine serialization. + llvm_unreachable("unimplemented"); +} + +void ASTStmtReader::VisitCoreturnStmt(CoreturnStmt *S) { + // FIXME: Implement coroutine serialization. + llvm_unreachable("unimplemented"); +} + +void ASTStmtReader::VisitCoawaitExpr(CoawaitExpr *S) { + // FIXME: Implement coroutine serialization. + llvm_unreachable("unimplemented"); +} + +void ASTStmtReader::VisitCoyieldExpr(CoyieldExpr *S) { + // FIXME: Implement coroutine serialization. + llvm_unreachable("unimplemented"); +} + void ASTStmtReader::VisitCapturedStmt(CapturedStmt *S) { VisitStmt(S); ++Idx; @@ -1178,9 +1198,10 @@ void ASTStmtReader::VisitCXXTryStmt(CXXTryStmt *S) { void ASTStmtReader::VisitCXXForRangeStmt(CXXForRangeStmt *S) { VisitStmt(S); - S->setForLoc(ReadSourceLocation(Record, Idx)); - S->setColonLoc(ReadSourceLocation(Record, Idx)); - S->setRParenLoc(ReadSourceLocation(Record, Idx)); + S->ForLoc = ReadSourceLocation(Record, Idx); + S->CoawaitLoc = ReadSourceLocation(Record, Idx); + S->ColonLoc = ReadSourceLocation(Record, Idx); + S->RParenLoc = ReadSourceLocation(Record, Idx); S->setRangeStmt(Reader.ReadSubStmt()); S->setBeginEndStmt(Reader.ReadSubStmt()); S->setCond(Reader.ReadSubExpr()); diff --git a/lib/Serialization/ASTWriterStmt.cpp b/lib/Serialization/ASTWriterStmt.cpp index 7fce321791..74dc56e72f 100644 --- a/lib/Serialization/ASTWriterStmt.cpp +++ b/lib/Serialization/ASTWriterStmt.cpp @@ -287,6 +287,26 @@ void ASTStmtWriter::VisitMSAsmStmt(MSAsmStmt *S) { Code = serialization::STMT_MSASM; } +void ASTStmtWriter::VisitCoroutineBodyStmt(CoroutineBodyStmt *S) { + // FIXME: Implement coroutine serialization. + llvm_unreachable("unimplemented"); +} + +void ASTStmtWriter::VisitCoreturnStmt(CoreturnStmt *S) { + // FIXME: Implement coroutine serialization. + llvm_unreachable("unimplemented"); +} + +void ASTStmtWriter::VisitCoawaitExpr(CoawaitExpr *S) { + // FIXME: Implement coroutine serialization. + llvm_unreachable("unimplemented"); +} + +void ASTStmtWriter::VisitCoyieldExpr(CoyieldExpr *S) { + // FIXME: Implement coroutine serialization. + llvm_unreachable("unimplemented"); +} + void ASTStmtWriter::VisitCapturedStmt(CapturedStmt *S) { VisitStmt(S); // NumCaptures @@ -1135,6 +1155,7 @@ void ASTStmtWriter::VisitCXXTryStmt(CXXTryStmt *S) { void ASTStmtWriter::VisitCXXForRangeStmt(CXXForRangeStmt *S) { VisitStmt(S); Writer.AddSourceLocation(S->getForLoc(), Record); + Writer.AddSourceLocation(S->getCoawaitLoc(), Record); Writer.AddSourceLocation(S->getColonLoc(), Record); Writer.AddSourceLocation(S->getRParenLoc(), Record); Writer.AddStmt(S->getRangeStmt()); diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp index eee4928c48..afef624f5d 100644 --- a/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -765,6 +765,10 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::PackExpansionExprClass: case Stmt::SubstNonTypeTemplateParmPackExprClass: case Stmt::FunctionParmPackExprClass: + case Stmt::CoroutineBodyStmtClass: + case Stmt::CoawaitExprClass: + case Stmt::CoreturnStmtClass: + case Stmt::CoyieldExprClass: case Stmt::SEHTryStmtClass: case Stmt::SEHExceptStmtClass: case Stmt::SEHLeaveStmtClass: diff --git a/test/Parser/cxx1z-coroutines.cpp b/test/Parser/cxx1z-coroutines.cpp index 3e698404a6..1ea2635aac 100644 --- a/test/Parser/cxx1z-coroutines.cpp +++ b/test/Parser/cxx1z-coroutines.cpp @@ -9,7 +9,7 @@ U f(T t) { 1 + co_yield t; // expected-error {{expected expression}} auto x = co_await t; - auto y = co_yield t; + auto y = co_yield t; // expected-error {{void}} FIXME for co_await (int x : t) {} for co_await (int x = 0; x != 10; ++x) {} // expected-error {{'co_await' modifier can only be applied to range-based for loop}} diff --git a/test/SemaCXX/coroutines.cpp b/test/SemaCXX/coroutines.cpp index ae24dd2979..3e18187295 100644 --- a/test/SemaCXX/coroutines.cpp +++ b/test/SemaCXX/coroutines.cpp @@ -1,37 +1,68 @@ // RUN: %clang_cc1 -std=c++14 -fcoroutines -verify %s +struct awaitable { + bool await_ready(); + void await_suspend(); // FIXME: coroutine_handle + void await_resume(); +} a; + +void no_coroutine_traits() { + co_await a; // expected-error {{need to include }} +} + +namespace std { + template struct coroutine_traits; // expected-note {{declared here}} +}; + +void no_specialization() { + co_await a; // expected-error {{implicit instantiation of undefined template 'std::coroutine_traits'}} +} + +template struct std::coroutine_traits {}; + +int no_promise_type() { + co_await a; // expected-error {{this function cannot be a coroutine: 'coroutine_traits' has no member named 'promise_type'}} +} + +struct promise; // expected-note {{forward declaration}} +template struct std::coroutine_traits { using promise_type = promise; }; + + // FIXME: This diagnostic is terrible. +void undefined_promise() { // expected-error {{variable has incomplete type 'promise_type'}} + co_await a; +} + +struct promise {}; + void mixed_yield() { - // FIXME: diagnose - co_yield 0; - return; + co_yield 0; // expected-note {{use of 'co_yield'}} + return; // expected-error {{not allowed in coroutine}} } void mixed_await() { - // FIXME: diagnose - co_await 0; - return; + co_await a; // expected-note {{use of 'co_await'}} + return; // expected-error {{not allowed in coroutine}} } void only_coreturn() { - // FIXME: diagnose - co_return; + co_return; // expected-warning {{'co_return' used in a function that uses neither 'co_await' nor 'co_yield'}} } void mixed_coreturn(bool b) { - // FIXME: diagnose if (b) - co_return; + // expected-warning@+1 {{'co_return' used in a function that uses neither}} + co_return; // expected-note {{use of 'co_return'}} else - return; + return; // expected-error {{not allowed in coroutine}} } struct CtorDtor { CtorDtor() { co_yield 0; // expected-error {{'co_yield' cannot be used in a constructor}} } - CtorDtor(int n) { + CtorDtor(awaitable a) { // The spec doesn't say this is ill-formed, but it must be. - co_await n; // expected-error {{'co_await' cannot be used in a constructor}} + co_await a; // expected-error {{'co_await' cannot be used in a constructor}} } ~CtorDtor() { co_return 0; // expected-error {{'co_return' cannot be used in a destructor}} @@ -42,10 +73,35 @@ struct CtorDtor { } }; -constexpr void constexpr_coroutine() { - co_yield 0; // expected-error {{'co_yield' cannot be used in a constexpr function}} +constexpr void constexpr_coroutine() { // expected-error {{never produces a constant expression}} + co_yield 0; // expected-error {{'co_yield' cannot be used in a constexpr function}} expected-note {{subexpression}} } void varargs_coroutine(const char *, ...) { - co_await 0; // expected-error {{'co_await' cannot be used in a varargs function}} + co_await a; // expected-error {{'co_await' cannot be used in a varargs function}} +} + +struct outer {}; + +namespace dependent_operator_co_await_lookup { + template void await_template(T t) { + // no unqualified lookup results + co_await t; // expected-error {{no member named 'await_ready' in 'dependent_operator_co_await_lookup::not_awaitable'}} + // expected-error@-1 {{call to function 'operator co_await' that is neither visible in the template definition nor found by argument-dependent lookup}} + }; + template void await_template(awaitable); + + struct indirectly_awaitable { indirectly_awaitable(outer); }; + awaitable operator co_await(indirectly_awaitable); // expected-note {{should be declared prior to}} + template void await_template(indirectly_awaitable); + + struct not_awaitable {}; + template void await_template(not_awaitable); // expected-note {{instantiation}} + + template void await_template_2(T t) { + // one unqualified lookup result + co_await t; + }; + template void await_template(outer); // expected-note {{instantiation}} + template void await_template_2(outer); } diff --git a/tools/libclang/CXCursor.cpp b/tools/libclang/CXCursor.cpp index 9a866c509d..0e47bf983d 100644 --- a/tools/libclang/CXCursor.cpp +++ b/tools/libclang/CXCursor.cpp @@ -227,6 +227,10 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, case Stmt::AtomicExprClass: case Stmt::BinaryConditionalOperatorClass: case Stmt::TypeTraitExprClass: + case Stmt::CoroutineBodyStmtClass: + case Stmt::CoawaitExprClass: + case Stmt::CoreturnStmtClass: + case Stmt::CoyieldExprClass: case Stmt::CXXBindTemporaryExprClass: case Stmt::CXXDefaultArgExprClass: case Stmt::CXXDefaultInitExprClass: -- 2.40.0