Title
Dynamically-unreachable handlers
Status
drafting
Section
14.4 [except.handle]
Submitter
2016-01-04

Created on 2017-02-06.00:00:00 last changed 81 months ago

Messages

Date: 2018-04-11.00:00:00

Consider the following example:

  #include <cstdio>
  #include <cstdlib>

  void f() {
    struct X {
     ~X() {
       std::puts("unwound");
       std::exit(0);
     }
    } x;
    throw 0;
  }

  int main(int argc, char**) {
    try {
      f();
    } catch (int) {
      std::puts("caught");
    }
  }

According to the Standard, this should print unwound and exit. Current optimizing implementations call terminate(), because:

  • f() obviously never unwinds (all paths out of it call exit)

  • Therefore the catch(int) clause is unreachable

  • Therefore the catch(int) clause is removed

  • When countering the throw 0; statement, the search for a handler finds nothing, so terminate is called without unwinding the stack.

More abstractly, before calling terminate, we're required to check whether there is an active handler for an exception of type int, and in some sense there is not (because the handler in main is dynamically unreachable).

There seem to be three possible solutions:

  1. Change the standard to say that terminate() is a valid response to this situation [this seems problematic, as any non-returning destructor now risks program termination, but is in fact the status quo on multiple implementations and does not seem to have resulted in any bug reports]

  2. Always fully unwind before calling terminate() [this significantly harms debugability of exceptions]

  3. Teach the compilers to not optimize out unreachable exception handlers [for some implementations, this is “remove, redesign and reimplement middle-end support for EH”-level difficult, and harms the ability to optimize code involving catch handlers]

History
Date User Action Args
2018-04-11 00:00:00adminsetstatus: open -> drafting
2017-02-06 00:00:00admincreate