llvm-project/clang/test/Driver/clang-exception-flags.cpp
Fangrui Song c0a73918bf [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
2023-11-05 00:39:38 -07:00

33 lines
1.7 KiB
C++

// We force the target to unknown because clang's default behavior for
// exception handling is target dependent.
// RUN: %clang -### -target unknown %s 2>&1 | FileCheck %s -check-prefix=DEFAULT
// DEFAULT: "-cc1" {{.*}} "-fcxx-exceptions" "-fexceptions"
//
// RUN: %clang -### -fexceptions %s 2>&1 | FileCheck %s -check-prefix=ON1
// ON1: "-cc1" {{.*}} "-fcxx-exceptions" "-fexceptions"
//
// RUN: %clang -### -fno-exceptions -fcxx-exceptions %s 2>&1 | FileCheck %s -check-prefix=ON2
// ON2: "-cc1" {{.*}} "-fcxx-exceptions" "-fexceptions"
//
// RUN: %clang -### -fno-cxx-exceptions -fexceptions %s 2>&1 | FileCheck %s -check-prefix=ON3
// ON3: "-cc1" {{.*}} "-fcxx-exceptions" "-fexceptions"
//
// RUN: %clang -### -fno-exceptions %s 2>&1 | FileCheck %s -check-prefix=OFF1
// OFF1-NOT: "-cc1" {{.*}} "-fcxx-exceptions"
//
// RUN: %clang -### -fno-cxx-exceptions %s 2>&1 | FileCheck %s -check-prefix=OFF2
// OFF2-NOT: "-cc1" {{.*}} "-fcxx-exceptions"
//
// RUN: %clang -### -fcxx-exceptions -fno-exceptions %s 2>&1 | FileCheck %s -check-prefix=OFF3
// OFF3-NOT: "-cc1" {{.*}} "-fcxx-exceptions"
//
// RUN: %clang -### -fexceptions -fno-cxx-exceptions %s 2>&1 | FileCheck %s -check-prefix=OFF4
// OFF4-NOT: "-cc1" {{.*}} "-fcxx-exceptions"
//
// 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"