cplusplus / CWG

Core Working Group
23 stars 7 forks source link

[expr.const] p18.1 The call operator vs. compound-statement of a lambda #496

Open xmh0511 opened 5 months ago

xmh0511 commented 5 months ago

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

[expr.const] p18 says:

An immediate-escalating function is

  • the call operator of a lambda that is not declared with the consteval specifier, -[...]

An immediate-escalating expression shall appear only in an immediate-escalating function.

Consider this example:

int consteval fun(int){
    return 0;
}
int main(){
   auto f = [](){
       int a = 0;
      fun(a); // #1
   };
}

In this example, Clang accepts it while GCC rejects it. According to [expr.const] p16 and [expr.const] p17.2

An invocation is an immediate invocation if it is a potentially-evaluated explicit or implicit invocation of an immediate function and is not in an immediate function context.

An expression or conversion is immediate-escalating if it is not initially in an immediate function context and it is either

  • [...]
  • an immediate invocation that is not a constant expression and is not a subexpression of an immediate invocation.

#1 is an immediate-escalating expression. However, the compound-statement of a lambda is not the call operator of the lambda even though the former can yield the latter

The lambda-expression's compound-statement yields the function-body ([dcl.fct.def]) of the function call operator, but it is not within the scope of the closure type.

Moreover, the current wording just says "call operator of a lambda", how about the "call operator template of a lambda"?

Suggested Resolution

The intent meaning of call operator of a lambda may be

The compound-statement of a lambda that is not declared with the consteval specifier,

frederick-vs-ja commented 5 months ago

GCC implements P2564R3 since GCC 14 (doc), while the linked example tested an old version. The latest version accepts the code (Godbolt link), so there's no valid implementation divergence.

The compound-statement of a lambda that is not declared with the consteval specifier,

This looks weird. Neither a compound statement nor a lambda can be declared.

It's a bit surprising that [expr.prim.lambda.closure] p6 doesn't directly say the operator() is declared static, constexpr, or consteval if the corresponding keyword is used in lambda-specifier-seq. Presumably we should say this.

xmh0511 commented 5 months ago

The immediate-escalating expression fun(a) does appear(lexically) in compound-statement of a lambda rather than in the call operator of the lambda.

Neither a compound statement nor a lambda can be declared.

A lambda-specifier can have consteval.

frederick-vs-ja commented 5 months ago

The immediate-escalating expression fun(a) does appear(lexically) in compound-statement of a lambda rather than in the call operator of the lambda.

It would be weird if they are not equivalent. Presumably we should make them equivalent first (if they are not).

A lambda-specifier can have consteval.

There's nothing declaring a lambda (expression). A lambda-specifier (except for mutable whose absence is transformed into const) should be used for declaring the function call operator.

xmh0511 commented 5 months ago

A lambda-specifier (except for mutable whose absence is transformed into const) should be used for declaring the function call operator.

[expr.prim.lambda.closure] p6 explicitly says this:

The function call operator or any given operator template specialization is a constexpr function if either the corresponding lambda-expression's parameter-declaration-clause is followed by constexpr or consteval, or it is constexpr-suitable ([dcl.constexpr]). It is an immediate function ([dcl.constexpr]) if the corresponding lambda-expression's parameter-declaration-clause is followed by consteval.

Presumably we should make them equivalent first (if they are not).

IMO, they are not, whether from the perspective of grammar or semantic, which is described in [expr.prim.lambda.closure] p14.

frederick-vs-ja commented 5 months ago

[expr.prim.lambda.closure] p6 explicitly says this:

The function call operator or any given operator template specialization is a constexpr function if either the corresponding lambda-expression's parameter-declaration-clause is followed by constexpr or consteval, or it is constexpr-suitable ([dcl.constexpr]). It is an immediate function ([dcl.constexpr]) if the corresponding lambda-expression's parameter-declaration-clause is followed by consteval.

I think at least the wording can be simplified if the effects of static, constexpr, are consteval are specified via the usage in the declaration of the function call operator.

IMO, they are not, whether from the perspective of grammar or semantic, which is described in [expr.prim.lambda.closure] p14.

This would make correct wording unintuitive and verbose, which is the major issue here to me.

xmh0511 commented 5 months ago

This would make correct wording unintuitive and verbose, which is the major issue here to me.

In this example, the immediate-escalating expression appear in the compound-statement of the lambda, which violates

shall appear only in an immediate-escalating function.

compound-statement is not a function.