The variable x in makeS() function is actually placed in closure object on heap, but it's not checked by the @nogc.
struct S(alias f)
{
auto foo()() { return f(0); }
void dummy() {}
}
auto makeS() @nogc
{
int x = 10;
S!(a => x) s;
// instantiating foo inside makeS will raise the @nogc violation error.
//assert(s.foo() == 10);
return s;
}
void main() @nogc
{
auto s = makeS();
// the hidden field of s is actually non-null,
// that points closure object on heap.
assert(s.tupleof[$-1] !is null);
// instantiating foo outside makeS will place the variable x in closure
// *after* the semantic3 completion of the function.
// --> @nogc attribute on makeS() is ignored!
// --> @nogc on main() has no effect, so foo itself has no GC-allocation.
assert(s.foo() == 10);
}
Comment #1 by k.hara.pg — 2015-07-07T17:11:29Z
Issue 5730 is also related with the closure handling timing.
Comment #2 by ag0aep6g — 2015-08-30T13:57:53Z
A variant is to make makeS a template instead of S.foo:
----
struct S(alias f)
{
auto foo() { return f(0); }
}
auto makeS()() @nogc
{
int x = 10;
S!(a => x) s;
return s;
}
void main() @nogc
{
auto s = makeS();
}
----
Compiles, but shouldn't.
Some change in 2.068's Phobos makes it possible to accidentally do this with std.algorithm.map:
----
import std.algorithm: equal, map;
import std.range: only;
auto foo()(int[] a, immutable int b)
{
return a.map!(x => x + b);
}
void main() @nogc
{
int[3] test = [1,2,3];
assert(test[].foo(3).equal(only(4,5,6)));
}
----
I've seen this twice recently:
http://forum.dlang.org/post/[email protected]http://forum.dlang.org/post/[email protected]
Comment #3 by stanislav.blinov — 2021-12-08T18:12:51Z
Still an issue in 2.098.
Comment #4 by robert.schadek — 2024-12-13T18:43:41Z