Title
Use of decltype(capture) in a lambda's parameter-declaration-clause
Status
cd6
Section
7.5.4.2 [expr.prim.id.unqual]
Submitter
Barry Revzin

Created on 2022-04-16.00:00:00 last changed 20 months ago

Messages

Date: 2022-04-15.00:00:00

Additional notes (April, 2022):

Forwarded to EWG with paper issue 1227, by decision of the CWG chair.

See paper P2579R0 for a more detailed discussion of the issue.

Date: 2022-04-15.00:00:00

Additional notes (April, 2022):

Forwarded to EWG with paper issue 1227, by decision of the CWG chair.

See paper P2579R0 for a more detailed discussion of the issue.

Date: 2022-07-15.00:00:00

[Accepted at the July, 2022 meeting as part of paper P2579R0 (Mitigation strategies for P2036 "Changing scope for lambda trailing-return-type").]

Paper P2036R3 disallowed using captures in the parameter-declaration-clause of a lambda, because it is not yet known at that point whether the lambda is going to be mutable, and thus the type of an expression referring to a capture may or may not be const. Such problematic uses of captures are now ill-formed. The paper was approved as a Defect Report, recommending to implementers to apply the change to all language modes back to C++11.

However, that broke legitimate uses in popular implementations of the standard library such as:

  [local_end](decltype(local_end) it) { return it != local_end; };

As specified in 9.2.9.6 [dcl.type.decltype] bullet 1.3, decltype(local_end) does not depend on whether the lambda ends up being mutable or not:

  • otherwise, if E is an unparenthesized id-expression or an unparenthesized class member access (7.6.1.5 [expr.ref]), decltype(E) is the type of the entity named by E. If there is no such entity, the program is ill-formed;

Possible approaches (not necessarily exclusive):

  • Redesignate paper P2036R3 as a C++23 feature, not a Defect Report.
  • Carve out an exception for such and similar uses of captures, e.g. decltype(x) or sizeof(x) are acceptable when x names a capture.
  • Limit the lookup change effected by P2036R3 to the trailing-return-type (option 1 in the list presented in section 4.1 of P2036R3).
  • Accept as much existing code as possible by requiring to check for mutable before parsing the grammatical elements preceding the location where mutable can appear. This approach might work for the attribute-specifier-seq and the parameter-declaration-clause, but is a serious implementation hardship for the template-parameter-list and the first requires-clause. For example, [=]<X<decltype((a))>::a<0> may or may not be a complete template-parameter-list, depending on whether ...::a is a template.

Suggested resolution (carves out an exception for decltype, sizeof, noexcept):

Change in 7.5.4.2 [expr.prim.id.unqual] paragraph 3 as follows:

An unqualified-id is mutable-agnostic if it is the operand of a decltype (9.2.9.6 [dcl.type.decltype]), sizeof (7.6.2.5 [expr.sizeof]), or noexcept (7.6.2.7 [expr.unary.noexcept]). If the unqualified-id appears in a lambda-expression at program point P and the entity is a local entity (6.1 [basic.pre]) or a variable declared by an init-capture (7.5.5.3 [expr.prim.lambda.capture]), then let S be the compound-statement of the innermost enclosing lambda-expression of P. If naming the entity from outside of an unevaluated operand within S would refer to an entity captured by copy in some intervening lambda-expression, then let E be the innermost such lambda-expression, and:
  • If the unqualified-id is mutable-agnostic or P is in E's function parameter scope but not its parameter-declaration-clause, then the type of the expression is the type of a class member access expression (7.6.1.5 [expr.ref]) naming the non-static data member that would be declared for such a capture in the object parameter (9.3.4.6 [dcl.fct]) of the function call operator of E. [Note 3: If E is not declared mutable, the type of such an identifier will typically be const qualified. —end note]
  • Otherwise (if the unqualified-id is not mutable-agnostic and P either precedes E's function parameter scope or is in E's parameter-declaration-clause), the program is ill-formed.
...

[Example:

  [=]<decltype(x) P>{};    // ok: P has type float
  [=]<decltype((x)) P>{};  // error: x refers to local entity but precedes the
                           // lambda's function parameter scope
  [=](decltype((x)) y){};  // error: x refers to local entity but is in the lambda's
                           // parameter-declaration-clause

-- end example]

Suggested resolution (carves out an exception for decltype only):

Change in 7.5.4.2 [expr.prim.id.unqual] paragraph 3 as follows:

If the unqualified-id appears in a lambda-expression at program point P and the entity is a local entity (6.1 [basic.pre]) or a variable declared by an init-capture (7.5.5.3 [expr.prim.lambda.capture]), then let S be the compound-statement of the innermost enclosing lambda-expression of P. If naming the entity from outside of an unevaluated operand within S would refer to an entity captured by copy in some intervening lambda-expression, then let E be the innermost such lambda-expression, and:
  • If the unqualified-id is the operand of a decltype (9.2.9.6 [dcl.type.decltype]) or P is in E's function parameter scope but not its parameter-declaration-clause, then the type of the expression is the type of a class member access expression (7.6.1.5 [expr.ref]) naming the non-static data member that would be declared for such a capture in the object parameter (9.3.4.6 [dcl.fct]) of the function call operator of E. [Note 3: If E is not declared mutable, the type of such an identifier will typically be const qualified. —end note]
  • Otherwise (if the unqualified-id is not the operand of a decltype and P either precedes E's function parameter scope or is in E's parameter-declaration-clause), the program is ill-formed.
...

[Example:

  [=]<decltype(x) P>{};    // ok: P has type float
  [=]<decltype((x)) P>{};  // error: x refers to local entity but precedes the
                           // lambda's function parameter scope
  [=](decltype((x)) y){};  // error: x refers to local entity but is in the lambda's
                           // parameter-declaration-clause

-- end example]

History
Date User Action Args
2022-08-19 07:54:33adminsetstatus: review -> cd6
2022-07-10 07:18:13adminsetstatus: open -> review
2022-04-22 21:07:05adminsetmessages: + msg6807
2022-04-17 04:50:03adminsetmessages: + msg6800
2022-04-16 00:00:00admincreate