Thread overview | ||||||||
---|---|---|---|---|---|---|---|---|
|
April 17, 2019 Nasty corner case behaviour | ||||
---|---|---|---|---|
| ||||
Got bitten by this today:
import std.algorithm : filter, map;
import std.stdio;
struct S {
bool flag;
auto method() {
return [ 1 ].filter!(e => flag);
}
}
void main() {
auto s = S(true);
writeln(s.method);
auto arr = [ s ];
writeln(arr[0].method);
auto mappedArray = arr.map!(e => e.method);
writeln(mappedArray.front);
writeln(mappedArray);
}
Expected output:
[1]
[1]
[1]
[[1]]
Actual output:
[1]
[1]
[1]
[[]]
Exercise for the reader: explain the problem.
Hint: it's not a bug in Phobos.
If method() is changed to the following, the problem goes away:
auto method() {
auto _flag = flag;
return [ 1 ].filter!(e => _flag);
}
It should be obvious by now that the problem is caused by the lambda incorrectly closing over a member variable in a struct instance that subsequently goes out of scope, while the returned closure lives on. Unfortunately, neither -dip25 nor -dip1000 catches this leakage, even if main() is marked @safe.
T
--
Тише едешь, дальше будешь.
|
April 19, 2019 Re: Nasty corner case behaviour | ||||
---|---|---|---|---|
| ||||
Posted in reply to H. S. Teoh | On Thursday, 18 April 2019 at 05:01:17 UTC, H. S. Teoh wrote:
> ...
The value of flag is lost.
To easily see this make flag an int.
import std.algorithm : filter, map;
import std.stdio;
struct S {
int flag;
auto method() {
return [ 1 ].filter!((e) {
writeln("\nflag: ", flag);
return flag == 10;
});
}
}
void main() {
auto s = S(10);
auto arr = [ s ];
auto mappedArray = arr.map!(e => e.method);
writeln(mappedArray);
}
Expected output:
[[
flag: 10
1]]
Actual output:
[[
flag: 1872694912
]]
|
April 18, 2019 Re: Nasty corner case behaviour | ||||
---|---|---|---|---|
| ||||
Posted in reply to H. S. Teoh | On 4/18/19 1:01 AM, H. S. Teoh wrote:
> Got bitten by this today:
>
Yeeouch! Bugzilla issue #?
|
April 19, 2019 Re: Nasty corner case behaviour | ||||
---|---|---|---|---|
| ||||
Posted in reply to H. S. Teoh | On Thursday, 18 April 2019 at 05:01:17 UTC, H. S. Teoh wrote:
> ...
Sneaky enough.
The problem is the value copy semantics of S which results in an address to a dead stack frame being saved.
If you make it a ref parameter the issue goes away.
import std.algorithm : filter, map;
import std.stdio;
struct S {
bool flag;
auto method() {
return [ 1 ].filter!(e => flag);
}
}
void main() {
auto s = S(true);
writeln(s.method);
auto arr = [ s ];
writeln(arr[0].method);
auto mappedArray = arr.map!((ref e) => e.method);
writeln(mappedArray.front);
writeln(mappedArray);
}
Output:
[1]
[1]
[1]
[[1]]
|
April 19, 2019 Re: Nasty corner case behaviour | ||||
---|---|---|---|---|
| ||||
Posted in reply to H. S. Teoh | On Thursday, 18 April 2019 at 05:01:17 UTC, H. S. Teoh wrote:
> Got bitten by this today:
>
> import std.algorithm : filter, map;
> import std.stdio;
>
> struct S {
> bool flag;
> auto method() {
> return [ 1 ].filter!(e => flag);
> }
> }
>
> void main() {
> auto s = S(true);
> writeln(s.method);
>
> auto arr = [ s ];
> writeln(arr[0].method);
>
> auto mappedArray = arr.map!(e => e.method);
> writeln(mappedArray.front);
> writeln(mappedArray);
> }
>
> Expected output:
>
> [1]
> [1]
> [1]
> [[1]]
>
> Actual output:
>
> [1]
> [1]
> [1]
> [[]]
>
> Exercise for the reader: explain the problem.
>
> Hint: it's not a bug in Phobos.
>
> If method() is changed to the following, the problem goes away:
>
> auto method() {
> auto _flag = flag;
> return [ 1 ].filter!(e => _flag);
> }
>
> It should be obvious by now that the problem is caused by the lambda incorrectly closing over a member variable in a struct instance that subsequently goes out of scope, while the returned closure lives on. Unfortunately, neither -dip25 nor -dip1000 catches this leakage, even if main() is marked @safe.
>
>
> T
I find this one a bit confusing. Shouldn't the struct be moved to the heap when the compiler detects that it's being closed over?
|
April 20, 2019 Re: Nasty corner case behaviour | ||||
---|---|---|---|---|
| ||||
Posted in reply to Meta | On Friday, 19 April 2019 at 23:42:53 UTC, Meta wrote:
> I find this one a bit confusing. Shouldn't the struct be moved to the heap when the compiler detects that it's being closed over?
It does. the `this` parameter in the following function is copied to a heap closure space.
auto method() {
return [ 1 ].filter!(e => this.flag);
}
But `this` is a reference so the pointer is copied instead of the value.
|
Copyright © 1999-2021 by the D Language Foundation