-
Notifications
You must be signed in to change notification settings - Fork 13.3k
refactor(resolve): splitting glob and non-glob name bindings #112659
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -870,16 +870,11 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { | |
let resolution = | ||
self.resolution(module, key).try_borrow_mut().map_err(|_| (Determined, Weak::No))?; // This happens when there is a cycle of imports. | ||
|
||
// If the primary binding is unusable, search further and return the shadowed glob | ||
// binding if it exists. What we really want here is having two separate scopes in | ||
// a module - one for non-globs and one for globs, but until that's done use this | ||
// hack to avoid inconsistent resolution ICEs during import validation. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you keep this comment? |
||
let binding = | ||
[resolution.binding, resolution.shadowed_glob].into_iter().find_map(|binding| { | ||
match (binding, ignore_binding) { | ||
(Some(binding), Some(ignored)) if ptr::eq(binding, ignored) => None, | ||
_ => binding, | ||
} | ||
let binding = [resolution.non_glob_binding(), resolution.glob_binding()] | ||
.into_iter() | ||
.find_map(|binding| match (binding, ignore_binding) { | ||
(Some(binding), Some(ignored)) if ptr::eq(binding, ignored) => None, | ||
_ => binding, | ||
}); | ||
|
||
if let Some(Finalize { path_span, report_private, .. }) = finalize { | ||
|
@@ -900,7 +895,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { | |
} | ||
|
||
// Forbid expanded shadowing to avoid time travel. | ||
if let Some(shadowed_glob) = resolution.shadowed_glob | ||
if let Some(shadowed_glob) = resolution.glob_binding() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This line alone looks like a change in semantics - the binding is still glob now, but not necessarily shadowed. |
||
&& restricted_shadowing | ||
&& binding.expansion != LocalExpnId::ROOT | ||
&& binding.res() != shadowed_glob.res() | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -9,7 +9,7 @@ use crate::{ | |||||
Resolver, Segment, | ||||||
}; | ||||||
use crate::{Finalize, Module, ModuleOrUniformRoot, ParentScope, PerNS, ScopeSet}; | ||||||
use crate::{NameBinding, NameBindingKind, PathResult}; | ||||||
use crate::{NameBinding, NameBindingKind, PathResult, Res}; | ||||||
|
||||||
use rustc_ast::NodeId; | ||||||
use rustc_data_structures::fx::FxHashSet; | ||||||
|
@@ -34,8 +34,6 @@ use smallvec::SmallVec; | |||||
use std::cell::Cell; | ||||||
use std::{mem, ptr}; | ||||||
|
||||||
type Res = def::Res<NodeId>; | ||||||
|
||||||
/// Contains data for specific kinds of imports. | ||||||
#[derive(Clone)] | ||||||
pub(crate) enum ImportKind<'a> { | ||||||
|
@@ -212,25 +210,32 @@ pub(crate) struct NameResolution<'a> { | |||||
/// Single imports that may define the name in the namespace. | ||||||
/// Imports are arena-allocated, so it's ok to use pointers as keys. | ||||||
pub single_imports: FxHashSet<Interned<'a, Import<'a>>>, | ||||||
/// The least shadowable known binding for this name, or None if there are no known bindings. | ||||||
pub binding: Option<&'a NameBinding<'a>>, | ||||||
pub shadowed_glob: Option<&'a NameBinding<'a>>, | ||||||
non_glob_binding: Option<&'a NameBinding<'a>>, | ||||||
glob_binding: Option<&'a NameBinding<'a>>, | ||||||
} | ||||||
|
||||||
impl<'a> NameResolution<'a> { | ||||||
/// Returns the binding for the name if it is known or None if it not known. | ||||||
pub(crate) fn binding(&self) -> Option<&'a NameBinding<'a>> { | ||||||
self.binding.and_then(|binding| { | ||||||
if !binding.is_glob_import() || self.single_imports.is_empty() { | ||||||
Some(binding) | ||||||
} else { | ||||||
None | ||||||
} | ||||||
self.non_glob_binding() | ||||||
.or_else(|| if self.single_imports.is_empty() { self.glob_binding() } else { None }) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This logic is likely incorrect and is a source of issues like #56593 (because it doesn't account for macros), but let's keep it as is in this PR. I'd just want to rename it to something more explanatory, like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I think that issue #56593 is interesting and could be the next task to work on. |
||||||
} | ||||||
|
||||||
pub(crate) fn non_glob_binding(&self) -> Option<&'a NameBinding<'a>> { | ||||||
self.non_glob_binding.and_then(|binding| { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||
assert!(!binding.is_glob_import()); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure these asserts are useful, there's literally one place where these fields are assigned and the glob-ness is checked there. |
||||||
Some(binding) | ||||||
}) | ||||||
} | ||||||
|
||||||
pub(crate) fn glob_binding(&self) -> Option<&'a NameBinding<'a>> { | ||||||
self.glob_binding.and_then(|binding| { | ||||||
assert!(binding.is_glob_import()); | ||||||
Some(binding) | ||||||
}) | ||||||
} | ||||||
|
||||||
pub(crate) fn add_single_import(&mut self, import: &'a Import<'a>) { | ||||||
self.single_imports.insert(Interned::new_unchecked(import)); | ||||||
pub(crate) fn available_binding(&self) -> Option<&'a NameBinding<'a>> { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Assuming the other method is renamed to |
||||||
self.non_glob_binding().or(self.glob_binding()) | ||||||
} | ||||||
} | ||||||
|
||||||
|
@@ -305,65 +310,41 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { | |||||
self.check_reserved_macro_name(key.ident, res); | ||||||
self.set_binding_parent_module(binding, module); | ||||||
self.update_resolution(module, key, |this, resolution| { | ||||||
if let Some(old_binding) = resolution.binding { | ||||||
if res == Res::Err && old_binding.res() != Res::Err { | ||||||
// Do not override real bindings with `Res::Err`s from error recovery. | ||||||
return Ok(()); | ||||||
} | ||||||
match (old_binding.is_glob_import(), binding.is_glob_import()) { | ||||||
(true, true) => { | ||||||
if res != old_binding.res() { | ||||||
resolution.binding = Some(this.ambiguity( | ||||||
AmbiguityKind::GlobVsGlob, | ||||||
old_binding, | ||||||
binding, | ||||||
)); | ||||||
} else if !old_binding.vis.is_at_least(binding.vis, this.tcx) { | ||||||
// We are glob-importing the same item but with greater visibility. | ||||||
resolution.binding = Some(binding); | ||||||
} | ||||||
} | ||||||
(old_glob @ true, false) | (old_glob @ false, true) => { | ||||||
let (glob_binding, nonglob_binding) = | ||||||
if old_glob { (old_binding, binding) } else { (binding, old_binding) }; | ||||||
if glob_binding.res() != nonglob_binding.res() | ||||||
&& key.ns == MacroNS | ||||||
&& nonglob_binding.expansion != LocalExpnId::ROOT | ||||||
{ | ||||||
resolution.binding = Some(this.ambiguity( | ||||||
AmbiguityKind::GlobVsExpanded, | ||||||
nonglob_binding, | ||||||
glob_binding, | ||||||
)); | ||||||
} else { | ||||||
resolution.binding = Some(nonglob_binding); | ||||||
} | ||||||
|
||||||
if let Some(old_binding) = resolution.shadowed_glob { | ||||||
assert!(old_binding.is_glob_import()); | ||||||
if glob_binding.res() != old_binding.res() { | ||||||
resolution.shadowed_glob = Some(this.ambiguity( | ||||||
AmbiguityKind::GlobVsGlob, | ||||||
old_binding, | ||||||
glob_binding, | ||||||
)); | ||||||
} else if !old_binding.vis.is_at_least(binding.vis, this.tcx) { | ||||||
resolution.shadowed_glob = Some(glob_binding); | ||||||
} | ||||||
} else { | ||||||
resolution.shadowed_glob = Some(glob_binding); | ||||||
} | ||||||
} | ||||||
(false, false) => { | ||||||
return Err(old_binding); | ||||||
} | ||||||
if let Some(old_binding) = resolution.available_binding() && res == Res::Err && old_binding.res() != Res::Err { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you keep the old code structure here when possible? |
||||||
// Do not override real bindings with `Res::Err`s from error recovery. | ||||||
Ok(()) | ||||||
} else if binding.is_glob_import() { | ||||||
if let Some(old_binding) = resolution.glob_binding() { | ||||||
if binding.res() != old_binding.res() { | ||||||
resolution.glob_binding = Some(this.ambiguity( | ||||||
AmbiguityKind::GlobVsGlob, | ||||||
old_binding, | ||||||
binding, | ||||||
)); | ||||||
} else if !old_binding.vis.is_at_least(binding.vis, this.tcx) { | ||||||
resolution.glob_binding = Some(binding); | ||||||
} | ||||||
} else { | ||||||
resolution.binding = Some(binding); | ||||||
resolution.glob_binding = Some(binding); | ||||||
} | ||||||
|
||||||
if let Some(old_binding) = resolution.non_glob_binding() { | ||||||
if binding.res() != old_binding.res() && key.ns == MacroNS && old_binding.expansion != LocalExpnId::ROOT { | ||||||
resolution.non_glob_binding = Some(this.ambiguity( | ||||||
AmbiguityKind::GlobVsExpanded, | ||||||
old_binding, | ||||||
binding, | ||||||
)); | ||||||
} | ||||||
} | ||||||
Ok(()) | ||||||
}) | ||||||
} else if let Some(old_binding) = resolution.non_glob_binding() { | ||||||
Err(old_binding) | ||||||
} else { | ||||||
resolution.non_glob_binding = Some(binding); | ||||||
Ok(()) | ||||||
} | ||||||
}) | ||||||
} | ||||||
|
||||||
fn ambiguity( | ||||||
|
@@ -549,11 +530,11 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { | |||||
for (key, resolution) in self.resolutions(module).borrow().iter() { | ||||||
let resolution = resolution.borrow(); | ||||||
|
||||||
if let Some(binding) = resolution.binding { | ||||||
if let NameBindingKind::Import { import, .. } = binding.kind | ||||||
&& let Some((amb_binding, _)) = binding.ambiguity | ||||||
&& binding.res() != Res::Err | ||||||
&& exported_ambiguities.contains(&Interned::new_unchecked(binding)) | ||||||
if let Some(glob_binding) = resolution.glob_binding() { | ||||||
if let NameBindingKind::Import { import, .. } = glob_binding.kind | ||||||
&& let Some((amb_binding, _)) = glob_binding.ambiguity | ||||||
&& glob_binding.res() != Res::Err | ||||||
&& exported_ambiguities.contains(&Interned::new_unchecked(glob_binding)) | ||||||
{ | ||||||
self.lint_buffer.buffer_lint_with_diagnostic( | ||||||
AMBIGUOUS_GLOB_REEXPORTS, | ||||||
|
@@ -569,7 +550,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { | |||||
); | ||||||
} | ||||||
|
||||||
if let Some(glob_binding) = resolution.shadowed_glob { | ||||||
if let Some(binding) = resolution.non_glob_binding() { | ||||||
let binding_id = match binding.kind { | ||||||
NameBindingKind::Res(res) => { | ||||||
Some(self.def_id_to_node_id[res.def_id().expect_local()]) | ||||||
|
@@ -769,9 +750,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { | |||||
.emit(); | ||||||
} | ||||||
let key = BindingKey::new(target, ns); | ||||||
this.update_resolution(parent, key, |_, resolution| { | ||||||
resolution.single_imports.remove(&Interned::new_unchecked(import)); | ||||||
}); | ||||||
let mut resolution = this.resolution(parent, key).borrow_mut(); | ||||||
resolution.single_imports.remove(&Interned::new_unchecked(import)); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is a cleanup, because it seems will never meet other logic in match resolution.binding() {
_ if old_binding.is_some() => return t,
None => return t,
// xxxxxx
} Of course, It should revert it back because it is irrelevant. |
||||||
} | ||||||
} | ||||||
} | ||||||
|
@@ -1025,29 +1005,25 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { | |||||
let resolutions = resolutions.as_ref().into_iter().flat_map(|r| r.iter()); | ||||||
let names = resolutions | ||||||
.filter_map(|(BindingKey { ident: i, .. }, resolution)| { | ||||||
let resolution = resolution.borrow(); | ||||||
if i.name == ident.name { | ||||||
return None; | ||||||
} // Never suggest the same name | ||||||
match *resolution.borrow() { | ||||||
NameResolution { binding: Some(name_binding), .. } => { | ||||||
match name_binding.kind { | ||||||
NameBindingKind::Import { binding, .. } => { | ||||||
match binding.kind { | ||||||
// Never suggest the name that has binding error | ||||||
// i.e., the name that cannot be previously resolved | ||||||
NameBindingKind::Res(Res::Err) => None, | ||||||
_ => Some(i.name), | ||||||
} | ||||||
None | ||||||
} else if let Some(name_binding) = resolution.available_binding() { | ||||||
match name_binding.kind { | ||||||
NameBindingKind::Import { binding, .. } => { | ||||||
match binding.kind { | ||||||
// Never suggest the name that has binding error | ||||||
// i.e., the name that cannot be previously resolved | ||||||
NameBindingKind::Res(Res::Err) => None, | ||||||
_ => Some(i.name), | ||||||
} | ||||||
_ => Some(i.name), | ||||||
} | ||||||
_ => Some(i.name), | ||||||
} | ||||||
NameResolution { ref single_imports, .. } | ||||||
if single_imports.is_empty() => | ||||||
{ | ||||||
None | ||||||
} | ||||||
_ => Some(i.name), | ||||||
} else if resolution.single_imports.is_empty() { | ||||||
None | ||||||
} else { | ||||||
Some(i.name) | ||||||
} | ||||||
}) | ||||||
.collect::<Vec<Symbol>>(); | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
// check-pass | ||
|
||
mod a { | ||
pub trait P {} | ||
} | ||
pub use a::*; | ||
|
||
mod b { | ||
#[derive(Clone)] | ||
pub enum P { | ||
A | ||
} | ||
} | ||
pub use b::P; | ||
|
||
mod c { | ||
use crate::*; | ||
pub struct _S(Vec<P>); | ||
} | ||
|
||
fn main() {} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there some specific issue for this test? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: unrelated change, could be moved to a separate cleanup commit.