Overzealous recursive template expansion protection?

Robert Jacques sandford at jhu.edu
Tue Nov 2 20:21:46 PDT 2010


On Tue, 02 Nov 2010 22:03:47 -0400, Gareth Charnock  
<gareth.charnock at gmail.com> wrote:
> I've been trying to correctly implement the interpreter  
> patten/expression templates in D (for reference this is a summary of the  
> C++ interpreter patten can be found here  
> http://www.drdobbs.com/184401627). I've run into a problem and I'm not  
> sure if it's a compiler bug or not. The testcase is:
>
> struct BinaryOp(L,string op,R) {
> 	pragma(msg,"Instansiating " ~ typeof(this).stringof);
> 	BinaryOp!(typeof(this),s,R1) opBinary(string s,R1)(R1 r) {
> 		pragma(msg,"Instansiating BinaryOp.opBinary ~L.stringof ~ op ~  
> R1.stringof);
> 		return typeof(return)();
> 	}
> }
>
> struct Leaf {
> 	BinaryOp!(typeof(this),s,R) opBinary(string s,R)(R r) {
> 		pragma(msg,"Instansiating leaf.opBinary(" ~ R.stringof ~ ")");
> 		return typeof(return)();
> 	}
> };
>
> void main() {
> 	Leaf v1,v2,v3;
> 	pragma(msg,"");
> 	pragma(msg,"======= This Compiles ======");
> 	v1*(v2*v3);
> 	pragma(msg,"");
> 	pragma(msg,"======= This Doesn't ======");
>   	(v1*v2)*v3;
> }
> Output:
> ======= This Compiles ======
> Instansiating BinaryOp!(Leaf,s,Leaf)
> Instansiating leaf.opBinary(Leaf)
> Instansiating BinaryOp!(Leaf,s,BinaryOp!(Leaf,s,Leaf))
> Instansiating leaf.opBinary(BinaryOp!(Leaf,s,Leaf))
>
> ======= This Doesn't ======
> Error: recursive template expansion for template argument  
> BinaryOp!(Leaf,s,Leaf)
>
> I've tracked the problem down to the return type of BinaryOp.opBinary.  
> Clearly putting BinaryOp!(typeof(this),...) would be a Bad Thing in the  
> main template body but opBinary is a template that may or may not be  
> instantiated so it shouldn't automatically lead to runaway  
> instantiation. It seems the compiler is a little bit overzealous in  
> making sure that such runaway instantiations do not happen.
>
> Is this a bug? Should I file it? Here's what I think a minimal test case  
> might look like:
>
> struct A(T1) {
> 	void templateFunc(T2)(T2 a) {
> 		 alias A!(typeof(this)) error;
> 	}
> }
>
>
> void main() {
> 	 A!int a;
> 	 a.templateFunc!int(0);
> }
>

I'm going to lean on the side of this being a compiler bug (so please  
file), as there are multiple workarounds without logically changing  
anythings

Here's one:

struct BinaryOp(alias L,string op, R) {
	pragma(msg,"Instansiating ", typeof(this).stringof);
	BinaryOp!(BinaryOp,s~"",R1) opBinary(string s, R1)(R1 r) {
		pragma(msg,"Instansiating BinaryOp.opBinary", L.stringof, " ", op,"  
",R1.stringof);
		return typeof(return)();
	}
}

struct Leaf {
	BinaryOp!(Leaf,s~"",R) opBinary(string s,R)(R r) {
		pragma(msg,"Instansiating leaf.opBinary(", R.stringof,  ")");
		return typeof(return)();
	}
};

And here's another

struct BinaryOp(L,string op, R) {
	pragma(msg,"Instansiating ", typeof(this).stringof);
	BinaryOp!(BinaryOp,s,R1) opBinary(string s, R1)(R1 r) {
		pragma(msg,"Instansiating BinaryOp.opBinary", L.stringof, " ", op,"  
",R1.stringof);
		return typeof(return)();
	}
}

struct Leaf {
	BinaryOp!(Leaf,s~"",R) opBinary(string s,R)(R r) {
		pragma(msg,"Instansiating leaf.opBinary(", R.stringof,  ")");
		return typeof(return)();
	}
};

In general, when passing template value parameters to another template,  
I'd recommend performing a no-op on them (i.e. ~"" or +0), since sometimes  
they're passed as N or op instead of 10 or "+". Also, your can use the  
template name inside it to refer to that instance's type (i.e. you don't  
have to use typeof(this)).


More information about the Digitalmars-d mailing list