Thread overview
[Issue 23136] closure in a loop should hold distinct values for each iteration
May 24, 2022
Walter Bright
May 24, 2022
Walter Bright
Jun 14, 2022
JR
Aug 05, 2022
Bolpat
Dec 17, 2022
Iain Buclaw
May 10, 2023
Bolpat
May 24, 2022
https://issues.dlang.org/show_bug.cgi?id=23136

Walter Bright <bugzilla@digitalmars.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
           Keywords|                            |safe

--
May 24, 2022
https://issues.dlang.org/show_bug.cgi?id=23136

Walter Bright <bugzilla@digitalmars.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
           See Also|                            |https://issues.dlang.org/sh
                   |                            |ow_bug.cgi?id=2043

--
June 14, 2022
https://issues.dlang.org/show_bug.cgi?id=23136

JR <zorael@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |zorael@gmail.com

--
August 05, 2022
https://issues.dlang.org/show_bug.cgi?id=23136

Bolpat <qs.il.paperinik@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |qs.il.paperinik@gmail.com

--- Comment #1 from Bolpat <qs.il.paperinik@gmail.com> ---
To be honest, I find this worse:

```d
import std.stdio;

void main() @safe
{
    int delegate() immutable pure @safe[] dgList;
    foreach(int i; [0,1,2])
    {
        immutable b = i;
        dgList ~= () immutable pure @safe => b;
        writeln(dgList[$-1]());
    }
    writeln;
    foreach(dg; dgList) writeln(dg());
}
```

An immutable pure @safe delegate should never ever be able to return different values for the same parameters (in particular: no parameters).

--
December 17, 2022
https://issues.dlang.org/show_bug.cgi?id=23136

Iain Buclaw <ibuclaw@gdcproject.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
           Priority|P1                          |P3

--
May 10, 2023
https://issues.dlang.org/show_bug.cgi?id=23136

--- Comment #2 from Bolpat <qs.il.paperinik@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 , )
```

--