resolve: Update NameBindingData::ambiguity in place

instead of creating fresh bindings, except in one case.
This commit is contained in:
Vadim Petrochenkov 2025-12-30 02:54:43 +03:00
parent c2379717a2
commit a251ae2615
6 changed files with 60 additions and 36 deletions

View file

@ -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),

View file

@ -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 {

View file

@ -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<T, F>(
@ -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)
{

View file

@ -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<Decl<'ra>>,
ambiguity: CmCell<Option<Decl<'ra>>>,
/// 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<bool>,
@ -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,

View file

@ -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
}

View file

@ -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`.