2018-05-04 22:02:37 +08:00
|
|
|
// 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; }
|
|
|
|
};
|
|
|
|
|
2018-06-24 02:57:26 +08:00
|
|
|
struct throwing_task {
|
2018-05-04 22:02:37 +08:00
|
|
|
struct promise_type {
|
2018-06-24 02:57:26 +08:00
|
|
|
auto get_return_object() { return throwing_task{}; }
|
2018-05-04 22:02:37 +08:00
|
|
|
auto initial_suspend() { return throwing_awaitable{}; }
|
[Coroutines] Ensure co_await promise.final_suspend() does not throw
Summary:
This patch addresses https://bugs.llvm.org/show_bug.cgi?id=46256
The spec of coroutine requires that the expression co_await promise.final_suspend() shall not be potentially-throwing.
To check this, we recursively look at every call (including Call, MemberCall, OperatorCall and Constructor) in all code
generated by the final suspend, and ensure that the callees are declared with noexcept. We also look at any returned data
type that requires explicit destruction, and check their destructors for noexcept.
This patch does not check declarations with dependent types yet, which will be done in future patches.
Updated all tests to add noexcept to the required functions, and added a dedicated test for this patch.
This patch might start to cause existing codebase fail to compile because most people may not have been strict in tagging
all the related functions noexcept.
Reviewers: lewissbaker, modocache, junparser
Reviewed By: modocache
Subscribers: arphaman, junparser, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D82029
2020-06-16 07:27:41 +08:00
|
|
|
auto final_suspend() noexcept { return coro::suspend_never{}; }
|
2018-05-04 22:02:37 +08:00
|
|
|
void return_void() {}
|
|
|
|
void unhandled_exception() {}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2020-02-04 02:09:39 +08:00
|
|
|
// CHECK-LABEL: define void @_Z1fv()
|
2018-06-24 02:57:26 +08:00
|
|
|
throwing_task f() {
|
2018-05-04 22:02:37 +08:00
|
|
|
// 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: init.ready:
|
|
|
|
// CHECK-NEXT: store i1 true, i1* %[[RESUMETHREW:.+]], align 1
|
|
|
|
// CHECK-NEXT: invoke void @_ZN18throwing_awaitable12await_resumeEv
|
|
|
|
// CHECK-NEXT: to label %[[RESUMECONT:.+]] unwind label %[[RESUMELPAD:.+]]
|
|
|
|
|
|
|
|
// If 'await_resume' does not throw an exception, 'false' is stored in
|
|
|
|
// variable RESUMETHREW.
|
|
|
|
// CHECK: [[RESUMECONT]]:
|
|
|
|
// CHECK-NEXT: store i1 false, i1* %[[RESUMETHREW]]
|
|
|
|
// CHECK-NEXT: br label %[[RESUMETRYCONT:.+]]
|
|
|
|
|
|
|
|
// 'unhandled_exception' is called for the exception thrown in
|
|
|
|
// 'await_resume'. The variable RESUMETHREW is never set to false,
|
|
|
|
// and a jump is made to RESUMETRYCONT.
|
|
|
|
// CHECK: [[RESUMELPAD]]:
|
|
|
|
// CHECK: br label %[[RESUMECATCH:.+]]
|
|
|
|
// CHECK: [[RESUMECATCH]]:
|
2018-06-24 02:57:26 +08:00
|
|
|
// CHECK: invoke void @_ZN13throwing_task12promise_type19unhandled_exceptionEv
|
2018-05-04 22:02:37 +08:00
|
|
|
// CHECK-NEXT: to label %[[RESUMEENDCATCH:.+]] unwind label
|
|
|
|
// CHECK: [[RESUMEENDCATCH]]:
|
|
|
|
// CHECK-NEXT: invoke void @__cxa_end_catch()
|
|
|
|
// CHECK-NEXT: to label %[[RESUMEENDCATCHCONT:.+]] unwind label
|
|
|
|
// CHECK: [[RESUMEENDCATCHCONT]]:
|
|
|
|
// CHECK-NEXT: br label %[[RESUMETRYCONT]]
|
|
|
|
|
|
|
|
// The variable RESUMETHREW is loaded and if true, then 'await_resume'
|
|
|
|
// threw an exception and the coroutine body is skipped, and the final
|
|
|
|
// suspend is executed immediately. Otherwise, the coroutine body is
|
|
|
|
// executed, and then the final suspend.
|
|
|
|
// CHECK: [[RESUMETRYCONT]]:
|
|
|
|
// CHECK-NEXT: %[[RESUMETHREWLOAD:.+]] = load i1, i1* %[[RESUMETHREW]]
|
|
|
|
// CHECK-NEXT: br i1 %[[RESUMETHREWLOAD]], label %[[RESUMEDCONT:.+]], label %[[RESUMEDBODY:.+]]
|
|
|
|
|
|
|
|
// CHECK: [[RESUMEDBODY]]:
|
2018-06-24 02:57:26 +08:00
|
|
|
// CHECK: invoke void @_ZN13throwing_task12promise_type11return_voidEv
|
2018-05-04 22:02:37 +08:00
|
|
|
// CHECK-NEXT: to label %[[REDUMEDBODYCONT:.+]] unwind label
|
|
|
|
// CHECK: [[REDUMEDBODYCONT]]:
|
|
|
|
// CHECK-NEXT: br label %[[COROFINAL:.+]]
|
|
|
|
|
|
|
|
// CHECK: [[RESUMEDCONT]]:
|
|
|
|
// CHECK-NEXT: br label %[[COROFINAL]]
|
|
|
|
|
|
|
|
// CHECK: [[COROFINAL]]:
|
[Coroutines] Ensure co_await promise.final_suspend() does not throw
Summary:
This patch addresses https://bugs.llvm.org/show_bug.cgi?id=46256
The spec of coroutine requires that the expression co_await promise.final_suspend() shall not be potentially-throwing.
To check this, we recursively look at every call (including Call, MemberCall, OperatorCall and Constructor) in all code
generated by the final suspend, and ensure that the callees are declared with noexcept. We also look at any returned data
type that requires explicit destruction, and check their destructors for noexcept.
This patch does not check declarations with dependent types yet, which will be done in future patches.
Updated all tests to add noexcept to the required functions, and added a dedicated test for this patch.
This patch might start to cause existing codebase fail to compile because most people may not have been strict in tagging
all the related functions noexcept.
Reviewers: lewissbaker, modocache, junparser
Reviewed By: modocache
Subscribers: arphaman, junparser, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D82029
2020-06-16 07:27:41 +08:00
|
|
|
// CHECK-NEXT: call void @_ZN13throwing_task12promise_type13final_suspendEv
|
2018-06-24 02:57:26 +08:00
|
|
|
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{}; }
|
[Coroutines] Ensure co_await promise.final_suspend() does not throw
Summary:
This patch addresses https://bugs.llvm.org/show_bug.cgi?id=46256
The spec of coroutine requires that the expression co_await promise.final_suspend() shall not be potentially-throwing.
To check this, we recursively look at every call (including Call, MemberCall, OperatorCall and Constructor) in all code
generated by the final suspend, and ensure that the callees are declared with noexcept. We also look at any returned data
type that requires explicit destruction, and check their destructors for noexcept.
This patch does not check declarations with dependent types yet, which will be done in future patches.
Updated all tests to add noexcept to the required functions, and added a dedicated test for this patch.
This patch might start to cause existing codebase fail to compile because most people may not have been strict in tagging
all the related functions noexcept.
Reviewers: lewissbaker, modocache, junparser
Reviewed By: modocache
Subscribers: arphaman, junparser, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D82029
2020-06-16 07:27:41 +08:00
|
|
|
auto final_suspend() noexcept { return coro::suspend_never{}; }
|
2018-06-24 02:57:26 +08:00
|
|
|
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*
|
2018-05-04 22:02:37 +08:00
|
|
|
co_return;
|
|
|
|
}
|