Bug 12698 – Overloads from multiple modules implicitly merge into a single overloadset
Status
RESOLVED
Resolution
INVALID
Severity
normal
Priority
P1
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2014-05-03T20:56:00Z
Last change time
2014-05-04T13:09:52Z
Assigned to
nobody
Creator
andrej.mitrovich
Comments
Comment #0 by andrej.mitrovich — 2014-05-03T20:56:41Z
I thought the following was supposed to emit a compiler error to prevent function hijacking:
-----
import std.algorithm; // defines copy
import std.file; // defines another copy (unrelated)
void main()
{
char[] src, target;
copy(src, target);
}
-----
No errors here, the copy from std.file is picked here.
But I thought we're required to explicitly merge overload sets via:
-----
alias copy = std.algorithm.copy;
alias copy = std.file.copy;
-----
Comment #1 by issues.dlang — 2014-05-03T22:36:22Z
Well, if there are overloads in two different modules, and a particular invocation of that function only can match one of them, then overload sets don't really get involved. I suspect that the issue is that because std.file.copy explicitly lists "in char[]", whereas std.algorithm.copy is templatized, std.file.copy is considered to match, whereas std.algorithm.copy isn't - and the overload sets are only brought in if it could match both. As such, it would be a side effect of how Kenji chose to solve the issue of making templated functions overloadable with non-templated functions.
After having to work around how the overloading rules now work, it's my conclusion that it only works to overload templated functions with a non-templated function when what they work with is completely disjoint, which tends to mean that it doesn't make sense to overload templated functions with non-templated functions. But if the repercussions of those overload rules leaks into the compiler's decision to use overload sets or not, then we have a big problem.
I suspect that part of the problem stems from the fact that the compiler seems to want to decide whether it wants to use a templated function or not before decided which overload to use - to the point that they aren't even considered if a match is found before then - whereas IMHO, each of the templated overloads should be on equal footing with all other overloads. The current situation seems too much like two-phase lookup in C++, which is a monstrosity that we at least tried to kill off in D.
Regardless, it looks like different sets of function overload rules are mixing very badly right now.
Comment #2 by k.hara.pg — 2014-05-04T11:02:15Z
(In reply to Andrej Mitrovic from comment #0)
> I thought the following was supposed to emit a compiler error to prevent
> function hijacking:
>
> -----
> import std.algorithm; // defines copy
> import std.file; // defines another copy (unrelated)
>
> void main()
> {
> char[] src, target;
> copy(src, target);
> }
> -----
>
> No errors here, the copy from std.file is picked here.
Why do you think this is an issue? std.algorithm.copy does not match to (char[], char[]) arguments, so std.file.copy is always chosen.
It follows the cross-module overload set resolution rule.
Comment #3 by andrej.mitrovich — 2014-05-04T11:11:06Z
(In reply to Kenji Hara from comment #2)
> Why do you think this is an issue? std.algorithm.copy does not match to
> (char[], char[]) arguments, so std.file.copy is always chosen.
Because it's accidental. char[] is not an output range, but byte[] is. So the user might without notice call functions with completely different semantics:
-----
/// Just image if the user did "import std;"
/// and the "std" package pull was merged,
/// it would be similar to the below situation:
import std.algorithm;
import std.array;
import std.conv;
import std.exception;
import std.file;
import std.range;
import std.stdio;
import std.string;
import std.traits;
import std.typecons;
import std.typetuple;
void main()
{
byte[] src = [1, 2, 3];
byte[] tgt = new byte[](3);
copy(src, tgt);
writeln(tgt); // [1, 2, 3]
char[] srcname = "newname.txt".dup;
char[] tgtname = "oldname.txt".dup;
// actually *writes to a file*,
// potentially overwriting existing contents
copy(srcname, tgtname);
}
-----
Comment #4 by k.hara.pg — 2014-05-04T11:27:23Z
(In reply to Andrej Mitrovic from comment #3)
> Because it's accidental. char[] is not an output range, but byte[] is.
This is because of the definition of output range.
So this is Phobos issue, not compiler's.
Comment #5 by andrej.mitrovich — 2014-05-04T12:39:54Z
(In reply to Kenji Hara from comment #4)
> (In reply to Andrej Mitrovic from comment #3)
> > Because it's accidental. char[] is not an output range, but byte[] is.
>
>
> This is because of the definition of output range.
> So this is Phobos issue, not compiler's.
I thought disallowing this was the point of function hijacking prevention?
Comment #6 by k.hara.pg — 2014-05-04T13:00:52Z
(In reply to Andrej Mitrovic from comment #5)
> (In reply to Kenji Hara from comment #4)
> > (In reply to Andrej Mitrovic from comment #3)
> > > Because it's accidental. char[] is not an output range, but byte[] is.
> >
> >
> > This is because of the definition of output range.
> > So this is Phobos issue, not compiler's.
>
> I thought disallowing this was the point of function hijacking prevention?
If a 'copy' call matches only for std.file.copy, the importing of std.algorithm won't hijack the call. Therefore, there's *no* hijacking which should be prevented. We cannot disallow nonexistent hijacking.
Comment #7 by andrej.mitrovich — 2014-05-04T13:09:52Z
(In reply to Kenji Hara from comment #6)
> If a 'copy' call matches only for std.file.copy, the importing of
> std.algorithm won't hijack the call. Therefore, there's *no* hijacking which
> should be prevented. We cannot disallow nonexistent hijacking.
Right, I went to re-read the docs again[1] and you're right. I guess I would use "import a : copy" syntax more often, but I'll wait for 314 and friends to be fixed first. :)
[1] : http://dlang.org/hijack.html