Thread overview
Recursive template instantiation
Mar 13, 2017
Jack Applegame
Mar 13, 2017
Jack Applegame
Mar 13, 2017
Stefan Koch
Mar 13, 2017
ag0aep6g
Mar 13, 2017
ag0aep6g
Mar 13, 2017
Stefan Koch
Mar 13, 2017
ag0aep6g
March 13, 2017
I'm pretty sure that this code should compile (https://dpaste.dzfl.pl/cf1e1ee6ef4b):

struct A(T) {
    ~this() {
        char[T.sizeof] data;
    }
}

struct B(T) {
    A!T foo;
}

struct C {
    B!C bar;
}

void main() {
    C c;
}

But it doesn't:
/d300/f416.d(3): Error: struct f416.C no size because of forward reference /d300/f416.d(12): Error: template instance f416.B!(C) error instantiating

Notice that the same C++ code compiles without problems:

template<typename T> struct A {
    ~A() {
        char data[sizeof(T)];
    }
};

template<typename T> struct B {
    A<T> foo;
};

struct C {
    B<C> bar;
};

int main() {
    C c;
}

A simple recursion is compiled successfully (https://dpaste.dzfl.pl/5a8ff73bfa88):

struct A(T) {
    ~this() {
        char[T.sizeof] data;
    }
}

struct C {
    A!C bar;
}

void main() {
    C c;
}

March 13, 2017
Is this a bug?
March 13, 2017
On Monday, 13 March 2017 at 22:05:24 UTC, Jack Applegame wrote:
> Is this a bug?

No it's not

struct C
{
  B!C;
}
is an error.

Howto compute C ?       <------\
let's check the members;       |
The member needs a template.   |
Howto compute the template ?   |
let's compute the parameters.  |
What is the first Parameter ?  |
Its C.                         |
Howoto compute C        -------/
March 13, 2017
On 03/13/2017 03:26 PM, Jack Applegame wrote:
> I'm pretty sure that this code should compile
> (https://dpaste.dzfl.pl/cf1e1ee6ef4b):
>
> struct A(T) {
>     ~this() {
>         char[T.sizeof] data;
>     }
> }
>
> struct B(T) {
>     A!T foo;
> }
>
> struct C {
>     B!C bar;
> }
>
> void main() {
>     C c;
> }
>
> But it doesn't:
> /d300/f416.d(3): Error: struct f416.C no size because of forward
> reference /d300/f416.d(12): Error: template instance f416.B!(C) error
> instantiating

It compiles when it's a normal method instead of a destructor:

----
struct A(T) {
    void m() {
        char[T.sizeof] data;
    }
}
/* ... rest as above ... */
----

I don't see how the destructor makes a difference. Soo, bug?
March 13, 2017
On Monday, 13 March 2017 at 22:59:36 UTC, ag0aep6g wrote:
> On 03/13/2017 03:26 PM, Jack Applegame wrote:
>> I'm pretty sure that this code should compile
>> (https://dpaste.dzfl.pl/cf1e1ee6ef4b):
>>
>> struct A(T) {
>>     ~this() {
>>         char[T.sizeof] data;
>>     }
>> }
>>
>> struct B(T) {
>>     A!T foo;
>> }
>>
>> struct C {
>>     B!C bar;
>> }
>>
>> void main() {
>>     C c;
>> }
>>
>> But it doesn't:
>> /d300/f416.d(3): Error: struct f416.C no size because of forward
>> reference /d300/f416.d(12): Error: template instance f416.B!(C) error
>> instantiating
>
> It compiles when it's a normal method instead of a destructor:
>
> ----
> struct A(T) {
>     void m() {
>         char[T.sizeof] data;
>     }
> }
> /* ... rest as above ... */
> ----
>
> I don't see how the destructor makes a difference. Soo, bug?

Try to use m.
March 14, 2017
On 03/14/2017 12:02 AM, Stefan Koch wrote:
> On Monday, 13 March 2017 at 22:59:36 UTC, ag0aep6g wrote:
[...]
>> ----
>> struct A(T) {
>>     void m() {
>>         char[T.sizeof] data;
>>     }
>> }
>> /* ... rest as above ... */
>> ----
>>
>> I don't see how the destructor makes a difference. Soo, bug?
>
> Try to use m.

Works no problem?

----
struct A(T) {
    void m() {
        char[T.sizeof] data;
        import std.stdio;
        writeln(T.sizeof);
    }
}

struct B(T) {
    A!T foo;
}

struct C {
    B!C bar;
}

void main() {
    C c;
    c.bar.foo.m();
}
----

Prints "1".
March 14, 2017
On 03/13/2017 11:58 PM, Stefan Koch wrote:
> On Monday, 13 March 2017 at 22:05:24 UTC, Jack Applegame wrote:
>> Is this a bug?
>
> No it's not
>
> struct C
> {
>   B!C;
> }
> is an error.
>
> Howto compute C ?       <------\
> let's check the members;       |
> The member needs a template.   |
> Howto compute the template ?   |
> let's compute the parameters.  |
> What is the first Parameter ?  |
> Its C.                         |
> Howoto compute C        -------/

I don't think that's it.

Here's a variant where B is not instantiated with C:

----
struct A()
{
    ~this() { enum s = C.sizeof; }
}

struct B() { A!() foo; }
struct C { B!() bar; }
----

How to compute C?
Check members.
For member `B!() bar;`, resolve `B!()`.
Check members of `B!()`.
For member `A!() foo;` resolve `A!()`.
Check members of `A!()`.
No members => size = 0 (or rather 1, I guess).
Bubble up.

But the compiler seems to get confused by the destructor. I guess it incorrectly(?) sees a need to analyze C again before it can finish up `A!()`?