diff options
| author | dianne <diannes.gm@gmail.com> | 2025-05-07 02:59:18 -0700 | 
|---|---|---|
| committer | dianne <diannes.gm@gmail.com> | 2025-05-07 06:47:16 -0700 | 
| commit | 30a0ac66dbb15ee8dd3951499b34df48e1d758a0 (patch) | |
| tree | a62470aa3d1788ea3bb1d1ec720e41d37083caab /compiler/rustc_resolve/src | |
| parent | 3007433e2c65e289aa02155debf500d836539890 (diff) | |
| download | rust-30a0ac66dbb15ee8dd3951499b34df48e1d758a0.tar.gz rust-30a0ac66dbb15ee8dd3951499b34df48e1d758a0.zip | |
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.
Diffstat (limited to 'compiler/rustc_resolve/src')
| -rw-r--r-- | compiler/rustc_resolve/src/late.rs | 82 | 
1 files changed, 52 insertions, 30 deletions
| diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 1da1bfeaf60..aa211a8f3c2 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -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> { | 
