From e454835eef0df5c920377bba45b115e896f3bff9 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 8 Jan 2026 10:25:58 +0300 Subject: [PATCH 1/5] resolve: Factor out and document the glob binding overwriting logic --- compiler/rustc_resolve/src/imports.rs | 116 ++++++++++++++++++-------- 1 file changed, 81 insertions(+), 35 deletions(-) diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index a8bb53cc7f276..3f998b8c8aa03 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -295,6 +295,24 @@ fn pub_use_of_private_extern_crate_hack(import: Import<'_>, decl: Decl<'_>) -> O } } +/// Removes identical import layers from two declarations. +fn remove_same_import<'ra>(d1: Decl<'ra>, d2: Decl<'ra>) -> (Decl<'ra>, Decl<'ra>) { + if let DeclKind::Import { import: import1, source_decl: d1_next } = d1.kind + && let DeclKind::Import { import: import2, source_decl: d2_next } = d2.kind + && import1 == import2 + && d1.ambiguity == d2.ambiguity + { + assert!(d1.ambiguity.is_none()); + assert_eq!(d1.warn_ambiguity, d2.warn_ambiguity); + assert_eq!(d1.expansion, d2.expansion); + assert_eq!(d1.span, d2.span); + assert_eq!(d1.vis, d2.vis); + remove_same_import(d1_next, d2_next) + } else { + (d1, d2) + } +} + impl<'ra, 'tcx> Resolver<'ra, 'tcx> { /// Given an import and the declaration that it points to, /// create the corresponding import declaration. @@ -325,6 +343,61 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { }) } + /// If `glob_decl` attempts to overwrite `old_glob_decl` in a module, + /// decide which one to keep. + fn select_glob_decl( + &self, + glob_decl: Decl<'ra>, + old_glob_decl: Decl<'ra>, + warn_ambiguity: bool, + ) -> Decl<'ra> { + assert!(glob_decl.is_glob_import()); + assert!(old_glob_decl.is_glob_import()); + assert_ne!(glob_decl, old_glob_decl); + // `best_decl` with a given key in a module may be overwritten in a + // number of cases (all of them can be seen below in the `match` in `try_define_local`), + // all these overwrites will be re-fetched by glob imports importing + // from that module without generating new ambiguities. + // - A glob decl is overwritten by a non-glob decl arriving later. + // - A glob decl is overwritten by an ambiguous glob decl. + // FIXME: avoid this by putting `DeclData::ambiguity` under a + // cell and updating it in place. + // - A glob decl is overwritten by the same decl with `warn_ambiguity == true`. + // FIXME: avoid this by putting `DeclData::warn_ambiguity` under a + // cell and updating it in place. + // - A glob decl is overwritten by a glob decl with larger visibility. + // FIXME: avoid this by putting `DeclData::vis` under a cell + // and updating it in place. + // - A glob decl is overwritten by a glob decl re-fetching an + // overwritten decl from other module (the recursive case). + // Here we are detecting all such re-fetches and overwrite old decls + // with the re-fetched decls. + // This is probably incorrect in corner cases, and the outdated decls still get + // propagated to other places and get stuck there, but that's what we have at the moment. + let (deep_decl, old_deep_decl) = remove_same_import(glob_decl, old_glob_decl); + if deep_decl != glob_decl { + // Some import layers have been removed, need to overwrite. + assert_ne!(old_deep_decl, old_glob_decl); + assert_ne!(old_deep_decl, deep_decl); + assert!(old_deep_decl.is_glob_import()); + if glob_decl.is_ambiguity_recursive() { + self.new_decl_with_warn_ambiguity(glob_decl) + } else { + glob_decl + } + } else if glob_decl.res() != old_glob_decl.res() { + self.new_decl_with_ambiguity(old_glob_decl, glob_decl, warn_ambiguity) + } else if !old_glob_decl.vis.is_at_least(glob_decl.vis, self.tcx) { + // We are glob-importing the same item but with greater visibility. + glob_decl + } else if glob_decl.is_ambiguity_recursive() { + // Overwriting with an ambiguous glob import. + self.new_decl_with_warn_ambiguity(glob_decl) + } else { + old_glob_decl + } + } + /// Attempt to put the declaration with the given name and namespace into the module, /// and return existing declaration if there is a collision. pub(crate) fn try_plant_decl_into_local_module( @@ -347,52 +420,25 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { }); self.update_local_resolution(module, key, warn_ambiguity, |this, resolution| { if let Some(old_decl) = resolution.best_decl() { + assert_ne!(decl, old_decl); if res == Res::Err && old_decl.res() != Res::Err { // Do not override real declarations with `Res::Err`s from error recovery. return Ok(()); } match (old_decl.is_glob_import(), decl.is_glob_import()) { (true, true) => { - let (glob_decl, old_glob_decl) = (decl, old_decl); - // FIXME: remove `!decl.is_ambiguity_recursive()` after delete the warning ambiguity. - if !decl.is_ambiguity_recursive() - && let DeclKind::Import { import: old_import, .. } = old_glob_decl.kind - && let DeclKind::Import { import, .. } = glob_decl.kind - && old_import == import - { - // When imported from the same glob-import statement, we should replace - // `old_glob_decl` with `glob_decl`, regardless of whether - // they have the same resolution or not. - resolution.glob_decl = Some(glob_decl); - } else if res != old_glob_decl.res() { - resolution.glob_decl = Some(this.new_decl_with_ambiguity( - old_glob_decl, - glob_decl, - warn_ambiguity, - )); - } else if !old_decl.vis.is_at_least(decl.vis, this.tcx) { - // We are glob-importing the same item but with greater visibility. - resolution.glob_decl = Some(glob_decl); - } else if decl.is_ambiguity_recursive() { - resolution.glob_decl = - Some(this.new_decl_with_warn_ambiguity(glob_decl)); - } + resolution.glob_decl = + Some(this.select_glob_decl(decl, old_decl, warn_ambiguity)); } (old_glob @ true, false) | (old_glob @ false, true) => { let (glob_decl, non_glob_decl) = if old_glob { (old_decl, decl) } else { (decl, old_decl) }; resolution.non_glob_decl = Some(non_glob_decl); - if let Some(old_glob_decl) = resolution.glob_decl { - assert!(old_glob_decl.is_glob_import()); - if glob_decl.res() != old_glob_decl.res() { - resolution.glob_decl = Some(this.new_decl_with_ambiguity( - old_glob_decl, - glob_decl, - false, - )); - } else if !old_glob_decl.vis.is_at_least(decl.vis, this.tcx) { - resolution.glob_decl = Some(glob_decl); - } + if let Some(old_glob_decl) = resolution.glob_decl + && old_glob_decl != glob_decl + { + resolution.glob_decl = + Some(this.select_glob_decl(glob_decl, old_glob_decl, false)); } else { resolution.glob_decl = Some(glob_decl); } From 32cf3a96d7d2695f16f00e594e291ae18c00dc32 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Mon, 29 Dec 2025 22:12:56 +0300 Subject: [PATCH 2/5] resolve: Update `NameBindingData::warn_ambiguity` in place instead of creating fresh bindings. --- .../rustc_resolve/src/build_reduced_graph.rs | 2 +- .../src/effective_visibilities.rs | 4 ++-- compiler/rustc_resolve/src/imports.rs | 21 +++++++------------ compiler/rustc_resolve/src/lib.rs | 12 +++++------ 4 files changed, 16 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index d56ca7c079cb7..0159e9c9eda41 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -91,7 +91,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { kind: DeclKind::Def(res), ambiguity, // External ambiguities always report the `AMBIGUOUS_GLOB_IMPORTS` lint at the moment. - warn_ambiguity: true, + warn_ambiguity: CmCell::new(true), vis, span, expansion, diff --git a/compiler/rustc_resolve/src/effective_visibilities.rs b/compiler/rustc_resolve/src/effective_visibilities.rs index e5144332f2b72..eee12922e511f 100644 --- a/compiler/rustc_resolve/src/effective_visibilities.rs +++ b/compiler/rustc_resolve/src/effective_visibilities.rs @@ -127,7 +127,7 @@ impl<'a, 'ra, 'tcx> EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx> { // lint. For all bindings added to the table this way `is_ambiguity` returns true. let is_ambiguity = |decl: Decl<'ra>, warn: bool| decl.ambiguity.is_some() && !warn; let mut parent_id = ParentId::Def(module_id); - let mut warn_ambiguity = decl.warn_ambiguity; + let mut warn_ambiguity = decl.warn_ambiguity.get(); while let DeclKind::Import { source_decl, .. } = decl.kind { self.update_import(decl, parent_id); @@ -140,7 +140,7 @@ impl<'a, 'ra, 'tcx> EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx> { parent_id = ParentId::Import(decl); decl = source_decl; - warn_ambiguity |= source_decl.warn_ambiguity; + warn_ambiguity |= source_decl.warn_ambiguity.get(); } if !is_ambiguity(decl, warn_ambiguity) && let Some(def_id) = decl.res().opt_def_id().and_then(|id| id.as_local()) diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 3f998b8c8aa03..7f28ac29ac1dc 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -303,7 +303,7 @@ fn remove_same_import<'ra>(d1: Decl<'ra>, d2: Decl<'ra>) -> (Decl<'ra>, Decl<'ra && d1.ambiguity == d2.ambiguity { assert!(d1.ambiguity.is_none()); - assert_eq!(d1.warn_ambiguity, d2.warn_ambiguity); + assert_eq!(d1.warn_ambiguity.get(), d2.warn_ambiguity.get()); assert_eq!(d1.expansion, d2.expansion); assert_eq!(d1.span, d2.span); assert_eq!(d1.vis, d2.vis); @@ -336,7 +336,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { self.arenas.alloc_decl(DeclData { kind: DeclKind::Import { source_decl: decl, import }, ambiguity: None, - warn_ambiguity: false, + warn_ambiguity: CmCell::new(false), span: import.span, vis, expansion: import.parent_scope.expansion, @@ -362,9 +362,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // - A glob decl is overwritten by an ambiguous glob decl. // FIXME: avoid this by putting `DeclData::ambiguity` under a // cell and updating it in place. - // - A glob decl is overwritten by the same decl with `warn_ambiguity == true`. - // FIXME: avoid this by putting `DeclData::warn_ambiguity` under a - // cell and updating it in place. // - A glob decl is overwritten by a glob decl with larger visibility. // FIXME: avoid this by putting `DeclData::vis` under a cell // and updating it in place. @@ -381,10 +378,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { assert_ne!(old_deep_decl, deep_decl); assert!(old_deep_decl.is_glob_import()); if glob_decl.is_ambiguity_recursive() { - self.new_decl_with_warn_ambiguity(glob_decl) - } else { - glob_decl + glob_decl.warn_ambiguity.set_unchecked(true); } + glob_decl } else if glob_decl.res() != old_glob_decl.res() { self.new_decl_with_ambiguity(old_glob_decl, glob_decl, warn_ambiguity) } else if !old_glob_decl.vis.is_at_least(glob_decl.vis, self.tcx) { @@ -392,7 +388,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { glob_decl } else if glob_decl.is_ambiguity_recursive() { // Overwriting with an ambiguous glob import. - self.new_decl_with_warn_ambiguity(glob_decl) + glob_decl.warn_ambiguity.set_unchecked(true); + glob_decl } else { old_glob_decl } @@ -466,15 +463,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { warn_ambiguity: bool, ) -> Decl<'ra> { let ambiguity = Some(secondary_decl); + let warn_ambiguity = CmCell::new(warn_ambiguity); let data = DeclData { ambiguity, warn_ambiguity, ..*primary_decl }; self.arenas.alloc_decl(data) } - fn new_decl_with_warn_ambiguity(&self, decl: Decl<'ra>) -> Decl<'ra> { - assert!(decl.is_ambiguity_recursive()); - self.arenas.alloc_decl(DeclData { warn_ambiguity: true, ..*decl }) - } - // Use `f` to mutate the resolution of the name in the module. // If the resolution becomes a success, define it in the module's glob importers. fn update_local_resolution( diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 063b6b4058f05..cf8d1952d4a03 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -802,13 +802,13 @@ impl<'ra> fmt::Debug for Module<'ra> { } /// Data associated with any name declaration. -#[derive(Clone, Copy, Debug)] +#[derive(Debug)] struct DeclData<'ra> { kind: DeclKind<'ra>, ambiguity: Option>, /// Produce a warning instead of an error when reporting ambiguities inside this binding. /// May apply to indirect ambiguities under imports, so `ambiguity.is_some()` is not required. - warn_ambiguity: bool, + warn_ambiguity: CmCell, expansion: LocalExpnId, span: Span, vis: Visibility, @@ -956,7 +956,7 @@ impl<'ra> DeclData<'ra> { } fn warn_ambiguity_recursive(&self) -> bool { - self.warn_ambiguity + self.warn_ambiguity.get() || match self.kind { DeclKind::Import { source_decl, .. } => source_decl.warn_ambiguity_recursive(), _ => false, @@ -1343,7 +1343,7 @@ impl<'ra> ResolverArenas<'ra> { self.alloc_decl(DeclData { kind: DeclKind::Def(res), ambiguity: None, - warn_ambiguity: false, + warn_ambiguity: CmCell::new(false), vis, span, expansion, @@ -2041,7 +2041,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } fn record_use(&mut self, ident: Ident, used_decl: Decl<'ra>, used: Used) { - self.record_use_inner(ident, used_decl, used, used_decl.warn_ambiguity); + self.record_use_inner(ident, used_decl, used, used_decl.warn_ambiguity.get()); } fn record_use_inner( @@ -2112,7 +2112,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ident, source_decl, Used::Other, - warn_ambiguity || source_decl.warn_ambiguity, + warn_ambiguity || source_decl.warn_ambiguity.get(), ); } } From 227e7bd48b5a7f3479bc4b231cf042e092fadd89 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Tue, 30 Dec 2025 01:47:50 +0300 Subject: [PATCH 3/5] resolve: Update `NameBindingData::vis` in place instead of overwriting bindings in modules. --- .../rustc_resolve/src/build_reduced_graph.rs | 26 +++++++------- compiler/rustc_resolve/src/diagnostics.rs | 8 ++--- .../src/effective_visibilities.rs | 4 +-- compiler/rustc_resolve/src/ident.rs | 11 +++--- compiler/rustc_resolve/src/imports.rs | 35 +++++++++---------- .../rustc_resolve/src/late/diagnostics.rs | 2 +- compiler/rustc_resolve/src/lib.rs | 8 +++-- 7 files changed, 49 insertions(+), 45 deletions(-) diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 0159e9c9eda41..0167b80386497 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -92,7 +92,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ambiguity, // External ambiguities always report the `AMBIGUOUS_GLOB_IMPORTS` lint at the moment. warn_ambiguity: CmCell::new(true), - vis, + vis: CmCell::new(vis), span, expansion, }); @@ -1158,18 +1158,18 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { self.r.potentially_unused_imports.push(import); module.for_each_child_mut(self, |this, ident, ns, binding| { if ns == MacroNS { - let import = if this.r.is_accessible_from(binding.vis, this.parent_scope.module) - { - import - } else { - // FIXME: This branch is used for reporting the `private_macro_use` lint - // and should eventually be removed. - if this.r.macro_use_prelude.contains_key(&ident.name) { - // Do not override already existing entries with compatibility entries. - return; - } - macro_use_import(this, span, true) - }; + let import = + if this.r.is_accessible_from(binding.vis(), this.parent_scope.module) { + import + } else { + // FIXME: This branch is used for reporting the `private_macro_use` lint + // and should eventually be removed. + if this.r.macro_use_prelude.contains_key(&ident.name) { + // Do not override already existing entries with compatibility entries. + return; + } + macro_use_import(this, span, true) + }; let import_decl = this.r.new_import_decl(binding, import); this.add_macro_use_decl(ident.name, import_decl, span, allow_shadowing); } diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 7c86ed91a07ad..d704280f3fa23 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1338,7 +1338,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } let child_accessible = - accessible && this.is_accessible_from(name_binding.vis, parent_scope.module); + accessible && this.is_accessible_from(name_binding.vis(), parent_scope.module); // do not venture inside inaccessible items of other crates if in_module_is_extern && !child_accessible { @@ -1358,8 +1358,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // #90113: Do not count an inaccessible reexported item as a candidate. if let DeclKind::Import { source_decl, .. } = name_binding.kind - && this.is_accessible_from(source_decl.vis, parent_scope.module) - && !this.is_accessible_from(name_binding.vis, parent_scope.module) + && this.is_accessible_from(source_decl.vis(), parent_scope.module) + && !this.is_accessible_from(name_binding.vis(), parent_scope.module) { return; } @@ -2243,7 +2243,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let first = binding == first_binding; let def_span = self.tcx.sess.source_map().guess_head_span(binding.span); let mut note_span = MultiSpan::from_span(def_span); - if !first && binding.vis.is_public() { + if !first && binding.vis().is_public() { let desc = match binding.kind { DeclKind::Import { .. } => "re-export", _ => "directly", diff --git a/compiler/rustc_resolve/src/effective_visibilities.rs b/compiler/rustc_resolve/src/effective_visibilities.rs index eee12922e511f..7272314b9aa7d 100644 --- a/compiler/rustc_resolve/src/effective_visibilities.rs +++ b/compiler/rustc_resolve/src/effective_visibilities.rs @@ -145,7 +145,7 @@ impl<'a, 'ra, 'tcx> EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx> { if !is_ambiguity(decl, warn_ambiguity) && let Some(def_id) = decl.res().opt_def_id().and_then(|id| id.as_local()) { - self.update_def(def_id, decl.vis.expect_local(), parent_id); + self.update_def(def_id, decl.vis().expect_local(), parent_id); } } } @@ -188,7 +188,7 @@ impl<'a, 'ra, 'tcx> EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx> { } fn update_import(&mut self, decl: Decl<'ra>, parent_id: ParentId<'ra>) { - let nominal_vis = decl.vis.expect_local(); + let nominal_vis = decl.vis().expect_local(); let Some(cheap_private_vis) = self.may_update(nominal_vis, parent_id) else { return }; let inherited_eff_vis = self.effective_vis_or_private(parent_id); let tcx = self.r.tcx; diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index e0acf043ffcf6..f47bb28d3fa1f 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -1017,7 +1017,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // Items and single imports are not shadowable, if we have one, then it's determined. if let Some(binding) = binding { - let accessible = self.is_accessible_from(binding.vis, parent_scope.module); + let accessible = self.is_accessible_from(binding.vis(), parent_scope.module); return if accessible { Ok(binding) } else { Err(ControlFlow::Break(Determined)) }; } @@ -1103,7 +1103,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // shadowing is enabled, see `macro_expanded_macro_export_errors`). if let Some(binding) = binding { return if binding.determined() || ns == MacroNS || shadowing == Shadowing::Restricted { - let accessible = self.is_accessible_from(binding.vis, parent_scope.module); + let accessible = self.is_accessible_from(binding.vis(), parent_scope.module); if accessible { Ok(binding) } else { Err(ControlFlow::Break(Determined)) } } else { Err(ControlFlow::Break(Undetermined)) @@ -1160,7 +1160,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { match result { Err(Determined) => continue, Ok(binding) - if !self.is_accessible_from(binding.vis, glob_import.parent_scope.module) => + if !self.is_accessible_from(binding.vis(), glob_import.parent_scope.module) => { continue; } @@ -1187,7 +1187,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { return Err(ControlFlow::Continue(Determined)); }; - if !self.is_accessible_from(binding.vis, parent_scope.module) { + if !self.is_accessible_from(binding.vis(), parent_scope.module) { if report_private { self.privacy_errors.push(PrivacyError { ident, @@ -1304,7 +1304,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ) { Err(Determined) => continue, Ok(binding) - if !self.is_accessible_from(binding.vis, single_import.parent_scope.module) => + if !self + .is_accessible_from(binding.vis(), single_import.parent_scope.module) => { continue; } diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 7f28ac29ac1dc..5b0755dbde25b 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -306,7 +306,7 @@ fn remove_same_import<'ra>(d1: Decl<'ra>, d2: Decl<'ra>) -> (Decl<'ra>, Decl<'ra assert_eq!(d1.warn_ambiguity.get(), d2.warn_ambiguity.get()); assert_eq!(d1.expansion, d2.expansion); assert_eq!(d1.span, d2.span); - assert_eq!(d1.vis, d2.vis); + assert_eq!(d1.vis(), d2.vis()); remove_same_import(d1_next, d2_next) } else { (d1, d2) @@ -318,12 +318,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { /// create the corresponding import declaration. pub(crate) fn new_import_decl(&self, decl: Decl<'ra>, import: Import<'ra>) -> Decl<'ra> { let import_vis = import.vis.to_def_id(); - let vis = if decl.vis.is_at_least(import_vis, self.tcx) + let vis = if decl.vis().is_at_least(import_vis, self.tcx) || pub_use_of_private_extern_crate_hack(import, decl).is_some() { import_vis } else { - decl.vis + decl.vis() }; if let ImportKind::Glob { ref max_vis, .. } = import.kind @@ -338,7 +338,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ambiguity: None, warn_ambiguity: CmCell::new(false), span: import.span, - vis, + vis: CmCell::new(vis), expansion: import.parent_scope.expansion, }) } @@ -362,9 +362,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // - A glob decl is overwritten by an ambiguous glob decl. // FIXME: avoid this by putting `DeclData::ambiguity` under a // cell and updating it in place. - // - A glob decl is overwritten by a glob decl with larger visibility. - // FIXME: avoid this by putting `DeclData::vis` under a cell - // and updating it in place. // - A glob decl is overwritten by a glob decl re-fetching an // overwritten decl from other module (the recursive case). // Here we are detecting all such re-fetches and overwrite old decls @@ -383,9 +380,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { glob_decl } else if glob_decl.res() != old_glob_decl.res() { self.new_decl_with_ambiguity(old_glob_decl, glob_decl, warn_ambiguity) - } else if !old_glob_decl.vis.is_at_least(glob_decl.vis, self.tcx) { + } else if !old_glob_decl.vis().is_at_least(glob_decl.vis(), self.tcx) { // We are glob-importing the same item but with greater visibility. - glob_decl + old_glob_decl.vis.set_unchecked(glob_decl.vis()); + old_glob_decl } else if glob_decl.is_ambiguity_recursive() { // Overwriting with an ambiguous glob import. glob_decl.warn_ambiguity.set_unchecked(true); @@ -464,7 +462,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ) -> Decl<'ra> { let ambiguity = Some(secondary_decl); let warn_ambiguity = CmCell::new(warn_ambiguity); - let data = DeclData { ambiguity, warn_ambiguity, ..*primary_decl }; + let vis = primary_decl.vis.clone(); + let data = DeclData { ambiguity, warn_ambiguity, vis, ..*primary_decl }; self.arenas.alloc_decl(data) } @@ -509,7 +508,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { Some(None) => import.parent_scope.module, None => continue, }; - if self.is_accessible_from(binding.vis, scope) { + if self.is_accessible_from(binding.vis(), scope) { let import_decl = self.new_import_decl(binding, *import); let _ = self.try_plant_decl_into_local_module( import.parent_scope.module, @@ -699,8 +698,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { && let Some(glob_import_id) = glob_import.id() && let glob_import_def_id = self.local_def_id(glob_import_id) && self.effective_visibilities.is_exported(glob_import_def_id) - && glob_decl.vis.is_public() - && !binding.vis.is_public() + && glob_decl.vis().is_public() + && !binding.vis().is_public() { let binding_id = match binding.kind { DeclKind::Def(res) => { @@ -1330,9 +1329,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { return; }; - if !binding.vis.is_at_least(import.vis, this.tcx) { + if !binding.vis().is_at_least(import.vis, this.tcx) { reexport_error = Some((ns, binding)); - if let Visibility::Restricted(binding_def_id) = binding.vis + if let Visibility::Restricted(binding_def_id) = binding.vis() && binding_def_id.is_top_level_module() { crate_private_reexport = true; @@ -1535,7 +1534,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { Some(None) => import.parent_scope.module, None => continue, }; - if self.is_accessible_from(binding.vis, scope) { + if self.is_accessible_from(binding.vis(), scope) { let import_decl = self.new_import_decl(binding, import); let warn_ambiguity = self .resolution(import.parent_scope.module, key) @@ -1577,7 +1576,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let child = |reexport_chain| ModChild { ident: ident.0, res, - vis: binding.vis, + vis: binding.vis(), reexport_chain, }; if let Some((ambig_binding1, ambig_binding2)) = binding.descent_to_ambiguity() { @@ -1585,7 +1584,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let second = ModChild { ident: ident.0, res: ambig_binding2.res().expect_non_local(), - vis: ambig_binding2.vis, + vis: ambig_binding2.vis(), reexport_chain: ambig_binding2.reexport_chain(this), }; ambig_children.push(AmbigModChild { main, second }) diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 73e1a8f0c3bcc..a2805497e7961 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -2793,7 +2793,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { in_module.for_each_child(self.r, |r, ident, _, name_binding| { // abort if the module is already found or if name_binding is private external - if result.is_some() || !name_binding.vis.is_visible_locally() { + if result.is_some() || !name_binding.vis().is_visible_locally() { return; } if let Some(module_def_id) = name_binding.res().module_like_def_id() { diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index cf8d1952d4a03..bce14f5376d7c 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -811,7 +811,7 @@ struct DeclData<'ra> { warn_ambiguity: CmCell, expansion: LocalExpnId, span: Span, - vis: Visibility, + vis: CmCell>, } /// All name declarations are unique and allocated on a same arena, @@ -923,6 +923,10 @@ struct AmbiguityError<'ra> { } impl<'ra> DeclData<'ra> { + fn vis(&self) -> Visibility { + self.vis.get() + } + fn res(&self) -> Res { match self.kind { DeclKind::Def(res) => res, @@ -1344,7 +1348,7 @@ impl<'ra> ResolverArenas<'ra> { kind: DeclKind::Def(res), ambiguity: None, warn_ambiguity: CmCell::new(false), - vis, + vis: CmCell::new(vis), span, expansion, }) From c2379717a2e9f2befdbea2795fea696d7c86d82c Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Tue, 30 Dec 2025 02:49:14 +0300 Subject: [PATCH 4/5] resolve: Tweak overwriting with ambiguous globs Do not overwrite unless necessary, and combine both globs instead of losing the first one. --- compiler/rustc_resolve/src/imports.rs | 7 +++---- tests/ui/imports/ambiguous-14.stderr | 12 ++++++------ tests/ui/imports/duplicate.stderr | 12 ++++++------ 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 5b0755dbde25b..76aeb7ba85b80 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -384,10 +384,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // We are glob-importing the same item but with greater visibility. old_glob_decl.vis.set_unchecked(glob_decl.vis()); old_glob_decl - } else if glob_decl.is_ambiguity_recursive() { - // Overwriting with an ambiguous glob import. - glob_decl.warn_ambiguity.set_unchecked(true); - glob_decl + } else if glob_decl.is_ambiguity_recursive() && !old_glob_decl.is_ambiguity_recursive() { + // Overwriting a non-ambiguous glob import with an ambiguous glob import. + self.new_decl_with_ambiguity(old_glob_decl, glob_decl, true) } else { old_glob_decl } diff --git a/tests/ui/imports/ambiguous-14.stderr b/tests/ui/imports/ambiguous-14.stderr index 4efa31c61e328..2a3557c31f120 100644 --- a/tests/ui/imports/ambiguous-14.stderr +++ b/tests/ui/imports/ambiguous-14.stderr @@ -8,15 +8,15 @@ LL | g::foo(); = note: for more information, see issue #114095 = note: ambiguous because of multiple glob imports of a name in the same module note: `foo` could refer to the function imported here - --> $DIR/ambiguous-14.rs:13:13 + --> $DIR/ambiguous-14.rs:18:13 | LL | pub use a::*; | ^^^^ = help: consider adding an explicit import of `foo` to disambiguate note: `foo` could also refer to the function imported here - --> $DIR/ambiguous-14.rs:14:13 + --> $DIR/ambiguous-14.rs:19:13 | -LL | pub use b::*; +LL | pub use f::*; | ^^^^ = help: consider adding an explicit import of `foo` to disambiguate = note: `#[deny(ambiguous_glob_imports)]` (part of `#[deny(future_incompatible)]`) on by default @@ -34,15 +34,15 @@ LL | g::foo(); = note: for more information, see issue #114095 = note: ambiguous because of multiple glob imports of a name in the same module note: `foo` could refer to the function imported here - --> $DIR/ambiguous-14.rs:13:13 + --> $DIR/ambiguous-14.rs:18:13 | LL | pub use a::*; | ^^^^ = help: consider adding an explicit import of `foo` to disambiguate note: `foo` could also refer to the function imported here - --> $DIR/ambiguous-14.rs:14:13 + --> $DIR/ambiguous-14.rs:19:13 | -LL | pub use b::*; +LL | pub use f::*; | ^^^^ = help: consider adding an explicit import of `foo` to disambiguate = note: `#[deny(ambiguous_glob_imports)]` (part of `#[deny(future_incompatible)]`) on by default diff --git a/tests/ui/imports/duplicate.stderr b/tests/ui/imports/duplicate.stderr index 5cd3b0c2c8a55..74829fc21e22f 100644 --- a/tests/ui/imports/duplicate.stderr +++ b/tests/ui/imports/duplicate.stderr @@ -78,15 +78,15 @@ LL | g::foo(); = note: for more information, see issue #114095 = note: ambiguous because of multiple glob imports of a name in the same module note: `foo` could refer to the function imported here - --> $DIR/duplicate.rs:24:13 + --> $DIR/duplicate.rs:29:13 | LL | pub use crate::a::*; | ^^^^^^^^^^^ = help: consider adding an explicit import of `foo` to disambiguate note: `foo` could also refer to the function imported here - --> $DIR/duplicate.rs:25:13 + --> $DIR/duplicate.rs:30:13 | -LL | pub use crate::b::*; +LL | pub use crate::f::*; | ^^^^^^^^^^^ = help: consider adding an explicit import of `foo` to disambiguate = note: `#[deny(ambiguous_glob_imports)]` (part of `#[deny(future_incompatible)]`) on by default @@ -106,15 +106,15 @@ LL | g::foo(); = note: for more information, see issue #114095 = note: ambiguous because of multiple glob imports of a name in the same module note: `foo` could refer to the function imported here - --> $DIR/duplicate.rs:24:13 + --> $DIR/duplicate.rs:29:13 | LL | pub use crate::a::*; | ^^^^^^^^^^^ = help: consider adding an explicit import of `foo` to disambiguate note: `foo` could also refer to the function imported here - --> $DIR/duplicate.rs:25:13 + --> $DIR/duplicate.rs:30:13 | -LL | pub use crate::b::*; +LL | pub use crate::f::*; | ^^^^^^^^^^^ = help: consider adding an explicit import of `foo` to disambiguate = note: `#[deny(ambiguous_glob_imports)]` (part of `#[deny(future_incompatible)]`) on by default From a251ae2615bd92b90e4c850a8026bff47e4786bf Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Tue, 30 Dec 2025 02:54:43 +0300 Subject: [PATCH 5/5] resolve: Update `NameBindingData::ambiguity` in place instead of creating fresh bindings, except in one case. --- .../rustc_resolve/src/build_reduced_graph.rs | 2 +- .../src/effective_visibilities.rs | 7 ++- compiler/rustc_resolve/src/imports.rs | 51 ++++++++++--------- compiler/rustc_resolve/src/lib.rs | 12 ++--- tests/ui/imports/issue-55884-1.rs | 2 +- tests/ui/imports/issue-55884-1.stderr | 22 +++++++- 6 files changed, 60 insertions(+), 36 deletions(-) diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 0167b80386497..6bface926edc8 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -89,7 +89,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ) { let decl = self.arenas.alloc_decl(DeclData { kind: DeclKind::Def(res), - ambiguity, + ambiguity: CmCell::new(ambiguity), // External ambiguities always report the `AMBIGUOUS_GLOB_IMPORTS` lint at the moment. warn_ambiguity: CmCell::new(true), vis: CmCell::new(vis), diff --git a/compiler/rustc_resolve/src/effective_visibilities.rs b/compiler/rustc_resolve/src/effective_visibilities.rs index 7272314b9aa7d..ebdb0060e1b94 100644 --- a/compiler/rustc_resolve/src/effective_visibilities.rs +++ b/compiler/rustc_resolve/src/effective_visibilities.rs @@ -100,7 +100,9 @@ impl<'a, 'ra, 'tcx> EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx> { if let Some(node_id) = import.id() { r.effective_visibilities.update_eff_vis(r.local_def_id(node_id), eff_vis, r.tcx) } - } else if decl.ambiguity.is_some() && eff_vis.is_public_at_level(Level::Reexported) { + } else if decl.ambiguity.get().is_some() + && eff_vis.is_public_at_level(Level::Reexported) + { exported_ambiguities.insert(*decl); } } @@ -125,7 +127,8 @@ impl<'a, 'ra, 'tcx> EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx> { // If the binding is ambiguous, put the root ambiguity binding and all reexports // leading to it into the table. They are used by the `ambiguous_glob_reexports` // lint. For all bindings added to the table this way `is_ambiguity` returns true. - let is_ambiguity = |decl: Decl<'ra>, warn: bool| decl.ambiguity.is_some() && !warn; + let is_ambiguity = + |decl: Decl<'ra>, warn: bool| decl.ambiguity.get().is_some() && !warn; let mut parent_id = ParentId::Def(module_id); let mut warn_ambiguity = decl.warn_ambiguity.get(); while let DeclKind::Import { source_decl, .. } = decl.kind { diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 76aeb7ba85b80..68f042c6e041d 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -300,10 +300,10 @@ fn remove_same_import<'ra>(d1: Decl<'ra>, d2: Decl<'ra>) -> (Decl<'ra>, Decl<'ra if let DeclKind::Import { import: import1, source_decl: d1_next } = d1.kind && let DeclKind::Import { import: import2, source_decl: d2_next } = d2.kind && import1 == import2 - && d1.ambiguity == d2.ambiguity + && d1.warn_ambiguity.get() == d2.warn_ambiguity.get() { - assert!(d1.ambiguity.is_none()); - assert_eq!(d1.warn_ambiguity.get(), d2.warn_ambiguity.get()); + assert_eq!(d1.ambiguity.get(), d2.ambiguity.get()); + assert!(!d1.warn_ambiguity.get()); assert_eq!(d1.expansion, d2.expansion); assert_eq!(d1.span, d2.span); assert_eq!(d1.vis(), d2.vis()); @@ -335,7 +335,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { self.arenas.alloc_decl(DeclData { kind: DeclKind::Import { source_decl: decl, import }, - ambiguity: None, + ambiguity: CmCell::new(None), warn_ambiguity: CmCell::new(false), span: import.span, vis: CmCell::new(vis), @@ -359,9 +359,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // all these overwrites will be re-fetched by glob imports importing // from that module without generating new ambiguities. // - A glob decl is overwritten by a non-glob decl arriving later. - // - A glob decl is overwritten by an ambiguous glob decl. - // FIXME: avoid this by putting `DeclData::ambiguity` under a - // cell and updating it in place. + // - A glob decl is overwritten by its clone after setting ambiguity in it. + // FIXME: avoid this by removing `warn_ambiguity`, or by triggering glob re-fetch + // with the same decl in some way. // - A glob decl is overwritten by a glob decl re-fetching an // overwritten decl from other module (the recursive case). // Here we are detecting all such re-fetches and overwrite old decls @@ -372,21 +372,34 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if deep_decl != glob_decl { // Some import layers have been removed, need to overwrite. assert_ne!(old_deep_decl, old_glob_decl); - assert_ne!(old_deep_decl, deep_decl); - assert!(old_deep_decl.is_glob_import()); + // FIXME: reenable the asserts when `warn_ambiguity` is removed (#149195). + // assert_ne!(old_deep_decl, deep_decl); + // assert!(old_deep_decl.is_glob_import()); + assert!(!deep_decl.is_glob_import()); if glob_decl.is_ambiguity_recursive() { glob_decl.warn_ambiguity.set_unchecked(true); } glob_decl } else if glob_decl.res() != old_glob_decl.res() { - self.new_decl_with_ambiguity(old_glob_decl, glob_decl, warn_ambiguity) + old_glob_decl.ambiguity.set_unchecked(Some(glob_decl)); + old_glob_decl.warn_ambiguity.set_unchecked(warn_ambiguity); + if warn_ambiguity { + old_glob_decl + } else { + // Need a fresh decl so other glob imports importing it could re-fetch it + // and set their own `warn_ambiguity` to true. + // FIXME: remove this when `warn_ambiguity` is removed (#149195). + self.arenas.alloc_decl((*old_glob_decl).clone()) + } } else if !old_glob_decl.vis().is_at_least(glob_decl.vis(), self.tcx) { // We are glob-importing the same item but with greater visibility. old_glob_decl.vis.set_unchecked(glob_decl.vis()); old_glob_decl } else if glob_decl.is_ambiguity_recursive() && !old_glob_decl.is_ambiguity_recursive() { // Overwriting a non-ambiguous glob import with an ambiguous glob import. - self.new_decl_with_ambiguity(old_glob_decl, glob_decl, true) + old_glob_decl.ambiguity.set_unchecked(Some(glob_decl)); + old_glob_decl.warn_ambiguity.set_unchecked(true); + old_glob_decl } else { old_glob_decl } @@ -415,6 +428,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { self.update_local_resolution(module, key, warn_ambiguity, |this, resolution| { if let Some(old_decl) = resolution.best_decl() { assert_ne!(decl, old_decl); + assert!(!decl.warn_ambiguity.get()); if res == Res::Err && old_decl.res() != Res::Err { // Do not override real declarations with `Res::Err`s from error recovery. return Ok(()); @@ -453,19 +467,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { }) } - fn new_decl_with_ambiguity( - &self, - primary_decl: Decl<'ra>, - secondary_decl: Decl<'ra>, - warn_ambiguity: bool, - ) -> Decl<'ra> { - let ambiguity = Some(secondary_decl); - let warn_ambiguity = CmCell::new(warn_ambiguity); - let vis = primary_decl.vis.clone(); - let data = DeclData { ambiguity, warn_ambiguity, vis, ..*primary_decl }; - self.arenas.alloc_decl(data) - } - // Use `f` to mutate the resolution of the name in the module. // If the resolution becomes a success, define it in the module's glob importers. fn update_local_resolution( @@ -671,7 +672,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let Some(binding) = resolution.best_decl() else { continue }; if let DeclKind::Import { import, .. } = binding.kind - && let Some(amb_binding) = binding.ambiguity + && let Some(amb_binding) = binding.ambiguity.get() && binding.res() != Res::Err && exported_ambiguities.contains(&binding) { diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index bce14f5376d7c..311efb3e881b1 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -802,10 +802,10 @@ impl<'ra> fmt::Debug for Module<'ra> { } /// Data associated with any name declaration. -#[derive(Debug)] +#[derive(Clone, Debug)] struct DeclData<'ra> { kind: DeclKind<'ra>, - ambiguity: Option>, + ambiguity: CmCell>>, /// Produce a warning instead of an error when reporting ambiguities inside this binding. /// May apply to indirect ambiguities under imports, so `ambiguity.is_some()` is not required. warn_ambiguity: CmCell, @@ -942,7 +942,7 @@ impl<'ra> DeclData<'ra> { } fn descent_to_ambiguity(self: Decl<'ra>) -> Option<(Decl<'ra>, Decl<'ra>)> { - match self.ambiguity { + match self.ambiguity.get() { Some(ambig_binding) => Some((self, ambig_binding)), None => match self.kind { DeclKind::Import { source_decl, .. } => source_decl.descent_to_ambiguity(), @@ -952,7 +952,7 @@ impl<'ra> DeclData<'ra> { } fn is_ambiguity_recursive(&self) -> bool { - self.ambiguity.is_some() + self.ambiguity.get().is_some() || match self.kind { DeclKind::Import { source_decl, .. } => source_decl.is_ambiguity_recursive(), _ => false, @@ -1346,7 +1346,7 @@ impl<'ra> ResolverArenas<'ra> { ) -> Decl<'ra> { self.alloc_decl(DeclData { kind: DeclKind::Def(res), - ambiguity: None, + ambiguity: CmCell::new(None), warn_ambiguity: CmCell::new(false), vis: CmCell::new(vis), span, @@ -2055,7 +2055,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { used: Used, warn_ambiguity: bool, ) { - if let Some(b2) = used_decl.ambiguity { + if let Some(b2) = used_decl.ambiguity.get() { let ambiguity_error = AmbiguityError { kind: AmbiguityKind::GlobVsGlob, ident, diff --git a/tests/ui/imports/issue-55884-1.rs b/tests/ui/imports/issue-55884-1.rs index 21744aa5d7bfa..3c9c033c158e1 100644 --- a/tests/ui/imports/issue-55884-1.rs +++ b/tests/ui/imports/issue-55884-1.rs @@ -17,5 +17,5 @@ mod m { fn main() { use m::S; //~ ERROR `S` is ambiguous - let s = S {}; + let s = S {}; //~ ERROR `S` is ambiguous } diff --git a/tests/ui/imports/issue-55884-1.stderr b/tests/ui/imports/issue-55884-1.stderr index ae8edb0495647..a0b63ddc91081 100644 --- a/tests/ui/imports/issue-55884-1.stderr +++ b/tests/ui/imports/issue-55884-1.stderr @@ -18,6 +18,26 @@ LL | pub use self::m2::*; | ^^^^^^^^^^^ = help: consider adding an explicit import of `S` to disambiguate -error: aborting due to 1 previous error +error[E0659]: `S` is ambiguous + --> $DIR/issue-55884-1.rs:20:13 + | +LL | let s = S {}; + | ^ ambiguous name + | + = note: ambiguous because of multiple glob imports of a name in the same module +note: `S` could refer to the struct imported here + --> $DIR/issue-55884-1.rs:14:13 + | +LL | pub use self::m1::*; + | ^^^^^^^^^^^ + = help: consider adding an explicit import of `S` to disambiguate +note: `S` could also refer to the struct imported here + --> $DIR/issue-55884-1.rs:15:13 + | +LL | pub use self::m2::*; + | ^^^^^^^^^^^ + = help: consider adding an explicit import of `S` to disambiguate + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0659`.