Thread overview
Specialization Not Allowed for Deduced Parameter
Aug 22, 2014
Yota
Aug 22, 2014
Yota
Aug 22, 2014
Ali Çehreli
Aug 22, 2014
Marc Schütz
Aug 22, 2014
Ali Çehreli
Aug 23, 2014
Yota
Aug 23, 2014
Ali Çehreli
Aug 23, 2014
Ali Çehreli
August 22, 2014
Heya.  I'm working on a simple units-of-measure implementation in DMD 2.066.0, and it doesn't seem to like the signature of my '*' operator below.  I'm afraid I don't understand what the error description is trying to tell me.  Here's a reduced case:

public struct UnitDef(string unitString) {
	// Error: "specialization not allowed for deduced parameter N"
	auto opBinary(string op, UT : UnitDef!N, string N)(UT rhs) if (op == "*") {
		return UnitDef!(unitString ~ " " ~ N)();
	}
}

enum ft = UnitDef!"ft"();
enum s = UnitDef!"s"();

pragma(msg, typeof(ft*s));

I've tried the same code in DPaste, and it compiles just fine.


Can anyone shed some light on what I may be doing wrong?  I always seem to get bitten when working with template arguments, so it's probably just me.
August 22, 2014
On Friday, 22 August 2014 at 20:42:49 UTC, Yota wrote:
> Heya.  I'm working on a simple units-of-measure implementation in DMD 2.066.0, and it doesn't seem to like the signature of my '*' operator below.  I'm afraid I don't understand what the error description is trying to tell me.  Here's a reduced case:

Same results for this simpler signature.

public struct UnitDef(string unitString) {
	auto opBinary(string op, string N)(UnitDef!N rhs)
	if (op == "*") {
		return UnitDef!(unitString ~ " " ~ N)();
	}
}
August 22, 2014
On 08/22/2014 01:45 PM, Yota wrote:
> On Friday, 22 August 2014 at 20:42:49 UTC, Yota wrote:
>> Heya.  I'm working on a simple units-of-measure implementation in DMD
>> 2.066.0, and it doesn't seem to like the signature of my '*' operator
>> below.  I'm afraid I don't understand what the error description is
>> trying to tell me.  Here's a reduced case:
>
> Same results for this simpler signature.
>
> public struct UnitDef(string unitString) {
>      auto opBinary(string op, string N)(UnitDef!N rhs)
>      if (op == "*") {
>          return UnitDef!(unitString ~ " " ~ N)();
>      }
> }

I don't know the details about how it should work but the following is a workaround:

public struct UnitDef(string unitString) {
    alias US = unitString;

    auto opBinary(string op, That)(That rhs)
        if (is (That == UnitDef!(That.US)) &&
            op == "*") {
        return UnitDef!(unitString ~ " " ~ rhs.US)();
    }
}

void main()
{
    auto u = UnitDef!"hello"();
    auto result = u * u;
    pragma(msg, result.US);
}

I admit that the template constraint is strange because it hopes that 'That' is an instance of UnitDef by reaching for its .US member and then it also checks whether the type equals that. :p

Admittedly, std.traits.isInstanceOf is the right tool to use but both of the following worked!

    auto opBinary(string op, That)(That rhs)
        if (isInstanceOf!(UnitDef, That) &&
            op == "*") {
        return UnitDef!(unitString ~ " " ~ rhs.US)();
    }

    auto opBinary(string op, That)(That rhs)
        if (isInstanceOf!(That, UnitDef) &&    // <-- REVERSED
            op == "*") {
        return UnitDef!(unitString ~ " " ~ rhs.US)();
    }

Note the reversed template arguments of isInstanceOf. Still works... Is that a bug?

Ali

August 22, 2014
On Friday, 22 August 2014 at 21:22:39 UTC, Ali Çehreli wrote:
> On 08/22/2014 01:45 PM, Yota wrote:
>> On Friday, 22 August 2014 at 20:42:49 UTC, Yota wrote:
>>> Heya.  I'm working on a simple units-of-measure implementation in DMD
>>> 2.066.0, and it doesn't seem to like the signature of my '*' operator
>>> below.  I'm afraid I don't understand what the error description is
>>> trying to tell me.  Here's a reduced case:
>>
>> Same results for this simpler signature.
>>
>> public struct UnitDef(string unitString) {
>>     auto opBinary(string op, string N)(UnitDef!N rhs)
>>     if (op == "*") {
>>         return UnitDef!(unitString ~ " " ~ N)();
>>     }
>> }
>
> I don't know the details about how it should work but the following is a workaround:
>
> public struct UnitDef(string unitString) {
>     alias US = unitString;
>
>     auto opBinary(string op, That)(That rhs)
>         if (is (That == UnitDef!(That.US)) &&
>             op == "*") {
>         return UnitDef!(unitString ~ " " ~ rhs.US)();
>     }
> }
>
> void main()
> {
>     auto u = UnitDef!"hello"();
>     auto result = u * u;
>     pragma(msg, result.US);
> }
>
> I admit that the template constraint is strange because it hopes that 'That' is an instance of UnitDef by reaching for its .US member and then it also checks whether the type equals that. :p
>
> Admittedly, std.traits.isInstanceOf is the right tool to use but both of the following worked!
>
>     auto opBinary(string op, That)(That rhs)
>         if (isInstanceOf!(UnitDef, That) &&
>             op == "*") {
>         return UnitDef!(unitString ~ " " ~ rhs.US)();
>     }
>
>     auto opBinary(string op, That)(That rhs)
>         if (isInstanceOf!(That, UnitDef) &&    // <-- REVERSED
>             op == "*") {
>         return UnitDef!(unitString ~ " " ~ rhs.US)();
>     }
>
> Note the reversed template arguments of isInstanceOf. Still works... Is that a bug?
>
> Ali

