[Coroutines] Catch exceptions in await_resume
Summary:
http://wg21.link/P0664r2 section "Evolution/Core Issues 24" describes a
proposed change to Coroutines TS that would have any exceptions thrown
after the initial suspend point of a coroutine be caught by the handler
specified by the promise type's 'unhandled_exception' member function.
This commit provides a sample implementation of the specified behavior.
Test Plan: `check-clang`
Reviewers: GorNishanov, EricWF
Reviewed By: GorNishanov
Subscribers: cfe-commits, lewissbaker, eric_niebler
Differential Revision: https://reviews.llvm.org/D45860
llvm-svn: 331519
diff --git a/clang/lib/CodeGen/CGCoroutine.cpp b/clang/lib/CodeGen/CGCoroutine.cpp
index 74c158c..bd16896 100644
--- a/clang/lib/CodeGen/CGCoroutine.cpp
+++ b/clang/lib/CodeGen/CGCoroutine.cpp
@@ -44,6 +44,15 @@
// A branch to this block is emitted when coroutine needs to suspend.
llvm::BasicBlock *SuspendBB = nullptr;
+ // The promise type's 'unhandled_exception' handler, if it defines one.
+ Stmt *ExceptionHandler = nullptr;
+
+ // A temporary i1 alloca that stores whether 'await_resume' threw an
+ // exception. If it did, 'true' is stored in this variable, and the coroutine
+ // body must be skipped. If the promise type does not define an exception
+ // handler, this is null.
+ llvm::Value *ResumeEHVar = nullptr;
+
// Stores the jump destination just before the coroutine memory is freed.
// This is the destination that every suspend point jumps to for the cleanup
// branch.
@@ -208,11 +217,32 @@
// Emit await_resume expression.
CGF.EmitBlock(ReadyBlock);
+ CXXTryStmt *TryStmt = nullptr;
+ if (Coro.ExceptionHandler && Kind == AwaitKind::Init) {
+ Coro.ResumeEHVar =
+ CGF.CreateTempAlloca(Builder.getInt1Ty(), Prefix + Twine("resume.eh"));
+ Builder.CreateFlagStore(true, Coro.ResumeEHVar);
+
+ auto Loc = S.getResumeExpr()->getExprLoc();
+ auto *Catch = new (CGF.getContext())
+ CXXCatchStmt(Loc, /*exDecl=*/nullptr, Coro.ExceptionHandler);
+ auto *TryBody =
+ CompoundStmt::Create(CGF.getContext(), S.getResumeExpr(), Loc, Loc);
+ TryStmt = CXXTryStmt::Create(CGF.getContext(), Loc, TryBody, Catch);
+ CGF.EnterCXXTryStmt(*TryStmt);
+ }
+
LValueOrRValue Res;
if (forLValue)
Res.LV = CGF.EmitLValue(S.getResumeExpr());
else
Res.RV = CGF.EmitAnyExpr(S.getResumeExpr(), aggSlot, ignoreResult);
+
+ if (TryStmt) {
+ Builder.CreateFlagStore(false, Coro.ResumeEHVar);
+ CGF.ExitCXXTryStmt(*TryStmt);
+ }
+
return Res;
}
@@ -588,19 +618,31 @@
EHStack.pushCleanup<CallCoroEnd>(EHCleanup);
CurCoro.Data->CurrentAwaitKind = AwaitKind::Init;
+ CurCoro.Data->ExceptionHandler = S.getExceptionHandler();
EmitStmt(S.getInitSuspendStmt());
CurCoro.Data->FinalJD = getJumpDestInCurrentScope(FinalBB);
CurCoro.Data->CurrentAwaitKind = AwaitKind::Normal;
- if (auto *OnException = S.getExceptionHandler()) {
+ if (CurCoro.Data->ExceptionHandler) {
+ BasicBlock *BodyBB = createBasicBlock("coro.resumed.body");
+ BasicBlock *ContBB = createBasicBlock("coro.resumed.cont");
+ Value *SkipBody =
+ Builder.CreateFlagLoad(CurCoro.Data->ResumeEHVar, "coro.resumed.eh");
+ Builder.CreateCondBr(SkipBody, ContBB, BodyBB);
+ EmitBlock(BodyBB);
+
auto Loc = S.getLocStart();
- CXXCatchStmt Catch(Loc, /*exDecl=*/nullptr, OnException);
- auto *TryStmt = CXXTryStmt::Create(getContext(), Loc, S.getBody(), &Catch);
+ CXXCatchStmt Catch(Loc, /*exDecl=*/nullptr,
+ CurCoro.Data->ExceptionHandler);
+ auto *TryStmt =
+ CXXTryStmt::Create(getContext(), Loc, S.getBody(), &Catch);
EnterCXXTryStmt(*TryStmt);
emitBodyAndFallthrough(*this, S, TryStmt->getTryBlock());
ExitCXXTryStmt(*TryStmt);
+
+ EmitBlock(ContBB);
}
else {
emitBodyAndFallthrough(*this, S, S.getBody());