Skip to content

Commit 91d0b57

Browse files
committed
register DerefMut bounds for implicit mutable derefs
1 parent e4b7b3d commit 91d0b57

File tree

6 files changed

+100
-18
lines changed

6 files changed

+100
-18
lines changed

compiler/rustc_hir_typeck/src/pat.rs

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
344344
let ty = self.check_pat_inner(pat, opt_path_res, adjust_mode, expected, pat_info);
345345
self.write_ty(pat.hir_id, ty);
346346

347+
// If we implicitly inserted overloaded dereferences before matching, check the pattern to
348+
// see if the dereferenced types need `DerefMut` bounds.
349+
if let Some(derefed_tys) = self.typeck_results.borrow().pat_adjustments().get(pat.hir_id)
350+
&& derefed_tys.iter().any(|adjust| adjust.kind == PatAdjust::OverloadedDeref)
351+
{
352+
self.register_deref_mut_bounds_if_needed(
353+
pat.span,
354+
pat,
355+
derefed_tys.iter().filter_map(|adjust| match adjust.kind {
356+
PatAdjust::OverloadedDeref => Some(adjust.source),
357+
PatAdjust::BuiltinDeref => None,
358+
}),
359+
);
360+
}
361+
347362
// (note_1): In most of the cases where (note_1) is referenced
348363
// (literals and constants being the exception), we relate types
349364
// using strict equality, even though subtyping would be sufficient.
@@ -483,7 +498,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
483498
// requirement that `expected: DerefPure`.
484499
let inner_ty = self.deref_pat_target(pat.span, expected);
485500
// Once we've checked `pat`, we'll add a `DerefMut` bound if it contains any
486-
// `ref mut` bindings. TODO: implement that, then reference here.
501+
// `ref mut` bindings. See `Self::register_deref_mut_bounds_if_needed`.
487502

488503
// Preserve the smart pointer type for THIR lowering and upvar analysis.
489504
self.typeck_results
@@ -2315,20 +2330,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
23152330
) -> Ty<'tcx> {
23162331
let target_ty = self.deref_pat_target(span, expected);
23172332
self.check_pat(inner, target_ty, pat_info);
2318-
2319-
// Check if the pattern has any `ref mut` bindings, which would require
2320-
// `DerefMut` to be emitted in MIR building instead of just `Deref`.
2321-
// We do this *after* checking the inner pattern, since we want to make
2322-
// sure to apply any match-ergonomics adjustments.
2323-
// TODO: move this to a separate definition to share it with implicit deref pats
2324-
if self.typeck_results.borrow().pat_has_ref_mut_binding(inner) {
2325-
self.register_bound(
2326-
expected,
2327-
self.tcx.require_lang_item(hir::LangItem::DerefMut, Some(span)),
2328-
self.misc(span),
2329-
);
2330-
}
2331-
2333+
self.register_deref_mut_bounds_if_needed(span, inner, [expected]);
23322334
expected
23332335
}
23342336

@@ -2350,6 +2352,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
23502352
self.try_structurally_resolve_type(span, target_ty)
23512353
}
23522354

2355+
/// Check if the interior of a deref pattern (either explicit or implicit) has any `ref mut`
2356+
/// bindings, which would require `DerefMut` to be emitted in MIR building instead of just
2357+
/// `Deref`. We do this *after* checking the inner pattern, since we want to make sure to
2358+
/// account for `ref mut` binding modes inherited from implicitly dereferencing `&mut` refs.
2359+
fn register_deref_mut_bounds_if_needed(
2360+
&self,
2361+
span: Span,
2362+
inner: &'tcx Pat<'tcx>,
2363+
derefed_tys: impl IntoIterator<Item = Ty<'tcx>>,
2364+
) {
2365+
if self.typeck_results.borrow().pat_has_ref_mut_binding(inner) {
2366+
for mutably_derefed_ty in derefed_tys {
2367+
self.register_bound(
2368+
mutably_derefed_ty,
2369+
self.tcx.require_lang_item(hir::LangItem::DerefMut, Some(span)),
2370+
self.misc(span),
2371+
);
2372+
}
2373+
}
2374+
}
2375+
23532376
// Precondition: Pat is Ref(inner)
23542377
fn check_pat_ref(
23552378
&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)