Don't know either, but it was introduced in this PR, according to
Digger:
https://github.com/D-Programming-Language/dmd/pull/3536
which by its title shouldn't have this effect.

Note that the regression mentioned there by Vladimir was posted
there erroneously, the linked bug was actually caused by a
different PR.
August 22, 2014
On 08/22/2014 02:22 PM, Ali Çehreli wrote:

> Admittedly, std.traits.isInstanceOf is the right tool to use

Answering myself: Yes, it is.

> but both of
> the following worked!

I figured that out.

>      auto opBinary(string op, That)(That rhs)
>          if (isInstanceOf!(UnitDef, That) &&

Note that UnitDef above means the template instance UnitDef!unitString. (There is such a shortcut in D and C++.)

Since I've been using the same type in my test code, UnitDef!unitString and That were the same type. (Hm. Does that mean that one is the instance of the other? Will have to test that separately.)

So, the correct check should use std.traits.TemplateOf first:

    auto opBinary(string op, That)(That rhs)
        if (isInstanceOf!(TemplateOf!UnitDef, That) &&
            op == "*") {
        return UnitDef!(unitString ~ " " ~ rhs.US)();
    }

Now, that's correct and allows different instances:

import std.traits;

public struct UnitDef(string unitString) {
    alias US = unitString;

    auto opBinary(string op, That)(That rhs)
        if (isInstanceOf!(TemplateOf!UnitDef, That) &&
            op == "*") {
            pragma(msg, typeof(this));
            pragma(msg, That);
        return UnitDef!(unitString ~ " " ~ rhs.US)();
    }
}

void main()
{
    auto u = UnitDef!"hello"();
    auto v = UnitDef!"world"();
    auto result = u * v;
    pragma(msg, result.US);
}

Ali

August 23, 2014
On Friday, 22 August 2014 at 21:38:29 UTC, Ali Çehreli wrote:
> So, the correct check should use std.traits.TemplateOf first:
>
>     auto opBinary(string op, That)(That rhs)
>         if (isInstanceOf!(TemplateOf!UnitDef, That) &&
>             op == "*") {
>         return UnitDef!(unitString ~ " " ~ rhs.US)();
>     }
>
> Now, that's correct and allows different instances:
>
> import std.traits;
>
> public struct UnitDef(string unitString) {
>     alias US = unitString;
>
>     auto opBinary(string op, That)(That rhs)
>         if (isInstanceOf!(TemplateOf!UnitDef, That) &&
>             op == "*") {
>             pragma(msg, typeof(this));
>             pragma(msg, That);
>         return UnitDef!(unitString ~ " " ~ rhs.US)();
>     }
> }
>
> void main()
> {
>     auto u = UnitDef!"hello"();
>     auto v = UnitDef!"world"();
>     auto result = u * v;
>     pragma(msg, result.US);
> }
>
> Ali

This certainly did the trick.  Thanks!

So what's up with the syntax I tried before?  Has it been
deprecated?
August 23, 2014
On 08/22/2014 07:47 PM, Yota wrote:

> So what's up with the syntax I tried before?  Has it been
> deprecated?

I don't know the details. I am curious as well.

I think it is related to value template parameters. The first example below fails but the second one works. The only difference is that the one that fails uses a string value parameter like your original code did.

// This program FAILS
struct A(string S)
{}

void foo(T : A!S, string S)(T t)  // <- string value
{}

void main()
{
    auto a = A!"hello"();
    foo(a);
}

// This program WORKS
struct A(S)
{}

void foo(T : A!S, S : int)(T t)  // <- int parameter
{}

void main()
{
    auto a = A!int();
    foo(a);
}

So, I don't know whether it should work but at least the previous programs show that it is indeed possible to specialize using a "deduced parameter" (i.e. S being 'int' in the program that works).

Ali

August 23, 2014
On 08/22/2014 02:38 PM, Ali Çehreli wrote:

> Does that mean that one is the
> instance of the other? Will have to test that separately.)

I opened the following bug about isInstanceOf!(Foo!int, Foo!int) producing 'true':

  https://issues.dlang.org/show_bug.cgi?id=13364

Ali