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

Reply via email to