
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
22 lines
765 B
C++
22 lines
765 B
C++
// 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;
|
|
}
|
|
}
|