www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Why code failed to compile for foo2?

reply apz28 <home home.com> writes:
void foo1(ubyte x) {}
void foo1(ushort x) {}
void foo1(uint x) {}
void foo1(ulong x) {}

import std.traits : isUnsigned, Unqual;
void foo2(T)(Unqual!T x) if(isUnsigned!T) {}

     void main()
     {
         import std.math;

         int s = 1;
         foo1(abs(s));
         foo2(abs(s)); //failed?
     }

/*
onlineapp.d(15): Error: template `onlineapp.foo2` cannot deduce 
function from argument types `!()(int)`
onlineapp.d(7):        Candidate is: `foo2(T)(Unqual!T x)`
*/
Dec 11 2021
parent reply Adam Ruppe <destructionator gmail.com> writes:
On Saturday, 11 December 2021 at 22:50:45 UTC, apz28 wrote:
 void foo2(T)(Unqual!T x) if(isUnsigned!T) {}
This means it treats foo2 as if it doesn't exist unless T is unsigned...
 onlineapp.d(15): Error: template `onlineapp.foo2` cannot deduce 
 function from argument types `!()(int)`
 onlineapp.d(7):        Candidate is: `foo2(T)(Unqual!T x)`
 */
And this is telling you it had a signed value - int - which means it doesn't match. For the other ones, the functions still exist, so it does whatever conversion it needs. Whereas with the template that constraint means the compiler acts like it doesn't exist at all and thus doesn't even attempt an automatic conversion.
Dec 11 2021
parent reply Stanislav Blinov <stanislav.blinov gmail.com> writes:
On Saturday, 11 December 2021 at 22:59:52 UTC, Adam Ruppe wrote:
 On Saturday, 11 December 2021 at 22:50:45 UTC, apz28 wrote:
 void foo2(T)(Unqual!T x) if(isUnsigned!T) {}
This means it treats foo2 as if it doesn't exist unless T is unsigned...
 onlineapp.d(15): Error: template `onlineapp.foo2` cannot 
 deduce function from argument types `!()(int)`
 onlineapp.d(7):        Candidate is: `foo2(T)(Unqual!T x)`
 */
And this is telling you it had a signed value - int - which means it doesn't match. For the other ones, the functions still exist, so it does whatever conversion it needs. Whereas with the template that constraint means the compiler acts like it doesn't exist at all and thus doesn't even attempt an automatic conversion.
? No. If it was unsatisfied constraint, the error would've shown that. What's going on here is that the argument being passed is an int, when the parameter is an Unqual!T. So, https://dlang.org/spec/template.html#ifti would fail because there is no way of figuring out a T from an Unqual!T. Hence the message: cannot deduce function.
Dec 11 2021
parent reply Adam Ruppe <destructionator gmail.com> writes:
On Saturday, 11 December 2021 at 23:17:17 UTC, Stanislav Blinov 
wrote:
 ? No. If it was unsatisfied constraint, the error would've 
 shown that.
And if you try to instantiate it, you'll see it is an unsatisfied constraint anyway. There's two layers of failure here. Using Unqual there is pretty iffy, i wouldn't bother with it at all, but if you do anything, instead qualify it const. But either way, then the constraint still fails since int isn't unsigned. I'd really recommend simplifying this a lot.
Dec 11 2021
next sibling parent reply Stanislav Blinov <stanislav.blinov gmail.com> writes:
On Saturday, 11 December 2021 at 23:44:59 UTC, Adam Ruppe wrote:
 On Saturday, 11 December 2021 at 23:17:17 UTC, Stanislav Blinov 
 wrote:
 ? No. If it was unsatisfied constraint, the error would've 
 shown that.
And if you try to instantiate it, you'll see it is an unsatisfied constraint anyway. There's two layers of failure here. Using Unqual there is pretty iffy, i wouldn't bother with it at all, but if you do anything, instead qualify it const. But either way, then the constraint still fails since int isn't unsigned. I'd really recommend simplifying this a lot.
??? There is no unsatisfied constraint, it doesn't get there :)
 unq.d(6): Error: template `unq.foo2` cannot deduce function 
 from argument types `!()(int)`
 unq.d(2):        Candidate is: `foo2(T)(Unqual!T x)`
