Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is calling a "noexcept function" through a "function" lvalue undefined?

[expr.call]/6:

Calling a function through an expression whose function type is different from the function type of the called function's definition results in undefined behavior.

void f() noexcept {}; // function type is "noexcept function"
void (*pf)() = f; // variable type is "pointer to function"; initialized by result of [conv.fctptr]([conv.func](f))
int main()
{
    (*pf)(); // `*pf`: lvalue expression's function type is "function" (without noexcept!)
}

Does the above call result in undefined behavior per the cited standardese?

like image 413
TSmith Avatar asked Jan 11 '21 22:01

TSmith


1 Answers

C++14 had a weaker requirement, from [expr.call]/6:

[...] Calling a function through an expression whose function type has a language linkage that is different from the language linkage of the function type of the called function's definition is undefined ([dcl.link]). [...]

However [expr.reinterpret.cast]/6 contained a similar, but stronger requirement:

A function pointer can be explicitly converted to a function pointer of a different type. The effect of calling a function through a pointer to a function type ([dcl.fct]) that is not the same as the type used in the definition of the function is undefined.

P0012R1 made exception specifications to be part of the type system, and was implemented for C++17

The exception specification of a function is now part of the function’s type: void f() noexcept(true); and void f() noexcept(false); are functions of two distinct types. Function pointers are convertible in the sensible direction. (But the two functions f may not form an overload set.) This change strengthens the type system, e.g. by allowing APIs to require non-throwing callbacks.

and moreover added [conv.fctptr]:

Add a new section after section 4.11 [conv.mem]:

4.12 [conv.fctptr] Function pointer conversions

A prvalue of type "pointer to noexcept function" can be converted to a prvalue of type "pointer to function". [...]

but included no changes to [expr.reinterpret.cast]/6; arguably an unintentional omission.

CWG 2215 highlighted the duplicated information in [expr.call] compared to [expr.reinterpret.cast]/6, flagging the weaker requirement in the former as redundant. The following cplusplus / draft commit implemented CWG 2215, and removed the weaker (redundant) requirement, made [expr.reinterpret.cast]/6 into a non-normative note and moved its (stronger) normative requirement to [expr.call]; eventually this stronger requirement was broken out into its own paragraph.

This confusion arguably lead to the unintentional (seemingly conflicting) rules that:

  • a prvalue of type “pointer to noexcept function” can be converted to a prvalue of type “pointer to function” ([conv.fctptr]/1), and
  • calling a function through an expression whose function type is different only by its exception specification is undefined behaviour.

Afaict, there are no defect reports covering this issue, and a new one should arguably be submitted.

like image 89
dfrib Avatar answered Nov 14 '22 23:11

dfrib