Thread overview | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
|
February 10, 2006 module madness | ||||
---|---|---|---|---|
| ||||
I hope I'm not retreading old ground. I've been browsing the archives for a while on this issue and I haven't seen this particular problem addressed. If I've overlooked it, I apologize. ------begin stuff.d----------- module stuff; private import std.stdio; [...] -------end stuff.d----------- -------begin main.d---------- private import stuff.d void main() { std.stdio.writefln("Hello crazy world!"); } --------end main.d----------- Correct me if I'm wrong, but doesn't "private import std.stdio;" in stuff.d make the import of std.stdio private to that module? That *is* the way it's supposed to work, right? So how am I able to reference std.stdio in the above code without an error? This behavior led me to a somewhat frustrating experience. I'm working on a project that has about 10 modules. One of these modules contains global variables (not classes) that several other modules need (and also need to sometimes change). A couple of those modules also reference each other, and every import in every module is "private import". However, in the main program file I was qualifying the globals as globals.whatever and didn't realize that I hadn't "private import"ed the globals module there at all; but since another module had imported it and that module was imported in the main file, I could access it using qualified notation. And now for the really weird part: When the main file doesn't import the globals module, some of the other modules which "private import" it complain that it now conflicts with the "private import" of that globals module in another module. But it shouldn't conflict at all! All imports are "private import" and there are no name clashes in any of the modules in any case. And simply importing the globals module in the main file causes the other conflicts to be resolved somehow, although no other change was made to any module. I can give a small example using 3 very short modules if my explanation here wasn't clear. This behavior makes no sense to me! Can someone please explain why this is happening? I realize that the private/public module namespace issue has been done to death, but I thought I understood it until this problem. --John |
February 10, 2006 Re: module madness | ||||
---|---|---|---|---|
| ||||
Posted in reply to John Stoneham | John Stoneham wrote:
> I hope I'm not retreading old ground. I've been browsing the archives for a while on this issue and I haven't seen this particular problem addressed. If I've overlooked it, I apologize.
>
> ------begin stuff.d-----------
> module stuff;
>
> private import std.stdio;
>
> [...]
> -------end stuff.d-----------
>
> -------begin main.d----------
> private import stuff.d
>
> void main()
> {
> std.stdio.writefln("Hello crazy world!");
> }
> --------end main.d-----------
>
> Correct me if I'm wrong, but doesn't "private import std.stdio;" in stuff.d make the import of std.stdio private to that module? That *is* the way it's supposed to work, right? So how am I able to reference std.stdio in the above code without an error?
>
> This behavior led me to a somewhat frustrating experience. I'm working on a project that has about 10 modules. One of these modules contains global variables (not classes) that several other modules need (and also need to sometimes change). A couple of those modules also reference each other, and every import in every module is "private import".
>
> However, in the main program file I was qualifying the globals as globals.whatever and didn't realize that I hadn't "private import"ed the globals module there at all; but since another module had imported it and that module was imported in the main file, I could access it using qualified notation.
>
> And now for the really weird part: When the main file doesn't import the globals module, some of the other modules which "private import" it complain that it now conflicts with the "private import" of that globals module in another module. But it shouldn't conflict at all! All imports are "private import" and there are no name clashes in any of the modules in any case. And simply importing the globals module in the main file causes the other conflicts to be resolved somehow, although no other change was made to any module. I can give a small example using 3 very short modules if my explanation here wasn't clear.
>
> This behavior makes no sense to me! Can someone please explain why this is happening? I realize that the private/public module namespace issue has been done to death, but I thought I understood it until this problem.
>
> --John
Sounds like something I reported with testcases 2 years ago :P Don't remember the details anymore though.
|
February 12, 2006 Re: module madness & compiler BUG | ||||
---|---|---|---|---|
| ||||
Posted in reply to John Stoneham | There is definitely a bug in the module namespace parsing. The code below demonstrates the bug, which is first evident as being able to access a module's variables/functions even though it hasn't been imported. In the code below 3 modules are defined and used in the main program. The first module, notes, simply declares an enum which is used by the other modules. The second, printer, contains one function which returns a string representation of the enum. The third, compose, contains one function which combines 3 of the string representations of the enum into one longer string, using the function defined in printer to return a string. (Don't worry about whether this is "good code", I just threw it together as a quick example to demonstrate the bug.) In the main file, 3 lines are commented out and numbered with a comment at the end of the line. When the 3 lines are commented out, the program compiles fine, even though in main.d there is no import of notes and the enum defined there is accessed using qualified notation. THIS IS A BUG AND SHOULD BE GENERATE AN ERROR WHEN COMPILED. According the documentation: If the import is private, such as: module abc; private import def; then def is not searched when another module imports abc. So when main imports compose, notes should not be searched (according to the spec) and main should know NOTHING about the definitions in notes. Trying to access a name local to notes when notes is not imported should generate a compile-time error. The problem exposes itself further when the lines (1) and (3) are uncommented. When this is done, the compiler generates an error stating "compose.d(4): import compose.notes conflicts with printer.notes at printer.d(3)" THERE IS NO CONFLICT. Both modules use "private import" to import notes. When line 1 is uncommented and both compose and printer are imported into main, each one has "private import notes" and the compiler should not "search" notes twice. If it is not an error to allow qualified notation to access a sub-module privately-imported by an imported module (although it should be), then there is a bug in the way the compiler parses the module namespaces and claiming a conflict which does not exist. When line 2 is uncommented the program compiles fine. Notice that no change was made to any of the modules to fix the compile errors once they appeared. The only change was importing into main the module that the other modules also import. -----begin notes.d----- module notes; enum { Do, Re, Mi, Fa, So, La, Ti } ------end notes.d------ -----begin printer.d----- module printer; private import notes; char[] print_note(uint a) { static char[][7] notes_str = ["Do", "Re", "Mi", "Fa", "So", "La", "Ti"]; // range check omitted return notes_str[a]; } ------end printer.d------ -----begin compose.d----- module compose; private import printer; private import notes; char[] chord(uint a, uint b, uint c) { if (a <= Ti && b <= Ti && c <= Ti) return print_note(a) ~ "+" ~ print_note(b) ~ "+" ~ print_note(c); else return ""; } ------end compose.d------ -----begin main.d----- import std.stdio; private import compose; //private import printer; // (1) //private import notes; // (2) void main() { writefln("chord of Do+Mi+So = %s", chord(notes.Do, notes.Mi, notes.So)); //writefln("note %d = %s", notes.Mi, print_note(notes.Mi)); // (3) } ------end main.d------ |
February 15, 2006 Re: module madness & compiler BUG | ||||
---|---|---|---|---|
| ||||
Posted in reply to John Stoneham | Or, more succinctly: ---------- Foo.d ------------ module Foo; int foovar; ---------- Bar.d ------------ module Bar; private import Foo; ---------- test.d ------------ module test; int a1 = foovar; // identifier not defined, that is OK int a2 = Foo.foovar; // identifier accessible, that shouldn't work ------------------------------- The problem is, 'private import' only makes private the non-qualified names imported, they don't make private the fully-qualified names. The other problem, the name conflict, is likely a consequence of this. -- Bruno Medeiros - CS/E student "Certain aspects of D are a pathway to many abilities some consider to be... unnatural." |
February 15, 2006 Re: module madness & compiler BUG | ||||
---|---|---|---|---|
| ||||
Posted in reply to John Stoneham | John Stoneham wrote: > There is definitely a bug in the module namespace parsing. The code below demonstrates the bug, which is first evident as being able to access a module's variables/functions even though it hasn't been imported. In the code below 3 modules are defined and used in the main program. The first module, notes, simply declares an enum which is used by the other modules. The second, printer, contains one function which returns a string representation of the enum. The third, compose, contains one function which combines 3 of the string representations of the enum into one longer string, using the function defined in printer to return a string. (Don't worry about whether this is "good code", I just threw it together as a quick example to demonstrate the bug.) > <snip> Thank you John for the "practical" example. This is the biggest problem I've had with D since my very first "big" project in 2003. I like the way it is written in the docs, but this implementation is terribly broken. It's practically impossible to create a project with >= 3 modules without using some quirks with imports (to avoid namespace conflicts). I think it shouldn't be allowed to access privately imported module members at all. Of course it improves flexibility to have access to every possible symbol everywhere in the program, but what's the point with visibility keywords then? Walter, can you please finally fix this? -- Jari-Matti |
February 16, 2006 Re: module madness & compiler BUG | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jari-Matti Mäkelä | Jari-Matti Mäkelä wrote: > John Stoneham wrote: >> There is definitely a bug in the module namespace parsing. The code below demonstrates the bug, which is first evident as being able to access a module's variables/functions even though it hasn't been imported. In the code below 3 modules are defined and used in the main program. The first module, notes, simply declares an enum which is used by the other modules. The second, printer, contains one function which returns a string representation of the enum. The third, compose, contains one function which combines 3 of the string representations of the enum into one longer string, using the function defined in printer to return a string. (Don't worry about whether this is "good code", I just threw it together as a quick example to demonstrate the bug.) >> > <snip> > > Thank you John for the "practical" example. This is the biggest problem I've had with D since my very first "big" project in 2003. I like the way it is written in the docs, but this implementation is terribly broken. It's practically impossible to create a project with >= 3 modules without using some quirks with imports (to avoid namespace conflicts). I think it shouldn't be allowed to access privately imported module members at all. > > Of course it improves flexibility to have access to every possible symbol everywhere in the program, but what's the point with visibility keywords then? Walter, can you please finally fix this? > Here's a solution proposal: I think the problem is that the compiler only keeps track of the "latest" module in the import chain. If the same symbol is imported using >= 2 different paths, the compiler regards these symbols as completely separate. Is there any possibility to include some sort of additional information that tells the "origin" of the symbol so that the compiler would be able to merge these identical symbols as one. "origin" = where the symbol was last declared/(re)implemented in one import chain The only chance of conflict I can think of is when the symbol contents are "tampered" along the way and two symbols with different implementations are introduced in the same module. Should we use some kind of simplified c++ multiple inheritance rules here then? Simplified, because D modules cannot inherit. I think this would be an elegant solution since it would not only allow the compiler to "pollute" the symbol namespace with private symbols and thus giving more precise error messages than C/C++ with the help of the symbol table, but also prevent conflicts with these "intelligent" merging rules. -- Jari-Matti |
February 17, 2006 Re: module madness & compiler BUG | ||||
---|---|---|---|---|
| ||||
Posted in reply to Bruno Medeiros Attachments: | Bruno Medeiros schrieb am 2006-02-15: > Or, more succinctly: > > ---------- Foo.d ------------ > module Foo; > > int foovar; > ---------- Bar.d ------------ > module Bar; > > private import Foo; > ---------- test.d ------------ > module test; > > int a1 = foovar; // identifier not defined, that is OK > int a2 = Foo.foovar; // identifier accessible, that shouldn't work > ------------------------------- > > The problem is, 'private import' only makes private the non-qualified names imported, they don't make private the fully-qualified names. The other problem, the name conflict, is likely a consequence of this. Added to DStress as http://dstress.kuehne.cn/nocompile/p/private_12_A.d http://dstress.kuehne.cn/nocompile/p/private_12_B.d http://dstress.kuehne.cn/nocompile/p/private_12_C.d http://dstress.kuehne.cn/nocompile/p/private_12_D.d http://dstress.kuehne.cn/nocompile/p/private_12_E.d http://dstress.kuehne.cn/nocompile/p/private_12_F.d Thomas |
February 17, 2006 Re: module madness & compiler BUG | ||||
---|---|---|---|---|
| ||||
Posted in reply to Bruno Medeiros | Bruno Medeiros wrote:
> Or, more succinctly:
>
> ---------- Foo.d ------------
> module Foo;
>
> int foovar;
> ---------- Bar.d ------------
> module Bar;
>
> private import Foo;
> ---------- test.d ------------
> module test;
>
> int a1 = foovar; // identifier not defined, that is OK
> int a2 = Foo.foovar; // identifier accessible, that shouldn't work
> -------------------------------
>
> The problem is, 'private import' only makes private the non-qualified names imported, they don't make private the fully-qualified names. The other problem, the name conflict, is likely a consequence of this.
>
>
>
Actually, the "fully-qualified" name in test.d would be Bar.Foo.foovar (also, you forgot to "import Bar" in test.d).
Here's the problem with the way the compiler is handling it now (i.e., allowing qualified names from a package which has been privately-imported by another package). In my example, when main.d tries to access a name in the notes module without importing notes directly but instead importing a module which itself privately-imports notes (say, the compose module in my example), what it is really resolving when it sees notes.Do is compose.notes.Do, since notes was imported in compose (and this should be a compile-time error since it was privately-imported!). Then, when main.d imports printer in addition to compose, which also privately-imports notes, there is a conflict because when you try to access notes.Do the compiler doesn't know if you're accessing compose.notes.Do or printer.notes.Do.
AND THEN THE ERROR MESSAGE SUCKS!! It doesn't tell you that the conflict arises in main.d where you've tried to access a name in notes. It tells you that the conflict is in both compose and printer which have imported notes! Huh??? This makes no sense to me. And it makes it almost impossible to actually find what's causing the problem in your code.
|
Copyright © 1999-2021 by the D Language Foundation