Skip to content

Commit bde0734

Browse files
committed
simplify &ref x to x
1 parent a31ec9e commit bde0734

File tree

5 files changed

+75
-44
lines changed

5 files changed

+75
-44
lines changed

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

+39-16
Original file line numberDiff line numberDiff line change
@@ -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);

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
338338
.expect("missing binding mode");
339339

340340
if let Some(m) = &mut self.rust_2024_migration {
341-
m.visit_binding(pat.span, mode, explicit_ba, ident);
341+
m.visit_binding(pat, mode, explicit_ba, ident);
342342
}
343343

344344
// 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
@@ -5,10 +5,11 @@ LL | let [&ref x] = &[&0];
55
| ^^^ cannot override to bind by-reference when that is the implicit default
66
|
77
= note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
8-
help: make the implied reference pattern explicit
8+
help: rewrite the pattern
9+
|
10+
LL - let [&ref x] = &[&0];
11+
LL + let &[x] = &[&0];
912
|
10-
LL | let &[&ref x] = &[&0];
11-
| +
1213

1314
error: this pattern relies on behavior which may change in edition 2024
1415
--> $DIR/ref-binding-on-inh-ref-errors.rs:21:11
@@ -17,10 +18,11 @@ LL | let [&ref x] = &mut [&0];
1718
| ^^^ cannot override to bind by-reference when that is the implicit default
1819
|
1920
= note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
20-
help: make the implied reference pattern explicit
21+
help: rewrite the pattern
22+
|
23+
LL - let [&ref x] = &mut [&0];
24+
LL + let &mut [x] = &mut [&0];
2125
|
22-
LL | let &mut [&ref x] = &mut [&0];
23-
| ++++
2426

2527
error: this pattern relies on behavior which may change in edition 2024
2628
--> $DIR/ref-binding-on-inh-ref-errors.rs:27:15
@@ -41,10 +43,11 @@ LL | let [&mut ref mut x] = &mut [&mut 0];
4143
| ^^^^^^^ cannot override to bind by-reference when that is the implicit default
4244
|
4345
= note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
44-
help: make the implied reference pattern explicit
46+
help: rewrite the pattern
47+
|
48+
LL - let [&mut ref mut x] = &mut [&mut 0];
49+
LL + let &mut [x] = &mut [&mut 0];
4550
|
46-
LL | let &mut [&mut ref mut x] = &mut [&mut 0];
47-
| ++++
4851

4952
error: this pattern relies on behavior which may change in edition 2024
5053
--> $DIR/ref-binding-on-inh-ref-errors.rs:43:11
@@ -53,10 +56,11 @@ LL | let [&ref x] = &[&mut 0];
5356
| ^^^ cannot override to bind by-reference when that is the implicit default
5457
|
5558
= note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
56-
help: make the implied reference pattern explicit
59+
help: rewrite the pattern
60+
|
61+
LL - let [&ref x] = &[&mut 0];
62+
LL + let &[x] = &[&mut 0];
5763
|
58-
LL | let &[&ref x] = &[&mut 0];
59-
| +
6064

6165
error: this pattern relies on behavior which may change in edition 2024
6266
--> $DIR/ref-binding-on-inh-ref-errors.rs:50:11
@@ -65,10 +69,11 @@ LL | let [&ref x] = &mut [&mut 0];
6569
| ^^^ cannot override to bind by-reference when that is the implicit default
6670
|
6771
= note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
68-
help: make the implied reference pattern explicit
72+
help: rewrite the pattern
73+
|
74+
LL - let [&ref x] = &mut [&mut 0];
75+
LL + let &mut [x] = &mut [&mut 0];
6976
|
70-
LL | let &mut [&ref x] = &mut [&mut 0];
71-
| ++++
7277

7378
error: this pattern relies on behavior which may change in edition 2024
7479
--> $DIR/ref-binding-on-inh-ref-errors.rs:60:15

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ fn main() {
166166
assert_type_eq(c, &0u32);
167167

168168
// Test that we don't change bindings' types when removing reference patterns.
169-
let &Foo(&ref a) = &Foo(&0);
169+
let &Foo(a) = &Foo(&0);
170170
//~^ ERROR: this pattern relies on behavior which may change in edition 2024
171171
//~| WARN: this changes meaning in Rust 2024
172172
assert_type_eq(a, &0u32);
@@ -212,7 +212,7 @@ fn main() {
212212
assert_type_eq(b, 0u32);
213213

214214
// Test that we respect bindings' subpatterns' types when rewriting `&ref x` to `x`.
215-
let [&Foo(&ref a @ ref b), &Foo(&ref c @ d)] = [&Foo(&0); 2];
215+
let [&Foo(a @ b), &Foo(&ref c @ d)] = [&Foo(&0); 2];
216216
//~^ ERROR: this pattern relies on behavior which may change in edition 2024
217217
//~| WARN: this changes meaning in Rust 2024
218218
assert_type_eq(a, &0u32);
@@ -221,7 +221,7 @@ fn main() {
221221
assert_type_eq(d, 0u32);
222222

223223
// Test that we respect bindings' subpatterns' modes 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: this pattern relies on behavior which may change in edition 2024
226226
//~| WARN: this changes meaning in Rust 2024
227227
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
@@ -274,10 +274,11 @@ LL | let Foo(&ref a) = &Foo(&0);
274274
|
275275
= warning: this changes meaning in Rust 2024
276276
= note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
277-
help: make the implied reference pattern explicit
277+
help: rewrite the pattern
278+
|
279+
LL - let Foo(&ref a) = &Foo(&0);
280+
LL + let &Foo(a) = &Foo(&0);
278281
|
279-
LL | let &Foo(&ref a) = &Foo(&0);
280-
| +
281282

282283
error: this pattern relies on behavior which may change in edition 2024
283284
--> $DIR/migration_lint.rs:175:10
@@ -357,10 +358,11 @@ LL | let [Foo(&ref a @ ref b), Foo(&ref c @ d)] = [&Foo(&0); 2];
357358
|
358359
= warning: this changes meaning in Rust 2024
359360
= note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
360-
help: make the implied reference patterns explicit
361+
help: rewrite the pattern
362+
|
363+
LL - let [Foo(&ref a @ ref b), Foo(&ref c @ d)] = [&Foo(&0); 2];
364+
LL + let [&Foo(a @ b), &Foo(&ref c @ d)] = [&Foo(&0); 2];
361365
|
362-
LL | let [&Foo(&ref a @ ref b), &Foo(&ref c @ d)] = [&Foo(&0); 2];
363-
| + +
364366

365367
error: this pattern relies on behavior which may change in edition 2024
366368
--> $DIR/migration_lint.rs:224:14
@@ -372,10 +374,11 @@ LL | let [Foo(&ref a @ [ref b]), Foo(&ref c @ [d])] = [&Foo(&[0]); 2];
372374
|
373375
= warning: this changes meaning in Rust 2024
374376
= note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
375-
help: make the implied reference patterns explicit
377+
help: rewrite the pattern
378+
|
379+
LL - let [Foo(&ref a @ [ref b]), Foo(&ref c @ [d])] = [&Foo(&[0]); 2];
380+
LL + let [&Foo(a @ [b]), &Foo(&ref c @ [d])] = [&Foo(&[0]); 2];
376381
|
377-
LL | let [&Foo(&ref a @ [ref b]), &Foo(&ref c @ [d])] = [&Foo(&[0]); 2];
378-
| + +
379382

380383
error: aborting due to 27 previous errors
381384

0 commit comments

Comments
 (0)