[Bug libstdc++/85813] make_exception_ptr should support __cxa_init_primary_exception path even with -fno-exceptions

redi at gcc dot gnu.org gcc-bugzilla@gcc.gnu.org
Thu Dec 9 18:36:05 GMT 2021


https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85813

--- Comment #9 from Jonathan Wakely <redi at gcc dot gnu.org> ---
(In reply to Mathias Stearn from comment #3)
> My assumption was that if E(...) throws and it can't be caught, it should be
> treated as any other case when an -fno-exceptions TU calls a throwing
> function. In this case that would mean calling terminate() due to the
> noexcept, which seems better than returning a null exception_ptr.

Unfortunately, if E(...) throws and std::make_exception_ptr<E> was compiled
with -fno-exceptions, the noexcept on that function is ignored and the
exception propagates to the caller of std::make_exception_ptr. So exceptions
can propagate out of a noexcept function compiled with -fno-exceptions, which
is pretty counterintuitive because that's the last kind of function you'd
expect to throw when you call it! But that's not unique to
std::make_exception_ptr, it's true for any noexcept function compiled with
-fno-exceptions that calls that could throw.

(In reply to Mathias Stearn from comment #7)
> Silently dropping errors always skeeves me out. I'm not sure if anyone is
> well served by the current behavior. If it were up to me (and I know it
> isn't) I'd make that case call std::terminate() or similar rather than
> returning the "no error" value. That seems consistent with the behavior of
> the __throw* functions, but it is a breaking change. Or even better, since
> gcc seems fine throwing through functions marked noexcept in -fno-exceptions
> TUs, maybe in the (very rare) case where copying/moving an exception throws
> inside an -fno-exceptions TU, just let it bubble out.

I'm persuaded that if constructing the E throws, we should just let it bubble
out. We can't really do anything else.

But we still have the case of -fno-rtti -fno-exceptions case where we can't use
the newer non-throwing implementation of make_exception_ptr (because it uses
typeid) and we can't use the original throwing one (because it uses throw). So
that either has to terminate or return an empty exception_ptr, and either case
is a problem if you mix'n'match, so ...

> Do you think this case warrants a [[gnu::always_inline]]?

I now think the -fno-rtti -fno-exceptions case should be always_inline.

Here's what I'm considering ...

  /// Obtain an exception_ptr pointing to a copy of the supplied object.
#if (__cplusplus >= 201103L && __cpp_rtti) || __cpp_exceptions
  template<typename _Ex>
    exception_ptr
    make_exception_ptr(_Ex __ex) _GLIBCXX_USE_NOEXCEPT
    {
#if __cplusplus >= 201103L && __cpp_rtti
      using _Ex2 = typename decay<_Ex>::type;
      void* __e = __cxxabiv1::__cxa_allocate_exception(sizeof(_Ex));
      (void) __cxxabiv1::__cxa_init_primary_exception(
          __e, const_cast<std::type_info*>(&typeid(_Ex)),
          __exception_ptr::__dest_thunk<_Ex2>);
      __try
        {
          ::new (__e) _Ex2(__ex);
          return exception_ptr(__e);
        }
      __catch(...)
        {
          __cxxabiv1::__cxa_free_exception(__e);
          return current_exception();
        }
#else
      try
        {
          throw __ex;
        }
      catch(...)
        {
          return current_exception();
        }
#endif
    }
#else // no RTTI and no exceptions
  // This is always_inline so the linker will never use this useless definition
  // instead of a working one compiled with RTTI and/or exceptions enabled.
  template<typename _Ex>
    __attribute__ ((__always_inline))
    exception_ptr
    make_exception_ptr(_Ex) _GLIBCXX_USE_NOEXCEPT
    { return exception_ptr(); }
#endif

Because only the no-op implementation is always_inline, there won't be any
bloat caused by inlining the working implementation into a hot path.


More information about the Gcc-bugs mailing list