lxfind updated this revision to Diff 271801.
lxfind added a comment.
Herald added a subscriber: arphaman.

Address feedback and update failed tests


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D82029/new/

https://reviews.llvm.org/D82029

Files:
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/include/clang/Sema/Sema.h
  clang/lib/Sema/SemaCoroutine.cpp
  clang/lib/Sema/SemaExceptionSpec.cpp
  clang/test/AST/Inputs/std-coroutine.h
  clang/test/AST/coroutine-source-location-crash.cpp
  clang/test/Analysis/more-dtors-cfg-output.cpp
  clang/test/CodeGenCXX/ubsan-coroutines.cpp
  clang/test/CodeGenCoroutines/Inputs/coroutine.h
  clang/test/CodeGenCoroutines/coro-alloc.cpp
  clang/test/CodeGenCoroutines/coro-always-inline.cpp
  clang/test/CodeGenCoroutines/coro-await-domination.cpp
  clang/test/CodeGenCoroutines/coro-await-resume-eh.cpp
  clang/test/CodeGenCoroutines/coro-await.cpp
  clang/test/CodeGenCoroutines/coro-dest-slot.cpp
  clang/test/CodeGenCoroutines/coro-gro-nrvo.cpp
  clang/test/CodeGenCoroutines/coro-newpm-pipeline.cpp
  clang/test/CodeGenCoroutines/coro-params.cpp
  clang/test/CodeGenCoroutines/coro-promise-dtor.cpp
  clang/test/CodeGenCoroutines/coro-ret-void.cpp
  clang/test/CodeGenCoroutines/coro-return-voidtype-initlist.cpp
  clang/test/CodeGenCoroutines/coro-return.cpp
  clang/test/CodeGenCoroutines/coro-unhandled-exception.cpp
  clang/test/Index/coroutines.cpp
  clang/test/SemaCXX/Inputs/std-coroutine.h
  clang/test/SemaCXX/co_await-range-for.cpp
  clang/test/SemaCXX/coreturn-eh.cpp
  clang/test/SemaCXX/coreturn.cpp
  clang/test/SemaCXX/coroutine-final-suspend-noexcept.cpp
  clang/test/SemaCXX/coroutine-rvo.cpp
  clang/test/SemaCXX/coroutine-unhandled_exception-warning.cpp
  clang/test/SemaCXX/coroutine-uninitialized-warning-crash.cpp
  clang/test/SemaCXX/coroutines.cpp

Index: clang/test/SemaCXX/coroutines.cpp
===================================================================
--- clang/test/SemaCXX/coroutines.cpp
+++ clang/test/SemaCXX/coroutines.cpp
@@ -52,21 +52,24 @@
 };
 
 struct awaitable {
-  bool await_ready();
-  template <typename F> void await_suspend(F);
-  void await_resume();
+  bool await_ready() noexcept;
+  template <typename F>
+  void await_suspend(F) noexcept;
+  void await_resume() noexcept;
 } a;
 
 struct suspend_always {
-  bool await_ready() { return false; }
-  template <typename F> void await_suspend(F);
-  void await_resume() {}
+  bool await_ready() noexcept { return false; }
+  template <typename F>
+  void await_suspend(F) noexcept;
+  void await_resume() noexcept {}
 };
 
 struct suspend_never {
-  bool await_ready() { return true; }
-  template <typename F> void await_suspend(F);
-  void await_resume() {}
+  bool await_ready() noexcept { return true; }
+  template <typename F>
+  void await_suspend(F) noexcept;
+  void await_resume() noexcept {}
 };
 
 struct auto_await_suspend {
@@ -127,7 +130,7 @@
 struct promise {
   void get_return_object();
   suspend_always initial_suspend();
-  suspend_always final_suspend();
+  suspend_always final_suspend() noexcept;
   awaitable yield_value(int); // expected-note 2{{candidate}}
   awaitable yield_value(yielded_thing); // expected-note 2{{candidate}}
   not_awaitable yield_value(void()); // expected-note 2{{candidate}}
@@ -138,7 +141,7 @@
 struct promise_void {
   void get_return_object();
   suspend_always initial_suspend();
-  suspend_always final_suspend();
+  suspend_always final_suspend() noexcept;
   void return_void();
   void unhandled_exception();
 };
@@ -152,13 +155,13 @@
 namespace experimental {
 template <class PromiseType = void>
 struct coroutine_handle {
-  static coroutine_handle from_address(void *);
+  static coroutine_handle from_address(void *) noexcept;
 };
 template <>
 struct coroutine_handle<void> {
   template <class PromiseType>
-  coroutine_handle(coroutine_handle<PromiseType>);
-  static coroutine_handle from_address(void *);
+  coroutine_handle(coroutine_handle<PromiseType>) noexcept;
+  static coroutine_handle from_address(void *) noexcept;
 };
 }} // namespace std::experimental
 
@@ -402,7 +405,7 @@
 
 namespace adl_ns {
 struct coawait_arg_type {};
-awaitable operator co_await(coawait_arg_type);
+awaitable operator co_await(coawait_arg_type) noexcept;
 }
 
 namespace dependent_operator_co_await_lookup {
@@ -434,7 +437,7 @@
     typedef transform_awaitable await_arg;
     coro<transform_promise> get_return_object();
     transformed initial_suspend();
-    ::adl_ns::coawait_arg_type final_suspend();
+    ::adl_ns::coawait_arg_type final_suspend() noexcept;
     transformed await_transform(transform_awaitable);
     void unhandled_exception();
     void return_void();
@@ -444,7 +447,7 @@
     typedef AwaitArg await_arg;
     coro<basic_promise> get_return_object();
     awaitable initial_suspend();
-    awaitable final_suspend();
+    awaitable final_suspend() noexcept;
     void unhandled_exception();
     void return_void();
   };
@@ -529,7 +532,7 @@
     void return_value(int());
 
     suspend_never initial_suspend();
-    suspend_never final_suspend();
+    suspend_never final_suspend() noexcept;
     void get_return_object();
     void unhandled_exception();
   };
@@ -563,7 +566,7 @@
 
 struct bad_promise_1 {
   suspend_always initial_suspend();
-  suspend_always final_suspend();
+  suspend_always final_suspend() noexcept;
   void unhandled_exception();
   void return_void();
 };
@@ -573,7 +576,7 @@
 
 struct bad_promise_2 {
   coro<bad_promise_2> get_return_object();
-  suspend_always final_suspend();
+  suspend_always final_suspend() noexcept;
   void unhandled_exception();
   void return_void();
 };
@@ -588,14 +591,14 @@
   void unhandled_exception();
   void return_void();
 };
-coro<bad_promise_3> missing_final_suspend() { // expected-error {{no member named 'final_suspend' in 'bad_promise_3'}}
+coro<bad_promise_3> missing_final_suspend() noexcept { // expected-error {{no member named 'final_suspend' in 'bad_promise_3'}}
   co_await a;
 }
 
 struct bad_promise_4 {
   coro<bad_promise_4> get_return_object();
   not_awaitable initial_suspend();
-  suspend_always final_suspend();
+  suspend_always final_suspend() noexcept;
   void return_void();
 };
 // FIXME: This diagnostic is terrible.
@@ -607,7 +610,7 @@
 struct bad_promise_5 {
   coro<bad_promise_5> get_return_object();
   suspend_always initial_suspend();
-  not_awaitable final_suspend();
+  not_awaitable final_suspend() noexcept;
   void return_void();
 };
 // FIXME: This diagnostic is terrible.
@@ -619,7 +622,7 @@
 struct bad_promise_6 {
   coro<bad_promise_6> get_return_object();
   suspend_always initial_suspend();
-  suspend_always final_suspend();
+  suspend_always final_suspend() noexcept;
   void unhandled_exception();
   void return_void();           // expected-note 2 {{member 'return_void' first declared here}}
   void return_value(int) const; // expected-note 2 {{member 'return_value' first declared here}}
@@ -638,7 +641,7 @@
 struct bad_promise_7 { // expected-note 2 {{defined here}}
   coro<bad_promise_7> get_return_object();
   suspend_always initial_suspend();
-  suspend_always final_suspend();
+  suspend_always final_suspend() noexcept;
   void return_void();
 };
 coro<bad_promise_7> no_unhandled_exception() { // expected-error {{'bad_promise_7' is required to declare the member 'unhandled_exception()'}}
@@ -658,7 +661,7 @@
 struct bad_promise_8 : bad_promise_base {
   coro<bad_promise_8> get_return_object();
   suspend_always initial_suspend();
-  suspend_always final_suspend();
+  suspend_always final_suspend() noexcept;
   void unhandled_exception() __attribute__((unavailable)); // expected-note 2 {{marked unavailable here}}
   void unhandled_exception() const;
   void unhandled_exception(void *) const;
@@ -680,7 +683,7 @@
 struct bad_promise_9 {
   coro<bad_promise_9> get_return_object();
   suspend_always initial_suspend();
-  suspend_always final_suspend();
+  suspend_always final_suspend() noexcept;
   void await_transform(void *);
   awaitable await_transform(int) __attribute__((unavailable)); // expected-note {{explicitly marked unavailable}}
   void return_void();
@@ -693,7 +696,7 @@
 struct bad_promise_10 {
   coro<bad_promise_10> get_return_object();
   suspend_always initial_suspend();
-  suspend_always final_suspend();
+  suspend_always final_suspend() noexcept;
   int await_transform;
   void return_void();
   void unhandled_exception();
@@ -712,7 +715,7 @@
 struct good_promise_1 {
   coro<good_promise_1> get_return_object();
   suspend_always initial_suspend();
-  suspend_always final_suspend();
+  suspend_always final_suspend() noexcept;
   void unhandled_exception();
   static const call_operator await_transform;
   using Fn = void (*)();
@@ -750,7 +753,7 @@
 struct good_promise_2 {
   float get_return_object();
   suspend_always initial_suspend();
-  suspend_always final_suspend();
+  suspend_always final_suspend() noexcept;
   void return_void();
   void unhandled_exception();
 };
@@ -783,7 +786,7 @@
   struct promise_type {
     int get_return_object() {}
     suspend_always initial_suspend() { return {}; }
-    suspend_always final_suspend() { return {}; }
+    suspend_always final_suspend() noexcept { return {}; }
     void return_void() {}
     int get_return_object_on_allocation_failure(); // expected-error{{'promise_type': 'get_return_object_on_allocation_failure()' must be a static member function}}
     void unhandled_exception();
@@ -797,7 +800,7 @@
 struct bad_promise_11 {
   coro<bad_promise_11> get_return_object();
   suspend_always initial_suspend();
-  suspend_always final_suspend();
+  suspend_always final_suspend() noexcept;
   void unhandled_exception();
   void return_void();
 
@@ -820,7 +823,7 @@
 struct bad_promise_12 {
   coro<bad_promise_12> get_return_object();
   suspend_always initial_suspend();
-  suspend_always final_suspend();
+  suspend_always final_suspend() noexcept;
   void unhandled_exception();
   void return_void();
   static coro<bad_promise_12> get_return_object_on_allocation_failure();
@@ -842,7 +845,7 @@
 struct good_promise_13 {
   coro<good_promise_13> get_return_object();
   suspend_always initial_suspend();
-  suspend_always final_suspend();
+  suspend_always final_suspend() noexcept;
   void unhandled_exception();
   void return_void();
   static coro<good_promise_13> get_return_object_on_allocation_failure();
@@ -860,7 +863,7 @@
 struct good_promise_custom_new_operator {
   coro<good_promise_custom_new_operator> get_return_object();
   suspend_always initial_suspend();
-  suspend_always final_suspend();
+  suspend_always final_suspend() noexcept;
   void return_void();
   void unhandled_exception();
   void *operator new(SizeT, double, float, int);
@@ -876,7 +879,7 @@
 struct good_promise_nonstatic_member_custom_new_operator {
   coro<good_promise_nonstatic_member_custom_new_operator> get_return_object();
   suspend_always initial_suspend();
-  suspend_always final_suspend();
+  suspend_always final_suspend() noexcept;
   void return_void();
   void unhandled_exception();
   void *operator new(SizeT, coroutine_nonstatic_member_struct &, double);
@@ -886,7 +889,7 @@
   static coro<good_promise_noexcept_custom_new_operator> get_return_object_on_allocation_failure();
   coro<good_promise_noexcept_custom_new_operator> get_return_object();
   suspend_always initial_suspend();
-  suspend_always final_suspend();
+  suspend_always final_suspend() noexcept;
   void return_void();
   void unhandled_exception();
   void *operator new(SizeT, double, float, int) noexcept;
@@ -903,7 +906,7 @@
   struct promise_type {
     void get_return_object() {} //expected-note {{member 'get_return_object' declared here}}
     suspend_always initial_suspend() { return {}; }
-    suspend_always final_suspend() { return {}; }
+    suspend_always final_suspend() noexcept { return {}; }
     void return_void() {}
     void unhandled_exception();
   };
@@ -920,7 +923,7 @@
   struct promise_type {
     void *get_return_object() {} //expected-note {{member 'get_return_object' declared here}}
     suspend_always initial_suspend() { return {}; }
-    suspend_always final_suspend() { return {}; }
+    suspend_always final_suspend() noexcept { return {}; }
     void return_void() {}
     void unhandled_exception();
   };
@@ -938,7 +941,7 @@
     int get_return_object() {}
     static void get_return_object_on_allocation_failure() {} //expected-note {{member 'get_return_object_on_allocation_failure' declared here}}
     suspend_always initial_suspend() { return {}; }
-    suspend_always final_suspend() { return {}; }
+    suspend_always final_suspend() noexcept { return {}; }
     void return_void() {}
     void unhandled_exception();
   };
@@ -957,7 +960,7 @@
     int get_return_object() {}
     static char *get_return_object_on_allocation_failure() {} //expected-note {{member 'get_return_object_on_allocation_failure' declared}}
     suspend_always initial_suspend() { return {}; }
-    suspend_always final_suspend() { return {}; }
+    suspend_always final_suspend() noexcept { return {}; }
     void return_void() {}
     void unhandled_exception();
   };
@@ -971,7 +974,7 @@
 struct bad_promise_no_return_func { // expected-note {{'bad_promise_no_return_func' defined here}}
   coro<bad_promise_no_return_func> get_return_object();
   suspend_always initial_suspend();
-  suspend_always final_suspend();
+  suspend_always final_suspend() noexcept;
   void unhandled_exception();
 };
 // FIXME: The PDTS currently specifies this as UB, technically forbidding a
@@ -1083,7 +1086,7 @@
 
   CoroMemberTag get_return_object();
   suspend_always initial_suspend();
-  suspend_always final_suspend();
+  suspend_always final_suspend() noexcept;
 
   AwaitTestT yield_value(int);
 
@@ -1292,7 +1295,7 @@
   bad_promise_deleted_constructor() = delete;
   coro<bad_promise_deleted_constructor> get_return_object();
   suspend_always initial_suspend();
-  suspend_always final_suspend();
+  suspend_always final_suspend() noexcept;
   void return_void();
   void unhandled_exception();
 };
@@ -1314,7 +1317,7 @@
   good_promise_default_constructor() = default;
   coro<good_promise_default_constructor> get_return_object();
   suspend_always initial_suspend();
-  suspend_always final_suspend();
+  suspend_always final_suspend() noexcept;
   void return_void();
   void unhandled_exception();
 };
@@ -1332,7 +1335,7 @@
   good_promise_custom_constructor() = delete;
   coro<good_promise_custom_constructor> get_return_object();
   suspend_always initial_suspend();
-  suspend_always final_suspend();
+  suspend_always final_suspend() noexcept;
   void return_void();
   void unhandled_exception();
 };
@@ -1359,7 +1362,7 @@
   bad_promise_no_matching_constructor() = delete;
   coro<bad_promise_no_matching_constructor> get_return_object();
   suspend_always initial_suspend();
-  suspend_always final_suspend();
+  suspend_always final_suspend() noexcept;
   void return_void();
   void unhandled_exception();
 };
@@ -1383,26 +1386,25 @@
 class awaitable_no_unused_warn {
 public:
   using handle_type = std::experimental::coroutine_handle<>;
-  constexpr bool await_ready()  { return false; }
+  constexpr bool await_ready() noexcept { return false; }
   void await_suspend(handle_type) noexcept {}
-  int await_resume() { return 1; }
+  int await_resume() noexcept { return 1; }
 };
 
 
 class awaitable_unused_warn {
 public:
   using handle_type = std::experimental::coroutine_handle<>;
-  constexpr bool await_ready()  { return false; }
+  constexpr bool await_ready() noexcept { return false; }
   void await_suspend(handle_type) noexcept {}
-  [[nodiscard]]
-  int await_resume() { return 1; }
+  [[nodiscard]] int await_resume() noexcept { return 1; }
 };
 
 template <class Await>
 struct check_warning_promise {
   coro<check_warning_promise> get_return_object();
   Await initial_suspend();
-  Await final_suspend();
+  Await final_suspend() noexcept;
   Await yield_value(int);
   void return_void();
   void unhandled_exception();
Index: clang/test/SemaCXX/coroutine-uninitialized-warning-crash.cpp
===================================================================
--- clang/test/SemaCXX/coroutine-uninitialized-warning-crash.cpp
+++ clang/test/SemaCXX/coroutine-uninitialized-warning-crash.cpp
@@ -16,7 +16,7 @@
   struct promise_type {
     coro_t get_return_object() { return {}; }
     suspend_never initial_suspend() { return {}; }
-    suspend_never final_suspend() { return {}; }
+    suspend_never final_suspend() noexcept { return {}; }
     A yield_value(int) { return {}; }
     void return_void() {}
     static void unhandled_exception() {}
Index: clang/test/SemaCXX/coroutine-unhandled_exception-warning.cpp
===================================================================
--- clang/test/SemaCXX/coroutine-unhandled_exception-warning.cpp
+++ clang/test/SemaCXX/coroutine-unhandled_exception-warning.cpp
@@ -23,7 +23,7 @@
 #endif
   void get_return_object();
   suspend_always initial_suspend();
-  suspend_always final_suspend();
+  suspend_always final_suspend() noexcept;
   void return_void();
 };
 
Index: clang/test/SemaCXX/coroutine-rvo.cpp
===================================================================
--- clang/test/SemaCXX/coroutine-rvo.cpp
+++ clang/test/SemaCXX/coroutine-rvo.cpp
@@ -49,7 +49,7 @@
 struct task {
   struct promise_type {
     auto initial_suspend() { return suspend_never{}; }
-    auto final_suspend() { return suspend_never{}; }
+    auto final_suspend() noexcept { return suspend_never{}; }
     auto get_return_object() { return task{}; }
     static void unhandled_exception() {}
     void return_value(T&& value) {}
Index: clang/test/SemaCXX/coroutine-final-suspend-noexcept.cpp
===================================================================
--- /dev/null
+++ clang/test/SemaCXX/coroutine-final-suspend-noexcept.cpp
@@ -0,0 +1,62 @@
+// This file contains references to sections of the Coroutines TS, which can be
+// found at http://wg21.link/coroutines.
+
+// RUN: %clang_cc1 -std=c++14 -fcoroutines-ts -verify %s -fcxx-exceptions -fexceptions -Wunused-result
+
+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 *); // expected-note {{must be declared with 'noexcept'}}
+};
+template <>
+struct coroutine_handle<void> {
+  template <class PromiseType>
+  coroutine_handle(coroutine_handle<PromiseType>); // expected-note {{must be declared with 'noexcept'}}
+};
+
+struct suspend_never {
+  bool await_ready() { return true; }       // expected-note {{must be declared with 'noexcept'}}
+  void await_suspend(coroutine_handle<>) {} // expected-note {{must be declared with 'noexcept'}}
+  void await_resume() {}                    // expected-note {{must be declared with 'noexcept'}}
+  ~suspend_never() noexcept(false);         // expected-note {{must be declared with 'noexcept'}}
+};
+
+struct suspend_always {
+  bool await_ready() { return false; }
+  void await_suspend(coroutine_handle<>) {}
+  void await_resume() {}
+  suspend_never operator co_await(); // expected-note {{must be declared with 'noexcept'}}
+  ~suspend_always() noexcept(false); // expected-note {{must be declared with 'noexcept'}}
+};
+
+} // namespace experimental
+} // namespace std
+
+using namespace std::experimental;
+
+struct A {
+  bool await_ready();
+  void await_resume();
+  template <typename F>
+  void await_suspend(F);
+};
+
+struct coro_t {
+  struct promise_type {
+    coro_t get_return_object();
+    suspend_never initial_suspend();
+    suspend_always final_suspend(); // expected-note {{must be declared with 'noexcept'}}
+    void return_void();
+    static void unhandled_exception();
+  };
+};
+
+coro_t f(int n) { // expected-error {{all code generated by co_await __promise.final_suspend() must not throw}}
+  A a{};
+  co_await a;
+}
Index: clang/test/SemaCXX/coreturn.cpp
===================================================================
--- clang/test/SemaCXX/coreturn.cpp
+++ clang/test/SemaCXX/coreturn.cpp
@@ -13,7 +13,7 @@
 struct promise_void {
   void get_return_object();
   suspend_always initial_suspend();
-  suspend_always final_suspend();
+  suspend_always final_suspend() noexcept;
   void return_void();
   void unhandled_exception();
 };
@@ -21,7 +21,7 @@
 struct promise_void_return_value {
   void get_return_object();
   suspend_always initial_suspend();
-  suspend_always final_suspend();
+  suspend_always final_suspend() noexcept;
   void unhandled_exception();
   void return_value(int);
 };
@@ -30,7 +30,7 @@
   struct promise_type {
     VoidTagNoReturn get_return_object();
     suspend_always initial_suspend();
-    suspend_always final_suspend();
+    suspend_always final_suspend() noexcept;
     void unhandled_exception();
   };
 };
@@ -39,7 +39,7 @@
   struct promise_type {
     VoidTagReturnValue get_return_object();
     suspend_always initial_suspend();
-    suspend_always final_suspend();
+    suspend_always final_suspend() noexcept;
     void unhandled_exception();
     void return_value(int);
   };
@@ -49,7 +49,7 @@
   struct promise_type {
     VoidTagReturnVoid get_return_object();
     suspend_always initial_suspend();
-    suspend_always final_suspend();
+    suspend_always final_suspend() noexcept;
     void unhandled_exception();
     void return_void();
   };
@@ -58,7 +58,7 @@
 struct promise_float {
   float get_return_object();
   suspend_always initial_suspend();
-  suspend_always final_suspend();
+  suspend_always final_suspend() noexcept;
   void return_void();
   void unhandled_exception();
 };
@@ -66,7 +66,7 @@
 struct promise_int {
   int get_return_object();
   suspend_always initial_suspend();
-  suspend_always final_suspend();
+  suspend_always final_suspend() noexcept;
   void return_value(int);
   void unhandled_exception();
 };
Index: clang/test/SemaCXX/coreturn-eh.cpp
===================================================================
--- clang/test/SemaCXX/coreturn-eh.cpp
+++ clang/test/SemaCXX/coreturn-eh.cpp
@@ -17,7 +17,7 @@
 struct promise_void_return_value {
   void get_return_object();
   suspend_always initial_suspend();
-  suspend_always final_suspend();
+  suspend_always final_suspend() noexcept;
   void unhandled_exception();
   void return_value(object);
 };
@@ -26,7 +26,7 @@
   struct promise_type {
     VoidTagReturnValue get_return_object();
     suspend_always initial_suspend();
-    suspend_always final_suspend();
+    suspend_always final_suspend() noexcept;
     void unhandled_exception();
     void return_value(object);
   };
Index: clang/test/SemaCXX/co_await-range-for.cpp
===================================================================
--- clang/test/SemaCXX/co_await-range-for.cpp
+++ clang/test/SemaCXX/co_await-range-for.cpp
@@ -44,7 +44,7 @@
     void return_void();
     void unhandled_exception();
     suspend_never initial_suspend();
-    suspend_never final_suspend();
+    suspend_never final_suspend() noexcept;
     template <class T>
     Awaiter<T *> await_transform(T *) = delete; // expected-note {{explicitly deleted}}
   };
@@ -62,7 +62,7 @@
     void return_void();
     void unhandled_exception();
     suspend_never initial_suspend();
-    suspend_never final_suspend();
+    suspend_never final_suspend() noexcept;
 
     template <class T>
     Awaiter<T> await_transform(BeginTag<T>) = delete; // expected-note 1+ {{explicitly deleted}}
@@ -96,7 +96,7 @@
     void return_void();
     void unhandled_exception();
     suspend_never initial_suspend();
-    suspend_never final_suspend();
+    suspend_never final_suspend() noexcept;
 
     template <class T>
     Awaiter<T> await_transform(BeginTag<T> e);
@@ -137,7 +137,7 @@
     void return_void();
     void unhandled_exception();
     suspend_never initial_suspend();
-    suspend_never final_suspend();
+    suspend_never final_suspend() noexcept;
     template <class T>
     CoawaitTag<T, false> await_transform(BeginTag<T> e);
     template <class T>
Index: clang/test/SemaCXX/Inputs/std-coroutine.h
===================================================================
--- clang/test/SemaCXX/Inputs/std-coroutine.h
+++ clang/test/SemaCXX/Inputs/std-coroutine.h
@@ -10,25 +10,25 @@
 
 template <class Promise = void>
 struct coroutine_handle {
-  static coroutine_handle from_address(void *);
+  static coroutine_handle from_address(void *) noexcept;
 };
 template <>
 struct coroutine_handle<void> {
   template <class PromiseType>
-  coroutine_handle(coroutine_handle<PromiseType>);
+  coroutine_handle(coroutine_handle<PromiseType>) noexcept;
   static coroutine_handle from_address(void *);
 };
 
 struct suspend_always {
-  bool await_ready() { return false; }
-  void await_suspend(coroutine_handle<>) {}
-  void await_resume() {}
+  bool await_ready() noexcept { return false; }
+  void await_suspend(coroutine_handle<>) noexcept {}
+  void await_resume() noexcept {}
 };
 
 struct suspend_never {
-  bool await_ready() { return true; }
-  void await_suspend(coroutine_handle<>) {}
-  void await_resume() {}
+  bool await_ready() noexcept { return true; }
+  void await_suspend(coroutine_handle<>) noexcept {}
+  void await_resume() noexcept {}
 };
 
 } // namespace experimental
Index: clang/test/Index/coroutines.cpp
===================================================================
--- clang/test/Index/coroutines.cpp
+++ clang/test/Index/coroutines.cpp
@@ -7,7 +7,7 @@
 struct promise_void {
   void get_return_object();
   suspend_always initial_suspend();
-  suspend_always final_suspend();
+  suspend_always final_suspend() noexcept;
   void return_void();
   void unhandled_exception();
 };
Index: clang/test/CodeGenCoroutines/coro-unhandled-exception.cpp
===================================================================
--- clang/test/CodeGenCoroutines/coro-unhandled-exception.cpp
+++ clang/test/CodeGenCoroutines/coro-unhandled-exception.cpp
@@ -17,7 +17,7 @@
       return {};
     }
     coro::suspend_never initial_suspend() { return {}; }
-    coro::suspend_never final_suspend() { return {}; }
+    coro::suspend_never final_suspend() noexcept { return {}; }
     void return_void(){}
     void unhandled_exception() noexcept;
   };
@@ -48,11 +48,9 @@
 // CHECK: [[CATCHRETDEST]]:
 // CHECK-NEXT: br label %[[TRYCONT:.+]]
 // CHECK: [[TRYCONT]]:
-// CHECK-NEXT: br label %[[RESUMECONT:.+]]
-// CHECK: [[RESUMECONT]]:
 // CHECK-NEXT: br label %[[COROFIN:.+]]
 // CHECK: [[COROFIN]]:
-// CHECK-NEXT: invoke void @"?final_suspend@promise_type@coro_t@@QEAA?AUsuspend_never@coroutines_v1@experimental@std@@XZ"(
+// CHECK-NEXT: call void @"?final_suspend@promise_type@coro_t@@QEAA?AUsuspend_never@coroutines_v1@experimental@std@@XZ"(
 
 // CHECK-LPAD: @_Z1fv(
 // CHECK-LPAD:   invoke void @_Z9may_throwv()
@@ -69,8 +67,6 @@
 // CHECK-LPAD: [[CATCHRETDEST]]:
 // CHECK-LPAD-NEXT: br label %[[TRYCONT:.+]]
 // CHECK-LPAD: [[TRYCONT]]:
-// CHECK-LPAD: br label %[[RESUMECONT:.+]]
-// CHECK-LPAD: [[RESUMECONT]]:
-// CHECK-LPAD-NEXT: br label %[[COROFIN:.+]]
+// CHECK-LPAD: br label %[[COROFIN:.+]]
 // CHECK-LPAD: [[COROFIN]]:
-// CHECK-LPAD-NEXT: invoke void @_ZN6coro_t12promise_type13final_suspendEv(
+// CHECK-LPAD-NEXT: call void @_ZN6coro_t12promise_type13final_suspendEv(
Index: clang/test/CodeGenCoroutines/coro-return.cpp
===================================================================
--- clang/test/CodeGenCoroutines/coro-return.cpp
+++ clang/test/CodeGenCoroutines/coro-return.cpp
@@ -5,27 +5,27 @@
 
 template <class Promise = void> struct coroutine_handle {
   coroutine_handle() = default;
-  static coroutine_handle from_address(void *) { return {}; }
+  static coroutine_handle from_address(void *) noexcept { return {}; }
 };
 template <> struct coroutine_handle<void> {
   static coroutine_handle from_address(void *) { return {}; }
   coroutine_handle() = default;
   template <class PromiseType>
-  coroutine_handle(coroutine_handle<PromiseType>) {}
+  coroutine_handle(coroutine_handle<PromiseType>) noexcept {}
 };
 }
 
 struct suspend_always {
-  bool await_ready();
-  void await_suspend(std::experimental::coroutine_handle<>);
-  void await_resume();
+  bool await_ready() noexcept;
+  void await_suspend(std::experimental::coroutine_handle<>) noexcept;
+  void await_resume() noexcept;
 };
 
 template <> struct std::experimental::coroutine_traits<void> {
   struct promise_type {
     void get_return_object();
     suspend_always initial_suspend();
-    suspend_always final_suspend();
+    suspend_always final_suspend() noexcept;
     void return_void();
   };
 };
@@ -44,7 +44,7 @@
   struct promise_type {
     int get_return_object();
     suspend_always initial_suspend();
-    suspend_always final_suspend();
+    suspend_always final_suspend() noexcept;
     void return_value(int);
   };
 };
Index: clang/test/CodeGenCoroutines/coro-return-voidtype-initlist.cpp
===================================================================
--- clang/test/CodeGenCoroutines/coro-return-voidtype-initlist.cpp
+++ clang/test/CodeGenCoroutines/coro-return-voidtype-initlist.cpp
@@ -14,7 +14,7 @@
 struct coroutine_handle<> {};
 template <typename>
 struct coroutine_handle : coroutine_handle<> {
-  static coroutine_handle from_address(void *);
+  static coroutine_handle from_address(void *) noexcept;
 };
 struct e {
   int await_ready();
@@ -29,13 +29,13 @@
 struct f;
 struct g {
   struct h {
-    int await_ready();
+    int await_ready() noexcept;
     template <typename al>
-    void await_suspend(std::experimental::coroutine_handle<al>);
-    void await_resume();
+    void await_suspend(std::experimental::coroutine_handle<al>) noexcept;
+    void await_resume() noexcept;
   };
   std::experimental::e initial_suspend();
-  h final_suspend();
+  h final_suspend() noexcept;
   template <typename ag>
   auto await_transform(ag) { return ah(ag()); }
 };
Index: clang/test/CodeGenCoroutines/coro-ret-void.cpp
===================================================================
--- clang/test/CodeGenCoroutines/coro-ret-void.cpp
+++ clang/test/CodeGenCoroutines/coro-ret-void.cpp
@@ -8,7 +8,7 @@
   struct promise_type {
     coro1 get_return_object();
     coro::suspend_never initial_suspend();
-    coro::suspend_never final_suspend();
+    coro::suspend_never final_suspend() noexcept;
     void return_void();
   };
 };
