return Prefix;
}
+static bool memberCallExpressionCanThrow(const Expr *E) {
+ if (const auto *CE = dyn_cast<CXXMemberCallExpr>(E))
+ if (const auto *Proto =
+ CE->getMethodDecl()->getType()->getAs<FunctionProtoType>())
+ if (isNoexceptExceptionSpec(Proto->getExceptionSpecType()) &&
+ Proto->canThrow() == CT_Cannot)
+ return false;
+ return true;
+}
+
// Emit suspend expression which roughly looks like:
//
// auto && x = CommonExpr();
// Emit await_resume expression.
CGF.EmitBlock(ReadyBlock);
+
+ // Exception handling requires additional IR. If the 'await_resume' function
+ // is marked as 'noexcept', we avoid generating this additional IR.
CXXTryStmt *TryStmt = nullptr;
- if (Coro.ExceptionHandler && Kind == AwaitKind::Init) {
+ if (Coro.ExceptionHandler && Kind == AwaitKind::Init &&
+ memberCallExpressionCanThrow(S.getResumeExpr())) {
Coro.ResumeEHVar =
CGF.CreateTempAlloca(Builder.getInt1Ty(), Prefix + Twine("resume.eh"));
Builder.CreateFlagStore(true, Coro.ResumeEHVar);
CurCoro.Data->CurrentAwaitKind = AwaitKind::Normal;
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);
+ // If we generated IR to record whether an exception was thrown from
+ // 'await_resume', then use that IR to determine whether the coroutine
+ // body should be skipped.
+ // If we didn't generate the IR (perhaps because 'await_resume' was marked
+ // as 'noexcept'), then we skip this check.
+ BasicBlock *ContBB = nullptr;
+ if (CurCoro.Data->ResumeEHVar) {
+ BasicBlock *BodyBB = createBasicBlock("coro.resumed.body");
+ 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,
emitBodyAndFallthrough(*this, S, TryStmt->getTryBlock());
ExitCXXTryStmt(*TryStmt);
- EmitBlock(ContBB);
+ if (ContBB)
+ EmitBlock(ContBB);
}
else {
emitBodyAndFallthrough(*this, S, S.getBody());
void await_resume() { throw 42; }
};
-struct task {
+struct throwing_task {
struct promise_type {
- task get_return_object() { return task{}; }
+ auto get_return_object() { return throwing_task{}; }
auto initial_suspend() { return throwing_awaitable{}; }
auto final_suspend() { return coro::suspend_never{}; }
void return_void() {}
};
// CHECK-LABEL: define void @_Z1fv()
-task f() {
+throwing_task f() {
// A variable RESUMETHREW is used to keep track of whether the body
// of 'await_resume' threw an exception. Exceptions thrown in
// 'await_resume' are unwound to RESUMELPAD.
// CHECK: [[RESUMELPAD]]:
// CHECK: br label %[[RESUMECATCH:.+]]
// CHECK: [[RESUMECATCH]]:
- // CHECK: invoke void @_ZN4task12promise_type19unhandled_exceptionEv
+ // CHECK: invoke void @_ZN13throwing_task12promise_type19unhandled_exceptionEv
// CHECK-NEXT: to label %[[RESUMEENDCATCH:.+]] unwind label
// CHECK: [[RESUMEENDCATCH]]:
// CHECK-NEXT: invoke void @__cxa_end_catch()
// CHECK-NEXT: br i1 %[[RESUMETHREWLOAD]], label %[[RESUMEDCONT:.+]], label %[[RESUMEDBODY:.+]]
// CHECK: [[RESUMEDBODY]]:
- // CHECK: invoke void @_ZN4task12promise_type11return_voidEv
+ // CHECK: invoke void @_ZN13throwing_task12promise_type11return_voidEv
// CHECK-NEXT: to label %[[REDUMEDBODYCONT:.+]] unwind label
// CHECK: [[REDUMEDBODYCONT]]:
// CHECK-NEXT: br label %[[COROFINAL:.+]]
// CHECK-NEXT: br label %[[COROFINAL]]
// CHECK: [[COROFINAL]]:
- // CHECK-NEXT: invoke void @_ZN4task12promise_type13final_suspendEv
+ // CHECK-NEXT: invoke void @_ZN13throwing_task12promise_type13final_suspendEv
+ co_return;
+}
+
+struct noexcept_awaitable {
+ bool await_ready() { return true; }
+ void await_suspend(coro::coroutine_handle<>) {}
+ void await_resume() noexcept {}
+};
+
+struct noexcept_task {
+ struct promise_type {
+ auto get_return_object() { return noexcept_task{}; }
+ auto initial_suspend() { return noexcept_awaitable{}; }
+ auto final_suspend() { return coro::suspend_never{}; }
+ void return_void() {}
+ void unhandled_exception() {}
+ };
+};
+
+// CHECK-LABEL: define void @_Z1gv()
+noexcept_task g() {
+ // If the await_resume function is marked as noexcept, none of the additional
+ // conditions that are present in f() above are added to the IR.
+ // This means that no i1 are stored before or after calling await_resume:
+ // CHECK: init.ready:
+ // CHECK-NEXT: call void @_ZN18noexcept_awaitable12await_resumeEv
+ // CHECK-NOT: store i1 false, i1*
co_return;
}