Thread overview
Is this nogc? dmd and gdc disagree
Feb 11, 2016
rcorre
Feb 11, 2016
tsbockman
Feb 11, 2016
tsbockman
Feb 11, 2016
rcorre
Feb 11, 2016
rcorre
Feb 11, 2016
tsbockman
Feb 12, 2016
Kapps
Feb 13, 2016
tsbockman
Feb 13, 2016
Kapps
February 11, 2016
I recently tried compiling enumap with GDC, and found that it disagrees with DMD on whether a function is @nogc. Here's a semi-reduced test-case:

---
import std.range;
import std.traits    : EnumMembers;
import std.typecons  : tuple, staticIota;
import std.algorithm : map;

struct Enumap(K, V)
  if(EnumMembers!K == staticIota!(0, EnumMembers!K.length))
{
  enum length = EnumMembers!K.length;
  private V[length] _store;

  ref auto opIndex(K key) inout {
    return _store[key];
  }

  @nogc auto byKeyValue() const {
    return only(EnumMembers!K).map!(key => tuple(key, this[key]));
  }
}

@nogc void main() {
    import std.typecons  : tuple;
    import std.algorithm : map;

    enum E { a, b, c, d };
    immutable elements = Enumap!(E, int)([1,2,3,4]);

    auto pairs = elements.byKeyValue.map!(pair => tuple(pair[0], pair[1] + 1));
}
---

GDC claims that byKeyValue() allocates a closure, but DMD is just fine with me calling it @nogc. I'm inclined to agree with GDC here, unless DMD is doing some magic so that actually doesn't allocate a closure.
February 11, 2016
On Thursday, 11 February 2016 at 03:09:51 UTC, rcorre wrote:
> GDC claims that byKeyValue() allocates a closure, but DMD is just fine with me calling it @nogc. I'm inclined to agree with GDC here, unless DMD is doing some magic so that actually doesn't allocate a closure.

I cannot reproduce your results. Your example won't compile for me with DMD HEAD:

source/app.d(16,14): Error: function app.Enumap!(E, int).Enumap.byKeyValue is @nogc yet allocates closures with the GC
source/app.d(17,37):        app.Enumap!(E, int).Enumap.byKeyValue.__lambda1 closes over variable this at source/app.d(16,14)
source/app.d(26,26): Error: template instance app.Enumap!(E, int) error instantiating

February 11, 2016
On Thursday, 11 February 2016 at 03:09:51 UTC, rcorre wrote:
> I recently tried compiling enumap with GDC, and found that it disagrees with DMD on whether a function is @nogc. Here's a semi-reduced test-case:

Here's an @nogc version of `byKeyValue()`:

@nogc auto byKeyValue() const {
  static immutable keys = [EnumMembers!K];
  return zip(keys[], _store[]);
}

Using zip and slices guarantees that the structure returned will be only 5*size_t.sizeof bytes, regardless of the types of K and V.
February 11, 2016
On Thursday, 11 February 2016 at 04:20:13 UTC, tsbockman wrote:
> On Thursday, 11 February 2016 at 03:09:51 UTC, rcorre wrote:
>> I recently tried compiling enumap with GDC, and found that it disagrees with DMD on whether a function is @nogc. Here's a semi-reduced test-case:
>
> Here's an @nogc version of `byKeyValue()`:
>
> @nogc auto byKeyValue() const {
>   static immutable keys = [EnumMembers!K];
>   return zip(keys[], _store[]);
> }
>
> Using zip and slices guarantees that the structure returned will be only 5*size_t.sizeof bytes, regardless of the types of K and V.

I'm on the DMD 2.070 release, so maybe its fixed in master.
Either way, thanks for the suggestion!
Somehow I didn't realize what I was doing was an over-complicated zip :)

February 11, 2016
On Thursday, 11 February 2016 at 12:41:16 UTC, rcorre wrote:
> On Thursday, 11 February 2016 at 04:20:13 UTC, tsbockman wrote:
>> Using zip and slices guarantees that the structure returned will be only 5*size_t.sizeof bytes, regardless of the types of K and V.
>
> I'm on the DMD 2.070 release, so maybe its fixed in master.
> Either way, thanks for the suggestion!
> Somehow I didn't realize what I was doing was an over-complicated zip :)

Though it appears (in 2.070 at least) that zip's range primitives aren't nogc:

