modocache created this revision. modocache added reviewers: GorNishanov, EricWF.
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` Repository: rC Clang https://reviews.llvm.org/D45860 Files: lib/CodeGen/CGCoroutine.cpp test/CodeGenCoroutines/coro-await-resume-eh.cpp
Index: test/CodeGenCoroutines/coro-await-resume-eh.cpp =================================================================== --- /dev/null +++ test/CodeGenCoroutines/coro-await-resume-eh.cpp @@ -0,0 +1,60 @@ +// Test the behavior of http://wg21.link/P0664, a proposal to catch any +// exceptions thrown after the initial suspend point of a coroutine by +// executing the handler specified by the promise type's 'unhandled_exception' +// member function. +// +// RUN: %clang_cc1 -std=c++14 -fcoroutines-ts \ +// RUN: -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s \ +// RUN: -fexceptions -fcxx-exceptions -disable-llvm-passes \ +// RUN: | FileCheck %s + +#include "Inputs/coroutine.h" + +namespace coro = std::experimental::coroutines_v1; + +struct throwing_awaitable { + bool await_ready() { return true; } + void await_suspend(coro::coroutine_handle<>) {} + void await_resume() { throw 42; } +}; + +struct task { + struct promise_type { + task get_return_object() { return task{}; } + auto initial_suspend() { return throwing_awaitable{}; } + auto final_suspend() { return coro::suspend_never{}; } + void return_void() {} + void unhandled_exception() {} + }; +}; + +// CHECK-LABEL: define void @_Z1fv() +task f() { + // CHECK: init.ready: + // CHECK: invoke void @_ZN4task12promise_type15initial_suspendEv + // CHECK-NEXT: to label %[[AWAITRESUME:.+]] unwind label {{.+}} + + // 'await_resume' unwinds to the LPAD basic block... + // CHECK: [[AWAITRESUME]]: + // CHECK: invoke void @_ZN18throwing_awaitable12await_resumeEv + // CHECK-NEXT: to label %[[RETVOID:.+]] unwind label %[[LPAD:.+]] + + // ...as does 'return_void'. + // CHECK: [[RETVOID]]: + // CHECK: invoke void @_ZN4task12promise_type11return_voidEv + // CHECK-NEXT: to label {{.+}} unwind label %[[LPAD]] + + // Ensure that LPAD's only two predecessors are the blocks that + // invoke 'await_resume' and 'return_void'. 'await_ready' and + // 'await_suspend' do NOT unwind here. + // CHECK: [[LPAD]]: + // CHECK-SAME: preds = %[[RETVOID]], %[[AWAITRESUME]]{{$}} + // CHECK: br label %[[CATCH:.+]] + + // LPAD unconditionally breaks to this basic block, which invokes + // the 'unhandled_exception' member function. + // CHECK: [[CATCH]]: + // CHECK-SAME: preds = %[[LPAD]]{{$}} + // CHECK: invoke void @_ZN4task12promise_type19unhandled_exceptionEv + co_return; +} Index: lib/CodeGen/CGCoroutine.cpp =================================================================== --- lib/CodeGen/CGCoroutine.cpp +++ lib/CodeGen/CGCoroutine.cpp @@ -41,6 +41,9 @@ // we need to add co_return; equivalent at the end of the user authored body. unsigned CoreturnCount = 0; + // Whether an exception handler is available. + bool HasAwaitResumeEH = false; + // A branch to this block is emitted when coroutine needs to suspend. llvm::BasicBlock *SuspendBB = nullptr; @@ -209,10 +212,12 @@ // Emit await_resume expression. CGF.EmitBlock(ReadyBlock); LValueOrRValue Res; - if (forLValue) - Res.LV = CGF.EmitLValue(S.getResumeExpr()); - else - Res.RV = CGF.EmitAnyExpr(S.getResumeExpr(), aggSlot, ignoreResult); + if (!(Kind == AwaitKind::Init && Coro.HasAwaitResumeEH)) { + if (forLValue) + Res.LV = CGF.EmitLValue(S.getResumeExpr()); + else + Res.RV = CGF.EmitAnyExpr(S.getResumeExpr(), aggSlot, ignoreResult); + } return Res; } @@ -587,19 +592,32 @@ EHStack.pushCleanup<CallCoroEnd>(EHCleanup); + auto InitSuspend = S.getInitSuspendStmt(); CurCoro.Data->CurrentAwaitKind = AwaitKind::Init; - EmitStmt(S.getInitSuspendStmt()); + CurCoro.Data->HasAwaitResumeEH = S.getExceptionHandler(); + EmitStmt(InitSuspend); CurCoro.Data->FinalJD = getJumpDestInCurrentScope(FinalBB); CurCoro.Data->CurrentAwaitKind = AwaitKind::Normal; if (auto *OnException = S.getExceptionHandler()) { auto Loc = S.getLocStart(); + auto AwaitExpr = + cast<CoawaitExpr>(cast<ExprWithCleanups>(InitSuspend)->getSubExpr()); + + SmallVector<Stmt *, 2> Stmts; + Stmts.push_back(AwaitExpr->getResumeExpr()); + Stmts.push_back(S.getBody()); + CXXCatchStmt Catch(Loc, /*exDecl=*/nullptr, OnException); - auto *TryStmt = CXXTryStmt::Create(getContext(), Loc, S.getBody(), &Catch); + auto *TryBody = CompoundStmt::Create(getContext(), Stmts, Loc, Loc); + auto *TryStmt = CXXTryStmt::Create(getContext(), Loc, TryBody, &Catch); + auto Binder = CodeGenFunction::OpaqueValueMappingData::bind( + *this, AwaitExpr->getOpaqueValue(), AwaitExpr->getCommonExpr()); + auto UnbindOnExit = llvm::make_scope_exit([&] { Binder.unbind(*this); }); EnterCXXTryStmt(*TryStmt); - emitBodyAndFallthrough(*this, S, TryStmt->getTryBlock()); + emitBodyAndFallthrough(*this, S, TryBody); ExitCXXTryStmt(*TryStmt); } else {
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits