Skip to content

Commit b7ff9e8

Browse files
committed
simplify &ref x to x
1 parent f65fde6 commit b7ff9e8

File tree

5 files changed

+81
-45
lines changed

5 files changed

+81
-45
lines changed

compiler/rustc_mir_build/src/thir/pattern/migration.rs

+44-16
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ struct PatDeref<'a, 'tcx> {
7474
sugg_default_mode: ByRef,
7575
/// The span that introduced the current default binding mode, or `None` for the top-level pat.
7676
default_mode_origin: Option<Span>,
77+
/// Whether this is an instance of `&ref x` which we may be able to simplify to `x`.
78+
/// Stores the HIR id of the binding pattern `ref x`, to identify it later.
79+
simplify_deref_ref: Option<HirId>,
7780
/// The next deref above this. Since we can't suggest using `&` or `&mut` on a by-ref default
7881
/// binding mode, a suggested deref's ancestors must also all be suggested.
7982
// FIXME(ref_pat_eat_one_layer_2024): By suggesting `&` and `&mut` patterns that can eat the
@@ -293,28 +296,45 @@ impl<'a, 'tcx> PatMigration<'a, 'tcx> {
293296
self.add_default_mode_label_if_needed();
294297
// This sets the default binding mode to by-value in the user's pattern, but we'll try to
295298
// suggest removing it.
296-
self.push_deref(pat_span, mutbl, PatDerefKind::Explicit { inner_span: subpat.span });
299+
let my_ix =
300+
self.push_deref(pat_span, mutbl, PatDerefKind::Explicit { inner_span: subpat.span });
297301

298302
// If this is inside a macro expansion, we won't be able to remove it.
299303
if pat_span.from_expansion() {
300304
self.add_derefs_to_suggestion(self.innermost_deref);
301305
return;
302306
}
303307

304-
// If the immediate subpattern is a binding, removing this reference pattern would change
305-
// its type. To avoid that, we include it and all its parents in that case.
308+
// If the subpattern is a binding, removing this reference pattern would change its type.
306309
// FIXME(ref_pat_eat_one_layer_2024): This assumes ref pats can't eat the binding mode
307310
// alone. Depending on the pattern typing rules in use, we can be more precise here.
308-
// TODO: if the binding is by-`ref`, we can keep only the parent derefs and remove the `ref`
309-
if matches!(subpat.kind, hir::PatKind::Binding(_, _, _, _)) {
310-
self.add_derefs_to_suggestion(self.innermost_deref);
311+
if let hir::PatKind::Binding(explicit_ba, _, _, _) = subpat.kind {
312+
if explicit_ba == BindingMode(ByRef::Yes(mutbl), Mutability::Not) {
313+
// If the binding has a `ref` modifier, we can elide both this `&` and the `ref`;
314+
// i.e. we can simplify `&ref x` to `x`, as long as all parent derefs are explicit.
315+
// NB: We don't rewrite `&ref x @ ...` to `x @ &...`, so we may end up needing to
316+
// reinstate this `&` later if the binding's subpattern requires it.
317+
// FIXME(ref_pat_eat_one_layer_2024): With RFC 3627's Rule 5, `&` patterns can match
318+
// `&mut` types; we'll have to check the mutability of the type rather than the
319+
// pattern to see whether we can elide it.
320+
self.derefs[my_ix].simplify_deref_ref = Some(subpat.hir_id);
321+
self.add_derefs_to_suggestion(self.derefs[my_ix].parent);
322+
} else {
323+
// Otherwise, we need to suggest including this `&` as well.
324+
self.add_derefs_to_suggestion(self.innermost_deref);
325+
}
311326
}
312327
}
313328

314329
/// Adds a deref to our deref-forest, so that we can track the default binding mode and
315330
/// propagate binding mode changes when we suggest adding patterns.
316331
/// See [`PatMigration::propagate_default_mode_change`].
317-
fn push_deref(&mut self, span: Span, mutbl: Mutability, kind: PatDerefKind<'a, 'tcx>) {
332+
fn push_deref(
333+
&mut self,
334+
span: Span,
335+
mutbl: Mutability,
336+
kind: PatDerefKind<'a, 'tcx>,
337+
) -> PatDerefIdx {
318338
let parent = self.innermost_deref;
319339
// Get the new default binding mode in the pattern the user wrote.
320340
let real_default_mode = match kind {
@@ -345,6 +365,7 @@ impl<'a, 'tcx> PatMigration<'a, 'tcx> {
345365
sugg_default_mode,
346366
real_default_mode,
347367
default_mode_origin,
368+
simplify_deref_ref: None,
348369
parent,
349370
next_sibling: parent.and_then(|p| self.derefs[p].first_child),
350371
first_child: None,
@@ -354,6 +375,7 @@ impl<'a, 'tcx> PatMigration<'a, 'tcx> {
354375
self.derefs[p].first_child = Some(my_ix);
355376
}
356377
self.innermost_deref = Some(my_ix);
378+
my_ix
357379
}
358380

359381
/// Restores the default binding mode after lowering a pattern that could change it.
@@ -371,7 +393,7 @@ impl<'a, 'tcx> PatMigration<'a, 'tcx> {
371393
/// Rust 2024) or if we need to suggest a binding modifier for them.
372394
pub(super) fn visit_binding(
373395
&mut self,
374-
pat_span: Span,
396+
pat: &hir::Pat<'_>,
375397
mode: BindingMode,
376398
explicit_ba: BindingMode,
377399
ident: Ident,
@@ -381,19 +403,26 @@ impl<'a, 'tcx> PatMigration<'a, 'tcx> {
381403
self.add_default_mode_label_if_needed();
382404
}
383405

384-
// If `mode` doesn't match the default, we'll need to specify its binding modifiers
385-
// explicitly, which in turn necessitates a by-move default binding mode.
406+
// As a special case, we may simplify `&ref x` to `x`; check our parent to see if we can.
407+
// The default binding mode will always be by-move in this case.
408+
let simplify_deref_ref = self.innermost_deref.is_some_and(|p| {
409+
self.derefs[p].simplify_deref_ref.is_some_and(|binding_id| pat.hir_id == binding_id)
410+
});
411+
412+
// Otherwise, if `mode` doesn't match the default, we'll need to specify its binding
413+
// modifiers explicitly, which in turn necessitates a by-move default binding mode.
386414
// Additionally, if this is inside a macro expansion, we won't be able to change it. If a
387415
// binding modifier is missing inside the expansion, there's not much we can do, but we can
388416
// avoid suggestions to elide binding modifiers that are explicit within expansions.
389-
let suggest = mode != BindingMode(self.sugg_default_mode(), Mutability::Not)
390-
|| pat_span.from_expansion() && explicit_ba != BindingMode::NONE;
417+
let suggest = !simplify_deref_ref
418+
&& mode != BindingMode(self.sugg_default_mode(), Mutability::Not)
419+
|| pat.span.from_expansion() && explicit_ba != BindingMode::NONE;
391420

392421
// Track the binding
393422
let span = if explicit_ba == BindingMode::NONE {
394-
pat_span.shrink_to_lo()
423+
pat.span.shrink_to_lo()
395424
} else {
396-
pat_span.with_hi(ident.span.lo())
425+
pat.span.with_hi(ident.span.lo())
397426
};
398427
// If we're not already suggesting an explicit binding modifier for this binding, we may
399428
// need to later, if adding reference patterns above it changes the default binding mode.
@@ -406,8 +435,6 @@ impl<'a, 'tcx> PatMigration<'a, 'tcx> {
406435
}
407436

408437
// If there was a mismatch, add `&`s to make sure we're in a by-move default binding mode.
409-
// TODO: to rewrite `&ref x` as `x`, we'll need to be able to accept a by-value default
410-
// binding mode if we remove the `&` that was eating a reference from `x`'s type.
411438
if suggest {
412439
self.add_derefs_to_suggestion(self.innermost_deref);
413440
}
@@ -425,6 +452,7 @@ impl<'a, 'tcx> PatMigration<'a, 'tcx> {
425452
}
426453
deref.suggest = true;
427454
deref.sugg_default_mode = ByRef::No;
455+
deref.simplify_deref_ref = None;
428456
opt_ix = deref.parent;
429457
let propagate_downstream_ref_mut = deref.mutbl.is_not();
430458
self.propagate_default_mode_change(ix, propagate_downstream_ref_mut);

compiler/rustc_mir_build/src/thir/pattern/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -344,8 +344,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
344344
.get(pat.hir_id)
345345
.expect("missing binding mode");
346346

347-
if let Some(s) = &mut self.rust_2024_migration {
348-
s.visit_binding(pat.span, mode, explicit_ba, ident);
347+
if let Some(m) = &mut self.rust_2024_migration {
348+
m.visit_binding(pat, mode, explicit_ba, ident);
349349
}
350350

351351
// A ref x pattern is the same node used for x, and as such it has

tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.structural2024.stderr

+20-15
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@ note: matching on a reference type with a non-reference pattern changes the defa
1010
|
1111
LL | let [&ref x] = &[&0];
1212
| ^^^^^^^^ this matches on type `&_`
13-
help: make the implied reference pattern explicit
13+
help: rewrite the pattern
14+
|
15+
LL - let [&ref x] = &[&0];
16+
LL + let &[x] = &[&0];
1417
|
15-
LL | let &[&ref x] = &[&0];
16-
| +
1718

1819
error: binding modifiers may only be written when the default binding mode is `move`
1920
--> $DIR/ref-binding-on-inh-ref-errors.rs:20:11
@@ -27,10 +28,11 @@ note: matching on a reference type with a non-reference pattern changes the defa
2728
|
2829
LL | let [&ref x] = &mut [&0];
2930
| ^^^^^^^^ this matches on type `&mut _`
30-
help: make the implied reference pattern explicit
31+
help: rewrite the pattern
32+
|
33+
LL - let [&ref x] = &mut [&0];
34+
LL + let &mut [x] = &mut [&0];
3135
|
32-
LL | let &mut [&ref x] = &mut [&0];
33-
| ++++
3436

3537
error: binding modifiers may only be written when the default binding mode is `move`
3638
--> $DIR/ref-binding-on-inh-ref-errors.rs:25:15
@@ -61,10 +63,11 @@ note: matching on a reference type with a non-reference pattern changes the defa
6163
|
6264
LL | let [&mut ref mut x] = &mut [&mut 0];
6365
| ^^^^^^^^^^^^^^^^ this matches on type `&mut _`
64-
help: make the implied reference pattern explicit
66+
help: rewrite the pattern
67+
|
68+
LL - let [&mut ref mut x] = &mut [&mut 0];
69+
LL + let &mut [x] = &mut [&mut 0];
6570
|
66-
LL | let &mut [&mut ref mut x] = &mut [&mut 0];
67-
| ++++
6871

6972
error: binding modifiers may only be written when the default binding mode is `move`
7073
--> $DIR/ref-binding-on-inh-ref-errors.rs:39:11
@@ -78,10 +81,11 @@ note: matching on a reference type with a non-reference pattern changes the defa
7881
|
7982
LL | let [&ref x] = &[&mut 0];
8083
| ^^^^^^^^ this matches on type `&_`
81-
help: make the implied reference pattern explicit
84+
help: rewrite the pattern
85+
|
86+
LL - let [&ref x] = &[&mut 0];
87+
LL + let &[x] = &[&mut 0];
8288
|
83-
LL | let &[&ref x] = &[&mut 0];
84-
| +
8589

8690
error: binding modifiers may only be written when the default binding mode is `move`
8791
--> $DIR/ref-binding-on-inh-ref-errors.rs:45:11
@@ -95,10 +99,11 @@ note: matching on a reference type with a non-reference pattern changes the defa
9599
|
96100
LL | let [&ref x] = &mut [&mut 0];
97101
| ^^^^^^^^ this matches on type `&mut _`
98-
help: make the implied reference pattern explicit
102+
help: rewrite the pattern
103+
|
104+
LL - let [&ref x] = &mut [&mut 0];
105+
LL + let &mut [x] = &mut [&mut 0];
99106
|
100-
LL | let &mut [&ref x] = &mut [&mut 0];
101-
| ++++
102107

103108
error: binding modifiers may only be written when the default binding mode is `move`
104109
--> $DIR/ref-binding-on-inh-ref-errors.rs:54:15

tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.fixed

+3-3
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ fn main() {
175175
assert_type_eq(c, &0u32);
176176

177177
// Test that we don't change bindings' types when removing reference patterns.
178-
let &Foo(&ref a) = &Foo(&0);
178+
let &Foo(a) = &Foo(&0);
179179
//~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024
180180
//~| WARN: this changes meaning in Rust 2024
181181
assert_type_eq(a, &0u32);
@@ -221,7 +221,7 @@ fn main() {
221221
assert_type_eq(b, 0u32);
222222

223223
// Test that we respect bindings' subpatterns' types when rewriting `&ref x` to `x`.
224-
let [&Foo(&ref a @ ref b), &Foo(&ref c @ d)] = [&Foo(&0); 2];
224+
let [&Foo(a @ b), &Foo(&ref c @ d)] = [&Foo(&0); 2];
225225
//~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024
226226
//~| WARN: this changes meaning in Rust 2024
227227
assert_type_eq(a, &0u32);
@@ -230,7 +230,7 @@ fn main() {
230230
assert_type_eq(d, 0u32);
231231

232232
// Test that we respect bindings' subpatterns' modes when rewriting `&ref x` to `x`.
233-
let [&Foo(&ref a @ [ref b]), &Foo(&ref c @ [d])] = [&Foo(&[0]); 2];
233+
let [&Foo(a @ [b]), &Foo(&ref c @ [d])] = [&Foo(&[0]); 2];
234234
//~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024
235235
//~| WARN: this changes meaning in Rust 2024
236236
assert_type_eq(a, &[0u32]);

tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.stderr

+12-9
Original file line numberDiff line numberDiff line change
@@ -419,10 +419,11 @@ note: matching on a reference type with a non-reference pattern changes the defa
419419
|
420420
LL | let Foo(&ref a) = &Foo(&0);
421421
| ^^^^^^^^^^^ this matches on type `&_`
422-
help: make the implied reference pattern explicit
422+
help: rewrite the pattern
423+
|
424+
LL - let Foo(&ref a) = &Foo(&0);
425+
LL + let &Foo(a) = &Foo(&0);
423426
|
424-
LL | let &Foo(&ref a) = &Foo(&0);
425-
| +
426427

427428
error: reference patterns may only be written when the default binding mode is `move` in Rust 2024
428429
--> $DIR/migration_lint.rs:184:10
@@ -537,10 +538,11 @@ note: matching on a reference type with a non-reference pattern changes the defa
537538
|
538539
LL | let [Foo(&ref a @ ref b), Foo(&ref c @ d)] = [&Foo(&0); 2];
539540
| ^^^^^^^^^^^^^^^^^^^ this matches on type `&_`
540-
help: make the implied reference patterns explicit
541+
help: rewrite the pattern
542+
|
543+
LL - let [Foo(&ref a @ ref b), Foo(&ref c @ d)] = [&Foo(&0); 2];
544+
LL + let [&Foo(a @ b), &Foo(&ref c @ d)] = [&Foo(&0); 2];
541545
|
542-
LL | let [&Foo(&ref a @ ref b), &Foo(&ref c @ d)] = [&Foo(&0); 2];
543-
| + +
544546

545547
error: reference patterns may only be written when the default binding mode is `move` in Rust 2024
546548
--> $DIR/migration_lint.rs:233:14
@@ -562,10 +564,11 @@ note: matching on a reference type with a non-reference pattern changes the defa
562564
|
563565
LL | let [Foo(&ref a @ [ref b]), Foo(&ref c @ [d])] = [&Foo(&[0]); 2];
564566
| ^^^^^^^^^^^^^^^^^^^^^ this matches on type `&_`
565-
help: make the implied reference patterns explicit
567+
help: rewrite the pattern
568+
|
569+
LL - let [Foo(&ref a @ [ref b]), Foo(&ref c @ [d])] = [&Foo(&[0]); 2];
570+
LL + let [&Foo(a @ [b]), &Foo(&ref c @ [d])] = [&Foo(&[0]); 2];
566571
|
567-
LL | let [&Foo(&ref a @ [ref b]), &Foo(&ref c @ [d])] = [&Foo(&[0]); 2];
568-
| + +
569572

570573
error: binding modifiers may only be written when the default binding mode is `move`
571574
--> $DIR/migration_lint.rs:242:10

0 commit comments

Comments
 (0)