about summary refs log tree commit diff
path: root/src/librustc_mir/dataflow
diff options
context:
space:
mode:
authorDylan MacKenzie <ecstaticmorse@gmail.com>2019-09-17 16:25:30 -0700
committerDylan MacKenzie <ecstaticmorse@gmail.com>2019-09-28 07:06:51 -0700
commite81297d990632d29a3a82ecbfc78dbc4e6017994 (patch)
tree7d5cbec99e1a72fb37b5f0e98a3c8ca914f99317 /src/librustc_mir/dataflow
parent457c3aa6722cd8d2599c7f78347b2f8f586f3527 (diff)
downloadrust-e81297d990632d29a3a82ecbfc78dbc4e6017994.tar.gz
rust-e81297d990632d29a3a82ecbfc78dbc4e6017994.zip
Add analysis to determine if a local is indirectly mutable
This adds a dataflow analysis that determines if a reference to a given
`Local` or part of a `Local` that would allow mutation exists before a
point in the CFG. If no such reference exists, we know for sure that
that `Local` cannot have been mutated via an indirect assignment or
function call.
Diffstat (limited to 'src/librustc_mir/dataflow')
-rw-r--r--src/librustc_mir/dataflow/impls/indirect_mutation.rs152
-rw-r--r--src/librustc_mir/dataflow/impls/mod.rs8
-rw-r--r--src/librustc_mir/dataflow/mod.rs1
3 files changed, 157 insertions, 4 deletions
diff --git a/src/librustc_mir/dataflow/impls/indirect_mutation.rs b/src/librustc_mir/dataflow/impls/indirect_mutation.rs
new file mode 100644
index 00000000000..f45fdb21463
--- /dev/null
+++ b/src/librustc_mir/dataflow/impls/indirect_mutation.rs
@@ -0,0 +1,152 @@
+use rustc::mir::visit::Visitor;
+use rustc::mir::{self, Local, Location};
+use rustc::ty::{self, TyCtxt};
+use rustc_data_structures::bit_set::BitSet;
+use syntax_pos::DUMMY_SP;
+
+use crate::dataflow::{self, GenKillSet};
+
+/// Whether a borrow to a `Local` has been created that could allow that `Local` to be mutated
+/// indirectly. This could either be a mutable reference (`&mut`) or a shared borrow if the type of
+/// that `Local` allows interior mutability.
+///
+/// If this returns `false` for a `Local` at a given `Location`, the user can assume that `Local`
+/// has not been mutated as a result of an indirect assignment (`*p = x`) or as a side-effect of a
+/// function call or drop terminator.
+#[derive(Copy, Clone)]
+pub struct IndirectlyMutableLocals<'mir, 'tcx> {
+    body: &'mir mir::Body<'tcx>,
+    tcx: TyCtxt<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+}
+
+impl<'mir, 'tcx> IndirectlyMutableLocals<'mir, 'tcx> {
+    pub fn new(
+        tcx: TyCtxt<'tcx>,
+        body: &'mir mir::Body<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+    ) -> Self {
+        IndirectlyMutableLocals { body, tcx, param_env }
+    }
+
+    fn transfer_function<'a>(
+        &self,
+        trans: &'a mut GenKillSet<Local>,
+    ) -> TransferFunction<'a, 'mir, 'tcx> {
+        TransferFunction {
+            body: self.body,
+            tcx: self.tcx,
+            param_env: self.param_env,
+            trans
+        }
+    }
+}
+
+impl<'mir, 'tcx> dataflow::BitDenotation<'tcx> for IndirectlyMutableLocals<'mir, 'tcx> {
+    type Idx = Local;
+
+    fn name() -> &'static str { "mut_borrowed_locals" }
+
+    fn bits_per_block(&self) -> usize {
+        self.body.local_decls.len()
+    }
+
+    fn start_block_effect(&self, _entry_set: &mut BitSet<Local>) {
+        // Nothing is borrowed on function entry
+    }
+
+    fn statement_effect(
+        &self,
+        trans: &mut GenKillSet<Local>,
+        loc: Location,
+    ) {
+        let stmt = &self.body[loc.block].statements[loc.statement_index];
+        self.transfer_function(trans).visit_statement(stmt, loc);
+    }
+
+    fn terminator_effect(
+        &self,
+        trans: &mut GenKillSet<Local>,
+        loc: Location,
+    ) {
+        let terminator = self.body[loc.block].terminator();
+        self.transfer_function(trans).visit_terminator(terminator, loc);
+    }
+
+    fn propagate_call_return(
+        &self,
+        _in_out: &mut BitSet<Local>,
+        _call_bb: mir::BasicBlock,
+        _dest_bb: mir::BasicBlock,
+        _dest_place: &mir::Place<'tcx>,
+    ) {
+        // Nothing to do when a call returns successfully
+    }
+}
+
+impl<'mir, 'tcx> dataflow::BottomValue for IndirectlyMutableLocals<'mir, 'tcx> {
+    // bottom = unborrowed
+    const BOTTOM_VALUE: bool = false;
+}
+
+/// A `Visitor` that defines the transfer function for `IndirectlyMutableLocals`.
+struct TransferFunction<'a, 'mir, 'tcx> {
+    trans: &'a mut GenKillSet<Local>,
+    body: &'mir mir::Body<'tcx>,
+    tcx: TyCtxt<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+}
+
+impl<'tcx> Visitor<'tcx> for TransferFunction<'_, '_, 'tcx> {
+    fn visit_rvalue(
+        &mut self,
+        rvalue: &mir::Rvalue<'tcx>,
+        location: Location,
+    ) {
+        if let mir::Rvalue::Ref(_, kind, ref borrowed_place) = *rvalue {
+            let is_mut = match kind {
+                mir::BorrowKind::Mut { .. } => true,
+
+                | mir::BorrowKind::Shared
+                | mir::BorrowKind::Shallow
+                | mir::BorrowKind::Unique
+                => {
+                    !borrowed_place
+                        .ty(self.body, self.tcx)
+                        .ty
+                        .is_freeze(self.tcx, self.param_env, DUMMY_SP)
+                }
+            };
+
+            if is_mut {
+                match borrowed_place.base {
+                    mir::PlaceBase::Local(borrowed_local) if !borrowed_place.is_indirect()
+                        => self.trans.gen(borrowed_local),
+
+                    _ => (),
+                }
+            }
+        }
+
+        self.super_rvalue(rvalue, location);
+    }
+
+
+    fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) {
+        self.super_terminator(terminator, location);
+
+        match &terminator.kind {
+            // Drop terminators borrow the location
+            mir::TerminatorKind::Drop { location: dropped_place, .. } |
+            mir::TerminatorKind::DropAndReplace { location: dropped_place, .. } => {
+                match dropped_place.base {
+                    mir::PlaceBase::Local(dropped_local) if !dropped_place.is_indirect()
+                        => self.trans.gen(dropped_local),
+
+                    _ => (),
+                }
+            }
+            _ => (),
+        }
+    }
+}
diff --git a/src/librustc_mir/dataflow/impls/mod.rs b/src/librustc_mir/dataflow/impls/mod.rs
index 69bbe087921..d669c786c09 100644
--- a/src/librustc_mir/dataflow/impls/mod.rs
+++ b/src/librustc_mir/dataflow/impls/mod.rs
@@ -18,13 +18,13 @@ use super::drop_flag_effects_for_function_entry;
 use super::drop_flag_effects_for_location;
 use super::on_lookup_result_bits;
 
-mod storage_liveness;
-
-pub use self::storage_liveness::*;
-
 mod borrowed_locals;
+mod indirect_mutation;
+mod storage_liveness;
 
 pub use self::borrowed_locals::*;
+pub use self::indirect_mutation::IndirectlyMutableLocals;
+pub use self::storage_liveness::*;
 
 pub(super) mod borrows;
 
diff --git a/src/librustc_mir/dataflow/mod.rs b/src/librustc_mir/dataflow/mod.rs
index 5ab4e25b683..47eb47cf664 100644
--- a/src/librustc_mir/dataflow/mod.rs
+++ b/src/librustc_mir/dataflow/mod.rs
@@ -23,6 +23,7 @@ pub use self::impls::DefinitelyInitializedPlaces;
 pub use self::impls::EverInitializedPlaces;
 pub use self::impls::borrows::Borrows;
 pub use self::impls::HaveBeenBorrowedLocals;
+pub use self::impls::IndirectlyMutableLocals;
 pub use self::at_location::{FlowAtLocation, FlowsAtLocation};
 pub(crate) use self::drop_flag_effects::*;