diff options
| -rw-r--r-- | clang/include/clang/Sema/ScopeInfo.h | 36 | ||||
| -rw-r--r-- | clang/lib/Sema/ScopeInfo.cpp | 8 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaCoroutine.cpp | 35 | ||||
| -rw-r--r-- | clang/lib/Sema/TreeTransform.h | 2 | ||||
| -rw-r--r-- | clang/test/SemaCXX/coroutines.cpp | 75 |
5 files changed, 126 insertions, 30 deletions
diff --git a/clang/include/clang/Sema/ScopeInfo.h b/clang/include/clang/Sema/ScopeInfo.h index e49e4d94576..4487c7c2ccb 100644 --- a/clang/include/clang/Sema/ScopeInfo.h +++ b/clang/include/clang/Sema/ScopeInfo.h @@ -24,6 +24,7 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringSwitch.h" #include <algorithm> namespace clang { @@ -139,6 +140,14 @@ public: /// to build, the initial and final coroutine suspend points bool NeedsCoroutineSuspends : 1; + /// \brief An enumeration represeting the kind of the first coroutine statement + /// in the function. One of co_return, co_await, or co_yield. + unsigned char FirstCoroutineStmtKind : 2; + + /// First coroutine statement in the current function. + /// (ex co_return, co_await, co_yield) + SourceLocation FirstCoroutineStmtLoc; + /// First 'return' statement in the current function. SourceLocation FirstReturnLoc; @@ -166,11 +175,6 @@ public: /// \brief The initial and final coroutine suspend points. std::pair<Stmt *, Stmt *> CoroutineSuspends; - /// \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; @@ -384,6 +388,28 @@ public: (HasBranchProtectedScope && HasBranchIntoScope)); } + void setFirstCoroutineStmt(SourceLocation Loc, StringRef Keyword) { + assert(FirstCoroutineStmtLoc.isInvalid() && + "first coroutine statement location already set"); + FirstCoroutineStmtLoc = Loc; + FirstCoroutineStmtKind = llvm::StringSwitch<unsigned char>(Keyword) + .Case("co_return", 0) + .Case("co_await", 1) + .Case("co_yield", 2); + } + + StringRef getFirstCoroutineStmtKeyword() const { + assert(FirstCoroutineStmtLoc.isValid() + && "no coroutine statement available"); + switch (FirstCoroutineStmtKind) { + case 0: return "co_return"; + case 1: return "co_await"; + case 2: return "co_yield"; + default: + llvm_unreachable("FirstCoroutineStmtKind has an invalid value"); + }; + } + void setNeedsCoroutineSuspends(bool value = true) { assert((!value || CoroutineSuspends.first == nullptr) && "we already have valid suspend points"); diff --git a/clang/lib/Sema/ScopeInfo.cpp b/clang/lib/Sema/ScopeInfo.cpp index 8050889d71a..b309a36a30a 100644 --- a/clang/lib/Sema/ScopeInfo.cpp +++ b/clang/lib/Sema/ScopeInfo.cpp @@ -40,13 +40,15 @@ void FunctionScopeInfo::Clear() { FirstCXXTryLoc = SourceLocation(); FirstSEHTryLoc = SourceLocation(); - SwitchStack.clear(); - Returns.clear(); + // Coroutine state + FirstCoroutineStmtLoc = SourceLocation(); CoroutinePromise = nullptr; NeedsCoroutineSuspends = true; CoroutineSuspends.first = nullptr; CoroutineSuspends.second = nullptr; - CoroutineStmts.clear(); + + SwitchStack.clear(); + Returns.clear(); ErrorTrap.reset(); PossiblyUnreachableDiags.clear(); WeakObjectUses.clear(); diff --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp index 7bbb2f46e56..4d0f8a8f768 100644 --- a/clang/lib/Sema/SemaCoroutine.cpp +++ b/clang/lib/Sema/SemaCoroutine.cpp @@ -400,7 +400,8 @@ VarDecl *Sema::buildCoroutinePromise(SourceLocation Loc) { /// Check that this is a context in which a coroutine suspension can appear. static FunctionScopeInfo *checkCoroutineContext(Sema &S, SourceLocation Loc, - StringRef Keyword) { + StringRef Keyword, + bool IsImplicit = false) { if (!isValidCoroutineContext(S, Loc, Keyword)) return nullptr; @@ -409,6 +410,9 @@ static FunctionScopeInfo *checkCoroutineContext(Sema &S, SourceLocation Loc, auto *ScopeInfo = S.getCurFunction(); assert(ScopeInfo && "missing function scope for function"); + if (ScopeInfo->FirstCoroutineStmtLoc.isInvalid() && !IsImplicit) + ScopeInfo->setFirstCoroutineStmt(Loc, Keyword); + if (ScopeInfo->CoroutinePromise) return ScopeInfo; @@ -488,7 +492,7 @@ ExprResult Sema::ActOnCoawaitExpr(Scope *S, SourceLocation Loc, Expr *E) { } ExprResult Sema::BuildUnresolvedCoawaitExpr(SourceLocation Loc, Expr *E, - UnresolvedLookupExpr *Lookup) { + UnresolvedLookupExpr *Lookup) { auto *FSI = checkCoroutineContext(*this, Loc, "co_await"); if (!FSI) return ExprError(); @@ -504,7 +508,6 @@ ExprResult Sema::BuildUnresolvedCoawaitExpr(SourceLocation Loc, Expr *E, if (Promise->getType()->isDependentType()) { Expr *Res = new (Context) DependentCoawaitExpr(Loc, Context.DependentTy, E, Lookup); - FSI->CoroutineStmts.push_back(Res); return Res; } @@ -528,7 +531,7 @@ ExprResult Sema::BuildUnresolvedCoawaitExpr(SourceLocation Loc, Expr *E, ExprResult Sema::BuildResolvedCoawaitExpr(SourceLocation Loc, Expr *E, bool IsImplicit) { - auto *Coroutine = checkCoroutineContext(*this, Loc, "co_await"); + auto *Coroutine = checkCoroutineContext(*this, Loc, "co_await", IsImplicit); if (!Coroutine) return ExprError(); @@ -541,8 +544,6 @@ ExprResult Sema::BuildResolvedCoawaitExpr(SourceLocation Loc, Expr *E, if (E->getType()->isDependentType()) { Expr *Res = new (Context) CoawaitExpr(Loc, Context.DependentTy, E, IsImplicit); - if (!IsImplicit) - Coroutine->CoroutineStmts.push_back(Res); return Res; } @@ -560,8 +561,7 @@ ExprResult Sema::BuildResolvedCoawaitExpr(SourceLocation Loc, Expr *E, Expr *Res = new (Context) CoawaitExpr(Loc, E, RSS.Results[0], RSS.Results[1], RSS.Results[2], RSS.OpaqueValue, IsImplicit); - if (!IsImplicit) - Coroutine->CoroutineStmts.push_back(Res); + return Res; } @@ -597,7 +597,6 @@ ExprResult Sema::BuildCoyieldExpr(SourceLocation Loc, Expr *E) { if (E->getType()->isDependentType()) { Expr *Res = new (Context) CoyieldExpr(Loc, Context.DependentTy, E); - Coroutine->CoroutineStmts.push_back(Res); return Res; } @@ -614,7 +613,7 @@ ExprResult Sema::BuildCoyieldExpr(SourceLocation Loc, Expr *E) { Expr *Res = new (Context) CoyieldExpr(Loc, E, RSS.Results[0], RSS.Results[1], RSS.Results[2], RSS.OpaqueValue); - Coroutine->CoroutineStmts.push_back(Res); + return Res; } @@ -628,7 +627,7 @@ StmtResult Sema::ActOnCoreturnStmt(Scope *S, SourceLocation Loc, Expr *E) { StmtResult Sema::BuildCoreturnStmt(SourceLocation Loc, Expr *E, bool IsImplicit) { - auto *FSI = checkCoroutineContext(*this, Loc, "co_return"); + auto *FSI = checkCoroutineContext(*this, Loc, "co_return", IsImplicit); if (!FSI) return StmtError(); @@ -656,8 +655,6 @@ StmtResult Sema::BuildCoreturnStmt(SourceLocation Loc, Expr *E, Expr *PCE = ActOnFinishFullExpr(PC.get()).get(); Stmt *Res = new (Context) CoreturnStmt(Loc, E, PCE, IsImplicit); - if (!IsImplicit) - FSI->CoroutineStmts.push_back(Res); return Res; } @@ -774,15 +771,11 @@ void Sema::CheckCompletedCoroutineBody(FunctionDecl *FD, Stmt *&Body) { // Coroutines [stmt.return]p1: // A return statement shall not appear in a coroutine. if (Fn->FirstReturnLoc.isValid()) { + assert(Fn->FirstCoroutineStmtLoc.isValid() && + "first coroutine location not set"); Diag(Fn->FirstReturnLoc, diag::err_return_in_coroutine); - // 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"); - } + Diag(Fn->FirstCoroutineStmtLoc, diag::note_declared_coroutine_here) + << Fn->getFirstCoroutineStmtKeyword(); } SubStmtBuilder Builder(*this, *FD, *Fn, Body); if (Builder.isInvalid()) diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 4e22762eb19..7a89c9dea13 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -6857,7 +6857,7 @@ TreeTransform<Derived>::TransformCoroutineBodyStmt(CoroutineBodyStmt *S) { ScopeInfo->NeedsCoroutineSuspends && ScopeInfo->CoroutineSuspends.first == nullptr && ScopeInfo->CoroutineSuspends.second == nullptr && - ScopeInfo->CoroutineStmts.empty() && "expected clean scope info"); + "expected clean scope info"); // Set that we have (possibly-invalid) suspend points before we do anything // that may fail. diff --git a/clang/test/SemaCXX/coroutines.cpp b/clang/test/SemaCXX/coroutines.cpp index 22fdcde1c21..8876a146557 100644 --- a/clang/test/SemaCXX/coroutines.cpp +++ b/clang/test/SemaCXX/coroutines.cpp @@ -162,11 +162,59 @@ void mixed_yield() { return; // expected-error {{not allowed in coroutine}} } +void mixed_yield_invalid() { + co_yield blah; // expected-error {{use of undeclared identifier}} + // expected-note@-1 {{function is a coroutine due to use of 'co_yield'}} + return; // expected-error {{return statement not allowed in coroutine}} +} + +template <class T> +void mixed_yield_template(T) { + co_yield blah; // expected-error {{use of undeclared identifier}} + // expected-note@-1 {{function is a coroutine due to use of 'co_yield'}} + return; // expected-error {{return statement not allowed in coroutine}} +} + +template <class T> +void mixed_yield_template2(T) { + co_yield 42; + // expected-note@-1 {{function is a coroutine due to use of 'co_yield'}} + return; // expected-error {{return statement not allowed in coroutine}} +} + +template <class T> +void mixed_yield_template3(T v) { + co_yield blah(v); + // expected-note@-1 {{function is a coroutine due to use of 'co_yield'}} + return; // expected-error {{return statement not allowed in coroutine}} +} + void mixed_await() { co_await a; // expected-note {{use of 'co_await'}} return; // expected-error {{not allowed in coroutine}} } +void mixed_await_invalid() { + co_await 42; // expected-error {{'int' is not a structure or union}} + // expected-note@-1 {{function is a coroutine due to use of 'co_await'}} + return; // expected-error {{not allowed in coroutine}} +} + +template <class T> +void mixed_await_template(T) { + co_await 42; + // expected-note@-1 {{function is a coroutine due to use of 'co_await'}} + return; // expected-error {{not allowed in coroutine}} +} + +template <class T> +void mixed_await_template2(T v) { + co_await v; // expected-error {{'long' is not a structure or union}} + // expected-note@-1 {{function is a coroutine due to use of 'co_await'}} + return; // expected-error {{not allowed in coroutine}} +} +template void mixed_await_template2(long); // expected-note {{requested here}} + void only_coreturn(void_tag) { co_return; // OK } @@ -178,6 +226,33 @@ void mixed_coreturn(void_tag, bool b) { return; // expected-error {{not allowed in coroutine}} } +void mixed_coreturn_invalid(bool b) { + if (b) + co_return; // expected-note {{use of 'co_return'}} + // expected-error@-1 {{no member named 'return_void' in 'promise'}} + else + return; // expected-error {{not allowed in coroutine}} +} + +template <class T> +void mixed_coreturn_template(void_tag, bool b, T v) { + if (b) + co_return v; // expected-note {{use of 'co_return'}} + // expected-error@-1 {{no member named 'return_value' in 'promise_void'}} + else + return; // expected-error {{not allowed in coroutine}} +} +template void mixed_coreturn_template(void_tag, bool, int); // expected-note {{requested here}} + +template <class T> +void mixed_coreturn_template2(bool b, T) { + if (b) + co_return v; // expected-note {{use of 'co_return'}} + // expected-error@-1 {{use of undeclared identifier 'v'}} + else + return; // expected-error {{not allowed in coroutine}} +} + struct CtorDtor { CtorDtor() { co_yield 0; // expected-error {{'co_yield' cannot be used in a constructor}} |

