about summary refs log tree commit diff
path: root/compiler/rustc_mir_build/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_mir_build/src')
-rw-r--r--compiler/rustc_mir_build/src/build/expr/as_place.rs2
-rw-r--r--compiler/rustc_mir_build/src/build/matches/mod.rs129
-rw-r--r--compiler/rustc_mir_build/src/build/matches/test.rs101
-rw-r--r--compiler/rustc_mir_build/src/build/matches/util.rs161
-rw-r--r--compiler/rustc_mir_build/src/check_unsafety.rs4
-rw-r--r--compiler/rustc_mir_build/src/errors.rs4
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/mod.rs4
-rw-r--r--compiler/rustc_mir_build/src/thir/print.rs2
8 files changed, 254 insertions, 153 deletions
diff --git a/compiler/rustc_mir_build/src/build/expr/as_place.rs b/compiler/rustc_mir_build/src/build/expr/as_place.rs
index f12e25db6fc..060b328ef48 100644
--- a/compiler/rustc_mir_build/src/build/expr/as_place.rs
+++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs
@@ -685,7 +685,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                             fake_borrow_temp.into(),
                             Rvalue::Ref(
                                 tcx.lifetimes.re_erased,
-                                BorrowKind::Fake,
+                                BorrowKind::Fake(FakeBorrowKind::Shallow),
                                 Place { local: base_place.local, projection },
                             ),
                         );
diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs
index f46dceeeedf..d36c51f4472 100644
--- a/compiler/rustc_mir_build/src/build/matches/mod.rs
+++ b/compiler/rustc_mir_build/src/build/matches/mod.rs
@@ -10,10 +10,7 @@ use crate::build::scope::DropKind;
 use crate::build::ForGuard::{self, OutsideGuard, RefWithinGuard};
 use crate::build::{BlockAnd, BlockAndExtension, Builder};
 use crate::build::{GuardFrame, GuardFrameLocal, LocalsForNode};
-use rustc_data_structures::{
-    fx::{FxHashSet, FxIndexMap, FxIndexSet},
-    stack::ensure_sufficient_stack,
-};
+use rustc_data_structures::{fx::FxIndexMap, stack::ensure_sufficient_stack};
 use rustc_hir::{BindingMode, ByRef};
 use rustc_middle::middle::region;
 use rustc_middle::mir::{self, *};
@@ -211,7 +208,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
     /// 2. Create the decision tree ([Builder::lower_match_tree]).
     /// 3. Determine the fake borrows that are needed from the places that were
     ///    matched against and create the required temporaries for them
-    ///    ([Builder::calculate_fake_borrows]).
+    ///    ([util::collect_fake_borrows]).
     /// 4. Create everything else: the guards and the arms ([Builder::lower_match_arms]).
     ///
     /// ## False edges
@@ -380,12 +377,19 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         match_start_span: Span,
         match_has_guard: bool,
         candidates: &mut [&mut Candidate<'pat, 'tcx>],
-    ) -> Vec<(Place<'tcx>, Local)> {
-        // The set of places that we are creating fake borrows of. If there are
-        // no match guards then we don't need any fake borrows, so don't track
-        // them.
-        let fake_borrows = match_has_guard
-            .then(|| util::FakeBorrowCollector::collect_fake_borrows(self, candidates));
+    ) -> Vec<(Place<'tcx>, Local, FakeBorrowKind)> {
+        // The set of places that we are creating fake borrows of. If there are no match guards then
+        // we don't need any fake borrows, so don't track them.
+        let fake_borrows: Vec<(Place<'tcx>, Local, FakeBorrowKind)> = if match_has_guard {
+            util::collect_fake_borrows(
+                self,
+                candidates,
+                scrutinee_span,
+                scrutinee_place_builder.base(),
+            )
+        } else {
+            Vec::new()
+        };
 
         // See the doc comment on `match_candidates` for why we have an
         // otherwise block. Match checking will ensure this is actually
@@ -439,11 +443,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             });
         }
 
