www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Value based overload resolution?

reply kdevel <kdevel vogtner.de> writes:
Today I came across this:

~~~id.d
import std.stdio : writeln;

T foo (T) (T s)
{
    __PRETTY_FUNCTION__.writeln;
    return s;
}

short foo (short s)
{
    __PRETTY_FUNCTION__.writeln;
    return s;
}

T id (T) (T t)
{
    return t;
}

int main ()
{
    foo (1);
    foo (1L);
    foo (id (1));
    foo (id (1L));
    foo (0x1_0000);
    foo (0x1_0000L);
    return 0;
}
~~~

Output:

$ ./id
short id.foo(short s)
short id.foo(short s)
int id.foo!int.foo(int s)
long id.foo!long.foo(long s)
int id.foo!int.foo(int s)
long id.foo!long.foo(long s)

It appears to me that the overload resolution may depend on the 
/value/ of the function argument. According to [1] the type of 1 
is int and that of 1L is long. Thus I would have expected foo!int 
and foo!long being called in those cases.

[1] https://dlang.org/spec/lex.html#integerliteral
Nov 09 2020
next sibling parent Adam D. Ruppe <destructionator gmail.com> writes:
On Monday, 9 November 2020 at 22:04:55 UTC, kdevel wrote:
 It appears to me that the overload resolution may depend on the 
 /value/ of the function argument. According to [1] the type of 
 1 is int and that of 1L is long.
That's not exactly true because of value range propagation. It is weird in this case. But the intention is for stuff like short a = 1; to work without an explicit cast from 1 being an int. So the compiler looks at what it can see in the expression - exact values of literals, ranges of possible values from other variables - and determines the smallest type it can fit inside. Then it automatically assigns it that type instead. What gets pretty weird is if you add a bool overload and pass 1.... the compiler considers bool to be a very restrained integral type... and it knows the values of `enum` too which can give some surprising results. But yeah the values or the possible ranges of values can actually affect the types of arithmetic expressions and this is considered in overloading for better or for worse.
Nov 09 2020
prev sibling parent Paul Backus <snarwin gmail.com> writes:
On Monday, 9 November 2020 at 22:04:55 UTC, kdevel wrote:
 It appears to me that the overload resolution may depend on the 
 /value/ of the function argument. According to [1] the type of 
 1 is int and that of 1L is long. Thus I would have expected 
 foo!int and foo!long being called in those cases.

 [1] https://dlang.org/spec/lex.html#integerliteral
As you've discovered, the types of integer literals (and literals in general) is somewhat fluid: the *default* type of `1` is `int`, but the compiler may infer a different type based on the value, or on the context in which the literal is used. For example: static assert(is(typeof([1, 2, 3]) == int[])); int[] a = [1, 2, 3]; ubyte[] b = [1, 2, 3]; Perhaps even more confusingly, this also applies to manifest (enum) constants: enum literal = [1, 2, 3]; static assert(is(typeof(literal) == int[])); int[] a = literal; ubyte[] b = literal;
Nov 09 2020