-
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
Rename a module on use statement #10799
Comments
Generally sounds like a good idea to me. I like 1 for submodules and 2 for top-level modules. Having 1 work also for top-level modules would seem to break some assumed nesting to me, so I'd prefer not. |
#3900 proposed the syntax in choice 2, and added a future for it. edit to note: there was a lot of discussion in the PR comments there. |
That would be why I didn't find it, it was a PR! |
See also #13831 which includes some discussion of renaming a module on an |
I agree that approach 2 is very attractive. 1a doesn't seem like it ought to work for me (I'd expect only / except clauses to only apply to module-level symbols within the module being 1b seems attractive and seems to work today (this was my hope in reading the issue, but I couldn't tell from the text whether that was the case at the time or not). I'm not as fond of approach 3. Somewhat related, in issue #13978, I propose a variation on approach 2 that would prevent the module name itself from being referenced: |
I've started implementing approach 2 and in writing tests, I thought of a couple of cases where there might be disagreement on the intended behavior. What do folks think should be the intended behavior in these cases?
module LongName {
var x: int;
}
module L {
var y: bool;
}
module User {
use L;
use LongName as L;
proc main() {
writeln(L.x); // This seems like it should fail because L is now ambiguous
// I could also see the argument for not allowing the second use to be written in the first place
// though we otherwise allow multiple uses of the same module (due to except/only list
// overlaps)
writeln(L.y);
}
}
module LongName {
var x: int;
}
module User {
use LongName as L;
enum L { red, blue, yellow };
proc main() {
writeln(L.x); // Uses are at a further scope than symbols in the scope, does that
// mean this should error due to not finding 'x' in the enum? What if the enum defined
// an 'x'?
}
}
module Foo {
var Bar: string;
}
module User {
use Foo as Bar;
proc main() {
writeln(Bar.Bar); // Should this work or fail?
}
} Note: if the module name already matches the name of a symbol in the module, module Foo {
var Foo: string;
}
module User {
use Foo;
proc main() {
writeln(Foo.Foo); // This line will fail
}
}
module Foo {
var x: int;
}
module User {
use Foo as Bar only x as y;
proc main() {
writeln(y); // Foo.x
writeln(Bar.x); // Should Foo.x be accessed like this?
writeln(Bar.y); // Or like this? Or should both be valid?
// Note that today if you *just* rename the symbol, it needs to use the old name for
// qualified naming, e.g. Foo.x, so I lean towards Bar.x
}
} |
I think this should behave similarly to defining multiple variables of the same name at the same scope.
Ditto
My intuition is that this should behave the same as it would if
My intuition is that At first, I didn't balk at the naked reference to |
Does that imply that you think unqualified access of the symbols in the module being used and renamed should not be allowed? E.g. use Mod as M; // would be redundant with:
use Mod as M only; // and thus this form potentially shouldn't be even provided? That seems confusing to me - I would rather write: use A only;
use B as C only; and have those mean the same thing than have use A only;
use B as C; mean the same thing. I think we would be sacrificing clarity for brevity. |
Hmm, that's a good point. Let's ignore the last paragraph of my previous comment for now and I'll stew a bit more on what I'm feeling hung up on. |
I would prefer the second I think the difference with Similarly: use LongName as L;
use LongName as L; Is this fine? I would think so because both (I don't actually know how the compiler does this resolution, so I could be way off base to how it actually works. This is simply how I think it should work.)
Can you remind me the reason why the behavior is like this? I remember it being explained before, but I don't remember why it behaves like this because it's not intuitive to me., especially if the expectation of I would prefer if the behavior was that both the
This is a consequence of use-statements allowing both qualified and unqualified access #13978. My opinion is that qualified access should always be preferred over unqualified access to get to a symbol because then this problem doesn't happen. (There might be some other problem I can't think of.) I also wonder why this behavior works this way instead of first resolving qualified access.
writeln(y); // OK: Foo.x
writeln(Bar.x); // Illegal: x was renamed to y.
writeln(Bar.y); // OK: This is the only way to get to Foo.x.
module L {
var x: int = 42;
}
use L only x as y;
writeln(y);
writeln(L.y); // <-- Error today. That seems confusing. The user asked to rename import L only x as y;
writeln(y); // Illegal. Qualified access is required.
writeln(L.y); // Also illegal! L.x, not L.y! That's confusing as heck. Edit (January 27, 2020): I no longer believe the behavior I outlined in this post to be the correct path. Please ignore most of it and see #14738 (comment) for followup. |
I think that's reasonable. The compiler is not set up for it today (because it didn't need to, any naming conflict would be a result of modules found when trying to resolve the use statement, not a name the use statement defines), but I think with the advent of "renaming while using" it probably should be set up to validate that.
The intention is to prevent symbols in a used module from conflicting with or shadowing symbols in the current scope that share a name. So in the following code: module A {
var x: int;
}
module B {
use A;
var x: bool;
proc main() {
writeln(x);
}
}
module A { // Option 1
var x: int; // Option 2
}
module B {
use A as C; // Option 3
...
} Note: I think we've been thinking about uses from the perspective of option 3, but that option 1 is what we actually have implemented for normal module uses. Option 1 treats the rename as though it was the original name of the module. From B's perspective, A was always C (though A will still know it is A within its own scope and for other uses of A). This means that the answers to the cases in my previous post are:
Option 2 is probably the strategy I least favor. I included it for completeness, but I'm not sure there's much justification for it.
Option 3 is I think what Brad is arguing for, and also matches what we've said in offline meetings. A
Hopefully my answer to the last section addressed this, but to draw connections together:
I think you have this backwards - SummaryI think we've been thinking about uses in a different way than the compiler actually implements them. If I'm remembering right, Option 3 is more in keeping with the way other languages have defined their use/import statements. However, if we intend to change and follow Option 3, Case 3 from my previous post seems to be inconsistent with that definition. I personally feel more inclined towards Option 1 as it seems more true to what we have said about the use statements, since the focus before now has really been on the symbols they bring in. However, I am comfortable with switching to Option 3 - adding the capacity for the user to have control over the qualified name seems like an appropriate point to rethink this behavior to me, since the new name needs to be defined somewhere. (I also started this comment leaning more towards option 3 but ended up shifting part way through.) |
Whoops! You're right! My point still stands and I amended my previous post to make it more clear I was proxying --
I think this behavior is exactly how I see modules being brought in. The So, I agree with Option 3 the most. For case 3: conservatively, I would force the user to fully qualify the symbol if it's named the same as the module symbol ( |
One thing that occurs to me as I implement this guidance is that changing the precedent for "module name" versus "symbol in the module" will especially impact types that share the same name as their module. The change itself is relatively simple to make, but it will mean that things like our usage of LocaleModels will need to adjust, because the LocaleModel subclass is defined within a module with the same name. There may be other cases remaining like this, as well. Is this acceptable fallout? A sign that we should maintain current behavior instead? Or a sign that maybe uses that rename symbols should be treated differently than uses that don't, in this regard? |
[I'm posting this with the admission that I'm behind on this thread since my last comment, but caught wind of this specific question today] I feel as though having a type named M within a module named M be usable in an easy way is a frequently requested feature, and a big part of the reason that we treat the scoping of module symbols in a slightly specialized way today (e.g., symbols from a use are just outside of the current scope, and their modules' symbols are just outside of that). It makes me nervous to change the language in that regard without great need (which may exist and I just don't understand it yet), both because I think it's worked well for us and because it's seemed to support the M-within-M pattern well and naturally. |
I think the main issue is that it's inconsistent. "Symbols from a use are just outside of the current scope, and their modules' symbols are just outside of that", but we still want to the renamed module symbols to conflict with both symbols defined at the scope with the use (meaning that the rename is at a closer scope than the symbols within the used module) and with other used modules (but those are supposed to be beyond symbols within the used module). |
I see. Reconsidering my previous answers in this light:
Does that help address the inconsistency? |
Yes, that helps a ton :) |
Allow renaming a module on its use statement [reviewed by @mppf] This enables support for renaming a module when using it. With this change, the following code will work, so long as Foo has a public symbol named `x` in it: ```chapel use Foo as Bar; writeln(Bar.x); // Refers to Foo.x ``` Prior to this change, we could only rename a module if it was a submodule of a module we were using, e.g. ```chapel use FooParent only Foo as Bar; use Bar; writeln(Bar.x); // Refers to Foo.x ``` Otherwise, we were unable to rename the module. Note that this does not change unqualified access of symbols within the module (nor was it expected to) Resolves #10799, Cray/chapel-private#529 Details: ------- Added a new field to UseStmts which is set by a new argument to the constructor, indicating the new name for the module being used. Adds two new methods to UseStmts as well, to determine if the module is renamed in this use, and to obtain that rename. Updated the OnlyRename parser structure to PotentialRename, to reflect that it is not just used for only lists. Uses this structure at parse time to pass the new name/old name pair for the used module to buildUseStmt. Allows both a use of multiple modules and a use of modules with `except` or `only`. Extends checkIfModuleNameMatches to account for renaming, and extends it to allow for renaming an enum when using it. Requires a future change to improve error messages in the case where a module has been renamed and we failed to resolve a symbol in it for qualified naming Updates the AST loggers for this new syntax. Covered test cases: -------------------- - Renaming to a shorter name - Renaming to a longer name - Trying to use the old name for qualified access - Renaming to the same name as another module that wasn't used - Renaming to the same name as another module that was used: - with qualified access - with unqualified access only - Renaming to the same name as an enum in scope - Renaming an enum when using it - when a symbol is excluded in a use that renames the module, we should not be able to use the symbol in an unqualified manner - we should be able to rename a module to the same original name. - renaming a module that was renamed in another module you used - Note: not currently implemented, see #14711 - renaming with an `except *;` clause, too - for qualified access - for unqualified access - renaming with an `only;` clause, too - for qualified access - for unqualified access - renaming both a module and a symbol inside it in the same use - with proper qualified access - with proper unqualified access - with improper qualified access (a few variations) - with improper unqualified access - renaming a module with an only list in the same use - with qualified and unqualified access - a client tries to use an unqualified symbol from a privately used module - a client tries to use a qualified symbol from a privately used module with the original module name - a client tries to use a qualified symbol from a privately used module with the new module name - Ensures that publicly using a module while renaming it impacts clients of the module with the use the same as the original module: - for unqualified access - for qualified access (both proper and improper) - publicly using and renaming a private module - when using the renamed module as a prefix to access its symbols - when using the old module name as a prefix to access its symbols - Ensures that you can rename multiple modules in a use, and that all modules will: - have their new name respected - have their symbols available unqualified - and have their old name not usable. - using functions from renamed modules - renaming when new name is also the name of a symbol in the module - and using just the new name - and using qualified access of the symbol - renaming to a symbol defined in another module - and using just the new name - and using qualified access to try and get a symbol in the first module - when a module is used twice in different ways - and is renamed once - and is renamed in both uses Testing: -------- - [x] full paratest with futures
Related #10798
Feature request. I want the ability to rename a module, especially if my module is CrazyLongName.
Some syntax choices:
1.
Works with today's parser, but I don't like the verbosity. If there's no reason to make this illegal, I'm not opposed to supporting it simply for symmetry when using submodules:
2.
3.
Is
only
redundant? If use-and-rename, does the user intend to still pollute the global namespace?The text was updated successfully, but these errors were encountered: