[clang] c0a7391 - [ItaniumCXXABI] Add -fassume-nothrow-exception-dtor to assume that all exception objects' destructors are non-throwing

Fangrui Song via cfe-commits cfe-commits at lists.llvm.org
Sun Nov 5 00:39:44 PDT 2023


Author: Fangrui Song
Date: 2023-11-05T00:39:38-07:00
New Revision: c0a73918bfddc6a04a897aab57fb95e8d2da7ec0

URL: https://github.com/llvm/llvm-project/commit/c0a73918bfddc6a04a897aab57fb95e8d2da7ec0
DIFF: https://github.com/llvm/llvm-project/commit/c0a73918bfddc6a04a897aab57fb95e8d2da7ec0.diff

LOG: [ItaniumCXXABI] Add -fassume-nothrow-exception-dtor to assume that all exception objects' destructors are non-throwing

Link: https://lists.llvm.org/pipermail/cfe-dev/2021-August/068740.html ("[Exception Handling] Could we mark __cxa_end_catch as nounwind conditionally?"
Link: https://github.com/llvm/llvm-project/issues/57375

A catch handler calls `__cxa_begin_catch` and `__cxa_end_catch`. For a catch-all
clause or a catch clause matching a record type, we:

* assume that the exception object may have a throwing destructor
* emit `invoke void @__cxa_end_catch` (as the call is not marked as the `nounwind` attribute).
* emit a landing pad to destroy local variables and call `_Unwind_Resume`

```
struct A { ~A(); };
struct B { int x; };
void opaque();
void foo() {
  A a;
  try { opaque(); } catch (...) { } // the exception object has an unknown type and may throw
  try { opaque(); } catch (B b) { } // B::~B is nothrow, but we do not utilize this
}
```

Per C++ [dcl.fct.def.coroutine], a coroutine's function body implies a `catch (...)`.
Our code generation pessimizes even simple code, like:
```
UserFacing foo() {
  A a;
  opaque();
  co_return;
  // For `invoke void @__cxa_end_catch()`, the landing pad destroys the
  // promise_type and deletes the coro frame.
}
```

Throwing destructors are typically discouraged. In many environments, the
destructors of exception objects are guaranteed to never throw, making our
conservative code generation approach seem wasteful.

Furthermore, throwing destructors tend not to work well in practice:

* GCC does not emit call site records for the region containing `__cxa_end_catch`. This has been a long time, since 2000.
* If a catch-all clause catches an exception object that throws, both GCC and Clang using libstdc++ leak the allocated exception object.

To avoid code generation pessimization, add an opt-in driver option
-fassume-nothrow-exception-dtor to assume that `__cxa_end_catch` calls have the
`nounwind` attribute. This implies that thrown exception objects' destructors
will never throw.

To detect misuses, diagnose throw expressions with a potentially-throwing
destructor. Technically, it is possible that a potentially-throwing destructor
never throws when called transitively by `__cxa_end_catch`, but these cases seem
rare enough to justify a relaxed mode.

Reviewed By: ChuanqiXu

Differential Revision: https://reviews.llvm.org/D108905

Added: 
    clang/test/SemaCXX/assume-nothrow-exception-dtor.cpp

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/docs/UsersManual.rst
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/include/clang/Basic/LangOptions.def
    clang/include/clang/Driver/Options.td
    clang/lib/CodeGen/ItaniumCXXABI.cpp
    clang/lib/Driver/ToolChains/Clang.cpp
    clang/lib/Sema/SemaExprCXX.cpp
    clang/test/CodeGenCXX/eh.cpp
    clang/test/CodeGenCXX/exceptions.cpp
    clang/test/CodeGenCoroutines/coro-cleanup.cpp
    clang/test/Driver/clang-exception-flags.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index afe7e2e79c2d087..3edf480665ba10c 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -232,6 +232,10 @@ New Compiler Flags
   preserving ``#include`` directives for "system" headers instead of copying
   the preprocessed text to the output. This can greatly reduce the size of the
   preprocessed output, which can be helpful when trying to reduce a test case.
+* ``-fassume-nothrow-exception-dtor`` is added to assume that the destructor of
+  an thrown exception object will not throw. The generated code for catch
+  handlers will be smaller. A throw expression of a type with a
+  potentially-throwing destructor will lead to an error.
 
 Deprecated Compiler Flags
 -------------------------

diff  --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst
index edc2bce6a964dc4..2e658557b0e310c 100644
--- a/clang/docs/UsersManual.rst
+++ b/clang/docs/UsersManual.rst
@@ -2134,6 +2134,18 @@ are listed below.
    new operator will always return a pointer that does not alias any
    other pointer when the function returns.
 
+.. option:: -fassume-nothrow-exception-dtor
+
+   Assume that an exception object' destructor will not throw, and generate
+   less code for catch handlers. A throw expression of a type with a
+   potentially-throwing destructor will lead to an error.
+
+   By default, Clang assumes that the exception object may have a throwing
+   destructor. For the Itanium C++ ABI, Clang generates a landing pad to
+   destroy local variables and call ``_Unwind_Resume`` for the code
+   ``catch (...) { ... }``. This option tells Clang that an exception object's
+   destructor will not throw and code simplification is possible.
+
 .. option:: -ftrap-function=[name]
 
    Instruct code generator to emit a function call to the specified

diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 474afc2fb99c1f0..4614324babb1c91 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -7977,6 +7977,8 @@ def err_return_in_constructor_handler : Error<
 def warn_cdtor_function_try_handler_mem_expr : Warning<
   "cannot refer to a non-static member from the handler of a "
   "%select{constructor|destructor}0 function try block">, InGroup<Exceptions>;
