Skip to content

Commit 4b71f8d

Browse files
committed
Auto merge of #32814 - jseyfried:improve_duplicate_glob_detection, r=nikomatsakis
resolve: Improve duplicate glob detection This fixes a bug introduced in #31726 in which we erroneously allow multiple imports of the same item under some circumstances. More specifically, we erroneously allow a module that is in a cycle of glob re-exports to have other re-exports (besides the glob from the cycle). For example, ```rust pub fn f() {} mod foo { pub use f; // (1) This defines `foo::f`. pub use bar::*; // (3) This also defines `foo::f`, which should be a duplicate error but is currently allowed. } mod bar { pub use foo::*; // (2) This defines `bar::f`. } ``` A module in a glob re-export cycle can still have `pub` items after this PR. For example, ```rust mod foo { pub fn f() {}; // (1) This defines `foo::f`. pub use bar::*; // (3) This is not a duplicate error since items shadow glob-imported re-exports (cf #31337). } mod bar { pub use foo::*; // (2) This defines `bar::f`. } ``` r? @nikomatsakis
2 parents 7c27cce + bc6daea commit 4b71f8d

File tree

3 files changed

+70
-14
lines changed

3 files changed

+70
-14
lines changed

src/librustc_resolve/resolve_imports.rs

+23-14
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,6 @@ impl<'a> ::ModuleS<'a> {
275275
// Define the name or return the existing binding if there is a collision.
276276
pub fn try_define_child(&self, name: Name, ns: Namespace, binding: NameBinding<'a>)
277277
-> Result<(), &'a NameBinding<'a>> {
278-
if self.resolutions.borrow_state() != ::std::cell::BorrowState::Unused { return Ok(()); }
279278
self.update_resolution(name, ns, |resolution| {
280279
resolution.try_define(self.arenas.alloc_name_binding(binding))
281280
})
@@ -318,15 +317,22 @@ impl<'a> ::ModuleS<'a> {
318317
fn update_resolution<T, F>(&self, name: Name, ns: Namespace, update: F) -> T
319318
where F: FnOnce(&mut NameResolution<'a>) -> T
320319
{
321-
let mut resolution = &mut *self.resolution(name, ns).borrow_mut();
322-
let was_known = resolution.binding().is_some();
323-
324-
let t = update(resolution);
325-
if !was_known {
326-
if let Some(binding) = resolution.binding() {
327-
self.define_in_glob_importers(name, ns, binding);
320+
// Ensure that `resolution` isn't borrowed during `define_in_glob_importers`,
321+
// where it might end up getting re-defined via a glob cycle.
322+
let (new_binding, t) = {
323+
let mut resolution = &mut *self.resolution(name, ns).borrow_mut();
324+
let was_known = resolution.binding().is_some();
325+
326+
let t = update(resolution);
327+
328+
if was_known { return t; }
329+
match resolution.binding() {
330+
Some(binding) => (binding, t),
331+
None => return t,
328332
}
329-
}
333+
};
334+
335+
self.define_in_glob_importers(name, ns, new_binding);
330336
t
331337
}
332338

@@ -650,11 +656,14 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> {
650656
// Add to target_module's glob_importers
651657
target_module.glob_importers.borrow_mut().push((module_, directive));
652658

653-
for (&(name, ns), resolution) in target_module.resolutions.borrow().iter() {
654-
if let Some(binding) = resolution.borrow().binding() {
655-
if binding.defined_with(DefModifiers::IMPORTABLE | DefModifiers::PUBLIC) {
656-
let _ = module_.try_define_child(name, ns, directive.import(binding, None));
657-
}
659+
// Ensure that `resolutions` isn't borrowed during `try_define_child`,
660+
// since it might get updated via a glob cycle.
661+
let bindings = target_module.resolutions.borrow().iter().filter_map(|(name, resolution)| {
662+
resolution.borrow().binding().map(|binding| (*name, binding))
663+
}).collect::<Vec<_>>();
664+
for ((name, ns), binding) in bindings {
665+
if binding.defined_with(DefModifiers::IMPORTABLE | DefModifiers::PUBLIC) {
666+
let _ = module_.try_define_child(name, ns, directive.import(binding, None));
658667
}
659668
}
660669

src/test/compile-fail/glob-cycles.rs

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
mod foo {
12+
pub use bar::*;
13+
pub use main as f; //~ ERROR has already been imported
14+
}
15+
16+
mod bar {
17+
pub use foo::*;
18+
}
19+
20+
pub use foo::*;
21+
pub use baz::*; //~ ERROR has already been imported
22+
mod baz {
23+
pub use super::*;
24+
}
25+
26+
pub fn main() {}

src/test/compile-fail/issue-32797.rs

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
pub use bar::*;
12+
mod bar {
13+
pub use super::*;
14+
}
15+
16+
pub use baz::*; //~ ERROR already been imported
17+
mod baz {
18+
pub use main as f;
19+
}
20+
21+
pub fn main() {}

0 commit comments

Comments
 (0)