---
import std.range;

immutable a = [1,2,3];
immutable b = [1,2,3];

@nogc void main() {
    foreach(x, y ; a[].zip(b[])) { }
}
---
other.d(7): Error: @nogc function 'D main' cannot call non-@nogc function 'std.range.Zip!(immutable(int)[], immutable(int)[]).Zip.empty'
other.d(7): Error: @nogc function 'D main' cannot call non-@nogc function 'std.range.Zip!(immutable(int)[], immutable(int)[]).Zip.popFront'
---

February 11, 2016
On Thursday, 11 February 2016 at 12:55:02 UTC, rcorre wrote:
> Though it appears (in 2.070 at least) that zip's range primitives aren't nogc:

I took a look at the source code for `zip()`, and the cause of this deficiency is that `Zip` takes a `StoppingPolicy` as a runtime parameter, rather than a compile-time parameter as it should.

`empty()` and `popFront()` can both throw - but only if `StoppingPolicy.requireSameLength` is selected. The default is `StoppingPolicy.shortest`.

This design dates back to 2009, which is probably before `nothrow` was implemented, and certainly before `@nogc`.
February 12, 2016
On Thursday, 11 February 2016 at 12:41:16 UTC, rcorre wrote:
> On Thursday, 11 February 2016 at 04:20:13 UTC, tsbockman wrote:
>> On Thursday, 11 February 2016 at 03:09:51 UTC, rcorre wrote:
>>> I recently tried compiling enumap with GDC, and found that it disagrees with DMD on whether a function is @nogc. Here's a semi-reduced test-case:
>>
>> Here's an @nogc version of `byKeyValue()`:
>>
>> @nogc auto byKeyValue() const {
>>   static immutable keys = [EnumMembers!K];
>>   return zip(keys[], _store[]);
>> }
>>
>> Using zip and slices guarantees that the structure returned will be only 5*size_t.sizeof bytes, regardless of the types of K and V.
>
> I'm on the DMD 2.070 release, so maybe its fixed in master.
> Either way, thanks for the suggestion!
> Somehow I didn't realize what I was doing was an over-complicated zip :)

You'll encounter this pretty often in Phobos I think. I really don't think @nogc is ready, and tend to avoid it in my code. Exceptions are a big reason for it, and lots of functions in Phobos throw exceptions which prevents them from being used in @nogc (barring ugly hacks). I don't want to discourage you before you try it, but I feel like @nogc is not yet worth using until something is done about issues such as exceptions, allowing Phobos to properly be able to handle @nogc.
February 13, 2016
On Friday, 12 February 2016 at 23:46:09 UTC, Kapps wrote:
> You'll encounter this pretty often in Phobos I think. I really don't think @nogc is ready, and tend to avoid it in my code. Exceptions are a big reason for it, and lots of functions in Phobos throw exceptions which prevents them from being used in @nogc (barring ugly hacks). I don't want to discourage you before you try it, but I feel like @nogc is not yet worth using until something is done about issues such as exceptions, allowing Phobos to properly be able to handle @nogc.

On the other hand... Realistically, the only way that Phobos will reach usability with @nogc, is if people keep trying and complaining when something doesn't work that should.

I guess the problem is that a lot of this stuff requires RC exceptions to be realistically fixable. (Not this case though.)
February 13, 2016
On Saturday, 13 February 2016 at 00:41:35 UTC, tsbockman wrote:
> On Friday, 12 February 2016 at 23:46:09 UTC, Kapps wrote:
>> You'll encounter this pretty often in Phobos I think. I really don't think @nogc is ready, and tend to avoid it in my code. Exceptions are a big reason for it, and lots of functions in Phobos throw exceptions which prevents them from being used in @nogc (barring ugly hacks). I don't want to discourage you before you try it, but I feel like @nogc is not yet worth using until something is done about issues such as exceptions, allowing Phobos to properly be able to handle @nogc.
>
> On the other hand... Realistically, the only way that Phobos will reach usability with @nogc, is if people keep trying and complaining when something doesn't work that should.
>
> I guess the problem is that a lot of this stuff requires RC exceptions to be realistically fixable. (Not this case though.)

Yeah. I originally intended to go through and make what I needed @nogc, but then I realized that it was pointless thanks to exceptions. Hopefully one day...