@@ -39,7 +39,7 @@
   struct promise_type {
     coro2 get_return_object();
     coro::suspend_never initial_suspend();
-    coro::suspend_never final_suspend();
+    coro::suspend_never final_suspend() noexcept;
     void return_value(int);
   };
 };
Index: clang/test/CodeGenCoroutines/coro-promise-dtor.cpp
===================================================================
--- clang/test/CodeGenCoroutines/coro-promise-dtor.cpp
+++ clang/test/CodeGenCoroutines/coro-promise-dtor.cpp
@@ -11,7 +11,7 @@
   struct promise_type {
     coro_t get_return_object();
     coro::suspend_never initial_suspend();
-    coro::suspend_never final_suspend();
+    coro::suspend_never final_suspend() noexcept;
     void return_void();
     promise_type();
     ~promise_type();
Index: clang/test/CodeGenCoroutines/coro-params.cpp
===================================================================
--- clang/test/CodeGenCoroutines/coro-params.cpp
+++ clang/test/CodeGenCoroutines/coro-params.cpp
@@ -142,7 +142,7 @@
     promise_type() = delete;
     void get_return_object() {}
     suspend_always initial_suspend() { return {}; }
-    suspend_always final_suspend() { return {}; }
+    suspend_always final_suspend() noexcept { return {}; }
     void return_void() {}
     void unhandled_exception() {}
   };
