Greetings.
I've faced strange behaviour while working on my project.
// First snipped
//---
struct IrcEvent(T)
{
private T[] _container;
bool opOpAssign(string op, R)(R dlg)
if (op == "+")
{
_container ~= dlg;
return true;
}
}
class IrcClient
{
public IrcEvent!(void delegate(string)) OnMessageReceived;
}
class Foo
{
static public void Dispatch(IrcClient session)
{
auto operator = new Foo;
session.OnMessageReceived += &operator.Dispatch;
}
public void Dispatch(string msg)
{
}
}
void main()
{
auto irc = new IrcClient;
Foo.Dispatch(irc);
}
//---
Compiler refuse to compile this code with such message:
//---
./test.d(10): Error: cannot append type void delegate(IrcClient session) to type void delegate(string)[]
./test.d(27): Error: template instance test.IrcEvent!(void delegate(string)).IrcEvent.opOpAssign!("+",void delegate(IrcClient session)) error instantiating
//---
But when I change position of Dispatch methods in Foo class...
//Second source
//---
struct IrcEvent(T)
{
private T[] _container;
bool opOpAssign(string op, R)(R dlg)
if (op == "+")
{
_container ~= dlg;
return true;
}
}
class IrcClient
{
public IrcEvent!(void delegate(string)) OnMessageReceived;
}
class Foo
{
public void Dispatch(string msg)
{
}
static public void Dispatch(IrcClient session)
{
auto operator = new Foo;
session.OnMessageReceived += &operator.Dispatch;
}
}
void main()
{
auto irc = new IrcClient;
Foo.Dispatch(irc);
}
//---
Compiler doesn't complain!
The only difference between those 2 code snippets is position of Dispatch method.
In first code example non-static version of method is under static version and in second example static version of method is under non-static version.
In my opinion this is a bug, compiler should look at methods definitions and try to fit delegate not fail on first attempt.
Best regards,
Damian Ziemba
Comment #1 by hsteoh — 2014-11-18T05:03:16Z
Bug is still present in git HEAD. Tested on Linux/64.
Comment #2 by dlang-bugzilla — 2017-06-26T01:56:15Z
I'm pretty sure this is not reasonably possible to make work.
The expression &operator.Dispatch is ambiguous, as it can refer to either the static or non-static method. Now, the D compiler has some internal capabilities of working with overload sets. However, in this case, this ambiguous expression is passed as a template value parameter to opOpAssign. Because it is passed as a regular parameter (and not e.g. alias), then it must have a type, so the compiler is forced to pick one overload at this point (assuming this didn't happen earlier, for the & operator). Only when the append occurs inside the opOpAssign implementation can the overload set be reasonably resolved to the one or other overload; however, at that point, the opOpAssign method had already been instantiated with a concrete type, so it is too late to do anything.
So, for this to work, the compiler would need to speculatively instantiate the function with the type of each overload in the overload set, which would almost surely be prohibitively complicated and expensive.
The other option would be to prefer non-static overloads when a class instance (not just type) is supplied before the dot, however I believe that the overload resolution rules are currently complicated enough to warrant this arguably strange and easily avoidable use case.
I'll leave this open because picking an overload by a "whichever comes first" rule is problematic - probably neither of the presented test cases should compile.
Comment #3 by robert.schadek — 2024-12-13T17:58:28Z