@@ -111,6 +111,19 @@ enum PatBoundCtx {
111111 Or ,
112112}
113113
114+ /// Tracks bindings resolved within a pattern. This serves two purposes:
115+ ///
116+ /// - This tracks when identifiers are bound multiple times within a pattern. In a product context,
117+ /// this is an error. In an or-pattern, this lets us reuse the same resolution for each instance.
118+ /// See `fresh_binding` and `resolve_pattern_inner` for more information.
119+ ///
120+ /// - The guard expression of a guard pattern may use bindings from within the guard pattern, but
121+ /// not from elsewhere in the pattern containing it. This allows us to isolate the bindings in the
122+ /// subpattern to construct the scope for the guard.
123+ ///
124+ /// Each identifier must map to at most one distinct [`Res`].
125+ type PatternBindings = SmallVec < [ ( PatBoundCtx , FxIndexMap < Ident , Res > ) ; 1 ] > ;
126+
114127/// Does this the item (from the item rib scope) allow generic parameters?
115128#[ derive( Copy , Clone , Debug ) ]
116129pub ( crate ) enum HasGenericParams {
@@ -786,7 +799,14 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r
786799 fn visit_pat ( & mut self , p : & ' ast Pat ) {
787800 let prev = self . diag_metadata . current_pat ;
788801 self . diag_metadata . current_pat = Some ( p) ;
789- visit:: walk_pat ( self , p) ;
802+
803+ if let PatKind :: Guard ( subpat, _) = & p. kind {
804+ // We walk the guard expression in `resolve_pattern_inner`. Don't resolve it twice.
805+ self . visit_pat ( subpat) ;
806+ } else {
807+ visit:: walk_pat ( self , p) ;
808+ }
809+
790810 self . diag_metadata . current_pat = prev;
791811 }
792812 fn visit_local ( & mut self , local : & ' ast Local ) {
@@ -2297,7 +2317,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
22972317 fn resolve_fn_params (
22982318 & mut self ,
22992319 has_self : bool ,
2300- inputs : impl Iterator < Item = ( Option < & ' ast Pat > , & ' ast Ty ) > ,
2320+ inputs : impl Iterator < Item = ( Option < & ' ast Pat > , & ' ast Ty ) > + Clone ,
23012321 ) -> Result < LifetimeRes , ( Vec < MissingLifetime > , Vec < ElisionFnParameter > ) > {
23022322 enum Elision {
23032323 /// We have not found any candidate.
@@ -2319,15 +2339,20 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
23192339 let mut parameter_info = Vec :: new ( ) ;
23202340 let mut all_candidates = Vec :: new ( ) ;
23212341
2342+ // Resolve and apply bindings first so diagnostics can see if they're used in types.
23222343 let mut bindings = smallvec ! [ ( PatBoundCtx :: Product , Default :: default ( ) ) ] ;
2323- for ( index , ( pat, ty ) ) in inputs. enumerate ( ) {
2324- debug ! ( ? pat, ?ty ) ;
2344+ for ( pat, _ ) in inputs. clone ( ) {
2345+ debug ! ( "resolving bindings in pat = {pat:?}" ) ;
23252346 self . with_lifetime_rib ( LifetimeRibKind :: Elided ( LifetimeRes :: Infer ) , |this| {
23262347 if let Some ( pat) = pat {
23272348 this. resolve_pattern ( pat, PatternSource :: FnParam , & mut bindings) ;
23282349 }
23292350 } ) ;
2351+ }
2352+ self . apply_pattern_bindings ( bindings) ;
23302353
2354+ for ( index, ( pat, ty) ) in inputs. enumerate ( ) {
2355+ debug ! ( "resolving type for pat = {pat:?}, ty = {ty:?}" ) ;
23312356 // Record elision candidates only for this parameter.
23322357 debug_assert_matches ! ( self . lifetime_elision_candidates, None ) ;
23332358 self . lifetime_elision_candidates = Some ( Default :: default ( ) ) ;
@@ -3615,16 +3640,10 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
36153640 self . visit_path ( & delegation. path , delegation. id ) ;
36163641 let Some ( body) = & delegation. body else { return } ;
36173642 self . with_rib ( ValueNS , RibKind :: FnOrCoroutine , |this| {
3618- // `PatBoundCtx` is not necessary in this context
3619- let mut bindings = smallvec ! [ ( PatBoundCtx :: Product , Default :: default ( ) ) ] ;
3620-
36213643 let span = delegation. path . segments . last ( ) . unwrap ( ) . ident . span ;
3622- this. fresh_binding (
3623- Ident :: new ( kw:: SelfLower , span) ,
3624- delegation. id ,
3625- PatternSource :: FnParam ,
3626- & mut bindings,
3627- ) ;
3644+ let ident = Ident :: new ( kw:: SelfLower , span. normalize_to_macro_rules ( ) ) ;
3645+ let res = Res :: Local ( delegation. id ) ;
3646+ this. innermost_rib_bindings ( ValueNS ) . insert ( ident, res) ;
36283647 this. visit_block ( body) ;
36293648 } ) ;
36303649 }
@@ -3635,6 +3654,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
36353654 for Param { pat, .. } in params {
36363655 this. resolve_pattern ( pat, PatternSource :: FnParam , & mut bindings) ;
36373656 }
3657+ this. apply_pattern_bindings ( bindings) ;
36383658 } ) ;
36393659 for Param { ty, .. } in params {
36403660 self . visit_ty ( ty) ;
@@ -3851,13 +3871,32 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
38513871 fn resolve_pattern_top ( & mut self , pat : & ' ast Pat , pat_src : PatternSource ) {
38523872 let mut bindings = smallvec ! [ ( PatBoundCtx :: Product , Default :: default ( ) ) ] ;
38533873 self . resolve_pattern ( pat, pat_src, & mut bindings) ;
3874+ self . apply_pattern_bindings ( bindings) ;
3875+ }
3876+
3877+ /// Apply the bindings from a pattern to the innermost rib of the current scope.
3878+ fn apply_pattern_bindings ( & mut self , mut pat_bindings : PatternBindings ) {
3879+ let rib_bindings = self . innermost_rib_bindings ( ValueNS ) ;
3880+ let Some ( ( _, pat_bindings) ) = pat_bindings. pop ( ) else {
3881+ bug ! ( "tried applying nonexistent bindings from pattern" ) ;
3882+ } ;
3883+
3884+ if rib_bindings. is_empty ( ) {
3885+ // Often, such as for match arms, the bindings are introduced into a new rib.
3886+ // In this case, we can move the bindings over directly.
3887+ * rib_bindings = pat_bindings;
3888+ } else {
3889+ rib_bindings. extend ( pat_bindings) ;
3890+ }
38543891 }
38553892
3893+ /// Resolve bindings in a pattern. `apply_pattern_bindings` must be called after to introduce
3894+ /// the bindings into scope.
38563895 fn resolve_pattern (
38573896 & mut self ,
38583897 pat : & ' ast Pat ,
38593898 pat_src : PatternSource ,
3860- bindings : & mut SmallVec < [ ( PatBoundCtx , FxHashSet < Ident > ) ; 1 ] > ,
3899+ bindings : & mut PatternBindings ,
38613900 ) {
38623901 // We walk the pattern before declaring the pattern's inner bindings,
38633902 // so that we avoid resolving a literal expression to a binding defined
@@ -3890,9 +3929,9 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
38903929 #[ tracing:: instrument( skip( self , bindings) , level = "debug" ) ]
38913930 fn resolve_pattern_inner (
38923931 & mut self ,
3893- pat : & Pat ,
3932+ pat : & ' ast Pat ,
38943933 pat_src : PatternSource ,
3895- bindings : & mut SmallVec < [ ( PatBoundCtx , FxHashSet < Ident > ) ; 1 ] > ,
3934+ bindings : & mut PatternBindings ,
38963935 ) {
38973936 // Visit all direct subpatterns of this pattern.
38983937 pat. walk ( & mut |pat| {
@@ -3950,6 +3989,27 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
39503989 // Prevent visiting `ps` as we've already done so above.
39513990 return false ;
39523991 }
3992+ PatKind :: Guard ( ref subpat, ref guard) => {
3993+ // Add a new set of bindings to the stack to collect bindings in `subpat`.
3994+ bindings. push ( ( PatBoundCtx :: Product , Default :: default ( ) ) ) ;
3995+ self . resolve_pattern_inner ( subpat, pat_src, bindings) ;
3996+ // These bindings, but none from the surrounding pattern, are visible in the
3997+ // guard; put them in scope and resolve `guard`.
3998+ let subpat_bindings = bindings. pop ( ) . unwrap ( ) . 1 ;
3999+ self . with_rib ( ValueNS , RibKind :: Normal , |this| {
4000+ * this. innermost_rib_bindings ( ValueNS ) = subpat_bindings. clone ( ) ;
4001+ this. resolve_expr ( guard, None ) ;
4002+ } ) ;
4003+ // Propagate the subpattern's bindings upwards.
4004+ // FIXME(guard_patterns): For `if let` guards, we'll also need to get the
4005+ // bindings introduced by the guard from its rib and propagate them upwards.
4006+ // This will require checking the identifiers for overlaps with `bindings`, like
4007+ // what `fresh_binding` does (ideally sharing its logic). To keep them separate
4008+ // from `subpat_bindings`, we can introduce a fresh rib for the guard.
4009+ bindings. last_mut ( ) . unwrap ( ) . 1 . extend ( subpat_bindings) ;
4010+ // Prevent visiting `subpat` as we've already done so above.
4011+ return false ;
4012+ }
39534013 _ => { }
39544014 }
39554015 true
@@ -3988,20 +4048,17 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
39884048 ident : Ident ,
39894049 pat_id : NodeId ,
39904050 pat_src : PatternSource ,
3991- bindings : & mut SmallVec < [ ( PatBoundCtx , FxHashSet < Ident > ) ; 1 ] > ,
4051+ bindings : & mut PatternBindings ,
39924052 ) -> Res {
3993- // Add the binding to the local ribs , if it doesn't already exist in the bindings map .
4053+ // Add the binding to the bindings map , if it doesn't already exist.
39944054 // (We must not add it if it's in the bindings map because that breaks the assumptions
39954055 // later passes make about or-patterns.)
39964056 let ident = ident. normalize_to_macro_rules ( ) ;
39974057
3998- let mut bound_iter = bindings. iter ( ) . filter ( |( _, set) | set. contains ( & ident) ) ;
39994058 // Already bound in a product pattern? e.g. `(a, a)` which is not allowed.
4000- let already_bound_and = bound_iter. clone ( ) . any ( |( ctx, _) | * ctx == PatBoundCtx :: Product ) ;
4001- // Already bound in an or-pattern? e.g. `V1(a) | V2(a)`.
4002- // This is *required* for consistency which is checked later.
4003- let already_bound_or = bound_iter. any ( |( ctx, _) | * ctx == PatBoundCtx :: Or ) ;
4004-
4059+ let already_bound_and = bindings
4060+ . iter ( )
4061+ . any ( |( ctx, map) | * ctx == PatBoundCtx :: Product && map. contains_key ( & ident) ) ;
40054062 if already_bound_and {
40064063 // Overlap in a product pattern somewhere; report an error.
40074064 use ResolutionError :: * ;
@@ -4014,19 +4071,23 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
40144071 self . report_error ( ident. span , error ( ident) ) ;
40154072 }
40164073
4017- // Record as bound.
4018- bindings. last_mut ( ) . unwrap ( ) . 1 . insert ( ident) ;
4019-
4020- if already_bound_or {
4074+ // Already bound in an or-pattern? e.g. `V1(a) | V2(a)`.
4075+ // This is *required* for consistency which is checked later.
4076+ let already_bound_or = bindings
4077+ . iter ( )
4078+ . find_map ( |( ctx, map) | if * ctx == PatBoundCtx :: Or { map. get ( & ident) } else { None } ) ;
4079+ let res = if let Some ( & res) = already_bound_or {
40214080 // `Variant1(a) | Variant2(a)`, ok
40224081 // Reuse definition from the first `a`.
4023- self . innermost_rib_bindings ( ValueNS ) [ & ident]
4024- } else {
4025- // A completely fresh binding is added to the set.
4026- let res = Res :: Local ( pat_id) ;
4027- self . innermost_rib_bindings ( ValueNS ) . insert ( ident, res) ;
40284082 res
4029- }
4083+ } else {
4084+ // A completely fresh binding is added to the map.
4085+ Res :: Local ( pat_id)
4086+ } ;
4087+
4088+ // Record as bound.
4089+ bindings. last_mut ( ) . unwrap ( ) . 1 . insert ( ident, res) ;
4090+ res
40304091 }
40314092
40324093 fn innermost_rib_bindings ( & mut self , ns : Namespace ) -> & mut FxIndexMap < Ident , Res > {
0 commit comments