@@ -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,31 @@ 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+ // Resolving `subpat` adds bindings onto the newly-pushed context. After, the
3996+ // total number of contexts on the stack should be the same as before.
3997+ let binding_ctx_stack_len = bindings. len ( ) ;
3998+ self . resolve_pattern_inner ( subpat, pat_src, bindings) ;
3999+ assert_eq ! ( bindings. len( ) , binding_ctx_stack_len) ;
4000+ // These bindings, but none from the surrounding pattern, are visible in the
4001+ // guard; put them in scope and resolve `guard`.
4002+ let subpat_bindings = bindings. pop ( ) . unwrap ( ) . 1 ;
4003+ self . with_rib ( ValueNS , RibKind :: Normal , |this| {
4004+ * this. innermost_rib_bindings ( ValueNS ) = subpat_bindings. clone ( ) ;
4005+ this. resolve_expr ( guard, None ) ;
4006+ } ) ;
4007+ // Propagate the subpattern's bindings upwards.
4008+ // FIXME(guard_patterns): For `if let` guards, we'll also need to get the
4009+ // bindings introduced by the guard from its rib and propagate them upwards.
4010+ // This will require checking the identifiers for overlaps with `bindings`, like
4011+ // what `fresh_binding` does (ideally sharing its logic). To keep them separate
4012+ // from `subpat_bindings`, we can introduce a fresh rib for the guard.
4013+ bindings. last_mut ( ) . unwrap ( ) . 1 . extend ( subpat_bindings) ;
4014+ // Prevent visiting `subpat` as we've already done so above.
4015+ return false ;
4016+ }
39534017 _ => { }
39544018 }
39554019 true
@@ -3988,20 +4052,17 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
39884052 ident : Ident ,
39894053 pat_id : NodeId ,
39904054 pat_src : PatternSource ,
3991- bindings : & mut SmallVec < [ ( PatBoundCtx , FxHashSet < Ident > ) ; 1 ] > ,
4055+ bindings : & mut PatternBindings ,
39924056 ) -> Res {
3993- // Add the binding to the local ribs , if it doesn't already exist in the bindings map .
4057+ // Add the binding to the bindings map , if it doesn't already exist.
39944058 // (We must not add it if it's in the bindings map because that breaks the assumptions
39954059 // later passes make about or-patterns.)
39964060 let ident = ident. normalize_to_macro_rules ( ) ;
39974061
3998- let mut bound_iter = bindings. iter ( ) . filter ( |( _, set) | set. contains ( & ident) ) ;
39994062 // 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-
4063+ let already_bound_and = bindings
4064+ . iter ( )
4065+ . any ( |( ctx, map) | * ctx == PatBoundCtx :: Product && map. contains_key ( & ident) ) ;
40054066 if already_bound_and {
40064067 // Overlap in a product pattern somewhere; report an error.
40074068 use ResolutionError :: * ;
@@ -4014,19 +4075,23 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
40144075 self . report_error ( ident. span , error ( ident) ) ;
40154076 }
40164077
4017- // Record as bound.
4018- bindings. last_mut ( ) . unwrap ( ) . 1 . insert ( ident) ;
4019-
4020- if already_bound_or {
4078+ // Already bound in an or-pattern? e.g. `V1(a) | V2(a)`.
4079+ // This is *required* for consistency which is checked later.
4080+ let already_bound_or = bindings
4081+ . iter ( )
4082+ . find_map ( |( ctx, map) | if * ctx == PatBoundCtx :: Or { map. get ( & ident) } else { None } ) ;
4083+ let res = if let Some ( & res) = already_bound_or {
40214084 // `Variant1(a) | Variant2(a)`, ok
40224085 // 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) ;
40284086 res
4029- }
4087+ } else {
4088+ // A completely fresh binding is added to the map.
4089+ Res :: Local ( pat_id)
4090+ } ;
4091+
4092+ // Record as bound.
4093+ bindings. last_mut ( ) . unwrap ( ) . 1 . insert ( ident, res) ;
4094+ res
40304095 }
40314096
40324097 fn innermost_rib_bindings ( & mut self , ns : Namespace ) -> & mut FxIndexMap < Ident , Res > {
0 commit comments