Thread overview
Is this a compiler error? "recursive template expansion"
Dec 08, 2020
Nathan S.
Dec 08, 2020
Basile B.
Dec 08, 2020
Nathan S.
December 08, 2020
The following code fails to compile. Is this a compiler error or if not what is wrong with the code?

---
struct Template2(T)
{
    // If both of the following are removed compilation succeeds
    // without any other changes:
    enum tString = T.stringof;
    static if (is(T == class))
        enum tLinkage = __traits(getLinkage, T);
}

struct Template1(Param1, Param2 = Template2!Param1) {}

// Moving the definition of AliasTemplate1S after the definition of S
// causes compilation to succeed without any other changes.
alias AliasTemplate1S = Template1!S;

class S
{
    // If the following line is removed compilation succeeds
    // without any other changes.
    Template1!int x;
}

void main()
{
}
---

Failure message:
main.d(20): Error: struct main.Template1(Param1, Param2 = Template2!Param1) recursive template expansion
main.d(20):        while looking for match for Template1!int
main.d(7): Error: class S is forward referenced
main.d(10): Error: template instance main.Template2!(S) error instantiating
main.d(14):        instantiated from here: Template1!(S)
December 08, 2020
On Tuesday, 8 December 2020 at 20:11:40 UTC, Nathan S. wrote:
> The following code fails to compile. Is this a compiler error or if not what is wrong with the code?

What is wrong is that partial specialization is not correct.
The correct partial specialization is:

---
struct Template2(T)
{
    enum tString = T.stringof;
    static if (is(T == class))
        enum tLinkage = __traits(getLinkage, T);
}

struct Template1(Param1, Param2 = Template2!Param1) {}

alias AliasTemplate1S(SecondParam) = Template1!(S,SecondParam);
//                    ^here

class S
{
    Template1!int x;
}
---

Now that being said, the compiler could complain about the incorrect partial spec instead of falling in the nonsensical error message.

There is a gap because the second template param looks optional so you dont put it the partial specialization but it is still required.

Anyway. This case is either a "bad diagnostic" or an "enhancement" request.
Or should be specified.
December 08, 2020
On Tuesday, 8 December 2020 at 22:01:52 UTC, Basile B. wrote:
> On Tuesday, 8 December 2020 at 20:11:40 UTC, Nathan S. wrote:
>> The following code fails to compile. Is this a compiler error or if not what is wrong with the code?
>
> What is wrong is that partial specialization is not correct.
> The correct partial specialization is:
>
> ---
> struct Template2(T)
> {
>     enum tString = T.stringof;
>     static if (is(T == class))
>         enum tLinkage = __traits(getLinkage, T);
> }
>
> struct Template1(Param1, Param2 = Template2!Param1) {}
>
> alias AliasTemplate1S(SecondParam) = Template1!(S,SecondParam);
> //                    ^here
>
> class S
> {
>     Template1!int x;
> }
> ---
>
> Now that being said, the compiler could complain about the incorrect partial spec instead of falling in the nonsensical error message.
>
> There is a gap because the second template param looks optional so you dont put it the partial specialization but it is still required.
>
> Anyway. This case is either a "bad diagnostic" or an "enhancement" request.
> Or should be specified.

Thanks a lot! In my case what I was intending was:

alias AliasTemplate1S = Template1!(S, Template2!S)

which as you suggest works fine. It's a bit odd that the non-optional second parameter becomes optional again if declaration order is shuffled, but my motivation to look into this has temporarily abated since it's no longer stopping me from doing something else.

For the program where I ran into this problem the most convenient fix turns out to be to get rid of the default template parameter and instead use a pattern like this:

---
struct Template1(Param1, Param2) {}
alias Template1(Param1) = Template1!(Param1, Template2!Param1);
---