@@ -166,7 +166,7 @@
     promise_type(some_class&, float);
     method get_return_object();
     suspend_always initial_suspend();
-    suspend_always final_suspend();
+    suspend_always final_suspend() noexcept;
     void return_void();
     void unhandled_exception();
   };
Index: clang/test/CodeGenCoroutines/coro-newpm-pipeline.cpp
===================================================================
--- clang/test/CodeGenCoroutines/coro-newpm-pipeline.cpp
+++ clang/test/CodeGenCoroutines/coro-newpm-pipeline.cpp
@@ -33,19 +33,19 @@
 struct handle {};
 
 struct awaitable {
-  bool await_ready() { return true; }
-  void await_suspend(handle) {}
-  bool await_resume() { return true; }
+  bool await_ready() noexcept { return true; }
+  void await_suspend(handle) noexcept {}
+  bool await_resume() noexcept { return true; }
 };
 
 template <typename T> struct coroutine_handle {
-  static handle from_address(void *address) { return {}; }
+  static handle from_address(void *address) noexcept { return {}; }
 };
 
 template <typename T = void> struct coroutine_traits {
   struct promise_type {
     awaitable initial_suspend() { return {}; }
-    awaitable final_suspend() { return {}; }
+    awaitable final_suspend() noexcept { return {}; }
     void return_void() {}
     T get_return_object() { return T(); }
     void unhandled_exception() {}
Index: clang/test/CodeGenCoroutines/coro-gro-nrvo.cpp
===================================================================
--- clang/test/CodeGenCoroutines/coro-gro-nrvo.cpp
+++ clang/test/CodeGenCoroutines/coro-gro-nrvo.cpp
@@ -21,7 +21,7 @@
 struct promise_type {
     RetObject get_return_object();
     suspend_always initial_suspend();
-    suspend_never final_suspend();
+    suspend_never final_suspend() noexcept;
     void return_void();
     static void unhandled_exception();
 };
@@ -52,7 +52,7 @@
     static RetObject get_return_object_on_allocation_failure();
     RetObject get_return_object();
     suspend_always initial_suspend();
-    suspend_never final_suspend();
+    suspend_never final_suspend() noexcept;
     void return_void();
     static void unhandled_exception();
 };
Index: clang/test/CodeGenCoroutines/coro-dest-slot.cpp
===================================================================
--- clang/test/CodeGenCoroutines/coro-dest-slot.cpp
+++ clang/test/CodeGenCoroutines/coro-dest-slot.cpp
@@ -8,7 +8,7 @@
   struct promise_type {
     coro get_return_object();
     suspend_always initial_suspend();
-    suspend_never final_suspend();
+    suspend_never final_suspend() noexcept;
     void return_void();
     static void unhandled_exception();
   };
Index: clang/test/CodeGenCoroutines/coro-await.cpp
===================================================================
--- clang/test/CodeGenCoroutines/coro-await.cpp
+++ clang/test/CodeGenCoroutines/coro-await.cpp
@@ -17,7 +17,7 @@
 
 template <typename Promise>
 struct coroutine_handle : coroutine_handle<> {
-  static coroutine_handle from_address(void *);
+  static coroutine_handle from_address(void *) noexcept;
 };
 
 }
