Bug 21500 – public import in mixin template in module a fails when module b imports a.

Status
RESOLVED
Resolution
INVALID
Severity
regression
Priority
P1
Component
dmd
Product
D
Version
D2
Platform
x86
OS
Windows
Creation time
2020-12-23T05:42:39Z
Last change time
2021-02-08T11:28:08Z
Assigned to
No Owner
Creator
noobnoobsai

Comments

Comment #0 by noobnoobsai — 2020-12-23T05:42:39Z
Is this supposed to work? ```bar.d mixin template baz() { public import std.stdio; // public import std.stdio : writeln; } } ``` ```foo.d import bar; mixin baz; // void main(){ // foo.d(8): Error: writeln is not defined, perhaps import std.stdio; is needed? writeln("foo"); // public import statement in mixin fails } ```
Comment #1 by noobnoobsai — 2020-12-23T05:46:03Z
Correction: ```bar.d mixin template baz() { public import std.stdio; // public import std.stdio : writeln; } ```
Comment #2 by razvan.nitu1305 — 2021-02-01T16:42:32Z
Yes, it is supposed to fail. Is is by design that imports inside mixins are visible only in the scope of the mixin. The explanation for that is that you do not want to risk hijacking symbols. Take for example: mixin template baz() { import std.stdio : write; void func() { /* use write */} } void write { /* log something */ } struct B { mixin baz; void foo() { write(); // oops, calls std.stdio.write } } Imagine that baz is written in a library and you don't know exactly what imports it is using, so you expect that your user-defined function is called, however, the symbol is hijacked by the import inside the mixin. This is prevented by the current behavior where imports inside mixins are only visibile in the scope of the mixin. The current behavior is the correct one, so closing this as invalid.
Comment #3 by maxsamukha — 2021-02-01T20:50:08Z
How are imported symbols different than other symbols introduced by a mixin? mixin template baz() { void func() {} } void func() { } struct B { mixin baz; void foo() { func(); // oops, baz.func hijacked .func } }
Comment #4 by razvan.nitu1305 — 2021-02-02T08:22:24Z
(In reply to Max Samukha from comment #3) > How are imported symbols different than other symbols introduced by a mixin? In the case of imported symbols, you are right, there isn't any difference, however, the problematic case is when you have module imports (`import std.stdio`) inside a mixin. In that case, you end up importing a lot of symbols that you have no idea about. The user can look through the code (or the documentation) of a mixin template and at least have a general understanding of the symbols that he is mixing in, whereas, in the case of imports there is no way you can know what symbols end up hijacking your other symbols (except if you audit all the imported modules).
Comment #5 by maxsamukha — 2021-02-04T14:45:00Z
(In reply to RazvanN from comment #4) > (In reply to Max Samukha from comment #3) > > How are imported symbols different than other symbols introduced by a mixin? > > In the case of imported symbols, you are right, there isn't any difference, > however, the problematic case is when you have module imports (`import > std.stdio`) inside a mixin. In that case, you end up importing a lot of > symbols that you have no idea about. The user can look through the code (or > the documentation) of a mixin template and at least have a general > understanding of the symbols that he is mixing in, whereas, in the case of > imports there is no way you can know what symbols end up hijacking your > other symbols (except if you audit all the imported modules). Sorry for the slow reply. Yes, my question was not so much about this particular case as about why directly imported symbols are considered eligible for anti-hijacking measures, while symbols introduced indirectly (via an imported base class/interface, mixin, or "alias this") are not.
Comment #6 by razvan.nitu1305 — 2021-02-05T08:58:30Z
(In reply to Max Samukha from comment #5) > (In reply to RazvanN from comment #4) > > (In reply to Max Samukha from comment #3) > > > How are imported symbols different than other symbols introduced by a mixin? > > > > In the case of imported symbols, you are right, there isn't any difference, > > however, the problematic case is when you have module imports (`import > > std.stdio`) inside a mixin. In that case, you end up importing a lot of > > symbols that you have no idea about. The user can look through the code (or > > the documentation) of a mixin template and at least have a general > > understanding of the symbols that he is mixing in, whereas, in the case of > > imports there is no way you can know what symbols end up hijacking your > > other symbols (except if you audit all the imported modules). > > Sorry for the slow reply. No worries. > > Yes, my question was not so much about this particular case as about why > directly imported symbols are considered eligible for anti-hijacking > measures, while symbols introduced indirectly (via an imported base > class/interface, mixin, or "alias this") are not. I think it would be much easier if you provided an example. Currently, I cannot find any scenarios regarding base classes, mixins or alias this that do not abide by the present rule.
Comment #7 by maxsamukha — 2021-02-08T11:28:08Z
(In reply to RazvanN from comment #6) > > I think it would be much easier if you provided an example. Currently, I > cannot find any scenarios regarding base classes, mixins or alias this that > do not abide by the present rule. To my understanding, D's concept of hijacking is about preventing silent breakage of the client code after a change in the API of a third-party module (according to https://dlang.org/articles/hijack.html). Consider these two cases: 1) module a; // third-party // void foo() {} // uncommenting doesn't affect module b module b; import a; void foo() {} void main() { foo(); } 2) module a; // third-party class Base { // void foo() {} // uncommenting silently breaks module b; } module b; void foo() {} class Subclass: Base { void bar() { foo(); } } void main() { (new Subclass).bar(); } I wonder why case 1 is taken seriously, while case 2 is neglected. I suspect there is some important subtlety here (such as Base's scope being subjectively more "local" to Subclass than b's scope).