From: Richard Smith Date: Sun, 22 Nov 2015 02:57:17 +0000 (+0000) Subject: [coroutines] Factor out co_await representation into common base class for co_await... X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=41c05c21b76ddada8c75178921ef6c2c9c92dba9;p=clang [coroutines] Factor out co_await representation into common base class for co_await and co_yield, and use it to hold await_* calls. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@253811 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h index e3c17e05de..73ccea8b08 100644 --- a/include/clang/AST/Expr.h +++ b/include/clang/AST/Expr.h @@ -4826,6 +4826,14 @@ public: const_semantics_iterator semantics_end() const { return getSubExprsBuffer() + getNumSubExprs(); } + + llvm::iterator_range semantics() { + return llvm::make_range(semantics_begin(), semantics_end()); + } + llvm::iterator_range semantics() const { + return llvm::make_range(semantics_begin(), semantics_end()); + } + Expr *getSemanticExpr(unsigned index) { assert(index + 1 < getNumSubExprs()); return getSubExprsBuffer()[index + 1]; diff --git a/include/clang/AST/ExprCXX.h b/include/clang/AST/ExprCXX.h index cd14afaf40..797ae62d42 100644 --- a/include/clang/AST/ExprCXX.h +++ b/include/clang/AST/ExprCXX.h @@ -4008,65 +4008,61 @@ 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: +/// \brief Represents an expression that might suspend coroutine execution; +/// either a co_await or co_yield expression. /// -/// .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 }; +/// Evaluation of this expression first evaluates its 'ready' expression. If +/// that returns 'false': +/// -- execution of the coroutine is suspended +/// -- the 'suspend' expression is evaluated +/// -- if the 'suspend' expression returns 'false', the coroutine is +/// resumed +/// -- otherwise, control passes back to the resumer. +/// If the coroutine is not suspended, or when it is resumed, the 'resume' +/// expression is evaluated, and its result is the result of the overall +/// expression. +class CoroutineSuspendExpr : public Expr { + SourceLocation KeywordLoc; + + enum SubExpr { Common, 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[CoawaitExpr::Operand] = Operand; - SubExprs[CoawaitExpr::Ready] = Ready; - SubExprs[CoawaitExpr::Suspend] = Suspend; - SubExprs[CoawaitExpr::Resume] = Resume; - } - CoawaitExpr(SourceLocation CoawaitLoc, QualType Ty, Expr *Operand) - : Expr(CoawaitExprClass, Ty, VK_RValue, OK_Ordinary, - true, true, true, Operand->containsUnexpandedParameterPack()), - CoawaitLoc(CoawaitLoc) { - assert(Operand->isTypeDependent() && Ty->isDependentType() && - "wrong constructor for non-dependent co_await expression"); - SubExprs[CoawaitExpr::Operand] = Operand; - SubExprs[CoawaitExpr::Ready] = nullptr; - SubExprs[CoawaitExpr::Suspend] = nullptr; - SubExprs[CoawaitExpr::Resume] = nullptr; - } - CoawaitExpr(EmptyShell Empty) : Expr(CoawaitExprClass, Empty) { - SubExprs[CoawaitExpr::Operand] = nullptr; - SubExprs[CoawaitExpr::Ready] = nullptr; - SubExprs[CoawaitExpr::Suspend] = nullptr; - SubExprs[CoawaitExpr::Resume] = nullptr; - } - - SourceLocation getKeywordLoc() const { return CoawaitLoc; } - Expr *getOperand() const { - return static_cast(SubExprs[SubExpr::Operand]); + CoroutineSuspendExpr(StmtClass SC, SourceLocation KeywordLoc, Expr *Common, + Expr *Ready, Expr *Suspend, Expr *Resume) + : Expr(SC, Resume->getType(), Resume->getValueKind(), + Resume->getObjectKind(), Resume->isTypeDependent(), + Resume->isValueDependent(), Common->isInstantiationDependent(), + Common->containsUnexpandedParameterPack()), + KeywordLoc(KeywordLoc) { + SubExprs[SubExpr::Common] = Common; + SubExprs[SubExpr::Ready] = Ready; + SubExprs[SubExpr::Suspend] = Suspend; + SubExprs[SubExpr::Resume] = Resume; + } + CoroutineSuspendExpr(StmtClass SC, SourceLocation KeywordLoc, QualType Ty, + Expr *Common) + : Expr(SC, Ty, VK_RValue, OK_Ordinary, true, true, true, + Common->containsUnexpandedParameterPack()), + KeywordLoc(KeywordLoc) { + assert(Common->isTypeDependent() && Ty->isDependentType() && + "wrong constructor for non-dependent co_await/co_yield expression"); + SubExprs[SubExpr::Common] = Common; + SubExprs[SubExpr::Ready] = nullptr; + SubExprs[SubExpr::Suspend] = nullptr; + SubExprs[SubExpr::Resume] = nullptr; + } + CoroutineSuspendExpr(StmtClass SC, EmptyShell Empty) : Expr(SC, Empty) { + SubExprs[SubExpr::Common] = nullptr; + SubExprs[SubExpr::Ready] = nullptr; + SubExprs[SubExpr::Suspend] = nullptr; + SubExprs[SubExpr::Resume] = nullptr; + } + + SourceLocation getKeywordLoc() const { return KeywordLoc; } + Expr *getCommonExpr() const { + return static_cast(SubExprs[SubExpr::Common]); } Expr *getReadyExpr() const { @@ -4080,10 +4076,10 @@ public: } SourceLocation getLocStart() const LLVM_READONLY { - return CoawaitLoc; + return KeywordLoc; } SourceLocation getLocEnd() const LLVM_READONLY { - return getOperand()->getLocEnd(); + return getCommonExpr()->getLocEnd(); } child_range children() { @@ -4091,52 +4087,50 @@ public: } static bool classof(const Stmt *T) { - return T->getStmtClass() == CoawaitExprClass; + return T->getStmtClass() == CoawaitExprClass || + T->getStmtClass() == CoyieldExprClass; } }; -/// \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; - +/// \brief Represents a 'co_await' expression. +class CoawaitExpr : public CoroutineSuspendExpr { 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); } + CoawaitExpr(SourceLocation CoawaitLoc, Expr *Operand, Expr *Ready, + Expr *Suspend, Expr *Resume) + : CoroutineSuspendExpr(CoawaitExprClass, CoawaitLoc, Operand, Ready, + Suspend, Resume) {} + CoawaitExpr(SourceLocation CoawaitLoc, QualType Ty, Expr *Operand) + : CoroutineSuspendExpr(CoawaitExprClass, CoawaitLoc, Ty, Operand) {} + CoawaitExpr(EmptyShell Empty) + : CoroutineSuspendExpr(CoawaitExprClass, Empty) {} - /// \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; } + Expr *getOperand() const { + // FIXME: Dig out the actual operand or store it. + return getCommonExpr(); + } - SourceLocation getLocStart() const LLVM_READONLY { return CoyieldLoc; } - SourceLocation getLocEnd() const LLVM_READONLY { - return Operand->getLocEnd(); + static bool classof(const Stmt *T) { + return T->getStmtClass() == CoawaitExprClass; } +}; - child_range children() { - Stmt **Which = PromiseCall ? &PromiseCall : &Operand; - return child_range(Which, Which + 1); +/// \brief Represents a 'co_yield' expression. +class CoyieldExpr : public CoroutineSuspendExpr { + friend class ASTStmtReader; +public: + CoyieldExpr(SourceLocation CoyieldLoc, Expr *Operand, Expr *Ready, + Expr *Suspend, Expr *Resume) + : CoroutineSuspendExpr(CoyieldExprClass, CoyieldLoc, Operand, Ready, + Suspend, Resume) {} + CoyieldExpr(SourceLocation CoyieldLoc, QualType Ty, Expr *Operand) + : CoroutineSuspendExpr(CoyieldExprClass, CoyieldLoc, Ty, Operand) {} + CoyieldExpr(EmptyShell Empty) + : CoroutineSuspendExpr(CoyieldExprClass, Empty) {} + + Expr *getOperand() const { + // FIXME: Dig out the actual operand or store it. + return getCommonExpr(); } static bool classof(const Stmt *T) { diff --git a/include/clang/Basic/StmtNodes.td b/include/clang/Basic/StmtNodes.td index 6e665322e2..7c4b9d28d7 100644 --- a/include/clang/Basic/StmtNodes.td +++ b/include/clang/Basic/StmtNodes.td @@ -145,8 +145,9 @@ def LambdaExpr : DStmt; def CXXFoldExpr : DStmt; // C++ Coroutines TS expressions -def CoawaitExpr : DStmt; -def CoyieldExpr : DStmt; +def CoroutineSuspendExpr : DStmt; +def CoawaitExpr : DStmt; +def CoyieldExpr : DStmt; // Obj-C Expressions. def ObjCStringLiteral : DStmt; diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp index 9e53c478ef..e2d044c00e 100644 --- a/lib/Sema/SemaChecking.cpp +++ b/lib/Sema/SemaChecking.cpp @@ -7493,18 +7493,16 @@ void AnalyzeImplicitConversions(Sema &S, Expr *OrigE, SourceLocation CC) { CheckImplicitConversion(S, E, T, CC); // Now continue drilling into this expression. - - if (PseudoObjectExpr * POE = dyn_cast(E)) { - if (POE->getResultExpr()) - E = POE->getResultExpr(); - } - - if (const OpaqueValueExpr *OVE = dyn_cast(E)) { - if (OVE->getSourceExpr()) - AnalyzeImplicitConversions(S, OVE->getSourceExpr(), CC); - return; + + if (PseudoObjectExpr *POE = dyn_cast(E)) { + // The bound subexpressions in a PseudoObjectExpr are not reachable + // as transitive children. + // FIXME: Use a more uniform representation for this. + for (auto *SE : POE->semantics()) + if (auto *OVE = dyn_cast(SE)) + AnalyzeImplicitConversions(S, OVE->getSourceExpr(), CC); } - + // Skip past explicit casts. if (isa(E)) { E = cast(E)->getSubExpr()->IgnoreParenImpCasts(); diff --git a/lib/Sema/SemaCoroutine.cpp b/lib/Sema/SemaCoroutine.cpp index 8d91e43f41..a0d24b8cec 100644 --- a/lib/Sema/SemaCoroutine.cpp +++ b/lib/Sema/SemaCoroutine.cpp @@ -300,8 +300,22 @@ ExprResult Sema::BuildCoyieldExpr(SourceLocation Loc, Expr *E) { E = R.get(); } - // FIXME: Build await_* calls. - Expr *Res = new (Context) CoyieldExpr(Loc, Context.VoidTy, E); + if (E->getType()->isDependentType()) { + Expr *Res = new (Context) CoyieldExpr(Loc, Context.DependentTy, E); + Coroutine->CoroutineStmts.push_back(Res); + return Res; + } + + // 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) CoyieldExpr(Loc, E, RSS.Results[0], RSS.Results[1], + RSS.Results[2]); Coroutine->CoroutineStmts.push_back(Res); return Res; } diff --git a/test/Parser/cxx1z-coroutines.cpp b/test/Parser/cxx1z-coroutines.cpp index 1ea2635aac..3e698404a6 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; // expected-error {{void}} FIXME + auto y = co_yield t; 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 53226f7af2..d5968fcd30 100644 --- a/test/SemaCXX/coroutines.cpp +++ b/test/SemaCXX/coroutines.cpp @@ -46,9 +46,12 @@ void undefined_promise() { // expected-error {{variable has incomplete type 'pro struct yielded_thing { const char *p; short a, b; }; +struct not_awaitable {}; + struct promise { awaitable yield_value(int); // expected-note {{candidate}} awaitable yield_value(yielded_thing); // expected-note {{candidate}} + not_awaitable yield_value(void()); // expected-note {{candidate}} }; void yield() { @@ -58,6 +61,8 @@ void yield() { co_yield {"foo", __LONG_LONG_MAX__}; // expected-error {{cannot be narrowed}} expected-note {{explicit cast}} expected-warning {{changes value}} co_yield {"foo"}; co_yield "foo"; // expected-error {{no matching}} + co_yield 1.0; + co_yield yield; // expected-error {{no member named 'await_ready' in 'not_awaitable'}} } void mixed_yield() {