RFC 0116: no-module-shadowing

lang (resolve)

Summary

Remove or feature gate the shadowing of view items on the same scope level, in order to have less complicated semantic and be more future proof for module system changes or experiments.

This means the names brought in scope by extern crate and use may never collide with each other, nor with any other item (unless they live in different namespaces). Eg, this will no longer work:

extern crate foo;
use foo::bar::foo; // ERROR: There is already a module `foo` in scope

Shadowing would still be allowed in case of lexical scoping, so this continues to work:

extern crate foo;

fn bar() {
    use foo::bar::foo; // Shadows the outer foo

    foo::baz();
}

Definitions

Due to a certain lack of official, clearly defined semantics and terminology, a list of relevant definitions is included:

Motivation

As explained above, what is currently visible under which namespace in a given scope is determined by a somewhat complicated three step process:

  1. First, every extern crate item creates a name in the module namespace.
  2. Then, every use can create a name in any namespace, shadowing the extern crate ones.
  3. Lastly, any definition item can shadow any name brought in scope by both extern crate and use.

These rules have developed mostly in response to the older, more complicated import system, and the existence of wildcard imports (use foo::*). In the case of wildcard imports, this shadowing behavior prevents local code from breaking if the source module gets updated to include new names that happen to be defined locally.

However, wildcard imports are now feature gated, and name conflicts in general can be resolved by using the renaming feature of extern crate and use, so in the current non-gated state of the language there is no need for this shadowing behavior.

Gating it off opens the door to remove it altogether in a backwards compatible way, or to re-enable it in case wildcard imports are officially supported again.

It also makes the mental model around items simpler: Any shadowing of items happens through lexical scoping only, and every item can be considered unordered and mutually recursive.

If this RFC gets accepted, a possible next step would be a RFC to lift the ordering restriction between extern crate, use and definition items, which would make them truly behave the same in regard to shadowing and the ability to be reordered. It would also lift the weirdness of use foo::bar; mod foo;.

Implementing this RFC would also not change anything about how name resolution works, as its just a tightening of the existing rules.

Drawbacks

Detailed design

A new feature gate import_shadowing gets created.

During the name resolution phase of compilation, every time the compiler detects a shadowing between extern crate, use and definition items in the same scope level, it bails out unless the feature gate got enabled. This amounts to two rules:

Just like for the globs feature, the libstd prelude import would be preempt from this, and still be allowed to be shadowed.

Alternatives

The alternative is to do nothing, and risk running into a backwards compatibility hazard, or committing to make a final design decision around the whole module system before 1.0 gets released.

Unresolved questions

fn main() { use Bar = std::result::Result; use Bar = std::option::Option; let x: Bar = None; }

where the latter `use` shadows the former. This would have to be forbidden as well,
however the current semantic seems like a accident anyway.