www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - An example of weird template error messaging

reply Ethan <gooberman gmail.com> writes:
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" );
}
Aug 04
next sibling parent Ethan <gooberman gmail.com> writes:
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 ) )
Aug 04
prev sibling next sibling parent Ethan <gooberman gmail.com> writes:
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.
Aug 04
prev sibling parent reply ag0aep6g <anonymous example.com> writes:
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`.
Aug 04
parent ag0aep6g <anonymous example.com> writes:
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 */ } ----
Aug 04