+def err_throw_object_throwing_dtor : Error<
+  "cannot throw object of type %0 with a potentially-throwing destructor">;
 
 let CategoryName = "Lambda Issue" in {
   def err_capture_more_than_once : Error<

diff  --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def
index c0ea4ecb9806a5b..ccd6c120435ffa3 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -145,6 +145,7 @@ ENUM_LANGOPT(ExceptionHandling, ExceptionHandlingKind, 3,
              ExceptionHandlingKind::None, "exception handling")
 LANGOPT(IgnoreExceptions  , 1, 0, "ignore exceptions")
 LANGOPT(ExternCNoUnwind   , 1, 0, "Assume extern C functions don't unwind")
+LANGOPT(AssumeNothrowExceptionDtor , 1, 0, "Assume exception object's destructor is nothrow")
 LANGOPT(TraditionalCPP    , 1, 0, "traditional CPP emulation")
 LANGOPT(RTTI              , 1, 1, "run-time type information")
 LANGOPT(RTTIData          , 1, 1, "emit run-time type information data")

diff  --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 9c0dcaa8b3f6276..009ac1cfd8dc1b4 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -1997,6 +1997,10 @@ def fignore_exceptions : Flag<["-"], "fignore-exceptions">, Group<f_Group>,
   Visibility<[ClangOption, CC1Option]>,
   HelpText<"Enable support for ignoring exception handling constructs">,
   MarshallingInfoFlag<LangOpts<"IgnoreExceptions">>;
+defm assume_nothrow_exception_dtor: BoolFOption<"assume-nothrow-exception-dtor",
+  LangOpts<"AssumeNothrowExceptionDtor">, DefaultFalse,
+  PosFlag<SetTrue, [], [ClangOption, CC1Option], "Assume that exception objects' destructors are non-throwing">,
+  NegFlag<SetFalse>>;
 def fexcess_precision_EQ : Joined<["-"], "fexcess-precision=">, Group<f_Group>,
   Visibility<[ClangOption, CLOption]>,
   HelpText<"Allows control over excess precision on targets where native "

diff  --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp
index 55d2696750ae7a0..eb70f56e5b57916 100644
--- a/clang/lib/CodeGen/ItaniumCXXABI.cpp
+++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp
@@ -4508,7 +4508,9 @@ namespace {
 }
 
 /// Emits a call to __cxa_begin_catch and enters a cleanup to call
-/// __cxa_end_catch.
+/// __cxa_end_catch. If -fassume-nothrow-exception-dtor is specified, we assume
+/// that the exception object's dtor is nothrow, therefore the __cxa_end_catch
+/// call can be marked as nounwind even if EndMightThrow is true.
 ///
 /// \param EndMightThrow - true if __cxa_end_catch might throw
 static llvm::Value *CallBeginCatch(CodeGenFunction &CGF,
@@ -4517,7 +4519,9 @@ static llvm::Value *CallBeginCatch(CodeGenFunction &CGF,
   llvm::CallInst *call =
     CGF.EmitNounwindRuntimeCall(getBeginCatchFn(CGF.CGM), Exn);
 
-  CGF.EHStack.pushCleanup<CallEndCatch>(NormalAndEHCleanup, EndMightThrow);
+  CGF.EHStack.pushCleanup<CallEndCatch>(
+      NormalAndEHCleanup,
+      EndMightThrow && !CGF.CGM.getLangOpts().AssumeNothrowExceptionDtor);
 
   return call;
 }

diff  --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 79f7fba22570746..8bf07a10274e737 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -385,6 +385,9 @@ static bool addExceptionArgs(const ArgList &Args, types::ID InputType,
   // So we do not set EH to false.
   Args.AddLastArg(CmdArgs, options::OPT_fignore_exceptions);
 
+  Args.addOptInFlag(CmdArgs, options::OPT_fassume_nothrow_exception_dtor,
+                    options::OPT_fno_assume_nothrow_exception_dtor);
+
   if (EH)
     CmdArgs.push_back("-fexceptions");
   return EH;

diff  --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 093c57d64a7124d..16a04d303c12c27 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -1101,6 +1101,16 @@ bool Sema::CheckCXXThrowOperand(SourceLocation ThrowLoc,
           << (unsigned)ExnObjAlign.getQuantity();
     }
   }
+  if (!isPointer && getLangOpts().AssumeNothrowExceptionDtor) {
+    if (CXXDestructorDecl *Dtor = RD->getDestructor()) {
+      auto Ty = Dtor->getType();
+      if (auto *FT = Ty.getTypePtr()->getAs<FunctionProtoType>()) {
+        if (!isUnresolvedExceptionSpec(FT->getExceptionSpecType()) &&
+            !FT->isNothrow())
+          Diag(ThrowLoc, diag::err_throw_object_throwing_dtor) << RD;
+      }
+    }
+  }
 
   return false;
 }

diff  --git a/clang/test/CodeGenCXX/eh.cpp b/clang/test/CodeGenCXX/eh.cpp
index c20eb3119688b6d..5c592a96e27b737 100644
--- a/clang/test/CodeGenCXX/eh.cpp
+++ b/clang/test/CodeGenCXX/eh.cpp
@@ -1,5 +1,6 @@
-// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -triple x86_64-apple-macosx10.13.99 -std=c++11 -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=UNALIGNED %s
-// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -triple x86_64-apple-macosx10.14 -std=c++11 -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=ALIGNED %s
+// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -triple x86_64-apple-macosx10.13.99 -std=c++11 -emit-llvm -o - %s | FileCheck --check-prefixes=CHECK,UNALIGNED,THROWEND %s
+// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -triple x86_64-apple-macosx10.14 -std=c++11 -emit-llvm -o - %s | FileCheck --check-prefixes=CHECK,ALIGNED,THROWEND %s
+// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -triple x86_64-apple-macosx10.14 -std=c++11 -emit-llvm -o - %s -fassume-nothrow-exception-dtor -DNOTHROWEND | FileCheck --check-prefixes=CHECK,ALIGNED,NOTHROWEND %s
 
 struct test1_D {
   double d;
@@ -218,13 +219,16 @@ namespace test10 {
     } catch (B a) {
     // CHECK:      call ptr @__cxa_begin_catch
     // CHECK-NEXT: call void @llvm.memcpy
-    // CHECK-NEXT: invoke void @__cxa_end_catch()
+    // THROWEND-NEXT:   invoke void @__cxa_end_catch()
+    // NOTHROWEND-NEXT: call void @__cxa_end_catch() [[NUW]]
     } catch (...) {
     // CHECK:      call ptr @__cxa_begin_catch
-    // CHECK-NEXT: invoke void @__cxa_end_catch()
+    // THROWEND-NEXT:   invoke void @__cxa_end_catch()
+    // NOTHROWEND-NEXT: call void @__cxa_end_catch() [[NUW]]
     }
 
-    // CHECK: call void @_ZN6test101AD1Ev(
+    // THROWEND:       call void @_ZN6test101AD1Ev(
+    // NOTHROWEND-NOT: call void @_ZN6test101AD1Ev(
   }
 }
 
