Skip to content

Commit b72c2dd

Browse files
committed
Merge remote-tracking branch 'kimundi/no_module_shadowing'
2 parents 943e08a + e5f3fb1 commit b72c2dd

File tree

1 file changed

+191
-0
lines changed

1 file changed

+191
-0
lines changed

active/0000-no-module-shadowing.md

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
- Start Date: 2014-06-12
2+
- RFC PR #:
3+
- Rust Issue #:
4+
5+
# Summary
6+
7+
Remove or feature gate the shadowing of view items on the same scope level, in order to have less
8+
complicated semantic and be more future proof for module system changes or experiments.
9+
10+
This means the names brought in scope by `extern crate` and `use` may never collide with
11+
each other, nor with any other item (unless they live in different namespaces).
12+
Eg, this will no longer work:
13+
14+
```rust
15+
extern crate foo;
16+
use foo::bar::foo; // ERROR: There is already a module `foo` in scope
17+
```
18+
19+
Shadowing would still be allowed in case of lexical scoping, so this continues to work:
20+
21+
```rust
22+
extern crate foo;
23+
24+
fn bar() {
25+
use foo::bar::foo; // Shadows the outer foo
26+
27+
foo::baz();
28+
}
29+
30+
```
31+
# Definitions
32+
Due to a certain lack of official, clearly defined semantics and terminology, a list of relevant
33+
definitions is included:
34+
35+
- __Scope__
36+
A _scope_ in Rust is basically defined by a block, following the rules of lexical
37+
scoping:
38+
39+
```
40+
scope 1 (visible: scope 1)
41+
{
42+
scope 1-1 (visible: scope 1, scope 1-1)
43+
{
44+
scope 1-1-1 (visible: scope 1, scope 1-1, scope 1-1-1)
45+
}
46+
scope 1-1
47+
{
48+
scope 1-1-2
49+
}
50+
scope 1-1
51+
}
52+
scope 1
53+
```
54+
55+
Blocks include block expressions, `fn` items and `mod` items, but not things like
56+
`extern`, `enum` or `struct`. Additionally, `mod` is special in that it isolates itself from
57+
parent scopes.
58+
- __Scope Level__
59+
Anything with the same name in the example above is on the same scope level.
60+
In a scope level, all names defined in parent scopes are visible, but can be shadowed
61+
by a new definition with the same name, which will be in scope for that scope itself and all its
62+
child scopes.
63+
- __Namespace__
64+
Rust has different namespaces, and the scoping rules apply to each one separately.
65+
The exact number of different namespaces is not well defined, but they are roughly
66+
- types (`enum Foo {}`)
67+
- modules (`mod foo {}`)
68+
- item values (`static FOO: uint = 0;`)
69+
- local values (`let foo = 0;`)
70+
- lifetimes (`impl<'a> ...`)
71+
- macros (`macro_rules! foo {...}`)
72+
- __Definition Item__
73+
Declarations that create new entities in a crate are called (by the author)
74+
definition items. They include `struct`, `enum`, `mod`, `fn`, etc.
75+
Each of them creates a name in the type, module, item value or macro namespace in the same
76+
scope level they are written in.
77+
- __View Item__
78+
Declarations that just create aliases to existing declarations in a crate are called
79+
view items. They include `use` and `extern crate`, and also create a name in the type,
80+
module, item value or macro namespace in the same scope level they are written in.
81+
- __Item__
82+
Both definition items and view items together are collectively called items.
83+
- __Shadowing__
84+
While the principle of shadowing exists in all namespaces, there are different forms of it:
85+
- item-style: Declarations shadow names from outer scopes, and are visible everywhere in their
86+
own, including lexically before their own definition.
87+
This requires there to be only one definition with the same name and namespace per scope level.
88+
Types, modules, item values and lifetimes fall under these rules.
89+
- sequentially: Declarations shadow names that are lexically before them, both in parent scopes
90+
and their own. This means you can reuse the same name in the same scope, but a definition
91+
will not be visibly before itself. This is how local values and macros work.
92+
(Due to sequential code execution and parsing, respectively)
93+
- _view item_:
94+
A special case exists with view items; In the same scope level,
95+
`extern crate` creates entries in the module namespace, which are shadowable by names created
96+
with `use`, which are shadowable with any definition item.
97+
__The singular goal of this RFC is to remove this shadowing behavior of view items__
98+
99+
# Motivation
100+
101+
As explained above, what is currently visible under which namespace in a given scope is determined
102+
by a somewhat complicated three step process:
103+
104+
1. First, every `extern crate` item creates a name in the module namespace.
105+
2. Then, every `use` can create a name in any namespace,
106+
shadowing the `extern crate` ones.
107+
3. Lastly, any definition item can shadow any name brought in scope by both `extern crate` and `use`.
108+
109+
These rules have developed mostly in response to the older, more complicated import system, and
110+
the existence of wildcard imports (`use foo::*`).
111+
In the case of wildcard imports, this shadowing behavior prevents local code from breaking if the
112+
source module gets updated to include new names that happen to be defined locally.
113+
114+
However, wildcard imports are now feature gated, and name conflicts in general can be resolved by
115+
using the renaming feature of `extern crate` and `use`, so in the current non-gated state
116+
of the language there is no need for this shadowing behavior.
117+
118+
Gating it off opens the door to remove it altogether in a backwards compatible way, or to
119+
re-enable it in case wildcard imports are officially supported again.
120+
121+
It also makes the mental model around items simpler: Any shadowing of items happens through
122+
lexical scoping only, and every item can be considered unordered and mutually recursive.
123+
124+
If this RFC gets accepted, a possible next step would be a RFC to lift the ordering restriction
125+
between `extern crate`, `use` and definition items, which would make them truly behave the same in
126+
regard to shadowing and the ability to be reordered. It would also lift the weirdness of
127+
`use foo::bar; mod foo;`.
128+
129+
Implementing this RFC would also not change anything about how name resolution works, as its just
130+
a tightening of the existing rules.
131+
132+
# Drawbacks
133+
134+
- Feature gating import shadowing might break some code using `#[feature(globs)]`.
135+
- The behavior of `libstd`s prelude becomes more magical if it still allows shadowing,
136+
but this could be de-magified again by a new feature, see below in unresolved questions.
137+
- Or the utility of `libstd`s prelude becomes more restricted if it doesn't allow shadowing.
138+
139+
# Detailed design
140+
141+
A new feature gate `import_shadowing` gets created.
142+
143+
During the name resolution phase of compilation, every time the compiler detects a shadowing
144+
between `extern crate`, `use` and definition items in the same scope level,
145+
it bails out unless the feature gate got enabled. This amounts to two rules:
146+
147+
- Items in the same scope level and either the type, module, item value or lifetime namespace
148+
may not shadow each other in the respective namespace.
149+
- Items may shadow names from outer scopes in any namespace.
150+
151+
Just like for the `globs` feature, the `libstd` prelude import would be preempt from this,
152+
and still be allowed to be shadowed.
153+
154+
# Alternatives
155+
156+
The alternative is to do nothing, and risk running into a backwards compatibility hazard,
157+
or committing to make a final design decision around the whole module system before 1.0 gets
158+
released.
159+
160+
# Unresolved questions
161+
162+
- It is unclear how the `libstd` preludes fits into this.
163+
164+
On the one hand, it basically acts like a hidden `use std::prelude::*;` import
165+
which ignores the `globs` feature, so it could simply also ignore the
166+
`import_shadowing` feature as well, and the rule becomes that the prelude is a magic
167+
compiler feature that injects imports into every module but doesn't prevent the user
168+
from taking the same names.
169+
170+
On the other hand, it is also thinkable to simply forbid shadowing of prelude items as well,
171+
as defining things with the same name as std exports is not recommended anyway, and this would
172+
nicely enforce that. It would however mean that the prelude can not change without breaking
173+
backwards compatibility, which might be too restricting.
174+
175+
A compromise would be to specialize wildcard imports into a new `prelude use` feature, which
176+
has the explicit properties of being shadow-able and using a wildcard import. `libstd`s prelude
177+
could then simply use that, and users could define and use their own preludes as well.
178+
But that's a somewhat orthogonal feature, and should be discussed in its own RFC.
179+
180+
- Interaction with overlapping imports.
181+
182+
Right now its legal to write this:
183+
```rust
184+
fn main() {
185+
use Bar = std::result::Result;
186+
use Bar = std::option::Option;
187+
let x: Bar<uint> = None;
188+
}
189+
```
190+
where the latter `use` shadows the former. This would have to be forbidden as well,
191+
however the current semantic seems like a accident anyway.

0 commit comments

Comments
 (0)