g++ -fno-enforce-eh-specs - why/how does this violate the C++ standard?

581 views Asked by At

From man gcc:

  -fno-enforce-eh-specs
      Don't generate code to check for violation of exception specifications
      at run time.  This option violates the C++ standard, but may be useful
      for reducing code size in production builds.

When a compiler optimizes, it removes all sorts of checks, it breaks the boundaries between functions, it might even avoid things like system calls the developer put in the code in some cases. So:

  • Why is exception specification violation checking so special that it cannot be skipped?
  • What exactly is being checked?
  • If using this switch is so useful for reducing code size, how come the C++ standard requires these checks? I thought the idea is zero-overhead abstractions whenever possible.
2

There are 2 answers

2
Bo Persson On

The (now old?) standard requires that a function declared void f() throw(x); must not throw any exception other than x (or possibly derived from x?). If it tries to throw something else, std::unexpected() should be called, and probably end up in a call to std::terminate() to kill the program.

But if the exact type of the exception cannot be determined at compile time, a run-time check might be needed to determine if the exception is of an acceptable type.

Of course, if that check is removed by this option, an incorrect exception might escape from the function. And that would be a violation of the standard.

0
AudioBubble On

Why is exception specification violation checking so special that it cannot be skipped?
What exactly is being checked?

This has been answered already, but for completeness in my answer as well: for functions with throw(...) and noexcept specifications, when exceptions get thrown that are not permitted, std::unexpected() or std::terminate() must be called.

Effectively, they turn something like

void f();
void g() noexcept { f(); }

into something very similar to:

void f();
void g() { try { f(); } catch (...) { std::terminate(); } }

If using this switch is so useful for reducing code size, how come the C++ standard requires these checks? I thought the idea is zero-overhead abstractions whenever possible.

Indeed. But there is no way to implement this without overhead. Compilers do already manage to implement exceptions in such a way that the code that gets executed at run-time, as long as no exceptions are thrown, is identical to the version without exception handling. In that sense, on platforms with suitable ABIs, zero-overhead exceptions has been achieved. They, however, do still annotate that code with special markers that lets the run-time library figure out what code to call when exceptions do end up thrown. Those special markers take up space. The code that ends up interpreting those markers takes up space as well. And the actual exception handling code, even if it's only std::terminate(), takes up yet more space.