[Issue 23136] closure in a loop should hold distinct values for each iteration

d-bugmail at puremagic.com d-bugmail at puremagic.com
Wed May 10 10:05:08 UTC 2023


https://issues.dlang.org/show_bug.cgi?id=23136

--- Comment #2 from Bolpat <qs.il.paperinik at gmail.com> ---
This could be solved by allowing captures to be explicitly specified in a
capture list, like C++ lambdas do it.

Here’s how it could be done:

A lambda or non-static local function may have a capture list after the
parameter list and before any constraints and contracts. The capture list is
syntactically expressed with square brackets (or, as an alternative, with
`catch` and parentheses). The capture list is optional; providing no capture
list is equivalent to providing `[ref]` (or `catch(ref)`).

The entries of a capture list are comma-separated.

The first entry of a capture list can be a specially handled token sequence,
called "capture default": It can be `ref` or `auto`. (Otherwise it is handled
like other entries.)

Every other entry must be (Grammar below)
1. `this`, or
2. an Identifier, or 
3. `ref` followed by `this`,
4. `ref` followed by an Identifier, or
5. `auto` followed by an Identifier, `=`, and a
[ConditionalExpression](https://dlang.org/spec/expression.html#ConditionalExpression),
or
6. `ref` followed by an Identifier `=`, and a ConditionalExpression, or
7. `auto ref` followed by an Identifier `=`, and a ConditionalExpression.

`this` can be specified at most once and Identifiers must be unique.
(Specifying `this` is only valid in a non-static member function.)

An `auto ref` capture is `auto` if the ConditionalExpression evaluates to an
rvalue and it is `ref` if the ConditionalExpression evaluates to an lvalue.
If the ConditionalExpression is a compile-time sequence (introduced by template
`...` in some way), `auto ref` becomes `auto` or `ref` for each sequence
element individually. (The primary use-case for this is parameter forwarding.)

A capture that includes the `ref` token is called a "reference capture" and a
"value capture" otherwise.

A non-`this` capture without `=` is treated as if it were followed by `=` and
the given identifier, and prefixed by `auto` if it is not `ref`. (This rewrite
is assumed in the following.)

The Identifiers/`this` on the left of `=` in the capture list are called
left-hand sides, the ConditionalExpressions in the capture list are called
right-hand sides.

The left-hand sides are only in scope of the lambda, not its parent.
The right-hand sides are resolved in the scope of the lambda’s parent.

For a value capture, the context of the lambda holds a value that is
initialized by the right-hand side when statement is encountered in which the
lambda resides. If the right-hand side is implicit, the type must be copyable;
a non-copyable type can be used if the right-hand side is an rvalue.

For a reference capture, the context of the lambda holds a reference to the
variable. If the lambda is `scope`, variables that hold the lambda must not
outlive the bound references. (There is no such restriction for value
captures.)

This has two important consequences:
1. Lambdas with value captures are dependent on the exact point of creation.
2. Value captures cannot be shared among lambdas.

In contrast to C++, in D, a lambda with captures may outlive the captured
variables (unless, of course, it is marked `scope`). A local variable that is
captured by a non-`scope` lambda must be allocated on the heap only if the
capture is by reference. A reference parameter cannot be captured unless the
lambda is `scope`.

In contrast to C++, in D, a lambda’s call operator is not `const`; value
captures can be written to (unless, of course, their type is `const` or
`immutable` or the lambda is itself marked `const` or `immutable`).

Grammar:
```
CaptureList:
    [ ]
    [ CaptureDefault ]
    [ CaptureDefault , ]
    [ CaptureList ]
    [ CaptureList , ]
    [ CaptureDefault , Captures ]
    [ CaptureDefault , Captures , ]

CaptureDefault:
    ref
    auto

Captures:
    Capture
    Capture , Captures

Capture
    this
    ref this
    Identifier
    ref Identifier
    auto Identifier = ConditionalExpression
    ref Identifier = ConditionalExpression
    auto ref Identifier = ConditionalExpression
```

As a syntactical alternative, instead of using brackets, the `catch` keyword
could be re-used:
```
CaptureList:
    catch ( CaptureDefault )
    catch ( CaptureDefault , )
    catch ( CaptureList )
    catch ( CaptureList , )
    catch ( CaptureDefault , Captures )
    catch ( CaptureDefault , Captures , )
```

--


More information about the Digitalmars-d-bugs mailing list