]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/dataflow/impls/indirect_mutation.rs
38401b42b48a8a3019a06c39fa988a0477893956
[rust.git] / src / librustc_mir / dataflow / impls / indirect_mutation.rs
1 use rustc::mir::visit::Visitor;
2 use rustc::mir::{self, Local, Location};
3 use rustc::ty::{self, TyCtxt};
4 use rustc_index::bit_set::BitSet;
5 use rustc_span::DUMMY_SP;
6
7 use crate::dataflow::{self, GenKillSet};
8
9 /// Whether a borrow to a `Local` has been created that could allow that `Local` to be mutated
10 /// indirectly. This could either be a mutable reference (`&mut`) or a shared borrow if the type of
11 /// that `Local` allows interior mutability. Operations that can mutate local's indirectly include:
12 /// assignments through a pointer (`*p = 42`), function calls, drop terminators and inline assembly.
13 ///
14 /// If this returns false for a `Local` at a given statement (or terminator), that `Local` could
15 /// not possibly have been mutated indirectly prior to that statement.
16 #[derive(Copy, Clone)]
17 pub struct IndirectlyMutableLocals<'mir, 'tcx> {
18     body: &'mir mir::Body<'tcx>,
19     tcx: TyCtxt<'tcx>,
20     param_env: ty::ParamEnv<'tcx>,
21 }
22
23 impl<'mir, 'tcx> IndirectlyMutableLocals<'mir, 'tcx> {
24     pub fn new(
25         tcx: TyCtxt<'tcx>,
26         body: &'mir mir::Body<'tcx>,
27         param_env: ty::ParamEnv<'tcx>,
28     ) -> Self {
29         IndirectlyMutableLocals { body, tcx, param_env }
30     }
31
32     fn transfer_function<'a>(
33         &self,
34         trans: &'a mut GenKillSet<Local>,
35     ) -> TransferFunction<'a, 'mir, 'tcx> {
36         TransferFunction { body: self.body, tcx: self.tcx, param_env: self.param_env, trans }
37     }
38 }
39
40 impl<'mir, 'tcx> dataflow::BitDenotation<'tcx> for IndirectlyMutableLocals<'mir, 'tcx> {
41     type Idx = Local;
42
43     fn name() -> &'static str {
44         "mut_borrowed_locals"
45     }
46
47     fn bits_per_block(&self) -> usize {
48         self.body.local_decls.len()
49     }
50
51     fn start_block_effect(&self, _entry_set: &mut BitSet<Local>) {
52         // Nothing is borrowed on function entry
53     }
54
55     fn statement_effect(&self, trans: &mut GenKillSet<Local>, loc: Location) {
56         let stmt = &self.body[loc.block].statements[loc.statement_index];
57         self.transfer_function(trans).visit_statement(stmt, loc);
58     }
59
60     fn terminator_effect(&self, trans: &mut GenKillSet<Local>, loc: Location) {
61         let terminator = self.body[loc.block].terminator();
62         self.transfer_function(trans).visit_terminator(terminator, loc);
63     }
64
65     fn propagate_call_return(
66         &self,
67         _in_out: &mut BitSet<Local>,
68         _call_bb: mir::BasicBlock,
69         _dest_bb: mir::BasicBlock,
70         _dest_place: &mir::Place<'tcx>,
71     ) {
72         // Nothing to do when a call returns successfully
73     }
74 }
75
76 impl<'mir, 'tcx> dataflow::BottomValue for IndirectlyMutableLocals<'mir, 'tcx> {
77     // bottom = unborrowed
78     const BOTTOM_VALUE: bool = false;
79 }
80
81 /// A `Visitor` that defines the transfer function for `IndirectlyMutableLocals`.
82 struct TransferFunction<'a, 'mir, 'tcx> {
83     trans: &'a mut GenKillSet<Local>,
84     body: &'mir mir::Body<'tcx>,
85     tcx: TyCtxt<'tcx>,
86     param_env: ty::ParamEnv<'tcx>,
87 }
88
89 impl<'tcx> TransferFunction<'_, '_, 'tcx> {
90     /// Returns `true` if this borrow would allow mutation of the `borrowed_place`.
91     fn borrow_allows_mutation(
92         &self,
93         kind: mir::BorrowKind,
94         borrowed_place: &mir::Place<'tcx>,
95     ) -> bool {
96         match kind {
97             mir::BorrowKind::Mut { .. } => true,
98
99             mir::BorrowKind::Shared | mir::BorrowKind::Shallow | mir::BorrowKind::Unique => {
100                 !borrowed_place.ty(self.body, self.tcx).ty.is_freeze(
101                     self.tcx,
102                     self.param_env,
103                     DUMMY_SP,
104                 )
105             }
106         }
107     }
108 }
109
110 impl<'tcx> Visitor<'tcx> for TransferFunction<'_, '_, 'tcx> {
111     fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) {
112         if let mir::Rvalue::Ref(_, kind, ref borrowed_place) = *rvalue {
113             if self.borrow_allows_mutation(kind, borrowed_place) {
114                 match borrowed_place.base {
115                     mir::PlaceBase::Local(borrowed_local) if !borrowed_place.is_indirect() => {
116                         self.trans.gen(borrowed_local)
117                     }
118
119                     _ => (),
120                 }
121             }
122         }
123
124         self.super_rvalue(rvalue, location);
125     }
126
127     fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) {
128         // This method purposely does nothing except call `super_terminator`. It exists solely to
129         // document the subtleties around drop terminators.
130
131         self.super_terminator(terminator, location);
132
133         if let mir::TerminatorKind::Drop { location: _, .. }
134         | mir::TerminatorKind::DropAndReplace { location: _, .. } = &terminator.kind
135         {
136             // Although drop terminators mutably borrow the location being dropped, that borrow
137             // cannot live beyond the drop terminator because the dropped location is invalidated.
138         }
139     }
140 }