...because passed argument - `int` - becomes `Unqual!T`, making `T` unknown - template arg list is empty. Deduction fails :) If `int` is an `Unqual!T`, what is `T`? The constraint is testing `T`. Now, if you explicitly instantiate - sure, you'd get unsatisfied constraint. But the OP seems to want IFTI, which simply can't work here. apz28, I can't figure out the intent here. To convert result of abs to an unsigned?
Dec 11 2021
parent reply apz28 <home home.com> writes:
On Sunday, 12 December 2021 at 00:02:25 UTC, Stanislav Blinov 
wrote:
  apz28, I can't figure out the intent here. To convert result 
 of abs to an unsigned?
The function logic works only for unsigned type and the parameter value can be altered in body hence Unqual. If not Unqual, must declare a local var and make a copy. Just asking if this can be done to avoid a cast by caller
Dec 11 2021
next sibling parent Tejas <notrealemail gmail.com> writes:
On Sunday, 12 December 2021 at 03:02:28 UTC, apz28 wrote:
 On Sunday, 12 December 2021 at 00:02:25 UTC, Stanislav Blinov 
 wrote:
  apz28, I can't figure out the intent here. To convert result 
 of abs to an unsigned?
The function logic works only for unsigned type and the parameter value can be altered in body hence Unqual. If not Unqual, must declare a local var and make a copy. Just asking if this can be done to avoid a cast by caller
AFAIK stuff like `const int` implicitly converts to `int` since `int` is passed by value so the `const`ness of the original value is not violated. That's why code like: ```d void main(){ const int a = 55; int b = a; } ``` compiles and executes. So, in a similar vein, your code will transform to: ```d void foo1(ubyte x) {} void foo1(ushort x) {} void foo1(uint x) {} void foo1(ulong x) {} import std.traits : Unconst; import std.stdio : writeln; void foo2(T)(T x) if(is(Unconst!(T) : ulong)) {//You don't need Unqual for this foo3(x); } void foo3(ulong param){ //I'm assuming this is your function that you will pass your uint to writeln(param); } void main() { import std.math; int s = int.min + 1; //abs returns int.min for abs(int.min) lol XD foo1(abs(s)); foo2(abs(s)); //failed? //Not anymore :D } ``` Again, please remember that `std.math.algebraic:abs` doesn't return `uint` for `int` and so on, please see [the documentation](https://dlang.org/phobos/std_math_algebraic.html#.abs):
Returns:
The absolute value of the number. If floating-point or 
integral, the __return type__ will be the __same as the input__.
Limitations
Does not work correctly for signed intergal types and value 
Num.min.
Dec 13 2021
prev sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 12/11/21 10:02 PM, apz28 wrote:
 On Sunday, 12 December 2021 at 00:02:25 UTC, Stanislav Blinov wrote:
  apz28, I can't figure out the intent here. To convert result of abs 
 to an unsigned?
The function logic works only for unsigned type and the parameter value can be altered in body hence Unqual. If not Unqual, must declare a local var and make a copy. Just asking if this can be done to avoid a cast by caller
To translate a bit here, what apz28 is looking for is, given ANY value type that implicitly converts from the qualified value to the unqualified value, generate *one* function that only accepts the unqualified value. You can wrap like: ```d void foo2(T)(T v) if (!is(Unqual!T == T)) { foo(Unqual!T(t)); } ``` This will inline as if you called it directly (which should just whittle down to a direct call to the correct foo2). But D isn't currently smart enough to see through aliases, so IFTI will not work for what you are doing, even in a case like: ```d template foo2(T) if (!isUnqual!T == T) { alias foo2 = .foo2!(Unqual!T); } ``` It would be nice if IFTI gave some hooks between the parameter and the deduction, but it currently doesn't. -Steve
Dec 13 2021
parent reply Tejas <notrealemail gmail.com> writes:
On Monday, 13 December 2021 at 20:43:51 UTC, Steven Schveighoffer 
wrote:
 On 12/11/21 10:02 PM, apz28 wrote:
 On Sunday, 12 December 2021 at 00:02:25 UTC, Stanislav Blinov 
 wrote:
  apz28, I can't figure out the intent here. To convert result 
 of abs to an unsigned?
The function logic works only for unsigned type and the parameter value can be altered in body hence Unqual. If not Unqual, must declare a local var and make a copy. Just asking if this can be done to avoid a cast by caller
To translate a bit here, what apz28 is looking for is, given ANY value type that implicitly converts from the qualified value to the unqualified value, generate *one* function that only accepts the unqualified value. You can wrap like: ```d void foo2(T)(T v) if (!is(Unqual!T == T)) { foo(Unqual!T(t)); } ``` This will inline as if you called it directly (which should just whittle down to a direct call to the correct foo2). But D isn't currently smart enough to see through aliases, so IFTI will not work for what you are doing, even in a case like: ```d template foo2(T) if (!isUnqual!T == T) { alias foo2 = .foo2!(Unqual!T); } ``` It would be nice if IFTI gave some hooks between the parameter and the deduction, but it currently doesn't. -Steve
Is there anything wrong with the answer I posted? Can you please tell me if there's anything dissatisfactory about it? I feel like it does everything the OP wants. Also, am I wrong in using `Unconst` over `Unqual`? Isn't `Unqual` overkill if you just want to cast away `const`?
Dec 13 2021
next sibling parent reply apz28 <home home.com> writes:
On Tuesday, 14 December 2021 at 05:04:46 UTC, Tejas wrote:
 Is there anything wrong with the answer I posted?

 Can you please tell me if there's anything dissatisfactory 
 about it? I feel like it does everything the OP wants.

 Also, am I wrong in using `Unconst` over `Unqual`? Isn't 
 `Unqual` overkill if you just want to cast away `const`?
1. A template function should behave as to replace 4 overload functions There is special logic for parameter type (2 vs 4...) 2. Your implementation does not remove 'const' as below import std.traits : Unconst; import std.stdio : writeln; void foo2(T)(T x) if(is(Unconst!(T) : ulong)) {//You don't need Unqual for this pragma(msg, T.stringof); writeln(x); } void main() { import std.math; const int s1 = -3; int s2 = -2; foo2(abs(s1)); foo2(abs(s2)); enum byte b1 = 0; const byte b2; byte b3; foo2(b1); foo2(b2); foo2(b3); } --Output const(int) int byte const(byte) 3 2 0 0 0
Dec 14 2021
parent Tejas <notrealemail gmail.com> writes:
On Tuesday, 14 December 2021 at 13:25:04 UTC, apz28 wrote:
 On Tuesday, 14 December 2021 at 05:04:46 UTC, Tejas wrote:
 Is there anything wrong with the answer I posted?

 Can you please tell me if there's anything dissatisfactory 
 about it? I feel like it does everything the OP wants.

 Also, am I wrong in using `Unconst` over `Unqual`? Isn't 
 `Unqual` overkill if you just want to cast away `const`?
1. A template function should behave as to replace 4 overload functions There is special logic for parameter type (2 vs 4...) 2. Your implementation does not remove 'const' as below import std.traits : Unconst; import std.stdio : writeln; void foo2(T)(T x) if(is(Unconst!(T) : ulong)) {//You don't need Unqual for this pragma(msg, T.stringof); writeln(x); } void main() { import std.math; const int s1 = -3; int s2 = -2; foo2(abs(s1)); foo2(abs(s2)); enum byte b1 = 0; const byte b2; byte b3; foo2(b1); foo2(b2); foo2(b3); } --Output const(int) int byte const(byte) 3 2 0 0 0
Then I suggest using `inout`, if you're trying to reduce number of template instantiations ```d import std.traits : Unconst; import std.stdio : writeln; void foo2(T)(inout(T) x) if(is(Unconst!(T) : ulong)) {//You don't need Unqual for this pragma(msg, T.stringof); writeln(x); } void main(){ import std.math; const int ci = -3; int i = -2; immutable int ii = 24342; foo2(abs(ci)); foo2(abs(i)); foo2(ii); byte b = 0; const byte cb; immutable byte ib; foo2(b); foo2(cb); foo2(ib); const long cl = 4554; long l = 12313; immutable long il = 3242343; foo2(cl); foo2(l); foo2(il); } Output(Compile-time): int byte long Output(Runtime): 3 2 24342 0 0 0 4554 12313 3242343 ```
Dec 14 2021
prev sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 12/14/21 12:04 AM, Tejas wrote:
 Is there anything wrong with the answer I posted?
 
 Can you please tell me if there's anything dissatisfactory about it? I 
 feel like it does everything the OP wants.
 
 Also, am I wrong in using `Unconst` over `Unqual`? Isn't `Unqual` 
 overkill if you just want to cast away `const`?
Unqual is fine for value types, it should work in all cases. The OP's problem is that it's entirely possible to build an overload set with *just* the unqualified types specified (in this case, an integral type), but there's not a way to express that and still have it work with IFTI. In other words, if you have a call like: ```d const int x; foo2(x); ``` You want to have the parameter be mutable inside foo2. There currently isn't a way to express that if `foo2` is an IFTI template. The opposite is actually easily expressable: ```d void foo2(T)(const(T) val) { // only one instantiation of foo2 per const(int), immutable(int), int, // and now val is const, even if the argument is not } ``` With a standard function it works just fine due to implicit conversion, but with IFTI, there's no way to express it because it goes through an alias. The closest you can come is to write a wrapper shim that calls the right instantiation. This should be OK as long as inlining is happening, but it seems like extra work for the optimizer, when it should be easy to express in the language somehow. BTW, there is a related issue: https://issues.dlang.org/show_bug.cgi?id=1807 -Steve
Dec 14 2021
parent Tejas <notrealemail gmail.com> writes:
On Tuesday, 14 December 2021 at 15:14:40 UTC, Steven 
Schveighoffer wrote:
 On 12/14/21 12:04 AM, Tejas wrote:
 Is there anything wrong with the answer I posted?
 
 Can you please tell me if there's anything dissatisfactory 
 about it? I feel like it does everything the OP wants.
 
 Also, am I wrong in using `Unconst` over `Unqual`? Isn't 
 `Unqual` overkill if you just want to cast away `const`?
Unqual is fine for value types, it should work in all cases. The OP's problem is that it's entirely possible to build an overload set with *just* the unqualified types specified (in this case, an integral type), but there's not a way to express that and still have it work with IFTI. In other words, if you have a call like: ```d const int x; foo2(x); ``` You want to have the parameter be mutable inside foo2. There currently isn't a way to express that if `foo2` is an IFTI template. The opposite is actually easily expressable: ```d void foo2(T)(const(T) val) { // only one instantiation of foo2 per const(int), immutable(int), int, // and now val is const, even if the argument is not } ``` With a standard function it works just fine due to implicit conversion, but with IFTI, there's no way to express it because it goes through an alias. The closest you can come is to write a wrapper shim that calls the right instantiation. This should be OK as long as inlining is happening, but it seems like extra work for the optimizer, when it should be easy to express in the language somehow. BTW, there is a related issue: https://issues.dlang.org/show_bug.cgi?id=1807 -Steve
Yeah, now I understand he's trying to reduce number of instantiations. In that case, I'd use `inout`, but it'd be the same as your case : can't modify the argument anymore. But we can pass that to another function that accepts `ulong` (as that seems to be the OP's usecase anyways), which can then modify it :D ```d import std.traits : Unconst; import std.stdio : writeln; void foo2(T)(inout(T) x) if(is(Unconst!(T) : ulong)) {//You don't need Unqual for this pragma(msg, T.stringof); foo3(x); } void foo3(ulong param){ writeln(param, " before"); param +=10; writeln(param, " after"); // can also modify params now } void main(){ import std.math; const int ci = -3; int i = -2; immutable int ii = 24342; foo2(abs(ci)); foo2(abs(i)); foo2(ii); byte b = 0; const byte cb; immutable byte ib; foo2(b); foo2(cb); foo2(ib); const long cl = 4554; long l = 12313; immutable long il = 3242343; foo2(cl); foo2(l); foo2(il); } ```
Dec 14 2021
prev sibling parent apz28 <home home.com> writes:
On Saturday, 11 December 2021 at 23:44:59 UTC, Adam Ruppe wrote:
 On Saturday, 11 December 2021 at 23:17:17 UTC, Stanislav Blinov 
 wrote:
 ? No. If it was unsatisfied constraint, the error would've 
 shown that.
And if you try to instantiate it, you'll see it is an unsatisfied constraint anyway. There's two layers of failure here. Using Unqual there is pretty iffy, i wouldn't bother with it at all, but if you do anything, instead qualify it const. But either way, then the constraint still fails since int isn't unsigned. I'd really recommend simplifying this a lot.
1. This is why there is a diverse logic for language rule vs template rule. The overload functions enjoy all benefits of implicit conversion rule but none for template 2. This is why template implementation create more function bloats more than it needs to be as compilable example below import std.traits : isUnsigned, Unqual; void foo2(T, alias UT1 = Unqual!T)(T x) // You can create alias UT1 but not able to use it in function parameter if(isUnsigned!T) { alias UT2 = Unqual!T; pragma(msg, T.stringof); pragma(msg, UT1.stringof); pragma(msg, UT2.stringof); } void main() { import std.math; int s = 1; foo2(cast(uint)abs(s)); foo2(cast(const(uint))abs(s)); } Output as below uint uint uint const(uint) uint uint
Dec 13 2021