Make exception-specifications be part of the type system (original) (raw)
N4320
Jens Maurer Jens.Maurer@gmx.net
2014-11-20
Introduction
The exception specification of a function was never considered a part of its type. Instead, 15.4 [except.spec] paragraphs 5 and 6 only restrict assignments and initializations of pointers and references to (member) functions. This leads to odd holes in the type system that have resulted in a number of core issues.
Core issue 92 asks the fundamental question "Should exception-specifications be part of the type system?" and gives this example:
void (*p)() throw(int);
void (**pp)() throw() = &p; // not currently an error
This issue was closed as NAD in February 2014, because "EWG determined that no action should be taken on this issue" (quote from the core issues list). Little technical argument seems to have been presented; closing the issue was rather seen as warding off work.
Core issue 1946 "exception-specifications vs pointer dereference" highlighted that clarifications for determining potential exceptions of an expression brought forward for core issue 1351 caused this example to be ill-formed, which was arguably well-formed under the earlier imprecise wording:
void (*p)() throw(int); void (&r)() throw(int) = *p; // ill-formed
A similar example involving templates is impossible to fix without changes to the type system:
template T& f(T* p); void (*p)() throw(int); void (&r)() throw(int) = f(p); // ill-formed
Core issue 2010 "exception-specifications and conversion operators" adds these examples:
struct S { typedef void (*p)(); operator p(); }; void (*q)() noexcept = S();
void (*r)() noexcept = noexcept {};
The recent work on a specification for transactional memory support (see N4265 "Transactional Memory Support for C++: Wording (revision 3)") required integrating the transaction_safe
specifier into the types of functions, among other things considering it for implicit conversions and overload resolution. This paper clones that approach to integrate the presence or absence of a non-throwing exception-specification (called noexcept
) into the types of functions, too.
Wording
Clause 4 [conv]
Change in section 4.3 [conv.func] paragraph 1:
An lvalue of
functiontype"noexcept
opt function"Tcan be converted to a prvalue of type "pointer toT."T". The result is a pointer to the function. [ Footnote: ... ]
Drafting note: This ensures that overload resolution doesn't perceive dropping the "noexcept" as two conversions instead of just one. The same trick was applied for converting unscoped enumerations with fixed underlying type to the promoted underlying type (4.5p4).
Add a new section section 4.14 [conv.fctptr]:
4.14 [conv.fctptr] Function pointer conversions
A prvalue of type "pointer to
noexcept
function" can be converted to a prvalue of type "pointer to function". The result is a pointer to the function. A prvalue of type "pointer to member of typenoexcept
function" can be converted to a prvalue of type "pointer to member of type function". The result points to the member function.[ Example:
void (*p)() throw(int); void (**pp)() throw() = &p; // error
struct S { typedef void (*p)(); operator p(); }; void (*q)() noexcept = S(); // error
-- end example ]
Clause 5 [expr] Expressions
[ Note: ... ] The composite pointer type of two operands p1 and p2 having types T1 and T2, respectively, where at least one is a pointer or pointer to member type or
std::nullptr_t
, is:
- ...
- if T1 or T2 is "pointer to cv1 void" and the other type is "pointer to cv2 T", "pointer to cv12 void", where cv12 is the union of cv1 and cv2 ;
- if T1 is "pointer to
noexcept
function" and T2 is "pointer to function", where the function types are otherwise the same, T2, and vice versa;- ...
Change in 5.1.2 [expr.prim.lambda] paragraph 6:
The closure type for a non-generic lambda-expression with no_lambda-capture_ has a public non-virtual non-explicit const conversion function to pointer to function with C++ language linkage (7.5 [dcl.link]) having the same parameter and return types as the closure type's function call operator. That pointer is a pointer to
noexcept
function if the function call operator has a non-throwing exception specification.
Change in 5.2.9 [expr.static.cast] paragraph 7:
The inverse of any standard conversion sequence (Clause 4 [conv]) not containing an lvalue-to-rvalue (4.1 [conv.lval]), array-to-pointer (4.2 [conv.array]), function-to-pointer (4.3), null pointer (4.10), null member pointer (4.11),
orboolean (4.12), or function pointer (4.14 [conv.fctptr]) conversion, can be performed explicitly usingstatic_cast
. ...
Change in 5.10 [expr.eq] paragraph 2:
If at least one of the operands is a pointer, pointer conversions (4.10 [conv.ptr]), function pointer conversions (4.14 [conv.fctptr]), and qualification conversions (4.4 [conv.qual]) are performed on both operands to bring them to their composite pointer type (clause 5 [expr]).
Change in 5.16 [expr.cond] paragraph 6:
- One or both of the second and third operands have pointer type; pointer conversions (4.10 [conv.ptr]), function pointer conversions (4.14 [conv.fctptr]), and qualification conversions (4.4 [conv.qual]) are performed to bring them to their composite pointer type (5 [expr]). ...
- ...
Change in 5.17 [expr.throw]:
Evaluating a throw-expression with an operand throws an exception (15.1 [except.throw]); 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 "
noexcept
opt function returning T" to "pointer to T" or "pointer to "noexcept
opt function returning T," respectively.
Change in 5.20 [expr.const] paragraph 3:
... A converted constant expression of type T is an expression, implicitly converted to a prvalue of type T, where the converted expression is a core constant expression and the implicit conversion sequence contains only user-defined conversions, lvalue-to-rvalue conversions (4.1 [conv.lval]), integral promotions (4.5 [conv.prom]),
andintegral conversions (4.7 [conv.integral]) other than narrowing conversions (8.5.4 [dcl.init.list]), and function pointer conversions (4.14 [conv.fctptr]). [ Note: ... ]
Section 8 [dcl.decl] Declarators
Change in 8.3.5 [dcl.fct] paragraphs 1 and 2:
In a declaration T D where D has the form
D1 ( parameter-declaration-clause ) cv-qualifier-seqopt ref-qualifieropt exception-specificationopt attribute-specifier-seqopt
and the type of the contained declarator-id in the declaration T D1 is "derived-declarator-type-list T", the type of the_declarator-id_ in D is "derived-declarator-type-list
noexcept
opt function of (parameter-declaration-clause)_cv-qualifier-seqopt ref-qualifieropt_returning T", where the optionalnoexcept
is present if the exception-specification is non-throwing. The optional attribute-specifier-seq appertains to the function type.In a declaration T D where D has the form
D1 ( parameter-declaration-clause ) cv-qualifier-seqopt ref-qualifieropt exception-specificationopt attribute-specifier-seqopt trailing-return-type
and the type of the contained declarator-id in the declaration T D1 is "derived-declarator-type-list T", T shall be the single type-specifier auto. The type of the_declarator-id_ in D is "derived-declarator-type-list
noexcept
opt function of (parameter-declaration-clause) cv-qualifier-seqoptref-qualifieropt returning_trailing-return-type_", where the optionalnoexcept
is present if the exception-specification is non-throwing. The optional attribute-specifier-seq appertains to the function type.
Change in 8.3.5 [dcl.fct] paragraph 5:
... After determining the type of each parameter, any parameter of type "array of T" or "
noexcept
opt function returning T" is adjusted to be "pointer to T" or "pointer tonoexcept
opt function returning T," respectively. ...
Change in 8.3.5 [dcl.fct] paragraph 6:
... The return type, the parameter-type-list, the_ref-qualifier_,
andthe cv-qualifier-seq,and whether the function has a non-throwing_exception-specification_, but not the default arguments (8.3.6 [dcl.fct.default]) or the exception specification(15.4 [except.spec]), are part of the function type. ...
Change in section 8.4.1 [dcl.fct.def.general] paragraph 2:
The declarator in a function-definition shall have the form
D1 ~~( _parameter-declaration-clause_ ) _cv-qualifier-seqopt_~~
ref-qualifieropt exception-specificationopt attribute-specifier-seqoptparameters-and-qualifiers trailing-return-typeopt
[ Drafting note: This is intended to reduce the grammar redundancies around function declarators. ]
Clause 13 [over] Overloading
Change in 13.1 [over.load] paragraph 2:
Certain function declarations cannot be overloaded:
- Function declarations that differ only in the return type cannot be overloaded.
- Function declarations that differ only in the exception specification cannot be overloaded (15.4 [except.spec]).
- ...
In 13.3.3.1.1 [over.ics.scs], add an entry to table 12:
- Conversion: Function pointer conversion
- Category: Lvalue transformation
- Rank: Exact Match
- Subclause: 4.14 [conv.fctptr]
Change in 13.4 [over.over] paragraph 1:
...
The function selected is the one whose type is identical to the function type of the target type required in the context.A function with typeF
is selected for the function typeFT
of the target type required in the context if F (after possibly applying the function pointer conversion (4.14 [conv.fctptr])) is identical to FT.[ Note: ... ]
Change in 13.4 [over.over] paragraph 7:
[ Note:
There are no standard conversions (Clause 4) of one pointer-to-function type into another. In particular, evenEven if B is a public base of D, we haveD* f(); B* (p1)() = &f; // error void g(D); void (p2)(B) = &g; // error
]
Clause 14 [temp] Templates
Change in 14.1 [temp.param] paragraph 8:
A non-type template-parameter of type "array of T" or "
noexcept
opt function returning T" is adjusted to be of type "pointer to T" or "pointer tonoexcept
opt function returning T", respectively. [ Example: ... ]
Change in 14.8.2.1 [temp.deduct.call] paragraph 4:
... However, there are three cases that allow a difference:
- ...
- The transformed A can be another pointer or pointer to member type that can be converted to the deduced A via a qualification conversion (4.4 [conv.qual]) or a function pointer conversion (4.14 [conv.fctptr]).
- ...
Clause 15 [except] Exception handling
Change in 15.3 except.handle paragraph 3:
A handler is a match for an exception object of type E if
- ...
- the handler is of type cv T or const T& where T is a pointer type and E is a pointer type that can be converted to T by
either or both ofone or more of
- a standard pointer conversion (4.10 [conv.ptr]) not involving conversions to pointers to private or protected or ambiguous classes
- a qualification conversion (4.4 [conv.qual])
- a function pointer conversion (4.14 [conv.fctptr])
- ...
Change in 15.4 [except.spec] paragraph 1:
A function
declarationdeclarator lists exceptions that its function might directly or indirectly throw by using an exception-specification as a suffixof its declarator.
Change in 15.4 [except.spec] paragraph 2:
An exception-specification shall appear only on a function declarator for a function type, pointer to function type, reference to function type, or pointer to member function type that is the top-level type of a declaration or definition, or on such a type appearing as a parameter or return type in a function declarator. An exception-specification shall not appear in a typedef declaration or alias-declaration. [ Example: ... ]...
Change in 15.4 [except.spec] paragraph 5 and delete paragraph 6:
[ Example: ... ]
A similar restriction applies to assignment to and initialization of pointers to functions, pointers to member functions, and references to functions: the target entity shall allow at least the exceptions allowed by the source value in the assignment or initialization. [ Example: ... ]
In such an assignment or initialization,_exception-specification_s on return types and parameter types shall be compatible. In other assignments or initializations, _exception-specification_s shall be compatible.
Add a new paragraph after 15.4 [except.spec] paragraph 15:
A function with an implied non-throwing exception specification, where the function's type is declared to be T, is instead considered to be of type
noexcept
T.