@@ -111,6 +111,19 @@ enum PatBoundCtx {
111
111
Or ,
112
112
}
113
113
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
+
114
127
/// Does this the item (from the item rib scope) allow generic parameters?
115
128
#[ derive( Copy , Clone , Debug ) ]
116
129
pub ( crate ) enum HasGenericParams {
@@ -786,7 +799,14 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r
786
799
fn visit_pat ( & mut self , p : & ' ast Pat ) {
787
800
let prev = self . diag_metadata . current_pat ;
788
801
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
+
790
810
self . diag_metadata . current_pat = prev;
791
811
}
792
812
fn visit_local ( & mut self , local : & ' ast Local ) {
@@ -2297,7 +2317,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
2297
2317
fn resolve_fn_params (
2298
2318
& mut self ,
2299
2319
has_self : bool ,
2300
- inputs : impl Iterator < Item = ( Option < & ' ast Pat > , & ' ast Ty ) > ,
2320
+ inputs : impl Iterator < Item = ( Option < & ' ast Pat > , & ' ast Ty ) > + Clone ,
2301
2321
) -> Result < LifetimeRes , ( Vec < MissingLifetime > , Vec < ElisionFnParameter > ) > {
2302
2322
enum Elision {
2303
2323
/// We have not found any candidate.
@@ -2319,15 +2339,20 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
2319
2339
let mut parameter_info = Vec :: new ( ) ;
2320
2340
let mut all_candidates = Vec :: new ( ) ;
2321
2341
2342
+ // Resolve and apply bindings first so diagnostics can see if they're used in types.
2322
2343
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:?}" ) ;
2325
2346
self . with_lifetime_rib ( LifetimeRibKind :: Elided ( LifetimeRes :: Infer ) , |this| {
2326
2347
if let Some ( pat) = pat {
2327
2348
this. resolve_pattern ( pat, PatternSource :: FnParam , & mut bindings) ;
2328
2349
}
2329
2350
} ) ;
2351
+ }
2352
+ self . apply_pattern_bindings ( bindings) ;
2330
2353
2354
+ for ( index, ( pat, ty) ) in inputs. enumerate ( ) {
2355
+ debug ! ( "resolving type for pat = {pat:?}, ty = {ty:?}" ) ;
2331
2356
// Record elision candidates only for this parameter.
2332
2357
debug_assert_matches ! ( self . lifetime_elision_candidates, None ) ;
2333
2358
self . lifetime_elision_candidates = Some ( Default :: default ( ) ) ;
@@ -3615,16 +3640,10 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
3615
3640
self . visit_path ( & delegation. path , delegation. id ) ;
3616
3641
let Some ( body) = & delegation. body else { return } ;
3617
3642
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
-
3621
3643
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) ;
3628
3647
this. visit_block ( body) ;
3629
3648
} ) ;
3630
3649
}
@@ -3635,6 +3654,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
3635
3654
for Param { pat, .. } in params {
3636
3655
this. resolve_pattern ( pat, PatternSource :: FnParam , & mut bindings) ;
3637
3656
}
3657
+ this. apply_pattern_bindings ( bindings) ;
3638
3658
} ) ;
3639
3659
for Param { ty, .. } in params {
3640
3660
self . visit_ty ( ty) ;
@@ -3851,13 +3871,32 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
3851
3871
fn resolve_pattern_top ( & mut self , pat : & ' ast Pat , pat_src : PatternSource ) {
3852
3872
let mut bindings = smallvec ! [ ( PatBoundCtx :: Product , Default :: default ( ) ) ] ;
3853
3873
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
+ }
3854
3891
}
3855
3892
3893
+ /// Resolve bindings in a pattern. `apply_pattern_bindings` must be called after to introduce
3894
+ /// the bindings into scope.
3856
3895
fn resolve_pattern (
3857
3896
& mut self ,
3858
3897
pat : & ' ast Pat ,
3859
3898
pat_src : PatternSource ,
3860
- bindings : & mut SmallVec < [ ( PatBoundCtx , FxHashSet < Ident > ) ; 1 ] > ,
3899
+ bindings : & mut PatternBindings ,
3861
3900
) {
3862
3901
// We walk the pattern before declaring the pattern's inner bindings,
3863
3902
// 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> {
3890
3929
#[ tracing:: instrument( skip( self , bindings) , level = "debug" ) ]
3891
3930
fn resolve_pattern_inner (
3892
3931
& mut self ,
3893
- pat : & Pat ,
3932
+ pat : & ' ast Pat ,
3894
3933
pat_src : PatternSource ,
3895
- bindings : & mut SmallVec < [ ( PatBoundCtx , FxHashSet < Ident > ) ; 1 ] > ,
3934
+ bindings : & mut PatternBindings ,
3896
3935
) {
3897
3936
// Visit all direct subpatterns of this pattern.
3898
3937
pat. walk ( & mut |pat| {
@@ -3950,6 +3989,27 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
3950
3989
// Prevent visiting `ps` as we've already done so above.
3951
3990
return false ;
3952
3991
}
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
+ }
3953
4013
_ => { }
3954
4014
}
3955
4015
true
@@ -3988,20 +4048,17 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
3988
4048
ident : Ident ,
3989
4049
pat_id : NodeId ,
3990
4050
pat_src : PatternSource ,
3991
- bindings : & mut SmallVec < [ ( PatBoundCtx , FxHashSet < Ident > ) ; 1 ] > ,
4051
+ bindings : & mut PatternBindings ,
3992
4052
) -> 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.
3994
4054
// (We must not add it if it's in the bindings map because that breaks the assumptions
3995
4055
// later passes make about or-patterns.)
3996
4056
let ident = ident. normalize_to_macro_rules ( ) ;
3997
4057
3998
- let mut bound_iter = bindings. iter ( ) . filter ( |( _, set) | set. contains ( & ident) ) ;
3999
4058
// 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) ) ;
4005
4062
if already_bound_and {
4006
4063
// Overlap in a product pattern somewhere; report an error.
4007
4064
use ResolutionError :: * ;
@@ -4014,19 +4071,23 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
4014
4071
self . report_error ( ident. span , error ( ident) ) ;
4015
4072
}
4016
4073
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 {
4021
4080
// `Variant1(a) | Variant2(a)`, ok
4022
4081
// 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) ;
4028
4082
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
4030
4091
}
4031
4092
4032
4093
fn innermost_rib_bindings ( & mut self , ns : Namespace ) -> & mut FxIndexMap < Ident , Res > {
0 commit comments