Skip to content

Commit e112367

Browse files
committed
extract code to build borrow-set into borrow_check::borrow_set
Also: - Extract common helper functions into a helper trait. - Kill a bit of dead code.
1 parent 7059266 commit e112367

File tree

4 files changed

+322
-407
lines changed

4 files changed

+322
-407
lines changed

src/librustc_mir/borrow_check/borrow_set.rs

Lines changed: 249 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,16 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
use borrow_check::place_ext::PlaceExt;
1112
use dataflow::indexes::BorrowIndex;
12-
use rustc::mir::{self, Location};
13-
use rustc::ty::{Region, RegionKind};
13+
use rustc::mir::traversal;
14+
use rustc::mir::visit::{PlaceContext, Visitor};
15+
use rustc::mir::{self, Location, Mir, Place};
16+
use rustc::ty::{self, Region, RegionKind, TyCtxt};
1417
use rustc::util::nodemap::{FxHashMap, FxHashSet};
1518
use rustc_data_structures::indexed_vec::IndexVec;
1619
use std::fmt;
20+
use std::hash::Hash;
1721
use syntax_pos::Span;
1822

1923
crate struct BorrowSet<'tcx> {
@@ -71,8 +75,250 @@ impl<'tcx> fmt::Display for BorrowData<'tcx> {
7175
mir::BorrowKind::Mut { .. } => "mut ",
7276
};
7377
let region = format!("{}", self.region);
74-
let region = if region.len() > 0 { format!("{} ", region) } else { region };
78+
let region = if region.len() > 0 {
79+
format!("{} ", region)
80+
} else {
81+
region
82+
};
7583
write!(w, "&{}{}{:?}", region, kind, self.borrowed_place)
7684
}
7785
}
7886

