cplusplus / CWG

Core Working Group
23 stars 7 forks source link

[basic.def.odr] p14.11 "present in the definition of D" is ambiguous #409

Open xmh0511 opened 1 year ago

xmh0511 commented 1 year ago

Full name of submitter (unless configured in github; will be published with the issue): Jim X

[basic.def.odr] p14.11 says:

In each such definition, a default argument used by an (implicit or explicit) function call or a default template argument used by an (implicit or explicit) template-id or simple-template-id is treated as if its token sequence were present in the definition of D; that is, the default argument or default template argument is subject to the requirements described in this paragraph (recursively).

According to [basic.def] p2, a definition of D is also D's declaration, then according to grammar, all default arguments are presented in the declaration of D, so this is true to say the default arguments are presented in the definition of D, so why do we need to assume an entity that is presented in the definition as if it were presented in that definition? This is obscure. According to the context, we may want to interpret the "definition" to the body of a function.

Furthermore, the [basic.def.odr] p14.11 has its issue that

that is, the default argument or default template argument is subject to the requirements described in this paragraph (recursively).

What is the intent of this wording? Do we need to check whether the closure type satisfies the requirements for the default argument if it is a closure expression because of the wording "recursively"?

Presumably, we only need to check whether the consisted token sequences are the same. The relevant issue is https://github.com/cplusplus/CWG/issues/272

jensmaurer commented 1 year ago

Presumably, we only need to check whether the consisted token sequences are the same.

No, we also need to check lookup results and same-partial-specializations etc.

jensmaurer commented 1 year ago

The point of [basic.def.odr] p14.11 is "used by a function call (in D)":

Default arguments of a called function (if used) become part of the odr-check of the caller (in addition to being checked for the callee, it seems).

xmh0511 commented 1 year ago

So, in the context "as if its token sequence were present in the definition of D;", do we check the associated type or something else regarding the token sequence, such as the token sequence composes a lambda-expression, just in the same way as we do for the entity that is defined in the definition? This is the concern in https://github.com/cplusplus/CWG/issues/272.

Or, the intent is that: if we explicitly exclude "entities defined within default arguments or default template arguments", the entities whose token sequences are assumed to be in the definition will be excluded by the rule.

xmh0511 commented 1 year ago

It is even that we cannot find the formal rule that can interpret the following explanation

If the definition of g appears in multiple translation units, the program is ill-formed (no diagnostic required) because each such definition uses a default argument that refers to a distinct lambda-expression closure type.

inline void g(bool cond, void (*p)() = []{}) {
  if (cond) g(false);
}

[basic.def.odr] p14.11 says:

is treated as if its token sequence were present in the definition of D; that is, the default argument or default template argument is subject to the requirements described in this paragraph (recursively).

Then [basic.def.odr] p14.4 says:

Each such definition shall consist of the same sequence of tokens, where the definition of a closure type is considered to consist of the sequence of tokens of the corresponding lambda-expression.

They have the same token sequence, hence their closure types are the same. Moreover, [basic.def.odr] p14.6 explicitly says it does not apply to default argument

In each such definition, except within the default arguments and default template arguments of D, corresponding lambda-expressions shall have the same closure type (see below).

So, there is no rule that requires the lambda-expression appearing in the default argument shall have the same type, anyway.

jensmaurer commented 1 year ago

They have the same token sequence, hence their closure types are the same.

That is, in general, not the case.

Closure types can differ unless a surrounding ODR application (e.g. to an enclosing class) causes the "single definition" interpretation.

There is nothing that would cause the "single definition" interpretation for the default argument, so the values of the default arguments can differ. When we're using that default argument, we're inlining the definition of the closure type into the definition of the g (per the default argument rule), which causes the ODR check on g to fail.

xmh0511 commented 1 year ago

There is nothing that would cause the "single definition" interpretation for the default argument, so the values of the default arguments can differ. When we're using that default argument, we're inlining the definition of the closure type into the definition of the g (per the default argument rule), which causes the ODR check on g to fail.

This is the issue here. There is no bullet imposing the requirement that the closure type of the lambda-expression in the default argument shall have the same type. After all, the only bullet that requires the lambda-expression to have the same type is [basic.def.odr] p14.6

In each such definition, except within the default arguments and default template arguments of D, corresponding lambda-expressions shall have the same closure type (see below).

However, it has explicitly excluded it from applying to the lambda-expression within the default arguments.

jensmaurer commented 1 year ago

This is the issue here. There is no bullet imposing the requirement that the closure type of the lambda-expression in the default argument shall have the same type.

And that's how we want it to be, so the status quo is good, right?

xmh0511 commented 1 year ago

And that's how we want it to be, so the status quo is good, right?

If there is no bullet in the list imposing the requirement, that they shall be the same, on the closure type of the lambda-expression in the default argument, then the explanation is wrong

If the definition of g appears in multiple translation units, the program is ill-formed (no diagnostic required) because each such definition uses a default argument that refers to a distinct lambda-expression closure type.

The example

inline void g(bool cond, void (*p)() = []{}) {
  if (cond) g(false);
}

should be ok.