about summary refs log tree commit diff
path: root/compiler/rustc_mir_transform/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_mir_transform/src')
-rw-r--r--compiler/rustc_mir_transform/src/check_alignment.rs10
-rw-r--r--compiler/rustc_mir_transform/src/check_null.rs10
-rw-r--r--compiler/rustc_mir_transform/src/check_pointers.rs57
3 files changed, 49 insertions, 28 deletions
diff --git a/compiler/rustc_mir_transform/src/check_alignment.rs b/compiler/rustc_mir_transform/src/check_alignment.rs
index 5115583f37c..8f88613b79f 100644
--- a/compiler/rustc_mir_transform/src/check_alignment.rs
+++ b/compiler/rustc_mir_transform/src/check_alignment.rs
@@ -6,7 +6,7 @@ use rustc_middle::mir::*;
 use rustc_middle::ty::{Ty, TyCtxt};
 use rustc_session::Session;
 
-use crate::check_pointers::{BorrowCheckMode, PointerCheck, check_pointers};
+use crate::check_pointers::{BorrowedFieldProjectionMode, PointerCheck, check_pointers};
 
 pub(super) struct CheckAlignment;
 
@@ -19,15 +19,15 @@ impl<'tcx> crate::MirPass<'tcx> for CheckAlignment {
         // Skip trivially aligned place types.
         let excluded_pointees = [tcx.types.bool, tcx.types.i8, tcx.types.u8];
 
-        // We have to exclude borrows here: in `&x.field`, the exact
-        // requirement is that the final reference must be aligned, but
-        // `check_pointers` would check that `x` is aligned, which would be wrong.
+        // When checking the alignment of references to field projections (`&(*ptr).a`),
+        // we need to make sure that the reference is aligned according to the field type
+        // and not to the pointer type.
         check_pointers(
             tcx,
             body,
             &excluded_pointees,
             insert_alignment_check,
-            BorrowCheckMode::ExcludeBorrows,
+            BorrowedFieldProjectionMode::FollowProjections,
         );
     }
 
diff --git a/compiler/rustc_mir_transform/src/check_null.rs b/compiler/rustc_mir_transform/src/check_null.rs
index 543e1845e65..ad74e335bd9 100644
--- a/compiler/rustc_mir_transform/src/check_null.rs
+++ b/compiler/rustc_mir_transform/src/check_null.rs
@@ -4,7 +4,7 @@ use rustc_middle::mir::*;
 use rustc_middle::ty::{Ty, TyCtxt};
 use rustc_session::Session;
 
-use crate::check_pointers::{BorrowCheckMode, PointerCheck, check_pointers};
+use crate::check_pointers::{BorrowedFieldProjectionMode, PointerCheck, check_pointers};
 
 pub(super) struct CheckNull;
 
@@ -14,7 +14,13 @@ impl<'tcx> crate::MirPass<'tcx> for CheckNull {
     }
 
     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
-        check_pointers(tcx, body, &[], insert_null_check, BorrowCheckMode::IncludeBorrows);
+        check_pointers(
+            tcx,
+            body,
+            &[],
+            insert_null_check,
+            BorrowedFieldProjectionMode::NoFollowProjections,
+        );
     }
 
     fn is_required(&self) -> bool {
diff --git a/compiler/rustc_mir_transform/src/check_pointers.rs b/compiler/rustc_mir_transform/src/check_pointers.rs
index 2d04b621935..7e2d0e4b894 100644
--- a/compiler/rustc_mir_transform/src/check_pointers.rs
+++ b/compiler/rustc_mir_transform/src/check_pointers.rs
@@ -12,13 +12,13 @@ pub(crate) struct PointerCheck<'tcx> {
     pub(crate) assert_kind: Box<AssertKind<Operand<'tcx>>>,
 }
 
-/// Indicates whether we insert the checks for borrow places of a raw pointer.
-/// Concretely places with [MutatingUseContext::Borrow] or
-/// [NonMutatingUseContext::SharedBorrow].
+/// When checking for borrows of field projections (`&(*ptr).a`), we might want
+/// to check for the field type (type of `.a` in the example). This enum defines
+/// the variations (pass the pointer [Ty] or the field [Ty]).
 #[derive(Copy, Clone)]
-pub(crate) enum BorrowCheckMode {
-    IncludeBorrows,
-    ExcludeBorrows,
+pub(crate) enum BorrowedFieldProjectionMode {
+    FollowProjections,
+    NoFollowProjections,
 }
 
 /// Utility for adding a check for read/write on every sized, raw pointer.
@@ -27,8 +27,8 @@ pub(crate) enum BorrowCheckMode {
 /// new basic block directly before the pointer access. (Read/write accesses
 /// are determined by the `PlaceContext` of the MIR visitor.) Then calls
 /// `on_finding` to insert the actual logic for a pointer check (e.g. check for
-/// alignment). A check can choose to be inserted for (mutable) borrows of
-/// raw pointers via the `borrow_check_mode` parameter.
+/// alignment). A check can choose to follow borrows of field projections via
+/// the `field_projection_mode` parameter.
 ///
 /// This utility takes care of the right order of blocks, the only thing a
 /// caller must do in `on_finding` is:
@@ -45,7 +45,7 @@ pub(crate) fn check_pointers<'tcx, F>(
     body: &mut Body<'tcx>,
     excluded_pointees: &[Ty<'tcx>],
     on_finding: F,
-    borrow_check_mode: BorrowCheckMode,
+    field_projection_mode: BorrowedFieldProjectionMode,
 ) where
     F: Fn(
         /* tcx: */ TyCtxt<'tcx>,
@@ -82,7 +82,7 @@ pub(crate) fn check_pointers<'tcx, F>(
                 local_decls,
                 typing_env,
                 excluded_pointees,
-                borrow_check_mode,
+                field_projection_mode,
             );
             finder.visit_statement(statement, location);
 
@@ -128,7 +128,7 @@ struct PointerFinder<'a, 'tcx> {
     typing_env: ty::TypingEnv<'tcx>,
     pointers: Vec<(Place<'tcx>, Ty<'tcx>, PlaceContext)>,
     excluded_pointees: &'a [Ty<'tcx>],
-    borrow_check_mode: BorrowCheckMode,
+    field_projection_mode: BorrowedFieldProjectionMode,
 }
 
 impl<'a, 'tcx> PointerFinder<'a, 'tcx> {
@@ -137,7 +137,7 @@ impl<'a, 'tcx> PointerFinder<'a, 'tcx> {
         local_decls: &'a mut LocalDecls<'tcx>,
         typing_env: ty::TypingEnv<'tcx>,
         excluded_pointees: &'a [Ty<'tcx>],
-        borrow_check_mode: BorrowCheckMode,
+        field_projection_mode: BorrowedFieldProjectionMode,
     ) -> Self {
         PointerFinder {
             tcx,
@@ -145,7 +145,7 @@ impl<'a, 'tcx> PointerFinder<'a, 'tcx> {
             typing_env,
             excluded_pointees,
             pointers: Vec::new(),
-            borrow_check_mode,
+            field_projection_mode,
         }
     }
 
@@ -163,15 +163,14 @@ impl<'a, 'tcx> PointerFinder<'a, 'tcx> {
                 MutatingUseContext::Store
                 | MutatingUseContext::Call
                 | MutatingUseContext::Yield
-                | MutatingUseContext::Drop,
+                | MutatingUseContext::Drop
+                | MutatingUseContext::Borrow,
             ) => true,
             PlaceContext::NonMutatingUse(
-                NonMutatingUseContext::Copy | NonMutatingUseContext::Move,
+                NonMutatingUseContext::Copy
+                | NonMutatingUseContext::Move
+                | NonMutatingUseContext::SharedBorrow,
             ) => true,
-            PlaceContext::MutatingUse(MutatingUseContext::Borrow)
-            | PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow) => {
-                matches!(self.borrow_check_mode, BorrowCheckMode::IncludeBorrows)
-            }
             _ => false,
         }
     }
@@ -194,8 +193,24 @@ impl<'a, 'tcx> Visitor<'tcx> for PointerFinder<'a, 'tcx> {
             return;
         }
 
-        let pointee_ty =
-            pointer_ty.builtin_deref(true).expect("no builtin_deref for an raw pointer");
+        // If we see a borrow of a field projection, we want to pass the field Ty to the
+        // check and not the pointee Ty.
+        let pointee_ty = match self.field_projection_mode {
+            BorrowedFieldProjectionMode::FollowProjections
+                if matches!(
+                    context,
+                    PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow)
+                        | PlaceContext::MutatingUse(MutatingUseContext::Borrow)
+                ) =>
+            {
+                if let Some(PlaceElem::Field(_, ty)) = place.projection.last() {
+                    *ty
+                } else {
+                    pointer_ty.builtin_deref(true).expect("no builtin_deref for an raw pointer")
+                }
+            }
+            _ => pointer_ty.builtin_deref(true).expect("no builtin_deref for an raw pointer"),
+        };
         // Ideally we'd support this in the future, but for now we are limited to sized types.
         if !pointee_ty.is_sized(self.tcx, self.typing_env) {
             trace!("Raw pointer, but pointee is not known to be sized: {:?}", pointer_ty);