@@ -50,6 +50,9 @@ struct PatDeref {
50
50
suggest : bool ,
51
51
/// The default binding mode for variables under this deref.
52
52
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 > ,
53
56
/// The next deref above this. Since we can't suggest using `&` or `&mut` on a by-ref default
54
57
/// binding mode, a suggested deref's ancestors must also all be suggested.
55
58
// FIXME(ref_pattern_eat_one_layer_2024): By suggesting `&` and `&mut` patterns that can eat
@@ -215,15 +218,27 @@ impl<'a> PatMigration<'a> {
215
218
mutbl : Mutability ,
216
219
subpat : & ' tcx hir:: Pat < ' tcx > ,
217
220
) {
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 } ) ;
219
223
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.
222
225
// FIXME(ref_pat_eat_one_layer_2024): This assumes ref pats can't eat the binding mode
223
226
// 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
+ }
227
242
}
228
243
}
229
244
@@ -250,7 +265,7 @@ impl<'a> PatMigration<'a> {
250
265
251
266
/// Adds a dereference to our deref-forest, so that we can propagate binding mode changes when
252
267
/// 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 {
254
269
let parent = self . innermost_deref ;
255
270
let default_ref_mutbl = match self . default_mode ( ) {
256
271
ByRef :: Yes ( parent_mutbl) => Ord :: min ( mutbl, parent_mutbl) ,
@@ -261,6 +276,7 @@ impl<'a> PatMigration<'a> {
261
276
mutbl,
262
277
kind,
263
278
suggest : false ,
279
+ simplify_deref_ref : None ,
264
280
parent,
265
281
next_sibling : parent. and_then ( |p| self . derefs [ p] . first_child ) ,
266
282
default_mode : ByRef :: Yes ( default_ref_mutbl) ,
@@ -271,6 +287,7 @@ impl<'a> PatMigration<'a> {
271
287
self . derefs [ p] . first_child = Some ( my_ix) ;
272
288
}
273
289
self . innermost_deref = Some ( my_ix) ;
290
+ my_ix
274
291
}
275
292
276
293
/// Tracks when we leave a reference (either implicitly or explicitly derefed) while lowering.
@@ -282,22 +299,29 @@ impl<'a> PatMigration<'a> {
282
299
}
283
300
284
301
/// Keeps track of bindings and adjusts the suggestion if necessary.
285
- pub ( super ) fn visit_binding (
302
+ pub ( super ) fn visit_binding < ' tcx > (
286
303
& mut self ,
287
- pat_span : Span ,
304
+ pat : & ' tcx hir :: Pat < ' tcx > ,
288
305
mode : BindingMode ,
289
306
explicit_ba : BindingMode ,
290
307
ident : Ident ,
291
308
) {
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 ) ;
295
319
296
320
// Track the binding
297
321
let span = if explicit_ba == BindingMode :: NONE {
298
- pat_span . shrink_to_lo ( )
322
+ pat . span . shrink_to_lo ( )
299
323
} else {
300
- pat_span . with_hi ( ident. span . lo ( ) )
324
+ pat . span . with_hi ( ident. span . lo ( ) )
301
325
} ;
302
326
// If we're not already suggesting an explicit binding modifier for this binding, we may
303
327
// need to later, if adding reference patterns above it changes the default binding mode.
@@ -310,8 +334,6 @@ impl<'a> PatMigration<'a> {
310
334
}
311
335
312
336
// 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.
315
337
if suggest {
316
338
self . add_derefs_to_suggestion ( self . innermost_deref ) ;
317
339
}
@@ -329,6 +351,7 @@ impl<'a> PatMigration<'a> {
329
351
}
330
352
deref. suggest = true ;
331
353
deref. default_mode = ByRef :: No ;
354
+ deref. simplify_deref_ref = None ;
332
355
opt_ix = deref. parent ;
333
356
let propagate_downstream_ref_mut = deref. mutbl . is_not ( ) ;
334
357
self . propagate_default_mode_change ( ix, propagate_downstream_ref_mut) ;
0 commit comments