N4133
Jens Maurer
2014-09-10

N4133: Cleanup for exception-specification and throw-expression

Introduction

This paper proposes to adjust the wording for exceptions to achieve the following goals:

The changes are intended to be editorial only, not changing semantics. Due to the size of the changes, it seems prudent to have a full CWG review for these instead of leaving the issue to the project editor alone.

Miscellaneous

Change in 3.7.4.1 basic.stc.dynamic.allocation paragraph 3:
... If an allocation function declared with that has a non-throwing exception-specification exception specification (15.4 except.spec) fails to allocate storage, it shall return a null pointer. Any other allocation function that fails to allocate storage shall indicate failure only by throwing an exception (15.1 except.throw) of a type that would match a handler (15.3) of type std::bad_alloc (18.6.2.1).
Change in 5.3.4 expr.new paragraph 15:
[ Note: unless an allocation function is declared with has a non-throwing exception-specification exception specification (15.4 except.spec), it indicates failure to allocate storage by throwing a std::bad_alloc exception (3.7.4.1 basic.stc.dynamic.allocation, Clause 15 except, 18.6.2.1); it returns a non-null pointer otherwise. If the allocation function is declared with has a non-throwing exception-specification exception specification, it returns null to indicate failure to allocate storage and a non-null pointer otherwise. -- end note ] If the allocation function returns null, initialization shall not be done, the deallocation function shall not be called, and the value of the new-expression shall be null.
Change in 8.4.2 dcl.fct.def.default paragraph 2: Change in 8.4.2 dcl.fct.def.default paragraph 3:
If a function that is explicitly defaulted has an explicit exception-specification is declared with an exception specification that is not compatible (15.4 except.spec) with the exception-specification exception specification on the implicit declaration, then ...
Change in 12.1 class.ctor paragraph 5:
... [ Note: An implicitly-declared default constructor has an exception-specification exception specification (15.4 except.spec). An explicitly-defaulted definition might have an implicit exception-specification exception specification, see 8.4 dcl.fct.def. -- end note ]
Change in 12.4 class.dtor paragraph 3:
A declaration of a destructor that does not have an exception-specification is implicitly considered to have the same exception-specification as an implicit declaration has the same exception specification as if had been implicitly declared (15.4 except.spec).
Change in 12.5 class.free paragraph:
[ Note: If a deallocation function has no explicit exception-specification, it is treated as if it were specified with noexcept(true) has a non-throwing exception specification (15.4 except.spec). -- end note ]
Change in 12.8 class.copy paragarph 14:
... [ Note: An implicitly-declared copy/move constructor has an exception-specification implied exception specification (15.4 except.spec). -- end note ]
Change in 12.9 class.inhctor paragraph 3:
[ Note: Default arguments are not inherited. An exception-specification exception specification is implied as specified in 15.4 except.spec. -- end note ]

5.17 Throwing an exception [expr.throw]

Insert the section immediately before 5.17 [expr.ass], renumbering the sections that follow.

Move the following grammar production from 15 except paragraph 1:
throw-expression:
      throw assignment-expressionopt
Move the following from 15 except paragraph 2:
A throw-expression is of type void.
Move the following sentence from 15.1 except.throw paragraph 3:
Evaluating a throw-expression with an operand throws an exception; the type of the exception object is determined by removing any top-level cv-qualifiers from the static type of the operand and adjusting the type from "array of T" or "function returning T" to "pointer to T" or "pointer to function returning T," respectively.
Move from 15.1 except.throw paragraphs 8 and 9, changing the highlighted parts::

A throw-expression with no operand rethrows the currently handled exception (15.3 except.handle). The exception is reactivated with the existing exception object; no new exception object is created. The exception is no longer considered to be caught; therefore, the value of std::uncaught_exception() (15.5.3 except.uncaught) will again be true. [ Example: code that must be executed because of an exception yet cannot completely handle the exception can be written like this:

   try {
       // ...
   } catch (...) {        // catch all exceptions
     // respond (partially) to exception
     throw;               // pass the exception to some
                          // other handler
   }
-- end example ]

If no exception is presently being handled, executing evaluating a throw-expression with no operand calls std:: terminate() (15.5.1 except.terminate).

Clause 15 Exceptions

Remove from 15 except paragraph 1:
throw-expression:
      throw assignment-expressionopt

Remove from 15 except paragraph 2:
A try-block is a statement (Clause 6 stmt). A throw-expression is of type void. [ Note: Within this Clause "try block" is taken to mean both try-block and function-try-block. -- end note ]
Change in 15.1 except.throw paragraph 1:
Throwing an exception transfers control to a handler. [ Note: An exception can be thrown from one of the following contexts: throw-expression (see below5.17 expr.throw), allocation functions (3.7.4.1), dynamic_cast (5.2.7), typeid (5.2.8), new-expression (5.3.4), and standard library functions (17.5.1.4). -- end note ] ...
Remove from 15.1 expr.throw paragraph 3:
... Evaluating a throw-expression with an operand ..., respectively.
Remove from 15.1 except throw paragraphs 8 and 9:

