r250993 - [coroutines] Initial stub Sema functionality for handling coroutine await / yield / return.

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Wed Oct 21 23:13:51 PDT 2015


Author: rsmith
Date: Thu Oct 22 01:13:50 2015
New Revision: 250993

URL: http://llvm.org/viewvc/llvm-project?rev=250993&view=rev
Log:
[coroutines] Initial stub Sema functionality for handling coroutine await / yield / return.

Added:
    cfe/trunk/lib/Sema/SemaCoroutine.cpp
    cfe/trunk/test/SemaCXX/coroutines.cpp
Modified:
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/include/clang/Sema/ScopeInfo.h
    cfe/trunk/include/clang/Sema/Sema.h
    cfe/trunk/lib/Parse/ParseExpr.cpp
    cfe/trunk/lib/Parse/ParseExprCXX.cpp
    cfe/trunk/lib/Parse/ParseStmt.cpp
    cfe/trunk/lib/Sema/CMakeLists.txt
    cfe/trunk/lib/Sema/SemaDecl.cpp
    cfe/trunk/lib/Sema/SemaStmt.cpp
    cfe/trunk/lib/Sema/TreeTransform.h

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=250993&r1=250992&r2=250993&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Thu Oct 22 01:13:50 2015
@@ -7827,6 +7827,27 @@ def err_module_import_in_implementation
   "@import of module '%0' in implementation of '%1'; use #import">;
 }
 
+let CategoryName = "Coroutines Issue" in {
+def err_return_in_coroutine : Error<"return statement in coroutine">;
+def note_declared_coroutine_here : Note<
+  "function is a coroutine due to use of "
+  "'%select{co_await|co_yield|co_return}0' here">;
+def err_coroutine_objc_method : Error<
+  "Objective-C methods as coroutines are not yet supported">;
+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_varargs : Error<
+  "'%0' cannot be used in a varargs function">;
+def ext_coroutine_without_coawait_coyield : ExtWarn<
+  "'co_return' used in a function "
+  "that uses neither 'co_await' nor 'co_yield'">,
+  InGroup<DiagGroup<"coreturn-without-coawait">>;
+}
+
 let CategoryName = "Documentation Issue" in {
 def warn_not_a_doxygen_trailing_member_comment : Warning<
   "not a Doxygen trailing comment">, InGroup<Documentation>, DefaultIgnore;

Modified: cfe/trunk/include/clang/Sema/ScopeInfo.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/ScopeInfo.h?rev=250993&r1=250992&r2=250993&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/ScopeInfo.h (original)
+++ cfe/trunk/include/clang/Sema/ScopeInfo.h Thu Oct 22 01:13:50 2015
@@ -142,6 +142,11 @@ public:
   /// optimization, or if we need to infer a return type.
   SmallVector<ReturnStmt*, 4> Returns;
 
+  /// \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.
+  SmallVector<Stmt*, 4> CoroutineStmts;
+
   /// \brief The stack of currently active compound stamement scopes in the
   /// function.
   SmallVector<CompoundScopeInfo, 4> CompoundScopes;

Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=250993&r1=250992&r2=250993&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Thu Oct 22 01:13:50 2015
@@ -3334,11 +3334,14 @@ public:
     BFRK_Check
   };
 
