EricWF updated this revision to Diff 73715.
EricWF added a comment.

- Address @majnemer's comment about using `auto`.


https://reviews.llvm.org/D25292

Files:
  include/clang/Basic/DiagnosticSemaKinds.td
  lib/Sema/SemaCoroutine.cpp
  test/SemaCXX/coroutines.cpp

Index: test/SemaCXX/coroutines.cpp
===================================================================
--- test/SemaCXX/coroutines.cpp
+++ test/SemaCXX/coroutines.cpp
@@ -167,7 +167,19 @@
   }
   // FIXME: The spec says this is ill-formed.
   void operator=(CtorDtor&) {
-    co_yield 0;
+    co_yield 0; // expected-error {{'co_yield' cannot be used in a copy assignment operator}}
+  }
+  void operator=(CtorDtor const &) {
+    co_yield 0; // expected-error {{'co_yield' cannot be used in a copy assignment operator}}
+  }
+  void operator=(CtorDtor &&) {
+    co_await a; // expected-error {{'co_await' cannot be used in a move assignment operator}}
+  }
+  void operator=(CtorDtor const &&) {
+    co_await a; // expected-error {{'co_await' cannot be used in a move assignment operator}}
+  }
+  void operator=(int) {
+    co_await a; // OK. Not a special member
   }
 };
 
@@ -180,14 +192,19 @@
   typeid(co_yield a); // expected-error {{cannot be used in an unevaluated context}}
 }
 
-constexpr void constexpr_coroutine() {
+constexpr auto constexpr_deduced_return_coroutine() {
   co_yield 0; // expected-error {{'co_yield' cannot be used in a constexpr function}}
+  // expected-error@-1 {{'co_yield' cannot be used in a function with a deduced return type}}
 }
 
 void varargs_coroutine(const char *, ...) {
   co_await a; // expected-error {{'co_await' cannot be used in a varargs function}}
 }
 
+auto deduced_return_coroutine() {
+  co_await a; // expected-error {{'co_await' cannot be used in a function with a deduced return type}}
+}
+
 struct outer {};
 
 namespace dependent_operator_co_await_lookup {
@@ -303,6 +320,6 @@
 template<> struct std::experimental::coroutine_traits<int, int, const char**>
 { using promise_type = promise; };
 
-int main(int, const char**) { // expected-error {{'main' cannot be a coroutine}}
-  co_await a; // expected-note {{function is a coroutine due to use of 'co_await' here}}
+int main(int, const char**) {
+  co_await a; // expected-error {{'co_await' cannot be used in the 'main' function}}
 }
Index: lib/Sema/SemaCoroutine.cpp
===================================================================
--- lib/Sema/SemaCoroutine.cpp
+++ lib/Sema/SemaCoroutine.cpp
@@ -99,67 +99,98 @@
   return PromiseType;
 }
 
-/// Check that this is a context in which a coroutine suspension can appear.
-static FunctionScopeInfo *
-checkCoroutineContext(Sema &S, SourceLocation Loc, StringRef Keyword) {
+static bool isValidCoroutineContext(Sema &S, SourceLocation Loc,
+                                    StringRef Keyword) {
   // 'co_await' and 'co_yield' are not permitted in unevaluated operands.
   if (S.isUnevaluatedContext()) {
     S.Diag(Loc, diag::err_coroutine_unevaluated_context) << Keyword;
-    return nullptr;
+    return false;
   }
 
   // Any other usage must be within a function.
-  // FIXME: Reject a coroutine with a deduced return type.
   auto *FD = dyn_cast<FunctionDecl>(S.CurContext);
   if (!FD) {
     S.Diag(Loc, isa<ObjCMethodDecl>(S.CurContext)
                     ? diag::err_coroutine_objc_method
                     : diag::err_coroutine_outside_function) << Keyword;
-  } else if (isa<CXXConstructorDecl>(FD) || isa<CXXDestructorDecl>(FD)) {
-    // Coroutines TS [special]/6:
-    //   A special member function shall not be a coroutine.
-    //
-    // FIXME: We assume that this really means that a coroutine cannot
-    //        be a constructor or destructor.
-    S.Diag(Loc, diag::err_coroutine_ctor_dtor)
-      << isa<CXXDestructorDecl>(FD) << Keyword;
-  } else if (FD->isConstexpr()) {
-    S.Diag(Loc, diag::err_coroutine_constexpr) << Keyword;
-  } else if (FD->isVariadic()) {
-    S.Diag(Loc, diag::err_coroutine_varargs) << Keyword;
-  } else if (FD->isMain()) {
-    S.Diag(FD->getLocStart(), diag::err_coroutine_main);
-    S.Diag(Loc, diag::note_declared_coroutine_here)
-      << (Keyword == "co_await" ? 0 :
-          Keyword == "co_yield" ? 1 : 2);
-  } 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) {
-      QualType T =
-          FD->getType()->isDependentType()
-              ? S.Context.DependentTy
-              : lookupPromiseType(S, FD->getType()->castAs<FunctionProtoType>(),
-                                  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 false;
+  }
+
+  // An enumeration for mapping the diagnostic type to the correct diagnostic
+  // selection index.
+  enum InvalidFuncDiag {
+    DiagCtor = 0,
+    DiagDtor,
+    DiagCopyAssign,
+    DiagMoveAssign,
+    DiagMain,
+    DiagConstexpr,
+    DiagAutoRet,
+    DiagVarargs,
+  };
+  bool Diagnosed = false;
+  auto DiagInvalid = [&](InvalidFuncDiag ID) {
+    S.Diag(Loc, diag::err_coroutine_invalid_func_context) << ID << Keyword;
+    Diagnosed = true;
+    return false;
+  };
+
+  // Diagnose when a constructor, destructor, copy/move assignment operator,
+  // or the function 'main' are declared as a coroutine.
+  auto *MD = dyn_cast<CXXMethodDecl>(FD);
+  if (MD && isa<CXXConstructorDecl>(MD))
+    return DiagInvalid(DiagCtor);
+  else if (MD && isa<CXXDestructorDecl>(MD))
+    return DiagInvalid(DiagDtor);
+  else if (MD && MD->isCopyAssignmentOperator())
+    return DiagInvalid(DiagCopyAssign);
+  else if (MD && MD->isMoveAssignmentOperator())
+    return DiagInvalid(DiagMoveAssign);
+  else if (FD->isMain())
+    return DiagInvalid(DiagMain);
+
+  // Emit a diagnostics for each of the following conditions which is not met.
+  if (FD->isConstexpr())
+    DiagInvalid(DiagConstexpr);
+  if (FD->getReturnType()->isUndeducedType())
+    DiagInvalid(DiagAutoRet);
+  if (FD->isVariadic())
+    DiagInvalid(DiagVarargs);
+
+  return !Diagnosed;
+}
+
+/// Check that this is a context in which a coroutine suspension can appear.
+static FunctionScopeInfo *checkCoroutineContext(Sema &S, SourceLocation Loc,
+                                                StringRef Keyword) {
+  if (!isValidCoroutineContext(S, Loc, Keyword))
+    return nullptr;
+
+  assert(isa<FunctionDecl>(S.CurContext) && "not in a function scope");
+  auto *FD = cast<FunctionDecl>(S.CurContext);
+  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) {
+    QualType T = FD->getType()->isDependentType()
+                     ? S.Context.DependentTy
+                     : lookupPromiseType(
+                           S, FD->getType()->castAs<FunctionProtoType>(), 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 nullptr;
+  return ScopeInfo;
 }
 
 /// Build a call to 'operator co_await' if there is a suitable operator for
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -8559,14 +8559,11 @@
   "'%0' cannot be used in an unevaluated context">;
 def err_coroutine_outside_function : Error<
   "'%0' cannot be used outside a function">;
-def err_coroutine_ctor_dtor : Error<
-  "'%1' cannot be used in a %select{constructor|destructor}0">;
-def err_coroutine_constexpr : Error<
-  "'%0' cannot be used in a constexpr function">;
-def err_coroutine_main : Error<
-  "'main' cannot be a coroutine">;
-def err_coroutine_varargs : Error<
-  "'%0' cannot be used in a varargs function">;
+def err_coroutine_invalid_func_context : Error<
+  "'%1' cannot be used in %select{a constructor|a destructor"
+  "|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 ext_coroutine_without_co_await_co_yield : ExtWarn<
   "'co_return' used in a function "
   "that uses neither 'co_await' nor 'co_yield'">,
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to