Thread overview
Aliasing multiple delegates to the same name - very strange behaviour
Feb 25, 2018
Meta
Feb 25, 2018
Nicholas Wilson
Feb 25, 2018
Meta
Feb 25, 2018
Basile B.
Feb 25, 2018
Meta
Feb 25, 2018
user1234
Feb 25, 2018
Meta
February 25, 2018
I just filed this bug: https://issues.dlang.org/show_bug.cgi?id=18520

Not only does the following code compile and link successfully, it prints 0 three times when ran:

alias f = (int n) => 0;
alias f = (char c) => 'a';
alias f = (bool b) => false;

void main()
{
    import std.stdio;
    writeln(f(int.init));  //Prints 0
    writeln(f(char.init)); //Prints 0
    writeln(f(bool.init)); //Prints 0
}

However, when I change the code to the following, it works as one could reasonably expect, given the circumstances:

int  f1(int n)  { return 0; }
char f2(char c) { return 'a'; }
bool f3(bool b) { return false; }

alias f = f1;
alias f = f2;
alias f = f3;

void main()
{
    import std.stdio;
    writeln(f(int.init));  //Prints 0
    writeln(f(char.init)); //Prints 'a'
    writeln(f(bool.init)); //Prints false
}

I've got some questions:

1. Which is the intended behaviour? Should this code fail to compile and there's a bug with aliases, or should this code compile and my first example work correctly, but there is currently a bug where this feature interacts badly with function/delegate literals?

2. If the answer to 1 is "this could should compile and work correctly", in what cases does D allow multiple aliases with the same name to be defined, as in my first and second example (which compile without issue)?

3. Is this related to overload sets in some way?

4. Is there any different semantically or mechanically between my first and second examples?
February 25, 2018
On Sunday, 25 February 2018 at 04:06:43 UTC, Meta wrote:
> I just filed this bug: https://issues.dlang.org/show_bug.cgi?id=18520
>
> Not only does the following code compile and link successfully, it prints 0 three times when ran:
>
> alias f = (int n) => 0;
> alias f = (char c) => 'a';
> alias f = (bool b) => false;
>
> void main()
> {
>     import std.stdio;
>     writeln(f(int.init));  //Prints 0
>     writeln(f(char.init)); //Prints 0
>     writeln(f(bool.init)); //Prints 0
> }
> [...]
> 4. Is there any different semantically or mechanically between my first and second examples?

Type promotions to int maybe?
Have you tried casting them?

> void main()
> {
>     import std.stdio;
>     writeln(f(cast(int)int.init));
>     writeln(f(cast(char)char.init));
>     writeln(f(cast(bool)bool.init));
> }
February 25, 2018
On Sunday, 25 February 2018 at 04:06:43 UTC, Meta wrote:
> I just filed this bug: https://issues.dlang.org/show_bug.cgi?id=18520
>
> Not only does the following code compile and link successfully, it prints 0 three times when ran:
>
> alias f = (int n) => 0;
> alias f = (char c) => 'a';
> alias f = (bool b) => false;
>
> void main()
> {
>     import std.stdio;
>     writeln(f(int.init));  //Prints 0
>     writeln(f(char.init)); //Prints 0
>     writeln(f(bool.init)); //Prints 0
> }
>
> However, when I change the code to the following, it works as one could reasonably expect, given the circumstances:
>
> int  f1(int n)  { return 0; }
> char f2(char c) { return 'a'; }
> bool f3(bool b) { return false; }
>
> alias f = f1;
> alias f = f2;
> alias f = f3;
>
> void main()
> {
>     import std.stdio;
>     writeln(f(int.init));  //Prints 0
>     writeln(f(char.init)); //Prints 'a'
>     writeln(f(bool.init)); //Prints false
> }
>
> I've got some questions:
>
> 1. Which is the intended behaviour? Should this code fail to compile and there's a bug with aliases, or should this code compile and my first example work correctly, but there is currently a bug where this feature interacts badly with function/delegate literals?
>
> 2. If the answer to 1 is "this could should compile and work correctly", in what cases does D allow multiple aliases with the same name to be defined, as in my first and second example (which compile without issue)?
>
> 3. Is this related to overload sets in some way?
>
> 4. Is there any different semantically or mechanically between my first and second examples?

Use templates to prevent implicit conversion:

alias f(T = int) = (T n) => 0;
alias f(T = char) = (T n) => 'a';
alias f(T = bool) = (T n) => false;

Bug report is invalid and can be closed.
February 25, 2018
On Sunday, 25 February 2018 at 04:47:47 UTC, Nicholas Wilson wrote:
> On Sunday, 25 February 2018 at 04:06:43 UTC, Meta wrote:
>> I just filed this bug: https://issues.dlang.org/show_bug.cgi?id=18520
>>
>> Not only does the following code compile and link successfully, it prints 0 three times when ran:
>>
>> alias f = (int n) => 0;
>> alias f = (char c) => 'a';
>> alias f = (bool b) => false;
>>
>> void main()
>> {
>>     import std.stdio;
>>     writeln(f(int.init));  //Prints 0
>>     writeln(f(char.init)); //Prints 0
>>     writeln(f(bool.init)); //Prints 0
>> }
>> [...]
>> 4. Is there any different semantically or mechanically between my first and second examples?
>
> Type promotions to int maybe?
> Have you tried casting them?
>
>> void main()
>> {
>>     import std.stdio;
>>     writeln(f(cast(int)int.init));
>>     writeln(f(cast(char)char.init));
>>     writeln(f(cast(bool)bool.init));
>> }

Ah, I tried changing it to the following:

struct NoPromote {}

alias f = (int n) => 0;
alias f = (char c) => 'a';
alias f = (NoPromote np) => NoPromote();

void main()
{
    import std.stdio;
    writeln(f(int.init));       //Prints 0
    writeln(f(char.init));      //Prints 0
    writeln(f(NoPromote.init)); //Prints 0
}

And I get "Error: function literal __lambda5 (int n) is not callable using argument types (NoPromote)". It was already apparent from the fact that the program printed 0 each time, but this confirms that the first function literal is the only one that _really_ gets aliased to f. Actually, this is unnecessary, because if I just change the order and move the bool function up to be the first, I get "Error: function literal __lambda4 (bool b) is not callable using argument types (char)".

Did I mention how much I hate the fact that char and bool implicitly convert to int?


February 25, 2018
On Sunday, 25 February 2018 at 04:59:58 UTC, Basile B. wrote:
> Use templates to prevent implicit conversion:
>
> alias f(T = int) = (T n) => 0;
> alias f(T = char) = (T n) => 'a';
> alias f(T = bool) = (T n) => false;
>
> Bug report is invalid and can be closed.

Please don't be so hasty. The main focus of that defect is whether it is a bug or a feature that the same alias can be declared multiple times. I've updated the title to reflect that.
February 25, 2018
On Sunday, 25 February 2018 at 05:16:21 UTC, Meta wrote:
> On Sunday, 25 February 2018 at 04:59:58 UTC, Basile B. wrote:
>> Use templates to prevent implicit conversion:
>>
>> alias f(T = int) = (T n) => 0;
>> alias f(T = char) = (T n) => 'a';
>> alias f(T = bool) = (T n) => false;
>>
>> Bug report is invalid and can be closed.
>
> Please don't be so hasty. The main focus of that defect is whether it is a bug or a feature that the same alias can be declared multiple times. I've updated the title to reflect that.

Aliases are not things, they are what they alias. In your case all are functions so this is an overload set.
February 25, 2018
On Sunday, 25 February 2018 at 08:07:03 UTC, user1234 wrote:
> On Sunday, 25 February 2018 at 05:16:21 UTC, Meta wrote:
>> On Sunday, 25 February 2018 at 04:59:58 UTC, Basile B. wrote:
>>> Use templates to prevent implicit conversion:
>>>
>>> alias f(T = int) = (T n) => 0;
>>> alias f(T = char) = (T n) => 'a';
>>> alias f(T = bool) = (T n) => false;
>>>
>>> Bug report is invalid and can be closed.
>>
>> Please don't be so hasty. The main focus of that defect is whether it is a bug or a feature that the same alias can be declared multiple times. I've updated the title to reflect that.
>
> Aliases are not things, they are what they alias. In your case all are functions so this is an overload set.

I was about to say that no such syntax for creating an overload set exists, but I found this tucked away in the documentation (https://dlang.org/spec/function.html#overload-sets):

Overload sets can be merged with an alias declaration:

import A;
import B;

alias foo = A.foo;
alias foo = B.foo;

void bar(C c)
{
    foo();    // calls A.foo()
    foo(1L);  // calls A.foo(long)
    foo(c);   // calls B.foo(C)
    foo(1,2); // error, does not match any foo
    foo(1);   // calls B.foo(int)
    A.foo(1); // calls A.foo(long)
}

So it looks like this *is* valid syntax, in which case the question becomes, is it intended behaviour that you can use the overload set syntax with function literals? I can't think of any possible method of assigning the same name to different literals. Where is the bug? Being able to add function literals to an overload set, or the fact that the set only contains the first function and none of the subsequently added ones?