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:
dianne 2025-05-07 02:59:18 -07:00
parent 3007433e2c
commit 30a0ac66db

View file

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