Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[expr.prim.lambda.capture] Which entity does the id-expression captured by reference refer to after evaluating the lambda-expression? #4619

Open
xmh0511 opened this issue May 31, 2021 · 11 comments

Comments

@xmh0511
Copy link
Contributor

xmh0511 commented May 31, 2021

[expr.prim.lambda#capture-15] When the lambda-expression is evaluated, the entities that are captured by copy are used to direct-initialize each corresponding non-static data member of the resulting closure object, and the non-static data members corresponding to the init-captures are initialized as indicated by the corresponding initializer (which may be copy- or direct-initialization). (For array members, the array elements are direct-initialized in increasing subscript order.) These initializations are performed in the (unspecified) order in which the non-static data members are declared.

It is underspecified about which entity the id-expression captured by reference refers to when the lambda-expression is evaluated. For instance:

void fun(){
  int a;
 auto f = [&a](){
    a = 1; //#3
 };
}
int main(){
  fun(); //#1
  fun(); //#2
}

In major implementations, each invocation of the function fun will make that the id-expression a at #3 denotes the different object. In other words, the evaluation of the prvalue lambda-expression will initialize its result object which is f, however, the standard does not specify what object the id-expression a will refer to in every result function object f.

The rule for an entity that is captured by reference is defined as the following:

[expr.prim.lambda#capture-12] An entity is captured by reference if it is implicitly or explicitly captured but not captured by copy. It is unspecified whether additional unnamed non-static data members are declared in the closure type for entities captured by reference. If declared, such non-static data members shall be of literal type.

It does not explicitly specify the otherwise case that there's no additional unnamed non-static data member to be declared. So, we may unclear know what entity the id-expression refers to when the lambda-expression is evaluated, where the id-expression is captured by reference. As far as I know, the following rule does not apply to the above example as well.

[expr.prim.lambda#capture-13] An id-expression within the compound-statement of a lambda-expression that is an odr-use of a reference captured by reference refers to the entity to which the captured reference is bound and not to the captured reference.

Since in the simple-capture &a where a is not a reference. Instead, it's an object.

In addition, even though the unnamed non-static data member is declared for such capture(captured by reference), I think the following rule is still not suitable for that case

[expr.prim.lambda#capture-11] Every id-expression within the compound-statement of a lambda-expression that is an odr-use of an entity captured by copy is transformed into an access to the corresponding unnamed data member of the closure type.

Also, [expr.prim.lambda#capture-15] does not specify how the initialization would be for the unnamed non-static data member declared for the case captured by reference.

Generally, the standard has no wording about what the id-expression would denote if it's reference capture in two cases(has/has no the corresponding unnamed non-static data member).

@xmh0511 xmh0511 changed the title The description for which entity the id-expression captured by reference refers to after evaluting the lambda-expression The description for which entity the id-expression captured by reference refers to after evaluating the lambda-expression May 31, 2021
@xmh0511 xmh0511 changed the title The description for which entity the id-expression captured by reference refers to after evaluating the lambda-expression The description for which entity does the id-expression captured by reference refer to after evaluating the lambda-expression Jun 1, 2021
@languagelawyer
Copy link
Contributor

... the standard does not specify what object the id-expression a will refer to in every result function object f.

You mean that the context in which variable a is mapped to the object is not tied to the closure object? I don't think this issue is solvable without variable/object divorce. There is just no adequate language to speak about this.

expr.prim.lambda#capture-12 ... It does not explicitly specify the otherwise case that there's no additional unnamed non-static data member to be declared.

Where do you want to put the «otherwise» part?

Generally, the standard has no wording about what the id-expression would denote if it's reference capture in two cases(has/has no the corresponding unnamed non-static data member).

Why the presence of the corresponding NSDM is relevant when such id-expression is never transformed to member access?

@xmh0511
Copy link
Contributor Author

xmh0511 commented Jun 1, 2021

... the standard does not specify what object the id-expression a will refer to in every result function object f.

You mean that the context in which variable a is mapped to the object is not tied to the closure object? I don't think this issue is solvable without variable/object divorce. There is just no adequate language to speak about this.

Since the wording is lack for the case of the reference capture when the prvalue(lambda-expression) is evaluated, it should clearly specify what the id-expression will denote(captured by reference) . [expr.prim.lambda#capture-15] totally talks about the case of captured by copy, regardless of simple-capture or init-capture

expr.prim.lambda#capture-12 ... It does not explicitly specify the otherwise case that there's no additional unnamed non-static data member to be declared.

Where do you want to put the «otherwise» part?

Maybe otherwise part would clarify what entity the id-expression denotes in the compound-statement of the lambda-expression(or in the function body of the call operator of the result object), after all, in this case, there's no unnamed NSDN. Such as:

Otherwise, the id-expression in the compound-statement denotes the entity named by the id-expression when the lambda-expression is evaluated.

It could tie the entity named by the id-expression to the id-expression that appears in the call operator function's body of the result object of the lambda-expression.

Generally, the standard has no wording about what the id-expression would denote if it's reference capture in two cases(has/has no the corresponding unnamed non-static data member).

Why the presence of the corresponding NSDM is relevant when such id-expression is never transformed to member access?

Since [expr.prim.lambda#capture-12] says

If declared, such non-static data members shall be of literal type.

The implementation can adopt the suggestion to declare an NSDM for this purpose as long as the member is of literal type. So, in this case, the standard should clarify how the member will be initialized when the lambda-expression is evaluated and how the id-expression in the function body will be transformed.

@languagelawyer
Copy link
Contributor

I still don't get why the description of how an id-expression is evaluated in capture-by-reference case should depend on the presence of the corresponding NSDM.

@xmh0511
Copy link
Contributor Author

xmh0511 commented Jun 1, 2021

I still don't get why the description of how an id-expression is evaluated in capture-by-reference case should depend on the presence of the corresponding NSDM.

Because the standard does not forbid the implementation to declare NSDM for this purpose(capture-by-reference case), since it can declare the member, the use of the id-expression should transform to the corresponding unnamed data member of the closure type; as the same manner as the following:

Every id-expression within the compound-statement of a lambda-expression that is an odr-use of an entity captured by copy is transformed into an access to the corresponding unnamed data member of the closure type.

Otherwise, how is the meaning of adding the unnamed NSDM for capture-by-reference? Hence, how the member will be initialized when the lambda-expression is evaluated is meaningful.

In contrast, if the implementation adopts the suggestion that does not declare such an NSDM, we should clarify what the id-expression denotes in every result object. Please consider the following example:

void fun(){
  int a;
 static auto f = [&a](){
    a = 10;
 };
}
int main(){
  fun();
  fun();
}

Even though the fun is called twice, however, only at the beginning, the result object f is initialized once. Please ignore the UB in this case, we just talk about the meaning of what a denotes. We should specify that the id-expression a in this result object is tied to the object denoted by the name a which is created at the beginning of the invocation of fun. In other words, in the subsequent invocation of fun, the id-expression in f still denotes the object that is created at the first time the fun is called, although the lifetime of the object has ended.

@languagelawyer
Copy link
Contributor

I still don't get why the description of how an id-expression is evaluated in capture-by-reference case should depend on the presence of the corresponding NSDM.

Because the standard does not forbid the implementation to declare NSDM for this purpose

What is «this purpose» here?

since it can declare the member, the use of the id-expression should transform …

I don't see a logical connection.

as the same manner as the following:

Every id-expression within the compound-statement of a lambda-expression that is an odr-use of an entity captured by copy is transformed into an access to the corresponding unnamed data member of the closure type.

In the capture-by-copy case, I see the reason to transform an id-expression to class member access. In the capture-by-reference case, I see none.

Otherwise, how is the meaning of adding the unnamed NSDM for capture-by-reference?

It is implementation's business/details why it adds a NSDM.

Hence, how the member will be initialized when the lambda-expression is evaluated is meaningful.

When adding the corresponding NSDM, I think an implementation is no allowed to change the observable behavior other than by changing the size of the closure type. So I don't think there is a need to describe non-observable things such as the initializer of this NSDM.

we should clarify what the id-expression denotes in every result object. Please consider the following example

I understand where the issue is. I don't see the connection with NSDMs.

@xmh0511
Copy link
Contributor Author

xmh0511 commented Jun 1, 2021

When adding the corresponding NSDM, I think an implementation is no allowed to change the observable behavior other than by changing the size of the closure type. So I don't think there is a need to describe non-observable things such as the initializer of this NSDM.

Please note the [expr.prim.lambda#capture-12]

It is unspecified whether additional unnamed non-static data members are declared in the closure type for entities captured by reference. If declared, such non-static data members shall be of literal type.

It appears to me that the standard supplies two options to implement the capture-by-reference; One is it does not declare the NSDM and the other is it declared the NSDM for that. It says If declared, that means the implementations may take the later option. However, if the implementation takes the later option but the standard does not say more detail about how this case would be, it's underspecified. Hence, I think the issue of what entity the id-expression would be tied to in each result object of lambda-expression also exists in this option. Since the standard defines the rule about NSDM for capture-by-copy case, why does not it define a similar rule for NSDM in capture-by-reference case, if the implementation takes that option? I think the non-observable thing here is which option the implementation takes for implementing capture-by-reference.

In the capture-by-reference case, I see none.

If the implementation takes the latter option, namely add the unnamed NSDM for implementing capture-by-reference, if we do not define the rule for transforming the original id-expression to the declared unnamed NSDM? what's the meaningful of the latter option?

@jensmaurer
Copy link
Member

I'm seeing the following problem being described:

The note in [expr.prim.lambda.capture] p16 lacks normative wording to base it on. In particular, it should be made clear that the evaluation of a lambda-expression causes the reference binding of entities captured by reference, and that (later) evaluation of an id-expression that names an entity captured by reference uses that reference binding (which might refer to an out-of-lifetime object).

@jensmaurer
Copy link
Member

@xmh0511
Copy link
Contributor Author

xmh0511 commented Jun 1, 2021

I'm seeing the following problem being described:

The note in [expr.prim.lambda.capture] p16 lacks normative wording to base it on. In particular, it should be made clear that the evaluation of a lambda-expression causes the reference binding of entities captured by reference, and that (later) evaluation of an id-expression that names an entity captured by reference uses that reference binding (which might refer to an out-of-lifetime object).

Yes, I exactly mean that. The standard introduces two options to implement capture-by-reference. We should clarify the above situation for these two options, respectively. It lacks in the current draft.

@jensmaurer
Copy link
Member

I don't think we need to differentiate those two options as long as we say what the effects are.

@xmh0511
Copy link
Contributor Author

xmh0511 commented Jun 1, 2021

I don't think we need to differentiate those two options as long as we say what the effects are.

I don't know how the concrete normative rule would be, maybe such two options can be unified described by that rule. However, in my humble opinion, if we adopt the latter option(namely, introduce an additional unnamed NSDM), the use of the id-expression in the compound-statement of the lambda-expression will be replaced by using that member. For the above example, the use of the id-expression a in a = 1; //#3 will look like

this->unnamed_member_for_a = 1;

This means the member should be initialized in order to refer to the entity captured by reference when the lambda-expression is evaluated to initialize its result object. Instead, the former option does not have the additional member, hence there's no member to be initialized and no transformation for the id-expression, where I think the difference is here. Specifically, for the former option, we should state the rule to make the id-expression clearly refer to the captured entity. For the latter option, we should state the rule to make the additional member clearly refer to the captured entity.

@jensmaurer jensmaurer changed the title The description for which entity does the id-expression captured by reference refer to after evaluating the lambda-expression [expr.prim.lambda.capture] Which entity does the id-expression captured by reference refer to after evaluating the lambda-expression? Jun 3, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants