Created attachment 29123 [details] Test program to illustrate the problem When a noexcept function is "violated", std::terminate is called. While the standard allows for the stack to be fully unwound, partially unwound, or not unwound at all, it is impossible to find the location where the exception was thrown, unless the stack is left unwound. GCC currently leaves the stack unwound in most cases, but in a very common case, which is illustrated by the attached example program, the stack is partially unwound. If the noexcept is removed to allow the exception to propagate all the way past main, std::terminate is called with the stack fully unwound, so it seems that GCC is able to do what I want. It would be highly desirable for the noexcept case to work in the same way as the unhandled exception case. # g++ -std=c++0x -g tmp3.cpp # gdb a.out [...] (gdb) bt #0 0x000000318f036285 in raise () from /lib64/libc.so.6 #1 0x000000318f037b9b in abort () from /lib64/libc.so.6 #2 0x00000031964bbc5d in __gnu_cxx::__verbose_terminate_handler() () from /usr/lib64/libstdc++.so.6 #3 0x00000031964b9e16 in ?? () from /usr/lib64/libstdc++.so.6 #4 0x00000031964b8e49 in ?? () from /usr/lib64/libstdc++.so.6 #5 0x00000031964b973d in __gxx_personality_v0 () from /usr/lib64/libstdc++.so.6 #6 0x000000319000f6fb in ?? () from /lib64/libgcc_s.so.1 #7 0x000000319000fb58 in _Unwind_Resume () from /lib64/libgcc_s.so.1 #8 0x0000000000400911 in level2 () at tmp3.cpp:16 #9 0x000000000040091a in level1 () at tmp3.cpp:20 #10 0x0000000000400925 in main () at tmp3.cpp:24 As you can see, bad_guy is not in the backtrace. There are two simple modifications that makes the backtrace complete. The first is to remove the noexcept from level1, and the second is to remove the Foo declaration in level2 (or delete Foo's destructor).
I guess Jason can say whether this is a bug in the current behaviour or if your request should be severity=enhancement, but at the very least we need to document the intended behaviour at http://gcc.gnu.org/onlinedocs/gcc/Exception-handling.html
If you're looking for 'bad_guy' to be in the backtrace, that sounds to me like you want it to not be unwound at all, and I'm surprised that it doesn't already work that way.
Yes, I want 'bad_guy' to be in the backtrace, so you are correct that I don't want the stack to be unwound at all, but unfortunately it is. It seems to be because of the Foo object somehow. If I remove the Foo object (or it's destructor), 'bad_guy' is in the backtrace.
This is the ABI problem. The search phase of the unwinding process cannot distinguish between a case when a true handler found and a case when the search process should stop, because it has reached a call site that must not pass the exception through. In both cases _URC_HANDLER_FOUND is returned by the personality routine, and the second phase of the unwinding process begins (the actual unwinding). There is another problem in LSDA format (in action table format to be precise). Consider the following example: void bad_guy() noexcept { try { Foo foo; throw 0; } catch (float) { // Don't catch int. } } void level1() { bad_guy(); throw "dead code"; } int main() { try { level1(); } catch (const char *) { } } When you compile and run it you get: Foo::Foo() terminate called after throwing an instance of 'int' Aborted (core dumped) It works as expected. But (the funny thing) if you change "catch (const char *)" in main to "catch (int)", you will get this: Foo::Foo() Foo::~Foo() terminate called without an active exception Aborted (core dumped) This is because there is currently no way to represent a "Terminate" action in the action table of LSDA. gcc represents it as a simply cleanup action, that means that the unwinder does not stop at a noexcept function and continues searching for a handler up the stack. In the original example there is no handler for the exception thrown, and the runtime terminates the program instantly (no stack unwinding done). When one changes "catch (const char *)" to "catch (int)", the unwinder finds this handler at the search phase and launches the second, unwinding, phase. The unwinding process then destroys the local object "foo", checks the exception type for matching "float", and falls into the code that calls std::terminate when the matching fails. Since there is no __cxa_begin_catch prior to std::terminate, we get the error message "terminate called without an active exception" instead of correct "terminate called after throwing an instance of 'int'".
The originally-reported program still appears to exhibit the issue. Just looking at the unwind code -- and without attempting to test anything -- it appears that the solution may be as simple as inserting two lines: if (found_type == found_terminate) return _URC_FATAL_PHASE1_ERROR; at line 651 of eh_personality.cc (https://github.com/gcc-mirror/gcc/blob/b85a03ae11b157d60ddf93b71632efe0bde3bafd/libstdc%2B%2B-v3/libsupc%2B%2B/eh_personality.cc#L651) I believe that would abort the search phase (similarly to what happens if you run out of unwind frames before finding any catch handler) and cause _Unwind_RaiseException to return _URC_FATAL_PHASE1_ERROR, which then causes _cxa_throw to call terminate().
I realize that my suggestion above could only solve _most_ of the problem -- e.g. the original example, where the noexcept function doesn't have a try/catch in it. In that original example, there's no entry for the IP of the call in the LSDA table. The personality fn already knows during phase1 that this indicates termination -- but it simply fails to actually trigger the terminate in phase1, even though it easily could (& should!). However, in the example from comment #4, there _WILL_ be an entry in the callsite table covering the throw (necessarily, in order to to catch type "float"), so the "missing callsite => terminate" mechanism isn't applicable in that case. As the comment mentioned, to handle that, we'd need some alternative indication for termination which can be put in the action list. ISTM this could be done most straightforwardly by using an action record with a ttype pointing to a new ABI-defined special-purpose symbol (e.g. "__cxxabiv1::__eh_noexcept"). In libsupc++'s impl, that symbol can be an object whose type is a new std::type_info subclass, whose __do_catch overload calls terminate. Thus, when the personality fn falls through all the _actual_ catch action records, and comes to this, last one, it will query whether it can catch an exception by calling its __do_catch, and immediately trigger termination. GCC, then, can emit that as the last "catch" action in the chain for a try/catch in a noexcept function (instead of the cleanup action with code that calls std::terminate explicitly in the cleanup, that it does now).
I have written a GCC plugin (available at https://github.com/m42a/gcc-noexcept-plugin ) based on comment #6 that consistently prevents stack unwinding in this case.
As discussed on PR97720, I think we could address this by changing noexcept regions to be represented as exception-specifications in the action table, and for C++17 and up using a different personality routine that knows that exception-specifications are noexcept.