forked from OSchip/llvm-project
[coroutines] Implement unhandled_exception changes.
Summary: This patch adopts the recent changes that renamed `set_exception(exception_pointer)` to `unhandled_exception()`. Additionally `unhandled_exception()` is now required, and so an error is emitted when exceptions are enabled but the promise type does not provide the member. When exceptions are disabled a warning is emitted instead of an error, The warning notes that the `unhandled_exception()` function is required when exceptions are enabled. Reviewers: rsmith, GorNishanov, aaron.ballman, majnemer Reviewed By: GorNishanov Subscribers: mehdi_amini, cfe-commits Differential Revision: https://reviews.llvm.org/D30859 llvm-svn: 298565
This commit is contained in:
parent
c573acd9e9
commit
a9fdb346db
|
@ -36,6 +36,7 @@ def GNUCompoundLiteralInitializer : DiagGroup<"gnu-compound-literal-initializer"
|
|||
def BitFieldConstantConversion : DiagGroup<"bitfield-constant-conversion">;
|
||||
def BitFieldEnumConversion : DiagGroup<"bitfield-enum-conversion">;
|
||||
def BitFieldWidth : DiagGroup<"bitfield-width">;
|
||||
def Coroutine : DiagGroup<"coroutine">;
|
||||
def ConstantConversion :
|
||||
DiagGroup<"constant-conversion", [ BitFieldConstantConversion ] >;
|
||||
def LiteralConversion : DiagGroup<"literal-conversion">;
|
||||
|
|
|
@ -8865,11 +8865,6 @@ def err_coroutine_promise_type_incomplete : Error<
|
|||
def err_coroutine_type_missing_specialization : Error<
|
||||
"this function cannot be a coroutine: missing definition of "
|
||||
"specialization %q0">;
|
||||
def err_implied_std_current_exception_not_found : Error<
|
||||
"you need to include <exception> before defining a coroutine that implicitly "
|
||||
"uses 'set_exception'">;
|
||||
def err_malformed_std_current_exception : Error<
|
||||
"'std::current_exception' must be a function">;
|
||||
def err_coroutine_promise_return_ill_formed : Error<
|
||||
"%0 declares both 'return_value' and 'return_void'">;
|
||||
def note_coroutine_promise_implicit_await_transform_required_here : Note<
|
||||
|
@ -8877,6 +8872,11 @@ def note_coroutine_promise_implicit_await_transform_required_here : Note<
|
|||
def note_coroutine_promise_call_implicitly_required : Note<
|
||||
"call to '%select{initial_suspend|final_suspend}0' implicitly "
|
||||
"required by the %select{initial suspend point|final suspend point}0">;
|
||||
def err_coroutine_promise_unhandled_exception_required : Error<
|
||||
"%0 is required to declare the member 'unhandled_exception()'">;
|
||||
def warn_coroutine_promise_unhandled_exception_required_with_exceptions : Warning<
|
||||
"%0 is required to declare the member 'unhandled_exception()' when exceptions are enabled">,
|
||||
InGroup<Coroutine>;
|
||||
}
|
||||
|
||||
let CategoryName = "Documentation Issue" in {
|
||||
|
|
|
@ -658,34 +658,6 @@ StmtResult Sema::BuildCoreturnStmt(SourceLocation Loc, Expr *E,
|
|||
return Res;
|
||||
}
|
||||
|
||||
static ExprResult buildStdCurrentExceptionCall(Sema &S, SourceLocation Loc) {
|
||||
NamespaceDecl *Std = S.getStdNamespace();
|
||||
if (!Std) {
|
||||
S.Diag(Loc, diag::err_implied_std_current_exception_not_found);
|
||||
return ExprError();
|
||||
}
|
||||
LookupResult Result(S, &S.PP.getIdentifierTable().get("current_exception"),
|
||||
Loc, Sema::LookupOrdinaryName);
|
||||
if (!S.LookupQualifiedName(Result, Std)) {
|
||||
S.Diag(Loc, diag::err_implied_std_current_exception_not_found);
|
||||
return ExprError();
|
||||
}
|
||||
|
||||
// FIXME The STL is free to provide more than one overload.
|
||||
FunctionDecl *FD = Result.getAsSingle<FunctionDecl>();
|
||||
if (!FD) {
|
||||
S.Diag(Loc, diag::err_malformed_std_current_exception);
|
||||
return ExprError();
|
||||
}
|
||||
ExprResult Res = S.BuildDeclRefExpr(FD, FD->getType(), VK_LValue, Loc);
|
||||
Res = S.ActOnCallExpr(/*Scope*/ nullptr, Res.get(), Loc, None, Loc);
|
||||
if (Res.isInvalid()) {
|
||||
S.Diag(Loc, diag::err_malformed_std_current_exception);
|
||||
return ExprError();
|
||||
}
|
||||
return Res;
|
||||
}
|
||||
|
||||
// Find an appropriate delete for the promise.
|
||||
static FunctionDecl *findDeleteForPromise(Sema &S, SourceLocation Loc,
|
||||
QualType PromiseType) {
|
||||
|
@ -913,36 +885,34 @@ bool SubStmtBuilder::makeOnFallthrough() {
|
|||
}
|
||||
|
||||
bool SubStmtBuilder::makeOnException() {
|
||||
// Try to form 'p.set_exception(std::current_exception());' to handle
|
||||
// uncaught exceptions.
|
||||
// TODO: Post WG21 Issaquah 2016 renamed set_exception to unhandled_exception
|
||||
// TODO: and dropped exception_ptr parameter. Make it so.
|
||||
// Try to form 'p.unhandled_exception();'
|
||||
|
||||
if (!PromiseRecordDecl)
|
||||
return true;
|
||||
|
||||
const bool RequireUnhandledException = S.getLangOpts().CXXExceptions;
|
||||
|
||||
if (!lookupMember(S, "unhandled_exception", PromiseRecordDecl, Loc)) {
|
||||
auto DiagID =
|
||||
RequireUnhandledException
|
||||
? diag::err_coroutine_promise_unhandled_exception_required
|
||||
: diag::
|
||||
warn_coroutine_promise_unhandled_exception_required_with_exceptions;
|
||||
S.Diag(Loc, DiagID) << PromiseRecordDecl;
|
||||
return !RequireUnhandledException;
|
||||
}
|
||||
|
||||
// If exceptions are disabled, don't try to build OnException.
|
||||
if (!S.getLangOpts().CXXExceptions)
|
||||
return true;
|
||||
|
||||
ExprResult SetException;
|
||||
ExprResult UnhandledException = buildPromiseCall(S, Fn.CoroutinePromise, Loc,
|
||||
"unhandled_exception", None);
|
||||
UnhandledException = S.ActOnFinishFullExpr(UnhandledException.get(), Loc);
|
||||
if (UnhandledException.isInvalid())
|
||||
return false;
|
||||
|
||||
// [dcl.fct.def.coroutine]/3
|
||||
// The unqualified-id set_exception is found in the scope of P by class
|
||||
// member access lookup (3.4.5).
|
||||
if (lookupMember(S, "set_exception", PromiseRecordDecl, Loc)) {
|
||||
// Form the call 'p.set_exception(std::current_exception())'
|
||||
SetException = buildStdCurrentExceptionCall(S, Loc);
|
||||
if (SetException.isInvalid())
|
||||
return false;
|
||||
Expr *E = SetException.get();
|
||||
SetException = buildPromiseCall(S, Fn.CoroutinePromise, Loc, "set_exception", E);
|
||||
SetException = S.ActOnFinishFullExpr(SetException.get(), Loc);
|
||||
if (SetException.isInvalid())
|
||||
return false;
|
||||
}
|
||||
|
||||
this->OnException = SetException.get();
|
||||
this->OnException = UnhandledException.get();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
// RUN: %clang_cc1 -triple x86_64-apple-darwin9 %s -std=c++14 -fcoroutines-ts -fsyntax-only -Wignored-qualifiers -Wno-error=return-type -verify -fblocks -Wno-unreachable-code -Wno-unused-value
|
||||
#ifndef STD_COROUTINE_H
|
||||
#define STD_COROUTINE_H
|
||||
|
||||
namespace std {
|
||||
namespace experimental {
|
||||
|
||||
template <class Ret, typename... T>
|
||||
struct coroutine_traits { using promise_type = typename Ret::promise_type; };
|
||||
|
||||
template <class Promise = void>
|
||||
struct coroutine_handle {
|
||||
static coroutine_handle from_address(void *);
|
||||
};
|
||||
template <>
|
||||
struct coroutine_handle<void> {
|
||||
template <class PromiseType>
|
||||
coroutine_handle(coroutine_handle<PromiseType>);
|
||||
static coroutine_handle from_address(void *);
|
||||
};
|
||||
|
||||
struct suspend_always {
|
||||
bool await_ready() { return false; }
|
||||
void await_suspend(coroutine_handle<>) {}
|
||||
void await_resume() {}
|
||||
};
|
||||
|
||||
struct suspend_never {
|
||||
bool await_ready() { return true; }
|
||||
void await_suspend(coroutine_handle<>) {}
|
||||
void await_resume() {}
|
||||
};
|
||||
|
||||
} // namespace experimental
|
||||
} // namespace std
|
||||
|
||||
#endif // STD_COROUTINE_H
|
|
@ -1,24 +1,8 @@
|
|||
// RUN: %clang_cc1 -triple x86_64-apple-darwin9 %s -std=c++14 -fcoroutines-ts -fsyntax-only -Wignored-qualifiers -Wno-error=return-type -verify -fblocks -Wno-unreachable-code -Wno-unused-value
|
||||
#include "Inputs/std-coroutine.h"
|
||||
|
||||
namespace std {
|
||||
namespace experimental {
|
||||
|
||||
template <class Ret, typename... T>
|
||||
struct coroutine_traits { using promise_type = typename Ret::promise_type; };
|
||||
|
||||
template <class Promise = void>
|
||||
struct coroutine_handle {
|
||||
static coroutine_handle from_address(void *);
|
||||
};
|
||||
template <>
|
||||
struct coroutine_handle<void> {
|
||||
template <class PromiseType>
|
||||
coroutine_handle(coroutine_handle<PromiseType>);
|
||||
static coroutine_handle from_address(void *);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
using std::experimental::suspend_always;
|
||||
using std::experimental::suspend_never;
|
||||
|
||||
struct awaitable {
|
||||
bool await_ready();
|
||||
|
@ -26,23 +10,12 @@ struct awaitable {
|
|||
void await_resume();
|
||||
} a;
|
||||
|
||||
struct suspend_always {
|
||||
bool await_ready() { return false; }
|
||||
void await_suspend(std::experimental::coroutine_handle<>) {}
|
||||
void await_resume() {}
|
||||
};
|
||||
|
||||
struct suspend_never {
|
||||
bool await_ready() { return true; }
|
||||
void await_suspend(std::experimental::coroutine_handle<>) {}
|
||||
void await_resume() {}
|
||||
};
|
||||
|
||||
struct promise_void {
|
||||
void get_return_object();
|
||||
suspend_always initial_suspend();
|
||||
suspend_always final_suspend();
|
||||
void return_void();
|
||||
void unhandled_exception();
|
||||
};
|
||||
|
||||
struct promise_float {
|
||||
|
@ -50,6 +23,7 @@ struct promise_float {
|
|||
suspend_always initial_suspend();
|
||||
suspend_always final_suspend();
|
||||
void return_void();
|
||||
void unhandled_exception();
|
||||
};
|
||||
|
||||
struct promise_int {
|
||||
|
@ -57,6 +31,7 @@ struct promise_int {
|
|||
suspend_always initial_suspend();
|
||||
suspend_always final_suspend();
|
||||
void return_value(int);
|
||||
void unhandled_exception();
|
||||
};
|
||||
|
||||
template <typename... T>
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
// RUN: %clang_cc1 -triple x86_64-apple-darwin9 %s -std=c++14 -fcoroutines-ts -fsyntax-only -Wignored-qualifiers -Wno-error=return-type -verify -fblocks -Wno-unreachable-code -Wno-unused-value
|
||||
|
||||
#if __has_feature(cxx_exceptions)
|
||||
#error This test requires exceptions be disabled
|
||||
#endif
|
||||
|
||||
#include "Inputs/std-coroutine.h"
|
||||
|
||||
using std::experimental::suspend_always;
|
||||
using std::experimental::suspend_never;
|
||||
|
||||
struct promise_void {
|
||||
void get_return_object();
|
||||
suspend_always initial_suspend();
|
||||
suspend_always final_suspend();
|
||||
void return_void();
|
||||
};
|
||||
|
||||
template <typename... T>
|
||||
struct std::experimental::coroutine_traits<void, T...> { using promise_type = promise_void; };
|
||||
|
||||
void test0() { // expected-warning {{'promise_void' is required to declare the member 'unhandled_exception()' when exceptions are enabled}}
|
||||
co_return;
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
// RUN: %clang_cc1 -std=c++14 -fcoroutines-ts -verify %s -fcxx-exceptions
|
||||
// RUN: %clang_cc1 -std=c++14 -fcoroutines-ts -verify %s -fcxx-exceptions -fexceptions
|
||||
|
||||
void no_coroutine_traits_bad_arg_await() {
|
||||
co_await a; // expected-error {{include <experimental/coroutine>}}
|
||||
|
@ -101,6 +101,7 @@ struct promise {
|
|||
awaitable yield_value(yielded_thing); // expected-note 2{{candidate}}
|
||||
not_awaitable yield_value(void()); // expected-note 2{{candidate}}
|
||||
void return_value(int); // expected-note 2{{here}}
|
||||
void unhandled_exception();
|
||||
};
|
||||
|
||||
struct promise_void {
|
||||
|
@ -108,6 +109,7 @@ struct promise_void {
|
|||
suspend_always initial_suspend();
|
||||
suspend_always final_suspend();
|
||||
void return_void();
|
||||
void unhandled_exception();
|
||||
};
|
||||
|
||||
void no_coroutine_handle() { // expected-error {{std::experimental::coroutine_handle type was not found; include <experimental/coroutine> before defining a coroutine}}
|
||||
|
@ -344,6 +346,7 @@ namespace dependent_operator_co_await_lookup {
|
|||
transformed initial_suspend();
|
||||
::adl_ns::coawait_arg_type final_suspend();
|
||||
transformed await_transform(transform_awaitable);
|
||||
void unhandled_exception();
|
||||
};
|
||||
template <class AwaitArg>
|
||||
struct basic_promise {
|
||||
|
@ -351,6 +354,7 @@ namespace dependent_operator_co_await_lookup {
|
|||
coro<basic_promise> get_return_object();
|
||||
awaitable initial_suspend();
|
||||
awaitable final_suspend();
|
||||
void unhandled_exception();
|
||||
};
|
||||
|
||||
awaitable operator co_await(await_arg_1);
|
||||
|
@ -435,6 +439,7 @@ struct std::experimental::coroutine_traits<void, yield_fn_tag> {
|
|||
suspend_never initial_suspend();
|
||||
suspend_never final_suspend();
|
||||
void get_return_object();
|
||||
void unhandled_exception();
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -467,6 +472,7 @@ namespace placeholder {
|
|||
struct bad_promise_1 {
|
||||
suspend_always initial_suspend();
|
||||
suspend_always final_suspend();
|
||||
void unhandled_exception();
|
||||
};
|
||||
coro<bad_promise_1> missing_get_return_object() { // expected-error {{no member named 'get_return_object' in 'bad_promise_1'}}
|
||||
co_await a;
|
||||
|
@ -476,6 +482,7 @@ struct bad_promise_2 {
|
|||
coro<bad_promise_2> get_return_object();
|
||||
// FIXME: We shouldn't offer a typo-correction here!
|
||||
suspend_always final_suspend(); // expected-note {{here}}
|
||||
void unhandled_exception();
|
||||
};
|
||||
// FIXME: This shouldn't happen twice
|
||||
coro<bad_promise_2> missing_initial_suspend() { // expected-error {{no member named 'initial_suspend' in 'bad_promise_2'}}
|
||||
|
@ -486,6 +493,7 @@ struct bad_promise_3 {
|
|||
coro<bad_promise_3> get_return_object();
|
||||
// FIXME: We shouldn't offer a typo-correction here!
|
||||
suspend_always initial_suspend(); // expected-note {{here}}
|
||||
void unhandled_exception();
|
||||
};
|
||||
coro<bad_promise_3> missing_final_suspend() { // expected-error {{no member named 'final_suspend' in 'bad_promise_3'}}
|
||||
co_await a;
|
||||
|
@ -517,6 +525,7 @@ struct bad_promise_6 {
|
|||
coro<bad_promise_6> get_return_object();
|
||||
suspend_always initial_suspend();
|
||||
suspend_always final_suspend();
|
||||
void unhandled_exception();
|
||||
void return_void();
|
||||
void return_value(int) const;
|
||||
void return_value(int);
|
||||
|
@ -530,17 +539,11 @@ struct bad_promise_7 {
|
|||
suspend_always initial_suspend();
|
||||
suspend_always final_suspend();
|
||||
void return_void();
|
||||
void set_exception(int *);
|
||||
};
|
||||
coro<bad_promise_7> no_std_current_exc() {
|
||||
// expected-error@-1 {{you need to include <exception> before defining a coroutine that implicitly uses 'set_exception'}}
|
||||
coro<bad_promise_7> no_unhandled_exception() { // expected-error {{'bad_promise_7' is required to declare the member 'unhandled_exception()'}}
|
||||
co_await a;
|
||||
}
|
||||
|
||||
namespace std {
|
||||
int *current_exception();
|
||||
}
|
||||
|
||||
struct bad_promise_base {
|
||||
private:
|
||||
void return_void();
|
||||
|
@ -549,14 +552,14 @@ struct bad_promise_8 : bad_promise_base {
|
|||
coro<bad_promise_8> get_return_object();
|
||||
suspend_always initial_suspend();
|
||||
suspend_always final_suspend();
|
||||
void set_exception(); // expected-note {{function not viable}}
|
||||
void set_exception(int *) __attribute__((unavailable)); // expected-note {{explicitly made unavailable}}
|
||||
void set_exception(void *); // expected-note {{candidate function}}
|
||||
void unhandled_exception() __attribute__((unavailable)); // expected-note {{made unavailable}}
|
||||
void unhandled_exception() const; // expected-note {{candidate}}
|
||||
void unhandled_exception(void *) const; // expected-note {{requires 1 argument, but 0 were provided}}
|
||||
};
|
||||
coro<bad_promise_8> calls_set_exception() {
|
||||
// expected-error@-1 {{call to unavailable member function 'set_exception'}}
|
||||
coro<bad_promise_8> calls_unhandled_exception() {
|
||||
// expected-error@-1 {{call to unavailable member function 'unhandled_exception'}}
|
||||
// FIXME: also warn about private 'return_void' here. Even though building
|
||||
// the call to set_exception has already failed.
|
||||
// the call to unhandled_exception has already failed.
|
||||
co_await a;
|
||||
}
|
||||
|
||||
|
@ -567,6 +570,7 @@ struct bad_promise_9 {
|
|||
void await_transform(void *); // expected-note {{candidate}}
|
||||
awaitable await_transform(int) __attribute__((unavailable)); // expected-note {{explicitly made unavailable}}
|
||||
void return_void();
|
||||
void unhandled_exception();
|
||||
};
|
||||
coro<bad_promise_9> calls_await_transform() {
|
||||
co_await 42; // expected-error {{call to unavailable member function 'await_transform'}}
|
||||
|
@ -579,6 +583,7 @@ struct bad_promise_10 {
|
|||
suspend_always final_suspend();
|
||||
int await_transform;
|
||||
void return_void();
|
||||
void unhandled_exception();
|
||||
};
|
||||
coro<bad_promise_10> bad_coawait() {
|
||||
// FIXME this diagnostic is terrible
|
||||
|
@ -595,6 +600,7 @@ struct good_promise_1 {
|
|||
coro<good_promise_1> get_return_object();
|
||||
suspend_always initial_suspend();
|
||||
suspend_always final_suspend();
|
||||
void unhandled_exception();
|
||||
static const call_operator await_transform;
|
||||
using Fn = void (*)();
|
||||
Fn return_void = ret_void;
|
||||
|
@ -617,6 +623,7 @@ struct good_promise_2 {
|
|||
suspend_always initial_suspend();
|
||||
suspend_always final_suspend();
|
||||
void return_void();
|
||||
void unhandled_exception();
|
||||
};
|
||||
template<> struct std::experimental::coroutine_handle<good_promise_2> {};
|
||||
|
||||
|
|
Loading…
Reference in New Issue