]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/dataflow/impls/indirect_mutation.rs
Merge branch 'master' into rusty-hermit
[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 syntax_pos::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 {
37             body: self.body,
38             tcx: self.tcx,
39             param_env: self.param_env,
40             trans
41         }
42     }
43 }
44
45 impl<'mir, 'tcx> dataflow::BitDenotation<'tcx> for IndirectlyMutableLocals<'mir, 'tcx> {
46     type Idx = Local;
47
48     fn name() -> &'static str { "mut_borrowed_locals" }
49
50     fn bits_per_block(&self) -> usize {
51         self.body.local_decls.len()
52     }
53
54     fn start_block_effect(&self, _entry_set: &mut BitSet<Local>) {
55         // Nothing is borrowed on function entry
56     }
57
58     fn statement_effect(
59         &self,
60         trans: &mut GenKillSet<Local>,
61         loc: Location,
62     ) {
63         let stmt = &self.body[loc.block].statements[loc.statement_index];
64         self.transfer_function(trans).visit_statement(stmt, loc);
65     }
66
67     fn terminator_effect(
68         &self,
69         trans: &mut GenKillSet<Local>,
70         loc: Location,
71     ) {
72         let terminator = self.body[loc.block].terminator();
73         self.transfer_function(trans).visit_terminator(terminator, loc);
74     }
75
76     fn propagate_call_return(
77         &self,
78         _in_out: &mut BitSet<Local>,
79         _call_bb: mir::BasicBlock,
80         _dest_bb: mir::BasicBlock,
81         _dest_place: &mir::Place<'tcx>,
82     ) {
83         // Nothing to do when a call returns successfully
84     }
85 }
86
87 impl<'mir, 'tcx> dataflow::BottomValue for IndirectlyMutableLocals<'mir, 'tcx> {
88     // bottom = unborrowed
89     const BOTTOM_VALUE: bool = false;
90 }
91
92 /// A `Visitor` that defines the transfer function for `IndirectlyMutableLocals`.
93 struct TransferFunction<'a, 'mir, 'tcx> {
94     trans: &'a mut GenKillSet<Local>,
95     body: &'mir mir::Body<'tcx>,
96     tcx: TyCtxt<'tcx>,
97     param_env: ty::ParamEnv<'tcx>,
98 }
99
100 impl<'tcx> TransferFunction<'_, '_, 'tcx> {
101     /// Returns `true` if this borrow would allow mutation of the `borrowed_place`.
102     fn borrow_allows_mutation(
103         &self,
104         kind: mir::BorrowKind,
105         borrowed_place: &mir::Place<'tcx>,
106     ) -> bool {
107         match kind {
108             mir::BorrowKind::Mut { .. } => true,
109
110             | mir::BorrowKind::Shared
111             | mir::BorrowKind::Shallow
112             | mir::BorrowKind::Unique
113             => !borrowed_place
114                 .ty(self.body, self.tcx)
115                 .ty
116                 .is_freeze(self.tcx, self.param_env, DUMMY_SP),
117         }
118     }
119 }
120
121 impl<'tcx> Visitor<'tcx> for TransferFunction<'_, '_, 'tcx> {
122     fn visit_rvalue(
123         &mut self,
124         rvalue: &mir::Rvalue<'tcx>,
125         location: Location,
126     ) {
127         if let mir::Rvalue::Ref(_, kind, ref borrowed_place) = *rvalue {
128             if self.borrow_allows_mutation(kind, borrowed_place) {
129                 match borrowed_place.base {
130                     mir::PlaceBase::Local(borrowed_local) if !borrowed_place.is_indirect()
131                         => self.trans.gen(borrowed_local),
132
133                     _ => (),
134                 }
135             }
136         }
137
138         self.super_rvalue(rvalue, location);
139     }
140
141
142     fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) {
143         // This method purposely does nothing except call `super_terminator`. It exists solely to
144         // document the subtleties around drop terminators.
145
146         self.super_terminator(terminator, location);
147
148         if let mir::TerminatorKind::Drop { location: _, .. }
149              | mir::TerminatorKind::DropAndReplace { location: _, .. } = &terminator.kind
150         {
151             // Although drop terminators mutably borrow the location being dropped, that borrow
152             // cannot live beyond the drop terminator because the dropped location is invalidated.
153         }
154     }
155 }