Thread overview
An example of weird template error messaging
Aug 04, 2019
Ethan
Aug 04, 2019
Ethan
Aug 04, 2019
Ethan
Aug 04, 2019
ag0aep6g
Aug 04, 2019
ag0aep6g
August 04, 2019
Cropped up with my code, I was making a registry system in a generic manner so that I could reuse the code later down the line on other projects if necessary.

Did some minimal reproduction in run.dlang.io and determined that the problem comes down to template constraints. If you compile the code you currently get the following error message:

Error: template instance onlineapp.Template!(ObjectOneImpl, ObjectBase) is used as a type

Well that tells me nothing. So let's dig. If you delete ApplyRight and import std.meta : ApplyRight you instead get:

Error: template instance `SmartAlias!(Template!(ObjectOneImpl, ObjectBase))` recursive template expansion

Okay. That gives me more information, but doesn't actually tell me what the problem is.

So let's poke at code. If you delete the constraint on ObjectRefImpl, everything compiles and runs exactly like you'd expect.

Ah ha. So that's what its beef is. By inspecting the type it needs a complete representation of the type. The type includes an ObjectRefImpl instantiated with a type that includes an ObjectRefImpl to the current type. So checking the constraints gets in to a recursive loop.

I want to submit this as a bug, but there's like several different issues here that need identifying so that I can report it properly.

----------

template ApplyRight( alias Template, Right... )
{
    alias ApplyRight( Left... ) = Template!( Left, Right );
}

struct ObjectRefImpl( Type, BaseType ) if( is( Type Super == super ) && is( Super == BaseType ) )
{
	Type refval;
}

alias ObjRef = ApplyRight!( ObjectRefImpl, ObjectBase );

class ObjectBase
{
	string name;
}

alias ObjectOne = ObjRef!ObjectOneImpl;

class ObjectOneImpl : ObjectBase
{
	ObjectTwo two;
}

alias ObjectTwo = ObjRef!ObjectTwoImpl;

class ObjectTwoImpl : ObjectBase
{
	ObjectOne one;
}

void main()
{
	import std.stdio : writeln;

	ObjectOne one = { new ObjectOneImpl() };
	writeln( "Oh hi Mark" );
}

August 04, 2019
On Sunday, 4 August 2019 at 15:18:40 UTC, Ethan wrote:
> struct ObjectRefImpl( Type, BaseType ) if( is( Type Super == super ) && is( Super == BaseType ) )

(Yes that should read is( Super[ 0 ] == BaseType ) )
August 04, 2019
On Sunday, 4 August 2019 at 15:18:40 UTC, Ethan wrote:
> So let's poke at code. If you delete the constraint on ObjectRefImpl, everything compiles and runs exactly like you'd expect.

I should also note that if you remove that constraint and rely on a static assert inside ObjectRefImpl to enforce the constraint that the code also compiles and runs as expected.
August 04, 2019
On 04.08.19 17:18, Ethan wrote:
> ----------
> 
> template ApplyRight( alias Template, Right... )
> {
>      alias ApplyRight( Left... ) = Template!( Left, Right );
> }
> 
> struct ObjectRefImpl( Type, BaseType ) if( is( Type Super == super ) && is( Super == BaseType ) )
> {
>      Type refval;
> }
> 
> alias ObjRef = ApplyRight!( ObjectRefImpl, ObjectBase );
> 
> class ObjectBase
> {
>      string name;
> }
> 
> alias ObjectOne = ObjRef!ObjectOneImpl;
> 
> class ObjectOneImpl : ObjectBase
> {
>      ObjectTwo two;
> }
> 
> alias ObjectTwo = ObjRef!ObjectTwoImpl;
> 
> class ObjectTwoImpl : ObjectBase
> {
>      ObjectOne one;
> }
> 
> void main()
> {
>      import std.stdio : writeln;
> 
>      ObjectOne one = { new ObjectOneImpl() };
>      writeln( "Oh hi Mark" );
> }
> 

Reduced further:

----
struct ObjectRefImpl1() if (is(ObjectOneImpl Super == super)) {}
struct ObjectRefImpl2() if (is(ObjectTwoImpl Super == super)) {}

alias ObjectOne = ObjectRefImpl1!();

class ObjectOneImpl
{
    alias Two = ObjectRefImpl2!();
}

class ObjectTwoImpl
{
    ObjectOne one; /* Error: template instance `test.ObjectRefImpl1!()` is used as a type */
}
----

Compilation succeeds if `ObjectOne` is moved below `ObjectOneImpl`.
August 04, 2019
On 04.08.19 22:58, ag0aep6g wrote:
> Reduced further:
> 
> ----
> struct ObjectRefImpl1() if (is(ObjectOneImpl Super == super)) {}
> struct ObjectRefImpl2() if (is(ObjectTwoImpl Super == super)) {}
> 
> alias ObjectOne = ObjectRefImpl1!();
> 
> class ObjectOneImpl
> {
>      alias Two = ObjectRefImpl2!();
> }
> 
> class ObjectTwoImpl
> {
>      ObjectOne one; /* Error: template instance `test.ObjectRefImpl1!()` is used as a type */
> }
> ----
> 
> Compilation succeeds if `ObjectOne` is moved below `ObjectOneImpl`.

And further:

----
struct ObjectRefImpl() if (is(ObjectTwoImpl Super == super)) {}

alias ObjectOne = ObjectRefImpl!();

class ObjectTwoImpl
{
    ObjectOne one; /* Error: template instance `test.ObjectRefImpl!()` is used as a type */
}
----