1 use rustc::mir::{self, Body, Location, Place, PlaceBase};
2 use rustc::ty::RegionVid;
5 use rustc_data_structures::fx::FxHashMap;
6 use rustc_index::bit_set::BitSet;
7 use rustc_index::vec::IndexVec;
9 use crate::borrow_check::{
10 places_conflict, BorrowData, BorrowSet, PlaceConflictBias, PlaceExt, RegionInferenceContext,
13 use crate::dataflow::{BitDenotation, BottomValue, GenKillSet};
17 rustc_index::newtype_index! {
18 pub struct BorrowIndex {
23 /// `Borrows` stores the data used in the analyses that track the flow
26 /// It uniquely identifies every borrow (`Rvalue::Ref`) by a
27 /// `BorrowIndex`, and maps each such index to a `BorrowData`
28 /// describing the borrow. These indexes are used for representing the
29 /// borrows in compact bitvectors.
30 pub struct Borrows<'a, 'tcx> {
34 borrow_set: Rc<BorrowSet<'tcx>>,
35 borrows_out_of_scope_at_location: FxHashMap<Location, Vec<BorrowIndex>>,
37 /// NLL region inference context with which NLL queries should be resolved
38 _nonlexical_regioncx: Rc<RegionInferenceContext<'tcx>>,
45 first_part_only: bool,
48 fn precompute_borrows_out_of_scope<'tcx>(
50 regioncx: &Rc<RegionInferenceContext<'tcx>>,
51 borrows_out_of_scope_at_location: &mut FxHashMap<Location, Vec<BorrowIndex>>,
52 borrow_index: BorrowIndex,
53 borrow_region: RegionVid,
56 // We visit one BB at a time. The complication is that we may start in the
57 // middle of the first BB visited (the one containing `location`), in which
58 // case we may have to later on process the first part of that BB if there
59 // is a path back to its start.
61 // For visited BBs, we record the index of the first statement processed.
62 // (In fully processed BBs this index is 0.) Note also that we add BBs to
63 // `visited` once they are added to `stack`, before they are actually
64 // processed, because this avoids the need to look them up again on
66 let mut visited = FxHashMap::default();
67 visited.insert(location.block, location.statement_index);
69 let mut stack = vec![];
70 stack.push(StackEntry {
72 lo: location.statement_index,
73 hi: body[location.block].statements.len(),
74 first_part_only: false,
77 while let Some(StackEntry { bb, lo, hi, first_part_only }) = stack.pop() {
78 let mut finished_early = first_part_only;
80 let location = Location { block: bb, statement_index: i };
81 // If region does not contain a point at the location, then add to list and skip
82 // successor locations.
83 if !regioncx.region_contains(borrow_region, location) {
84 debug!("borrow {:?} gets killed at {:?}", borrow_index, location);
85 borrows_out_of_scope_at_location.entry(location).or_default().push(borrow_index);
86 finished_early = true;
92 // Add successor BBs to the work list, if necessary.
93 let bb_data = &body[bb];
94 assert!(hi == bb_data.statements.len());
95 for &succ_bb in bb_data.terminator().successors() {
99 // `succ_bb` has been seen before. If it wasn't
100 // fully processed, add its first part to `stack`
103 stack.push(StackEntry {
107 first_part_only: true,
110 // And update this entry with 0, to represent the
111 // whole BB being processed.
115 // succ_bb hasn't been seen before. Add it to
116 // `stack` for processing.
117 stack.push(StackEntry {
120 hi: body[succ_bb].statements.len(),
121 first_part_only: false,
123 // Insert 0 for this BB, to represent the whole BB
132 impl<'a, 'tcx> Borrows<'a, 'tcx> {
135 body: &'a Body<'tcx>,
136 nonlexical_regioncx: Rc<RegionInferenceContext<'tcx>>,
137 borrow_set: &Rc<BorrowSet<'tcx>>,
139 let mut borrows_out_of_scope_at_location = FxHashMap::default();
140 for (borrow_index, borrow_data) in borrow_set.borrows.iter_enumerated() {
141 let borrow_region = borrow_data.region.to_region_vid();
142 let location = borrow_set.borrows[borrow_index].reserve_location;
144 precompute_borrows_out_of_scope(
146 &nonlexical_regioncx,
147 &mut borrows_out_of_scope_at_location,
157 borrow_set: borrow_set.clone(),
158 borrows_out_of_scope_at_location,
159 _nonlexical_regioncx: nonlexical_regioncx,
163 crate fn borrows(&self) -> &IndexVec<BorrowIndex, BorrowData<'tcx>> {
164 &self.borrow_set.borrows
167 pub fn location(&self, idx: BorrowIndex) -> &Location {
168 &self.borrow_set.borrows[idx].reserve_location
171 /// Add all borrows to the kill set, if those borrows are out of scope at `location`.
172 /// That means they went out of a nonlexical scope
173 fn kill_loans_out_of_scope_at_location(
175 trans: &mut GenKillSet<BorrowIndex>,
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 trans.kill_all(indices);
194 /// Kill any borrows that conflict with `place`.
195 fn kill_borrows_on_place(&self, trans: &mut GenKillSet<BorrowIndex>, place: &Place<'tcx>) {
196 debug!("kill_borrows_on_place: place={:?}", place);
198 if let PlaceBase::Local(local) = place.base {
199 let other_borrows_of_local =
200 self.borrow_set.local_map.get(&local).into_iter().flat_map(|bs| bs.into_iter());
202 // If the borrowed place is a local with no projections, all other borrows of this
203 // local must conflict. This is purely an optimization so we don't have to call
204 // `places_conflict` for every borrow.
205 if place.projection.is_empty() {
206 if !self.body.local_decls[local].is_ref_to_static() {
207 trans.kill_all(other_borrows_of_local);
212 // By passing `PlaceConflictBias::NoOverlap`, we conservatively assume that any given
213 // pair of array indices are unequal, so that when `places_conflict` returns true, we
214 // will be assured that two places being compared definitely denotes the same sets of
216 let definitely_conflicting_borrows = other_borrows_of_local.filter(|&&i| {
220 &self.borrow_set.borrows[i].borrowed_place,
222 PlaceConflictBias::NoOverlap,
226 trans.kill_all(definitely_conflicting_borrows);
231 impl<'a, 'tcx> BitDenotation<'tcx> for Borrows<'a, 'tcx> {
232 type Idx = BorrowIndex;
233 fn name() -> &'static str {
236 fn bits_per_block(&self) -> usize {
237 self.borrow_set.borrows.len() * 2
240 fn start_block_effect(&self, _entry_set: &mut BitSet<Self::Idx>) {
241 // no borrows of code region_scopes have been taken prior to
242 // function execution, so this method has no effect.
245 fn before_statement_effect(&self, trans: &mut GenKillSet<Self::Idx>, location: Location) {
246 debug!("Borrows::before_statement_effect trans: {:?} location: {:?}", trans, location);
247 self.kill_loans_out_of_scope_at_location(trans, location);
250 fn statement_effect(&self, trans: &mut GenKillSet<Self::Idx>, location: Location) {
251 debug!("Borrows::statement_effect: trans={:?} location={:?}", trans, location);
253 let block = &self.body.basic_blocks().get(location.block).unwrap_or_else(|| {
254 panic!("could not find block at location {:?}", location);
256 let stmt = block.statements.get(location.statement_index).unwrap_or_else(|| {
257 panic!("could not find statement at location {:?}");
260 debug!("Borrows::statement_effect: stmt={:?}", stmt);
262 mir::StatementKind::Assign(box (ref lhs, ref rhs)) => {
263 if let mir::Rvalue::Ref(_, _, ref place) = *rhs {
264 if place.ignore_borrow(
267 &self.borrow_set.locals_state_at_exit,
271 let index = self.borrow_set.location_map.get(&location).unwrap_or_else(|| {
272 panic!("could not find BorrowIndex for location {:?}", location);
278 // Make sure there are no remaining borrows for variables
279 // that are assigned over.
280 self.kill_borrows_on_place(trans, lhs);
283 mir::StatementKind::StorageDead(local) => {
284 // Make sure there are no remaining borrows for locals that
285 // are gone out of scope.
286 self.kill_borrows_on_place(trans, &Place::from(local));
289 mir::StatementKind::InlineAsm(ref asm) => {
290 for (output, kind) in asm.outputs.iter().zip(&asm.asm.outputs) {
291 if !kind.is_indirect && !kind.is_rw {
292 self.kill_borrows_on_place(trans, output);
297 mir::StatementKind::FakeRead(..)
298 | mir::StatementKind::SetDiscriminant { .. }
299 | mir::StatementKind::StorageLive(..)
300 | mir::StatementKind::Retag { .. }
301 | mir::StatementKind::AscribeUserType(..)
302 | mir::StatementKind::Nop => {}
306 fn before_terminator_effect(&self, trans: &mut GenKillSet<Self::Idx>, location: Location) {
307 debug!("Borrows::before_terminator_effect: trans={:?} location={:?}", trans, location);
308 self.kill_loans_out_of_scope_at_location(trans, location);
311 fn terminator_effect(&self, _: &mut GenKillSet<Self::Idx>, _: Location) {}
313 fn propagate_call_return(
315 _in_out: &mut BitSet<BorrowIndex>,
316 _call_bb: mir::BasicBlock,
317 _dest_bb: mir::BasicBlock,
318 _dest_place: &mir::Place<'tcx>,
323 impl<'a, 'tcx> BottomValue for Borrows<'a, 'tcx> {
324 /// bottom = nothing is reserved or activated yet;
325 const BOTTOM_VALUE: bool = false;