about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_hir_typeck/src/expr_use_visitor.rs51
-rw-r--r--compiler/rustc_middle/src/thir.rs7
-rw-r--r--compiler/rustc_middle/src/ty/typeck_results.rs15
-rw-r--r--compiler/rustc_mir_build/src/builder/matches/match_pair.rs11
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/mod.rs11
5 files changed, 64 insertions, 31 deletions
diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
index f5e0f01e4c5..17e13ec0a37 100644
--- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
+++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
@@ -1000,13 +1000,15 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
                     // determines whether to borrow *at the level of the deref pattern* rather than
                     // borrowing the bound place (since that inner place is inside the temporary that
                     // stores the result of calling `deref()`/`deref_mut()` so can't be captured).
+                    // Deref patterns on boxes don't borrow, so we ignore them here.
                     // HACK: this could be a fake pattern corresponding to a deref inserted by match
                     // ergonomics, in which case `pat.hir_id` will be the id of the subpattern.
-                    let mutable = self.cx.typeck_results().pat_has_ref_mut_binding(subpattern);
-                    let mutability =
-                        if mutable { hir::Mutability::Mut } else { hir::Mutability::Not };
-                    let bk = ty::BorrowKind::from_mutbl(mutability);
-                    self.delegate.borrow_mut().borrow(place, discr_place.hir_id, bk);
+                    if let hir::ByRef::Yes(mutability) =
+                        self.cx.typeck_results().deref_pat_borrow_mode(place.place.ty(), subpattern)
+                    {
+                        let bk = ty::BorrowKind::from_mutbl(mutability);
+                        self.delegate.borrow_mut().borrow(place, discr_place.hir_id, bk);
+                    }
                 }
                 PatKind::Never => {
                     // A `!` pattern always counts as an immutable read of the discriminant,
@@ -1691,18 +1693,19 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
             place_with_id = match adjust.kind {
                 adjustment::PatAdjust::BuiltinDeref => self.cat_deref(pat.hir_id, place_with_id)?,
                 adjustment::PatAdjust::OverloadedDeref => {
-                    // This adjustment corresponds to an overloaded deref; it borrows the scrutinee to
-                    // call `Deref::deref` or `DerefMut::deref_mut`. Invoke the callback before setting
-                    // `place_with_id` to the temporary storing the result of the deref.
+                    // This adjustment corresponds to an overloaded deref; unless it's on a box, it
+                    // borrows the scrutinee to call `Deref::deref` or `DerefMut::deref_mut`. Invoke
+                    // the callback before setting `place_with_id` to the temporary storing the
+                    // result of the deref.
                     // HACK(dianne): giving the callback a fake deref pattern makes sure it behaves the
-                    // same as it would if this were an explicit deref pattern.
+                    // same as it would if this were an explicit deref pattern (including for boxes).
                     op(&place_with_id, &hir::Pat { kind: PatKind::Deref(pat), ..*pat })?;
                     let target_ty = match adjusts.peek() {
                         Some(&&next_adjust) => next_adjust.source,
                         // At the end of the deref chain, we get `pat`'s scrutinee.
                         None => self.pat_ty_unadjusted(pat)?,
                     };
-                    self.pat_deref_temp(pat.hir_id, pat, target_ty)?
+                    self.pat_deref_place(pat.hir_id, place_with_id, pat, target_ty)?
                 }
             };
         }
@@ -1810,7 +1813,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
             }
             PatKind::Deref(subpat) => {
                 let ty = self.pat_ty_adjusted(subpat)?;
-                let place = self.pat_deref_temp(pat.hir_id, subpat, ty)?;
+                let place = self.pat_deref_place(pat.hir_id, place_with_id, subpat, ty)?;
                 self.cat_pattern(place, subpat, op)?;
             }
 
@@ -1863,21 +1866,27 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
         Ok(())
     }
 
