1
Fork 0

register DerefMut bounds for implicit mutable derefs

This commit is contained in:
dianne 2025-03-09 00:06:00 -08:00
parent e4b7b3d820
commit 91d0b579f0
6 changed files with 100 additions and 18 deletions

View file

@ -344,6 +344,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let ty = self.check_pat_inner(pat, opt_path_res, adjust_mode, expected, pat_info);
self.write_ty(pat.hir_id, ty);
// If we implicitly inserted overloaded dereferences before matching, check the pattern to
// see if the dereferenced types need `DerefMut` bounds.
if let Some(derefed_tys) = self.typeck_results.borrow().pat_adjustments().get(pat.hir_id)
&& derefed_tys.iter().any(|adjust| adjust.kind == PatAdjust::OverloadedDeref)
{
self.register_deref_mut_bounds_if_needed(
pat.span,
pat,
derefed_tys.iter().filter_map(|adjust| match adjust.kind {
PatAdjust::OverloadedDeref => Some(adjust.source),
PatAdjust::BuiltinDeref => None,
}),
);
}
// (note_1): In most of the cases where (note_1) is referenced
// (literals and constants being the exception), we relate types
// using strict equality, even though subtyping would be sufficient.
@ -483,7 +498,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// requirement that `expected: DerefPure`.
let inner_ty = self.deref_pat_target(pat.span, expected);
// Once we've checked `pat`, we'll add a `DerefMut` bound if it contains any
// `ref mut` bindings. TODO: implement that, then reference here.
// `ref mut` bindings. See `Self::register_deref_mut_bounds_if_needed`.
// Preserve the smart pointer type for THIR lowering and upvar analysis.
self.typeck_results
@ -2315,20 +2330,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
) -> Ty<'tcx> {
let target_ty = self.deref_pat_target(span, expected);
self.check_pat(inner, target_ty, pat_info);
// Check if the pattern has any `ref mut` bindings, which would require
// `DerefMut` to be emitted in MIR building instead of just `Deref`.
// We do this *after* checking the inner pattern, since we want to make
// sure to apply any match-ergonomics adjustments.
// TODO: move this to a separate definition to share it with implicit deref pats
if self.typeck_results.borrow().pat_has_ref_mut_binding(inner) {
self.register_bound(
expected,
self.tcx.require_lang_item(hir::LangItem::DerefMut, Some(span)),
self.misc(span),
);
}
self.register_deref_mut_bounds_if_needed(span, inner, [expected]);
expected
}
@ -2350,6 +2352,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.try_structurally_resolve_type(span, target_ty)
}
/// Check if the interior of a deref pattern (either explicit or implicit) has any `ref mut`
/// bindings, which would require `DerefMut` to be emitted in MIR building instead of just
/// `Deref`. We do this *after* checking the inner pattern, since we want to make sure to
/// account for `ref mut` binding modes inherited from implicitly dereferencing `&mut` refs.
fn register_deref_mut_bounds_if_needed(
&self,
span: Span,
inner: &'tcx Pat<'tcx>,
derefed_tys: impl IntoIterator<Item = Ty<'tcx>>,
) {
if self.typeck_results.borrow().pat_has_ref_mut_binding(inner) {
for mutably_derefed_ty in derefed_tys {
self.register_bound(
mutably_derefed_ty,
self.tcx.require_lang_item(hir::LangItem::DerefMut, Some(span)),
self.misc(span),
);
}
}
}
// Precondition: Pat is Ref(inner)
fn check_pat_ref(
&self,

View file

@ -51,6 +51,7 @@ fn nested_vec(vecvec: Vec<Vec<u32>>) -> u32 {
}
}
#[cfg(explicit)]
fn ref_mut(val: u32) -> u32 {
let mut b = Box::new(0u32);
match &mut b {
@ -64,6 +65,21 @@ fn ref_mut(val: u32) -> u32 {
*x
}
#[cfg(implicit)]
fn ref_mut(val: u32) -> u32 {
let mut b = Box::new((0u32,));
match &mut b {
(_x,) if false => unreachable!(),
(x,) => {
*x = val;
}
_ => unreachable!(),
}
let (x,) = &b else { unreachable!() };
*x
}
#[cfg(explicit)]
#[rustfmt::skip]
fn or_and_guard(tuple: (u32, u32)) -> u32 {
let mut sum = 0;
@ -75,6 +91,18 @@ fn or_and_guard(tuple: (u32, u32)) -> u32 {
sum
}
#[cfg(implicit)]
#[rustfmt::skip]
fn or_and_guard(tuple: (u32, u32)) -> u32 {
let mut sum = 0;
let b = Box::new(tuple);
match b {
(x, _) | (_, x) if { sum += x; false } => {},
_ => {},
}
sum
}
fn main() {
assert_eq!(simple_vec(vec![1]), 1);
assert_eq!(simple_vec(vec![1, 2]), 202);

View file

@ -11,4 +11,11 @@ fn main() {
deref!(false) => {}
_ => {},
}
match b {
true => {}
_ if { *b = true; false } => {}
//~^ ERROR cannot assign `*b` in match guard
false => {}
_ => {},
}
}

View file

@ -7,6 +7,15 @@ LL | deref!(true) => {}
LL | _ if { *b = true; false } => {}
| ^^^^^^^^^ cannot assign
error: aborting due to 1 previous error
error[E0510]: cannot assign `*b` in match guard
--> $DIR/fake_borrows.rs:16:16
|
LL | match b {
| - value is immutable in match guard
LL | true => {}
LL | _ if { *b = true; false } => {}
| ^^^^^^^^^ cannot assign
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0510`.

View file

@ -8,10 +8,19 @@ fn main() {
deref!(x) => {}
_ => {}
}
match &mut vec![1] {
[x] => {}
_ => {}
}
match &mut Rc::new(1) {
deref!(x) => {}
//~^ ERROR the trait bound `Rc<{integer}>: DerefMut` is not satisfied
_ => {}
}
match &mut Rc::new((1,)) {
(x,) => {}
//~^ ERROR the trait bound `Rc<({integer},)>: DerefMut` is not satisfied
_ => {}
}
}

View file

@ -8,13 +8,19 @@ LL | #![feature(deref_patterns)]
= note: `#[warn(incomplete_features)]` on by default
error[E0277]: the trait bound `Rc<{integer}>: DerefMut` is not satisfied
--> $DIR/ref-mut.rs:13:9
--> $DIR/ref-mut.rs:17:9
|
LL | deref!(x) => {}
| ^^^^^^^^^ the trait `DerefMut` is not implemented for `Rc<{integer}>`
|
= note: this error originates in the macro `deref` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 1 previous error; 1 warning emitted
error[E0277]: the trait bound `Rc<({integer},)>: DerefMut` is not satisfied
--> $DIR/ref-mut.rs:22:9
|
LL | (x,) => {}
| ^^^^ the trait `DerefMut` is not implemented for `Rc<({integer},)>`
error: aborting due to 2 previous errors; 1 warning emitted
For more information about this error, try `rustc --explain E0277`.