r297356 - [coroutines] Build and pass coroutine_handle to await_suspend

Gor Nishanov via cfe-commits cfe-commits at lists.llvm.org
Wed Mar 8 19:09:44 PST 2017


Author: gornishanov
Date: Wed Mar  8 21:09:43 2017
New Revision: 297356

URL: http://llvm.org/viewvc/llvm-project?rev=297356&view=rev
Log:
[coroutines] Build and pass coroutine_handle to await_suspend

Summary:
This patch adds passing a coroutine_handle object to await_suspend calls.
It builds the coroutine_handle using coroutine_handle<PromiseType>::from_address(__builtin_coro_frame()).

(a revision of https://reviews.llvm.org/D26316 that for some reason refuses to apply via arc patch)

Reviewers: GorNishanov

Subscribers: mehdi_amini, cfe-commits, EricWF

Differential Revision: https://reviews.llvm.org/D30769

Modified:
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/lib/Sema/SemaCoroutine.cpp
    cfe/trunk/lib/Sema/SemaExprMember.cpp
    cfe/trunk/test/CodeGenCoroutines/coro-alloc.cpp
    cfe/trunk/test/CodeGenCoroutines/coro-return.cpp
    cfe/trunk/test/SemaCXX/coreturn.cpp
    cfe/trunk/test/SemaCXX/coroutines.cpp

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=297356&r1=297355&r2=297356&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Wed Mar  8 21:09:43 2017
@@ -8828,8 +8828,13 @@ def err_coroutine_invalid_func_context :
   "|a copy assignment operator|a move assignment operator|the 'main' function"
   "|a constexpr function|a function with a deduced return type"
   "|a varargs function}0">;
-def err_implied_std_coroutine_traits_not_found : Error<
-  "you need to include <experimental/coroutine> before defining a coroutine">;
+def err_implied_coroutine_type_not_found : Error<
+  "%0 type was not found; include <experimental/coroutine> before defining "
+  "a coroutine">;
+def err_malformed_std_coroutine_handle : Error<
+  "std::experimental::coroutine_handle must be a class template">;
+def err_coroutine_handle_missing_member : Error<
+  "std::experimental::coroutine_handle missing a member named '%0'">;
 def err_malformed_std_coroutine_traits : Error<
   "'std::experimental::coroutine_traits' must be a class template">;
 def err_implied_std_coroutine_traits_promise_type_not_found : Error<
@@ -8838,7 +8843,7 @@ def err_implied_std_coroutine_traits_pro
   "this function cannot be a coroutine: %0 is not a class">;
 def err_coroutine_promise_type_incomplete : Error<
   "this function cannot be a coroutine: %0 is an incomplete type">;
-def err_coroutine_traits_missing_specialization : Error<
+def err_coroutine_type_missing_specialization : Error<
   "this function cannot be a coroutine: missing definition of "
   "specialization %q0">;
 def err_implied_std_current_exception_not_found : Error<

Modified: cfe/trunk/lib/Sema/SemaCoroutine.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaCoroutine.cpp?rev=297356&r1=297355&r2=297356&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaCoroutine.cpp (original)
+++ cfe/trunk/lib/Sema/SemaCoroutine.cpp Wed Mar  8 21:09:43 2017
@@ -39,14 +39,16 @@ static QualType lookupPromiseType(Sema &
   // FIXME: Cache std::coroutine_traits once we've found it.
   NamespaceDecl *StdExp = S.lookupStdExperimentalNamespace();
   if (!StdExp) {
-    S.Diag(KwLoc, diag::err_implied_std_coroutine_traits_not_found);
+    S.Diag(KwLoc, diag::err_implied_coroutine_type_not_found)
+        << "std::experimental::coroutine_traits";
     return QualType();
   }
 
   LookupResult Result(S, &S.PP.getIdentifierTable().get("coroutine_traits"),
                       FuncLoc, Sema::LookupOrdinaryName);
   if (!S.LookupQualifiedName(Result, StdExp)) {
-    S.Diag(KwLoc, diag::err_implied_std_coroutine_traits_not_found);
+    S.Diag(KwLoc, diag::err_implied_coroutine_type_not_found)
+        << "std::experimental::coroutine_traits";
     return QualType();
   }
 
@@ -76,7 +78,7 @@ static QualType lookupPromiseType(Sema &
   if (CoroTrait.isNull())
     return QualType();
   if (S.RequireCompleteType(KwLoc, CoroTrait,
-                            diag::err_coroutine_traits_missing_specialization))
+                            diag::err_coroutine_type_missing_specialization))
     return QualType();
 
   auto *RD = CoroTrait->getAsCXXRecordDecl();
@@ -116,6 +118,51 @@ static QualType lookupPromiseType(Sema &
   return PromiseType;
 }
 
+/// Look up the std::coroutine_traits<...>::promise_type for the given
+/// function type.
+static QualType lookupCoroutineHandleType(Sema &S, QualType PromiseType,
+                                          SourceLocation Loc) {
+  if (PromiseType.isNull())
+    return QualType();
+
+  NamespaceDecl *StdExp = S.lookupStdExperimentalNamespace();
+  assert(StdExp && "Should already be diagnosed");
+
+  LookupResult Result(S, &S.PP.getIdentifierTable().get("coroutine_handle"),
+                      Loc, Sema::LookupOrdinaryName);
+  if (!S.LookupQualifiedName(Result, StdExp)) {
+    S.Diag(Loc, diag::err_implied_coroutine_type_not_found)
+        << "std::experimental::coroutine_handle";
+    return QualType();
+  }
+
+  ClassTemplateDecl *CoroHandle = Result.getAsSingle<ClassTemplateDecl>();
+  if (!CoroHandle) {
+    Result.suppressDiagnostics();
+    // We found something weird. Complain about the first thing we found.
+    NamedDecl *Found = *Result.begin();
+    S.Diag(Found->getLocation(), diag::err_malformed_std_coroutine_handle);
+    return QualType();
+  }
+
+  // Form template argument list for coroutine_handle<Promise>.
+  TemplateArgumentListInfo Args(Loc, Loc);
+  Args.addArgument(TemplateArgumentLoc(
+      TemplateArgument(PromiseType),
+      S.Context.getTrivialTypeSourceInfo(PromiseType, Loc)));
+
+  // Build the template-id.
+  QualType CoroHandleType =
+      S.CheckTemplateIdType(TemplateName(CoroHandle), Loc, Args);
+  if (CoroHandleType.isNull())
+    return QualType();
+  if (S.RequireCompleteType(Loc, CoroHandleType,
+                            diag::err_coroutine_type_missing_specialization))
+    return QualType();
+
+  return CoroHandleType;
+}
+
 static bool isValidCoroutineContext(Sema &S, SourceLocation Loc,
                                     StringRef Keyword) {
   // 'co_await' and 'co_yield' are not permitted in unevaluated operands.
@@ -237,6 +284,32 @@ static Expr *buildBuiltinCall(Sema &S, S
   return Call.get();
 }
 
+static ExprResult buildCoroutineHandle(Sema &S, QualType PromiseType,
+                                       SourceLocation Loc) {
+  QualType CoroHandleType = lookupCoroutineHandleType(S, PromiseType, Loc);
+  if (CoroHandleType.isNull())
+    return ExprError();
+
+  DeclContext *LookupCtx = S.computeDeclContext(CoroHandleType);
+  LookupResult Found(S, &S.PP.getIdentifierTable().get("from_address"), Loc,
+                     Sema::LookupOrdinaryName);
+  if (!S.LookupQualifiedName(Found, LookupCtx)) {
+    S.Diag(Loc, diag::err_coroutine_handle_missing_member)
+        << "from_address";
+    return ExprError();
+  }
+
+  Expr *FramePtr =
+      buildBuiltinCall(S, Loc, Builtin::BI__builtin_coro_frame, {});
+
+  CXXScopeSpec SS;
+  ExprResult FromAddr =
+      S.BuildDeclarationNameExpr(SS, Found, /*NeedsADL=*/false);
+  if (FromAddr.isInvalid())
+    return ExprError();
+
+  return S.ActOnCallExpr(nullptr, FromAddr.get(), Loc, FramePtr, Loc);
+}
 
 struct ReadySuspendResumeResult {
   bool IsInvalid;
@@ -261,18 +334,23 @@ static ExprResult buildMemberCall(Sema &
 
 /// Build calls to await_ready, await_suspend, and await_resume for a co_await
 /// expression.
-static ReadySuspendResumeResult buildCoawaitCalls(Sema &S, SourceLocation Loc,
-                                                  Expr *E) {
+static ReadySuspendResumeResult buildCoawaitCalls(Sema &S, VarDecl *CoroPromise,
+                                                  SourceLocation Loc, Expr *E) {
   // Assume invalid until we see otherwise.
   ReadySuspendResumeResult Calls = {true, {}};
 
+  ExprResult CoroHandleRes = buildCoroutineHandle(S, CoroPromise->getType(), Loc);
+  if (CoroHandleRes.isInvalid())
+    return Calls;
+  Expr *CoroHandle = CoroHandleRes.get();
+
   const StringRef Funcs[] = {"await_ready", "await_suspend", "await_resume"};
+  MultiExprArg Args[] = {None, CoroHandle, None};
   for (size_t I = 0, N = llvm::array_lengthof(Funcs); I != N; ++I) {
     Expr *Operand = new (S.Context) OpaqueValueExpr(
       Loc, E->getType(), VK_LValue, E->getObjectKind(), E);
 
-    // FIXME: Pass coroutine handle to await_suspend.
-    ExprResult Result = buildMemberCall(S, Operand, Loc, Funcs[I], None);
+    ExprResult Result = buildMemberCall(S, Operand, Loc, Funcs[I], Args[I]);
     if (Result.isInvalid())
       return Calls;
     Calls.Results[I] = Result.get();
@@ -473,7 +551,8 @@ ExprResult Sema::BuildResolvedCoawaitExp
     E = CreateMaterializeTemporaryExpr(E->getType(), E, true);
 
   // Build the await_ready, await_suspend, await_resume calls.
-  ReadySuspendResumeResult RSS = buildCoawaitCalls(*this, Loc, E);
+  ReadySuspendResumeResult RSS =
+      buildCoawaitCalls(*this, Coroutine->CoroutinePromise, Loc, E);
   if (RSS.IsInvalid)
     return ExprError();
 
@@ -526,7 +605,8 @@ ExprResult Sema::BuildCoyieldExpr(Source
     E = CreateMaterializeTemporaryExpr(E->getType(), E, true);
 
   // Build the await_ready, await_suspend, await_resume calls.
-  ReadySuspendResumeResult RSS = buildCoawaitCalls(*this, Loc, E);
+  ReadySuspendResumeResult RSS =
+      buildCoawaitCalls(*this, Coroutine->CoroutinePromise, Loc, E);
   if (RSS.IsInvalid)
     return ExprError();
 
@@ -677,14 +757,30 @@ void Sema::CheckCompletedCoroutineBody(F
   FunctionScopeInfo *Fn = getCurFunction();
   assert(Fn && Fn->CoroutinePromise && "not a coroutine");
 
+  if (!Body) {
+    assert(FD->isInvalidDecl() &&
+           "a null body is only allowed for invalid declarations");
+    return;
+  }
+
+  if (isa<CoroutineBodyStmt>(Body)) {
+    // FIXME(EricWF): Nothing todo. the body is already a transformed coroutine
+    // body statement.
+    return;
+  }
+
   // Coroutines [stmt.return]p1:
   //   A return statement shall not appear in a coroutine.
   if (Fn->FirstReturnLoc.isValid()) {
     Diag(Fn->FirstReturnLoc, diag::err_return_in_coroutine);
-    auto *First = Fn->CoroutineStmts[0];
-    Diag(First->getLocStart(), diag::note_declared_coroutine_here)
-        << (isa<CoawaitExpr>(First) ? "co_await" :
-            isa<CoyieldExpr>(First) ? "co_yield" : "co_return");
+    // FIXME: Every Coroutine statement may be invalid and therefore not added
+    // to CoroutineStmts. Find another way to provide location information.
+    if (!Fn->CoroutineStmts.empty()) {
+      auto *First = Fn->CoroutineStmts[0];
+      Diag(First->getLocStart(), diag::note_declared_coroutine_here)
+          << (isa<CoawaitExpr>(First) ? "co_await" :
+              isa<CoyieldExpr>(First) ? "co_yield" : "co_return");
+    }
   }
   SubStmtBuilder Builder(*this, *FD, *Fn, Body);
   if (Builder.isInvalid())

Modified: cfe/trunk/lib/Sema/SemaExprMember.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExprMember.cpp?rev=297356&r1=297355&r2=297356&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExprMember.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExprMember.cpp Wed Mar  8 21:09:43 2017
@@ -973,7 +973,7 @@ Sema::BuildMemberReferenceExpr(Expr *Bas
 
   // C++1z [expr.ref]p2:
   //   For the first option (dot) the first expression shall be a glvalue [...]
-  if (!IsArrow && BaseExpr->isRValue()) {
+  if (!IsArrow && BaseExpr && BaseExpr->isRValue()) {
     ExprResult Converted = TemporaryMaterializationConversion(BaseExpr);
     if (Converted.isInvalid())
       return ExprError();

Modified: cfe/trunk/test/CodeGenCoroutines/coro-alloc.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCoroutines/coro-alloc.cpp?rev=297356&r1=297355&r2=297356&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenCoroutines/coro-alloc.cpp (original)
+++ cfe/trunk/test/CodeGenCoroutines/coro-alloc.cpp Wed Mar  8 21:09:43 2017
@@ -4,12 +4,27 @@ namespace std {
 namespace experimental {
 template <typename... T>
 struct coroutine_traits; // expected-note {{declared here}}
+
+template <class Promise = void>
+struct coroutine_handle {
+  coroutine_handle() = default;
+  static coroutine_handle from_address(void *) { return {}; }
+};
+
+template <>
+struct coroutine_handle<void> {
+  static coroutine_handle from_address(void *) { return {}; }
+  coroutine_handle() = default;
+  template <class PromiseType>
+  coroutine_handle(coroutine_handle<PromiseType>) {}
+};
+
 }
 }
 
 struct suspend_always {
   bool await_ready() { return false; }
-  void await_suspend() {}
+  void await_suspend(std::experimental::coroutine_handle<>) {}
   void await_resume() {}
 };
 

Modified: cfe/trunk/test/CodeGenCoroutines/coro-return.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCoroutines/coro-return.cpp?rev=297356&r1=297355&r2=297356&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenCoroutines/coro-return.cpp (original)
+++ cfe/trunk/test/CodeGenCoroutines/coro-return.cpp Wed Mar  8 21:09:43 2017
@@ -4,12 +4,27 @@ namespace std {
 namespace experimental {
 template <typename... T>
 struct coroutine_traits;
+
+template <class Promise = void>
+struct coroutine_handle {
+  coroutine_handle() = default;
+  static coroutine_handle from_address(void *) { return {}; }
+};
+
+template <>
+struct coroutine_handle<void> {
+  static coroutine_handle from_address(void *) { return {}; }
+  coroutine_handle() = default;
+  template <class PromiseType>
+  coroutine_handle(coroutine_handle<PromiseType>) {}
+};
+
 }
 }
 
 struct suspend_always {
   bool await_ready();
-  void await_suspend();
+  void await_suspend(std::experimental::coroutine_handle<>);
   void await_resume();
 };
 

Modified: cfe/trunk/test/SemaCXX/coreturn.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/coreturn.cpp?rev=297356&r1=297355&r2=297356&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/coreturn.cpp (original)
+++ cfe/trunk/test/SemaCXX/coreturn.cpp Wed Mar  8 21:09:43 2017
@@ -1,34 +1,43 @@
 // RUN: %clang_cc1 -triple x86_64-apple-darwin9 %s -std=c++14 -fcoroutines-ts -fsyntax-only -Wignored-qualifiers -Wno-error=return-type -verify -fblocks -Wno-unreachable-code -Wno-unused-value
 
+namespace std {
+namespace experimental {
+
+template <class Ret, typename... T>
+struct coroutine_traits { using promise_type = typename Ret::promise_type; };
+
+template <class Promise = void>
+struct coroutine_handle {
+  static coroutine_handle from_address(void *);
+};
+template <>
+struct coroutine_handle<void> {
+  template <class PromiseType>
+  coroutine_handle(coroutine_handle<PromiseType>);
+  static coroutine_handle from_address(void *);
+};
+
+}
+}
+
 struct awaitable {
   bool await_ready();
-  void await_suspend(); // FIXME: coroutine_handle
+  void await_suspend(std::experimental::coroutine_handle<>); // FIXME: coroutine_handle
   void await_resume();
 } a;
 
 struct suspend_always {
   bool await_ready() { return false; }
-  void await_suspend() {}
+  void await_suspend(std::experimental::coroutine_handle<>) {}
   void await_resume() {}
 };
 
 struct suspend_never {
   bool await_ready() { return true; }
-  void await_suspend() {}
+  void await_suspend(std::experimental::coroutine_handle<>) {}
   void await_resume() {}
 };
 
-namespace std {
-namespace experimental {
-
-template <class Ret, typename... T>
-struct coroutine_traits { using promise_type = typename Ret::promise_type; };
-
-template <class Promise = void>
-struct coroutine_handle {};
-}
-}
-
 struct promise_void {
   void get_return_object();
   suspend_always initial_suspend();

Modified: cfe/trunk/test/SemaCXX/coroutines.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/coroutines.cpp?rev=297356&r1=297355&r2=297356&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/coroutines.cpp (original)
+++ cfe/trunk/test/SemaCXX/coroutines.cpp Wed Mar  8 21:09:43 2017
@@ -16,42 +16,40 @@ void no_coroutine_traits_bad_arg_return(
   // expected-error at -1 {{use of undeclared identifier 'a'}}
 }
 
+void no_coroutine_traits() {
+  co_await 4; // expected-error {{std::experimental::coroutine_traits type was not found; include <experimental/coroutine>}}
+}
+
+namespace std {
+namespace experimental {
+template <typename... T>
+struct coroutine_traits; // expected-note {{declared here}}
+}}  // namespace std::experimental
+
+template<typename Promise> struct coro {};
+template <typename Promise, typename... Ps>
+struct std::experimental::coroutine_traits<coro<Promise>, Ps...> {
+  using promise_type = Promise;
+};
 
 struct awaitable {
   bool await_ready();
-  void await_suspend(); // FIXME: coroutine_handle
+  template <typename F> void await_suspend(F);
   void await_resume();
 } a;
 
 struct suspend_always {
   bool await_ready() { return false; }
-  void await_suspend() {}
+  template <typename F> void await_suspend(F);
   void await_resume() {}
 };
 
 struct suspend_never {
   bool await_ready() { return true; }
-  void await_suspend() {}
+  template <typename F> void await_suspend(F);
   void await_resume() {}
 };
 
-void no_coroutine_traits() {
-  co_await a; // expected-error {{need to include <experimental/coroutine>}}
-}
-
-namespace std {
-namespace experimental {
-template <typename... T>
-struct coroutine_traits; // expected-note {{declared here}}
-}
-}
-
-template<typename Promise> struct coro {};
-template <typename Promise, typename... Ps>
-struct std::experimental::coroutine_traits<coro<Promise>, Ps...> {
-  using promise_type = Promise;
-};
-
 void no_specialization() {
   co_await a; // expected-error {{implicit instantiation of undefined template 'std::experimental::coroutine_traits<void>'}}
 }
@@ -86,13 +84,6 @@ template <typename... T>
 struct std::experimental::coroutine_traits<void, void_tag, T...>
 { using promise_type = promise_void; };
 
-namespace std {
-namespace experimental {
-template <typename Promise = void>
-struct coroutine_handle;
-}
-}
-
 // FIXME: This diagnostic is terrible.
 void undefined_promise() { // expected-error {{this function cannot be a coroutine: 'experimental::coroutine_traits<void>::promise_type' (aka 'promise') is an incomplete type}}
   co_await a;
@@ -119,6 +110,25 @@ struct promise_void {
   void return_void();
 };
 
+void no_coroutine_handle() { // expected-error {{std::experimental::coroutine_handle type was not found; include <experimental/coroutine> before defining a coroutine}}
+  //expected-note at -1 {{call to 'initial_suspend' implicitly required by the initial suspend point}}
+  co_return 5; //expected-note {{function is a coroutine due to use of 'co_return' here}}
+}
+
+namespace std {
+namespace experimental {
+template <class PromiseType = void>
+struct coroutine_handle {
+  static coroutine_handle from_address(void *);
+};
+template <>
+struct coroutine_handle<void> {
+  template <class PromiseType>
+  coroutine_handle(coroutine_handle<PromiseType>);
+  static coroutine_handle from_address(void *);
+};
+}} // namespace std::experimental
+
 void yield() {
   co_yield 0;
   co_yield {"foo", 1, 2};
@@ -520,3 +530,19 @@ template<> struct std::experimental::cor
 int main(int, const char**) {
   co_await a; // expected-error {{'co_await' cannot be used in the 'main' function}}
 }
+
+struct good_promise_2 {
+  float get_return_object();
+  suspend_always initial_suspend();
+  suspend_always final_suspend();
+  void return_void();
+};
+template<> struct std::experimental::coroutine_handle<good_promise_2> {};
+
+template<> struct std::experimental::coroutine_traits<float>
+{ using promise_type = good_promise_2; };
+
+float badly_specialized_coro_handle() { // expected-error {{std::experimental::coroutine_handle missing a member named 'from_address'}}
+  //expected-note at -1 {{call to 'initial_suspend' implicitly required by the initial suspend point}}
+  co_return; //expected-note {{function is a coroutine due to use of 'co_return' here}}
+}




More information about the cfe-commits mailing list