www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Mixin template parameter overloading bug?

reply Andrey Zherikov <andrey.zherikov gmail.com> writes:
The simplest code that shows the issue:
```d
struct S{}

template f(void function(S) F) {}
template f(int function(S) F) {}

mixin f!((_) {});
mixin f!((_) => 0);  // Error: cannot return non-void from `void` 
function
                      // mixin f!((_) => 0);
                      //                 ^
                      //        while looking for match for 
`f!((_) => 0)`
                      // mixin f!((_) => 0);
```

If I swap `template f` declarations:
```d
struct S{}

template f(int function(S) F) {}
template f(void function(S) F) {}

mixin f!((_) {}); // Error: function 
`onlineapp.__lambda_L8_C10(__T1)(_)` has no `return` statement, 
but is expected to return a value of type `int`
                   // mixin f!((_) {});
                   //          ^
                   //         while looking for match for `f!((_)
                   // {
                   // }
                   // )`
                   // mixin f!((_) {});
                   // ^
mixin f!((_) => 0);
```

But if I remove `S` from template parameters, everything works:
```d
template f(int function() F) {}
template f(void function() F) {}

mixin f!(() {});
mixin f!(() => 0);
```

Is this a bug somewhere in compiler or in my code?
Jun 13
next sibling parent reply monkyyy <crazymonkyyy gmail.com> writes:
On Saturday, 14 June 2025 at 00:02:32 UTC, Andrey Zherikov wrote:
 The simplest code that shows the issue:
 ```d
 struct S{}

 template f(void function(S) F) {}
 template f(int function(S) F) {}

 mixin f!((_) {});
 mixin f!((_) => 0);  // Error: cannot return non-void from 
 `void` function
                      // mixin f!((_) => 0);
                      //                 ^
                      //        while looking for match for 
 `f!((_) => 0)`
                      // mixin f!((_) => 0);
 ```

 If I swap `template f` declarations:
 ```d
 struct S{}

 template f(int function(S) F) {}
 template f(void function(S) F) {}

 mixin f!((_) {}); // Error: function 
 `onlineapp.__lambda_L8_C10(__T1)(_)` has no `return` statement, 
 but is expected to return a value of type `int`
                   // mixin f!((_) {});
                   //          ^
                   //         while looking for match for `f!((_)
                   // {
                   // }
                   // )`
                   // mixin f!((_) {});
                   // ^
 mixin f!((_) => 0);
 ```

 But if I remove `S` from template parameters, everything works:
 ```d
 template f(int function() F) {}
 template f(void function() F) {}

 mixin f!(() {});
 mixin f!(() => 0);
 ```

 Is this a bug somewhere in compiler or in my code?
I believe every change in compilation from (top level) declaration order is considered a compiler bug However, I see.... Allot of issues with this code, Id want to see something near functional code around this subject; its worth poking, but its possible every possible way to make this code work would eliminate the pattern here for example this compiles: ```d import std; struct S{} template f(void function(S) F) {alias f=void;} template f(int function(S) F) {alias f=int;} unittest{ alias a=f!((_) {}); alias b=f!((_) => 0); a.stringof.writeln; b.stringof.writeln; } ``` mixin templates vs declaration templates was a bad decision in my opinion but thats old news.
Jun 13
parent Andrey Zherikov <andrey.zherikov gmail.com> writes:
On Saturday, 14 June 2025 at 00:26:27 UTC, monkyyy wrote:
 I believe every change in compilation from (top level) 
 declaration order is considered a compiler bug

 However, I see.... Allot of issues with this code, Id want to 
 see something near functional code around this subject; its 
 worth poking, but its possible every possible way to make this 
 code work would eliminate the pattern here

 for example this compiles:
 ```d
 import std;
 struct S{}
 template f(void function(S) F) {alias f=void;}
 template f(int function(S) F) {alias f=int;}

 unittest{
 	alias a=f!((_) {});
 	alias b=f!((_) => 0);
 	a.stringof.writeln;
 	b.stringof.writeln;
 }
 ```

 mixin templates vs declaration templates was a bad decision in 
 my opinion but thats old news.
Mixin templates and regular templates have different use cases: the former can inject declaration on caller's site while the latter can't. In my case mixin template generates top-level `main()` function but the content of the template is not important here. Also using `alias F` as template parameter doesn't allow me to introspect the actual type.
Jun 13
prev sibling next sibling parent reply Andrey Zherikov <andrey.zherikov gmail.com> writes:
On Saturday, 14 June 2025 at 00:02:32 UTC, Andrey Zherikov wrote:
Simplified test case a bit more.
This works:
```d
template f(void function(int) F) {}
template f(int function(int) F) {}

mixin f!((int _) {});
mixin f!((int _) => 0);

mixin f!((int) {});
mixin f!((int) => 0);

mixin f!((_) {});
mixin f!((_) => 0);  // Error: cannot return non-void from `void` 
function
```

Can anyone explain why adding type of the parameter in lambda 
(`int`) makes this working?
Jun 13
parent reply monkyyy <crazymonkyyy gmail.com> writes:
On Saturday, 14 June 2025 at 01:46:31 UTC, Andrey Zherikov wrote:
 On Saturday, 14 June 2025 at 00:02:32 UTC, Andrey Zherikov 
 wrote:
 Simplified test case a bit more.
 This works:
 ```d
 template f(void function(int) F) {}
 template f(int function(int) F) {}

 mixin f!((int _) {});
 mixin f!((int _) => 0);

 mixin f!((int) {});
 mixin f!((int) => 0);

 mixin f!((_) {});
 mixin f!((_) => 0);  // Error: cannot return non-void from 
 `void` function
 ```

 Can anyone explain why adding type of the parameter in lambda 
 (`int`) makes this working?
https://dlang.org/spec/template-mixin.html Your still mixing syntax; mixin templates are suppose to be a separate system according to the spec. ```d import std; void f(int function(int) F)(){"int".writeln;} void f(void function(int) F)(){"void".writeln;} unittest{ f!((_) {}); f!((_) => 0); } mixin template g(int function(int) F){string s1="int";} mixin template g(void function(int) F){string s2="void";} unittest{ mixin g!((_) {}); mixin g!((_) => 0); s1.writeln; s2.writeln; } ```
Jun 13
parent reply Andrey Zherikov <andrey.zherikov gmail.com> writes:
On Saturday, 14 June 2025 at 02:10:03 UTC, monkyyy wrote:
 On Saturday, 14 June 2025 at 01:46:31 UTC, Andrey Zherikov 
 wrote:
 On Saturday, 14 June 2025 at 00:02:32 UTC, Andrey Zherikov 
 wrote:
 Simplified test case a bit more.
 This works:
 ```d
 template f(void function(int) F) {}
 template f(int function(int) F) {}

 mixin f!((int _) {});
 mixin f!((int _) => 0);

 mixin f!((int) {});
 mixin f!((int) => 0);

 mixin f!((_) {});
 mixin f!((_) => 0);  // Error: cannot return non-void from 
 `void` function
 ```

 Can anyone explain why adding type of the parameter in lambda 
 (`int`) makes this working?
https://dlang.org/spec/template-mixin.html Your still mixing syntax; mixin templates are suppose to be a separate system according to the spec. ```d import std; void f(int function(int) F)(){"int".writeln;} void f(void function(int) F)(){"void".writeln;} unittest{ f!((_) {}); f!((_) => 0); } mixin template g(int function(int) F){string s1="int";} mixin template g(void function(int) F){string s2="void";} unittest{ mixin g!((_) {}); mixin g!((_) => 0); s1.writeln; s2.writeln; } ```
Modified your example a bit. This works: ```d mixin template f(void function(int) F) { void s1() {"void".writeln; } } mixin template f(int function(int) F) { void s2() {"int".writeln; } } void main() { mixin f!((_) {}); mixin f!((_) => 0); s1(); s2(); } ``` This does not: ```d mixin template f(void function(int) F) { void s1() {"void".writeln; } } mixin template f(int function(int) F) { void s2() {"int".writeln; } } mixin f!((_) {}); mixin f!((_) => 0); // Error: cannot return non-void from `void` function void main() { s1(); s2(); } ```
Jun 13
parent reply monkyyy <crazymonkyyy gmail.com> writes:
On Saturday, 14 June 2025 at 02:16:58 UTC, Andrey Zherikov wrote:
 On Saturday, 14 June 2025 at 02:10:03 UTC, monkyyy wrote:
 On Saturday, 14 June 2025 at 01:46:31 UTC, Andrey Zherikov 
 wrote:
 On Saturday, 14 June 2025 at 00:02:32 UTC, Andrey Zherikov 
 wrote:
 Simplified test case a bit more.
 This works:
 ```d
 template f(void function(int) F) {}
 template f(int function(int) F) {}

 mixin f!((int _) {});
 mixin f!((int _) => 0);

 mixin f!((int) {});
 mixin f!((int) => 0);

 mixin f!((_) {});
 mixin f!((_) => 0);  // Error: cannot return non-void from 
 `void` function
 ```

 Can anyone explain why adding type of the parameter in lambda 
 (`int`) makes this working?
https://dlang.org/spec/template-mixin.html Your still mixing syntax; mixin templates are suppose to be a separate system according to the spec. ```d import std; void f(int function(int) F)(){"int".writeln;} void f(void function(int) F)(){"void".writeln;} unittest{ f!((_) {}); f!((_) => 0); } mixin template g(int function(int) F){string s1="int";} mixin template g(void function(int) F){string s2="void";} unittest{ mixin g!((_) {}); mixin g!((_) => 0); s1.writeln; s2.writeln; } ```
Modified your example a bit. This works: ```d mixin template f(void function(int) F) { void s1() {"void".writeln; } } mixin template f(int function(int) F) { void s2() {"int".writeln; } } void main() { mixin f!((_) {}); mixin f!((_) => 0); s1(); s2(); } ``` This does not: ```d mixin template f(void function(int) F) { void s1() {"void".writeln; } } mixin template f(int function(int) F) { void s2() {"int".writeln; } } mixin f!((_) {}); mixin f!((_) => 0); // Error: cannot return non-void from `void` function void main() { s1(); s2(); } ```
```d import std; mixin template f(int function(int) F){} mixin template f(void function(int) F){unittest{"void".writeln;}} //mixin f!((_){}); //FAILS mixin template g(void function(int) F){unittest{"void".writeln;}} mixin template g(int function(int) F){} mixin g!((_){}); //works ``` this example makes it purely a compiler bug I cant escape any useful information(I tried `typeof(return)`), seems to be limited to just void return functions
Jun 13
parent Andrey Zherikov <andrey.zherikov gmail.com> writes:
On Saturday, 14 June 2025 at 03:17:09 UTC, monkyyy wrote:
 

 ```d
 import std;
 mixin template f(int function(int) F){}
 mixin template f(void function(int) 
 F){unittest{"void".writeln;}}

 //mixin f!((_){}); //FAILS


 mixin template g(void function(int) 
 F){unittest{"void".writeln;}}
 mixin template g(int function(int) F){}

 mixin g!((_){});  //works
 ```
 this example makes it purely a compiler bug

 I cant escape any useful information(I tried `typeof(return)`), 
 seems to be limited to just void return functions
I created https://github.com/dlang/dmd/issues/21447
Jun 14
prev sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On Saturday, 14 June 2025 at 00:02:32 UTC, Andrey Zherikov wrote:
 The simplest code that shows the issue:
 ```d
 struct S{}

 template f(void function(S) F) {}
 template f(int function(S) F) {}

 mixin f!((_) {});
 mixin f!((_) => 0);  // Error: cannot return non-void from 
 `void` function
                      // mixin f!((_) => 0);
                      //                 ^
                      //        while looking for match for 
 `f!((_) => 0)`
                      // mixin f!((_) => 0);
 ```

 If I swap `template f` declarations:
 ```d
 struct S{}

 template f(int function(S) F) {}
 template f(void function(S) F) {}

 mixin f!((_) {}); // Error: function 
 `onlineapp.__lambda_L8_C10(__T1)(_)` has no `return` statement, 
 but is expected to return a value of type `int`
                   // mixin f!((_) {});
                   //          ^
                   //         while looking for match for `f!((_)
                   // {
                   // }
                   // )`
                   // mixin f!((_) {});
                   // ^
 mixin f!((_) => 0);
 ```

 But if I remove `S` from template parameters, everything works:
 ```d
 template f(int function() F) {}
 template f(void function() F) {}

 mixin f!(() {});
 mixin f!(() => 0);
 ```

 Is this a bug somewhere in compiler or in my code?
A lambda is a shortened syntax for a function literal or delegate literal. HOWEVER, when you do not give the parameters types, the lambda becomes a template! Well, not actually a template, but a quasi-template. If you add types to your lambda, then the compiler can figure it out: ```d mixin f!((S _) {}); mixin f!((S _) => 0); ``` These are now no longer templates, but instead concrete function literals. BIG NOTE: just putting `S` doesn't work, because `S` becomes the parameter name in that case. This is why `int` might work there -- `int` cannot be a parameter name. If I had to guess, I think the compiler is not able to exactly deduce what form your lambda should be instantiated with. I think it's picking the first form it sees. I think if anything, the error message is really weird. I'd expect possibly an ambiguity error. But I'm not sure the language is supposed to handle your case. -Steve
Jun 14
next sibling parent monkyyy <crazymonkyyy gmail.com> writes:
On Saturday, 14 June 2025 at 23:49:19 UTC, Steven Schveighoffer 
wrote:
 If I had to guess, I think the compiler is not able to exactly 
 deduce what form your lambda should be instantiated with. I 
 think it's picking the first form it sees.

 I think if anything, the error message is really weird. I'd 
 expect possibly an ambiguity error. But I'm not sure the 
 language is supposed to handle your case.

 -Steve
theres correct ambiguity errors when the return types are well typed; the code was wierd but I confirm theres a bug in here
Jun 14
prev sibling parent reply Nick Treleaven <nick geany.org> writes:
On Saturday, 14 June 2025 at 23:49:19 UTC, Steven Schveighoffer 
wrote:
 A lambda is a shortened syntax for a function literal or 
 delegate literal.

 HOWEVER, when you do not give the parameters types, the lambda 
 becomes a template! Well, not actually a template, but a 
 quasi-template.
An untyped parameter does make the literal an actual template: ```d pragma(msg, __traits(isTemplate, (x) {})); // true ``` It can be instantiated with a type parameter. https://dlang.org/spec/expression.html#function-literal-alias
 If you add types to your lambda, then the compiler can figure 
 it out:

 ```d
 mixin f!((S _) {});
 mixin f!((S _) => 0);
 ```

 These are now no longer templates, but instead concrete 
 function literals.
When there are no parameters, a literal is not a template: ```d pragma(msg, __traits(isTemplate, () {})); // false alias f = () {}; pragma(msg, is(typeof(*f) == function)); // true ``` So I think there must be another reason why your unary literals work.
Jun 15
next sibling parent Monkyyy <crazymonkyyy gmail.com> writes:
On Sunday, 15 June 2025 at 10:51:37 UTC, Nick Treleaven wrote:
 So I think there must be another reason why your unary literals 
 work.
Given it's specifically void return, and top level and it acts like it matches the first return, I expect that void is incorrectly being considered a invalid return type thru 1 very specific pathway of the compiler
Jun 15
prev sibling parent reply Manfred Nowak <svv1999 hotmail.com> writes:
On Sunday, 15 June 2025 at 10:51:37 UTC, Nick Treleaven wrote:

 An untyped parameter does make the literal an actual template:
 ```d
 pragma(msg, __traits(isTemplate, (x) {})); // true
 ```
 It can be instantiated with a type parameter.
Therefore in the case discussed, the code `(_){}' declares a template. This template is used as an actual parameter to a parameterized mixin. If used and in general, templates must be instantiated and the instantiatons stored for later use. It seems, that for a parameterized mixin those actual instantiatons are not deleted when they do not conform with the formal parameter of the mixin.
Jun 15
parent Nick Treleaven <nick geany.org> writes:
On Sunday, 15 June 2025 at 16:14:28 UTC, Manfred Nowak wrote:
 On Sunday, 15 June 2025 at 10:51:37 UTC, Nick Treleaven wrote:

 An untyped parameter does make the literal an actual template:
 ```d
 pragma(msg, __traits(isTemplate, (x) {})); // true
 ```
 It can be instantiated with a type parameter.
Therefore in the case discussed, the code `(_){}' declares a template. This template is used as an actual parameter to a parameterized mixin.
Yes, sorry I misread.
Jun 17