-  StmtResult ActOnCXXForRangeStmt(SourceLocation ForLoc, Stmt *LoopVar,
+  StmtResult ActOnCXXForRangeStmt(SourceLocation ForLoc,
+                                  SourceLocation CoawaitLoc,
+                                  Stmt *LoopVar,
                                   SourceLocation ColonLoc, Expr *Collection,
                                   SourceLocation RParenLoc,
                                   BuildForRangeKind Kind);
   StmtResult BuildCXXForRangeStmt(SourceLocation ForLoc,
+                                  SourceLocation CoawaitLoc,
                                   SourceLocation ColonLoc,
                                   Stmt *RangeDecl, Stmt *BeginEndDecl,
                                   Expr *Cond, Expr *Inc,
@@ -7703,7 +7706,18 @@ public:
   void AddLaunchBoundsAttr(SourceRange AttrRange, Decl *D, Expr *MaxThreads,
                            Expr *MinBlocks, unsigned SpellingListIndex);
 
+  //===--------------------------------------------------------------------===//
+  // C++ Coroutines TS
+  //
+  ExprResult ActOnCoawaitExpr(SourceLocation KwLoc, Expr *E);
+  ExprResult ActOnCoyieldExpr(SourceLocation KwLoc, Expr *E);
+  StmtResult ActOnCoreturnStmt(SourceLocation KwLoc, Expr *E);
+
+  void CheckCompletedCoroutineBody(FunctionDecl *FD, Stmt *Body);
+
+  //===--------------------------------------------------------------------===//
   // OpenMP directives and clauses.
+  //
 private:
   void *VarDataSharingAttributesStack;
   /// \brief Initialization of data-sharing attributes stack.

Modified: cfe/trunk/lib/Parse/ParseExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseExpr.cpp?rev=250993&r1=250992&r2=250993&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseExpr.cpp (original)
+++ cfe/trunk/lib/Parse/ParseExpr.cpp Thu Oct 22 01:13:50 2015
@@ -1045,10 +1045,10 @@ ExprResult Parser::ParseCastExpression(b
   }
 
   case tok::kw_co_await: {  // unary-expression: 'co_await' cast-expression
-    SourceLocation SavedLoc = ConsumeToken();
+    SourceLocation CoawaitLoc = ConsumeToken();
     Res = ParseCastExpression(false);
-    (void)SavedLoc;
-    // FIXME: Pass to Sema.
+    if (!Res.isInvalid())
+      Res = Actions.ActOnCoawaitExpr(CoawaitLoc, Res.get());
     return Res;
   }
 

Modified: cfe/trunk/lib/Parse/ParseExprCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseExprCXX.cpp?rev=250993&r1=250992&r2=250993&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseExprCXX.cpp (original)
+++ cfe/trunk/lib/Parse/ParseExprCXX.cpp Thu Oct 22 01:13:50 2015
@@ -1567,8 +1567,8 @@ ExprResult Parser::ParseCoyieldExpressio
 
   SourceLocation Loc = ConsumeToken();
   ExprResult Expr = ParseAssignmentExpression();
-  (void)Loc;
-  // FIXME: Pass to Sema.
+  if (!Expr.isInvalid())
+    Expr = Actions.ActOnCoyieldExpr(Loc, Expr.get());
   return Expr;
 }
 

Modified: cfe/trunk/lib/Parse/ParseStmt.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseStmt.cpp?rev=250993&r1=250992&r2=250993&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseStmt.cpp (original)
+++ cfe/trunk/lib/Parse/ParseStmt.cpp Thu Oct 22 01:13:50 2015
@@ -1691,8 +1691,8 @@ StmtResult Parser::ParseForStatement(Sou
   StmtResult ForEachStmt;
 
   if (ForRange) {
-    // FIXME: Pass CoawaitLoc to Sema.
-    ForRangeStmt = Actions.ActOnCXXForRangeStmt(ForLoc, FirstPart.get(),
+    ForRangeStmt = Actions.ActOnCXXForRangeStmt(ForLoc, CoawaitLoc,
+                                                FirstPart.get(),
                                                 ForRangeInit.ColonLoc,
                                                 ForRangeInit.RangeExpr.get(),
                                                 T.getCloseLocation(),
@@ -1851,7 +1851,8 @@ StmtResult Parser::ParseReturnStatement(
       return StmtError();
     }
   }
-  // FIXME: Pass IsCoreturn to Sema.
+  if (IsCoreturn)
+    return Actions.ActOnCoreturnStmt(ReturnLoc, R.get());
   return Actions.ActOnReturnStmt(ReturnLoc, R.get(), getCurScope());
 }
 

Modified: cfe/trunk/lib/Sema/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/CMakeLists.txt?rev=250993&r1=250992&r2=250993&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/CMakeLists.txt (original)
+++ cfe/trunk/lib/Sema/CMakeLists.txt Thu Oct 22 01:13:50 2015
@@ -21,6 +21,7 @@ add_clang_library(clangSema
   SemaChecking.cpp
   SemaCodeComplete.cpp
   SemaConsumer.cpp
+  SemaCoroutine.cpp
   SemaCUDA.cpp
   SemaDecl.cpp
   SemaDeclAttr.cpp

Added: cfe/trunk/lib/Sema/SemaCoroutine.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaCoroutine.cpp?rev=250993&view=auto
==============================================================================
--- cfe/trunk/lib/Sema/SemaCoroutine.cpp (added)
+++ cfe/trunk/lib/Sema/SemaCoroutine.cpp Thu Oct 22 01:13:50 2015
@@ -0,0 +1,106 @@
+//===--- SemaCoroutines.cpp - Semantic Analysis for Coroutines ------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file implements semantic analysis for C++ Coroutines.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Sema/SemaInternal.h"
+using namespace clang;
+using namespace sema;
+
+static FunctionScopeInfo *
+checkCoroutineContext(Sema &S, SourceLocation Loc, StringRef Keyword) {
+  // 'co_await' and 'co_yield' are permitted in unevaluated operands.
+  if (S.isUnevaluatedContext())
+    return nullptr;
+
+  // Any other usage must be within a function.
+  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 {
+    auto *ScopeInfo = S.getCurFunction();
+    assert(ScopeInfo && "missing function scope for function");
+    return ScopeInfo;
+  }
+
+  return nullptr;
+}
+
+ExprResult Sema::ActOnCoawaitExpr(SourceLocation Loc, Expr *E) {
+  auto *Context = checkCoroutineContext(*this, Loc, "co_await");
+  ExprResult Res = ExprError();
+
+  if (Context && !Res.isInvalid())
+    Context->CoroutineStmts.push_back(Res.get());
+  return Res;
+}
+
+ExprResult Sema::ActOnCoyieldExpr(SourceLocation Loc, Expr *E) {
+  auto *Context = checkCoroutineContext(*this, Loc, "co_yield");
+  ExprResult Res = ExprError();
+
+  if (Context && !Res.isInvalid())
+    Context->CoroutineStmts.push_back(Res.get());
+  return Res;
+}
+
+StmtResult Sema::ActOnCoreturnStmt(SourceLocation Loc, Expr *E) {
+  auto *Context = checkCoroutineContext(*this, Loc, "co_return");
+  StmtResult Res = StmtError();
+
+  if (Context && !Res.isInvalid())
+    Context->CoroutineStmts.push_back(Res.get());
+  return Res;
+}
+
+void Sema::CheckCompletedCoroutineBody(FunctionDecl *FD, Stmt *Body) {
+  FunctionScopeInfo *Fn = getCurFunction();
+  assert(Fn && !Fn->CoroutineStmts.empty() && "not a coroutine");
+
+  // 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);
+    auto *First = Fn->CoroutineStmts[0];
+    Diag(First->getLocStart(), diag::note_declared_coroutine_here)
+      << 0; // FIXME: Indicate the kind here
+  }
+
+  bool AnyCoawaits = false;
+  bool AnyCoyields = false;
+  for (auto *CoroutineStmt : Fn->CoroutineStmts) {
+    (void)CoroutineStmt;
+    AnyCoawaits = AnyCoyields = true; // FIXME
+  }
+
+  if (!AnyCoawaits && !AnyCoyields)
+    Diag(Fn->CoroutineStmts.front()->getLocStart(),
+         diag::ext_coroutine_without_coawait_coyield);
+
+  // 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.
+}

Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=250993&r1=250992&r2=250993&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Thu Oct 22 01:13:50 2015
@@ -10907,6 +10907,9 @@ Decl *Sema::ActOnFinishFunctionBody(Decl
   sema::AnalysisBasedWarnings::Policy WP = AnalysisWarnings.getDefaultPolicy();
   sema::AnalysisBasedWarnings::Policy *ActivePolicy = nullptr;
 
+  if (getLangOpts().Coroutines && !getCurFunction()->CoroutineStmts.empty())
+    CheckCompletedCoroutineBody(FD, Body);
+
   if (FD) {
     FD->setBody(Body);
 

Modified: cfe/trunk/lib/Sema/SemaStmt.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaStmt.cpp?rev=250993&r1=250992&r2=250993&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaStmt.cpp (original)
+++ cfe/trunk/lib/Sema/SemaStmt.cpp Thu Oct 22 01:13:50 2015
@@ -1924,7 +1924,7 @@ static bool ObjCEnumerationCollection(Ex
 /// 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,
+Sema::ActOnCXXForRangeStmt(SourceLocation ForLoc, SourceLocation CoawaitLoc,
                            Stmt *First, SourceLocation ColonLoc, Expr *Range,
                            SourceLocation RParenLoc, BuildForRangeKind Kind) {
   if (!First)
@@ -1948,6 +1948,13 @@ Sema::ActOnCXXForRangeStmt(SourceLocatio
     return StmtError();
   }
 
+  // Coroutines: 'for co_await' implicitly co_awaits its range.
+  if (CoawaitLoc.isValid()) {
+    ExprResult Coawait = ActOnCoawaitExpr(CoawaitLoc, Range);
+    if (Coawait.isInvalid()) return StmtError();
+    Range = Coawait.get();
+  }
+
   // Build  auto && __range = range-init
   SourceLocation RangeLoc = Range->getLocStart();
   VarDecl *RangeVar = BuildForRangeVarDecl(*this, RangeLoc,
@@ -1969,7 +1976,7 @@ Sema::ActOnCXXForRangeStmt(SourceLocatio
     return StmtError();
   }
 
-  return BuildCXXForRangeStmt(ForLoc, ColonLoc, RangeDecl.get(),
+  return BuildCXXForRangeStmt(ForLoc, CoawaitLoc, ColonLoc, RangeDecl.get(),
                               /*BeginEndDecl=*/nullptr, /*Cond=*/nullptr,
                               /*Inc=*/nullptr, DS, RParenLoc, Kind);
 }
@@ -2063,6 +2070,7 @@ static Sema::ForRangeStatus BuildNonArra
 /// and emit no diagnostics.
 static StmtResult RebuildForRangeWithDereference(Sema &SemaRef, Scope *S,
                                                  SourceLocation ForLoc,
+                                                 SourceLocation CoawaitLoc,
                                                  Stmt *LoopVarDecl,
                                                  SourceLocation ColonLoc,
                                                  Expr *Range,
@@ -2079,7 +2087,7 @@ static StmtResult RebuildForRangeWithDer
       return StmtResult();
 
     StmtResult SR =
-      SemaRef.ActOnCXXForRangeStmt(ForLoc, LoopVarDecl, ColonLoc,
+      SemaRef.ActOnCXXForRangeStmt(ForLoc, CoawaitLoc, LoopVarDecl, ColonLoc,
                                    AdjustedRange.get(), RParenLoc,
                                    Sema::BFRK_Check);
     if (SR.isInvalid())
@@ -2091,7 +2099,7 @@ static StmtResult RebuildForRangeWithDer
   // 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, LoopVarDecl, ColonLoc,
+  return SemaRef.ActOnCXXForRangeStmt(ForLoc, CoawaitLoc, LoopVarDecl, ColonLoc,
                                       AdjustedRange.get(), RParenLoc,
                                       Sema::BFRK_Rebuild);
 }
@@ -2114,7 +2122,8 @@ struct InvalidateOnErrorScope {
 
 /// BuildCXXForRangeStmt - Build or instantiate a C++11 for-range statement.
 StmtResult
-Sema::BuildCXXForRangeStmt(SourceLocation ForLoc, SourceLocation ColonLoc,
+Sema::BuildCXXForRangeStmt(SourceLocation ForLoc, SourceLocation CoawaitLoc,
+                           SourceLocation ColonLoc,
                            Stmt *RangeDecl, Stmt *BeginEnd, Expr *Cond,
                            Expr *Inc, Stmt *LoopVarDecl,
                            SourceLocation RParenLoc, BuildForRangeKind Kind) {
@@ -2244,6 +2253,7 @@ Sema::BuildCXXForRangeStmt(SourceLocatio
         // If building the range failed, try dereferencing the range expression
         // unless a diagnostic was issued or the end function is problematic.
         StmtResult SR = RebuildForRangeWithDereference(*this, S, ForLoc,
+                                                       CoawaitLoc,
                                                        LoopVarDecl, ColonLoc,
                                                        Range, RangeLoc,
                                                        RParenLoc);
@@ -2314,7 +2324,10 @@ Sema::BuildCXXForRangeStmt(SourceLocatio
       return StmtError();
 
     IncrExpr = ActOnUnaryOp(S, ColonLoc, tok::plusplus, BeginRef.get());
-    IncrExpr = ActOnFinishFullExpr(IncrExpr.get());
+    if (!IncrExpr.isInvalid() && CoawaitLoc.isValid())
+      IncrExpr = ActOnCoawaitExpr(CoawaitLoc, IncrExpr.get());
+    if (!IncrExpr.isInvalid())
+      IncrExpr = ActOnFinishFullExpr(IncrExpr.get());
     if (IncrExpr.isInvalid()) {
       Diag(RangeLoc, diag::note_for_range_invalid_iterator)
         << RangeLoc << 2 << BeginRangeRef.get()->getType() ;
@@ -2351,6 +2364,7 @@ Sema::BuildCXXForRangeStmt(SourceLocatio
   if (Kind == BFRK_Check)
     return StmtResult();
 
+  // FIXME: Pass in CoawaitLoc in the dependent case.
   return new (Context) CXXForRangeStmt(
       RangeDS, cast_or_null<DeclStmt>(BeginEndDecl.get()), NotEqExpr.get(),
       IncrExpr.get(), LoopVarDS, /*Body=*/nullptr, ForLoc, ColonLoc, RParenLoc);

Modified: cfe/trunk/lib/Sema/TreeTransform.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/TreeTransform.h?rev=250993&r1=250992&r2=250993&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/TreeTransform.h (original)
+++ cfe/trunk/lib/Sema/TreeTransform.h Thu Oct 22 01:13:50 2015
@@ -1737,7 +1737,9 @@ public:
       }
     }
 
-    return getSema().BuildCXXForRangeStmt(ForLoc, ColonLoc, Range, BeginEnd,
+    SourceLocation CoawaitLoc; // FIXME
+    return getSema().BuildCXXForRangeStmt(ForLoc, CoawaitLoc, ColonLoc,
+                                          Range, BeginEnd,
                                           Cond, Inc, LoopVar, RParenLoc,
                                           Sema::BFRK_Rebuild);
   }

Added: cfe/trunk/test/SemaCXX/coroutines.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/coroutines.cpp?rev=250993&view=auto
==============================================================================
--- cfe/trunk/test/SemaCXX/coroutines.cpp (added)
+++ cfe/trunk/test/SemaCXX/coroutines.cpp Thu Oct 22 01:13:50 2015
@@ -0,0 +1,51 @@
+// RUN: %clang_cc1 -std=c++14 -fcoroutines -verify %s
+
+void mixed_yield() {
+  // FIXME: diagnose
+  co_yield 0;
+  return;
+}
+
+void mixed_await() {
+  // FIXME: diagnose
+  co_await 0;
+  return;
+}
+
+void only_coreturn() {
+  // FIXME: diagnose
+  co_return;
+}
+
+void mixed_coreturn(bool b) {
+  // FIXME: diagnose
+  if (b)
+    co_return;
+  else
+    return;
+}
+
+struct CtorDtor {
+  CtorDtor() {
+    co_yield 0; // expected-error {{'co_yield' cannot be used in a constructor}}
+  }
+  CtorDtor(int n) {
+    // 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}}
+  }
+  ~CtorDtor() {
+    co_return 0; // expected-error {{'co_return' cannot be used in a destructor}}
+  }
+  // FIXME: The spec says this is ill-formed.
+  void operator=(CtorDtor&) {
+    co_yield 0;
+  }
+};
+
+constexpr void constexpr_coroutine() {
+  co_yield 0; // expected-error {{'co_yield' cannot be used in a constexpr function}}
+}
+
+void varargs_coroutine(const char *, ...) {
+  co_await 0; // expected-error {{'co_await' cannot be used in a varargs function}}
+}




More information about the cfe-commits mailing list