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