From ab5b54230ee3bad3b307ad4d5b943b43639d3b60 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Thu, 4 Apr 2024 00:46:35 +0200 Subject: [PATCH] Allow mutable bindings inside deref patterns --- .../rustc_mir_build/src/build/matches/mod.rs | 7 ++-- .../rustc_mir_build/src/build/matches/test.rs | 35 ++++++++++++++----- .../rustc_mir_build/src/build/matches/util.rs | 6 ++-- tests/ui/pattern/deref-patterns/bindings.rs | 15 ++++++++ 4 files changed, 48 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 2ad3c393ff182..dd2afea90deea 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -1168,7 +1168,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>]> }, } @@ -1229,10 +1229,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, }, } diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs index f6827fd7c5256..5dd478aa4224a 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -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 = ::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 = ::deref(&place)`. + /// or `let temp = ::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 = ::deref(ref_src);` + // or `let temp = ::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; diff --git a/compiler/rustc_mir_build/src/build/matches/util.rs b/compiler/rustc_mir_build/src/build/matches/util.rs index 9ea49b94f8a66..55002208812d5 100644 --- a/compiler/rustc_mir_build/src/build/matches/util.rs +++ b/compiler/rustc_mir_build/src/build/matches/util.rs @@ -250,15 +250,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 } } }; diff --git a/tests/ui/pattern/deref-patterns/bindings.rs b/tests/ui/pattern/deref-patterns/bindings.rs index c0c8a70dbf0dd..41be4c1a8653e 100644 --- a/tests/ui/pattern/deref-patterns/bindings.rs +++ b/tests/ui/pattern/deref-patterns/bindings.rs @@ -24,6 +24,19 @@ fn nested_vec(vecvec: Vec>) -> 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) }