www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Template Instantiation Bug

reply "Jonathan Marler" <johnnymarler gmail.com> writes:
In the past I don't get much response when I file a bug in 
BugZilla so I wanted to see if anyone could tell me anything 
about this issue in the forums.

I believe a bug has already been created here: 
https://issues.dlang.org/show_bug.cgi?id=2372

Here's the code to reproduce it:
--------------------------------------
import std.stdio;

template Transform(T)
{
   alias Transform = T;
}
void testTransform(T)(Transform!T t)
{
}
void testNoTransform(T)(T t)
{
}
void main(string[] args)
{
   testTransform(3);     // FAILS "cannot deduce function..."
   testTransform!int(3);

   testNoTransform(3);
   testNoTransform!int(3);
}
--------------------------------------

The test(3) line fails with this error message:

main.d(15): Error: template main.testTransform cannot deduce 
function from argument types !()(int), candidates are:
main.d(7):        main.testTransform(T)(Transform!T t)

Does anyone know anything about this, maybe this is "as 
designed"?  Thanks in advance for the info.
Nov 04 2014
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 11/4/14 11:01 AM, Jonathan Marler wrote:
 In the past I don't get much response when I file a bug in BugZilla so I
 wanted to see if anyone could tell me anything about this issue in the
 forums.

 I believe a bug has already been created here:
 https://issues.dlang.org/show_bug.cgi?id=2372

 Here's the code to reproduce it:
 --------------------------------------
 import std.stdio;

 template Transform(T)
 {
    alias Transform = T;
 }
 void testTransform(T)(Transform!T t)
 {
 }
 void testNoTransform(T)(T t)
 {
 }
 void main(string[] args)
 {
    testTransform(3);     // FAILS "cannot deduce function..."
    testTransform!int(3);

    testNoTransform(3);
    testNoTransform!int(3);
 }
 --------------------------------------

 The test(3) line fails with this error message:

 main.d(15): Error: template main.testTransform cannot deduce function
 from argument types !()(int), candidates are:
 main.d(7):        main.testTransform(T)(Transform!T t)

 Does anyone know anything about this, maybe this is "as designed"?
 Thanks in advance for the info.
This is as designed. The reason is because the compiler cannot deduce *backwards* how to instantiate a template to get the right result. I ran into this a long time ago when porting Tango. Here is the counter-case: template Transform(T) { static if(is(T == string)) alias Transform = int; else static if(is(T == int)) alias Transform = string; else alias Transform = T; } Basically, the compiler has to figure out how to instantiate Transform in order for it to result in an int. While it's easy for us to see what it needs to do, it doesn't have that capability. People have argued in the past that anything with a simple alias should be doable. I don't know if that's true or not, but I don't think it works today. There may be a bug report somewhere on it, I didn't search. The bug you found is not it. -Steve
Nov 04 2014
next sibling parent reply "Jonathan Marler" <johnnymarler gmail.com> writes:
On Tuesday, 4 November 2014 at 16:55:10 UTC, Steven Schveighoffer 
wrote:
 This is as designed.

 The reason is because the compiler cannot deduce *backwards* 
 how to instantiate a template to get the right result.

 I ran into this a long time ago when porting Tango.

 Here is the counter-case:

 template Transform(T)
 {
     static if(is(T == string)) alias Transform = int;
     else static if(is(T == int)) alias Transform = string;
     else alias Transform = T;
 }

 Basically, the compiler has to figure out how to instantiate 
 Transform in order for it to result in an int. While it's easy 
 for us to see what it needs to do, it doesn't have that 
 capability.
Ah I see now. After reading your comment I had to walk through how the compiler works out the types but after doing so it makes sense. I've included my thoughts to aid any others with the same question. Given a template signature like this: void mytemplate(T)(T t) Whenever a call to the template does not specify the template parameter T, the compiler needs to be able to "deduce" the type of T. In the example given above, T is equal to the type of the first argument "typeof(t)". If you were to apply some type of transformation to the argument, then the compiler cannot deduce T because it cannot reverse the transformation. For example, if you had the following type transformation: template Transform(T) { alias Transform = T; } The compiler does not know how to "reverse" this template, meaning, given the output of Transform, the compiler cannot deduce what the input of Transform was EVEN IF THE TEMPLATE IS AS SIMPLE AS THIS ONE. I hope this helps for anybody else with the same question.
Nov 04 2014
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 11/4/2014 9:51 AM, Jonathan Marler wrote:
 given the
 output of Transform, the compiler cannot deduce what the input of Transform was
 EVEN IF THE TEMPLATE IS AS SIMPLE AS THIS ONE.
To answer a question not asked, why doesn't the compiler see the simple case and handle it? The problem is that this becomes a special case in the language specification, making the language harder to understand. Cue bug reports of people confused about why the simpler cases work and the more complex ones do not, and they overall get a negative impression of the language. And I don't blame them.
Nov 04 2014
next sibling parent "Jonathan Marler" <johnnymarler gmail.com> writes:
On Tuesday, 4 November 2014 at 21:48:29 UTC, Walter Bright wrote:
 On 11/4/2014 9:51 AM, Jonathan Marler wrote:
 given the
 output of Transform, the compiler cannot deduce what the input 
 of Transform was
 EVEN IF THE TEMPLATE IS AS SIMPLE AS THIS ONE.
