Cannot use std.array.Appender in recursive types

Timon Gehr via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Wed Aug 9 12:04:10 PDT 2017


On 09.08.2017 21:00, Steven Schveighoffer wrote:
> On 8/9/17 2:25 PM, Nordlöw wrote:
>> Why doesn't appending to `subs` work with std.array.Appender in
>>
>>      struct T
>>      {
>>          string src;
>>          import std.array : Appender;
>>          Appender!(T[]) subs;
>>      }
>>      T t;
>>      t.subs ~= T.init; // ERRORS
>>      t.subs.put(T.init); // ERRORS
>>
>> when it works with builtin arrays as in
>>
>>      struct S
>>      {
>>          string src;
>>          S[] subs;
>>      }
>>      S s;
>>      s.subs ~= S.init;
>>
>> ?
>>
>> Specifically
>>
>>      t.subs ~= T.init
>>
>> errors as
>>
>>      Error: cannot append type T to type Appender!(T[])
> 
> Here is the problem:
> 
> ElementType!(T[]) is void.
> 
> Here is ElementType:
> template ElementType(R)
> {
>      static if (is(typeof(R.init.front.init) T))
>          alias ElementType = T;
>      else
>          alias ElementType = void;
> }
> 
> So what is happening here (I think), is that T isn't fully defined, so 
> T.init.front.init is an error at this point. Therefore the else clause 
> is selected.
> 
> This is one of the problems with using is(typeof) (or 
> __traits(compiles)) with an "else" clause. You may not be checking what 
> you think you are checking, and end up with the wrong result.
> 
> So essentially, Appender!(T[]) inside a T becomes (essentially) 
> Appender!(void[]). And you can't append a T to a void[].
> 
> If I change the definition of ElementType to use R.init.front instead of 
> R.init.front.init, it compiles. But I'm pretty sure this will break 
> other ranges.
> 
> Somewhere, there's an answer.
> 
> -Steve

It's a forward reference bug. Self-contained test case:

auto front(T)(T[] a){ return a[0]; }

template ElementType(R){
     pragma(msg, typeof(R.init.front)); /* Error: struct bug.T no size 
because of forward reference */
     static if (is(typeof(R.init.front) T)) alias ElementType = T;
     else alias ElementType = void;
}
struct Appender(A){
     A a;
     alias T=ElementType!A;
     private enum canPutItem(U)=is(U==T);
     void put(T)(T t)if(canPutItem!T){
         a~=t;
     }
}
struct T{
     string src;
     Appender!(T[]) subs;
}
void main(){
     T t;
     t.subs.put(T.init);
}



More information about the Digitalmars-d-learn mailing list