-    /// Represents the place of the temp that stores the scrutinee of a deref pattern's interior.
-    fn pat_deref_temp(
+    /// Represents the place matched on by a deref pattern's interior.
+    fn pat_deref_place(
         &self,
         hir_id: HirId,
+        base_place: PlaceWithHirId<'tcx>,
         inner: &hir::Pat<'_>,
         target_ty: Ty<'tcx>,
     ) -> Result<PlaceWithHirId<'tcx>, Cx::Error> {
-        let mutable = self.cx.typeck_results().pat_has_ref_mut_binding(inner);
-        let mutability = if mutable { hir::Mutability::Mut } else { hir::Mutability::Not };
-        let re_erased = self.cx.tcx().lifetimes.re_erased;
-        let ty = Ty::new_ref(self.cx.tcx(), re_erased, target_ty, mutability);
-        // A deref pattern stores the result of `Deref::deref` or `DerefMut::deref_mut` ...
-        let base = self.cat_rvalue(hir_id, ty);
-        // ... and the inner pattern matches on the place behind that reference.
-        self.cat_deref(hir_id, base)
+        match self.cx.typeck_results().deref_pat_borrow_mode(base_place.place.ty(), inner) {
+            // Deref patterns on boxes are lowered using a built-in deref.
+            hir::ByRef::No => self.cat_deref(hir_id, base_place),
+            // For other types, we create a temporary to match on.
+            hir::ByRef::Yes(mutability) => {
+                let re_erased = self.cx.tcx().lifetimes.re_erased;
+                let ty = Ty::new_ref(self.cx.tcx(), re_erased, target_ty, mutability);
+                // A deref pattern stores the result of `Deref::deref` or `DerefMut::deref_mut` ...
+                let base = self.cat_rvalue(hir_id, ty);
+                // ... and the inner pattern matches on the place behind that reference.
+                self.cat_deref(hir_id, base)
+            }
+        }
     }
 
     fn is_multivariant_adt(&self, ty: Ty<'tcx>, span: Span) -> bool {
diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs
index c168142fb1e..086ec529f33 100644
--- a/compiler/rustc_middle/src/thir.rs
+++ b/compiler/rustc_middle/src/thir.rs
@@ -799,7 +799,12 @@ pub enum PatKind<'tcx> {
     /// Deref pattern, written `box P` for now.
     DerefPattern {
         subpattern: Box<Pat<'tcx>>,
-        mutability: hir::Mutability,
+        /// Whether the pattern scrutinee needs to be borrowed in order to call `Deref::deref` or
+        /// `DerefMut::deref_mut`, and if so, which. This is `ByRef::No` for deref patterns on
+        /// boxes; they are lowered using a built-in deref rather than a method call, thus they
+        /// don't borrow the scrutinee.
+        #[type_visitable(ignore)]
+        borrow: ByRef,
     },
 
     /// One of the following:
diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs
index 4c5c669771f..8c5827d36df 100644
--- a/compiler/rustc_middle/src/ty/typeck_results.rs
+++ b/compiler/rustc_middle/src/ty/typeck_results.rs
@@ -475,6 +475,21 @@ impl<'tcx> TypeckResults<'tcx> {
         has_ref_mut
     }
 
+    /// How should a deref pattern find the place for its inner pattern to match on?
+    ///
+    /// In most cases, if the pattern recursively contains a `ref mut` binding, we find the inner
+    /// pattern's scrutinee by calling `DerefMut::deref_mut`, and otherwise we call `Deref::deref`.
+    /// However, for boxes we can use a built-in deref instead, which doesn't borrow the scrutinee;
+    /// in this case, we return `ByRef::No`.
+    pub fn deref_pat_borrow_mode(&self, pointer_ty: Ty<'_>, inner: &hir::Pat<'_>) -> ByRef {
+        if pointer_ty.is_box() {
+            ByRef::No
+        } else {
+            let mutable = self.pat_has_ref_mut_binding(inner);
+            ByRef::Yes(if mutable { Mutability::Mut } else { Mutability::Not })
+        }
+    }
+
     /// For a given closure, returns the iterator of `ty::CapturedPlace`s that are captured
     /// by the closure.
     pub fn closure_min_captures_flattened(
diff --git a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs
index d66b38c5b00..3a7854a5e11 100644
--- a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs
+++ b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs
@@ -1,5 +1,6 @@
 use std::sync::Arc;
 
+use rustc_hir::ByRef;
 use rustc_middle::mir::*;
 use rustc_middle::thir::*;
 use rustc_middle::ty::{self, Ty, TypeVisitableExt};
@@ -260,7 +261,13 @@ impl<'tcx> MatchPairTree<'tcx> {
                 None
             }
 
-            PatKind::Deref { ref subpattern } => {
+            PatKind::Deref { ref subpattern }
+            | PatKind::DerefPattern { ref subpattern, borrow: ByRef::No } => {
+                if cfg!(debug_assertions) && matches!(pattern.kind, PatKind::DerefPattern { .. }) {
+                    // Only deref patterns on boxes can be lowered using a built-in deref.
+                    debug_assert!(pattern.ty.is_box());
+                }
+
                 MatchPairTree::for_pattern(
                     place_builder.deref(),
                     subpattern,
@@ -271,7 +278,7 @@ impl<'tcx> MatchPairTree<'tcx> {
                 None
             }
 
-            PatKind::DerefPattern { ref subpattern, mutability } => {
+            PatKind::DerefPattern { ref subpattern, borrow: ByRef::Yes(mutability) } => {
                 // Create a new temporary for each deref pattern.
                 // FIXME(deref_patterns): dedup temporaries to avoid multiple `deref()` calls?
                 let temp = cx.temp(
diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
index 8f058efdfac..8e69ff568b9 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
@@ -111,10 +111,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
             let kind = match adjust.kind {
                 PatAdjust::BuiltinDeref => PatKind::Deref { subpattern: thir_pat },
                 PatAdjust::OverloadedDeref => {
-                    let mutable = self.typeck_results.pat_has_ref_mut_binding(pat);
-                    let mutability =
-                        if mutable { hir::Mutability::Mut } else { hir::Mutability::Not };
-                    PatKind::DerefPattern { subpattern: thir_pat, mutability }
+                    let borrow = self.typeck_results.deref_pat_borrow_mode(adjust.source, pat);
+                    PatKind::DerefPattern { subpattern: thir_pat, borrow }
                 }
             };
             Box::new(Pat { span, ty: adjust.source, kind })
@@ -308,9 +306,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
             }
 
             hir::PatKind::Deref(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 }
+                let borrow = self.typeck_results.deref_pat_borrow_mode(ty, subpattern);
+                PatKind::DerefPattern { subpattern: self.lower_pattern(subpattern), borrow }
             }
             hir::PatKind::Ref(subpattern, _) => {
                 // Track the default binding mode for the Rust 2024 migration suggestion.