C:\...>gcc -v Using built-in specs. Target: djgpp Configured with: /v203/gcc-4.32/configure msdosdjgpp Thread model: single gcc version 4.3.2 (GCC) Build command line: gpp -oexcpt -DTESTBADE excpt.C Execute command line: excpt The following source code is a pared down version of a short program I wrote to try the boundaries of the functions in <exception>. An exception object's constructor itself throws and catches an exception. After that, uncaught_exception always returns true. In the "Output:" comment at the end of the source file, places where uncaught_exception should return false are marked with "!!!". // excpt.C uses exceptions in strange and unusual ways // // build command line: gpp -oexcpt -DTESTBADE excpt.C // execute command line: excpt #include <iostream> #include <exception> void uncaught(int i = 0) { std::cerr << "[" << i <<"] std::uncaught_exception() = " << std::boolalpha << std::uncaught_exception() << std::endl; } class E { const char* descr; public: E(const char* str): descr(str) { std::cerr << "E::E(\"" << descr << "\")\n"; uncaught(); } E(const E& e): descr(e.descr) { std::cerr << "E::E(const E&)\n"; uncaught(); } ~E() { std::cerr << "E::~E()\n"; uncaught(); } const char* describe() { return descr; } }; /*****************************************************/ #ifdef TESTBADE class BadE { const char* str; public: BadE(const char* s) try : str(s) { std::cerr << "BadE::BadE(const char*) throwing \"thrown by Bade::BadE(const char*)\"\n"; throw "thrown by BadE::BadE(const char*)"; } catch (const char* str) { std::cerr << "BadE::BadE(const char*) caught \"" << str << "\"\n"; } ~BadE() { std::cerr << "BadE::~BadE()\n"; } const char* describe() { return str; } }; void throwBadE() { std::cerr << "throwBadE(): throw BadE(\"thrown by throwBadE()\")\n"; throw BadE("thrown by throwBadE()"); } #endif /*****************************************************/ void x(int i) { try { std::cerr << "[" << i <<"] x(int) throwing E(\"E from x(int) try block\")\n"; throw E("E from x(int) try block"); } catch (E e) { std::cerr << "[" << i <<"] x(int) entering catch (E) block\n"; uncaught(i); std::cerr << "[" << i <<"] x(int) caught E(\"" << e.describe() << "\") by value\n"; uncaught(i); std::cerr << "[" << i <<"] x(int) exiting catch (E) block\n"; } std::cerr << "[" << i << "] x(int) tries uncaught before exiting\n"; uncaught(i); } int main() { x(1); #ifdef TESTBADE /*****************************************************/ std::cerr << "*****************************************************\n"; try { std::cerr << "main() calling void throwBadE();\n"; try { throwBadE(); } catch (BadE& be) { std::cerr << "catch (BadE&) block => \"" << be.describe() << "\"\n"; } catch (const char* str) { std::cerr << "catch (const char*) block: " << str << "\n"; } catch (...) { std::cerr << "catch (...) block after calling void throwBadE();\n"; } } catch (...) { std::cerr << "catch (...) looking for leftover exceptions ???\n"; } /*****************************************************/ #endif std::cerr << "*****************************************************\n"; x(2); std::cerr << "*****************************************************\n"; std::cerr << "before returning from main() check uncaught\n"; uncaught(3); } /* Output: [1] x(int) throwing E("E from x(int) try block") E::E("E from x(int) try block") [0] std::uncaught_exception() = true E::E(const E&) [0] std::uncaught_exception() = true [1] x(int) entering catch (E) block [1] std::uncaught_exception() = false [1] x(int) caught E("E from x(int) try block") by value [1] std::uncaught_exception() = false [1] x(int) exiting catch (E) block E::~E() [0] std::uncaught_exception() = false E::~E() [0] std::uncaught_exception() = false [1] x(int) tries uncaught before exiting [1] std::uncaught_exception() = false ***************************************************** main() calling void throwBadE(); throwBadE(): throw BadE("thrown by throwBadE()") BadE::BadE(const char*) throwing "thrown by Bade::BadE(const char*)" BadE::BadE(const char*) caught "thrown by BadE::BadE(const char*)" catch (const char*) block: thrown by BadE::BadE(const char*) ***************************************************** [2] x(int) throwing E("E from x(int) try block") E::E("E from x(int) try block") [0] std::uncaught_exception() = true E::E(const E&) [0] std::uncaught_exception() = true [2] x(int) entering catch (E) block [2] std::uncaught_exception() = true !!! [2] x(int) caught E("E from x(int) try block") by value [2] std::uncaught_exception() = true !!! [2] x(int) exiting catch (E) block E::~E() [0] std::uncaught_exception() = true !!! E::~E() [0] std::uncaught_exception() = true !!! [2] x(int) tries uncaught before exiting [2] std::uncaught_exception() = true !!! ***************************************************** before returning from main() check uncaught [3] std::uncaught_exception() = true !!! */
Reduced: #include <cassert> #include <exception> struct GoodE { GoodE() { try { throw 1; } catch (...) { } } }; struct BadE { BadE() try { throw 1; } catch (...) { } }; int main() { try { throw GoodE(); } catch (...) { assert( !std::uncaught_exception() ); } try { throw BadE(); } catch (...) { assert( !std::uncaught_exception() ); } } Note that GoodE doesn't cause the problem. The difference is that BadE has a function-try-block
It looks as though uncaught_exception() does not always become false when entering the handler of a function-try-block, and this causes it to stay true. Maybe the count of uncaught exceptions is not decremented in the function-try-block's handler, but is incremented when the exception is rethrown at the end of the handler, causing it to be one more than it should not be.
(In reply to comment #2) > at the end of the handler, causing it to be one more than it should not be. Oops, obviously I meant "one more than it should be"
(In reply to comment #1) > Note that GoodE doesn't cause the problem. The difference is that BadE has a > function-try-block And, of course, that the exception is rethrown by BadE at the end of the handler. Changing GoodE to rethrow makes it have the same behaviour, so it's not specific to function-try-blocks. I give up for now, I'll just link to bug 10606 and http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#475 for reference
I think the problem is that the uncaught_exception() is true as soon as the memory for the exception has been allocated, but if the exception's copy constructor is elided then happens before entering the exception's constructor. If the exception constructor throws another exception then uncaughtExceptions goes to 2, and never goes back to zero. uncaught_exception() should return true after evaluating the operand of throw. If the operand cannot be constructed (because it throws) then that evaluation never completes. My understanding is that this should run to completeion, but all four assertions fail: #include <cassert> #include <exception> struct e { e() { assert( !std::uncaught_exception() ); try { throw 1; } catch (int i) { assert( !std::uncaught_exception() ); throw; } } }; int main() { try { throw e(); } catch (int i) { assert( !std::uncaught_exception() ); } assert( !std::uncaught_exception() ); }
Is this related to PR 37477?
(In reply to comment #6) > Is this related to PR 37477? It looks like a slightly different issue. PR 37477 relates to when uncaught_exception() stops being true and fixing it might need to wait for the resolution to core issue 475. This bug relates to when uncaught_exception() starts being true, and I don't think it depends on issue 475 (although it might make sense to wait for a resolution anyway.) The table in issue 475 would show today's GCC as "1 1 1 1 ABRT" and I believe that first "1" is wrong and is the root cause of this bug.
As a general rule, if we are sure about the dependency on a DR, we suspend the PR and remember the DR # in the Summary.
(In reply to comment #5) > I think the problem is that the uncaught_exception() is true as soon as the > memory for the exception has been allocated, but if the exception's copy > constructor is elided then happens before entering the exception's constructor. > If the exception constructor throws another exception then uncaughtExceptions > goes to 2, and never goes back to zero. After re-reading [except.throw] and [except.terminate] I think it is OK to elide the copy of the thrown object into the exception object here: throw e(); This means that uncaught_exception() can be true in the call e::e() *But* if the copy is elided and e::e() throws then std::terminate() should be called. This means the OP's testcase and my one in comment #5 should terminate, rather than exiting normally with std::uncaught_exception()==true This is because of the following in [except.terminate]/1 "when the exception handling mechanism, after completing evaluation of the expression to be thrown but before the exception is caught (15.1), calls a function that exits via an uncaught exception, 141" "141) For example, if the object being thrown is of a class with a copy constructor, std::terminate() will be called if that copy constructor exits with an exception during a throw." Now, either (A) e::e() happens before completing evaluation of the expression to be thrown and uncaught_exception() should be false during e::e() or (B) the copy is elided and e::e() happens after evaluating completing evaluation. In this case, uncaught_exception() should be true during e::e() but if it exits with an exception then std::terminate() should be called. GCC elides the copy, but does not call std::terminate() if e::e() exits with an exception. I don't think this interpretation will be changed by DR 475
(In reply to comment #9) > ... > "when the exception handling mechanism, after completing evaluation of the > expression to be thrown but before the exception is caught (15.1), calls a > function that exits via an uncaught exception, 141" > "141) For example, if the object being thrown is of a class with a copy > constructor, std::terminate() will be called if that copy constructor exits > with an exception during a throw." > ... I'm not sure that this applies in this situation. An instance of BadE is constructed because it is thrown, but BadE::BadE does not "[exit] via an uncaught exception". It both throws and catches an exception, and then returns normally. There is still an uncaught exception when BadE::BadE exits, but it is the one that caused BadE to be constructed, not the one that BadE::BadE has thrown (and caught).
(In reply to comment #10) > > I'm not sure that this applies in this situation. An instance of BadE is > constructed because it is thrown, but BadE::BadE does not "[exit] via an > uncaught exception". It both throws and catches an exception, and then returns > normally. There is still an uncaught exception when BadE::BadE exits, but it > is the one that caused BadE to be constructed, not the one that BadE::BadE has > thrown (and caught). No, in [except.handle]/16 the standard says "The exception being handled is rethrown if control reaches the end of a handler of the function-try-block of a constructor or destructor." You cannot swallow exceptions in a constructor's function-try-block, they will be rethrown. >
(In reply to comment #11) > (In reply to comment #10) > > > > I'm not sure that this applies in this situation. An instance of BadE is > > constructed because it is thrown, but BadE::BadE does not "[exit] via an > > uncaught exception". It both throws and catches an exception, and then returns > > normally. There is still an uncaught exception when BadE::BadE exits, but it > > is the one that caused BadE to be constructed, not the one that BadE::BadE has > > thrown (and caught). > > No, in [except.handle]/16 the standard says > > "The exception being handled is rethrown if control reaches the end of a > handler of the function-try-block of a constructor or destructor." > > You cannot swallow exceptions in a constructor's function-try-block, they will > be rethrown. > > > > 1,000 apologies, of course you are right; my own code and shows that (blush). Maybe uncaught_exception assumes that the throw BadE will succeed and doesn't notice that the BadE object is not fully constructed?
(In reply to comment #12) > ... > Maybe uncaught_exception assumes that the throw BadE will succeed and doesn't > notice that the BadE object is not fully constructed? > Apparently that is the case. Here's another test case where throw fails to throw an exception: #include <cstdlib> #include <iostream> #include <exception> void ae() { std::cerr << "At exit std::uncaught_exception() = " << std::boolalpha << std::uncaught_exception() << ".\n"; } struct NoE { NoE() { std::exit(0); } }; int main() { std::atexit(ae); throw NoE(); } /* Output: At exit std::uncaught_exception() = true. */
I'm asking Rth to have a look to this one, apparently unrelated to DR Core 475.
Author: jason Date: Mon Jan 27 13:57:39 2014 New Revision: 207129 URL: http://gcc.gnu.org/viewcvs?rev=207129&root=gcc&view=rev Log: Core DR 475 PR c++/41174 PR c++/59224 * libsupc++/eh_throw.cc (__cxa_throw): Set uncaughtExceptions. * libsupc++/eh_alloc.cc (__cxa_allocate_dependent_exception) (__cxa_allocate_exception): Don't set it here. Added: trunk/gcc/testsuite/g++.dg/eh/uncaught4.C Modified: trunk/gcc/testsuite/g++.dg/eh/uncaught1.C trunk/libstdc++-v3/ChangeLog trunk/libstdc++-v3/libsupc++/eh_alloc.cc trunk/libstdc++-v3/libsupc++/eh_throw.cc
Author: jason Date: Mon Jan 27 13:58:48 2014 New Revision: 207131 URL: http://gcc.gnu.org/viewcvs?rev=207131&root=gcc&view=rev Log: Core DR 475 PR c++/41174 PR c++/59224 * libsupc++/eh_throw.cc (__cxa_throw): Set uncaughtExceptions. * libsupc++/eh_alloc.cc (__cxa_allocate_dependent_exception) (__cxa_allocate_exception): Don't set it here. Added: branches/gcc-4_8-branch/gcc/testsuite/g++.dg/eh/uncaught4.C Modified: branches/gcc-4_8-branch/gcc/testsuite/g++.dg/eh/uncaught1.C branches/gcc-4_8-branch/libstdc++-v3/ChangeLog branches/gcc-4_8-branch/libstdc++-v3/libsupc++/eh_alloc.cc branches/gcc-4_8-branch/libstdc++-v3/libsupc++/eh_throw.cc
Fixed for 4.8.3/4.9.
Author: jason Date: Tue Apr 1 17:28:29 2014 New Revision: 208991 URL: http://gcc.gnu.org/viewcvs?rev=208991&root=gcc&view=rev Log: Core DR 475 PR c++/41174 PR c++/59224 * libsupc++/eh_throw.cc (__cxa_throw): Set uncaughtExceptions. * libsupc++/eh_alloc.cc (__cxa_allocate_dependent_exception) (__cxa_allocate_exception): Don't set it here. Added: branches/gcc-4_7-branch/gcc/testsuite/g++.dg/eh/uncaught4.C Modified: branches/gcc-4_7-branch/gcc/testsuite/g++.dg/eh/uncaught1.C branches/gcc-4_7-branch/libstdc++-v3/ChangeLog branches/gcc-4_7-branch/libstdc++-v3/libsupc++/eh_alloc.cc branches/gcc-4_7-branch/libstdc++-v3/libsupc++/eh_throw.cc