EricWF created this revision. Herald added a subscriber: mehdi_amini. 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. https://reviews.llvm.org/D30859 Files: include/clang/Basic/DiagnosticGroups.td include/clang/Basic/DiagnosticSemaKinds.td lib/Sema/SemaCoroutine.cpp test/SemaCXX/Inputs/std-coroutine.h test/SemaCXX/coreturn.cpp test/SemaCXX/coroutine-unhandled_exception-warning.cpp test/SemaCXX/coroutines.cpp
Index: test/SemaCXX/coroutines.cpp =================================================================== --- test/SemaCXX/coroutines.cpp +++ test/SemaCXX/coroutines.cpp @@ -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,13 +101,15 @@ 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 { void get_return_object(); 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,13 +346,15 @@ transformed initial_suspend(); ::adl_ns::coawait_arg_type final_suspend(); transformed await_transform(transform_awaitable); + void unhandled_exception(); }; template <class AwaitArg> struct basic_promise { typedef AwaitArg await_arg; 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 @@ suspend_never initial_suspend(); suspend_never final_suspend(); void get_return_object(); + void unhandled_exception(); }; }; @@ -467,6 +472,7 @@ 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 @@ 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 @@ 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 @@ 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,33 +539,27 @@ 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(); }; 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 @@ 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 @@ 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 @@ 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 @@ suspend_always initial_suspend(); suspend_always final_suspend(); void return_void(); + void unhandled_exception(); }; template<> struct std::experimental::coroutine_handle<good_promise_2> {}; Index: test/SemaCXX/coroutine-unhandled_exception-warning.cpp =================================================================== --- /dev/null +++ test/SemaCXX/coroutine-unhandled_exception-warning.cpp @@ -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; +} Index: test/SemaCXX/coreturn.cpp =================================================================== --- test/SemaCXX/coreturn.cpp +++ test/SemaCXX/coreturn.cpp @@ -1,62 +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 +#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(); void await_suspend(std::experimental::coroutine_handle<>); // FIXME: coroutine_handle 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 { float get_return_object(); suspend_always initial_suspend(); suspend_always final_suspend(); void return_void(); + void unhandled_exception(); }; struct promise_int { int get_return_object(); suspend_always initial_suspend(); suspend_always final_suspend(); void return_value(int); + void unhandled_exception(); }; template <typename... T> Index: test/SemaCXX/Inputs/std-coroutine.h =================================================================== --- /dev/null +++ test/SemaCXX/Inputs/std-coroutine.h @@ -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 Index: lib/Sema/SemaCoroutine.cpp =================================================================== --- lib/Sema/SemaCoroutine.cpp +++ lib/Sema/SemaCoroutine.cpp @@ -658,34 +658,6 @@ 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::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; - - // [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; - } + ExprResult UnhandledException = buildPromiseCall(S, Fn.CoroutinePromise, Loc, + "unhandled_exception", None); + UnhandledException = S.ActOnFinishFullExpr(UnhandledException.get(), Loc); + if (UnhandledException.isInvalid()) + return false; - this->OnException = SetException.get(); + this->OnException = UnhandledException.get(); return true; } Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -8858,6 +8858,11 @@ 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 { Index: include/clang/Basic/DiagnosticGroups.td =================================================================== --- include/clang/Basic/DiagnosticGroups.td +++ include/clang/Basic/DiagnosticGroups.td @@ -35,6 +35,7 @@ def GNUCompoundLiteralInitializer : DiagGroup<"gnu-compound-literal-initializer">; def BitFieldConstantConversion : DiagGroup<"bitfield-constant-conversion">; def BitFieldWidth : DiagGroup<"bitfield-width">; +def Coroutine : DiagGroup<"coroutine">; def ConstantConversion : DiagGroup<"constant-conversion", [ BitFieldConstantConversion ] >; def LiteralConversion : DiagGroup<"literal-conversion">;
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits