|
8 | 8 | // option. This file may not be copied, modified, or distributed
|
9 | 9 | // except according to those terms.
|
10 | 10 |
|
| 11 | +use borrow_check::place_ext::PlaceExt; |
11 | 12 | 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}; |
14 | 17 | use rustc::util::nodemap::{FxHashMap, FxHashSet};
|
15 | 18 | use rustc_data_structures::indexed_vec::IndexVec;
|
16 | 19 | use std::fmt;
|
| 20 | +use std::hash::Hash; |
17 | 21 | use syntax_pos::Span;
|
18 | 22 |
|
19 | 23 | crate struct BorrowSet<'tcx> {
|
@@ -71,8 +75,250 @@ impl<'tcx> fmt::Display for BorrowData<'tcx> {
|
71 | 75 | mir::BorrowKind::Mut { .. } => "mut ",
|
72 | 76 | };
|
73 | 77 | 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 | + }; |
75 | 83 | write!(w, "&{}{}{:?}", region, kind, self.borrowed_place)
|
76 | 84 | }
|
77 | 85 | }
|
78 | 86 |
|
| 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, ®ion, 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 | +} |
0 commit comments