Bug 12359 – implicit overload merging with selective imports should be removed
Status
RESOLVED
Resolution
WONTFIX
Severity
enhancement
Priority
P2
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2014-03-13T17:22:11Z
Last change time
2022-08-22T12:30:52Z
Keywords
pull
Assigned to
No Owner
Creator
Kenji Hara
Comments
Comment #0 by k.hara.pg — 2014-03-13T17:22:11Z
Currently, selective or renamed imports create alias declaration implicitly.
import a : b;
// is mostly equivalent with:
// import a; alias b = a.b;
import a : x = b;
// is mostly equivalent with:
// import a; alias x = a.b;
But it is problematic behavior, because it would implicitly merge overloads in the imported module. For example:
import std.ascii : isDigit; // bool isDigit(dchar c)
bool isDigit(char c) { return true; }
// Here isDigit is an overload set of bool(char) and bool(dchar)
void main() {
dchar d = 'a';
isDigit(d); // matches to std.ascii.isDiigt
}
I think import declarations should only handle symbol visibility. So the current behavior is not orthogonal. Therefore it should be deprecated and finally removed.
To reproduce same behavior, explicit alias should work.
import std.ascii : isDigit; // Don't create alias silently
bool isDigit(char c) { return true; }
alias isDigit = std.ascii.isDigit; // explicitly merge overloads
void main() {
dchar d = 'a';
isDigit(d); // matches to std.ascii.isDiigt
}
Comment #2 by andrej.mitrovich — 2014-03-14T00:22:31Z
(In reply to comment #0)
> To reproduce same behavior, explicit alias should work.
>
> import std.ascii : isDigit; // Don't create alias silently
>
> bool isDigit(char c) { return true; }
> alias isDigit = std.ascii.isDigit; // explicitly merge overloads
I don't see how this can work, "std.ascii" should not be visible in the last statement.
Comment #3 by k.hara.pg — 2014-03-14T06:55:24Z
(In reply to comment #2)
> (In reply to comment #0)
> > To reproduce same behavior, explicit alias should work.
> >
> > import std.ascii : isDigit; // Don't create alias silently
> >
> > bool isDigit(char c) { return true; }
> > alias isDigit = std.ascii.isDigit; // explicitly merge overloads
>
> I don't see how this can work, "std.ascii" should not be visible in the last
> statement.
I think selective imports should make the fully qualified module name still visible. It's consistent behavior with basic imports.
====
For the future enhancement, I'm planning to add variations of import declarations. Just the ideas:
1. static selective import
static import std.stdio : writeln;
//writeln(); // NG
std.stdio.writeln(); // OK
//std.stdio.writefln(""); // NG
2. static renamed import
static import io = std.stdio;
//writeln(); // NG
//std.stdio.writeln(); // NG
io.writeln(); // OK
3. static selective renamed import
static import io = std.stdio : writeln;
io.writeln(); // OK
//io.writefln(""); // NG
4. unnamed import
import = std.stdio;
writeln(); // OK
writefln(""); // OK
//std.stdio.writeln(); // NG
5. unnamed selective import
import = std.stdio : writeln;
writeln(); // OK
//writefln(""); // NG
//std.stdio.writeln(); // NG
They are much flexible than the current behavior. Increasing orthogonality will give to programmers the ability to control imported names more precisely.
Comment #4 by andrej.mitrovich — 2014-03-14T07:14:39Z
(In reply to comment #3)
> I think selective imports should make the fully qualified module name still
> visible. It's consistent behavior with basic imports.
Ok.
> 4. unnamed import
>
> import = std.stdio;
> writeln(); // OK
> writefln(""); // OK
> //std.stdio.writeln(); // NG
My initial reaction is that I'm not a fan of this syntax. But anyway, we can discuss this later.
Comment #5 by bearophile_hugs — 2014-03-14T07:18:01Z
(In reply to comment #3)
> 4. unnamed import
>
> import = std.stdio;
> writeln(); // OK
> writefln(""); // OK
> //std.stdio.writeln(); // NG
>
> 5. unnamed selective import
>
> import = std.stdio : writeln;
> writeln(); // OK
> //writefln(""); // NG
> //std.stdio.writeln(); // NG
This will need to be discussed. I don't like this a lot. What's the purpose of this?
Comment #6 by k.hara.pg — 2014-03-14T07:36:42Z
(In reply to comment #5)
> (In reply to comment #3)
>
> > 4. unnamed import
> >
> > import = std.stdio;
> > writeln(); // OK
> > writefln(""); // OK
> > //std.stdio.writeln(); // NG
> >
> > 5. unnamed selective import
> >
> > import = std.stdio : writeln;
> > writeln(); // OK
> > //writefln(""); // NG
> > //std.stdio.writeln(); // NG
>
> This will need to be discussed. I don't like this a lot. What's the purpose of
> this?
They are just funny ideas to mask fully qualified names.
Comment #7 by github-bugzilla — 2014-03-15T16:49:21Z
Very sorry, I was misused the word "renamed imports" for the feature "selective imports with renaming selected symbols".
I tweak the issue summary.
Comment #10 by k.hara.pg — 2014-04-06T21:40:13Z
The negative effect of the implicit alias is that it makes selective imports and basic imports unexchangeable. For example:
module a;
auto foo(int[] ) { return 2; }
module b;
import a; // [A]
auto foo(long[] ) { return 1; }
void main()
{
assert(foo([1,2]) == 1);
}
At the line [A], the basic import makes b.foo visible, but it is hidden by b.foo. Therefore foo([1,2]) in main will call b.foo properly.
Next, what's will occur if you replace `import a;` to `import a : foo`?
import a : foo; // [A2]
auto foo(long[] ) { return 1; }
The selected a.foo is *implicitly* added in the overload set of b.foo, then foo([1,2]) will match a.foo(int[] ) more better than b.foo(long[] ), and the assertion in main will fail in runtime.
I think this is much non-intuitive behavior. No one would expect the silent code breaking in module b.
Comment #11 by code — 2014-04-06T22:28:26Z
(In reply to comment #10)
> I think this is much non-intuitive behavior. No one would expect the silent
> code breaking in module b.
I do agree with the diagnosis but we absolutely need a proper upgrade plan. I know it's tricky, but we need a warning for code that relies on the implicit alias without affecting code that just uses the imported symbol.
First of all you want to reach a consensus on the topic though. I haven't yet seen any good argument in favor of the current behavior, still it's used quite deliberately in many places.
> They are just funny ideas to mask fully qualified names.
Fully qualified names hardly do any harm, except for rare cases where a package name collides with a rename. So maybe a syntax isn't necessary.
import core = core.win;
Comment #12 by bugzilla — 2014-04-10T04:42:37Z
Also posted on https://github.com/D-Programming-Language/dmd/pull/3416 :
I just don't see much justification for such a wholesale reengineering of how imports work. The current rules have been in place for a very long time, and likely a whole lot of code relies on it. The new behavior doesn't seem particularly better - just different.
Comment #13 by bugzilla — 2014-04-10T04:43:19Z
Also posted on https://github.com/D-Programming-Language/dmd/pull/3416 :
I just don't see much justification for such a wholesale reengineering of how imports work. The current rules have been in place for a very long time, and likely a whole lot of code relies on it. The new behavior doesn't seem particularly better - just different.
Comment #14 by k.hara.pg — 2014-04-16T04:32:04Z
See also issue 8667.
Comment #15 by code — 2015-04-11T16:47:44Z
*** Issue 8667 has been marked as a duplicate of this issue. ***
Comment #16 by github-bugzilla — 2016-01-06T01:37:56Z
Comment #18 by razvan.nitu1305 — 2022-08-22T12:30:52Z
In the meantime doing selective imports doing overload sets merging has become the norm. Changing this will likely break a lot of code and surprise people that are already accustomed to this rule. I, personally, don't see anything wrong with doing the overload set merge automatically. If you don't want that just don't selective import the symbol.