87+
impl<'tcx> BorrowSet<'tcx> {
88+
pub fn build(tcx: TyCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>) -> Self {
89+
let mut visitor = GatherBorrows {
90+
tcx,
91+
mir,
92+
idx_vec: IndexVec::new(),
93+
location_map: FxHashMap(),
94+
activation_map: FxHashMap(),
95+
region_map: FxHashMap(),
96+
local_map: FxHashMap(),
97+
region_span_map: FxHashMap(),
98+
pending_activations: FxHashMap(),
99+
};
100+
101+
for (block, block_data) in traversal::preorder(mir) {
102+
visitor.visit_basic_block_data(block, block_data);
103+
}
104+
105+
// Double check: We should have found an activation for every pending
106+
// activation.
107+
assert_eq!(
108+
visitor
109+
.pending_activations
110+
.iter()
111+
.find(|&(_local, &borrow_index)| visitor.idx_vec[borrow_index]
112+
.activation_location
113+
.is_none()),
114+
None,
115+
"never found an activation for this borrow!",
116+
);
117+
118+
BorrowSet {
119+
borrows: visitor.idx_vec,
120+
location_map: visitor.location_map,
121+
activation_map: visitor.activation_map,
122+
region_map: visitor.region_map,
123+
local_map: visitor.local_map,
124+
region_span_map: visitor.region_span_map,
125+
}
126+
}
127+
}
128+
129+
struct GatherBorrows<'a, 'gcx: 'tcx, 'tcx: 'a> {
130+
tcx: TyCtxt<'a, 'gcx, 'tcx>,
131+
mir: &'a Mir<'tcx>,
132+
idx_vec: IndexVec<BorrowIndex, BorrowData<'tcx>>,
133+
location_map: FxHashMap<Location, BorrowIndex>,
134+
activation_map: FxHashMap<Location, FxHashSet<BorrowIndex>>,
135+
region_map: FxHashMap<Region<'tcx>, FxHashSet<BorrowIndex>>,
136+
local_map: FxHashMap<mir::Local, FxHashSet<BorrowIndex>>,
137+
region_span_map: FxHashMap<RegionKind, Span>,
138+
139+
/// When we encounter a 2-phase borrow statement, it will always
140+
/// be assigning into a temporary TEMP:
141+
///
142+
/// TEMP = &foo
143+
///
144+
/// We add TEMP into this map with `b`, where `b` is the index of
145+
/// the borrow. When we find a later use of this activation, we
146+
/// remove from the map (and add to the "tombstone" set below).
147+
pending_activations: FxHashMap<mir::Local, BorrowIndex>,
148+
}
149+
150+
impl<'a, 'gcx, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'gcx, 'tcx> {
151+
fn visit_assign(
152+
&mut self,
153+
block: mir::BasicBlock,
154+
assigned_place: &mir::Place<'tcx>,
155+
rvalue: &mir::Rvalue<'tcx>,
156+
location: mir::Location,
157+
) {
158+
if let mir::Rvalue::Ref(region, kind, ref borrowed_place) = *rvalue {
159+
if borrowed_place.is_unsafe_place(self.tcx, self.mir) {
160+
return;
161+
}
162+
163+
let borrow = BorrowData {
164+
kind,
165+
region,
166+
reserve_location: location,
167+
activation_location: None,
168+
borrowed_place: borrowed_place.clone(),
169+
assigned_place: assigned_place.clone(),
170+
};
171+
let idx = self.idx_vec.push(borrow);
172+
self.location_map.insert(location, idx);
173+
174+
self.insert_as_pending_if_two_phase(location, &assigned_place, region, kind, idx);
175+
176+
insert(&mut self.region_map, &region, idx);
177+
if let Some(local) = borrowed_place.root_local() {
178+
insert(&mut self.local_map, &local, idx);
179+
}
180+
}
181+
182+
return self.super_assign(block, assigned_place, rvalue, location);
183+
184+
fn insert<'a, K, V>(map: &'a mut FxHashMap<K, FxHashSet<V>>, k: &K, v: V)
185+
where
186+
K: Clone + Eq + Hash,
187+
V: Eq + Hash,
188+
{
189+
map.entry(k.clone()).or_insert(FxHashSet()).insert(v);
190+
}
191+
}
192+
193+
fn visit_place(
194+
&mut self,
195+
place: &mir::Place<'tcx>,
196+
context: PlaceContext<'tcx>,
197+
location: Location,
198+
) {
199+
self.super_place(place, context, location);
200+
201+
// We found a use of some temporary TEMP...
202+
if let Place::Local(temp) = place {
203+
// ... check whether we (earlier) saw a 2-phase borrow like
204+
//
205+
// TMP = &mut place
206+
match self.pending_activations.get(temp) {
207+
Some(&borrow_index) => {
208+
let borrow_data = &mut self.idx_vec[borrow_index];
209+
210+
// Watch out: the use of TMP in the borrow
211+
// itself doesn't count as an
212+
// activation. =)
213+
if borrow_data.reserve_location == location && context == PlaceContext::Store {
214+
return;
215+
}
216+
217+
if let Some(other_activation) = borrow_data.activation_location {
218+
span_bug!(
219+
self.mir.source_info(location).span,
220+
"found two activations for 2-phase borrow temporary {:?}: \
221+
{:?} and {:?}",
222+
temp,
223+
location,
224+
other_activation,
225+
);
226+
}
227+
228+
// Otherwise, this is the unique later use
229+
// that we expect.
230+
borrow_data.activation_location = Some(location);
231+
self.activation_map
232+
.entry(location)
233+
.or_insert(FxHashSet())
234+
.insert(borrow_index);
235+
}
236+
237+
None => {}
238+
}
239+
}
240+
}
241+
242+
fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: mir::Location) {
243+
if let mir::Rvalue::Ref(region, kind, ref place) = *rvalue {
244+
// double-check that we already registered a BorrowData for this
245+
246+
let borrow_index = self.location_map[&location];
247+
let borrow_data = &self.idx_vec[borrow_index];
248+
assert_eq!(borrow_data.reserve_location, location);
249+
assert_eq!(borrow_data.kind, kind);
250+
assert_eq!(borrow_data.region, region);
251+
assert_eq!(borrow_data.borrowed_place, *place);
252+
}
253+
254+
return self.super_rvalue(rvalue, location);
255+
}
256+
257+
fn visit_statement(
258+
&mut self,
259+
block: mir::BasicBlock,
260+
statement: &mir::Statement<'tcx>,
261+
location: Location,
262+
) {
263+
if let mir::StatementKind::EndRegion(region_scope) = statement.kind {
264+
self.region_span_map
265+
.insert(ty::ReScope(region_scope), statement.source_info.span);
266+
}
267+
return self.super_statement(block, statement, location);
268+
}
269+
}
270+
271+
impl<'a, 'gcx, 'tcx> GatherBorrows<'a, 'gcx, 'tcx> {
272+
/// Returns true if the borrow represented by `kind` is
273+
/// allowed to be split into separate Reservation and
274+
/// Activation phases.
275+
fn allow_two_phase_borrow(&self, kind: mir::BorrowKind) -> bool {
276+
self.tcx.two_phase_borrows()
277+
&& (kind.allows_two_phase_borrow()
278+
|| self.tcx.sess.opts.debugging_opts.two_phase_beyond_autoref)
279+
}
280+
281+
/// If this is a two-phase borrow, then we will record it
282+
/// as "pending" until we find the activating use.
283+
fn insert_as_pending_if_two_phase(
284+
&mut self,
285+
start_location: Location,
286+
assigned_place: &mir::Place<'tcx>,
287+
region: Region<'tcx>,
288+
kind: mir::BorrowKind,
289+
borrow_index: BorrowIndex,
290+
) {
291+
debug!(
292+
"Borrows::insert_as_pending_if_two_phase({:?}, {:?}, {:?}, {:?})",
293+
start_location, assigned_place, region, borrow_index,
294+
);
295+
296+
if !self.allow_two_phase_borrow(kind) {
297+
debug!(" -> {:?}", start_location);
298+
return;
299+
}
300+
301+
// When we encounter a 2-phase borrow statement, it will always
302+
// be assigning into a temporary TEMP:
303+
//
304+
// TEMP = &foo
305+
//
306+
// so extract `temp`.
307+
let temp = if let &mir::Place::Local(temp) = assigned_place {
308+
temp
309+
} else {
310+
span_bug!(
311+
self.mir.source_info(start_location).span,
312+
"expected 2-phase borrow to assign to a local, not `{:?}`",
313+
assigned_place,
314+
);
315+
};
316+
317+
// Insert `temp` into the list of pending activations. From
318+
// now on, we'll be on the lookout for a use of it. Note that
319+
// we are guaranteed that this use will come after the
320+
// assignment.
321+
let old_value = self.pending_activations.insert(temp, borrow_index);
322+
assert!(old_value.is_none());
323+
}
324+
}

