Skip to content

Commit c529678

Browse files
committed
register DerefMut bounds for implicit mutable derefs
1 parent bf722f1 commit c529678

File tree

6 files changed

+97
-18
lines changed

6 files changed

+97
-18
lines changed

compiler/rustc_hir_typeck/src/pat.rs

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
407407

408408
self.write_ty(pat.hir_id, ty);
409409

410+
// If we implicitly inserted overloaded dereferences before matching, check the pattern to
411+
// see if the dereferenced types need `DerefMut` bounds.
412+
if let Some(derefed_tys) = self.typeck_results.borrow().pat_adjustments().get(pat.hir_id)
413+
&& derefed_tys.iter().any(|ty| !ty.is_ref())
414+
{
415+
self.register_deref_mut_bounds_if_needed(
416+
pat.span,
417+
pat,
418+
derefed_tys.iter().filter(|ty| !ty.is_ref()).copied(),
419+
);
420+
}
421+
410422
// (note_1): In most of the cases where (note_1) is referenced
411423
// (literals and constants being the exception), we relate types
412424
// using strict equality, even though subtyping would be sufficient.
@@ -604,7 +616,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
604616
// requirement that `expected: DerefPure`.
605617
self.deref_pat_target(pat.span, expected)
606618
// Once we've checked `pat`, we'll add a `DerefMut` bound if it contains any
607-
// `ref mut` bindings. TODO: implement that, then reference here.
619+
// `ref mut` bindings. See `Self::register_deref_mut_bounds_if_needed`.
608620
} else {
609621
// Bail, so we get a normal type error.
610622
break;
@@ -2343,20 +2355,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
23432355
) -> Ty<'tcx> {
23442356
let target_ty = self.deref_pat_target(span, expected);
23452357
self.check_pat(inner, target_ty, pat_info);
2346-
2347-
// Check if the pattern has any `ref mut` bindings, which would require
2348-
// `DerefMut` to be emitted in MIR building instead of just `Deref`.
2349-
// We do this *after* checking the inner pattern, since we want to make
2350-
// sure to apply any match-ergonomics adjustments.
2351-
// TODO: move this to a separate definition to share it with implicit deref pats
2352-
if self.typeck_results.borrow().pat_has_ref_mut_binding(inner) {
2353-
self.register_bound(
2354-
expected,
2355-
self.tcx.require_lang_item(hir::LangItem::DerefMut, Some(span)),
2356-
self.misc(span),
2357-
);
2358-
}
2359-
2358+
self.register_deref_mut_bounds_if_needed(span, inner, [expected]);
23602359
expected
23612360
}
23622361

@@ -2378,6 +2377,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
23782377
self.try_structurally_resolve_type(span, target_ty)
23792378
}
23802379

2380+
/// Check if the interior of a deref pattern (either explicit or implicit) has any `ref mut`
2381+
/// bindings, which would require `DerefMut` to be emitted in MIR building instead of just
2382+
/// `Deref`. We do this *after* checking the inner pattern, since we want to make sure to
2383+
/// account for `ref mut` binding modes inherited from implicitly dereferencing `&mut` refs.
2384+
fn register_deref_mut_bounds_if_needed(
2385+
&self,
2386+
span: Span,
2387+
inner: &'tcx Pat<'tcx>,
2388+
derefed_tys: impl IntoIterator<Item = Ty<'tcx>>,
2389+
) {
2390+
if self.typeck_results.borrow().pat_has_ref_mut_binding(inner) {
2391+
for mutably_derefed_ty in derefed_tys {
2392+
self.register_bound(
2393+
mutably_derefed_ty,
2394+
self.tcx.require_lang_item(hir::LangItem::DerefMut, Some(span)),
2395+
self.misc(span),
2396+
);
2397+
}
2398+
}
2399+
}
2400+
23812401
// Precondition: Pat is Ref(inner)
23822402
fn check_pat_ref(
23832403
&self,

tests/ui/pattern/deref-patterns/bindings.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ fn nested_vec(vecvec: Vec<Vec<u32>>) -> u32 {
5151
}
5252
}
5353

