diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2019-08-20 20:50:49 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2019-08-20 20:50:49 +0000 |
commit | 2298981669bf3bd63335a4be179bc0f96823a8f4 (patch) | |
tree | 1cbe2eb27f030d2d70b80ee5ca3c86bee7326a9f /lib/Sema/SemaCoroutine.cpp | |
parent | 9a83721404652cea39e9f02ae3e3b5c964602a5c (diff) |
Diffstat (limited to 'lib/Sema/SemaCoroutine.cpp')
-rw-r--r-- | lib/Sema/SemaCoroutine.cpp | 102 |
1 files changed, 63 insertions, 39 deletions
diff --git a/lib/Sema/SemaCoroutine.cpp b/lib/Sema/SemaCoroutine.cpp index 181efa6d3dd0..f0347af6a1bb 100644 --- a/lib/Sema/SemaCoroutine.cpp +++ b/lib/Sema/SemaCoroutine.cpp @@ -1,9 +1,8 @@ -//===--- SemaCoroutines.cpp - Semantic Analysis for Coroutines ------------===// +//===-- SemaCoroutine.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. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -186,21 +185,8 @@ static QualType lookupCoroutineHandleType(Sema &S, QualType PromiseType, static bool isValidCoroutineContext(Sema &S, SourceLocation Loc, StringRef Keyword) { - // 'co_await' and 'co_yield' are not permitted in unevaluated operands, - // such as subexpressions of \c sizeof. - // - // [expr.await]p2, emphasis added: "An await-expression shall appear only in - // a *potentially evaluated* expression within the compound-statement of a - // function-body outside of a handler [...] A context within a function where - // an await-expression can appear is called a suspension context of the - // function." And per [expr.yield]p1: "A yield-expression shall appear only - // within a suspension context of a function." - if (S.isUnevaluatedContext()) { - S.Diag(Loc, diag::err_coroutine_unevaluated_context) << Keyword; - return false; - } - - // Per [expr.await]p2, any other usage must be within a function. + // [expr.await]p2 dictates that 'co_await' and 'co_yield' must be used within + // a function body. // FIXME: This also covers [expr.await]p2: "An await-expression shall not // appear in a default argument." But the diagnostic QoI here could be // improved to inform the user that default arguments specifically are not @@ -218,12 +204,11 @@ static bool isValidCoroutineContext(Sema &S, SourceLocation Loc, enum InvalidFuncDiag { DiagCtor = 0, DiagDtor, - DiagCopyAssign, - DiagMoveAssign, DiagMain, DiagConstexpr, DiagAutoRet, DiagVarargs, + DiagConsteval, }; bool Diagnosed = false; auto DiagInvalid = [&](InvalidFuncDiag ID) { @@ -232,23 +217,15 @@ static bool isValidCoroutineContext(Sema &S, SourceLocation Loc, return false; }; - // Diagnose when a constructor, destructor, copy/move assignment operator, + // Diagnose when a constructor, destructor // or the function 'main' are declared as a coroutine. auto *MD = dyn_cast<CXXMethodDecl>(FD); - // [class.ctor]p6: "A constructor shall not be a coroutine." + // [class.ctor]p11: "A constructor shall not be a coroutine." if (MD && isa<CXXConstructorDecl>(MD)) return DiagInvalid(DiagCtor); // [class.dtor]p17: "A destructor shall not be a coroutine." else if (MD && isa<CXXDestructorDecl>(MD)) return DiagInvalid(DiagDtor); - // N4499 [special]p6: "A special member function shall not be a coroutine." - // Per C++ [special]p1, special member functions are the "default constructor, - // copy constructor and copy assignment operator, move constructor and move - // assignment operator, and destructor." - else if (MD && MD->isCopyAssignmentOperator()) - return DiagInvalid(DiagCopyAssign); - else if (MD && MD->isMoveAssignmentOperator()) - return DiagInvalid(DiagMoveAssign); // [basic.start.main]p3: "The function main shall not be a coroutine." else if (FD->isMain()) return DiagInvalid(DiagMain); @@ -258,7 +235,7 @@ static bool isValidCoroutineContext(Sema &S, SourceLocation Loc, // evaluation of e [...] would evaluate one of the following expressions: // [...] an await-expression [...] a yield-expression." if (FD->isConstexpr()) - DiagInvalid(DiagConstexpr); + DiagInvalid(FD->isConsteval() ? DiagConsteval : DiagConstexpr); // [dcl.spec.auto]p15: "A function declared with a return type that uses a // placeholder type shall not be a coroutine." if (FD->getReturnType()->isUndeducedType()) @@ -326,7 +303,7 @@ static Expr *buildBuiltinCall(Sema &S, SourceLocation Loc, Builtin::ID Id, assert(DeclRef.isUsable() && "Builtin reference cannot fail"); ExprResult Call = - S.ActOnCallExpr(/*Scope=*/nullptr, DeclRef.get(), Loc, CallArgs, Loc); + S.BuildCallExpr(/*Scope=*/nullptr, DeclRef.get(), Loc, CallArgs, Loc); assert(!Call.isInvalid() && "Call to builtin cannot fail!"); return Call.get(); @@ -356,7 +333,7 @@ static ExprResult buildCoroutineHandle(Sema &S, QualType PromiseType, if (FromAddr.isInvalid()) return ExprError(); - return S.ActOnCallExpr(nullptr, FromAddr.get(), Loc, FramePtr, Loc); + return S.BuildCallExpr(nullptr, FromAddr.get(), Loc, FramePtr, Loc); } struct ReadySuspendResumeResult { @@ -388,7 +365,7 @@ static ExprResult buildMemberCall(Sema &S, Expr *Base, SourceLocation Loc, return ExprError(); } - return S.ActOnCallExpr(nullptr, Result.get(), Loc, Args, Loc, nullptr); + return S.BuildCallExpr(nullptr, Result.get(), Loc, Args, Loc, nullptr); } // See if return type is coroutine-handle and if so, invoke builtin coro-resume @@ -669,12 +646,57 @@ bool Sema::ActOnCoroutineBodyStart(Scope *SC, SourceLocation KWLoc, return true; } +// Recursively walks up the scope hierarchy until either a 'catch' or a function +// scope is found, whichever comes first. +static bool isWithinCatchScope(Scope *S) { + // 'co_await' and 'co_yield' keywords are disallowed within catch blocks, but + // lambdas that use 'co_await' are allowed. The loop below ends when a + // function scope is found in order to ensure the following behavior: + // + // void foo() { // <- function scope + // try { // + // co_await x; // <- 'co_await' is OK within a function scope + // } catch { // <- catch scope + // co_await x; // <- 'co_await' is not OK within a catch scope + // []() { // <- function scope + // co_await x; // <- 'co_await' is OK within a function scope + // }(); + // } + // } + while (S && !(S->getFlags() & Scope::FnScope)) { + if (S->getFlags() & Scope::CatchScope) + return true; + S = S->getParent(); + } + return false; +} + +// [expr.await]p2, emphasis added: "An await-expression shall appear only in +// a *potentially evaluated* expression within the compound-statement of a +// function-body *outside of a handler* [...] A context within a function +// where an await-expression can appear is called a suspension context of the +// function." +static void checkSuspensionContext(Sema &S, SourceLocation Loc, + StringRef Keyword) { + // First emphasis of [expr.await]p2: must be a potentially evaluated context. + // That is, 'co_await' and 'co_yield' cannot appear in subexpressions of + // \c sizeof. + if (S.isUnevaluatedContext()) + S.Diag(Loc, diag::err_coroutine_unevaluated_context) << Keyword; + + // Second emphasis of [expr.await]p2: must be outside of an exception handler. + if (isWithinCatchScope(S.getCurScope())) + S.Diag(Loc, diag::err_coroutine_within_handler) << Keyword; +} + ExprResult Sema::ActOnCoawaitExpr(Scope *S, SourceLocation Loc, Expr *E) { if (!ActOnCoroutineBodyStart(S, Loc, "co_await")) { CorrectDelayedTyposInExpr(E); return ExprError(); } + checkSuspensionContext(*this, Loc, "co_await"); + if (E->getType()->isPlaceholderType()) { ExprResult R = CheckPlaceholderExpr(E); if (R.isInvalid()) return ExprError(); @@ -772,6 +794,8 @@ ExprResult Sema::ActOnCoyieldExpr(Scope *S, SourceLocation Loc, Expr *E) { return ExprError(); } + checkSuspensionContext(*this, Loc, "co_yield"); + // Build yield_value call. ExprResult Awaitable = buildPromiseCall( *this, getCurFunction()->CoroutinePromise, Loc, "yield_value", E); @@ -1072,7 +1096,7 @@ bool CoroutineStmtBuilder::makeReturnOnAllocFailure() { return false; ExprResult ReturnObjectOnAllocationFailure = - S.ActOnCallExpr(nullptr, DeclNameExpr.get(), Loc, {}, Loc); + S.BuildCallExpr(nullptr, DeclNameExpr.get(), Loc, {}, Loc); if (ReturnObjectOnAllocationFailure.isInvalid()) return false; @@ -1235,7 +1259,7 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() { NewArgs.push_back(Arg); ExprResult NewExpr = - S.ActOnCallExpr(S.getCurScope(), NewRef.get(), Loc, NewArgs, Loc); + S.BuildCallExpr(S.getCurScope(), NewRef.get(), Loc, NewArgs, Loc); NewExpr = S.ActOnFinishFullExpr(NewExpr.get(), /*DiscardedValue*/ false); if (NewExpr.isInvalid()) return false; @@ -1261,7 +1285,7 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() { DeleteArgs.push_back(FrameSize); ExprResult DeleteExpr = - S.ActOnCallExpr(S.getCurScope(), DeleteRef.get(), Loc, DeleteArgs, Loc); + S.BuildCallExpr(S.getCurScope(), DeleteRef.get(), Loc, DeleteArgs, Loc); DeleteExpr = S.ActOnFinishFullExpr(DeleteExpr.get(), /*DiscardedValue*/ false); if (DeleteExpr.isInvalid()) |