@@ -29,9 +29,9 @@
   void await_resume();
 };
 struct final_susp {
-  bool await_ready();
-  void await_suspend(std::experimental::coroutine_handle<>);
-  void await_resume();
+  bool await_ready() noexcept;
+  void await_suspend(std::experimental::coroutine_handle<>) noexcept;
+  void await_resume() noexcept;
 };
 
 struct suspend_always {
@@ -46,7 +46,7 @@
   struct promise_type {
     void get_return_object();
     init_susp initial_suspend();
-    final_susp final_suspend();
+    final_susp final_suspend() noexcept;
     void return_void();
   };
 };
@@ -119,7 +119,7 @@
   struct promise_type {
     void get_return_object();
     init_susp initial_suspend();
-    final_susp final_suspend();
+    final_susp final_suspend() noexcept;
     void return_void();
     suspend_maybe yield_value(int);
   };
@@ -295,7 +295,7 @@
   struct promise_type {
     void get_return_object();
     init_susp initial_suspend();
-    final_susp final_suspend();
+    final_susp final_suspend() noexcept;
     void return_void();
     AwaitResumeReturnsLValue yield_value(int);
   };
Index: clang/test/CodeGenCoroutines/coro-await-resume-eh.cpp
===================================================================
--- clang/test/CodeGenCoroutines/coro-await-resume-eh.cpp
+++ clang/test/CodeGenCoroutines/coro-await-resume-eh.cpp
@@ -22,7 +22,7 @@
   struct promise_type {
     auto get_return_object() { return throwing_task{}; }
     auto initial_suspend() { return throwing_awaitable{}; }
-    auto final_suspend() { return coro::suspend_never{}; }
+    auto final_suspend() noexcept { return coro::suspend_never{}; }
     void return_void() {}
     void unhandled_exception() {}
   };
@@ -76,7 +76,7 @@
   // CHECK-NEXT: br label %[[COROFINAL]]
 
   // CHECK: [[COROFINAL]]:
-  // CHECK-NEXT: invoke void @_ZN13throwing_task12promise_type13final_suspendEv
+  // CHECK-NEXT: call void @_ZN13throwing_task12promise_type13final_suspendEv
   co_return;
 }
 
@@ -90,7 +90,7 @@
   struct promise_type {
     auto get_return_object() { return noexcept_task{}; }
     auto initial_suspend() { return noexcept_awaitable{}; }
-    auto final_suspend() { return coro::suspend_never{}; }
+    auto final_suspend() noexcept { return coro::suspend_never{}; }
     void return_void() {}
     void unhandled_exception() {}
   };
Index: clang/test/CodeGenCoroutines/coro-await-domination.cpp
===================================================================
--- clang/test/CodeGenCoroutines/coro-await-domination.cpp
+++ clang/test/CodeGenCoroutines/coro-await-domination.cpp
@@ -7,7 +7,7 @@
   struct promise_type {
     coro get_return_object();
     suspend_never initial_suspend();
-    suspend_never final_suspend();
+    suspend_never final_suspend() noexcept;
     void return_void();
     static void unhandled_exception();
   };
@@ -35,4 +35,3 @@
   x = co_await A{};
   consume(x);
 }
-
Index: clang/test/CodeGenCoroutines/coro-always-inline.cpp
===================================================================
--- clang/test/CodeGenCoroutines/coro-always-inline.cpp
+++ clang/test/CodeGenCoroutines/coro-always-inline.cpp
@@ -14,22 +14,22 @@
 struct handle {};
 
 struct awaitable {
-  bool await_ready() { return true; }
+  bool await_ready() noexcept { return true; }
   // CHECK-NOT: await_suspend
-  inline void __attribute__((__always_inline__)) await_suspend(handle) {}
-  bool await_resume() { return true; }
+  inline void __attribute__((__always_inline__)) await_suspend(handle) noexcept {}
+  bool await_resume() noexcept { return true; }
 };
 
 template <typename T>
 struct coroutine_handle {
-  static handle from_address(void *address) { return {}; }
+  static handle from_address(void *address) noexcept { return {}; }
 };
 
 template <typename T = void>
 struct coroutine_traits {
   struct promise_type {
     awaitable initial_suspend() { return {}; }
-    awaitable final_suspend() { return {}; }
+    awaitable final_suspend() noexcept { return {}; }
     void return_void() {}
     T get_return_object() { return T(); }
     void unhandled_exception() {}
Index: clang/test/CodeGenCoroutines/coro-alloc.cpp
===================================================================
--- clang/test/CodeGenCoroutines/coro-alloc.cpp
+++ clang/test/CodeGenCoroutines/coro-alloc.cpp
@@ -10,7 +10,7 @@
 template <class Promise = void>
 struct coroutine_handle {
   coroutine_handle() = default;
-  static coroutine_handle from_address(void *) { return {}; }
+  static coroutine_handle from_address(void *) noexcept { return {}; }
 };
 
 template <>
@@ -18,7 +18,7 @@
   static coroutine_handle from_address(void *) { return {}; }
   coroutine_handle() = default;
   template <class PromiseType>
-  coroutine_handle(coroutine_handle<PromiseType>) {}
+  coroutine_handle(coroutine_handle<PromiseType>) noexcept {}
 };
 
 } // end namespace experimental
@@ -36,9 +36,9 @@
 
 
 struct suspend_always {
-  bool await_ready() { return false; }
-  void await_suspend(std::experimental::coroutine_handle<>) {}
-  void await_resume() {}
+  bool await_ready() noexcept { return false; }
+  void await_suspend(std::experimental::coroutine_handle<>) noexcept {}
+  void await_resume() noexcept {}
 };
 
 struct global_new_delete_tag {};
@@ -48,7 +48,7 @@
   struct promise_type {
     void get_return_object() {}
     suspend_always initial_suspend() { return {}; }
-    suspend_always final_suspend() { return {}; }
+    suspend_always final_suspend() noexcept { return {}; }
     void return_void() {}
   };
 };
@@ -89,7 +89,7 @@
     void *operator new(unsigned long);
     void get_return_object() {}
     suspend_always initial_suspend() { return {}; }
-    suspend_always final_suspend() { return {}; }
+    suspend_always final_suspend() noexcept { return {}; }
     void return_void() {}
   };
 };
@@ -115,7 +115,7 @@
                        int, float, double);
     void get_return_object() {}
     suspend_always initial_suspend() { return {}; }
-    suspend_always final_suspend() { return {}; }
+    suspend_always final_suspend() noexcept { return {}; }
     void return_void() {}
   };
 };
@@ -145,7 +145,7 @@
   struct promise_type {
     void get_return_object() {}
     suspend_always initial_suspend() { return {}; }
-    suspend_always final_suspend() { return {}; }
+    suspend_always final_suspend() noexcept { return {}; }
     void return_void() {}
   };
 };
@@ -168,7 +168,7 @@
     void operator delete(void*);
     void get_return_object() {}
     suspend_always initial_suspend() { return {}; }
-    suspend_always final_suspend() { return {}; }
+    suspend_always final_suspend() noexcept { return {}; }
     void return_void() {}
   };
 };
@@ -193,7 +193,7 @@
     void operator delete(void*, unsigned long);
     void get_return_object() {}
     suspend_always initial_suspend() { return {}; }
-    suspend_always final_suspend() { return {}; }
+    suspend_always final_suspend() noexcept { return {}; }
     void return_void() {}
   };
 };
@@ -218,7 +218,7 @@
   struct promise_type {
     int get_return_object() { return 0; }
     suspend_always initial_suspend() { return {}; }
-    suspend_always final_suspend() { return {}; }
+    suspend_always final_suspend() noexcept { return {}; }
     void return_void() {}
     static int get_return_object_on_allocation_failure() { return -1; }
   };
Index: clang/test/CodeGenCoroutines/Inputs/coroutine.h
===================================================================
--- clang/test/CodeGenCoroutines/Inputs/coroutine.h
+++ clang/test/CodeGenCoroutines/Inputs/coroutine.h
@@ -72,9 +72,9 @@
   void await_resume() {}
 };
 struct suspend_never {
-  bool await_ready() { return true; }
-  void await_suspend(coroutine_handle<>) {}
-  void await_resume() {}
+  bool await_ready() noexcept { return true; }
+  void await_suspend(coroutine_handle<>) noexcept {}
+  void await_resume() noexcept {}
 };
 
 }}}
Index: clang/test/CodeGenCXX/ubsan-coroutines.cpp
===================================================================
--- clang/test/CodeGenCXX/ubsan-coroutines.cpp
+++ clang/test/CodeGenCXX/ubsan-coroutines.cpp
@@ -32,7 +32,7 @@
   struct promise_type {
     task get_return_object() { return task(); }
     suspend_always initial_suspend() { return {}; }
-    suspend_always final_suspend() { return {}; }
+    suspend_always final_suspend() noexcept { return {}; }
     void return_void() {}
     void unhandled_exception() {}
   };
Index: clang/test/Analysis/more-dtors-cfg-output.cpp
===================================================================
--- clang/test/Analysis/more-dtors-cfg-output.cpp
+++ clang/test/Analysis/more-dtors-cfg-output.cpp
@@ -278,16 +278,16 @@
 namespace std::experimental {
   template <typename Promise>
   struct coroutine_handle {
-    static coroutine_handle from_address(void *);
+    static coroutine_handle from_address(void *) noexcept;
   };
 }
 
 struct TestPromise {
   TestPromise initial_suspend();
-  TestPromise final_suspend();
-  bool await_ready();
-  void await_suspend(const std::experimental::coroutine_handle<TestPromise> &);
-  void await_resume();
+  TestPromise final_suspend() noexcept;
+  bool await_ready() noexcept;
+  void await_suspend(const std::experimental::coroutine_handle<TestPromise> &) noexcept;
+  void await_resume() noexcept;
   Foo return_value(const Bar &);
   Bar get_return_object();
   void unhandled_exception();
Index: clang/test/AST/coroutine-source-location-crash.cpp
===================================================================
--- clang/test/AST/coroutine-source-location-crash.cpp
+++ clang/test/AST/coroutine-source-location-crash.cpp
@@ -15,7 +15,7 @@
   struct promise_type {
     coro_t get_return_object();
     suspend_never initial_suspend();
-    suspend_never final_suspend();
+    suspend_never final_suspend() noexcept;
     void return_void();
     static void unhandled_exception();
   };
Index: clang/test/AST/Inputs/std-coroutine.h
===================================================================
--- clang/test/AST/Inputs/std-coroutine.h
+++ clang/test/AST/Inputs/std-coroutine.h
@@ -10,12 +10,12 @@
 
 template <class Promise = void>
 struct coroutine_handle {
-  static coroutine_handle from_address(void *);
+  static coroutine_handle from_address(void *) noexcept;
 };
 template <>
 struct coroutine_handle<void> {
   template <class PromiseType>
-  coroutine_handle(coroutine_handle<PromiseType>);
+  coroutine_handle(coroutine_handle<PromiseType>) noexcept;
   static coroutine_handle from_address(void *);
 };
 
@@ -26,9 +26,9 @@
 };
 
 struct suspend_never {
-  bool await_ready() { return true; }
-  void await_suspend(coroutine_handle<>) {}
-  void await_resume() {}
+  bool await_ready() noexcept { return true; }
+  void await_suspend(coroutine_handle<>) noexcept {}
+  void await_resume() noexcept {}
 };
 
 } // namespace experimental
Index: clang/lib/Sema/SemaExceptionSpec.cpp
===================================================================
--- clang/lib/Sema/SemaExceptionSpec.cpp
+++ clang/lib/Sema/SemaExceptionSpec.cpp
@@ -999,10 +999,8 @@
   return R;
 }
 
