digitalmars.D - Overzealous recursive template expansion protection?
- Gareth Charnock (54/54) Nov 02 2010 I've been trying to correctly implement the interpreter
- Robert Jacques (40/94) Nov 02 2010 I'm going to lean on the side of this being a compiler bug (so please
- Gareth Charnock (3/35) Nov 03 2010 Thanks, the workaround seems to work. I've reported the bug:
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); }
Nov 02 2010
On Tue, 02 Nov 2010 22:03:47 -0400, Gareth Charnock <gareth.charnock 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)).
Nov 02 2010
On 03/11/10 03:21, Robert Jacques wrote: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)Thanks, the workaround seems to work. I've reported the bug: http://d.puremagic.com/issues/show_bug.cgi?id=5160
Nov 03 2010