summaryrefslogtreecommitdiffstats
path: root/clang/lib/Sema/SemaCoroutine.cpp
diff options
context:
space:
mode:
authorEric Fiselier <eric@efcs.ca>2016-10-27 07:30:31 +0000
committerEric Fiselier <eric@efcs.ca>2016-10-27 07:30:31 +0000
commit709d1b30ab9e649fc3e2485c1fa11f2f203e9482 (patch)
tree91a86f9bd809c120803fb1f3bf8eed53ca2758bb /clang/lib/Sema/SemaCoroutine.cpp
parentec47065795fd8ce007e168ebb886baf16a72fdfd (diff)
downloadbcm5719-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.cpp86
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);
}
OpenPOWER on IntegriCloud