1 // Copyright 2012-2017 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 use borrow_check::borrow_set::{BorrowSet, BorrowData};
12 use borrow_check::place_ext::PlaceExt;
15 use rustc::mir::{self, Location, Place, Mir};
16 use rustc::ty::TyCtxt;
17 use rustc::ty::RegionVid;
19 use rustc_data_structures::bit_set::{BitSet, BitSetOperator};
20 use rustc_data_structures::fx::FxHashMap;
21 use rustc_data_structures::indexed_vec::{Idx, IndexVec};
23 use dataflow::{BitDenotation, BlockSets, InitialFlow};
24 pub use dataflow::indexes::BorrowIndex;
25 use borrow_check::nll::region_infer::RegionInferenceContext;
26 use borrow_check::nll::ToRegionVid;
30 /// `Borrows` stores the data used in the analyses that track the flow
33 /// It uniquely identifies every borrow (`Rvalue::Ref`) by a
34 /// `BorrowIndex`, and maps each such index to a `BorrowData`
35 /// describing the borrow. These indexes are used for representing the
36 /// borrows in compact bitvectors.
37 pub struct Borrows<'a, 'gcx: 'tcx, 'tcx: 'a> {
38 tcx: TyCtxt<'a, 'gcx, 'tcx>,
41 borrow_set: Rc<BorrowSet<'tcx>>,
42 borrows_out_of_scope_at_location: FxHashMap<Location, Vec<BorrowIndex>>,
44 /// NLL region inference context with which NLL queries should be resolved
45 _nonlexical_regioncx: Rc<RegionInferenceContext<'tcx>>,
55 fn precompute_borrows_out_of_scope<'tcx>(
57 regioncx: &Rc<RegionInferenceContext<'tcx>>,
58 borrows_out_of_scope_at_location: &mut FxHashMap<Location, Vec<BorrowIndex>>,
59 borrow_index: BorrowIndex,
60 borrow_region: RegionVid,
63 // We visit one BB at a time. The complication is that we may start in the
64 // middle of the first BB visited (the one containing `location`), in which
65 // case we may have to later on process the first part of that BB if there
66 // is a path back to its start.
68 // For visited BBs, we record the index of the first statement processed.
69 // (In fully processed BBs this index is 0.) Note also that we add BBs to
70 // `visited` once they are added to `stack`, before they are actually
71 // processed, because this avoids the need to look them up again on
73 let mut visited = FxHashMap::default();
74 visited.insert(location.block, location.statement_index);
76 let mut stack = vec![];
77 stack.push(StackEntry {
79 lo: location.statement_index,
80 hi: mir[location.block].statements.len(),
81 first_part_only: false,
84 while let Some(StackEntry { bb, lo, hi, first_part_only }) = stack.pop() {
85 let mut finished_early = first_part_only;
87 let location = Location { block: bb, statement_index: i };
88 // If region does not contain a point at the location, then add to list and skip
89 // successor locations.
90 if !regioncx.region_contains(borrow_region, location) {
91 debug!("borrow {:?} gets killed at {:?}", borrow_index, location);
92 borrows_out_of_scope_at_location
96 finished_early = true;
102 // Add successor BBs to the work list, if necessary.
103 let bb_data = &mir[bb];
104 assert!(hi == bb_data.statements.len());
105 for &succ_bb in bb_data.terminator.as_ref().unwrap().successors() {
106 visited.entry(succ_bb)
108 // `succ_bb` has been seen before. If it wasn't
109 // fully processed, add its first part to `stack`
112 stack.push(StackEntry {
116 first_part_only: true,
119 // And update this entry with 0, to represent the
120 // whole BB being processed.
124 // succ_bb hasn't been seen before. Add it to
125 // `stack` for processing.
126 stack.push(StackEntry {
129 hi: mir[succ_bb].statements.len(),
130 first_part_only: false,
132 // Insert 0 for this BB, to represent the whole BB
141 impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
143 tcx: TyCtxt<'a, 'gcx, 'tcx>,
145 nonlexical_regioncx: Rc<RegionInferenceContext<'tcx>>,
146 borrow_set: &Rc<BorrowSet<'tcx>>,
148 let mut borrows_out_of_scope_at_location = FxHashMap::default();
149 for (borrow_index, borrow_data) in borrow_set.borrows.iter_enumerated() {
150 let borrow_region = borrow_data.region.to_region_vid();
151 let location = borrow_set.borrows[borrow_index].reserve_location;
153 precompute_borrows_out_of_scope(mir, &nonlexical_regioncx,
154 &mut borrows_out_of_scope_at_location,
155 borrow_index, borrow_region, location);
161 borrow_set: borrow_set.clone(),
162 borrows_out_of_scope_at_location,
163 _nonlexical_regioncx: nonlexical_regioncx,
167 crate fn borrows(&self) -> &IndexVec<BorrowIndex, BorrowData<'tcx>> { &self.borrow_set.borrows }
169 pub fn location(&self, idx: BorrowIndex) -> &Location {
170 &self.borrow_set.borrows[idx].reserve_location
173 /// Add all borrows to the kill set, if those borrows are out of scope at `location`.
174 /// That means they went out of a nonlexical scope
175 fn kill_loans_out_of_scope_at_location(&self,
176 sets: &mut BlockSets<BorrowIndex>,
177 location: Location) {
178 // NOTE: The state associated with a given `location`
179 // reflects the dataflow on entry to the statement.
180 // Iterate over each of the borrows that we've precomputed
181 // to have went out of scope at this location and kill them.
183 // We are careful always to call this function *before* we
184 // set up the gen-bits for the statement or
185 // termanator. That way, if the effect of the statement or
186 // terminator *does* introduce a new loan of the same
187 // region, then setting that gen-bit will override any
188 // potential kill introduced here.
189 if let Some(indices) = self.borrows_out_of_scope_at_location.get(&location) {
190 sets.kill_all(indices);
194 fn kill_borrows_on_local(&self,
195 sets: &mut BlockSets<BorrowIndex>,
196 local: &rustc::mir::Local)
198 if let Some(borrow_indexes) = self.borrow_set.local_map.get(local) {
199 sets.kill_all(borrow_indexes);
204 impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> {
205 type Idx = BorrowIndex;
206 fn name() -> &'static str { "borrows" }
207 fn bits_per_block(&self) -> usize {
208 self.borrow_set.borrows.len() * 2
211 fn start_block_effect(&self, _entry_set: &mut BitSet<BorrowIndex>) {
212 // no borrows of code region_scopes have been taken prior to
213 // function execution, so this method has no effect on
217 fn before_statement_effect(&self,
218 sets: &mut BlockSets<BorrowIndex>,
219 location: Location) {
220 debug!("Borrows::before_statement_effect sets: {:?} location: {:?}", sets, location);
221 self.kill_loans_out_of_scope_at_location(sets, location);
224 fn statement_effect(&self, sets: &mut BlockSets<BorrowIndex>, location: Location) {
225 debug!("Borrows::statement_effect sets: {:?} location: {:?}", sets, location);
227 let block = &self.mir.basic_blocks().get(location.block).unwrap_or_else(|| {
228 panic!("could not find block at location {:?}", location);
230 let stmt = block.statements.get(location.statement_index).unwrap_or_else(|| {
231 panic!("could not find statement at location {:?}");
235 mir::StatementKind::Assign(ref lhs, ref rhs) => {
236 // Make sure there are no remaining borrows for variables
237 // that are assigned over.
238 if let Place::Local(ref local) = *lhs {
239 // FIXME: Handle the case in which we're assigning over
240 // a projection (`foo.bar`).
241 self.kill_borrows_on_local(sets, local);
244 // NOTE: if/when the Assign case is revised to inspect
245 // the assigned_place here, make sure to also
246 // re-consider the current implementations of the
247 // propagate_call_return method.
249 if let mir::Rvalue::Ref(_, _, ref place) = **rhs {
250 if place.ignore_borrow(
253 &self.borrow_set.locals_state_at_exit,
257 let index = self.borrow_set.location_map.get(&location).unwrap_or_else(|| {
258 panic!("could not find BorrowIndex for location {:?}", location);
263 // Issue #46746: Two-phase borrows handles
264 // stmts of form `Tmp = &mut Borrow` ...
267 Place::Local(..) | Place::Static(..) => {} // okay
268 Place::Projection(..) => {
269 // ... can assign into projections,
270 // e.g. `box (&mut _)`. Current
271 // conservative solution: force
272 // immediate activation here.
279 mir::StatementKind::StorageDead(local) => {
280 // Make sure there are no remaining borrows for locals that
281 // are gone out of scope.
282 self.kill_borrows_on_local(sets, &local)
285 mir::StatementKind::InlineAsm { ref outputs, ref asm, .. } => {
286 for (output, kind) in outputs.iter().zip(&asm.outputs) {
287 if !kind.is_indirect && !kind.is_rw {
288 // Make sure there are no remaining borrows for direct
290 if let Place::Local(ref local) = *output {
291 // FIXME: Handle the case in which we're assigning over
292 // a projection (`foo.bar`).
293 self.kill_borrows_on_local(sets, local);
299 mir::StatementKind::FakeRead(..) |
300 mir::StatementKind::SetDiscriminant { .. } |
301 mir::StatementKind::StorageLive(..) |
302 mir::StatementKind::Retag { .. } |
303 mir::StatementKind::EscapeToRaw { .. } |
304 mir::StatementKind::AscribeUserType(..) |
305 mir::StatementKind::Nop => {}
310 fn before_terminator_effect(&self,
311 sets: &mut BlockSets<BorrowIndex>,
312 location: Location) {
313 debug!("Borrows::before_terminator_effect sets: {:?} location: {:?}", sets, location);
314 self.kill_loans_out_of_scope_at_location(sets, location);
317 fn terminator_effect(&self, _: &mut BlockSets<BorrowIndex>, _: Location) {}
319 fn propagate_call_return(&self,
320 _in_out: &mut BitSet<BorrowIndex>,
321 _call_bb: mir::BasicBlock,
322 _dest_bb: mir::BasicBlock,
323 _dest_place: &mir::Place) {
324 // there are no effects on borrows from method call return...
326 // ... but if overwriting a place can affect flow state, then
327 // latter is not true; see NOTE on Assign case in
328 // statement_effect_on_borrows.
332 impl<'a, 'gcx, 'tcx> BitSetOperator for Borrows<'a, 'gcx, 'tcx> {
334 fn join<T: Idx>(&self, inout_set: &mut BitSet<T>, in_set: &BitSet<T>) -> bool {
335 inout_set.union(in_set) // "maybe" means we union effects of both preds
339 impl<'a, 'gcx, 'tcx> InitialFlow for Borrows<'a, 'gcx, 'tcx> {
341 fn bottom_value() -> bool {
342 false // bottom = nothing is reserved or activated yet