@@ -80,7 +80,7 @@ struct TopInfo<'tcx> {
80
80
#[ derive( Copy , Clone ) ]
81
81
struct PatInfo < ' tcx , ' a > {
82
82
binding_mode : ByRef ,
83
- max_ref_mutbl : Mutability ,
83
+ max_ref_mutbl : MutblCap ,
84
84
top_info : TopInfo < ' tcx > ,
85
85
decl_origin : Option < DeclOrigin < ' a > > ,
86
86
@@ -126,6 +126,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
126
126
}
127
127
128
128
/// Mode for adjusting the expected type and binding mode.
129
+ #[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
129
130
enum AdjustMode {
130
131
/// Peel off all immediate reference types.
131
132
Peel ,
@@ -137,11 +138,44 @@ enum AdjustMode {
137
138
/// and if the old biding mode was by-reference
138
139
/// with mutability matching the pattern,
139
140
/// mark the pattern as having consumed this reference.
140
- ResetAndConsumeRef ( Mutability ) ,
141
+ ///
142
+ /// `Span` is that of the inside of the reference pattern
143
+ ResetAndConsumeRef ( Mutability , Span ) ,
141
144
/// Pass on the input binding mode and expected type.
142
145
Pass ,
143
146
}
144
147
148
+ /// `ref mut` patterns (explicit or match-ergonomics)
149
+ /// are not allowed behind an `&` reference.
150
+ ///
151
+ /// This includes explicit `ref mut` behind `&` patterns
152
+ /// that match against `&mut` references,
153
+ /// where the code would have compiled
154
+ /// had the pattern been written as `&mut`.
155
+ /// However, the borrow checker will not catch
156
+ /// this last case, so we need to throw an error ourselves.
157
+ #[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
158
+ enum MutblCap {
159
+ /// Mutability restricted to immutable;
160
+ /// contained span, if present, should be shown in diagnostics as the reason.
161
+ Not ( Option < Span > ) ,
162
+ /// No restriction on mutability
163
+ Mut ,
164
+ }
165
+
166
+ impl MutblCap {
167
+ fn cap_mutbl_to_not ( self , span : Option < Span > ) -> Self {
168
+ if self == MutblCap :: Mut { MutblCap :: Not ( span) } else { self }
169
+ }
170
+
171
+ fn as_mutbl ( self ) -> Mutability {
172
+ match self {
173
+ MutblCap :: Not ( _) => Mutability :: Not ,
174
+ MutblCap :: Mut => Mutability :: Mut ,
175
+ }
176
+ }
177
+ }
178
+
145
179
impl < ' a , ' tcx > FnCtxt < ' a , ' tcx > {
146
180
/// Type check the given top level pattern against the `expected` type.
147
181
///
@@ -162,7 +196,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
162
196
let info = TopInfo { expected, origin_expr, span } ;
163
197
let pat_info = PatInfo {
164
198
binding_mode : ByRef :: No ,
165
- max_ref_mutbl : Mutability :: Mut ,
199
+ max_ref_mutbl : MutblCap :: Mut ,
166
200
top_info : info,
167
201
decl_origin,
168
202
current_depth : 0 ,
@@ -203,8 +237,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
203
237
PatKind :: Never => expected,
204
238
PatKind :: Lit ( lt) => self . check_pat_lit ( pat. span , lt, expected, ti) ,
205
239
PatKind :: Range ( lhs, rhs, _) => self . check_pat_range ( pat. span , lhs, rhs, expected, ti) ,
206
- PatKind :: Binding ( ba, var_id, _ , sub) => {
207
- self . check_pat_ident ( pat, ba, var_id, sub, expected, pat_info)
240
+ PatKind :: Binding ( ba, var_id, ident , sub) => {
241
+ self . check_pat_ident ( pat, ba, var_id, ident , sub, expected, pat_info)
208
242
}
209
243
PatKind :: TupleStruct ( ref qpath, subpats, ddpos) => {
210
244
self . check_pat_tuple_struct ( pat, qpath, subpats, ddpos, expected, pat_info)
@@ -296,20 +330,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
296
330
expected : Ty < ' tcx > ,
297
331
def_br : ByRef ,
298
332
adjust_mode : AdjustMode ,
299
- max_ref_mutbl : Mutability ,
300
- ) -> ( Ty < ' tcx > , ByRef , Mutability , bool ) {
301
- if let ByRef :: Yes ( mutbl ) = def_br {
302
- debug_assert ! ( mutbl <= max_ref_mutbl ) ;
333
+ max_ref_mutbl : MutblCap ,
334
+ ) -> ( Ty < ' tcx > , ByRef , MutblCap , bool ) {
335
+ if let ByRef :: Yes ( Mutability :: Mut ) = def_br {
336
+ debug_assert ! ( max_ref_mutbl == MutblCap :: Mut ) ;
303
337
}
304
338
match adjust_mode {
305
339
AdjustMode :: Pass => ( expected, def_br, max_ref_mutbl, false ) ,
306
- AdjustMode :: Reset => ( expected, ByRef :: No , Mutability :: Mut , false ) ,
307
- AdjustMode :: ResetAndConsumeRef ( ref_pat_mutbl) => {
308
- let mutbls_match = def_br == ByRef :: Yes ( ref_pat_mutbl) ;
340
+ AdjustMode :: Reset => ( expected, ByRef :: No , MutblCap :: Mut , false ) ,
341
+ AdjustMode :: ResetAndConsumeRef ( ref_pat_mutbl, inner_span) => {
342
+ // `&` pattern eats `&mut`
343
+ let mutbls_match =
344
+ if let ByRef :: Yes ( def_mut) = def_br { ref_pat_mutbl <= def_mut } else { false } ;
345
+
309
346
if pat. span . at_least_rust_2024 ( ) && self . tcx . features ( ) . ref_pat_eat_one_layer_2024 {
347
+ let max_ref_mutbl = if ref_pat_mutbl == Mutability :: Not {
348
+ max_ref_mutbl. cap_mutbl_to_not ( Some ( pat. span . until ( inner_span) ) )
349
+ } else {
350
+ max_ref_mutbl
351
+ } ;
352
+
310
353
if mutbls_match {
311
354
debug ! ( "consuming inherited reference" ) ;
312
- ( expected, ByRef :: No , cmp :: min ( max_ref_mutbl, ref_pat_mutbl ) , true )
355
+ ( expected, ByRef :: No , max_ref_mutbl, true )
313
356
} else {
314
357
let ( new_ty, new_bm, max_ref_mutbl) = if ref_pat_mutbl == Mutability :: Mut {
315
358
self . peel_off_references (
@@ -320,7 +363,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
320
363
max_ref_mutbl,
321
364
)
322
365
} else {
323
- ( expected, def_br. cap_ref_mutability ( Mutability :: Not ) , Mutability :: Not )
366
+ ( expected, def_br. cap_ref_mutability ( Mutability :: Not ) , max_ref_mutbl )
324
367
} ;
325
368
( new_ty, new_bm, max_ref_mutbl, false )
326
369
}
@@ -387,7 +430,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
387
430
// ```
388
431
//
389
432
// See issue #46688.
390
- PatKind :: Ref ( _ , mutbl) => AdjustMode :: ResetAndConsumeRef ( * mutbl) ,
433
+ PatKind :: Ref ( inner , mutbl) => AdjustMode :: ResetAndConsumeRef ( * mutbl, inner . span ) ,
391
434
// A `_` pattern works with any expected type, so there's no need to do anything.
392
435
PatKind :: Wild
393
436
// A malformed pattern doesn't have an expected type, so let's just accept any type.
@@ -413,8 +456,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
413
456
expected : Ty < ' tcx > ,
414
457
mut def_br : ByRef ,
415
458
max_peelable_mutability : Mutability ,
416
- mut max_ref_mutability : Mutability ,
417
- ) -> ( Ty < ' tcx > , ByRef , Mutability ) {
459
+ mut max_ref_mutability : MutblCap ,
460
+ ) -> ( Ty < ' tcx > , ByRef , MutblCap ) {
418
461
let mut expected = self . try_structurally_resolve_type ( pat. span , expected) ;
419
462
// Peel off as many `&` or `&mut` from the scrutinee type as possible. For example,
420
463
// for `match &&&mut Some(5)` the loop runs three times, aborting when it reaches
@@ -448,9 +491,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
448
491
}
449
492
450
493
if pat. span . at_least_rust_2024 ( ) && self . tcx . features ( ) . ref_pat_eat_one_layer_2024 {
451
- def_br = def_br. cap_ref_mutability ( max_ref_mutability) ;
494
+ def_br = def_br. cap_ref_mutability ( max_ref_mutability. as_mutbl ( ) ) ;
452
495
if def_br == ByRef :: Yes ( Mutability :: Not ) {
453
- max_ref_mutability = Mutability :: Not ;
496
+ max_ref_mutability = max_ref_mutability . cap_mutbl_to_not ( None ) ;
454
497
}
455
498
}
456
499
@@ -667,16 +710,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
667
710
fn check_pat_ident (
668
711
& self ,
669
712
pat : & ' tcx Pat < ' tcx > ,
670
- ba : BindingMode ,
713
+ explicit_ba : BindingMode ,
671
714
var_id : HirId ,
715
+ ident : Ident ,
672
716
sub : Option < & ' tcx Pat < ' tcx > > ,
673
717
expected : Ty < ' tcx > ,
674
718
pat_info : PatInfo < ' tcx , ' _ > ,
675
719
) -> Ty < ' tcx > {
676
720
let PatInfo { binding_mode : def_br, top_info : ti, .. } = pat_info;
677
721
678
722
// Determine the binding mode...
679
- let bm = match ba {
723
+ let bm = match explicit_ba {
680
724
BindingMode ( ByRef :: No , Mutability :: Mut )
681
725
if !( pat. span . at_least_rust_2024 ( )
682
726
&& self . tcx . features ( ) . mut_preserve_binding_mode_2024 )
@@ -692,8 +736,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
692
736
BindingMode ( ByRef :: No , Mutability :: Mut )
693
737
}
694
738
BindingMode ( ByRef :: No , mutbl) => BindingMode ( def_br, mutbl) ,
695
- BindingMode ( ByRef :: Yes ( _) , _) => ba ,
739
+ BindingMode ( ByRef :: Yes ( _) , _) => explicit_ba ,
696
740
} ;
741
+
742
+ if bm. 0 == ByRef :: Yes ( Mutability :: Mut )
743
+ && let MutblCap :: Not ( Some ( and_pat_span) ) = pat_info. max_ref_mutbl
744
+ {
745
+ let mut err = struct_span_code_err ! (
746
+ self . tcx. dcx( ) ,
747
+ ident. span,
748
+ E0596 ,
749
+ "cannot bind with `ref mut` behind an `&` pattern"
750
+ ) ;
751
+ err. span_help ( and_pat_span, "change this `&` pattern to an `&mut`" ) ;
752
+ err. emit ( ) ;
753
+ }
754
+
697
755
// ...and store it in a side table:
698
756
self . typeck_results . borrow_mut ( ) . pat_binding_modes_mut ( ) . insert ( pat. hir_id , bm) ;
699
757
@@ -719,7 +777,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
719
777
// If there are multiple arms, make sure they all agree on
720
778
// what the type of the binding `x` ought to be.
721
779
if var_id != pat. hir_id {
722
- self . check_binding_alt_eq_ty ( ba , pat. span , var_id, local_ty, ti) ;
780
+ self . check_binding_alt_eq_ty ( explicit_ba , pat. span , var_id, local_ty, ti) ;
723
781
}
724
782
725
783
if let Some ( p) = sub {
@@ -2115,7 +2173,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2115
2173
} else {
2116
2174
let tcx = self . tcx ;
2117
2175
let expected = self . shallow_resolve ( expected) ;
2118
- let ( ref_ty, inner_ty) = match self . check_dereferenceable ( pat. span , expected, inner) {
2176
+ let ( ref_ty, inner_ty, pat_info) = match self
2177
+ . check_dereferenceable ( pat. span , expected, inner)
2178
+ {
2119
2179
Ok ( ( ) ) => {
2120
2180
// `demand::subtype` would be good enough, but using `eqtype` turns
2121
2181
// out to be equally general. See (note_1) for details.
@@ -2125,45 +2185,65 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2125
2185
// the bad interactions of the given hack detailed in (note_1).
2126
2186
debug ! ( "check_pat_ref: expected={:?}" , expected) ;
2127
2187
match * expected. kind ( ) {
2128
- ty:: Ref ( _, r_ty, r_mutbl) if r_mutbl == mutbl => ( expected, r_ty) ,
2188
+ ty:: Ref ( _, r_ty, r_mutbl) if r_mutbl == mutbl => ( expected, r_ty, pat_info) ,
2189
+
2190
+ // `&` pattern eats `&mut` reference
2191
+ ty:: Ref ( _, r_ty, Mutability :: Mut )
2192
+ if mutbl == Mutability :: Not
2193
+ && ( ( pat. span . at_least_rust_2024 ( )
2194
+ && self . tcx . features ( ) . ref_pat_eat_one_layer_2024 )
2195
+ || self . tcx . features ( ) . ref_pat_everywhere ) =>
2196
+ {
2197
+ (
2198
+ expected,
2199
+ r_ty,
2200
+ PatInfo {
2201
+ max_ref_mutbl : pat_info
2202
+ . max_ref_mutbl
2203
+ . cap_mutbl_to_not ( Some ( pat. span . until ( inner. span ) ) ) ,
2204
+ ..pat_info
2205
+ } ,
2206
+ )
2207
+ }
2208
+
2209
+ _ if consumed_inherited_ref && self . tcx . features ( ) . ref_pat_everywhere => {
2210
+ // We already matched against a match-ergonmics inserted reference,
2211
+ // so we don't need to match against a reference from the original type.
2212
+ // Save this infor for use in lowering later
2213
+ self . typeck_results
2214
+ . borrow_mut ( )
2215
+ . skipped_ref_pats_mut ( )
2216
+ . insert ( pat. hir_id ) ;
2217
+ ( expected, expected, pat_info)
2218
+ }
2219
+
2129
2220
_ => {
2130
- if consumed_inherited_ref && self . tcx . features ( ) . ref_pat_everywhere {
2131
- // We already matched against a match-ergonmics inserted reference,
2132
- // so we don't need to match against a reference from the original type.
2133
- // Save this infor for use in lowering later
2134
- self . typeck_results
2135
- . borrow_mut ( )
2136
- . skipped_ref_pats_mut ( )
2137
- . insert ( pat. hir_id ) ;
2138
- ( expected, expected)
2139
- } else {
2140
- let inner_ty = self . next_ty_var ( TypeVariableOrigin {
2141
- param_def_id : None ,
2142
- span : inner. span ,
2143
- } ) ;
2144
- let ref_ty = self . new_ref_ty ( pat. span , mutbl, inner_ty) ;
2145
- debug ! ( "check_pat_ref: demanding {:?} = {:?}" , expected, ref_ty) ;
2146
- let err = self . demand_eqtype_pat_diag (
2147
- pat. span ,
2148
- expected,
2149
- ref_ty,
2150
- pat_info. top_info ,
2151
- ) ;
2221
+ let inner_ty = self . next_ty_var ( TypeVariableOrigin {
2222
+ param_def_id : None ,
2223
+ span : inner. span ,
2224
+ } ) ;
2225
+ let ref_ty = self . new_ref_ty ( pat. span , mutbl, inner_ty) ;
2226
+ debug ! ( "check_pat_ref: demanding {:?} = {:?}" , expected, ref_ty) ;
2227
+ let err = self . demand_eqtype_pat_diag (
2228
+ pat. span ,
2229
+ expected,
2230
+ ref_ty,
2231
+ pat_info. top_info ,
2232
+ ) ;
2152
2233
2153
- // Look for a case like `fn foo(&foo: u32)` and suggest
2154
- // `fn foo(foo: &u32)`
2155
- if let Some ( mut err) = err {
2156
- self . borrow_pat_suggestion ( & mut err, pat) ;
2157
- err. emit ( ) ;
2158
- }
2159
- ( ref_ty, inner_ty)
2234
+ // Look for a case like `fn foo(&foo: u32)` and suggest
2235
+ // `fn foo(foo: &u32)`
2236
+ if let Some ( mut err) = err {
2237
+ self . borrow_pat_suggestion ( & mut err, pat) ;
2238
+ err. emit ( ) ;
2160
2239
}
2240
+ ( ref_ty, inner_ty, pat_info)
2161
2241
}
2162
2242
}
2163
2243
}
2164
2244
Err ( guar) => {
2165
2245
let err = Ty :: new_error ( tcx, guar) ;
2166
- ( err, err)
2246
+ ( err, err, pat_info )
2167
2247
}
2168
2248
} ;
2169
2249
self . check_pat ( inner, inner_ty, pat_info) ;
0 commit comments