August 16, 2015
On 08/16/2015 03:36 PM, cym13 wrote:
> On Sunday, 16 August 2015 at 22:22:07 UTC, Ali Çehreli wrote:
>>
>> // HERE:
>> // Error: function deneme.func @nogc function allocates
>> //        a closure with the GC
>> @nogc auto func(uint[] arr, DelegateRef d)
>> {
>>     return arr.map!(a => d.d(a));
>> }
>
> Aren't you making another delegate in the map by using "=>" that needs
> to allocate because it uses 'd' which is out of its scope?

I did not see that at all. :) I've finally gotten it to work by stepping into template realm where the compiler is a master of attributes: :)

import std.stdio;
import std.range;
import std.algorithm;

struct Caller
{
    this(uint context) {
        _context = context;
    }

    // ADDED pure
    pure uint method(uint value) {
        return _context * value;
    }

    uint _context;
}

// Now the type of d is a template parameter
@nogc auto func(Func)(uint[] arr, Func d)
{
    return arr.map!(d);
}

void main(string[] args)
{
    uint[] arr = [1,2,3];
    uint context = 2;
    auto c = Caller(context);
    auto d = &c.method;

    writeln(func(arr, d));
}

Prints:

  [2, 4, 6]

Ali

August 17, 2015
On Sunday, 16 August 2015 at 23:05:42 UTC, Ali Çehreli wrote:
> // Now the type of d is a template parameter
> @nogc auto func(Func)(uint[] arr, Func d)
> {
>     return arr.map!(d);
> }

Huh. I think func being a template is the key here. When the original code is put in a template, it works too (with 2.068):

----
void func()() @nogc
{
    import std.algorithm;
    uint[3] arr = [1,2,3];
    uint context = 2;
    auto r = arr[].map!(delegate(value) { return value * context; });
}
void main()
{
    func();
}
----

August 17, 2015
On Monday, 17 August 2015 at 09:51:47 UTC, anonymous wrote:
> Huh. I think func being a template is the key here. When the original code is put in a template, it works too (with 2.068):

Nope, it "works" only because "r" is unreferenced and gets thrown out. Just try using r.front there, for example, and the error returns.

August 17, 2015
On Monday, 17 August 2015 at 10:28:33 UTC, thedeemon wrote:
> Nope, it "works" only because "r" is unreferenced and gets thrown out. Just try using r.front there, for example, and the error returns.

You're right, it falls short.

But I think r not being referenced is not exactly it. Using front in Ali's func breaks it in the same way. And returning r from func works.

Wait, returning r from func works? Yes:

----
auto func()(uint[] arr, uint context) @nogc
{
    import std.algorithm;
    auto r = arr[].map!(delegate(value) { return value * context; });
    return r;
}
void main() @nogc
{
    uint[3] arr = [1,2,3];
    uint context = 2;
    auto r = func(arr[], context);

    import std.algorithm: equal;
    import std.range: only;
    assert(equal(r, only(2, 4, 6)));
}
----

Is that supposed to compile? A closure is needed for the delegate, isn't it? With @nogc, where is the closure stored? This looks like an accepts-invalid bug to me.

It doesn't compile when func is not a template. So maybe the check is broken for templates.

It also doesn't compile with 2.067, so this may be a regression.

Coming back to Ali's code, here's a version that shows that func doesn't need template parameters, but it needs to be a template:

----
import std.stdio;
import std.algorithm;

struct Caller
{
    uint method(uint value) pure @nogc {
        return _context * value;
    }

    uint _context;
}

auto func()(uint[] arr, uint delegate(uint) pure @nogc d) @nogc
{
    return arr.map!(d);
}

void main() @nogc
{
    uint[3] arr = [1,2,3];
    uint context = 2;
    auto c = Caller(context);
    auto d = &c.method;

    auto r = func(arr[], d);

    import std.algorithm: equal;
    import std.range: only;
    assert(equal(r, only(2, 4, 6)));
}
----

I think this relies on the same discrepancy as the problematic code above.
August 17, 2015
On Monday, 17 August 2015 at 12:38:05 UTC, anonymous wrote:
> auto func()(uint[] arr, uint delegate(uint) pure @nogc d) @nogc
> {
>     return arr.map!(d);
> }
>
> void main() @nogc
> {
>     uint[3] arr = [1,2,3];
>     uint context = 2;
>     auto c = Caller(context);
>     auto d = &c.method;
>
>     auto r = func(arr[], d);
>
>     import std.algorithm: equal;
>     import std.range: only;
>     assert(equal(r, only(2, 4, 6)));
> }

I've just checked with my runtime GC hook. Here the call to func() allocates 12 bytes via gc_malloc, and it's the same for a 4-elements array, so it's not for the array itself, it's for a closure, I think.
August 17, 2015
On Monday, 17 August 2015 at 16:18:50 UTC, thedeemon wrote:
> I've just checked with my runtime GC hook. Here the call to func() allocates 12 bytes via gc_malloc, and it's the same for a 4-elements array, so it's not for the array itself, it's for a closure, I think.

Also, compiling with -vgc says nothing, compiler (2.068) seems to miss this allocation.

August 17, 2015
On Monday, 17 August 2015 at 16:21:16 UTC, thedeemon wrote:
> On Monday, 17 August 2015 at 16:18:50 UTC, thedeemon wrote:
>> I've just checked with my runtime GC hook. Here the call to func() allocates 12 bytes via gc_malloc, and it's the same for a 4-elements array, so it's not for the array itself, it's for a closure, I think.
>
> Also, compiling with -vgc says nothing, compiler (2.068) seems to miss this allocation.

Thanks for confirming.

It seems to be a known issue that the compiler doesn't recognize the @nogc violation by the closure:
https://issues.dlang.org/show_bug.cgi?id=14771

That's not a regression, though. 2.067 rejecting the code has something to do with this assert:
https://github.com/D-Programming-Language/phobos/blob/v2.067.1/std/algorithm/iteration.d#L453 (code is the same for 2.068).

I don't know if that assert should trigger a @nogc violation or not. But anyway, the real issue is 14771, as far as I can tell.
1 2
Next ›   Last »