delay introducing pattern bindings into scope
This splits introduction of bindings into scope (`apply_pattern_bindings`) apart from manipulation of the pattern's binding map (`fresh_binding`). By delaying the latter, we can keep bindings from appearing in-scope in guards. Since `fresh_binding` is now specifically for manipulating a pattern's bindings map, this commit also inlines a use of `fresh_binding` that was only adding to the innermost rib.
This commit is contained in:
parent
3007433e2c
commit
30a0ac66db
1 changed files with 52 additions and 30 deletions
|
|
@ -120,7 +120,9 @@ enum PatBoundCtx {
|
|||
/// - The guard expression of a guard pattern may use bindings from within the guard pattern, but
|
||||
/// not from elsewhere in the pattern containing it. This allows us to isolate the bindings in the
|
||||
/// subpattern to construct the scope for the guard.
|
||||
type PatternBindings = SmallVec<[(PatBoundCtx, FxHashSet<Ident>); 1]>;
|
||||
///
|
||||
/// Each identifier must map to at most one distinct [`Res`].
|
||||
type PatternBindings = SmallVec<[(PatBoundCtx, FxIndexMap<Ident, Res>); 1]>;
|
||||
|
||||
/// Does this the item (from the item rib scope) allow generic parameters?
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
|
|
@ -2308,7 +2310,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
|
|||
fn resolve_fn_params(
|
||||
&mut self,
|
||||
has_self: bool,
|
||||
inputs: impl Iterator<Item = (Option<&'ast Pat>, &'ast Ty)>,
|
||||
inputs: impl Iterator<Item = (Option<&'ast Pat>, &'ast Ty)> + Clone,
|
||||
) -> Result<LifetimeRes, (Vec<MissingLifetime>, Vec<ElisionFnParameter>)> {
|
||||
enum Elision {
|
||||
/// We have not found any candidate.
|
||||
|
|
@ -2330,15 +2332,20 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
|
|||
let mut parameter_info = Vec::new();
|
||||
let mut all_candidates = Vec::new();
|
||||
|
||||
// Resolve and apply bindings first so diagnostics can see if they're used in types.
|
||||
let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())];
|
||||
for (index, (pat, ty)) in inputs.enumerate() {
|
||||
debug!(?pat, ?ty);
|
||||
for (pat, _) in inputs.clone() {
|
||||
debug!("resolving bindings in pat = {pat:?}");
|
||||
self.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| {
|
||||
if let Some(pat) = pat {
|
||||
this.resolve_pattern(pat, PatternSource::FnParam, &mut bindings);
|
||||
}
|
||||
});
|
||||
}
|
||||
self.apply_pattern_bindings(bindings);
|
||||
|
||||
for (index, (pat, ty)) in inputs.enumerate() {
|
||||
debug!("resolving type for pat = {pat:?}, ty = {ty:?}");
|
||||
// Record elision candidates only for this parameter.
|
||||
debug_assert_matches!(self.lifetime_elision_candidates, None);
|
||||
self.lifetime_elision_candidates = Some(Default::default());
|
||||
|
|
@ -3626,16 +3633,10 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
|
|||
self.visit_path(&delegation.path, delegation.id);
|
||||
let Some(body) = &delegation.body else { return };
|
||||
self.with_rib(ValueNS, RibKind::FnOrCoroutine, |this| {
|
||||
// `PatBoundCtx` is not necessary in this context
|
||||
let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())];
|
||||
|
||||
let span = delegation.path.segments.last().unwrap().ident.span;
|
||||
this.fresh_binding(
|
||||
Ident::new(kw::SelfLower, span),
|
||||
delegation.id,
|
||||
PatternSource::FnParam,
|
||||
&mut bindings,
|
||||
);
|
||||
let ident = Ident::new(kw::SelfLower, span.normalize_to_macro_rules());
|
||||
let res = Res::Local(delegation.id);
|
||||
this.innermost_rib_bindings(ValueNS).insert(ident, res);
|
||||
this.visit_block(body);
|
||||
});
|
||||
}
|
||||
|
|
@ -3646,6 +3647,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
|
|||
for Param { pat, .. } in params {
|
||||
this.resolve_pattern(pat, PatternSource::FnParam, &mut bindings);
|
||||
}
|
||||
this.apply_pattern_bindings(bindings);
|
||||
});
|
||||
for Param { ty, .. } in params {
|
||||
self.visit_ty(ty);
|
||||
|
|
@ -3862,8 +3864,27 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
|
|||
fn resolve_pattern_top(&mut self, pat: &'ast Pat, pat_src: PatternSource) {
|
||||
let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())];
|
||||
self.resolve_pattern(pat, pat_src, &mut bindings);
|
||||
self.apply_pattern_bindings(bindings);
|
||||
}
|
||||
|
||||
/// Apply the bindings from a pattern to the innermost rib of the current scope.
|
||||
fn apply_pattern_bindings(&mut self, mut pat_bindings: PatternBindings) {
|
||||
let rib_bindings = self.innermost_rib_bindings(ValueNS);
|
||||
let Some((_, pat_bindings)) = pat_bindings.pop() else {
|
||||
bug!("tried applying nonexistent bindings from pattern");
|
||||
};
|
||||
|
||||
if rib_bindings.is_empty() {
|
||||
// Often, such as for match arms, the bindings are introduced into a new rib.
|
||||
// In this case, we can move the bindings over directly.
|
||||
*rib_bindings = pat_bindings;
|
||||
} else {
|
||||
rib_bindings.extend(pat_bindings);
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolve bindings in a pattern. `apply_pattern_bindings` must be called after to introduce
|
||||
/// the bindings into scope.
|
||||
fn resolve_pattern(
|
||||
&mut self,
|
||||
pat: &'ast Pat,
|
||||
|
|
@ -4001,18 +4022,15 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
|
|||
pat_src: PatternSource,
|
||||
bindings: &mut PatternBindings,
|
||||
) -> Res {
|
||||
// Add the binding to the local ribs, if it doesn't already exist in the bindings map.
|
||||
// Add the binding to the bindings map, if it doesn't already exist.
|
||||
// (We must not add it if it's in the bindings map because that breaks the assumptions
|
||||
// later passes make about or-patterns.)
|
||||
let ident = ident.normalize_to_macro_rules();
|
||||
|
||||
let mut bound_iter = bindings.iter().filter(|(_, set)| set.contains(&ident));
|
||||
// Already bound in a product pattern? e.g. `(a, a)` which is not allowed.
|
||||
let already_bound_and = bound_iter.clone().any(|(ctx, _)| *ctx == PatBoundCtx::Product);
|
||||
// Already bound in an or-pattern? e.g. `V1(a) | V2(a)`.
|
||||
// This is *required* for consistency which is checked later.
|
||||
let already_bound_or = bound_iter.any(|(ctx, _)| *ctx == PatBoundCtx::Or);
|
||||
|
||||
let already_bound_and = bindings
|
||||
.iter()
|
||||
.any(|(ctx, map)| *ctx == PatBoundCtx::Product && map.contains_key(&ident));
|
||||
if already_bound_and {
|
||||
// Overlap in a product pattern somewhere; report an error.
|
||||
use ResolutionError::*;
|
||||
|
|
@ -4025,19 +4043,23 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
|
|||
self.report_error(ident.span, error(ident));
|
||||
}
|
||||
|
||||
// Record as bound.
|
||||
bindings.last_mut().unwrap().1.insert(ident);
|
||||
|
||||
if already_bound_or {
|
||||
// Already bound in an or-pattern? e.g. `V1(a) | V2(a)`.
|
||||
// This is *required* for consistency which is checked later.
|
||||
let already_bound_or = bindings
|
||||
.iter()
|
||||
.find_map(|(ctx, map)| if *ctx == PatBoundCtx::Or { map.get(&ident) } else { None });
|
||||
let res = if let Some(&res) = already_bound_or {
|
||||
// `Variant1(a) | Variant2(a)`, ok
|
||||
// Reuse definition from the first `a`.
|
||||
self.innermost_rib_bindings(ValueNS)[&ident]
|
||||
} else {
|
||||
// A completely fresh binding is added to the set.
|
||||
let res = Res::Local(pat_id);
|
||||
self.innermost_rib_bindings(ValueNS).insert(ident, res);
|
||||
res
|
||||
}
|
||||
} else {
|
||||
// A completely fresh binding is added to the map.
|
||||
Res::Local(pat_id)
|
||||
};
|
||||
|
||||
// Record as bound.
|
||||
bindings.last_mut().unwrap().1.insert(ident, res);
|
||||
res
|
||||
}
|
||||
|
||||
fn innermost_rib_bindings(&mut self, ns: Namespace) -> &mut FxIndexMap<Ident, Res> {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue