https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86286
--- Comment #5 from Jonathan Wakely <redi at gcc dot gnu.org> ---
I don't know how common it is, but a realistic example is std::unique_ptr<T, D>
where the deleter D::operator() is not inline (so the compiler can't see
whether it throws) and is not noexcept (maybe it throws in some cases, but not
in the specific cases where it's called by unique_ptr in this particular
program).
The standard says it's undefined behaviour for the deleter to throw, so the EH
table entry that calls terminate isn't needed. It might be nice if the
unique_ptr implementation could give that information to the compiler,
something like this:
--- a/libstdc++-v3/include/bits/unique_ptr.h
+++ b/libstdc++-v3/include/bits/unique_ptr.h
@@ -151,18 +151,25 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
"unique_ptr's deleter type must be a function object type"
" or an lvalue reference type" );
+ [[__gnu__::__nothrow__]]
__uniq_ptr_impl() = default;
- __uniq_ptr_impl(pointer __p) : _M_t() { _M_ptr() = __p; }
+
+ [[__gnu__::__nothrow__]]
+ __uniq_ptr_impl(pointer __p) : _M_t() { _M_ptr() = __p; }
template<typename _Del>
- __uniq_ptr_impl(pointer __p, _Del&& __d)
- : _M_t(__p, std::forward<_Del>(__d)) { }
+ [[__gnu__::__nothrow__]]
+ __uniq_ptr_impl(pointer __p, _Del&& __d)
+ : _M_t(__p, std::forward<_Del>(__d))
+ { }
+ [[__gnu__::__nothrow__]]
- __uniq_ptr_impl(__uniq_ptr_impl&& __u) noexcept
+ __uniq_ptr_impl(__uniq_ptr_impl&& __u)
: _M_t(std::move(__u._M_t))
{ __u._M_ptr() = nullptr; }
- __uniq_ptr_impl& operator=(__uniq_ptr_impl&& __u) noexcept
+ [[__gnu__::__nothrow__]]
+ __uniq_ptr_impl& operator=(__uniq_ptr_impl&& __u)
{
reset(__u.release());
_M_deleter() = std::forward<_Dp>(__u._M_deleter());
Replacing noexcept with __attribute__((nothrow)) already has the desired effect
of removing the EH table that calls terminate. But the noexcept operator is
false for a function with nothrow unless it's *also* noexcept:
int g (int i);
__attribute__((nothrow)) int boo (int i) { return g (i); }
static_assert(noexcept(boo(0)), ""); // FAILS
__attribute__((nothrow)) int too (int i) noexcept { return g (i); }
static_assert(noexcept(too(0)), "");
But if it's also noexcept then GCC puts the EH table back in.
FWIW, both assertions pass with clang, but that seems to be because it maps
nothrow exactly to noexcept. That means it still generates the extra code to
call terminate for both of 'boo' and 'too' (and clang doesn't use the EH tables
for that, so the code is much larger, which is exactly what I want to avoid).
So maybe a new attribute would be best, and we don't change the meaning of any
existing code using nothrow. __attribute__((undefined_if_throw)) or something.
Not a priority, but I wanted to stash my latest thoughts on this here.