src/librustc_mir/borrow_check/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ use self::MutateMode::{JustWrite, WriteAndRead};
5454
crate mod borrow_set;
5555
mod error_reporting;
5656
mod flows;
57+
crate mod place_ext;
5758
mod prefixes;
5859

5960
pub(crate) mod nll;
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Copyright 2012-2014 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.
4+
//
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.
10+
11+
use rustc::hir;
12+
use rustc::mir::ProjectionElem;
13+
use rustc::mir::{Local, Mir, Place};
14+
use rustc::ty::{self, TyCtxt};
15+
16+
/// Extension methods for the `Place` type.
17+
crate trait PlaceExt<'tcx> {
18+
/// True if this is a deref of a raw pointer.
19+
fn is_unsafe_place(&self, tcx: TyCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>) -> bool;
20+
21+
/// If this is a place like `x.f.g`, returns the local
22+
/// `x`. Returns `None` if this is based in a static.
23+
fn root_local(&self) -> Option<Local>;
24+
}
25+
26+
impl<'tcx> PlaceExt<'tcx> for Place<'tcx> {
27+
fn is_unsafe_place(&self, tcx: TyCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>) -> bool {
28+
match self {
29+
Place::Local(_) => false,
30+
Place::Static(static_) => {
31+
tcx.is_static(static_.def_id) == Some(hir::Mutability::MutMutable)
32+
}
33+
Place::Projection(proj) => match proj.elem {
34+
ProjectionElem::Field(..)
35+
| ProjectionElem::Downcast(..)
36+
| ProjectionElem::Subslice { .. }
37+
| ProjectionElem::ConstantIndex { .. }
38+
| ProjectionElem::Index(_) => proj.base.is_unsafe_place(tcx, mir),
39+
ProjectionElem::Deref => {
40+
let ty = proj.base.ty(mir, tcx).to_ty(tcx);
41+
match ty.sty {
42+
ty::TyRawPtr(..) => true,
43+
_ => proj.base.is_unsafe_place(tcx, mir),
44+
}
45+
}
46+
},
47+
}
48+
}
49+
50+
fn root_local(&self) -> Option<Local> {
51+
let mut p = self;
52+
loop {
53+
match p {
54+
Place::Projection(pi) => p = &pi.base,
55+
Place::Static(_) => return None,
56+
Place::Local(l) => return Some(*l),
57+
}
58+
}
59+
}
60+
}

0 commit comments

Comments
 (0)