@@ -391,40 +395,42 @@ namespace test16 {
 
   // CHECK-LABEL: define{{.*}} void @_ZN6test163barEv()
   void bar() {
-    // CHECK:      [[EXN_SAVE:%.*]] = alloca ptr
-    // CHECK-NEXT: [[EXN_ACTIVE:%.*]] = alloca i1
-    // CHECK-NEXT: [[TEMP:%.*]] = alloca [[A:%.*]],
-    // CHECK-NEXT: [[EXNSLOT:%.*]] = alloca ptr
-    // CHECK-NEXT: [[SELECTORSLOT:%.*]] = alloca i32
-    // CHECK-NEXT: [[TEMP_ACTIVE:%.*]] = alloca i1
-
+    // THROWEND:      [[EXN_SAVE:%.*]] = alloca ptr
+    // THROWEND-NEXT: [[EXN_ACTIVE:%.*]] = alloca i1
+    // THROWEND-NEXT: [[TEMP:%.*]] = alloca [[A:%.*]],
+    // THROWEND-NEXT: [[EXNSLOT:%.*]] = alloca ptr
+    // THROWEND-NEXT: [[SELECTORSLOT:%.*]] = alloca i32
+    // THROWEND-NEXT: [[TEMP_ACTIVE:%.*]] = alloca i1
+
+#ifndef NOTHROWEND
     cond() ? throw B(A()) : foo();
-
-    // CHECK-NEXT: [[COND:%.*]] = call noundef zeroext i1 @_ZN6test164condEv()
-    // CHECK-NEXT: store i1 false, ptr [[EXN_ACTIVE]]
-    // CHECK-NEXT: store i1 false, ptr [[TEMP_ACTIVE]]
-    // CHECK-NEXT: br i1 [[COND]],
-
-    // CHECK:      [[EXN:%.*]] = call ptr @__cxa_allocate_exception(i64 4)
-    // CHECK-NEXT: store ptr [[EXN]], ptr [[EXN_SAVE]]
-    // CHECK-NEXT: store i1 true, ptr [[EXN_ACTIVE]]
-    // CHECK-NEXT: invoke void @_ZN6test161AC1Ev(ptr {{[^,]*}} [[TEMP]])
-    // CHECK:      store i1 true, ptr [[TEMP_ACTIVE]]
-    // CHECK-NEXT: invoke void @_ZN6test161BC1ERKNS_1AE(ptr {{[^,]*}} [[EXN]], ptr noundef nonnull align {{[0-9]+}} dereferenceable({{[0-9]+}}) [[TEMP]])
-    // CHECK:      store i1 false, ptr [[EXN_ACTIVE]]
-    // CHECK-NEXT: invoke void @__cxa_throw(ptr [[EXN]],
-
-    // CHECK:      invoke void @_ZN6test163fooEv()
-    // CHECK:      br label
-
-    // CHECK:      invoke void @_ZN6test161AD1Ev(ptr {{[^,]*}} [[TEMP]])
-    // CHECK:      ret void
-
-    // CHECK:      [[T0:%.*]] = load i1, ptr [[EXN_ACTIVE]]
-    // CHECK-NEXT: br i1 [[T0]]
-    // CHECK:      [[T1:%.*]] = load ptr, ptr [[EXN_SAVE]]
-    // CHECK-NEXT: call void @__cxa_free_exception(ptr [[T1]])
-    // CHECK-NEXT: br label
+#endif
+
+    // THROWEND-NEXT: [[COND:%.*]] = call noundef zeroext i1 @_ZN6test164condEv()
+    // THROWEND-NEXT: store i1 false, ptr [[EXN_ACTIVE]]
+    // THROWEND-NEXT: store i1 false, ptr [[TEMP_ACTIVE]]
+    // THROWEND-NEXT: br i1 [[COND]],
+
+    // THROWEND:      [[EXN:%.*]] = call ptr @__cxa_allocate_exception(i64 4)
+    // THROWEND-NEXT: store ptr [[EXN]], ptr [[EXN_SAVE]]
+    // THROWEND-NEXT: store i1 true, ptr [[EXN_ACTIVE]]
+    // THROWEND-NEXT: invoke void @_ZN6test161AC1Ev(ptr {{[^,]*}} [[TEMP]])
+    // THROWEND:      store i1 true, ptr [[TEMP_ACTIVE]]
+    // THROWEND-NEXT: invoke void @_ZN6test161BC1ERKNS_1AE(ptr {{[^,]*}} [[EXN]], ptr noundef nonnull align {{[0-9]+}} dereferenceable({{[0-9]+}}) [[TEMP]])
+    // THROWEND:      store i1 false, ptr [[EXN_ACTIVE]]
+    // THROWEND-NEXT: invoke void @__cxa_throw(ptr [[EXN]],
+
+    // THROWEND:      invoke void @_ZN6test163fooEv()
+    // THROWEND:      br label
+
+    // THROWEND:      invoke void @_ZN6test161AD1Ev(ptr {{[^,]*}} [[TEMP]])
+    // THROWEND:      ret void
+
+    // THROWEND:      [[T0:%.*]] = load i1, ptr [[EXN_ACTIVE]]
+    // THROWEND-NEXT: br i1 [[T0]]
+    // THROWEND:      [[T1:%.*]] = load ptr, ptr [[EXN_SAVE]]
+    // THROWEND-NEXT: call void @__cxa_free_exception(ptr [[T1]])
+    // THROWEND-NEXT: br label
   }
 }
 

