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

Reply via email to