-/// Determine whether the callee of a particular function call can throw.
-/// E and D are both optional, but at least one of E and Loc must be specified.
-static CanThrowResult canCalleeThrow(Sema &S, const Expr *E, const Decl *D,
-                                     SourceLocation Loc = SourceLocation()) {
+CanThrowResult Sema::canCalleeThrow(Sema &S, const Expr *E, const Decl *D,
+                                    SourceLocation Loc) {
   // As an extension, we assume that __attribute__((nothrow)) functions don't
   // throw.
   if (D && isa<FunctionDecl>(D) && D->hasAttr<NoThrowAttr>())
@@ -1048,7 +1046,8 @@
   if (!FT)
     return CT_Can;
 
-  FT = S.ResolveExceptionSpec(Loc.isInvalid() ? E->getBeginLoc() : Loc, FT);
+  if (Loc.isValid() || (Loc.isInvalid() && E))
+    FT = S.ResolveExceptionSpec(Loc.isInvalid() ? E->getBeginLoc() : Loc, FT);
   if (!FT)
     return CT_Can;
 
@@ -1069,7 +1068,7 @@
             VD->getType()->getBaseElementTypeUnsafe()->getAsCXXRecordDecl()) {
       if (auto *Dtor = RD->getDestructor()) {
         CT = mergeCanThrow(
-            CT, canCalleeThrow(Self, nullptr, Dtor, VD->getLocation()));
+            CT, Sema::canCalleeThrow(Self, nullptr, Dtor, VD->getLocation()));
       }
     }
   }
Index: clang/lib/Sema/SemaCoroutine.cpp
===================================================================
--- clang/lib/Sema/SemaCoroutine.cpp
+++ clang/lib/Sema/SemaCoroutine.cpp
@@ -24,6 +24,7 @@
 #include "clang/Sema/Overload.h"
 #include "clang/Sema/ScopeInfo.h"
 #include "clang/Sema/SemaInternal.h"
+#include "llvm/ADT/SmallSet.h"
 
 using namespace clang;
 using namespace sema;
@@ -604,6 +605,66 @@
   return ScopeInfo;
 }
 
+/// Recursively check \p E and all its children to see if any call target
+/// (including constructor call) is declared noexcept. Also any value returned
+/// from the call has a noexcept destructor.
+static void checkNoThrow(Sema &S, const Stmt *E,
+                         llvm::SmallPtrSetImpl<const Decl *> &ThrowingDecls) {
+  auto checkDeclNoexcept = [&](const Decl *D, bool IsDtor = false) {
+    // In the case of dtor, the call to dtor is implicit and hence we should
+    // pass nullptr to canCalleeThrow.
+    if (Sema::canCalleeThrow(S, IsDtor ? nullptr : cast<Expr>(E), D)) {
+      if (ThrowingDecls.empty()) {
+        // First time seeing an error, emit the error message.
+        S.Diag(cast<FunctionDecl>(S.CurContext)->getLocation(),
+               diag::err_coroutine_promise_final_suspend_requires_nothrow);
+      }
+      ThrowingDecls.insert(D);
+    }
+  };
+  auto SC = E->getStmtClass();
+  if (SC == Expr::CXXConstructExprClass) {
+    auto const *Ctor = cast<CXXConstructExpr>(E)->getConstructor();
+    checkDeclNoexcept(Ctor);
+    // Check the corresponding destructor of the constructor.
+    checkDeclNoexcept(Ctor->getParent()->getDestructor(), true);
+  } else if (SC == Expr::CallExprClass || SC == Expr::CXXMemberCallExprClass ||
+             SC == Expr::CXXOperatorCallExprClass) {
+    if (!cast<CallExpr>(E)->isTypeDependent()) {
+      // FIXME: Handle dependent types.
+      checkDeclNoexcept(cast<CallExpr>(E)->getCalleeDecl());
+      auto ReturnType = cast<CallExpr>(E)->getCallReturnType(S.getASTContext());
+      // Check the destructor of the call return type, if any.
+      if (ReturnType.isDestructedType() ==
+          QualType::DestructionKind::DK_cxx_destructor) {
+        const auto *T =
+            cast<RecordType>(ReturnType.getCanonicalType().getTypePtr());
+        checkDeclNoexcept(
+            dyn_cast<CXXRecordDecl>(T->getDecl())->getDestructor(), true);
+      }
+    }
+  }
+  for (const auto *Child : E->children()) {
+    if (!Child)
+      continue;
+    checkNoThrow(S, Child, ThrowingDecls);
+  }
+}
+
+/// Check that the expression co_await promise.final_suspend() shall not be
+/// potentially-throwing.
+static bool checkNoThrow(Sema &S, const Stmt *FinalSuspend) {
+  llvm::SmallPtrSet<const Decl *, 4> ThrowingDecls;
+  // We first collect all declarations that should not throw but not declared
+  // with noexcept. This is to avoid emitting the same note multiple times
+  // on the same declaration.
+  checkNoThrow(S, FinalSuspend, ThrowingDecls);
+  for (const auto *D : ThrowingDecls) {
+    S.Diag(D->getEndLoc(), diag::note_coroutine_function_declare_noexcept);
+  }
+  return ThrowingDecls.empty();
+}
+
 bool Sema::ActOnCoroutineBodyStart(Scope *SC, SourceLocation KWLoc,
                                    StringRef Keyword) {
   if (!checkCoroutineContext(*this, KWLoc, Keyword))
@@ -646,7 +707,7 @@
     return true;
 
   StmtResult FinalSuspend = buildSuspends("final_suspend");
-  if (FinalSuspend.isInvalid())
+  if (FinalSuspend.isInvalid() || !checkNoThrow(*this, FinalSuspend.get()))
     return true;
 
   ScopeInfo->setCoroutineSuspends(InitSuspend.get(), FinalSuspend.get());
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -1697,6 +1697,11 @@
   static QualType GetTypeFromParser(ParsedType Ty,
                                     TypeSourceInfo **TInfo = nullptr);
   CanThrowResult canThrow(const Stmt *E);
+  /// Determine whether the callee of a particular function call can throw.
+  /// E and D are both optional, but at least one of E and Loc must be
+  /// specified.
+  static CanThrowResult canCalleeThrow(Sema &S, const Expr *E, const Decl *D,
+                                       SourceLocation Loc = SourceLocation());
   const FunctionProtoType *ResolveExceptionSpec(SourceLocation Loc,
                                                 const FunctionProtoType *FPT);
   void UpdateExceptionSpec(FunctionDecl *FD,
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -10517,7 +10517,13 @@
 def note_await_ready_no_bool_conversion : Note<
   "return type of 'await_ready' is required to be contextually convertible to 'bool'"
 >;
-}
+def err_coroutine_promise_final_suspend_requires_nothrow : Error<
+  "all code generated by co_await __promise.final_suspend() must not throw"
+>;
+def note_coroutine_function_declare_noexcept : Note<
+  "must be declared with 'noexcept'"
+>;
+} // end of coroutines issue category
 
 let CategoryName = "Documentation Issue" in {
 def warn_not_a_doxygen_trailing_member_comment : Warning<
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to