diff  --git a/clang/test/CodeGenCXX/exceptions.cpp b/clang/test/CodeGenCXX/exceptions.cpp
index 8301c68b8fbd254..483876cc212e301 100644
--- a/clang/test/CodeGenCXX/exceptions.cpp
+++ b/clang/test/CodeGenCXX/exceptions.cpp
@@ -1,5 +1,6 @@
 // RUN: %clang_cc1 -no-enable-noundef-analysis %s -triple=x86_64-linux-gnu -emit-llvm -std=c++98 -o - -fcxx-exceptions -fexceptions | FileCheck -check-prefix=CHECK -check-prefix=CHECK98 %s
-// RUN: %clang_cc1 -no-enable-noundef-analysis %s -triple=x86_64-linux-gnu -emit-llvm -std=c++11 -o - -fcxx-exceptions -fexceptions | FileCheck -check-prefix=CHECK -check-prefix=CHECK11 %s
+// RUN: %clang_cc1 -no-enable-noundef-analysis %s -triple=x86_64-linux-gnu -emit-llvm -std=c++11 -o - -fcxx-exceptions -fexceptions | FileCheck --check-prefixes=CHECK,CHECK11,THROWEND11 %s
+// RUN: %clang_cc1 -no-enable-noundef-analysis %s -triple=x86_64-linux-gnu -emit-llvm -std=c++11 -o - -fcxx-exceptions -fexceptions -fassume-nothrow-exception-dtor | FileCheck --check-prefixes=CHECK,CHECK11,NOTHROWEND11 %s
 
 // CHECK: %[[STRUCT_TEST13_A:.*]] = type { i32, i32 }
 
@@ -479,11 +480,16 @@ namespace test10 {
 
   // CHECK98:      call void @__cxa_end_catch()
   // CHECK98-NEXT: br label
-  // CHECK11:      invoke void @__cxa_end_catch()
-  // CHECK11-NEXT: to label
+  // THROWEND11:        invoke void @__cxa_end_catch()
+  // THROWEND11-NEXT:   to label %invoke.cont[[#]] unwind label %terminate.lpad
+  // NOTHROWEND11:      call void @__cxa_end_catch()
+  // NOTHROWEND11-NEXT: br label %try.cont
 
   // CHECK:      invoke void @__cxa_rethrow()
   // CHECK:      unreachable
+
+  // CHECK:      terminate.lpad:
+  // CHECK:        call void @__clang_call_terminate(
 }
 
 // Ensure that an exception in a constructor destroys

diff  --git a/clang/test/CodeGenCoroutines/coro-cleanup.cpp b/clang/test/CodeGenCoroutines/coro-cleanup.cpp
index e07876305dabf67..98f150758e2d106 100644
--- a/clang/test/CodeGenCoroutines/coro-cleanup.cpp
+++ b/clang/test/CodeGenCoroutines/coro-cleanup.cpp
@@ -1,5 +1,6 @@
 // Verify that coroutine promise and allocated memory are freed up on exception.
