Allow mutable bindings inside deref patterns

This commit is contained in:
Nadrieril 2024-04-04 00:46:35 +02:00
parent 5c4909b8e1
commit 377e095371
4 changed files with 48 additions and 15 deletions

View File

@ -1163,7 +1163,7 @@ enum TestCase<'pat, 'tcx> {
Constant { value: mir::Const<'tcx> },
Range(&'pat PatRange<'tcx>),
Slice { len: usize, variable_length: bool },
Deref { temp: Place<'tcx> },
Deref { temp: Place<'tcx>, mutability: Mutability },
Or { pats: Box<[FlatPat<'pat, 'tcx>]> },
}
@ -1224,10 +1224,11 @@ enum TestKind<'tcx> {
/// Test that the length of the slice is equal to `len`.
Len { len: u64, op: BinOp },
/// Call `Deref::deref` on the value.
/// Call `Deref::deref[_mut]` on the value.
Deref {
/// Temporary to store the result of `deref()`.
/// Temporary to store the result of `deref()`/`deref_mut()`.
temp: Place<'tcx>,
mutability: Mutability,
},
}

View File

@ -42,7 +42,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
TestKind::Len { len: len as u64, op }
}
TestCase::Deref { temp } => TestKind::Deref { temp },
TestCase::Deref { temp, mutability } => TestKind::Deref { temp, mutability },
TestCase::Or { .. } => bug!("or-patterns should have already been handled"),
@ -149,7 +149,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let ref_str = self.temp(ref_str_ty, test.span);
let eq_block = self.cfg.start_new_block();
// `let ref_str: &str = <String as Deref>::deref(&place);`
self.call_deref(block, eq_block, place, ty, ref_str, test.span);
self.call_deref(
block,
eq_block,
place,
Mutability::Not,
ty,
ref_str,
test.span,
);
self.non_scalar_compare(
eq_block,
success_block,
@ -249,37 +257,46 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
);
}
TestKind::Deref { temp } => {
TestKind::Deref { temp, mutability } => {
let ty = place_ty.ty;
let target = target_block(TestBranch::Success);
self.call_deref(block, target, place, ty, temp, test.span);
self.call_deref(block, target, place, mutability, ty, temp, test.span);
}
}
}
/// Perform `let temp = <ty as Deref>::deref(&place)`.
/// or `let temp = <ty as DerefMut>::deref_mut(&mut place)`.
pub(super) fn call_deref(
&mut self,
block: BasicBlock,
target_block: BasicBlock,
place: Place<'tcx>,
mutability: Mutability,
ty: Ty<'tcx>,
temp: Place<'tcx>,
span: Span,
) {
let (trait_item, method) = match mutability {
Mutability::Not => (LangItem::Deref, sym::deref),
Mutability::Mut => (LangItem::DerefMut, sym::deref_mut),
};
let borrow_kind = super::util::ref_pat_borrow_kind(mutability);
let source_info = self.source_info(span);
let re_erased = self.tcx.lifetimes.re_erased;
let deref = self.tcx.require_lang_item(LangItem::Deref, None);
let method = trait_method(self.tcx, deref, sym::deref, [ty]);
let ref_src = self.temp(Ty::new_imm_ref(self.tcx, re_erased, ty), span);
let trait_item = self.tcx.require_lang_item(trait_item, None);
let method = trait_method(self.tcx, trait_item, method, [ty]);
let ref_src = self.temp(Ty::new_ref(self.tcx, re_erased, ty, mutability), span);
// `let ref_src = &src_place;`
// or `let ref_src = &mut src_place;`
self.cfg.push_assign(
block,
source_info,
ref_src,
Rvalue::Ref(re_erased, BorrowKind::Shared, place),
Rvalue::Ref(re_erased, borrow_kind, place),
);
// `let temp = <Ty as Deref>::deref(ref_src);`
// or `let temp = <Ty as DerefMut>::deref_mut(ref_src);`
self.cfg.terminate(
block,
source_info,
@ -686,7 +703,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
}
(TestKind::Deref { temp: test_temp }, TestCase::Deref { temp })
(TestKind::Deref { temp: test_temp, .. }, TestCase::Deref { temp, .. })
if test_temp == temp =>
{
fully_matched = true;

View File

@ -249,15 +249,15 @@ impl<'pat, 'tcx> MatchPair<'pat, 'tcx> {
default_irrefutable()
}
PatKind::DerefPattern { ref subpattern, .. } => {
PatKind::DerefPattern { ref subpattern, mutability } => {
// Create a new temporary for each deref pattern.
// FIXME(deref_patterns): dedup temporaries to avoid multiple `deref()` calls?
let temp = cx.temp(
Ty::new_imm_ref(cx.tcx, cx.tcx.lifetimes.re_erased, subpattern.ty),
Ty::new_ref(cx.tcx, cx.tcx.lifetimes.re_erased, subpattern.ty, mutability),
pattern.span,
);
subpairs.push(MatchPair::new(PlaceBuilder::from(temp).deref(), subpattern, cx));
TestCase::Deref { temp }
TestCase::Deref { temp, mutability }
}
};

View File

@ -24,6 +24,19 @@ fn nested_vec(vecvec: Vec<Vec<u32>>) -> u32 {
}
}
fn ref_mut(val: u32) -> u32 {
let mut b = Box::new(0u32);
match &mut b {
deref!(_x) if false => unreachable!(),
deref!(x) => {
*x = val;
}
_ => unreachable!(),
}
let deref!(x) = &b else { unreachable!() };
*x
}
fn main() {
assert_eq!(simple_vec(vec![1]), 1);
assert_eq!(simple_vec(vec![1, 2]), 202);
@ -34,4 +47,6 @@ fn main() {
assert_eq!(nested_vec(vec![vec![1, 42]]), 42);
assert_eq!(nested_vec(vec![vec![1, 2, 3]]), 6);
assert_eq!(nested_vec(vec![vec![], vec![1, 2, 3]]), 1);
assert_eq!(ref_mut(42), 42)
}