This is an unofficial snapshot of the ISO/IEC JTC1 SC22 WG21 Core Issues List revision 115d. See http://www.open-std.org/jtc1/sc22/wg21/ for the official list.

2024-10-26


1774. Discrepancy between subobject destruction and stack unwinding

Section: 14.3  [except.ctor]     Status: CD4     Submitter: Canada     Date: 2013-09-24

[Moved to DR at the November, 2014 meeting.]

N3690 comment CA 24

The current wording of 14.6.2 [except.terminate] paragraph 2 affords implementations a significant degree of freedom when exception handling results in a call to std::terminate:

In the situation where no matching handler is found, it is implementation-defined whether or not the stack is unwound before std::terminate() is called. In the situation where the search for a handler (14.4 [except.handle]) encounters the outermost block of a function with a noexcept-specification that does not allow the exception (14.5 [except.spec]), it is implementation-defined whether the stack is unwound, unwound partially, or not unwound at all before std::terminate() is called. In all other situations, the stack shall not be unwound before std::terminate() is called.

This contrasts with the treatment of subobjects and objects constructed via delegating constructos in 14.3 [except.ctor] paragraph 2:

An object of any storage duration whose initialization or destruction is terminated by an exception will have destructors executed for all of its fully constructed subobjects (excluding the variant members of a union-like class), that is, for subobjects for which the principal constructor (11.9.3 [class.base.init]) has completed execution and the destructor has not yet begun execution. Similarly, if the non-delegating constructor for an object has completed execution and a delegating constructor for that object exits with an exception, the object's destructor will be invoked.

Here the destructors must be called. It would be helpful if these requirements were harmonized.

Notes from the September, 2013 meeting:

Although the Canadian NB comment principally was a request to reconsider the resolution of issue 1424, which CWG decided to retain, the comment also raised the question above, which CWG felt merited its own issue.

Proposed resolution (June, 2014):

  1. Change all of 14.3 [except.ctor], reparagraphing as follows:

  2. As control passes from the point where an exception is thrown to a handler, destructors are invoked by a process, specified in this section, called stack unwinding. If a destructor directly invoked by stack unwinding exits with an exception, std::terminate is called (14.6.2 [except.terminate]). [Note: Consequently, destructors should generally catch exceptions and not let them propagate out of the destructor. —end note]

    The destructor is invoked for all automatic objects each automatic object of class type constructed since the try block was entered. The automatic objects are destroyed in the reverse order of the completion of their construction.

    An For an object of class type of any storage duration whose initialization or destruction is terminated by an exception will have destructors executed, the destructor is invoked for all each of its the object's fully constructed subobjects (excluding the variant members of a union-like class), that is, for subobjects each subobject for which the principal constructor (11.9.3 [class.base.init]) has completed execution and the destructor has not yet begun execution. The subobjects are destroyed in the reverse order of the completion of their construction. Such destruction is sequenced before entering a handler of the function-try-block of the constructor or destructor, if any.

    Similarly, if the non-delegating constructor for an object has completed execution and a delegating constructor for that object exits with an exception, the object's destructor will be is invoked. Such destruction is sequenced before entering a handler of the function-try-block of a delegating constructor for that object, if any.

    [Note: If the object was allocated in by a new-expression (7.6.2.8 [expr.new]), the matching deallocation function (6.7.5.5.3 [basic.stc.dynamic.deallocation], 7.6.2.8 [expr.new], 11.4.11 [class.free]), if any, is called to free the storage occupied by the object. end note]

    The process of calling destructors for automatic objects constructed on the path from a try block to the point where an exception is thrown is called “stack unwinding.” If a destructor called during stack unwinding exits with an exception, std::terminate is called (14.6.2 [except.terminate]). [Note: So destructors should generally catch exceptions and not let them propagate out of the destructor. —end note]

  3. Delete 14.4 [except.handle] paragraph 11:

  4. The fully constructed base classes and members of an object shall be destroyed before entering the handler of a function-try-block of a constructor for that object. Similarly, if a delegating constructor for an object exits with an exception after the non-delegating constructor for that object has completed execution, the object's destructor shall be executed before entering the handler of a function-try-block of a constructor for that object. The base classes and non-variant members of an object shall be destroyed before entering the handler of a function-try-block of a destructor for that object (11.4.7 [class.dtor]).

This resolution also resolves issue 1807.