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;



     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;
         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.


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){
struct T{
    string src;
    Appender!(T[]) subs;
void main(){
    T t;

Reply via email to