www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Weird template instantiation problem

reply Arafel <er.krali gmail.com> writes:
Hi,

I've found a strange problem, and I'm not sure if it's a bug. To give a 
bit of background, I'm implementing a multi-threaded producer-consumer 
where the next work item to be picked depends not only on the "waiting 
queue", but also on what else is being run (and potentially where) at 
the same moment, so things like "sort"'ing the queue won't probably 
work, because I don't think you use a delegate as a predicate for "sort" 
(that's what I think it would be needed to get the extra context 
information).

The idea here is that the "chooser" function returns the *index* of the 
work item to be picked.

So, the reduced problem looks like this (I've removed the extra 
information about the running jobs to make the example simpler):

```
enum defaultChooser(T) = function size_t(T[] queue) {
	return 0;
};

struct S(T, size_t function(T[]) chooser = defaultChooser!T) {
}

void main() {
	S!int s;
}
```

this fails and I get this:

 Error: template instance S!int does not match template declaration S(T, ulong
function(T[]) chooser = defaultChooser!T)
If instead of returning the index the actual item is returned, it works! ``` enum defaultChooser(T) = function T(T[] queue) { return queue[0]; }; struct S(T, T function(T[]) chooser = defaultChooser!T) { } void main() { S!int s; } ``` As you can see, the only change is the type the function returns, but I don't see how it should make any difference. Also, changing from "enum" to "static immutable", or even removing the "enum" and directly embedding the function literal doesn't seem to make any difference. Any ideas on what might be going on??
Jun 12
next sibling parent Arafel <er.krali gmail.com> writes:
On 06/12/2017 05:31 PM, Arafel wrote:
 Hi,
 
 I've found a strange problem, and I'm not sure if it's a bug. To give a 
 bit of background, I'm implementing a multi-threaded producer-consumer 
 where the next work item to be picked depends not only on the "waiting 
 queue", but also on what else is being run (and potentially where) at 
 the same moment, so things like "sort"'ing the queue won't probably 
 work, because I don't think you use a delegate as a predicate for "sort" 
 (that's what I think it would be needed to get the extra context 
 information).
 
 The idea here is that the "chooser" function returns the *index* of the 
 work item to be picked.
 
 So, the reduced problem looks like this (I've removed the extra 
 information about the running jobs to make the example simpler):
 
 ```
 enum defaultChooser(T) = function size_t(T[] queue) {
      return 0;
 };
 
 struct S(T, size_t function(T[]) chooser = defaultChooser!T) {
 }
 
 void main() {
      S!int s;
 }
 ```
 
 this fails and I get this:
 
 Error: template instance S!int does not match template declaration 
 S(T, ulong function(T[]) chooser = defaultChooser!T)
If instead of returning the index the actual item is returned, it works! ``` enum defaultChooser(T) = function T(T[] queue) { return queue[0]; }; struct S(T, T function(T[]) chooser = defaultChooser!T) { } void main() { S!int s; } ``` As you can see, the only change is the type the function returns, but I don't see how it should make any difference. Also, changing from "enum" to "static immutable", or even removing the "enum" and directly embedding the function literal doesn't seem to make any difference. Any ideas on what might be going on??
Even more strange: ``` enum defaultChooser(T) = function size_t(T[] queue) { return 0; }; static assert (is (typeof(defaultChooser!int) == size_t function(int[] queue) pure nothrow nogc safe)); struct S(T, size_t function(T[] queue) pure nothrow nogc safe chooser) { } void main() { S!(int, defaultChooser!int) s; } ``` The static assert passes (tried with the wrong values), yet I get this error message:
 Error: template instance S!(int, function ulong(int[] queue) => 0LU) does not
match template declaration S(T, ulong function(T[] queue) pure nothrow  nogc
 safe chooser) 
Am I missing something fundamental? But then, why does it work if I change the return type in the template parameter?
Jun 12
prev sibling next sibling parent reply ketmar <ketmar ketmar.no-ip.org> writes:
more funny compiler messages:

	alias xx = size_t function (int[]);
	struct S1(T, typeof(xx) X) {}
	void main() {
	  S1!(int, defaultChooser!int) s;
	}

Error: type uint function(int[]) is not an expression

but:

	struct S2(T, typeof(defaultChooser!T) chooser=defaultChooser!T) {}
	void main() {
	  S2!int s;
	}

Error: undefined identifier T

error messages are totally random (and why `typeof()` is not allowed there?)
Jun 12
parent reply ketmar <ketmar ketmar.no-ip.org> writes:
p.s.: while i understand the technical reason for second error message, it 
is still random and confusing.
Jun 12
parent reply Arafel <er.krali gmail.com> writes:
On Monday, 12 June 2017 at 19:23:10 UTC, ketmar wrote:
 p.s.: while i understand the technical reason for second error 
 message, it is still random and confusing.
I think the reason for the typeof problem is that it works with expressions, not with types (so, typeof (int) is also not valid), and the alias resolves ultimately to a type. I actually found a workaround for the original issue: ``` enum defaultChooser(T) = function size_t(T[] queue) { return 0; }; struct S(T, alias chooser = defaultChooser!int) if (is(typeof(chooser) : size_t function(T[]))) { } void main() { S!(int, defaultChooser!int) s; } ``` This works, but strangely if I try "==" instead of ":" in the template condition, then it fails again. Honestly I don't know why it makes a difference, I guess attribute inference might be at fault... but in the version with the "static assert" I was explicitly checking them, and they apparently matched... Also, this is just a(n ugly) workaround, and there might be side effects of using an alias parameter that I'm not aware of... and more importantly, I still think the original version should work! ;-)
Jun 12
parent reply ketmar <ketmar ketmar.no-ip.org> writes:
Arafel wrote:

 I actually found a workaround for the original issue:
yeah, sorry for not proposing a workaround: i thought that you already did it, and now you're just interested why the original code doesn't work. ;-) i think that this is a bug (or, rather, unimplemented feature).
Jun 12
parent Arafel <er.krali gmail.com> writes:
Well, I had kind of found a workaround (changing the return type to 
return the element and not the index) which I didn't like too much (what 
if there are duplicates?).

Now that I've found a "proper" workaround.... well, I'm still interested 
in knowing the reason, if possible, or if it's a bug.

On 06/12/2017 09:49 PM, ketmar wrote:
 yeah, sorry for not proposing a workaround: i thought that you already 
 did it, and now you're just interested why the original code doesn't 
 work. ;-)
 
 i think that this is a bug (or, rather, unimplemented feature).
Jun 13
prev sibling parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 6/12/17 11:31 AM, Arafel wrote:

 As you can see, the only change is the type the function returns, but I
 don't see how it should make any difference.

 Also, changing from "enum" to "static immutable", or even removing the
 "enum" and directly embedding the function literal doesn't seem to make
 any difference.

 Any ideas on what might be going on??
Looks like a bug to me. Please file. -Steve
Jun 13