-        if let Some(ref borrows) = fake_borrows {
-            self.calculate_fake_borrows(borrows, scrutinee_span)
-        } else {
-            Vec::new()
-        }
+        fake_borrows
     }
 
     /// Lower the bindings, guards and arm bodies of a `match` expression.
@@ -459,7 +459,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         scrutinee_span: Span,
         arm_candidates: Vec<(&'_ Arm<'tcx>, Candidate<'_, 'tcx>)>,
         outer_source_info: SourceInfo,
-        fake_borrow_temps: Vec<(Place<'tcx>, Local)>,
+        fake_borrow_temps: Vec<(Place<'tcx>, Local, FakeBorrowKind)>,
     ) -> BlockAnd<()> {
         let arm_end_blocks: Vec<_> = arm_candidates
             .into_iter()
@@ -543,7 +543,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         &mut self,
         outer_source_info: SourceInfo,
         candidate: Candidate<'_, 'tcx>,
-        fake_borrow_temps: &[(Place<'tcx>, Local)],
+        fake_borrow_temps: &[(Place<'tcx>, Local, FakeBorrowKind)],
         scrutinee_span: Span,
         arm_match_scope: Option<(&Arm<'tcx>, region::Scope)>,
         storages_alive: bool,
@@ -940,7 +940,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 self.visit_primary_bindings(subpattern, pattern_user_ty.deref(), f);
             }
 
-            PatKind::DerefPattern { ref subpattern } => {
+            PatKind::DerefPattern { ref subpattern, .. } => {
                 self.visit_primary_bindings(subpattern, UserTypeProjections::none(), f);
             }
 
@@ -1165,6 +1165,7 @@ enum TestCase<'pat, 'tcx> {
     Constant { value: mir::Const<'tcx> },
     Range(&'pat PatRange<'tcx>),
     Slice { len: usize, variable_length: bool },
+    Deref { temp: Place<'tcx>, mutability: Mutability },
     Or { pats: Box<[FlatPat<'pat, 'tcx>]> },
 }
 
@@ -1224,6 +1225,13 @@ enum TestKind<'tcx> {
 
     /// Test that the length of the slice is equal to `len`.
     Len { len: u64, op: BinOp },
+
+    /// Call `Deref::deref[_mut]` on the value.
+    Deref {
+        /// Temporary to store the result of `deref()`/`deref_mut()`.
+        temp: Place<'tcx>,
+        mutability: Mutability,
+    },
 }
 
 /// A test to perform to determine which [`Candidate`] matches a value.
@@ -1905,81 +1913,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             target_blocks,
         );
     }
-
-    /// Determine the fake borrows that are needed from a set of places that
-    /// have to be stable across match guards.
-    ///
-    /// Returns a list of places that need a fake borrow and the temporary
-    /// that's used to store the fake borrow.
-    ///
-    /// Match exhaustiveness checking is not able to handle the case where the
-    /// place being matched on is mutated in the guards. We add "fake borrows"
-    /// to the guards that prevent any mutation of the place being matched.
-    /// There are a some subtleties:
-    ///
-    /// 1. Borrowing `*x` doesn't prevent assigning to `x`. If `x` is a shared
-    ///    reference, the borrow isn't even tracked. As such we have to add fake
-    ///    borrows of any prefixes of a place
-    /// 2. We don't want `match x { _ => (), }` to conflict with mutable
-    ///    borrows of `x`, so we only add fake borrows for places which are
-    ///    bound or tested by the match.
-    /// 3. We don't want the fake borrows to conflict with `ref mut` bindings,
-    ///    so we use a special BorrowKind for them.
-    /// 4. The fake borrows may be of places in inactive variants, so it would
-    ///    be UB to generate code for them. They therefore have to be removed
-    ///    by a MIR pass run after borrow checking.
-    fn calculate_fake_borrows<'b>(
-        &mut self,
-        fake_borrows: &'b FxIndexSet<Place<'tcx>>,
-        temp_span: Span,
-    ) -> Vec<(Place<'tcx>, Local)> {
-        let tcx = self.tcx;
-
-        debug!("add_fake_borrows fake_borrows = {:?}", fake_borrows);
-
-        let mut all_fake_borrows = Vec::with_capacity(fake_borrows.len());
-
-        // Insert a Shallow borrow of the prefixes of any fake borrows.
-        for place in fake_borrows {
-            let mut cursor = place.projection.as_ref();
-            while let [proj_base @ .., elem] = cursor {
-                cursor = proj_base;
-
-                if let ProjectionElem::Deref = elem {
-                    // Insert a shallow borrow after a deref. For other
-                    // projections the borrow of prefix_cursor will
-                    // conflict with any mutation of base.
-                    all_fake_borrows.push(PlaceRef { local: place.local, projection: proj_base });
-                }
-            }
-
-            all_fake_borrows.push(place.as_ref());
-        }
-
-        // Deduplicate
-        let mut dedup = FxHashSet::default();
-        all_fake_borrows.retain(|b| dedup.insert(*b));
-
-        debug!("add_fake_borrows all_fake_borrows = {:?}", all_fake_borrows);
-
-        all_fake_borrows
-            .into_iter()
-            .map(|matched_place_ref| {
-                let matched_place = Place {
-                    local: matched_place_ref.local,
-                    projection: tcx.mk_place_elems(matched_place_ref.projection),
-                };
-                let fake_borrow_deref_ty = matched_place.ty(&self.local_decls, tcx).ty;
-                let fake_borrow_ty =
-                    Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, fake_borrow_deref_ty);
-                let mut fake_borrow_temp = LocalDecl::new(fake_borrow_ty, temp_span);
-                fake_borrow_temp.local_info = ClearCrossCrate::Set(Box::new(LocalInfo::FakeBorrow));
-                let fake_borrow_temp = self.local_decls.push(fake_borrow_temp);
-
-                (matched_place, fake_borrow_temp)
-            })
-            .collect()
-    }
 }
 
 ///////////////////////////////////////////////////////////////////////////
@@ -2044,7 +1977,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         &mut self,
         candidate: Candidate<'pat, 'tcx>,
         parent_data: &[PatternExtraData<'tcx>],
-        fake_borrows: &[(Place<'tcx>, Local)],
+        fake_borrows: &[(Place<'tcx>, Local, FakeBorrowKind)],
         scrutinee_span: Span,
         arm_match_scope: Option<(&Arm<'tcx>, region::Scope)>,
         schedule_drops: bool,
@@ -2174,8 +2107,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
 
             let re_erased = tcx.lifetimes.re_erased;
             let scrutinee_source_info = self.source_info(scrutinee_span);
-            for &(place, temp) in fake_borrows {
-                let borrow = Rvalue::Ref(re_erased, BorrowKind::Fake, place);
+            for &(place, temp, kind) in fake_borrows {
+                let borrow = Rvalue::Ref(re_erased, BorrowKind::Fake(kind), place);
                 self.cfg.push_assign(block, scrutinee_source_info, Place::from(temp), borrow);
             }
 
@@ -2198,7 +2131,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             let guard_frame = self.guard_context.pop().unwrap();
             debug!("Exiting guard building context with locals: {:?}", guard_frame);
 
-            for &(_, temp) in fake_borrows {
+            for &(_, temp, _) in fake_borrows {
                 let cause = FakeReadCause::ForMatchGuard;
                 self.cfg.push_fake_read(post_guard_block, guard_end, cause, Place::from(temp));
             }
diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs
index 690879b9488..5dd478aa422 100644
--- a/compiler/rustc_mir_build/src/build/matches/test.rs
+++ b/compiler/rustc_mir_build/src/build/matches/test.rs
@@ -42,6 +42,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 TestKind::Len { len: len as u64, op }
             }
 
+            TestCase::Deref { temp, mutability } => TestKind::Deref { temp, mutability },
+
             TestCase::Or { .. } => bug!("or-patterns should have already been handled"),
 
             TestCase::Irrefutable { .. } => span_bug!(
@@ -143,34 +145,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                         );
                     }
                     let re_erased = tcx.lifetimes.re_erased;
-                    let ref_string = self.temp(Ty::new_imm_ref(tcx, re_erased, ty), test.span);
                     let ref_str_ty = Ty::new_imm_ref(tcx, re_erased, tcx.types.str_);
                     let ref_str = self.temp(ref_str_ty, test.span);
-                    let deref = tcx.require_lang_item(LangItem::Deref, None);
-                    let method = trait_method(tcx, deref, sym::deref, [ty]);
                     let eq_block = self.cfg.start_new_block();
-                    self.cfg.push_assign(
-                        block,
-                        source_info,
-                        ref_string,
-                        Rvalue::Ref(re_erased, BorrowKind::Shared, place),
-                    );
-                    self.cfg.terminate(
+                    // `let ref_str: &str = <String as Deref>::deref(&place);`
+                    self.call_deref(
                         block,
-                        source_info,
-                        TerminatorKind::Call {
-                            func: Operand::Constant(Box::new(ConstOperand {
-                                span: test.span,
-                                user_ty: None,
-                                const_: method,
-                            })),
-                            args: vec![Spanned { node: Operand::Move(ref_string), span: DUMMY_SP }],
-                            destination: ref_str,
-                            target: Some(eq_block),
-                            unwind: UnwindAction::Continue,
-                            call_source: CallSource::Misc,
-                            fn_span: source_info.span,
-                        },
+                        eq_block,
+                        place,
+                        Mutability::Not,
+                        ty,
+                        ref_str,
+                        test.span,
                     );
                     self.non_scalar_compare(
                         eq_block,
@@ -270,9 +256,66 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                     Operand::Move(expected),
                 );
             }
+
+            TestKind::Deref { temp, mutability } => {
+                let ty = place_ty.ty;
+                let target = target_block(TestBranch::Success);
+                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 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, 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,
+            TerminatorKind::Call {
+                func: Operand::Constant(Box::new(ConstOperand {
+                    span,
+                    user_ty: None,
+                    const_: method,
+                })),
+                args: vec![Spanned { node: Operand::Move(ref_src), span }],
+                destination: temp,
+                target: Some(target_block),
+                unwind: UnwindAction::Continue,
+                call_source: CallSource::Misc,
+                fn_span: source_info.span,
+            },
+        );
+    }
+
     /// Compare using the provided built-in comparison operator
     fn compare(
         &mut self,
@@ -660,13 +703,21 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 }
             }
 
+            (TestKind::Deref { temp: test_temp, .. }, TestCase::Deref { temp, .. })
+                if test_temp == temp =>
+            {
+                fully_matched = true;
+                Some(TestBranch::Success)
+            }
+
             (
                 TestKind::Switch { .. }
                 | TestKind::SwitchInt { .. }
                 | TestKind::If
                 | TestKind::Len { .. }
                 | TestKind::Range { .. }
-                | TestKind::Eq { .. },
+                | TestKind::Eq { .. }
+                | TestKind::Deref { .. },
                 _,
             ) => {
                 fully_matched = false;
diff --git a/compiler/rustc_mir_build/src/build/matches/util.rs b/compiler/rustc_mir_build/src/build/matches/util.rs
index d6376b7b0dc..2f9390c22a8 100644
--- a/compiler/rustc_mir_build/src/build/matches/util.rs
+++ b/compiler/rustc_mir_build/src/build/matches/util.rs
@@ -1,12 +1,13 @@
 use crate::build::expr::as_place::{PlaceBase, PlaceBuilder};
 use crate::build::matches::{Binding, Candidate, FlatPat, MatchPair, TestCase};
 use crate::build::Builder;
-use rustc_data_structures::fx::FxIndexSet;
+use rustc_data_structures::fx::FxIndexMap;
 use rustc_infer::infer::type_variable::TypeVariableOrigin;
 use rustc_middle::mir::*;
 use rustc_middle::thir::{self, *};
-use rustc_middle::ty;
 use rustc_middle::ty::TypeVisitableExt;
+use rustc_middle::ty::{self, Ty};
+use rustc_span::Span;
 
 impl<'a, 'tcx> Builder<'a, 'tcx> {
     pub(crate) fn field_match_pairs<'pat>(
@@ -249,10 +250,15 @@ impl<'pat, 'tcx> MatchPair<'pat, 'tcx> {
                 default_irrefutable()
             }
 
-            PatKind::DerefPattern { .. } => {
-                // FIXME(deref_patterns)
-                // Treat it like a wildcard for now.
-                default_irrefutable()
+            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_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, mutability }
             }
         };
 
@@ -262,19 +268,103 @@ impl<'pat, 'tcx> MatchPair<'pat, 'tcx> {
 
 pub(super) struct FakeBorrowCollector<'a, 'b, 'tcx> {
     cx: &'a mut Builder<'b, 'tcx>,
-    fake_borrows: FxIndexSet<Place<'tcx>>,
+    /// Base of the scrutinee place. Used to distinguish bindings inside the scrutinee place from
+    /// bindings inside deref patterns.
+    scrutinee_base: PlaceBase,
+    /// Store for each place the kind of borrow to take. In case of conflicts, we take the strongest
+    /// borrow (i.e. Deep > Shallow).
+    /// Invariant: for any place in `fake_borrows`, all the prefixes of this place that are
+    /// dereferences are also borrowed with the same of stronger borrow kind.
+    fake_borrows: FxIndexMap<Place<'tcx>, FakeBorrowKind>,
+}
+
+/// Determine the set of places that have to be stable across match guards.
+///
+/// Returns a list of places that need a fake borrow along with a local to store it.
+///
+/// Match exhaustiveness checking is not able to handle the case where the place being matched on is
+/// mutated in the guards. We add "fake borrows" to the guards that prevent any mutation of the
+/// place being matched. There are a some subtleties:
+///
+/// 1. Borrowing `*x` doesn't prevent assigning to `x`. If `x` is a shared reference, the borrow
+///    isn't even tracked. As such we have to add fake borrows of any prefixes of a place.
+/// 2. We don't want `match x { (Some(_), _) => (), .. }` to conflict with mutable borrows of `x.1`, so we
+///    only add fake borrows for places which are bound or tested by the match.
+/// 3. We don't want `match x { Some(_) => (), .. }` to conflict with mutable borrows of `(x as
+///    Some).0`, so the borrows are a special shallow borrow that only affects the place and not its
+///    projections.
+///    ```rust
+///    let mut x = (Some(0), true);
+///    match x {
+///        (Some(_), false) => {}
+///        _ if { if let Some(ref mut y) = x.0 { *y += 1 }; true } => {}
+///        _ => {}
+///    }
+///    ```
+/// 4. The fake borrows may be of places in inactive variants, e.g. here we need to fake borrow `x`
+///    and `(x as Some).0`, but when we reach the guard `x` may not be `Some`.
+///    ```rust
+///    let mut x = (Some(Some(0)), true);
+///    match x {
+///        (Some(Some(_)), false) => {}
+///        _ if { if let Some(Some(ref mut y)) = x.0 { *y += 1 }; true } => {}
+///        _ => {}
+///    }
+///    ```
+///    So it would be UB to generate code for the fake borrows. They therefore have to be removed by
+///    a MIR pass run after borrow checking.
+pub(super) fn collect_fake_borrows<'tcx>(
+    cx: &mut Builder<'_, 'tcx>,
+    candidates: &[&mut Candidate<'_, 'tcx>],
+    temp_span: Span,
+    scrutinee_base: PlaceBase,
+) -> Vec<(Place<'tcx>, Local, FakeBorrowKind)> {
+    let mut collector =
+        FakeBorrowCollector { cx, scrutinee_base, fake_borrows: FxIndexMap::default() };
+    for candidate in candidates.iter() {
+        collector.visit_candidate(candidate);
+    }
+    let fake_borrows = collector.fake_borrows;
+    debug!("add_fake_borrows fake_borrows = {:?}", fake_borrows);
+    let tcx = cx.tcx;
+    fake_borrows
+        .iter()
+        .map(|(matched_place, borrow_kind)| {
+            let fake_borrow_deref_ty = matched_place.ty(&cx.local_decls, tcx).ty;
+            let fake_borrow_ty =
+                Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, fake_borrow_deref_ty);
+            let mut fake_borrow_temp = LocalDecl::new(fake_borrow_ty, temp_span);
+            fake_borrow_temp.local_info = ClearCrossCrate::Set(Box::new(LocalInfo::FakeBorrow));
+            let fake_borrow_temp = cx.local_decls.push(fake_borrow_temp);
+            (*matched_place, fake_borrow_temp, *borrow_kind)
+        })
+        .collect()
 }
 
 impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> {
-    pub(super) fn collect_fake_borrows(
-        cx: &'a mut Builder<'b, 'tcx>,
-        candidates: &[&mut Candidate<'_, 'tcx>],
-    ) -> FxIndexSet<Place<'tcx>> {
-        let mut collector = Self { cx, fake_borrows: FxIndexSet::default() };
-        for candidate in candidates.iter() {
-            collector.visit_candidate(candidate);
+    // Fake borrow this place and its dereference prefixes.
+    fn fake_borrow(&mut self, place: Place<'tcx>, kind: FakeBorrowKind) {
+        if self.fake_borrows.get(&place).is_some_and(|k| *k >= kind) {
+            return;
+        }
+        self.fake_borrows.insert(place, kind);
+        // Also fake borrow the prefixes of any fake borrow.
+        self.fake_borrow_deref_prefixes(place, kind);
+    }
+
+    // Fake borrow the prefixes of this place that are dereferences.
+    fn fake_borrow_deref_prefixes(&mut self, place: Place<'tcx>, kind: FakeBorrowKind) {
+        for (place_ref, elem) in place.as_ref().iter_projections().rev() {
+            if let ProjectionElem::Deref = elem {
+                // Insert a shallow borrow after a deref. For other projections the borrow of
+                // `place_ref` will conflict with any mutation of `place.base`.
+                let place = place_ref.to_place(self.cx.tcx);
+                if self.fake_borrows.get(&place).is_some_and(|k| *k >= kind) {
+                    return;
+                }
+                self.fake_borrows.insert(place, kind);
+            }
         }
-        collector.fake_borrows
     }
 
     fn visit_candidate(&mut self, candidate: &Candidate<'_, 'tcx>) {
@@ -300,10 +390,27 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> {
             for flat_pat in pats.iter() {
                 self.visit_flat_pat(flat_pat)
             }
+        } else if matches!(match_pair.test_case, TestCase::Deref { .. }) {
+            // The subpairs of a deref pattern are all places relative to the deref temporary, so we
+            // don't fake borrow them. Problem is, if we only shallowly fake-borrowed
+            // `match_pair.place`, this would allow:
+            // ```
+            // let mut b = Box::new(false);
+            // match b {
+            //     deref!(true) => {} // not reached because `*b == false`
+            //     _ if { *b = true; false } => {} // not reached because the guard is `false`
+            //     deref!(false) => {} // not reached because the guard changed it
+            //     // UB because we reached the unreachable.
+            // }
+            // ```
+            // Hence we fake borrow using a deep borrow.
+            if let Some(place) = match_pair.place {
+                self.fake_borrow(place, FakeBorrowKind::Deep);
+            }
         } else {
             // Insert a Shallow borrow of any place that is switched on.
             if let Some(place) = match_pair.place {
-                self.fake_borrows.insert(place);
+                self.fake_borrow(place, FakeBorrowKind::Shallow);
             }
 
             for subpair in &match_pair.subpairs {
@@ -313,6 +420,14 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> {
     }
 
     fn visit_binding(&mut self, Binding { source, .. }: &Binding<'tcx>) {
+        if let PlaceBase::Local(l) = self.scrutinee_base
+            && l != source.local
+        {
+            // The base of this place is a temporary created for deref patterns. We don't emit fake
+            // borrows for these as they are not initialized in all branches.
+            return;
+        }
+
         // Insert a borrows of prefixes of places that are bound and are
         // behind a dereference projection.
         //
@@ -329,13 +444,13 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> {
         //     y if { y == 1 && (x = &2) == () } => y,
         //     _ => 3,
         // }
-        if let Some(i) = source.projection.iter().rposition(|elem| elem == ProjectionElem::Deref) {
-            let proj_base = &source.projection[..i];
-            self.fake_borrows.insert(Place {
-                local: source.local,
-                projection: self.cx.tcx.mk_place_elems(proj_base),
-            });
-        }
+        //
+        // We don't just fake borrow the whole place because this is allowed:
+        // match u {
+        //     _ if { u = true; false } => (),
+        //     x => (),
+        // }
+        self.fake_borrow_deref_prefixes(*source, FakeBorrowKind::Shallow);
     }
 }
 
diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs
index 0c1e1d59c4f..227d19c3e43 100644
--- a/compiler/rustc_mir_build/src/check_unsafety.rs
+++ b/compiler/rustc_mir_build/src/check_unsafety.rs
@@ -513,7 +513,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
                 visit::walk_expr(&mut visitor, expr);
                 if visitor.found {
                     match borrow_kind {
-                        BorrowKind::Fake | BorrowKind::Shared
+                        BorrowKind::Fake(_) | BorrowKind::Shared
                             if !self.thir[arg].ty.is_freeze(self.tcx, self.param_env) =>
                         {
                             self.requires_unsafe(expr.span, BorrowOfLayoutConstrainedField)
@@ -521,7 +521,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
                         BorrowKind::Mut { .. } => {
                             self.requires_unsafe(expr.span, MutationOfLayoutConstrainedField)
                         }
-                        BorrowKind::Fake | BorrowKind::Shared => {}
+                        BorrowKind::Fake(_) | BorrowKind::Shared => {}
                     }
                 }
             }
diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs
index 9ddfb12bf76..f67113afd6d 100644
--- a/compiler/rustc_mir_build/src/errors.rs
+++ b/compiler/rustc_mir_build/src/errors.rs
@@ -423,7 +423,7 @@ impl Subdiagnostic for UnsafeNotInheritedLintNote {
     fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
         self,
         diag: &mut Diag<'_, G>,
-        _f: F,
+        _f: &F,
     ) {
         diag.span_note(self.signature_span, fluent::mir_build_unsafe_fn_safe_body);
         let body_start = self.body_span.shrink_to_lo();
@@ -871,7 +871,7 @@ impl<'tcx> Subdiagnostic for AdtDefinedHere<'tcx> {
     fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
         self,
         diag: &mut Diag<'_, G>,
-        _f: F,
+        _f: &F,
     ) {
         diag.arg("ty", self.ty);
         let mut spans = MultiSpan::from(self.adt_def_span);
diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
index bcb43a00547..5c016682d8d 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
@@ -264,7 +264,9 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
             }
 
             hir::PatKind::Deref(subpattern) => {
-                PatKind::DerefPattern { subpattern: self.lower_pattern(subpattern) }
+                let mutable = self.typeck_results.pat_has_ref_mut_binding(subpattern);
+                let mutability = if mutable { hir::Mutability::Mut } else { hir::Mutability::Not };
+                PatKind::DerefPattern { subpattern: self.lower_pattern(subpattern), mutability }
             }
             hir::PatKind::Ref(subpattern, _) | hir::PatKind::Box(subpattern) => {
                 PatKind::Deref { subpattern: self.lower_pattern(subpattern) }
diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs
index 49e48427b65..619bfbcf43d 100644
--- a/compiler/rustc_mir_build/src/thir/print.rs
+++ b/compiler/rustc_mir_build/src/thir/print.rs
@@ -688,7 +688,7 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> {
                 self.print_pat(subpattern, depth_lvl + 2);
                 print_indented!(self, "}", depth_lvl + 1);
             }
-            PatKind::DerefPattern { subpattern } => {
+            PatKind::DerefPattern { subpattern, .. } => {
                 print_indented!(self, "DerefPattern { ", depth_lvl + 1);
                 print_indented!(self, "subpattern:", depth_lvl + 2);
                 self.print_pat(subpattern, depth_lvl + 2);