D issues are now tracked on GitHub. This Bugzilla instance remains as a read-only archive.
Issue 14771 - Hidden @nogc violation around closure creation
Summary: Hidden @nogc violation around closure creation
Status: NEW
Alias: None
Product: D
Classification: Unclassified
Component: dmd (show other issues)
Version: D2
Hardware: All All
: P2 major
Assignee: No Owner
URL:
Keywords: accepts-invalid
Depends on:
Blocks:
 
Reported: 2015-07-05 05:15 UTC by Kenji Hara
Modified: 2024-12-13 18:43 UTC (History)
2 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this issue.
Description Kenji Hara 2015-07-05 05:15:12 UTC
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 Kenji Hara 2015-07-07 17:11:29 UTC
Issue 5730 is also related with the closure handling timing.
Comment 2 ag0aep6g 2015-08-30 13:57:53 UTC
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/xpdntswucmqboejhxiit@forum.dlang.org
http://forum.dlang.org/post/mrv0td$ci6$1@digitalmars.com
Comment 3 Stanislav Blinov 2021-12-08 18:12:51 UTC
Still an issue in 2.098.
Comment 4 dlangBugzillaToGithub 2024-12-13 18:43:41 UTC
THIS ISSUE HAS BEEN MOVED TO GITHUB

https://github.com/dlang/dmd/issues/19012

DO NOT COMMENT HERE ANYMORE, NOBODY WILL SEE IT, THIS ISSUE HAS BEEN MOVED TO GITHUB