www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Aliasing multiple delegates to the same name - very strange behaviour

reply Meta <jared771 gmail.com> writes:
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?
Feb 24 2018
next sibling parent reply Nicholas Wilson <iamthewilsonator hotmail.com> writes:
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));
 }
Feb 24 2018
parent Meta <jared771 gmail.com> writes:
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?
Feb 24 2018
prev sibling parent reply Basile B. <b2.temp gmx.com> writes:
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.
Feb 24 2018
parent reply Meta <jared771 gmail.com> writes:
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.
Feb 24 2018
parent reply user1234 <user1234 12.nl> writes:
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.
Feb 25 2018
parent Meta <jared771 gmail.com> writes:
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?
Feb 25 2018