Skip to content

Use statements don't check for duplicates #7663

Closed
@Blei

Description

@Blei

I stumbled upon this issue by accident, as I noticed that using a symbol that has been imported in multiple glob statements does not result in an error:

mod foo {
    pub fn p() { println("foo"); }
}
mod bar {
    pub fn p() { println("bar"); }
}

mod baz {
   use foo::*;
   use bar::*;
   #[main]
   fn my_main() {
       p();
   }
}

A simple oversight, I thought, and dove into resolve to fix the bug. Then I noticed that not even single imports are checked for conflicts:

mod foo {
    pub fn p() { println("foo"); }
}
mod bar {
    pub fn p() { println("bar"); }
}

mod baz {
   use foo::p;
   use bar::p;
   #[main]
   fn my_main() {
       p();
   }
}

I assumed that the reason for this was that currently both glob and single imports are flattened into a hashmap per module and tried to introduce a 2-level import scheme that allows duplicates only in glob imports but not in single imports.

And then I noticed that it's possible to export glob imports from a crate, making that scheme impossible. Even worse: this makes the global API of a crate dependent on the vigilance of the developer to not export multiple symbols with the same name, as a simple rearranging of use statements in the source code could break existing users of the crate.

So, there are four possible ways to go about this problem:

  1. Don't change anything. I.e. don't check for duplicate imported symbols, neither single imports nor glob imports. (Maybe add a lint, but that would probably have to be turned of in many cases, e.g. libstd is full of duplicate glob imports).

  2. Disallow duplicate imports, even when glob importing. This is IMO not workable.

  3. Disallow exporting glob imports from crates, making the aforementioned 2-level duplicate checking possible (i.e. disallow duplicate single imports, disallow used duplicate glob imports, allow unused duplicate glob imports).

  4. A variant on 3): allow exporting glob imports, implement the 2-level scheme for imports that are not visible from outside the crate, but disallow any duplicate imports otherwise.

  5. or 4) would be my preferred solution, but would incur a lot of work, both in implementing the scheme and restructuring existing code. 1) is the most realistic solution, but I personally don't really like it, as Rust is all about safety after all.

Fun example at the end:

mod foo {
    pub fn p() { println("foo"); }
}
mod bar {
    pub fn p() { println("bar"); }
}

mod baz {
   use bar::p;
   use foo::*;
   #[main]
   fn my_main() {
       p();
   }
}

Here, the use foo::*; is marked as being unused, even though when running the program, foo::foo() is actually the implementation of foo() that is used.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-resolveArea: Name/path resolution done by `rustc_resolve` specificallyC-bugCategory: This is a bug.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions