Skip to content

Commit

Permalink
Allow mutable bindings inside deref patterns
Browse files Browse the repository at this point in the history
  • Loading branch information
Nadrieril committed Apr 20, 2024
1 parent 5c4909b commit 377e095
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 15 deletions.
7 changes: 4 additions & 3 deletions compiler/rustc_mir_build/src/build/matches/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>]> },
}

Expand Down Expand Up @@ -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,
},
}

Expand Down
35 changes: 26 additions & 9 deletions compiler/rustc_mir_build/src/build/matches/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"),

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_mir_build/src/build/matches/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
}
};

Expand Down
15 changes: 15 additions & 0 deletions tests/ui/pattern/deref-patterns/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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)
}

0 comments on commit 377e095

Please sign in to comment.