-// RUN: %clang_cc1 -std=c++20 -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s -fexceptions -fcxx-exceptions -disable-llvm-passes | FileCheck %s
+// RUN: %clang_cc1 -std=c++20 -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s -fexceptions -fcxx-exceptions -disable-llvm-passes | FileCheck %s --check-prefixes=CHECK,THROWEND
+// RUN: %clang_cc1 -std=c++20 -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s -fexceptions -fcxx-exceptions -fassume-nothrow-exception-dtor -disable-llvm-passes | FileCheck %s --check-prefixes=CHECK,NOTHROWEND
 
 namespace std {
 template <typename... T> struct coroutine_traits;
@@ -49,7 +50,9 @@ void f() {
   // CHECK: [[DeallocPad]]:
   // CHECK-NEXT: landingpad
   // CHECK-NEXT:   cleanup
-  // CHECK: br label %[[Dealloc:.+]]
+  // THROWEND:        br label %[[Dealloc:.+]]
+  // NOTHROWEND:      icmp ne ptr %[[#]], null
+  // NOTHROWEND-NEXT: br i1 %[[#]], label %[[Dealloc:.+]], label
 
   Cleanup cleanup;
   may_throw();
@@ -68,13 +71,15 @@ void f() {
   // CHECK: [[Catch]]:
   // CHECK:    call ptr @__cxa_begin_catch(
   // CHECK:    call void @_ZNSt16coroutine_traitsIJvEE12promise_type19unhandled_exceptionEv(
-  // CHECK:    invoke void @__cxa_end_catch()
-  // CHECK-NEXT:    to label %[[Cont:.+]] unwind
+  // THROWEND:        invoke void @__cxa_end_catch()
+  // THROWEND-NEXT:     to label %[[Cont:.+]] unwind
+  // NOTHROWEND:      call void @__cxa_end_catch()
+  // NOTHROWEND-NEXT:   br label %[[Cont2:.+]]
 
-  // CHECK: [[Cont]]:
-  // CHECK-NEXT: br label %[[Cont2:.+]]
-  // CHECK: [[Cont2]]:
-  // CHECK-NEXT: br label %[[Cleanup:.+]]
+  // THROWEND:      [[Cont]]:
+  // THROWEND-NEXT:   br label %[[Cont2:.+]]
+  // CHECK:         [[Cont2]]:
+  // CHECK-NEXT:      br label %[[Cleanup:.+]]
 
   // CHECK: [[Cleanup]]:
   // CHECK: call void @_ZNSt16coroutine_traitsIJvEE12promise_typeD1Ev(
@@ -82,8 +87,8 @@ void f() {
   // CHECK: call void @_ZdlPv(ptr noundef %[[Mem0]]
 
   // CHECK: [[Dealloc]]:
-  // CHECK:   %[[Mem:.+]] = call ptr @llvm.coro.free(
-  // CHECK:   call void @_ZdlPv(ptr noundef %[[Mem]])
+  // THROWEND:   %[[Mem:.+]] = call ptr @llvm.coro.free(
+  // THROWEND:   call void @_ZdlPv(ptr noundef %[[Mem]])
 
   co_return;
 }

diff  --git a/clang/test/Driver/clang-exception-flags.cpp b/clang/test/Driver/clang-exception-flags.cpp
index af63a0ab03be21a..7b88ecdfcf33350 100644
--- a/clang/test/Driver/clang-exception-flags.cpp
+++ b/clang/test/Driver/clang-exception-flags.cpp
@@ -27,3 +27,6 @@
 // RUN: %clang -### -target x86_64-scei-ps4 %s 2>&1 | FileCheck %s -check-prefix=PS-OFF
 // RUN: %clang -### -target x86_64-sie-ps5 %s 2>&1 | FileCheck %s -check-prefix=PS-OFF
 // PS-OFF-NOT: "-cc1" {{.*}} "-f{{(cxx-)?}}exceptions"
+
+// RUN: %clang -### -fexceptions -fno-assume-nothrow-exception-dtor -fassume-nothrow-exception-dtor %s 2>&1 | FileCheck %s --check-prefix=NOTHROW-DTOR
+// NOTHROW-DTOR: "-cc1"{{.*}} "-fcxx-exceptions" "-fassume-nothrow-exception-dtor"

diff  --git a/clang/test/SemaCXX/assume-nothrow-exception-dtor.cpp b/clang/test/SemaCXX/assume-nothrow-exception-dtor.cpp
new file mode 100644
index 000000000000000..db070310b6096ce
--- /dev/null
+++ b/clang/test/SemaCXX/assume-nothrow-exception-dtor.cpp
@@ -0,0 +1,21 @@
+// RUN: %clang_cc1 -triple %itanium_abi_triple -fsyntax-only %s -fcxx-exceptions -fassume-nothrow-exception-dtor -verify
+
+namespace test1 {
+template <typename T> struct A { A(); ~A(); };
+struct B { ~B() noexcept(false); };
+struct B1 : B {};
+struct B2 { B b; };
+struct C { virtual void f(); } c;
+struct MoveOnly { MoveOnly(); MoveOnly(MoveOnly&&); };
+void run() {
+  throw A<int>();
+  throw B();  // expected-error{{cannot throw object of type 'B' with a potentially-throwing destructor}}
+  throw new B;
+  throw B1(); // expected-error{{cannot throw object of type 'B1' with a potentially-throwing destructor}}
+  B2 b2;
+  throw b2;   // expected-error{{cannot throw object of type 'B2' with a potentially-throwing destructor}}
+  throw c;
+  MoveOnly m;
+  throw m;
+}
+}


        


More information about the cfe-commits mailing list