Thread overview | ||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
May 18, 2021 [Issue 21929] delegates capture do not respect scoping | ||||
---|---|---|---|---|
| ||||
https://issues.dlang.org/show_bug.cgi?id=21929 Vladimir Panteleev <dlang-bugzilla@thecybershadow.net> changed: What |Removed |Added ---------------------------------------------------------------------------- Status|NEW |RESOLVED CC| |dlang-bugzilla@thecybershad | |ow.net Resolution|--- |INVALID --- Comment #1 from Vladimir Panteleev <dlang-bugzilla@thecybershadow.net> --- In JavaScript: --------------------------------------------------------------- var dgs = []; for (var i = 0; i < 10; i++) { dgs.push(function() { console.log(i); }); } dgs.push(function() { console.log("With cached variables"); }); for (var i = 0; i < 10; i++) { var index = i; dgs.push(function() { console.log(index); }); } for (var dg of dgs) { dg(); } --------------------------------------------------------------- Prints: --------------------------------------------------------------- 10 10 10 10 10 10 10 10 10 10 With cached variables 9 9 9 9 9 9 9 9 9 9 --------------------------------------------------------------- So, I don't think D's behavior is unexpected compared to other languages. Note that if you change "var" to "let" in JS then it behaves closer as to what you may expect. At this point this is part of the language and is not a bug, so I don't think the bugtracker is the proper place to post this. The workaround in D is the same as in JavaScript (before "let"), pass the mutable values to the lambda so that their current values become part of the lambda's state (or create an outer lambda which does this). -- |
May 18, 2021 [Issue 21929] delegates capture do not respect scoping | ||||
---|---|---|---|---|
| ||||
https://issues.dlang.org/show_bug.cgi?id=21929 Vladimir Panteleev <dlang-bugzilla@thecybershadow.net> changed: What |Removed |Added ---------------------------------------------------------------------------- Status|RESOLVED |REOPENED Resolution|INVALID |--- --- Comment #2 from Vladimir Panteleev <dlang-bugzilla@thecybershadow.net> --- (In reply to deadalnix from comment #0) > Failure to do allows for very strange things to happen, such as observing mutation in immutable variables. This however is a good reason to possibly deprecate/disallow doing so at least in @safe code. -- |
May 18, 2021 [Issue 21929] delegates capture do not respect scoping | ||||
---|---|---|---|---|
| ||||
https://issues.dlang.org/show_bug.cgi?id=21929 --- Comment #3 from deadalnix <deadalnix@gmail.com> --- (In reply to Vladimir Panteleev from comment #1) > In JavaScript: > > [...] > > So, I don't think D's behavior is unexpected compared to other languages. > > Note that if you change "var" to "let" in JS then it behaves closer as to what you may expect. > > At this point this is part of the language and is not a bug, so I don't think the bugtracker is the proper place to post this. > > The workaround in D is the same as in JavaScript (before "let"), pass the mutable values to the lambda so that their current values become part of the lambda's state (or create an outer lambda which does this). This code is erroneous. In JS, var declare a variable at the function scope level. Try again declaring the variable using `let`, which creates a properly scoped variable in JS and see how it goes. -- |
May 18, 2021 [Issue 21929] delegates capture do not respect scoping | ||||
---|---|---|---|---|
| ||||
https://issues.dlang.org/show_bug.cgi?id=21929 Ketmar Dark <ketmar@ketmar.no-ip.org> changed: What |Removed |Added ---------------------------------------------------------------------------- CC| |ketmar@ketmar.no-ip.org -- |
May 19, 2021 [Issue 21929] delegates capture do not respect scoping | ||||
---|---|---|---|---|
| ||||
https://issues.dlang.org/show_bug.cgi?id=21929 Walter Bright <bugzilla@digitalmars.com> changed: What |Removed |Added ---------------------------------------------------------------------------- CC| |bugzilla@digitalmars.com --- Comment #4 from Walter Bright <bugzilla@digitalmars.com> --- Let's rewrite it to something that does not use closures: int test() @safe { int j; int*[20] ps; for (int i = 0; i < 10; i++) { ps[j++] = &i; } for (int i = 0; i < 10; i++) { int index = i; ps[j++] = &index; } int x; foreach (p; ps) { x += *p; } return x; } This code is equivalent in terms of what is happening with references and scopes. Compiling it with -dip1000 yields: Error: address of variable i assigned to ps with longer lifetime Error: address of variable index assigned to ps with longer lifetime Which is pragmatically what the behavior of the delegate example would be, because the delegate is also storing a pointer to the variable. -- |
May 19, 2021 [Issue 21929] delegates capture do not respect scoping | ||||
---|---|---|---|---|
| ||||
https://issues.dlang.org/show_bug.cgi?id=21929 Walter Bright <bugzilla@digitalmars.com> changed: What |Removed |Added ---------------------------------------------------------------------------- Keywords| |safe -- |
May 19, 2021 [Issue 21929] delegates capture do not respect scoping | ||||
---|---|---|---|---|
| ||||
https://issues.dlang.org/show_bug.cgi?id=21929 --- Comment #5 from Walter Bright <bugzilla@digitalmars.com> --- The general rule for determining "what should happen here" when there are abstractions around pointers (such as arrays, delegates, refs, outs, class references, etc.), is to rewrite it in explicit terms of those pointers. The (sometimes baffling) behavior is then exposed for what it actually is, and the behavior should match. Granted, there is a special kludge in the compiler to sometimes put the variables referenced by the delegate into a closure allocated by the gc, but that doesn't account for variables that go out of scope before the function scope ends. There is no process to make a closure for them, and adding such a capability is likely much more complication than added value, and so should just be an error. -- |
May 19, 2021 [Issue 21929] delegates capture do not respect scoping | ||||
---|---|---|---|---|
| ||||
https://issues.dlang.org/show_bug.cgi?id=21929 --- Comment #6 from deadalnix <deadalnix@gmail.com> --- (In reply to Walter Bright from comment #4) > Let's rewrite it to something that does not use closures: > > int test() @safe { > int j; > int*[20] ps; > > for (int i = 0; i < 10; i++) { > ps[j++] = &i; > } > > for (int i = 0; i < 10; i++) { > int index = i; > ps[j++] = &index; > } > > int x; > foreach (p; ps) { > x += *p; > } > > return x; > } > > This code is equivalent in terms of what is happening with references and scopes. > > Compiling it with -dip1000 yields: > > Error: address of variable i assigned to ps with longer lifetime > Error: address of variable index assigned to ps with longer lifetime > > Which is pragmatically what the behavior of the delegate example would be, because the delegate is also storing a pointer to the variable. Except it is not. In D, closures do allocate on heap. -- |
May 20, 2021 [Issue 21929] delegates capture do not respect scoping | ||||
---|---|---|---|---|
| ||||
https://issues.dlang.org/show_bug.cgi?id=21929 a11e99z <black80@bk.ru> changed: What |Removed |Added ---------------------------------------------------------------------------- CC| |black80@bk.ru --- Comment #7 from a11e99z <black80@bk.ru> --- agree with bug. man creating new delegate/lambda with new internal state. what is reason to store just one shared state for created multiple delegetes in loop? what is using case for it? if D-team don't want to break current behavior let add new keyword "lambda" that is 100% compatible with "delegate" but it grabs totally new state when created - just changed compile time generated code. C++ byRef & byValue capturing is more viable. -- |
May 20, 2021 [Issue 21929] delegates capture do not respect scoping | ||||
---|---|---|---|---|
| ||||
https://issues.dlang.org/show_bug.cgi?id=21929 --- Comment #8 from deadalnix <deadalnix@gmail.com> --- (In reply to a11e99z from comment #7) > C++ byRef & byValue capturing is more viable. You'll not that C++'s std::function will allocate on heap if you capture. The equivalent code in C++ WILL allocate in a loop too. -- |
Copyright © 1999-2021 by the D Language Foundation