@@ -50,6 +50,9 @@ struct PatDeref {
5050 suggest : bool ,
5151 /// The default binding mode for variables under this deref.
5252 default_mode : ByRef ,
53+ /// Whether this is an instance of `&ref x` which we may be able to simplify to `x`.
54+ /// Stores the HIR id of the binding pattern `ref x`, to identify it later.
55+ simplify_deref_ref : Option < HirId > ,
5356 /// The next deref above this. Since we can't suggest using `&` or `&mut` on a by-ref default
5457 /// binding mode, a suggested deref's ancestors must also all be suggested.
5558 // FIXME(ref_pattern_eat_one_layer_2024): By suggesting `&` and `&mut` patterns that can eat
@@ -215,15 +218,27 @@ impl<'a> PatMigration<'a> {
215218 mutbl : Mutability ,
216219 subpat : & ' tcx hir:: Pat < ' tcx > ,
217220 ) {
218- self . push_deref ( pat_span, mutbl, PatDerefKind :: Explicit { inner_span : subpat. span } ) ;
221+ let my_ix =
222+ self . push_deref ( pat_span, mutbl, PatDerefKind :: Explicit { inner_span : subpat. span } ) ;
219223
220- // If the immediate subpattern is a binding, removing this reference pattern would change
221- // its type. To avoid that, we include it and all its parents in that case.
224+ // If the subpattern is a binding, removing this reference pattern would change its type.
222225 // FIXME(ref_pat_eat_one_layer_2024): This assumes ref pats can't eat the binding mode
223226 // alone. Depending on the pattern typing rules in use, we can be more precise here.
224- // TODO: if the binding is by-`ref`, we can keep only the parent derefs and remove the `ref`
225- if matches ! ( subpat. kind, hir:: PatKind :: Binding ( _, _, _, _) ) {
226- self . add_derefs_to_suggestion ( self . innermost_deref ) ;
227+ if let hir:: PatKind :: Binding ( explicit_ba, _, _, _) = subpat. kind {
228+ if explicit_ba == BindingMode ( ByRef :: Yes ( mutbl) , Mutability :: Not ) {
229+ // If the binding has a `ref` modifier, we can elide both this `&` and the `ref`;
230+ // i.e. we can simplify `&ref x` to `x`, as long as all parent derefs are explicit.
231+ // NB: We don't rewrite `&ref x @ ...` to `x @ &...`, so we may end up needing to
232+ // reinstate this `&` later if the binding's subpattern requires it.
233+ // FIXME(ref_pat_eat_one_layer_2024): With RFC 3627's Rule 5, `&` patterns can match
234+ // `&mut` types; we'll have to check the mutability of the type rather than the
235+ // pattern to see whether we can elide it.
236+ self . derefs [ my_ix] . simplify_deref_ref = Some ( subpat. hir_id ) ;
237+ self . add_derefs_to_suggestion ( self . derefs [ my_ix] . parent ) ;
238+ } else {
239+ // Otherwise, we need to suggest including this `&` as well.
240+ self . add_derefs_to_suggestion ( self . innermost_deref ) ;
241+ }
227242 }
228243 }
229244
@@ -250,7 +265,7 @@ impl<'a> PatMigration<'a> {
250265
251266 /// Adds a dereference to our deref-forest, so that we can propagate binding mode changes when
252267 /// we suggest adding patterns. See [`PatMigration::propagate_default_mode_change`].
253- fn push_deref ( & mut self , span : Span , mutbl : Mutability , kind : PatDerefKind ) {
268+ fn push_deref ( & mut self , span : Span , mutbl : Mutability , kind : PatDerefKind ) -> PatDerefIdx {
254269 let parent = self . innermost_deref ;
255270 let default_ref_mutbl = match self . default_mode ( ) {
256271 ByRef :: Yes ( parent_mutbl) => Ord :: min ( mutbl, parent_mutbl) ,
@@ -261,6 +276,7 @@ impl<'a> PatMigration<'a> {
261276 mutbl,
262277 kind,
263278 suggest : false ,
279+ simplify_deref_ref : None ,
264280 parent,
265281 next_sibling : parent. and_then ( |p| self . derefs [ p] . first_child ) ,
266282 default_mode : ByRef :: Yes ( default_ref_mutbl) ,
@@ -271,6 +287,7 @@ impl<'a> PatMigration<'a> {
271287 self . derefs [ p] . first_child = Some ( my_ix) ;
272288 }
273289 self . innermost_deref = Some ( my_ix) ;
290+ my_ix
274291 }
275292
276293 /// Tracks when we leave a reference (either implicitly or explicitly derefed) while lowering.
@@ -282,22 +299,29 @@ impl<'a> PatMigration<'a> {
282299 }
283300
284301 /// Keeps track of bindings and adjusts the suggestion if necessary.
285- pub ( super ) fn visit_binding (
302+ pub ( super ) fn visit_binding < ' tcx > (
286303 & mut self ,
287- pat_span : Span ,
304+ pat : & ' tcx hir :: Pat < ' tcx > ,
288305 mode : BindingMode ,
289306 explicit_ba : BindingMode ,
290307 ident : Ident ,
291308 ) {
292- // If `mode` doesn't match the default, we'll need to specify its binding modifiers
293- // explicitly, which in turn necessitates a by-move default binding mode.
294- let suggest = mode != BindingMode ( self . default_mode ( ) , Mutability :: Not ) ;
309+ // As a special case, we may simplify `&ref x` to `x`; check our parent to see if we can.
310+ // The default binding mode will always be by-move in this case.
311+ let simplify_deref_ref = self . innermost_deref . is_some_and ( |p| {
312+ self . derefs [ p] . simplify_deref_ref . is_some_and ( |binding_id| pat. hir_id == binding_id)
313+ } ) ;
314+
315+ // Otherwise, if `mode` doesn't match the default, we'll need to specify its binding
316+ // modifiers explicitly, which in turn necessitates a by-move default binding mode.
317+ let suggest =
318+ !simplify_deref_ref && mode != BindingMode ( self . default_mode ( ) , Mutability :: Not ) ;
295319
296320 // Track the binding
297321 let span = if explicit_ba == BindingMode :: NONE {
298- pat_span . shrink_to_lo ( )
322+ pat . span . shrink_to_lo ( )
299323 } else {
300- pat_span . with_hi ( ident. span . lo ( ) )
324+ pat . span . with_hi ( ident. span . lo ( ) )
301325 } ;
302326 // If we're not already suggesting an explicit binding modifier for this binding, we may
303327 // need to later, if adding reference patterns above it changes the default binding mode.
@@ -310,8 +334,6 @@ impl<'a> PatMigration<'a> {
310334 }
311335
312336 // If there was a mismatch, add `&`s to make sure we're in a by-move default binding mode.
313- // TODO: to rewrite `&ref x` as `x`, we'll need to be able to accept a by-value default
314- // binding mode if we remove the `&` that was eating a reference from `x`'s type.
315337 if suggest {
316338 self . add_derefs_to_suggestion ( self . innermost_deref ) ;
317339 }
@@ -329,6 +351,7 @@ impl<'a> PatMigration<'a> {
329351 }
330352 deref. suggest = true ;
331353 deref. default_mode = ByRef :: No ;
354+ deref. simplify_deref_ref = None ;
332355 opt_ix = deref. parent ;
333356 let propagate_downstream_ref_mut = deref. mutbl . is_not ( ) ;
334357 self . propagate_default_mode_change ( ix, propagate_downstream_ref_mut) ;
0 commit comments