Auto merge of #89100 - petrochenkov:localbind, r=cjgillot

resolve: Use `NameBinding` for local variables and generic parameters

`NameBinding` is a structure used for representing any name introduction (an item, or import, or even a built-in).
Except that local variables and generic parameters weren't represented as `NameBinding`s, for this reason they requires separate paths in name resolution code in several places.

This PR introduces `NameBinding`s for local variables as well and simplifies all the code working with them leaving only the `NameBinding` paths.
This commit is contained in:
bors 2021-10-20 07:21:01 +00:00
commit 6162529a01
3 changed files with 147 additions and 215 deletions

View file

@ -7,7 +7,7 @@
use RibKind::*;
use crate::{path_names_to_string, BindingError, CrateLint, LexicalScopeBinding};
use crate::{path_names_to_string, BindingError, CrateLint, NameBinding, ToNameBinding};
use crate::{Module, ModuleOrUniformRoot, ParentScope, PathResult};
use crate::{ResolutionError, Resolver, Segment, UseError};
@ -21,27 +21,22 @@
use rustc_hir::def::{self, CtorKind, DefKind, PartialRes, PerNS};
use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX};
use rustc_hir::{PrimTy, TraitCandidate};
use rustc_middle::{bug, span_bug};
use rustc_middle::{bug, span_bug, ty};
use rustc_session::lint;
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::Span;
use smallvec::{smallvec, SmallVec};
use rustc_span::source_map::{respan, Spanned};
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::{Span, DUMMY_SP};
use smallvec::{smallvec, SmallVec};
use tracing::debug;
use std::collections::{hash_map::Entry, BTreeSet};
use std::mem::{replace, take};
use tracing::debug;
mod diagnostics;
crate mod lifetimes;
type Res = def::Res<NodeId>;
type IdentMap<T> = FxHashMap<Ident, T>;
/// Map from the name in a pattern to its binding mode.
type BindingMap = IdentMap<BindingInfo>;
#[derive(Copy, Clone, Debug)]
struct BindingInfo {
span: Span,
@ -172,8 +167,8 @@ impl RibKind<'_> {
/// The resolution keeps a separate stack of ribs as it traverses the AST for each namespace. When
/// resolving, the name is looked up from inside out.
#[derive(Debug)]
crate struct Rib<'a, R = Res> {
pub bindings: IdentMap<R>,
crate struct Rib<'a, R = &'a NameBinding<'a>> {
pub bindings: FxHashMap<Ident, R>,
pub kind: RibKind<'a>,
}
@ -567,12 +562,12 @@ fn visit_generics(&mut self, generics: &'ast Generics) {
GenericParamKind::Type { .. } => {
forward_ty_ban_rib
.bindings
.insert(Ident::with_dummy_span(param.ident.name), Res::Err);
.insert(Ident::with_dummy_span(param.ident.name), self.r.dummy_binding);
}
GenericParamKind::Const { .. } => {
forward_const_ban_rib
.bindings
.insert(Ident::with_dummy_span(param.ident.name), Res::Err);
.insert(Ident::with_dummy_span(param.ident.name), self.r.dummy_binding);
}
GenericParamKind::Lifetime => {}
}
@ -589,7 +584,9 @@ fn visit_generics(&mut self, generics: &'ast Generics) {
// such as in the case of `trait Add<Rhs = Self>`.)
if self.diagnostic_metadata.current_self_item.is_some() {
// (`Some` if + only if we are in ADT's generics.)
forward_ty_ban_rib.bindings.insert(Ident::with_dummy_span(kw::SelfUpper), Res::Err);
forward_ty_ban_rib
.bindings
.insert(Ident::with_dummy_span(kw::SelfUpper), self.r.dummy_binding);
}
for param in &generics.params {
@ -737,15 +734,17 @@ fn resolve_ident_in_lexical_scope(
ns: Namespace,
record_used_id: Option<NodeId>,
path_span: Span,
) -> Option<LexicalScopeBinding<'a>> {
self.r.resolve_ident_in_lexical_scope(
ident,
ns,
&self.parent_scope,
record_used_id,
path_span,
&self.ribs[ns],
)
) -> Option<&'a NameBinding<'a>> {
self.r
.resolve_ident_in_lexical_scope(
ident,
ns,
&self.parent_scope,
record_used_id,
path_span,
&self.ribs[ns],
)
.ok()
}
fn resolve_path(
@ -903,6 +902,10 @@ fn resolve_adt(&mut self, item: &'ast Item, generics: &'ast Generics) {
}
fn future_proof_import(&mut self, use_tree: &UseTree) {
if !self.should_report_errs() {
return;
}
let segments = &use_tree.prefix.segments;
if !segments.is_empty() {
let ident = segments[0].ident;
@ -914,31 +917,42 @@ fn future_proof_import(&mut self, use_tree: &UseTree) {
UseTreeKind::Simple(..) if segments.len() == 1 => &[TypeNS, ValueNS][..],
_ => &[TypeNS],
};
let from_ribs = |binding: &NameBinding<'_>| {
matches!(
binding.res(),
Res::Local(..)
| Res::SelfTy(..)
| Res::Def(DefKind::TyParam | DefKind::ConstParam, ..)
)
};
let report_error = |this: &Self, ns| {
let what = if ns == TypeNS { "type parameters" } else { "local variables" };
if this.should_report_errs() {
this.r
.session
.span_err(ident.span, &format!("imports cannot refer to {}", what));
}
let msg = format!("imports cannot refer to {what}");
this.r.session.span_err(ident.span, &msg);
};
for &ns in nss {
match self.resolve_ident_in_lexical_scope(ident, ns, None, use_tree.prefix.span) {
Some(LexicalScopeBinding::Res(..)) => {
if let Some(binding) =
self.resolve_ident_in_lexical_scope(ident, ns, None, use_tree.prefix.span)
{
if from_ribs(binding) {
report_error(self, ns);
}
Some(LexicalScopeBinding::Item(binding)) => {
} else {
let orig_unusable_binding =
replace(&mut self.r.unusable_binding, Some(binding));
if let Some(LexicalScopeBinding::Res(..)) = self
.resolve_ident_in_lexical_scope(ident, ns, None, use_tree.prefix.span)
{
report_error(self, ns);
if let Some(binding) = self.resolve_ident_in_lexical_scope(
ident,
ns,
None,
use_tree.prefix.span,
) {
if from_ribs(binding) {
report_error(self, ns);
}
}
self.r.unusable_binding = orig_unusable_binding;
}
None => {}
}
}
} else if let UseTreeKind::Nested(use_trees) = &use_tree.kind {
@ -1135,8 +1149,12 @@ fn with_generic_param_rib<'c, F>(&'c mut self, generics: &'c Generics, kind: Rib
_ => unreachable!(),
};
let res = Res::Def(def_kind, self.r.local_def_id(param.id).to_def_id());
let binding =
(res, ty::Visibility::Invisible, param.ident.span, self.parent_scope.expansion)
.to_name_binding(self.r.arenas);
self.r.record_partial_res(param.id, PartialRes::new(res));
rib.bindings.insert(ident, res);
rib.bindings.insert(ident, binding);
}
self.ribs[ValueNS].push(function_value_rib);
@ -1256,10 +1274,12 @@ fn with_optional_trait_ref<T>(
}
fn with_self_rib_ns(&mut self, ns: Namespace, self_res: Res, f: impl FnOnce(&mut Self)) {
let binding = (self_res, ty::Visibility::Invisible, DUMMY_SP, self.parent_scope.expansion)
.to_name_binding(self.r.arenas);
let mut self_type_rib = Rib::new(NormalRibKind);
// Plain insert (no renaming, since types are not currently hygienic)
self_type_rib.bindings.insert(Ident::with_dummy_span(kw::SelfUpper), self_res);
self_type_rib.bindings.insert(Ident::with_dummy_span(kw::SelfUpper), binding);
self.ribs[ns].push(self_type_rib);
f(self);
self.ribs[ns].pop();
@ -1470,7 +1490,7 @@ fn resolve_local(&mut self, local: &'ast Local) {
/// this is done hygienically. This could arise for a macro
/// that expands into an or-pattern where one 'x' was from the
/// user and one 'x' came from the macro.
fn binding_mode_map(&mut self, pat: &Pat) -> BindingMap {
fn binding_mode_map(&mut self, pat: &Pat) -> FxHashMap<Ident, BindingInfo> {
let mut binding_map = FxHashMap::default();
pat.walk(&mut |pat| {
@ -1503,7 +1523,7 @@ fn is_base_res_local(&self, nid: NodeId) -> bool {
/// Checks that all of the arms in an or-pattern have exactly the
/// same set of bindings, with the same binding modes for each.
fn check_consistent_bindings(&mut self, pats: &[P<Pat>]) -> Vec<BindingMap> {
fn check_consistent_bindings(&mut self, pats: &[P<Pat>]) -> Vec<FxHashMap<Ident, BindingInfo>> {
let mut missing_vars = FxHashMap::default();
let mut inconsistent_vars = FxHashMap::default();
@ -1645,7 +1665,6 @@ fn resolve_pattern_inner(
.try_resolve_as_non_binding(pat_src, pat, bmode, ident, has_sub)
.unwrap_or_else(|| self.fresh_binding(ident, pat.id, pat_src, bindings));
self.r.record_partial_res(pat.id, PartialRes::new(res));
self.r.record_pat_span(pat.id, pat.span);
}
PatKind::TupleStruct(ref qself, ref path, ref sub_patterns) => {
self.smart_resolve_path(
@ -1735,18 +1754,24 @@ fn fresh_binding(
if already_bound_or {
// `Variant1(a) | Variant2(a)`, ok
// Reuse definition from the first `a`.
self.innermost_rib_bindings(ValueNS)[&ident]
self.innermost_rib_bindings(ValueNS)[&ident].res()
} else {
let res = Res::Local(pat_id);
if ident_valid {
// A completely fresh binding add to the set if it's valid.
self.innermost_rib_bindings(ValueNS).insert(ident, res);
let binding =
(res, ty::Visibility::Invisible, ident.span, self.parent_scope.expansion)
.to_name_binding(self.r.arenas);
self.innermost_rib_bindings(ValueNS).insert(ident, binding);
}
res
}
}
fn innermost_rib_bindings(&mut self, ns: Namespace) -> &mut IdentMap<Res> {
fn innermost_rib_bindings(
&mut self,
ns: Namespace,
) -> &mut FxHashMap<Ident, &'a NameBinding<'a>> {
&mut self.ribs[ns].last_mut().unwrap().bindings
}
@ -1763,22 +1788,17 @@ fn try_resolve_as_non_binding(
// also be interpreted as a path to e.g. a constant, variant, etc.
let is_syntactic_ambiguity = !has_sub && bm == BindingMode::ByValue(Mutability::Not);
let ls_binding = self.resolve_ident_in_lexical_scope(ident, ValueNS, None, pat.span)?;
let (res, binding) = match ls_binding {
LexicalScopeBinding::Item(binding)
if is_syntactic_ambiguity && binding.is_ambiguity() =>
{
// For ambiguous bindings we don't know all their definitions and cannot check
// whether they can be shadowed by fresh bindings or not, so force an error.
// issues/33118#issuecomment-233962221 (see below) still applies here,
// but we have to ignore it for backward compatibility.
self.r.record_use(ident, binding, false);
return None;
}
LexicalScopeBinding::Item(binding) => (binding.res(), Some(binding)),
LexicalScopeBinding::Res(res) => (res, None),
};
let binding = self.resolve_ident_in_lexical_scope(ident, ValueNS, None, pat.span)?;
if is_syntactic_ambiguity && binding.is_ambiguity() {
// For ambiguous bindings we don't know all their definitions and cannot check
// whether they can be shadowed by fresh bindings or not, so force an error.
// issues/33118#issuecomment-233962221 (see below) still applies here,
// but we have to ignore it for backward compatibility.
self.r.record_use(ident, binding, false);
return None;
}
let res = binding.res();
match res {
Res::SelfCtor(_) // See #70549.
| Res::Def(
@ -1786,9 +1806,7 @@ fn try_resolve_as_non_binding(
_,
) if is_syntactic_ambiguity => {
// Disambiguate in favor of a unit struct/variant or constant pattern.
if let Some(binding) = binding {
self.r.record_use(ident, binding, false);
}
self.r.record_use(ident, binding, false);
Some(res)
}
Res::Def(DefKind::Ctor(..) | DefKind::Const | DefKind::Static, _) => {
@ -1797,7 +1815,6 @@ fn try_resolve_as_non_binding(
// to something unusable as a pattern (e.g., constructor function),
// but we still conservatively report an error, see
// issues/33118#issuecomment-233962221 for one reason why.
let binding = binding.expect("no binding for a ctor or static");
self.report_error(
ident.span,
ResolutionError::BindingShadowsSomethingUnacceptable {
@ -2037,19 +2054,15 @@ fn smart_resolve_path_fragment(
}
fn self_type_is_available(&mut self, span: Span) -> bool {
let binding = self.resolve_ident_in_lexical_scope(
Ident::with_dummy_span(kw::SelfUpper),
TypeNS,
None,
span,
);
if let Some(LexicalScopeBinding::Res(res)) = binding { res != Res::Err } else { false }
let ident = Ident::with_dummy_span(kw::SelfUpper);
self.resolve_ident_in_lexical_scope(ident, TypeNS, None, span)
.map_or(false, |binding| binding.res() != Res::Err)
}
fn self_value_is_available(&mut self, self_span: Span, path_span: Span) -> bool {
let ident = Ident::new(kw::SelfLower, self_span);
let binding = self.resolve_ident_in_lexical_scope(ident, ValueNS, None, path_span);
if let Some(LexicalScopeBinding::Res(res)) = binding { res != Res::Err } else { false }
self.resolve_ident_in_lexical_scope(ident, ValueNS, None, path_span)
.map_or(false, |binding| binding.res() != Res::Err)
}
/// A wrapper around [`Resolver::report_error`].

View file

@ -1294,7 +1294,8 @@ fn lookup_typo_candidate(
// Walk backwards up the ribs in scope and collect candidates.
for rib in self.ribs[ns].iter().rev() {
// Locals and type parameters
for (ident, &res) in &rib.bindings {
for (ident, binding) in &rib.bindings {
let res = binding.res();
if filter_fn(res) {
names.push(TypoSuggestion::typo_from_res(ident.name, res));
}

View file

@ -373,26 +373,6 @@ fn visit_item(&mut self, item: &'tcx ast::Item) {
}
}
/// An intermediate resolution result.
///
/// This refers to the thing referred by a name. The difference between `Res` and `Item` is that
/// items are visible in their whole block, while `Res`es only from the place they are defined
/// forward.
#[derive(Debug)]
enum LexicalScopeBinding<'a> {
Item(&'a NameBinding<'a>),
Res(Res),
}
impl<'a> LexicalScopeBinding<'a> {
fn res(self) -> Res {
match self {
LexicalScopeBinding::Item(binding) => binding.res(),
LexicalScopeBinding::Res(res) => res,
}
}
}
#[derive(Copy, Clone, Debug)]
enum ModuleOrUniformRoot<'a> {
/// Regular module.
@ -918,10 +898,6 @@ pub struct Resolver<'a> {
/// "self-confirming" import resolutions during import validation.
unusable_binding: Option<&'a NameBinding<'a>>,
// Spans for local variables found during pattern resolution.
// Used for suggestions during error reporting.
pat_span_map: NodeMap<Span>,
/// Resolutions for nodes that have a single resolution.
partial_res_map: NodeMap<PartialRes>,
/// Resolutions for import nodes, which have multiple resolutions in different namespaces.
@ -1332,7 +1308,6 @@ pub fn new(
last_import_segment: false,
unusable_binding: None,
pat_span_map: Default::default(),
partial_res_map: Default::default(),
import_res_map: Default::default(),
label_res_map: Default::default(),
@ -1358,13 +1333,8 @@ pub fn new(
macro_expanded_macro_export_errors: BTreeSet::new(),
arenas,
dummy_binding: arenas.alloc_name_binding(NameBinding {
kind: NameBindingKind::Res(Res::Err, false),
ambiguity: None,
expansion: LocalExpnId::ROOT,
span: DUMMY_SP,
vis: ty::Visibility::Public,
}),
dummy_binding: (Res::Err, ty::Visibility::Public, DUMMY_SP, LocalExpnId::ROOT)
.to_name_binding(arenas),
crate_loader: CrateLoader::new(session, metadata_loader, crate_name),
macro_names: FxHashSet::default(),
@ -1921,11 +1891,11 @@ fn resolve_ident_in_lexical_scope(
record_used_id: Option<NodeId>,
path_span: Span,
ribs: &[Rib<'a>],
) -> Option<LexicalScopeBinding<'a>> {
) -> Result<&'a NameBinding<'a>, Determinacy> {
assert!(ns == TypeNS || ns == ValueNS);
let orig_ident = ident;
if ident.name == kw::Empty {
return Some(LexicalScopeBinding::Res(Res::Err));
return Ok(self.dummy_binding);
}
let (general_span, normalized_span) = if ident.name == kw::SelfUpper {
// FIXME(jseyfried) improve `Self` hygiene
@ -1948,18 +1918,30 @@ fn resolve_ident_in_lexical_scope(
// Use the rib kind to determine whether we are resolving parameters
// (macro 2.0 hygiene) or local variables (`macro_rules` hygiene).
let rib_ident = if ribs[i].kind.contains_params() { normalized_ident } else { ident };
if let Some((original_rib_ident_def, res)) = ribs[i].bindings.get_key_value(&rib_ident)
if let Some((&original_rib_ident_def, &binding)) =
ribs[i].bindings.get_key_value(&rib_ident)
{
// The ident resolves to a type parameter or local variable.
return Some(LexicalScopeBinding::Res(self.validate_res_from_ribs(
let res = self.validate_res_from_ribs(
i,
rib_ident,
*res,
binding.res(),
record_used,
path_span,
*original_rib_ident_def,
original_rib_ident_def,
ribs,
)));
);
// We have to create a new binding in case of validation errors,
// or in case of const generic hack changing the resolution.
return Ok(if res != binding.res() {
self.arenas.alloc_name_binding(NameBinding {
kind: NameBindingKind::Res(res, false),
..binding.clone()
})
} else {
binding
});
}
module = match ribs[i].kind {
@ -1978,7 +1960,7 @@ fn resolve_ident_in_lexical_scope(
_ => break,
}
let item = self.resolve_ident_in_module_unadjusted(
let binding = self.resolve_ident_in_module_unadjusted(
ModuleOrUniformRoot::Module(module),
ident,
ns,
@ -1986,9 +1968,9 @@ fn resolve_ident_in_lexical_scope(
record_used,
path_span,
);
if let Ok(binding) = item {
if binding.is_ok() {
// The ident resolves to an item.
return Some(LexicalScopeBinding::Item(binding));
return binding;
}
}
self.early_resolve_ident_in_lexical_scope(
@ -1999,8 +1981,6 @@ fn resolve_ident_in_lexical_scope(
record_used,
path_span,
)
.ok()
.map(LexicalScopeBinding::Item)
}
fn hygienic_lexical_parent(
@ -2225,16 +2205,6 @@ fn resolve_path_with_ribs(
for (i, &Segment { ident, id, has_generic_args: _ }) in path.iter().enumerate() {
debug!("resolve_path ident {} {:?} {:?}", i, ident, id);
let record_segment_res = |this: &mut Self, res| {
if record_used {
if let Some(id) = id {
if !this.partial_res_map.contains_key(&id) {
assert!(id != ast::DUMMY_NODE_ID, "Trying to resolve dummy id");
this.record_partial_res(id, PartialRes::new(res));
}
}
}
};
let is_last = i == path.len() - 1;
let ns = if is_last { opt_ns.unwrap_or(TypeNS) } else { TypeNS };
@ -2313,12 +2283,8 @@ fn resolve_path_with_ribs(
};
}
enum FindBindingResult<'a> {
Binding(Result<&'a NameBinding<'a>, Determinacy>),
PathResult(PathResult<'a>),
}
let find_binding_in_ns = |this: &mut Self, ns| {
let binding = if let Some(module) = module {
if let Some(module) = module {
this.resolve_ident_in_module(
module,
ident,
@ -2343,44 +2309,34 @@ enum FindBindingResult<'a> {
} else {
None
};
match this.resolve_ident_in_lexical_scope(
this.resolve_ident_in_lexical_scope(
ident,
ns,
parent_scope,
record_used_id,
path_span,
&ribs.unwrap()[ns],
) {
// we found a locally-imported or available item/module
Some(LexicalScopeBinding::Item(binding)) => Ok(binding),
// we found a local variable or type param
Some(LexicalScopeBinding::Res(res))
if opt_ns == Some(TypeNS) || opt_ns == Some(ValueNS) =>
{
record_segment_res(this, res);
return FindBindingResult::PathResult(PathResult::NonModule(
PartialRes::with_unresolved_segments(res, path.len() - 1),
));
}
_ => Err(Determinacy::determined(record_used)),
}
};
FindBindingResult::Binding(binding)
)
}
};
let binding = match find_binding_in_ns(self, ns) {
FindBindingResult::PathResult(x) => return x,
FindBindingResult::Binding(binding) => binding,
};
match binding {
match find_binding_in_ns(self, ns) {
Ok(binding) => {
if i == 1 {
second_binding = Some(binding);
}
let res = binding.res();
if record_used {
if let Some(id) = id {
if !self.partial_res_map.contains_key(&id) {
assert!(id != ast::DUMMY_NODE_ID, "Trying to resolve dummy id");
self.record_partial_res(id, PartialRes::new(res));
}
}
}
let maybe_assoc = opt_ns != Some(MacroNS) && PathSource::Type.is_expected(res);
if let Some(next_module) = binding.module() {
module = Some(ModuleOrUniformRoot::Module(next_module));
record_segment_res(self, res);
} else if res == Res::ToolMod && i + 1 != path.len() {
if binding.is_import() {
self.session
@ -2470,56 +2426,25 @@ enum FindBindingResult<'a> {
.map_or(false, |c| c.is_ascii_uppercase())
{
// Check whether the name refers to an item in the value namespace.
let suggestion = if ribs.is_some() {
let match_span = match self.resolve_ident_in_lexical_scope(
ident,
ValueNS,
parent_scope,
None,
path_span,
&ribs.unwrap()[ValueNS],
) {
// Name matches a local variable. For example:
// ```
// fn f() {
// let Foo: &str = "";
// println!("{}", Foo::Bar); // Name refers to local
// // variable `Foo`.
// }
// ```
Some(LexicalScopeBinding::Res(Res::Local(id))) => {
Some(*self.pat_span_map.get(&id).unwrap())
}
// Name matches item from a local name binding
// created by `use` declaration. For example:
// ```
// pub Foo: &str = "";
//
// mod submod {
// use super::Foo;
// println!("{}", Foo::Bar); // Name refers to local
// // binding `Foo`.
// }
// ```
Some(LexicalScopeBinding::Item(name_binding)) => {
Some(name_binding.span)
}
_ => None,
};
if let Some(span) = match_span {
Some((
vec![(span, String::from(""))],
let suggestion = ribs
.and_then(|ribs| {
self.resolve_ident_in_lexical_scope(
ident,
ValueNS,
parent_scope,
None,
path_span,
&ribs[ValueNS],
)
.ok()
})
.map(|binding| {
(
vec![(binding.span, String::from(""))],
format!("`{}` is defined here, but is not a type", ident),
Applicability::MaybeIncorrect,
))
} else {
None
}
} else {
None
};
)
});
(format!("use of undeclared type `{}`", ident), suggestion)
} else {
@ -2557,9 +2482,7 @@ enum FindBindingResult<'a> {
let mut msg = format!("could not find `{}` in {}", ident, parent);
if ns == TypeNS || ns == ValueNS {
let ns_to_try = if ns == TypeNS { ValueNS } else { TypeNS };
if let FindBindingResult::Binding(Ok(binding)) =
find_binding_in_ns(self, ns_to_try)
{
if let Ok(binding) = find_binding_in_ns(self, ns_to_try) {
let mut found = |what| {
msg = format!(
"expected {}, found {} `{}` in {}",
@ -2901,11 +2824,6 @@ fn record_partial_res(&mut self, node_id: NodeId, resolution: PartialRes) {
}
}
fn record_pat_span(&mut self, node: NodeId, span: Span) {
debug!("(recording pat) recording {:?} for {:?}", node, span);
self.pat_span_map.insert(node, span);
}
fn is_accessible_from(&self, vis: ty::Visibility, module: Module<'a>) -> bool {
vis.is_accessible_from(module.nearest_parent_mod(), self)
}