diff options
author | Eric Fiselier <eric@efcs.ca> | 2016-10-27 07:30:31 +0000 |
---|---|---|
committer | Eric Fiselier <eric@efcs.ca> | 2016-10-27 07:30:31 +0000 |
commit | 709d1b30ab9e649fc3e2485c1fa11f2f203e9482 (patch) | |
tree | 91a86f9bd809c120803fb1f3bf8eed53ca2758bb /clang/lib/Sema/SemaCoroutine.cpp | |
parent | ec47065795fd8ce007e168ebb886baf16a72fdfd (diff) | |
download | bcm5719-llvm-709d1b30ab9e649fc3e2485c1fa11f2f203e9482.tar.gz bcm5719-llvm-709d1b30ab9e649fc3e2485c1fa11f2f203e9482.zip |
[coroutines] Build fallthrough and set_exception statements.
Summary:
This patch adds semantic checking and building of the fall-through `co_return;` statement as well as the `p.set_exception(std::current_exception())` call for handling uncaught exceptions.
The fall-through statement is built and checked according to:
> [dcl.fct.def.coroutine]/4
> The unqualified-ids return_void and return_value are looked up in the scope of class P. If
> both are found, the program is ill-formed. If the unqualified-id return_void is found, flowing
> off the end of a coroutine is equivalent to a co_return with no operand. Otherwise, flowing off
> the end of a coroutine results in undefined behavior.
Similarly the `set_exception` call is only built when that unqualified-id is found in the scope of class P.
Additionally this patch adds fall-through warnings for non-void returning coroutines. Since it's surprising undefined behavior I thought it would be important to add the warning right away.
Reviewers: majnemer, GorNishanov, rsmith
Subscribers: mehdi_amini, cfe-commits
Differential Revision: https://reviews.llvm.org/D25349
llvm-svn: 285271
Diffstat (limited to 'clang/lib/Sema/SemaCoroutine.cpp')
-rw-r--r-- | clang/lib/Sema/SemaCoroutine.cpp | 86 |
1 files changed, 81 insertions, 5 deletions
diff --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp index 323878e2d57..5231e23c1c1 100644 --- a/clang/lib/Sema/SemaCoroutine.cpp +++ b/clang/lib/Sema/SemaCoroutine.cpp @@ -378,6 +378,34 @@ StmtResult Sema::BuildCoreturnStmt(SourceLocation Loc, Expr *E) { return Res; } +static ExprResult buildStdCurrentExceptionCall(Sema &S, SourceLocation Loc) { + NamespaceDecl *Std = S.getStdNamespace(); + if (!Std) { + S.Diag(Loc, diag::err_implied_std_current_exception_not_found); + return ExprError(); + } + LookupResult Result(S, &S.PP.getIdentifierTable().get("current_exception"), + Loc, Sema::LookupOrdinaryName); + if (!S.LookupQualifiedName(Result, Std)) { + S.Diag(Loc, diag::err_implied_std_current_exception_not_found); + return ExprError(); + } + + // FIXME The STL is free to provide more than one overload. + FunctionDecl *FD = Result.getAsSingle<FunctionDecl>(); + if (!FD) { + S.Diag(Loc, diag::err_malformed_std_current_exception); + return ExprError(); + } + ExprResult Res = S.BuildDeclRefExpr(FD, FD->getType(), VK_LValue, Loc); + Res = S.ActOnCallExpr(/*Scope*/ nullptr, Res.get(), Loc, None, Loc); + if (Res.isInvalid()) { + S.Diag(Loc, diag::err_malformed_std_current_exception); + return ExprError(); + } + return Res; +} + void Sema::CheckCompletedCoroutineBody(FunctionDecl *FD, Stmt *&Body) { FunctionScopeInfo *Fn = getCurFunction(); assert(Fn && !Fn->CoroutineStmts.empty() && "not a coroutine"); @@ -432,10 +460,59 @@ void Sema::CheckCompletedCoroutineBody(FunctionDecl *FD, Stmt *&Body) { if (FinalSuspend.isInvalid()) return FD->setInvalidDecl(); - // FIXME: Perform analysis of set_exception call. - - // FIXME: Try to form 'p.return_void();' expression statement to handle + // Try to form 'p.return_void();' expression statement to handle // control flowing off the end of the coroutine. + // Also try to form 'p.set_exception(std::current_exception());' to handle + // uncaught exceptions. + ExprResult SetException; + StmtResult Fallthrough; + if (Fn->CoroutinePromise && + !Fn->CoroutinePromise->getType()->isDependentType()) { + CXXRecordDecl *RD = Fn->CoroutinePromise->getType()->getAsCXXRecordDecl(); + assert(RD && "Type should have already been checked"); + // [dcl.fct.def.coroutine]/4 + // The unqualified-ids 'return_void' and 'return_value' are looked up in + // the scope of class P. If both are found, the program is ill-formed. + DeclarationName RVoidDN = PP.getIdentifierInfo("return_void"); + LookupResult RVoidResult(*this, RVoidDN, Loc, Sema::LookupMemberName); + const bool HasRVoid = LookupQualifiedName(RVoidResult, RD); + + DeclarationName RValueDN = PP.getIdentifierInfo("return_value"); + LookupResult RValueResult(*this, RValueDN, Loc, Sema::LookupMemberName); + const bool HasRValue = LookupQualifiedName(RValueResult, RD); + + if (HasRVoid && HasRValue) { + // FIXME Improve this diagnostic + Diag(FD->getLocation(), diag::err_coroutine_promise_return_ill_formed) + << RD; + return FD->setInvalidDecl(); + } else if (HasRVoid) { + // If the unqualified-id return_void is found, flowing off the end of a + // coroutine is equivalent to a co_return with no operand. Otherwise, + // flowing off the end of a coroutine results in undefined behavior. + Fallthrough = BuildCoreturnStmt(FD->getLocation(), nullptr); + Fallthrough = ActOnFinishFullStmt(Fallthrough.get()); + if (Fallthrough.isInvalid()) + return FD->setInvalidDecl(); + } + + // [dcl.fct.def.coroutine]/3 + // The unqualified-id set_exception is found in the scope of P by class + // member access lookup (3.4.5). + DeclarationName SetExDN = PP.getIdentifierInfo("set_exception"); + LookupResult SetExResult(*this, SetExDN, Loc, Sema::LookupMemberName); + if (LookupQualifiedName(SetExResult, RD)) { + // Form the call 'p.set_exception(std::current_exception())' + SetException = buildStdCurrentExceptionCall(*this, Loc); + if (SetException.isInvalid()) + return FD->setInvalidDecl(); + Expr *E = SetException.get(); + SetException = buildPromiseCall(*this, Fn, Loc, "set_exception", E); + SetException = ActOnFinishFullExpr(SetException.get(), Loc); + if (SetException.isInvalid()) + return FD->setInvalidDecl(); + } + } // Build implicit 'p.get_return_object()' expression and form initialization // of return type from it. @@ -462,6 +539,5 @@ void Sema::CheckCompletedCoroutineBody(FunctionDecl *FD, Stmt *&Body) { // Build body for the coroutine wrapper statement. Body = new (Context) CoroutineBodyStmt( Body, PromiseStmt.get(), InitialSuspend.get(), FinalSuspend.get(), - /*SetException*/nullptr, /*Fallthrough*/nullptr, - ReturnObject.get(), ParamMoves); + SetException.get(), Fallthrough.get(), ReturnObject.get(), ParamMoves); } |