about summary refs log tree commit diff
path: root/compiler/rustc_middle/src
diff options
context:
space:
mode:
authorLeón Orell Valerian Liehr <me@fmease.dev>2024-04-23 17:25:15 +0200
committerGitHub <noreply@github.com>2024-04-23 17:25:15 +0200
commit332cac2c6dbb198a725e7927d273a75ff6c6a4eb (patch)
tree1c4a7497a089b05ccba8cc1006948cf9c1546849 /compiler/rustc_middle/src
parent68939f78267f0d0d3dcb614d760b6638a33573a5 (diff)
parent726fb55ae29a778a698e1c880aa0ae46920ece88 (diff)
downloadrust-332cac2c6dbb198a725e7927d273a75ff6c6a4eb.tar.gz
rust-332cac2c6dbb198a725e7927d273a75ff6c6a4eb.zip
Rollup merge of #122598 - Nadrieril:full-derefpats, r=matthewjasper
deref patterns: lower deref patterns to MIR

This lowers deref patterns to MIR. This is a bit tricky because this is the first kind of pattern that requires storing a value in a temporary. Thanks to https://github.com/rust-lang/rust/pull/123324 false edges are no longer a problem.

The thing I'm not confident about is the handling of fake borrows. This PR ignores any fake borrows inside a deref pattern. We are guaranteed to at least fake borrow the place of the first pointer value, which could be enough, but I'm not certain.
Diffstat (limited to 'compiler/rustc_middle/src')
-rw-r--r--compiler/rustc_middle/src/mir/pretty.rs3
-rw-r--r--compiler/rustc_middle/src/mir/statement.rs9
-rw-r--r--compiler/rustc_middle/src/mir/syntax.rs71
-rw-r--r--compiler/rustc_middle/src/mir/tcx.rs2
-rw-r--r--compiler/rustc_middle/src/mir/visit.rs4
-rw-r--r--compiler/rustc_middle/src/thir.rs5
-rw-r--r--compiler/rustc_middle/src/thir/visit.rs2
-rw-r--r--compiler/rustc_middle/src/ty/typeck_results.rs2
8 files changed, 79 insertions, 19 deletions
diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs
index 7a91d7383e5..a350eb05226 100644
--- a/compiler/rustc_middle/src/mir/pretty.rs
+++ b/compiler/rustc_middle/src/mir/pretty.rs
@@ -985,7 +985,8 @@ impl<'tcx> Debug for Rvalue<'tcx> {
             Ref(region, borrow_kind, ref place) => {
                 let kind_str = match borrow_kind {
                     BorrowKind::Shared => "",
-                    BorrowKind::Fake => "fake ",
+                    BorrowKind::Fake(FakeBorrowKind::Deep) => "fake ",
+                    BorrowKind::Fake(FakeBorrowKind::Shallow) => "fake shallow ",
                     BorrowKind::Mut { .. } => "mut ",
                 };
 
diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs
index 069c8019cb2..375f1f15a39 100644
--- a/compiler/rustc_middle/src/mir/statement.rs
+++ b/compiler/rustc_middle/src/mir/statement.rs
@@ -237,6 +237,11 @@ impl<'tcx> PlaceRef<'tcx> {
     }
 
     #[inline]
+    pub fn to_place(&self, tcx: TyCtxt<'tcx>) -> Place<'tcx> {
+        Place { local: self.local, projection: tcx.mk_place_elems(self.projection) }
+    }
+
+    #[inline]
     pub fn last_projection(&self) -> Option<(PlaceRef<'tcx>, PlaceElem<'tcx>)> {
         if let &[ref proj_base @ .., elem] = self.projection {
             Some((PlaceRef { local: self.local, projection: proj_base }, elem))
@@ -446,7 +451,7 @@ impl<'tcx> Rvalue<'tcx> {
 impl BorrowKind {
     pub fn mutability(&self) -> Mutability {
         match *self {
-            BorrowKind::Shared | BorrowKind::Fake => Mutability::Not,
+            BorrowKind::Shared | BorrowKind::Fake(_) => Mutability::Not,
             BorrowKind::Mut { .. } => Mutability::Mut,
         }
     }
@@ -454,7 +459,7 @@ impl BorrowKind {
     pub fn allows_two_phase_borrow(&self) -> bool {
         match *self {
             BorrowKind::Shared
-            | BorrowKind::Fake
+            | BorrowKind::Fake(_)
             | BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::ClosureCapture } => {
                 false
             }
diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs
index db13bb9a3e8..5ddd71178ca 100644
--- a/compiler/rustc_middle/src/mir/syntax.rs
+++ b/compiler/rustc_middle/src/mir/syntax.rs
@@ -165,13 +165,16 @@ pub enum BorrowKind {
     /// Data must be immutable and is aliasable.
     Shared,
 
-    /// The immediately borrowed place must be immutable, but projections from
-    /// it don't need to be. For example, a shallow borrow of `a.b` doesn't
-    /// conflict with a mutable borrow of `a.b.c`.
+    /// An immutable, aliasable borrow that is discarded after borrow-checking. Can behave either
+    /// like a normal shared borrow or like a special shallow borrow (see [`FakeBorrowKind`]).
     ///
-    /// This is used when lowering matches: when matching on a place we want to
-    /// ensure that place have the same value from the start of the match until
-    /// an arm is selected. This prevents this code from compiling:
+    /// This is used when lowering index expressions and matches. This is used to prevent code like
+    /// the following from compiling:
+    /// ```compile_fail,E0510
+    /// let mut x: &[_] = &[[0, 1]];
+    /// let y: &[_] = &[];
+    /// let _ = x[0][{x = y; 1}];
+    /// ```
     /// ```compile_fail,E0510
     /// let mut x = &Some(0);
     /// match *x {
@@ -180,11 +183,8 @@ pub enum BorrowKind {
     ///     Some(_) => (),
     /// }
     /// ```
-    /// This can't be a shared borrow because mutably borrowing (*x as Some).0
-    /// should not prevent `if let None = x { ... }`, for example, because the
-    /// mutating `(*x as Some).0` can't affect the discriminant of `x`.
     /// We can also report errors with this kind of borrow differently.
-    Fake,
+    Fake(FakeBorrowKind),
 
     /// Data is mutable and not aliasable.
     Mut { kind: MutBorrowKind },
@@ -240,6 +240,57 @@ pub enum MutBorrowKind {
     ClosureCapture,
 }
 
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, TyEncodable, TyDecodable)]
+#[derive(Hash, HashStable)]
+pub enum FakeBorrowKind {
+    /// A shared shallow borrow. The immediately borrowed place must be immutable, but projections
+    /// from it don't need to be. For example, a shallow borrow of `a.b` doesn't conflict with a
+    /// mutable borrow of `a.b.c`.
+    ///
+    /// This is used when lowering matches: when matching on a place we want to ensure that place
+    /// have the same value from the start of the match until an arm is selected. This prevents this
+    /// code from compiling:
+    /// ```compile_fail,E0510
+    /// let mut x = &Some(0);
+    /// match *x {
+    ///     None => (),
+    ///     Some(_) if { x = &None; false } => (),
+    ///     Some(_) => (),
+    /// }
+    /// ```
+    /// This can't be a shared borrow because mutably borrowing `(*x as Some).0` should not checking
+    /// the discriminant or accessing other variants, because the mutating `(*x as Some).0` can't
+    /// affect the discriminant of `x`. E.g. the following is allowed:
+    /// ```rust
+    /// let mut x = Some(0);
+    /// match x {
+    ///     Some(_)
+    ///         if {
+    ///             if let Some(ref mut y) = x {
+    ///                 *y += 1;
+    ///             };
+    ///             true
+    ///         } => {}
+    ///     _ => {}
+    /// }
+    /// ```
+    Shallow,
+    /// A shared (deep) borrow. Data must be immutable and is aliasable.
+    ///
+    /// This is used when lowering deref patterns, where shallow borrows wouldn't prevent something
+    /// like:
+    // ```compile_fail
+    // 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.
+    // }
+    // ```
+    Deep,
+}
+
 ///////////////////////////////////////////////////////////////////////////
 // Statements
 
diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs
index abe99f3e95c..ded2b93d6a1 100644
--- a/compiler/rustc_middle/src/mir/tcx.rs
+++ b/compiler/rustc_middle/src/mir/tcx.rs
@@ -294,7 +294,7 @@ impl BorrowKind {
 
             // We have no type corresponding to a shallow borrow, so use
             // `&` as an approximation.
-            BorrowKind::Fake => hir::Mutability::Not,
+            BorrowKind::Fake(_) => hir::Mutability::Not,
         }
     }
 }
diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs
index a6d525230b0..d97abc3f190 100644
--- a/compiler/rustc_middle/src/mir/visit.rs
+++ b/compiler/rustc_middle/src/mir/visit.rs
@@ -655,7 +655,7 @@ macro_rules! make_mir_visitor {
                             BorrowKind::Shared => PlaceContext::NonMutatingUse(
                                 NonMutatingUseContext::SharedBorrow
                             ),
-                            BorrowKind::Fake => PlaceContext::NonMutatingUse(
+                            BorrowKind::Fake(_) => PlaceContext::NonMutatingUse(
                                 NonMutatingUseContext::FakeBorrow
                             ),
                             BorrowKind::Mut { .. } =>
@@ -1284,6 +1284,8 @@ pub enum NonMutatingUseContext {
     /// Shared borrow.
     SharedBorrow,
     /// A fake borrow.
+    /// FIXME: do we need to distinguish shallow and deep fake borrows? In fact, do we need to
+    /// distinguish fake and normal deep borrows?
     FakeBorrow,
     /// AddressOf for *const pointer.
     AddressOf,
diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs
index d52b1efce4b..99498fedcca 100644
--- a/compiler/rustc_middle/src/thir.rs
+++ b/compiler/rustc_middle/src/thir.rs
@@ -642,7 +642,7 @@ impl<'tcx> Pat<'tcx> {
             AscribeUserType { subpattern, .. }
             | Binding { subpattern: Some(subpattern), .. }
             | Deref { subpattern }
-            | DerefPattern { subpattern }
+            | DerefPattern { subpattern, .. }
             | InlineConstant { subpattern, .. } => subpattern.walk_(it),
             Leaf { subpatterns } | Variant { subpatterns, .. } => {
                 subpatterns.iter().for_each(|field| field.pattern.walk_(it))
@@ -760,6 +760,7 @@ pub enum PatKind<'tcx> {
     /// Deref pattern, written `box P` for now.
     DerefPattern {
         subpattern: Box<Pat<'tcx>>,
+        mutability: hir::Mutability,
     },
 
     /// One of the following:
@@ -1166,7 +1167,7 @@ impl<'tcx> fmt::Display for Pat<'tcx> {
                 }
                 write!(f, "{subpattern}")
             }
-            PatKind::DerefPattern { ref subpattern } => {
+            PatKind::DerefPattern { ref subpattern, .. } => {
                 write!(f, "deref!({subpattern})")
             }
             PatKind::Constant { value } => write!(f, "{value}"),
diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs
index e42b85530b5..f1988810437 100644
--- a/compiler/rustc_middle/src/thir/visit.rs
+++ b/compiler/rustc_middle/src/thir/visit.rs
@@ -229,7 +229,7 @@ pub fn walk_pat<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>(
     match &pat.kind {
         AscribeUserType { subpattern, ascription: _ }
         | Deref { subpattern }
-        | DerefPattern { subpattern }
+        | DerefPattern { subpattern, .. }
         | Binding { subpattern: Some(subpattern), .. } => visitor.visit_pat(subpattern),
         Binding { .. } | Wild | Never | Error(_) => {}
         Variant { subpatterns, adt_def: _, args: _, variant_index: _ } | Leaf { subpatterns } => {
diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs
index a28afcc4fb8..25bca70f102 100644
--- a/compiler/rustc_middle/src/ty/typeck_results.rs
+++ b/compiler/rustc_middle/src/ty/typeck_results.rs
@@ -451,7 +451,7 @@ impl<'tcx> TypeckResults<'tcx> {
     /// This is computed from the typeck results since we want to make
     /// sure to apply any match-ergonomics adjustments, which we cannot
     /// determine from the HIR alone.
-    pub fn pat_has_ref_mut_binding(&self, pat: &'tcx hir::Pat<'tcx>) -> bool {
+    pub fn pat_has_ref_mut_binding(&self, pat: &hir::Pat<'_>) -> bool {
         let mut has_ref_mut = false;
         pat.walk(|pat| {
             if let hir::PatKind::Binding(_, id, _, _) = pat.kind