@@ -78,7 +78,7 @@ struct TopInfo<'tcx> {
7878#[ derive( Copy , Clone ) ]
7979struct PatInfo < ' tcx , ' a > {
8080 binding_mode : ByRef ,
81- max_ref_mutbl : Mutability ,
81+ max_ref_mutbl : MutblCap ,
8282 top_info : TopInfo < ' tcx > ,
8383 decl_origin : Option < DeclOrigin < ' a > > ,
8484
@@ -124,6 +124,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
124124}
125125
126126/// Mode for adjusting the expected type and binding mode.
127+ #[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
127128enum AdjustMode {
128129 /// Peel off all immediate reference types.
129130 Peel ,
@@ -135,11 +136,44 @@ enum AdjustMode {
135136 /// and if the old biding mode was by-reference
136137 /// with mutability matching the pattern,
137138 /// mark the pattern as having consumed this reference.
138- ResetAndConsumeRef ( Mutability ) ,
139+ ///
140+ /// `Span` is that of the inside of the reference pattern
141+ ResetAndConsumeRef ( Mutability , Span ) ,
139142 /// Pass on the input binding mode and expected type.
140143 Pass ,
141144}
142145
146+ /// `ref mut` patterns (explicit or match-ergonomics)
147+ /// are not allowed behind an `&` reference.
148+ ///
149+ /// This includes explicit `ref mut` behind `&` patterns
150+ /// that match against `&mut` references,
151+ /// where the code would have compiled
152+ /// had the pattern been written as `&mut`.
153+ /// However, the borrow checker will not catch
154+ /// this last case, so we need to throw an error ourselves.
155+ #[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
156+ enum MutblCap {
157+ /// Mutability restricted to immutable;
158+ /// contained span, if present, should be shown in diagnostics as the reason.
159+ Not ( Option < Span > ) ,
160+ /// No restriction on mutability
161+ Mut ,
162+ }
163+
164+ impl MutblCap {
165+ fn cap_mutbl_to_not ( self , span : Option < Span > ) -> Self {
166+ if self == MutblCap :: Mut { MutblCap :: Not ( span) } else { self }
167+ }
168+
169+ fn as_mutbl ( self ) -> Mutability {
170+ match self {
171+ MutblCap :: Not ( _) => Mutability :: Not ,
172+ MutblCap :: Mut => Mutability :: Mut ,
173+ }
174+ }
175+ }
176+
143177impl < ' a , ' tcx > FnCtxt < ' a , ' tcx > {
144178 /// Type check the given top level pattern against the `expected` type.
145179 ///
@@ -160,7 +194,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
160194 let info = TopInfo { expected, origin_expr, span } ;
161195 let pat_info = PatInfo {
162196 binding_mode : ByRef :: No ,
163- max_ref_mutbl : Mutability :: Mut ,
197+ max_ref_mutbl : MutblCap :: Mut ,
164198 top_info : info,
165199 decl_origin,
166200 current_depth : 0 ,
@@ -201,8 +235,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
201235 PatKind :: Never => expected,
202236 PatKind :: Lit ( lt) => self . check_pat_lit ( pat. span , lt, expected, ti) ,
203237 PatKind :: Range ( lhs, rhs, _) => self . check_pat_range ( pat. span , lhs, rhs, expected, ti) ,
204- PatKind :: Binding ( ba, var_id, _ , sub) => {
205- self . check_pat_ident ( pat, ba, var_id, sub, expected, pat_info)
238+ PatKind :: Binding ( ba, var_id, ident , sub) => {
239+ self . check_pat_ident ( pat, ba, var_id, ident , sub, expected, pat_info)
206240 }
207241 PatKind :: TupleStruct ( ref qpath, subpats, ddpos) => {
208242 self . check_pat_tuple_struct ( pat, qpath, subpats, ddpos, expected, pat_info)
@@ -294,20 +328,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
294328 expected : Ty < ' tcx > ,
295329 def_br : ByRef ,
296330 adjust_mode : AdjustMode ,
297- max_ref_mutbl : Mutability ,
298- ) -> ( Ty < ' tcx > , ByRef , Mutability , bool ) {
299- if let ByRef :: Yes ( mutbl ) = def_br {
300- debug_assert ! ( mutbl <= max_ref_mutbl ) ;
331+ max_ref_mutbl : MutblCap ,
332+ ) -> ( Ty < ' tcx > , ByRef , MutblCap , bool ) {
333+ if let ByRef :: Yes ( Mutability :: Mut ) = def_br {
334+ debug_assert ! ( max_ref_mutbl == MutblCap :: Mut ) ;
301335 }
302336 match adjust_mode {
303337 AdjustMode :: Pass => ( expected, def_br, max_ref_mutbl, false ) ,
304- AdjustMode :: Reset => ( expected, ByRef :: No , Mutability :: Mut , false ) ,
305- AdjustMode :: ResetAndConsumeRef ( ref_pat_mutbl) => {
306- let mutbls_match = def_br == ByRef :: Yes ( ref_pat_mutbl) ;
338+ AdjustMode :: Reset => ( expected, ByRef :: No , MutblCap :: Mut , false ) ,
339+ AdjustMode :: ResetAndConsumeRef ( ref_pat_mutbl, inner_span) => {
340+ // `&` pattern eats `&mut`
341+ let mutbls_match =
342+ if let ByRef :: Yes ( def_mut) = def_br { ref_pat_mutbl <= def_mut } else { false } ;
343+
307344 if pat. span . at_least_rust_2024 ( ) && self . tcx . features ( ) . ref_pat_eat_one_layer_2024 {
345+ let max_ref_mutbl = if ref_pat_mutbl == Mutability :: Not {
346+ max_ref_mutbl. cap_mutbl_to_not ( Some ( pat. span . until ( inner_span) ) )
347+ } else {
348+ max_ref_mutbl
349+ } ;
350+
308351 if mutbls_match {
309352 debug ! ( "consuming inherited reference" ) ;
310- ( expected, ByRef :: No , cmp :: min ( max_ref_mutbl, ref_pat_mutbl ) , true )
353+ ( expected, ByRef :: No , max_ref_mutbl, true )
311354 } else {
312355 let ( new_ty, new_bm, max_ref_mutbl) = if ref_pat_mutbl == Mutability :: Mut {
313356 self . peel_off_references (
@@ -318,7 +361,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
318361 max_ref_mutbl,
319362 )
320363 } else {
321- ( expected, def_br. cap_ref_mutability ( Mutability :: Not ) , Mutability :: Not )
364+ ( expected, def_br. cap_ref_mutability ( Mutability :: Not ) , max_ref_mutbl )
322365 } ;
323366 ( new_ty, new_bm, max_ref_mutbl, false )
324367 }
@@ -385,7 +428,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
385428 // ```
386429 //
387430 // See issue #46688.
388- PatKind :: Ref ( _ , mutbl) => AdjustMode :: ResetAndConsumeRef ( * mutbl) ,
431+ PatKind :: Ref ( inner , mutbl) => AdjustMode :: ResetAndConsumeRef ( * mutbl, inner . span ) ,
389432 // A `_` pattern works with any expected type, so there's no need to do anything.
390433 PatKind :: Wild
391434 // A malformed pattern doesn't have an expected type, so let's just accept any type.
@@ -411,8 +454,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
411454 expected : Ty < ' tcx > ,
412455 mut def_br : ByRef ,
413456 max_peelable_mutability : Mutability ,
414- mut max_ref_mutability : Mutability ,
415- ) -> ( Ty < ' tcx > , ByRef , Mutability ) {
457+ mut max_ref_mutability : MutblCap ,
458+ ) -> ( Ty < ' tcx > , ByRef , MutblCap ) {
416459 let mut expected = self . try_structurally_resolve_type ( pat. span , expected) ;
417460 // Peel off as many `&` or `&mut` from the scrutinee type as possible. For example,
418461 // for `match &&&mut Some(5)` the loop runs three times, aborting when it reaches
@@ -446,9 +489,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
446489 }
447490
448491 if pat. span . at_least_rust_2024 ( ) && self . tcx . features ( ) . ref_pat_eat_one_layer_2024 {
449- def_br = def_br. cap_ref_mutability ( max_ref_mutability) ;
492+ def_br = def_br. cap_ref_mutability ( max_ref_mutability. as_mutbl ( ) ) ;
450493 if def_br == ByRef :: Yes ( Mutability :: Not ) {
451- max_ref_mutability = Mutability :: Not ;
494+ max_ref_mutability = max_ref_mutability . cap_mutbl_to_not ( None ) ;
452495 }
453496 }
454497
@@ -665,16 +708,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
665708 fn check_pat_ident (
666709 & self ,
667710 pat : & ' tcx Pat < ' tcx > ,
668- ba : BindingMode ,
711+ explicit_ba : BindingMode ,
669712 var_id : HirId ,
713+ ident : Ident ,
670714 sub : Option < & ' tcx Pat < ' tcx > > ,
671715 expected : Ty < ' tcx > ,
672716 pat_info : PatInfo < ' tcx , ' _ > ,
673717 ) -> Ty < ' tcx > {
674718 let PatInfo { binding_mode : def_br, top_info : ti, .. } = pat_info;
675719
676720 // Determine the binding mode...
677- let bm = match ba {
721+ let bm = match explicit_ba {
678722 BindingMode ( ByRef :: No , Mutability :: Mut )
679723 if !( pat. span . at_least_rust_2024 ( )
680724 && self . tcx . features ( ) . mut_preserve_binding_mode_2024 )
@@ -690,8 +734,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
690734 BindingMode ( ByRef :: No , Mutability :: Mut )
691735 }
692736 BindingMode ( ByRef :: No , mutbl) => BindingMode ( def_br, mutbl) ,
693- BindingMode ( ByRef :: Yes ( _) , _) => ba ,
737+ BindingMode ( ByRef :: Yes ( _) , _) => explicit_ba ,
694738 } ;
739+
740+ if bm. 0 == ByRef :: Yes ( Mutability :: Mut )
741+ && let MutblCap :: Not ( Some ( and_pat_span) ) = pat_info. max_ref_mutbl
742+ {
743+ let mut err = struct_span_code_err ! (
744+ self . tcx. dcx( ) ,
745+ ident. span,
746+ E0596 ,
747+ "cannot bind with `ref mut` behind an `&` pattern"
748+ ) ;
749+ err. span_help ( and_pat_span, "change this `&` pattern to an `&mut`" ) ;
750+ err. emit ( ) ;
751+ }
752+
695753 // ...and store it in a side table:
696754 self . typeck_results . borrow_mut ( ) . pat_binding_modes_mut ( ) . insert ( pat. hir_id , bm) ;
697755
@@ -717,7 +775,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
717775 // If there are multiple arms, make sure they all agree on
718776 // what the type of the binding `x` ought to be.
719777 if var_id != pat. hir_id {
720- self . check_binding_alt_eq_ty ( ba , pat. span , var_id, local_ty, ti) ;
778+ self . check_binding_alt_eq_ty ( explicit_ba , pat. span , var_id, local_ty, ti) ;
721779 }
722780
723781 if let Some ( p) = sub {
@@ -2117,7 +2175,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
21172175 } else {
21182176 let tcx = self . tcx ;
21192177 let expected = self . shallow_resolve ( expected) ;
2120- let ( ref_ty, inner_ty) = match self . check_dereferenceable ( pat. span , expected, inner) {
2178+ let ( ref_ty, inner_ty, pat_info) = match self
2179+ . check_dereferenceable ( pat. span , expected, inner)
2180+ {
21212181 Ok ( ( ) ) => {
21222182 // `demand::subtype` would be good enough, but using `eqtype` turns
21232183 // out to be equally general. See (note_1) for details.
@@ -2127,42 +2187,62 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
21272187 // the bad interactions of the given hack detailed in (note_1).
21282188 debug ! ( "check_pat_ref: expected={:?}" , expected) ;
21292189 match * expected. kind ( ) {
2130- ty:: Ref ( _, r_ty, r_mutbl) if r_mutbl == mutbl => ( expected, r_ty) ,
2190+ ty:: Ref ( _, r_ty, r_mutbl) if r_mutbl == mutbl => ( expected, r_ty, pat_info) ,
2191+
2192+ // `&` pattern eats `&mut` reference
2193+ ty:: Ref ( _, r_ty, Mutability :: Mut )
2194+ if mutbl == Mutability :: Not
2195+ && ( ( pat. span . at_least_rust_2024 ( )
2196+ && self . tcx . features ( ) . ref_pat_eat_one_layer_2024 )
2197+ || self . tcx . features ( ) . ref_pat_everywhere ) =>
2198+ {
2199+ (
2200+ expected,
2201+ r_ty,
2202+ PatInfo {
2203+ max_ref_mutbl : pat_info
2204+ . max_ref_mutbl
2205+ . cap_mutbl_to_not ( Some ( pat. span . until ( inner. span ) ) ) ,
2206+ ..pat_info
2207+ } ,
2208+ )
2209+ }
2210+
2211+ _ if consumed_inherited_ref && self . tcx . features ( ) . ref_pat_everywhere => {
2212+ // We already matched against a match-ergonmics inserted reference,
2213+ // so we don't need to match against a reference from the original type.
2214+ // Save this infor for use in lowering later
2215+ self . typeck_results
2216+ . borrow_mut ( )
2217+ . skipped_ref_pats_mut ( )
2218+ . insert ( pat. hir_id ) ;
2219+ ( expected, expected, pat_info)
2220+ }
2221+
21312222 _ => {
2132- if consumed_inherited_ref && self . tcx . features ( ) . ref_pat_everywhere {
2133- // We already matched against a match-ergonmics inserted reference,
2134- // so we don't need to match against a reference from the original type.
2135- // Save this infor for use in lowering later
2136- self . typeck_results
2137- . borrow_mut ( )
2138- . skipped_ref_pats_mut ( )
2139- . insert ( pat. hir_id ) ;
2140- ( expected, expected)
2141- } else {
2142- let inner_ty = self . next_ty_var ( inner. span ) ;
2143- let ref_ty = self . new_ref_ty ( pat. span , mutbl, inner_ty) ;
2144- debug ! ( "check_pat_ref: demanding {:?} = {:?}" , expected, ref_ty) ;
2145- let err = self . demand_eqtype_pat_diag (
2146- pat. span ,
2147- expected,
2148- ref_ty,
2149- pat_info. top_info ,
2150- ) ;
2223+ let inner_ty = self . next_ty_var ( inner. span ) ;
2224+ let ref_ty = self . new_ref_ty ( pat. span , mutbl, inner_ty) ;
2225+ debug ! ( "check_pat_ref: demanding {:?} = {:?}" , expected, ref_ty) ;
2226+ let err = self . demand_eqtype_pat_diag (
2227+ pat. span ,
2228+ expected,
2229+ ref_ty,
2230+ pat_info. top_info ,
2231+ ) ;
21512232
2152- // Look for a case like `fn foo(&foo: u32)` and suggest
2153- // `fn foo(foo: &u32)`
2154- if let Some ( mut err) = err {
2155- self . borrow_pat_suggestion ( & mut err, pat) ;
2156- err. emit ( ) ;
2157- }
2158- ( ref_ty, inner_ty)
2233+ // Look for a case like `fn foo(&foo: u32)` and suggest
2234+ // `fn foo(foo: &u32)`
2235+ if let Some ( mut err) = err {
2236+ self . borrow_pat_suggestion ( & mut err, pat) ;
2237+ err. emit ( ) ;
21592238 }
2239+ ( ref_ty, inner_ty, pat_info)
21602240 }
21612241 }
21622242 }
21632243 Err ( guar) => {
21642244 let err = Ty :: new_error ( tcx, guar) ;
2165- ( err, err)
2245+ ( err, err, pat_info )
21662246 }
21672247 } ;
21682248 self . check_pat ( inner, inner_ty, pat_info) ;
0 commit comments