A throw-expression with no operand ...

If no exception is presently...

Change in 15.4 except.spec paragraph 1:
The exception specification of a function is a (possibly empty) set of types, indicating that the function might exit via an exception that matches a handler of one of the types in the set; the (conceptual) set of all types is used to denote that the function might exit via an exception of arbitrary type. If the set is empty, the function is said to have a non-throwing exception specification. The exception specification is either defined explicitly A function declaration lists exceptions that its function might directly or indirectly throw by using an exception-specification as a suffix of its a function declaration's declarator (8.3.5 dcl.fct) or implicitly. ... A noexcept-specification noexcept is equivalent to noexcept(true).
Replace 15.4 except.spec paragraph 2:
... A type denoted in an exception-specification a dynamic-exception-specification shall not denote an incomplete type or an rvalue reference type. A type denoted in an exception-specification a dynamic-exception-specification shall not denote a pointer or reference to an incomplete type, other than cv void* "pointer to cv void". A type cv T, "array of T", or "function returning T" denoted in an exception-specification a dynamic-exception-specification is adjusted to type T, "pointer to T", or "pointer to function returning T", respectively. A dynamic-exception-specification denotes an exception specification that is the set of adjusted types specified thereby.
Change in 15.4 except.spec paragraph 3:
The exception-specification noexcept or noexcept(constant-expression), where the constant-expression yields true, denotes an exception specification that is the empty set. The exception-specification noexcept(constant-expression), where the constant-expression yields false, or the absence of an exception-specification in a function declarator other than that for a destructor (12.4 class.dtor) or a deallocation function (3.7.4.2 basic.stc.dynamic.deallocation) denotes an exception specification that is the set of all types.

Two exception-specifications are compatible if: the sets of types they denote are the same.

Change in 15.4 except.spec paragraph 5:
If a virtual function has an exception-specification exception specification, all declarations, including the definition, of any function that overrides that virtual function in any derived class shall only allow exceptions that are allowed by the exception-specification exception specification of the base class virtual function. [ Example: ... ]
Change in 15.4 except.spec paragraph 8:
A function is said to allow an exception of type E if the constant-expression in its noexcept-specification evaluates to false or its dynamic-exception-specification its exception specification contains a type T for which a handler of type T would be a match (15.3 except.handle) for an exception of type E. A function is said to allow all exceptions if its exception specification is the set of all types.
Change in 15.4 except.spec paragraph 9:
Whenever an exception is thrown and the search for a handler (15.3 except.handle) encounters the outermost block of a function with an exception-specification exception specification that does not allow the exception, then, ... [ Note: A function can have multiple declarations with different non-throwing exception-specifications; for this purpose, the one on the function definition is used. -- end note ]
Change in 15.4 except.spec paragraph 10:
The function std::unexpected() may throw an exception that will satisfy the exception-specification is allowed by the exception specification of the function for which it was invoked, and in this case the search for another handler will continue at the call of the that function with this exception-specification (see 15.5.2), or it may call std::terminate().
Change in 15.4 except.spec paragraph 12:
A function with no exception-specification or with an exception-specification of the form noexcept(constant-expression ) where the constant-expression yields false allows all exceptions. An exception-specification is non-throwing if it is of the form throw(), noexcept, or noexcept(constant-expression ) where the constant-expression yields true. A function with a non-throwing exception-specification does not allow any exceptions.
Change in 15.4 except.spec paragraph 13:
[ Note: An exception-specification exception specification is not considered part of a function's type; see 8.3.5 dcl.fct. -- end note ]
Change in 15.4 except.spec paragraph 15:
A deallocation function (3.7.4.2) with no explicit exception-specification has an exception specification that is the empty set is treated as if it were specified with noexcept(true).
Change in 15.5.1 except.terminate paragraph 1:
Change in 15.5.2 except.unexpected paragraph 1:
If a function with a dynamic-exception-specification throws exits via an exception that is not listed in the dynamic-exception-specification allowed by its exception specification, the function std::unexpected() is called (D.11) immediately after completing the stack unwinding for the former function.
Change in 15.5.2 except.unexpected paragraph 3:
.... If it throws or rethrows exits via an exception that the dynamic-exception-specification does not allow then the following happens: If the dynamic-exception-specification does not include the class std::bad_exception (18.8.2) then the function std::terminate() is called, otherwise the thrown exception is replaced by an implementation-defined object of the type std::bad_exception and the search for another handler will continue at the call of the function whose dynamic-exception-specification was violated.

[ Note: Thus, a dynamic-exception-specification guarantees that a function exits only via the listed exceptions will be thrown. If the dynamic-exception-specification includes the type std::bad_exception then any exception not on the list may be replaced by std::bad_exception within the function std::unexpected(). -- end note ]

Change in 17.6.5.12 res.on.exception.handling paragraph 1:
Any of the functions defined in the C++ standard library can report a failure by throwing an exception of a type described in its Throws: paragraph. An implementation may strengthen the exception-specification exception specification for a non-virtual function by adding a non-throwing noexcept-specification.