-
Notifications
You must be signed in to change notification settings - Fork 421
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Unclear behavior when use statement brings in a module with the same name #11262
Comments
I don't think we should take approach 3 of having If we did stick with resolving to the top-level module (as we do today), I could imagine adding module line numbers to the error message might help, but am not sure this is likely to come up often enough to be worth the effort. But I think it might be more consistent in your example if we were to issue an error like the ones we do for the following programs: Program 1: Two uses result in the same symbol being available twice: module M {
var a: int = 42;
module M {
var a: int = 23;
}
}
use M;
use M.M;
writeln(a);
Program two: Two modules at the same scope have the same name Or this one: module M {
}
module M {
}
I'm not sure whether these errors would come about as a result of the |
Yeah, I would guess the latter as well. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Would it be crazy to change the behavior of |
I wouldn't call it crazy, but it might be annoying to have to type both an import and a use of a module if you want both behaviors. Note that Haskell will always allow both qualified and unqualified access when unqualified access is allowed (to rephrase, its import statement always enables at least qualified access) |
Certainly, though I would say that the intent becomes much more clear because you want two behaviors, you need to type two statements. I don't think it'd be common that users would want both behaviors. Anyway, that suggestion is for another issue but it would cleanly disambiguate this issue and #13925. |
Does it work with module M {
module M {
proc whatev() {
writeln("whee");
}
}
}
use M only;
M.M.whatev(); ? |
My (to be another issue) use M only; // This would be a no-op; Make it an error.
// Or treat as special case (but why not just `import` then).
use M;
M.whatev();
M.M.whatev(); // Illegal. outer `M` not available via `use`. Use `import`. which is why this behavior wouldn't be useful unless |
@BryantLam - I think you are saying the same as what I proposed here: #13119 (comment) - is that right? AFAIK this doesn't have its own issue yet, but comes up in #13831. |
|
After some more thinking and talking with Lydia, I think the current behavior of the compiler is correct for this code. Showing the code again: module M {
module M {
proc whatev() {
writeln("whee");
}
}
}
use M only M;
M.whatev(); What threw me is that the
The reason I was expecting a different behavior is that I was mentally thinking of the program more like this: module M {
module M {
proc whatev() {
writeln("whee");
}
}
}
{
use M only M;
M.whatev();
} (that is, the So I think that this future could be retired given the current definition and implementation of the language. That said, the issue itself could remain open where perhaps it should be requesting an error message that refers not just to module
But, as mentioned there, I'm not sure the effort is worth it for this specific case, and that when we encounter users who write code like this, we should have their friends and mentors slap them rather than complaining to us. (So I'd probably just close this issue as well). |
Michael asked about this case: module M {
module M {
proc whatev() {
writeln("whee");
}
}
}
use M only;
M.M.whatev(); which works, but for similar reasons: the |
I believe innerModuleSharesName-topLevel.chpl is working as expected so should be retired, which I do here. This PR also adds a number of variations on it that came up in discussions around issue chapel-lang#11262.
PR #13997 adds the aforementioned variants (and retires the future if nobody disagrees). |
Capture module visibility use case puzzle codes and retire the original [reviewed by @lydia-duncan] I believe innerModuleSharesName-topLevel.chpl is working as expected so should be retired, which I do here. This PR also adds a number of variations on it that came up in discussions around issue #11262. Resolves #11262.
I understand your explanation. What are the implications for having the compiler treat the original code as a multiple symbol error on |
Are you saying that the conflicting multiple symbols would be due to the first I think part of the reason I have trouble worrying too much about this issue's specific case is that it seems pretty heavily related to the fact that the author of module M {
module N {
proc whatev() {
writeln("whee");
}
}
}
use M only N;
M.whatev(); // obviously an error; M doesn't define `whatev()`
N.whatev(); // obviously OK; N is visible and does
whatev(); // obviously an error; N hasn't been `use`d As a result, it seems like we've spent a lot of effort discussing how to make poorly written code less confusing rather than focusing on problems that might come up in more realistic situations. Or do you think that there are realistic patterns that are similar to the original example which suggest the current behavior is wrong? (for |
This comment has been minimized.
This comment has been minimized.
I think the practical choice of using shadowing for symbol conflicts introduced via
I'd like my mental model to think of For example, here are some current behaviors: // 1.chpl
module M {
proc fn() { writeln("M"); }
}
proc fn() { writeln("g"); }
{
use M;
fn();
}
Demonstrates (4). Strictly to your interpretation, why is this ambiguous? Shouldn't it only see // 2.chpl
module M {
module N {
proc fn() { writeln("M"); }
}
}
module N {
proc fn() { writeln("g"); }
}
{
use N only;
use M;
N.fn();
}
This error makes sense because different // 3.chpl
module M {
var x = 100;
}
var x = 42;
{
use M;
writeln(x);
} This prints "100" and is consistent with the shadowing rules. // 4.chpl
module M {
module N {
var x = 100;
}
}
module N {
var x = 42;
}
{
use N only; // It doesn't matter if first or second.
use M;
writeln(N.x); // Always prints 42.
} This prints "42". Why does it not error as I would expect on the multiple-defined (chapel/master Sep 2, 2019) While some changes have hit master, if you run these examples on 1.19.0 in TIO, you get different answers for two of them. Though I recognize without It's also reasonable to say that the two cases above that don't behave right are just bugs, but I think it points to the fact that shadowing is really hard to debug. Edit: To clarify, codes 1 and 3 should shadow whereas codes 2 and 4 should not. The difference is the |
When first reading this, I was going to say that I thought Chapel had traditionally treated symbols very similarly to one another, including module symbols—and that #13930 represented one of the recent major ways in which we started treating module symbols more differently. Your example points out a case I was brushing past in that thought, though, which is that functions are treated differently in order to support overloading (i.e., it's legal to have multiple functions with the same name and to select between them based on the arguments provided; but it's not legal to have multiple variables, modules, etc. of the same name because those symbol kinds don't support overloading). Looking at the specific example you provided: // 1.chpl
module M {
proc fn() { writeln("M"); }
}
proc fn() { writeln("g"); }
{
use M;
fn();
} I don't feel confident that the current behavior is best / ideal if users wanted to push back against them. For example, we could potentially amend the current rules to permit functions to shadow one another if their signatures match (by some definition of match... these ones obviously do, but decisions would need to be made about argument names, default arguments, intents and the like. Ideally these would be similar to the rules used for
The traditional definition has been more like: "use statements make symbols available as though they were in a scope just outside of the current lexical scope but closer in that its parent scope (and the module name that was used is in a scope just above that)." To illustrate that, this code: module M {
var x = ...;
}
module N {
var y = ...;
{
use M;
var z = ...;
}
} would cause N to act something like this: module N {
var y = ...;
{
...module M... is available from here
{
...var [M.]x... is available from here
{
var z = ...;
}
}
}
} I think the historical rationale for this interpretation has been due to Cases 2 and 4 behave the same on master now (e.g., use M;
use N only;
...N.x...
...N.fn()... results in: { // scope making module symbols themselves available
...module M...
...module N...
{ // scope making module contents available
...module [M.]N...
...<nothing from N>...
{ // scope in which the `use` statements appeared
...N.x...
...N.fn()...
}
}
} So then when the compiler resolves Interestingly, I tried dumping both sets of symbols into the same scope as part of #13930, but this resulted in problems when a module and its symbols shared the same name, which is what made me ask how important it was in #13925. It seemed that this was pretty important/standard practice in other languages which is why I reverted to the traditional interpretation above. With respect to this point:
Chapel does have symbol renaming now for everything other than the module whose contents the Specifically, note that if a case like 2 and 4 came up in real life, symbol renaming could be used to help avoid the confusion: module M {
module N {
var x = 100;
}
}
module N {
var x = 42;
}
{
use N only;
use M only N as N2;
writeln(N.x); // Always prints 42.
writeln(N2.x);
} Of course, it would be better if And of course, for programming in the large cases for which |
Bryant pointed out that the behavior of these tests has varied over the past few releases/months which motivated me to capture them to make sure further variation doesn't happen unexpectedly (I believe the current behavior is correct, or at least, as currently intended).
(Since these patterns are interesting, have recently varied in behavior as you've noted, and are behaving as I believe we intend them to according to present rules, I captured them in PR #14018 to prevent further changes from occurring unintentionally). |
Capture some more sample programs from issue #11262 [trivial, not reviewed] Bryant pointed out that the behavior of these tests has varied over the past few releases/months which motivated me to capture them to make sure further variation doesn't happen unexpectedly (I believe the current behavior is correct, or at least, as currently intended).
Summary of Problem
When a nested module shared a name with a top-level module (or a module that is already in scope) and the nested module is brought into scope via a use statement, the contents of the nested module cannot be accessed via the module prefix, because the top-level module is searched instead. This seems confusing, so I think one of three things should happen here:
I've included a test where the module is defined within a module sharing the name at the top level and the use statement explicitly mentioned the module with the duplicate name. This situation will likely also occur when
except
oronly
list)Steps to Reproduce
Source Code:
Compile command:
chpl innerModuleSharesName-topLevel.chpl
Associated Future Test(s):
test/visibility/only/innerModuleSharesName-topLevel.chpl
#11226Configuration Information
chpl --version
: chapel 1.19.0 (pre-release)The text was updated successfully, but these errors were encountered: