Bug 23136 – closure in a loop should hold distinct values for each iteration

Status
NEW
Severity
normal
Priority
P3
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2022-05-24T23:50:45Z
Last change time
2024-12-13T19:23:05Z
Keywords
safe
Assigned to
No Owner
Creator
Walter Bright
See also
https://issues.dlang.org/show_bug.cgi?id=2043
Moved to GitHub: dmd#18108 →

Comments

Comment #0 by bugzilla — 2022-05-24T23:50:45Z
Reported by Timon Gehr. This compiles even with -dip1000: ```d import std.stdio; void main()@safe{ void delegate()@safe[] dgList; foreach(i; [1, 2, 3]) { immutable b = i; dgList ~= { writeln(b); }; } foreach(dg; dgList) dg(); } ```
Comment #1 by qs.il.paperinik — 2022-08-05T09:23:56Z
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).
Comment #2 by qs.il.paperinik — 2023-05-10T10:05:08Z
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 , ) ```
Comment #3 by nick — 2024-05-06T09:08:44Z
Seems to be a duplicate of this bug: https://issues.dlang.org/show_bug.cgi?id=21929
Comment #4 by robert.schadek — 2024-12-13T19:23:05Z
THIS ISSUE HAS BEEN MOVED TO GITHUB https://github.com/dlang/dmd/issues/18108 DO NOT COMMENT HERE ANYMORE, NOBODY WILL SEE IT, THIS ISSUE HAS BEEN MOVED TO GITHUB