r253811 - [coroutines] Factor out co_await representation into common base class for co_await and co_yield, and use it to hold await_* calls.

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Sat Nov 21 18:57:18 PST 2015


Author: rsmith
Date: Sat Nov 21 20:57:17 2015
New Revision: 253811

URL: http://llvm.org/viewvc/llvm-project?rev=253811&view=rev
Log:
[coroutines] Factor out co_await representation into common base class for co_await and co_yield, and use it to hold await_* calls.

Modified:
    cfe/trunk/include/clang/AST/Expr.h
    cfe/trunk/include/clang/AST/ExprCXX.h
    cfe/trunk/include/clang/Basic/StmtNodes.td
    cfe/trunk/lib/Sema/SemaChecking.cpp
    cfe/trunk/lib/Sema/SemaCoroutine.cpp
    cfe/trunk/test/Parser/cxx1z-coroutines.cpp
    cfe/trunk/test/SemaCXX/coroutines.cpp

Modified: cfe/trunk/include/clang/AST/Expr.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Expr.h?rev=253811&r1=253810&r2=253811&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/Expr.h (original)
+++ cfe/trunk/include/clang/AST/Expr.h Sat Nov 21 20:57:17 2015
@@ -4826,6 +4826,14 @@ public:
   const_semantics_iterator semantics_end() const {
     return getSubExprsBuffer() + getNumSubExprs();
   }
+
+  llvm::iterator_range<semantics_iterator> semantics() {
+    return llvm::make_range(semantics_begin(), semantics_end());
+  }
+  llvm::iterator_range<const_semantics_iterator> semantics() const {
+    return llvm::make_range(semantics_begin(), semantics_end());
+  }
+
   Expr *getSemanticExpr(unsigned index) {
     assert(index + 1 < getNumSubExprs());
     return getSubExprsBuffer()[index + 1];

Modified: cfe/trunk/include/clang/AST/ExprCXX.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/ExprCXX.h?rev=253811&r1=253810&r2=253811&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/ExprCXX.h (original)
+++ cfe/trunk/include/clang/AST/ExprCXX.h Sat Nov 21 20:57:17 2015
@@ -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.
 ///
-///   <operand>.operator co_await() or operator co_await(<operand>)
-///   <result>.await_ready()
-///   <result>.await_suspend(h)
-///   <result>.await_resume()
-///
-/// where h is a handle to the coroutine, and <result> 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;
+/// 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 { Operand, Ready, Suspend, Resume, Count };
+  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<Expr*>(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<Expr*>(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 <promise>.yield_value(<operand>), 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<Expr*>(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<Expr*>(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; }
+  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) {}
 
-  SourceLocation getLocStart() const LLVM_READONLY { return CoyieldLoc; }
-  SourceLocation getLocEnd() const LLVM_READONLY {
-    return Operand->getLocEnd();
+  Expr *getOperand() const {
+    // FIXME: Dig out the actual operand or store it.
+    return getCommonExpr();
   }
 
-  child_range children() {
-    Stmt **Which = PromiseCall ? &PromiseCall : &Operand;
-    return child_range(Which, Which + 1);
+  static bool classof(const Stmt *T) {
+    return T->getStmtClass() == CoawaitExprClass;
+  }
+};
+
+/// \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) {

Modified: cfe/trunk/include/clang/Basic/StmtNodes.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/StmtNodes.td?rev=253811&r1=253810&r2=253811&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/StmtNodes.td (original)
+++ cfe/trunk/include/clang/Basic/StmtNodes.td Sat Nov 21 20:57:17 2015
@@ -145,8 +145,9 @@ def LambdaExpr : DStmt<Expr>;
 def CXXFoldExpr : DStmt<Expr>;
 
 // C++ Coroutines TS expressions
-def CoawaitExpr : DStmt<Expr>;
-def CoyieldExpr : DStmt<Expr>;
+def CoroutineSuspendExpr : DStmt<Expr, 1>;
+def CoawaitExpr : DStmt<CoroutineSuspendExpr>;
+def CoyieldExpr : DStmt<CoroutineSuspendExpr>;
 
 // Obj-C Expressions.
 def ObjCStringLiteral : DStmt<Expr>;

Modified: cfe/trunk/lib/Sema/SemaChecking.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaChecking.cpp?rev=253811&r1=253810&r2=253811&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaChecking.cpp (original)
+++ cfe/trunk/lib/Sema/SemaChecking.cpp Sat Nov 21 20:57:17 2015
@@ -7493,18 +7493,16 @@ void AnalyzeImplicitConversions(Sema &S,
     CheckImplicitConversion(S, E, T, CC);
 
   // Now continue drilling into this expression.
-  
-  if (PseudoObjectExpr * POE = dyn_cast<PseudoObjectExpr>(E)) {
-    if (POE->getResultExpr())
-      E = POE->getResultExpr();
-  }
-  
-  if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(E)) {
-    if (OVE->getSourceExpr())
-      AnalyzeImplicitConversions(S, OVE->getSourceExpr(), CC);
-    return;
+
+  if (PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(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<OpaqueValueExpr>(SE))
+        AnalyzeImplicitConversions(S, OVE->getSourceExpr(), CC);
   }
-  
+
   // Skip past explicit casts.
   if (isa<ExplicitCastExpr>(E)) {
     E = cast<ExplicitCastExpr>(E)->getSubExpr()->IgnoreParenImpCasts();

Modified: cfe/trunk/lib/Sema/SemaCoroutine.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaCoroutine.cpp?rev=253811&r1=253810&r2=253811&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaCoroutine.cpp (original)
+++ cfe/trunk/lib/Sema/SemaCoroutine.cpp Sat Nov 21 20:57:17 2015
@@ -300,8 +300,22 @@ ExprResult Sema::BuildCoyieldExpr(Source
     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;
 }

Modified: cfe/trunk/test/Parser/cxx1z-coroutines.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Parser/cxx1z-coroutines.cpp?rev=253811&r1=253810&r2=253811&view=diff
==============================================================================
--- cfe/trunk/test/Parser/cxx1z-coroutines.cpp (original)
+++ cfe/trunk/test/Parser/cxx1z-coroutines.cpp Sat Nov 21 20:57:17 2015
@@ -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}}

Modified: cfe/trunk/test/SemaCXX/coroutines.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/coroutines.cpp?rev=253811&r1=253810&r2=253811&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/coroutines.cpp (original)
+++ cfe/trunk/test/SemaCXX/coroutines.cpp Sat Nov 21 20:57:17 2015
@@ -46,9 +46,12 @@ void undefined_promise() { // expected-e
 
 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() {




More information about the cfe-commits mailing list