To answer a question not asked, why doesn't the compiler see the simple case and handle it? The problem is that this becomes a special case in the language specification, making the language harder to understand. Cue bug reports of people confused about why the simpler cases work and the more complex ones do not, and they overall get a negative impression of the language. And I don't blame them.
Yes, trying to reverse the logic inside a template in the GENERAL case would likely be next to impossible, or at least, require alot of work/code to support something thay may not be very useful. And if you can't solve the general case, I agree you shouldn't support the simple cases. However, I'll bring up the reason I wanted this functionality in case you have another solution. I wanted to create a template that used different parameter attributes depending on the type. For example, I just submitted a pull request to phobos to optimize the put/doPut functions in std.range (https://github.com/D-Programming-Language/phobos/pull/2655) to only use ref OutputRange when necessary. For types like pointers, classes, delegates, and functions, the ref attribute is unnecessary and can inhibit compiler optimization. For my pull request, the only solution I could come up with was to have 2 instances of each function with a template guard that handles ref and non-ref paramters: private template isPassByValue(T) { enum bool isPassByValue = isPointer!T || is( T == class ) || is( T == delegate ) || is( T == function ) ; } void put(R, E)(R r, E e) if( isPassByValue!R ) { // put code } void put(R, E)(ref R r, E e) if( !isPassByValue!R ) { // exact same put code } There's got to be a better way to do this. If anyone has any ideas let me know.
Nov 04 2014
prev sibling next sibling parent "Jonathan Marler" <johnnymarler gmail.com> writes:
On Tuesday, 4 November 2014 at 21:48:29 UTC, Walter Bright wrote:
 On 11/4/2014 9:51 AM, Jonathan Marler wrote:
 given the
 output of Transform, the compiler cannot deduce what the input 
 of Transform was
 EVEN IF THE TEMPLATE IS AS SIMPLE AS THIS ONE.
To answer a question not asked, why doesn't the compiler see the simple case and handle it? The problem is that this becomes a special case in the language specification, making the language harder to understand. Cue bug reports of people confused about why the simpler cases work and the more complex ones do not, and they overall get a negative impression of the language. And I don't blame them.
Actually I just realized that this would be IMPOSSIBLE in the general case since a template transormation isn't necessarily a 1-1 mapping. Consider, template Normalize(T) { static if(isIntegral!T) { alias Normalize = int; } else { alias Normalize = string; } } Given the output of Normalize, say int, there's no way of knowing if T was ubyte, long, int or whatever.
Nov 05 2014
prev sibling parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 11/4/14 4:48 PM, Walter Bright wrote:
 On 11/4/2014 9:51 AM, Jonathan Marler wrote:
 given the
 output of Transform, the compiler cannot deduce what the input of
 Transform was
 EVEN IF THE TEMPLATE IS AS SIMPLE AS THIS ONE.
To answer a question not asked, why doesn't the compiler see the simple case and handle it? The problem is that this becomes a special case in the language specification, making the language harder to understand. Cue bug reports of people confused about why the simpler cases work and the more complex ones do not, and they overall get a negative impression of the language. And I don't blame them.
The real problem is that D singles out specialized type construction for inference of types. For example: void foo(T)(const(T)*) This works, because D singles out type construction of an array as one that it can infer. However, try to apply this to a custom type: void foo(T)(Rebindable!(const(T))) This *doesn't* work. I wish it would, but I can understand the opposition to it. The issue is that D has gone down the path of using templates for type construction instead of adding language features, and this is one of the main drawbacks. I continue to think we should be providing mechanisms to hook IFTI. There are several cases where things that "just work" with builtins cannot be extended to custom types. -Steve
Nov 06 2014
prev sibling parent reply "Daniel Murphy" <yebbliesnospam gmail.com> writes:
"Steven Schveighoffer"  wrote in message 
news:m3b0dd$6e0$1 digitalmars.com...

 People have argued in the past that anything with a simple alias should be 
 doable. I don't know if that's true or not, but I don't think it works 
 today. There may be a bug report somewhere on it, I didn't search. The bug 
 you found is not it.
https://issues.dlang.org/show_bug.cgi?id=1807
Nov 04 2014
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 11/4/14 1:05 PM, Daniel Murphy wrote:
 "Steven Schveighoffer"  wrote in message
 news:m3b0dd$6e0$1 digitalmars.com...

 People have argued in the past that anything with a simple alias
 should be doable. I don't know if that's true or not, but I don't
 think it works today. There may be a bug report somewhere on it, I
 didn't search. The bug you found is not it.
https://issues.dlang.org/show_bug.cgi?id=1807
Hm.. interesting to note that Martin's suggestion has come to fruition, might this be revisited? -Steve
Nov 04 2014
parent "Daniel Murphy" <yebbliesnospam gmail.com> writes:
"Steven Schveighoffer"  wrote in message 
news:m3bap5$khl$1 digitalmars.com...

 https://issues.dlang.org/show_bug.cgi?id=1807
Hm.. interesting to note that Martin's suggestion has come to fruition, might this be revisited?
Not really, templated aliases behave exactly the same as aliases in templates. The difference is purely syntax. I don't think it would be a good idea to add different semantics for them.
Nov 06 2014