54+
#[cfg(explicit)]
5455
fn ref_mut(val: u32) -> u32 {
5556
let mut b = Box::new(0u32);
5657
match &mut b {
@@ -64,6 +65,21 @@ fn ref_mut(val: u32) -> u32 {
6465
*x
6566
}
6667

68+
#[cfg(implicit)]
69+
fn ref_mut(val: u32) -> u32 {
70+
let mut b = Box::new((0u32,));
71+
match &mut b {
72+
(_x,) if false => unreachable!(),
73+
(x,) => {
74+
*x = val;
75+
}
76+
_ => unreachable!(),
77+
}
78+
let (x,) = &b else { unreachable!() };
79+
*x
80+
}
81+
82+
#[cfg(explicit)]
6783
#[rustfmt::skip]
6884
fn or_and_guard(tuple: (u32, u32)) -> u32 {
6985
let mut sum = 0;
@@ -75,6 +91,18 @@ fn or_and_guard(tuple: (u32, u32)) -> u32 {
7591
sum
7692
}
7793

94+
#[cfg(implicit)]
95+
#[rustfmt::skip]
96+
fn or_and_guard(tuple: (u32, u32)) -> u32 {
97+
let mut sum = 0;
98+
let b = Box::new(tuple);
99+
match b {
100+
(x, _) | (_, x) if { sum += x; false } => {},
101+
_ => {},
102+
}
103+
sum
104+
}
105+
78106
fn main() {
79107
assert_eq!(simple_vec(vec![1]), 1);
80108
assert_eq!(simple_vec(vec![1, 2]), 202);

tests/ui/pattern/deref-patterns/fake_borrows.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,11 @@ fn main() {
1111
deref!(false) => {}
1212
_ => {},
1313
}
14+
match b {
15+
true => {}
16+
_ if { *b = true; false } => {}
17+
//~^ ERROR cannot assign `*b` in match guard
18+
false => {}
19+
_ => {},
20+
}
1421
}

tests/ui/pattern/deref-patterns/fake_borrows.stderr

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,15 @@ LL | deref!(true) => {}
77
LL | _ if { *b = true; false } => {}
88
| ^^^^^^^^^ cannot assign
99

10-
error: aborting due to 1 previous error
10+
error[E0510]: cannot assign `*b` in match guard
11+
--> $DIR/fake_borrows.rs:16:16
12+
|
13+
LL | match b {
14+
| - value is immutable in match guard
15+
LL | true => {}
16+
LL | _ if { *b = true; false } => {}
17+
| ^^^^^^^^^ cannot assign
18+
19+
error: aborting due to 2 previous errors
1120

1221
For more information about this error, try `rustc --explain E0510`.

tests/ui/pattern/deref-patterns/ref-mut.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,19 @@ fn main() {
88
deref!(x) => {}
99
_ => {}
1010
}
11+
match &mut vec![1] {
12+
[x] => {}
13+
_ => {}
14+
}
1115

1216
match &mut Rc::new(1) {
1317
deref!(x) => {}
1418
//~^ ERROR the trait bound `Rc<{integer}>: DerefMut` is not satisfied
1519
_ => {}
1620
}
21+
match &mut Rc::new((1,)) {
22+
(x,) => {}
23+
//~^ ERROR the trait bound `Rc<({integer},)>: DerefMut` is not satisfied
24+
_ => {}
25+
}
1726
}

tests/ui/pattern/deref-patterns/ref-mut.stderr

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,19 @@ LL | #![feature(deref_patterns)]
88
= note: `#[warn(incomplete_features)]` on by default
99

1010
error[E0277]: the trait bound `Rc<{integer}>: DerefMut` is not satisfied
11-
--> $DIR/ref-mut.rs:13:9
11+
--> $DIR/ref-mut.rs:17:9
1212
|
1313
LL | deref!(x) => {}
1414
| ^^^^^^^^^ the trait `DerefMut` is not implemented for `Rc<{integer}>`
1515
|
1616
= note: this error originates in the macro `deref` (in Nightly builds, run with -Z macro-backtrace for more info)
1717

18-
error: aborting due to 1 previous error; 1 warning emitted
18+
error[E0277]: the trait bound `Rc<({integer},)>: DerefMut` is not satisfied
19+
--> $DIR/ref-mut.rs:22:9
20+
|
21+
LL | (x,) => {}
22+
| ^^^^ the trait `DerefMut` is not implemented for `Rc<({integer},)>`
23+
24+
error: aborting due to 2 previous errors; 1 warning emitted
1925

2026
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)