]> git.proxmox.com Git - rustc.git/blame - src/librustc_mir/dataflow/impls/borrows.rs
New upstream version 1.26.0+dfsg1
[rustc.git] / src / librustc_mir / dataflow / impls / borrows.rs
CommitLineData
3b2f2976
XL
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.
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
ff7c6d11
XL
11use rustc;
12use rustc::hir;
13use rustc::hir::def_id::DefId;
14use rustc::middle::region;
15use rustc::mir::{self, Location, Place, Mir};
16use rustc::mir::visit::{PlaceContext, Visitor};
abe05a73 17use rustc::ty::{self, Region, TyCtxt};
ea8adc8c 18use rustc::ty::RegionKind;
3b2f2976
XL
19use rustc::ty::RegionKind::ReScope;
20use rustc::util::nodemap::{FxHashMap, FxHashSet};
21
22use rustc_data_structures::bitslice::{BitwiseOperator};
23use rustc_data_structures::indexed_set::{IdxSet};
ff7c6d11 24use rustc_data_structures::indexed_vec::{Idx, IndexVec};
0531ce1d 25use rustc_data_structures::sync::Lrc;
3b2f2976 26
ff7c6d11
XL
27use dataflow::{BitDenotation, BlockSets, InitialFlow};
28pub use dataflow::indexes::{BorrowIndex, ReserveOrActivateIndex};
29use borrow_check::nll::region_infer::RegionInferenceContext;
30use borrow_check::nll::ToRegionVid;
3b2f2976 31
ea8adc8c
XL
32use syntax_pos::Span;
33
3b2f2976 34use std::fmt;
ff7c6d11
XL
35use std::hash::Hash;
36use std::rc::Rc;
3b2f2976 37
ff7c6d11
XL
38/// `Borrows` stores the data used in the analyses that track the flow
39/// of borrows.
40///
41/// It uniquely identifies every borrow (`Rvalue::Ref`) by a
42/// `BorrowIndex`, and maps each such index to a `BorrowData`
43/// describing the borrow. These indexes are used for representing the
44/// borrows in compact bitvectors.
abe05a73
XL
45pub struct Borrows<'a, 'gcx: 'tcx, 'tcx: 'a> {
46 tcx: TyCtxt<'a, 'gcx, 'tcx>,
3b2f2976 47 mir: &'a Mir<'tcx>,
0531ce1d 48 scope_tree: Lrc<region::ScopeTree>,
ff7c6d11
XL
49 root_scope: Option<region::Scope>,
50
51 /// The fundamental map relating bitvector indexes to the borrows
52 /// in the MIR.
3b2f2976 53 borrows: IndexVec<BorrowIndex, BorrowData<'tcx>>,
ff7c6d11
XL
54
55 /// Each borrow is also uniquely identified in the MIR by the
56 /// `Location` of the assignment statement in which it appears on
57 /// the right hand side; we map each such location to the
58 /// corresponding `BorrowIndex`.
3b2f2976 59 location_map: FxHashMap<Location, BorrowIndex>,
ff7c6d11
XL
60
61 /// Every borrow in MIR is immediately stored into a place via an
62 /// assignment statement. This maps each such assigned place back
63 /// to its borrow-indexes.
64 assigned_map: FxHashMap<Place<'tcx>, FxHashSet<BorrowIndex>>,
65
0531ce1d
XL
66 /// Locations which activate borrows.
67 /// NOTE: A given location may activate more than one borrow in the future
68 /// when more general two-phase borrow support is introduced, but for now we
69 /// only need to store one borrow index
70 activation_map: FxHashMap<Location, BorrowIndex>,
71
ff7c6d11
XL
72 /// Every borrow has a region; this maps each such regions back to
73 /// its borrow-indexes.
3b2f2976 74 region_map: FxHashMap<Region<'tcx>, FxHashSet<BorrowIndex>>,
0531ce1d
XL
75
76 /// Map from local to all the borrows on that local
ff7c6d11 77 local_map: FxHashMap<mir::Local, FxHashSet<BorrowIndex>>,
ff7c6d11 78
0531ce1d
XL
79 /// Maps regions to their corresponding source spans
80 /// Only contains ReScope()s as keys
81 region_span_map: FxHashMap<RegionKind, Span>,
ff7c6d11 82
0531ce1d
XL
83 /// NLL region inference context with which NLL queries should be resolved
84 nonlexical_regioncx: Option<Rc<RegionInferenceContext<'tcx>>>,
3b2f2976
XL
85}
86
87// temporarily allow some dead fields: `kind` and `region` will be
ff7c6d11 88// needed by borrowck; `borrowed_place` will probably be a MovePathIndex when
3b2f2976
XL
89// that is extended to include borrowed data paths.
90#[allow(dead_code)]
91#[derive(Debug)]
92pub struct BorrowData<'tcx> {
0531ce1d
XL
93 /// Location where the borrow reservation starts.
94 /// In many cases, this will be equal to the activation location but not always.
95 pub(crate) reserve_location: Location,
96 /// What kind of borrow this is
3b2f2976 97 pub(crate) kind: mir::BorrowKind,
0531ce1d 98 /// The region for which this borrow is live
3b2f2976 99 pub(crate) region: Region<'tcx>,
0531ce1d 100 /// Place from which we are borrowing
ff7c6d11 101 pub(crate) borrowed_place: mir::Place<'tcx>,
0531ce1d 102 /// Place to which the borrow was stored
ff7c6d11 103 pub(crate) assigned_place: mir::Place<'tcx>,
3b2f2976
XL
104}
105
106impl<'tcx> fmt::Display for BorrowData<'tcx> {
107 fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
108 let kind = match self.kind {
109 mir::BorrowKind::Shared => "",
110 mir::BorrowKind::Unique => "uniq ",
2c00a5a8 111 mir::BorrowKind::Mut { .. } => "mut ",
3b2f2976
XL
112 };
113 let region = format!("{}", self.region);
114 let region = if region.len() > 0 { format!("{} ", region) } else { region };
ff7c6d11
XL
115 write!(w, "&{}{}{:?}", region, kind, self.borrowed_place)
116 }
117}
118
119impl ReserveOrActivateIndex {
2c00a5a8 120 fn reserved(i: BorrowIndex) -> Self { ReserveOrActivateIndex::new(i.index() * 2) }
ff7c6d11
XL
121 fn active(i: BorrowIndex) -> Self { ReserveOrActivateIndex::new((i.index() * 2) + 1) }
122
123 pub(crate) fn is_reservation(self) -> bool { self.index() % 2 == 0 }
124 pub(crate) fn is_activation(self) -> bool { self.index() % 2 == 1}
125
126 pub(crate) fn kind(self) -> &'static str {
127 if self.is_reservation() { "reserved" } else { "active" }
128 }
129 pub(crate) fn borrow_index(self) -> BorrowIndex {
130 BorrowIndex::new(self.index() / 2)
3b2f2976
XL
131 }
132}
133
abe05a73
XL
134impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
135 pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
136 mir: &'a Mir<'tcx>,
ff7c6d11
XL
137 nonlexical_regioncx: Option<Rc<RegionInferenceContext<'tcx>>>,
138 def_id: DefId,
139 body_id: Option<hir::BodyId>)
abe05a73 140 -> Self {
ff7c6d11
XL
141 let scope_tree = tcx.region_scope_tree(def_id);
142 let root_scope = body_id.map(|body_id| {
143 region::Scope::CallSite(tcx.hir.body(body_id).value.hir_id.local_id)
144 });
abe05a73
XL
145 let mut visitor = GatherBorrows {
146 tcx,
147 mir,
148 idx_vec: IndexVec::new(),
149 location_map: FxHashMap(),
ff7c6d11 150 assigned_map: FxHashMap(),
0531ce1d 151 activation_map: FxHashMap(),
abe05a73 152 region_map: FxHashMap(),
ff7c6d11 153 local_map: FxHashMap(),
0531ce1d
XL
154 region_span_map: FxHashMap(),
155 nonlexical_regioncx: nonlexical_regioncx.clone()
abe05a73 156 };
3b2f2976
XL
157 visitor.visit_mir(mir);
158 return Borrows { tcx: tcx,
159 mir: mir,
160 borrows: visitor.idx_vec,
ff7c6d11
XL
161 scope_tree,
162 root_scope,
3b2f2976 163 location_map: visitor.location_map,
ff7c6d11 164 assigned_map: visitor.assigned_map,
0531ce1d 165 activation_map: visitor.activation_map,
ea8adc8c 166 region_map: visitor.region_map,
ff7c6d11 167 local_map: visitor.local_map,
abe05a73
XL
168 region_span_map: visitor.region_span_map,
169 nonlexical_regioncx };
3b2f2976 170
abe05a73
XL
171 struct GatherBorrows<'a, 'gcx: 'tcx, 'tcx: 'a> {
172 tcx: TyCtxt<'a, 'gcx, 'tcx>,
173 mir: &'a Mir<'tcx>,
3b2f2976
XL
174 idx_vec: IndexVec<BorrowIndex, BorrowData<'tcx>>,
175 location_map: FxHashMap<Location, BorrowIndex>,
ff7c6d11 176 assigned_map: FxHashMap<Place<'tcx>, FxHashSet<BorrowIndex>>,
0531ce1d 177 activation_map: FxHashMap<Location, BorrowIndex>,
3b2f2976 178 region_map: FxHashMap<Region<'tcx>, FxHashSet<BorrowIndex>>,
ff7c6d11 179 local_map: FxHashMap<mir::Local, FxHashSet<BorrowIndex>>,
ea8adc8c 180 region_span_map: FxHashMap<RegionKind, Span>,
0531ce1d 181 nonlexical_regioncx: Option<Rc<RegionInferenceContext<'tcx>>>,
3b2f2976 182 }
abe05a73
XL
183
184 impl<'a, 'gcx, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'gcx, 'tcx> {
ff7c6d11
XL
185 fn visit_assign(&mut self,
186 block: mir::BasicBlock,
187 assigned_place: &mir::Place<'tcx>,
3b2f2976
XL
188 rvalue: &mir::Rvalue<'tcx>,
189 location: mir::Location) {
ff7c6d11
XL
190 fn root_local(mut p: &mir::Place<'_>) -> Option<mir::Local> {
191 loop { match p {
192 mir::Place::Projection(pi) => p = &pi.base,
193 mir::Place::Static(_) => return None,
194 mir::Place::Local(l) => return Some(*l)
195 }}
196 }
197
198 if let mir::Rvalue::Ref(region, kind, ref borrowed_place) = *rvalue {
199 if is_unsafe_place(self.tcx, self.mir, borrowed_place) { return; }
abe05a73 200
0531ce1d
XL
201 let activate_location = self.compute_activation_location(location,
202 &assigned_place,
203 region,
204 kind);
3b2f2976 205 let borrow = BorrowData {
0531ce1d
XL
206 kind, region,
207 reserve_location: location,
ff7c6d11
XL
208 borrowed_place: borrowed_place.clone(),
209 assigned_place: assigned_place.clone(),
3b2f2976
XL
210 };
211 let idx = self.idx_vec.push(borrow);
212 self.location_map.insert(location, idx);
ff7c6d11 213
0531ce1d
XL
214 // This assert is a good sanity check until more general 2-phase borrow
215 // support is introduced. See NOTE on the activation_map field for more
216 assert!(!self.activation_map.contains_key(&activate_location),
217 "More than one activation introduced at the same location.");
218 self.activation_map.insert(activate_location, idx);
219
ff7c6d11
XL
220 insert(&mut self.assigned_map, assigned_place, idx);
221 insert(&mut self.region_map, &region, idx);
222 if let Some(local) = root_local(borrowed_place) {
223 insert(&mut self.local_map, &local, idx);
224 }
225 }
226
227 return self.super_assign(block, assigned_place, rvalue, location);
228
229 fn insert<'a, K, V>(map: &'a mut FxHashMap<K, FxHashSet<V>>,
230 k: &K,
231 v: V)
232 where K: Clone+Eq+Hash, V: Eq+Hash
233 {
234 map.entry(k.clone())
235 .or_insert(FxHashSet())
236 .insert(v);
3b2f2976
XL
237 }
238 }
ea8adc8c 239
ff7c6d11
XL
240 fn visit_rvalue(&mut self,
241 rvalue: &mir::Rvalue<'tcx>,
242 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 mut found_it = false;
247 for idx in &self.region_map[region] {
248 let bd = &self.idx_vec[*idx];
0531ce1d 249 if bd.reserve_location == location &&
ff7c6d11
XL
250 bd.kind == kind &&
251 bd.region == region &&
252 bd.borrowed_place == *place
253 {
254 found_it = true;
255 break;
256 }
257 }
258 assert!(found_it, "Ref {:?} at {:?} missing BorrowData", rvalue, location);
259 }
260
261 return self.super_rvalue(rvalue, location);
262 }
263
ea8adc8c
XL
264 fn visit_statement(&mut self,
265 block: mir::BasicBlock,
266 statement: &mir::Statement<'tcx>,
267 location: Location) {
268 if let mir::StatementKind::EndRegion(region_scope) = statement.kind {
269 self.region_span_map.insert(ReScope(region_scope), statement.source_info.span);
270 }
ff7c6d11 271 return self.super_statement(block, statement, location);
ea8adc8c 272 }
3b2f2976 273 }
0531ce1d
XL
274
275 /// A MIR visitor that determines if a specific place is used in a two-phase activating
276 /// manner in a given chunk of MIR.
277 struct ContainsUseOfPlace<'b, 'tcx: 'b> {
278 target: &'b Place<'tcx>,
279 use_found: bool,
280 }
281
282 impl<'b, 'tcx: 'b> ContainsUseOfPlace<'b, 'tcx> {
283 fn new(place: &'b Place<'tcx>) -> Self {
284 Self { target: place, use_found: false }
285 }
286
287 /// return whether `context` should be considered a "use" of a
288 /// place found in that context. "Uses" activate associated
289 /// borrows (at least when such uses occur while the borrow also
290 /// has a reservation at the time).
291 fn is_potential_use(context: PlaceContext) -> bool {
292 match context {
293 // storage effects on a place do not activate it
294 PlaceContext::StorageLive | PlaceContext::StorageDead => false,
295
296 // validation effects do not activate a place
297 //
298 // FIXME: Should they? Is it just another read? Or can we
299 // guarantee it won't dereference the stored address? How
300 // "deep" does validation go?
301 PlaceContext::Validate => false,
302
303 // FIXME: This is here to not change behaviour from before
304 // AsmOutput existed, but it's not necessarily a pure overwrite.
305 // so it's possible this should activate the place.
306 PlaceContext::AsmOutput |
307 // pure overwrites of a place do not activate it. (note
308 // PlaceContext::Call is solely about dest place)
309 PlaceContext::Store | PlaceContext::Call => false,
310
311 // reads of a place *do* activate it
312 PlaceContext::Move |
313 PlaceContext::Copy |
314 PlaceContext::Drop |
315 PlaceContext::Inspect |
316 PlaceContext::Borrow { .. } |
317 PlaceContext::Projection(..) => true,
318 }
319 }
320 }
321
322 impl<'b, 'tcx: 'b> Visitor<'tcx> for ContainsUseOfPlace<'b, 'tcx> {
323 fn visit_place(&mut self,
324 place: &mir::Place<'tcx>,
325 context: PlaceContext<'tcx>,
326 location: Location) {
327 if Self::is_potential_use(context) && place == self.target {
328 self.use_found = true;
329 return;
330 // There is no need to keep checking the statement, we already found a use
331 }
332
333 self.super_place(place, context, location);
334 }
335 }
336
337 impl<'a, 'gcx, 'tcx> GatherBorrows<'a, 'gcx, 'tcx> {
338 /// Returns true if the borrow represented by `kind` is
339 /// allowed to be split into separate Reservation and
340 /// Activation phases.
341 fn allow_two_phase_borrow(&self, kind: mir::BorrowKind) -> bool {
342 self.tcx.two_phase_borrows() &&
343 (kind.allows_two_phase_borrow() ||
344 self.tcx.sess.opts.debugging_opts.two_phase_beyond_autoref)
345 }
346
347 /// Returns true if the given location contains an NLL-activating use of the given place
348 fn location_contains_use(&self, location: Location, place: &Place) -> bool {
349 let mut use_checker = ContainsUseOfPlace::new(place);
350 let block = &self.mir.basic_blocks().get(location.block).unwrap_or_else(|| {
351 panic!("could not find block at location {:?}", location);
352 });
353 if location.statement_index != block.statements.len() {
354 // This is a statement
355 let stmt = block.statements.get(location.statement_index).unwrap_or_else(|| {
356 panic!("could not find statement at location {:?}");
357 });
358 use_checker.visit_statement(location.block, stmt, location);
359 } else {
360 // This is a terminator
361 match block.terminator {
362 Some(ref term) => {
363 use_checker.visit_terminator(location.block, term, location);
364 }
365 None => {
366 // There is no way for Place to be used by the terminator if there is no
367 // terminator
368 }
369 }
370 }
371
372 use_checker.use_found
373 }
374
375 /// Determines if the provided region is terminated after the provided location.
376 /// EndRegion statements terminate their enclosed region::Scope.
377 /// We also consult with the NLL region inference engine, should one be available
378 fn region_terminated_after(&self, region: Region<'tcx>, location: Location) -> bool {
379 let block_data = &self.mir[location.block];
380 if location.statement_index != block_data.statements.len() {
381 let stmt = &block_data.statements[location.statement_index];
382 if let mir::StatementKind::EndRegion(region_scope) = stmt.kind {
383 if &ReScope(region_scope) == region {
384 // We encountered an EndRegion statement that terminates the provided
385 // region
386 return true;
387 }
388 }
389 }
390 if let Some(ref regioncx) = self.nonlexical_regioncx {
391 if !regioncx.region_contains_point(region, location) {
392 // NLL says the region has ended already
393 return true;
394 }
395 }
396
397 false
398 }
399
400 /// Computes the activation location of a borrow.
401 /// The general idea is to start at the beginning of the region and perform a DFS
402 /// until we exit the region, either via an explicit EndRegion or because NLL tells
403 /// us so. If we find more than one valid activation point, we currently panic the
404 /// compiler since two-phase borrows are only currently supported for compiler-
405 /// generated code. More precisely, we only allow two-phase borrows for:
406 /// - Function calls (fn some_func(&mut self, ....))
407 /// - *Assign operators (a += b -> fn add_assign(&mut self, other: Self))
408 /// See
409 /// - https://github.com/rust-lang/rust/issues/48431
410 /// for detailed design notes.
411 /// See the FIXME in the body of the function for notes on extending support to more
412 /// general two-phased borrows.
413 fn compute_activation_location(&self,
414 start_location: Location,
415 assigned_place: &mir::Place<'tcx>,
416 region: Region<'tcx>,
417 kind: mir::BorrowKind) -> Location {
418 debug!("Borrows::compute_activation_location({:?}, {:?}, {:?})",
419 start_location,
420 assigned_place,
421 region);
422 if !self.allow_two_phase_borrow(kind) {
423 debug!(" -> {:?}", start_location);
424 return start_location;
425 }
426
427 // Perform the DFS.
428 // `stack` is the stack of locations still under consideration
429 // `visited` is the set of points we have already visited
430 // `found_use` is an Option that becomes Some when we find a use
431 let mut stack = vec![start_location];
432 let mut visited = FxHashSet();
433 let mut found_use = None;
434 while let Some(curr_loc) = stack.pop() {
435 let block_data = &self.mir.basic_blocks()
436 .get(curr_loc.block)
437 .unwrap_or_else(|| {
438 panic!("could not find block at location {:?}", curr_loc);
439 });
440
441 if self.region_terminated_after(region, curr_loc) {
442 // No need to process this statement.
443 // It's either an EndRegion (and thus couldn't use assigned_place) or not
444 // contained in the NLL region and thus a use would be invalid
445 continue;
446 }
447
448 if !visited.insert(curr_loc) {
449 debug!(" Already visited {:?}", curr_loc);
450 continue;
451 }
452
453 if self.location_contains_use(curr_loc, assigned_place) {
454 // FIXME: Handle this case a little more gracefully. Perhaps collect
455 // all uses in a vector, and find the point in the CFG that dominates
456 // all of them?
457 // Right now this is sufficient though since there should only be exactly
458 // one borrow-activating use of the borrow.
459 assert!(found_use.is_none(), "Found secondary use of place");
460 found_use = Some(curr_loc);
461 }
462
463 // Push the points we should consider next.
464 if curr_loc.statement_index < block_data.statements.len() {
465 stack.push(curr_loc.successor_within_block());
466 } else {
467 stack.extend(block_data.terminator().successors().iter().map(
468 |&basic_block| {
469 Location {
470 statement_index: 0,
471 block: basic_block
472 }
473 }
474 ))
475 }
476 }
477
478 let found_use = found_use.expect("Did not find use of two-phase place");
479 debug!(" -> {:?}", found_use);
480 found_use
481 }
482 }
483 }
484
485 /// Returns the span for the "end point" given region. This will
486 /// return `None` if NLL is enabled, since that concept has no
487 /// meaning there. Otherwise, return region span if it exists and
488 /// span for end of the function if it doesn't exist.
489 pub(crate) fn opt_region_end_span(&self, region: &Region) -> Option<Span> {
490 match self.nonlexical_regioncx {
491 Some(_) => None,
492 None => {
493 match self.region_span_map.get(region) {
494 Some(span) => Some(self.tcx.sess.codemap().end_point(*span)),
495 None => Some(self.tcx.sess.codemap().end_point(self.mir.span))
496 }
497 }
498 }
3b2f2976
XL
499 }
500
501 pub fn borrows(&self) -> &IndexVec<BorrowIndex, BorrowData<'tcx>> { &self.borrows }
502
0531ce1d 503 pub fn scope_tree(&self) -> &Lrc<region::ScopeTree> { &self.scope_tree }
ff7c6d11 504
3b2f2976 505 pub fn location(&self, idx: BorrowIndex) -> &Location {
0531ce1d 506 &self.borrows[idx].reserve_location
3b2f2976 507 }
ea8adc8c 508
abe05a73 509 /// Add all borrows to the kill set, if those borrows are out of scope at `location`.
0531ce1d
XL
510 /// That means either they went out of either a nonlexical scope, if we care about those
511 /// at the moment, or the location represents a lexical EndRegion
abe05a73 512 fn kill_loans_out_of_scope_at_location(&self,
ff7c6d11 513 sets: &mut BlockSets<ReserveOrActivateIndex>,
0531ce1d 514 location: Location) {
ff7c6d11
XL
515 if let Some(ref regioncx) = self.nonlexical_regioncx {
516 // NOTE: The state associated with a given `location`
517 // reflects the dataflow on entry to the statement. If it
518 // does not contain `borrow_region`, then then that means
519 // that the statement at `location` kills the borrow.
520 //
521 // We are careful always to call this function *before* we
522 // set up the gen-bits for the statement or
523 // termanator. That way, if the effect of the statement or
524 // terminator *does* introduce a new loan of the same
525 // region, then setting that gen-bit will override any
526 // potential kill introduced here.
abe05a73
XL
527 for (borrow_index, borrow_data) in self.borrows.iter_enumerated() {
528 let borrow_region = borrow_data.region.to_region_vid();
529 if !regioncx.region_contains_point(borrow_region, location) {
ff7c6d11 530 sets.kill(&ReserveOrActivateIndex::reserved(borrow_index));
0531ce1d 531 sets.kill(&ReserveOrActivateIndex::active(borrow_index));
abe05a73
XL
532 }
533 }
534 }
ea8adc8c 535 }
3b2f2976 536
0531ce1d
XL
537 fn kill_borrows_on_local(&self,
538 sets: &mut BlockSets<ReserveOrActivateIndex>,
539 local: &rustc::mir::Local)
540 {
541 if let Some(borrow_indexes) = self.local_map.get(local) {
542 sets.kill_all(borrow_indexes.iter()
543 .map(|b| ReserveOrActivateIndex::reserved(*b)));
544 sets.kill_all(borrow_indexes.iter()
545 .map(|b| ReserveOrActivateIndex::active(*b)));
546 }
547 }
548
549 /// Performs the activations for a given location
550 fn perform_activations_at_location(&self,
551 sets: &mut BlockSets<ReserveOrActivateIndex>,
552 location: Location) {
553 // Handle activations
554 match self.activation_map.get(&location) {
555 Some(&activated) => {
556 debug!("activating borrow {:?}", activated);
557 sets.gen(&ReserveOrActivateIndex::active(activated))
558 }
559 None => {}
560 }
561 }
562}
563
564impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> {
565 type Idx = ReserveOrActivateIndex;
566 fn name() -> &'static str { "borrows" }
567 fn bits_per_block(&self) -> usize {
568 self.borrows.len() * 2
569 }
570
571 fn start_block_effect(&self, _entry_set: &mut IdxSet<ReserveOrActivateIndex>) {
572 // no borrows of code region_scopes have been taken prior to
573 // function execution, so this method has no effect on
574 // `_sets`.
575 }
576
577 fn before_statement_effect(&self,
578 sets: &mut BlockSets<ReserveOrActivateIndex>,
579 location: Location) {
580 debug!("Borrows::before_statement_effect sets: {:?} location: {:?}", sets, location);
581 self.kill_loans_out_of_scope_at_location(sets, location);
582 }
583
584 fn statement_effect(&self, sets: &mut BlockSets<ReserveOrActivateIndex>, location: Location) {
585 debug!("Borrows::statement_effect sets: {:?} location: {:?}", sets, location);
586
3b2f2976
XL
587 let block = &self.mir.basic_blocks().get(location.block).unwrap_or_else(|| {
588 panic!("could not find block at location {:?}", location);
589 });
590 let stmt = block.statements.get(location.statement_index).unwrap_or_else(|| {
591 panic!("could not find statement at location {:?}");
592 });
ff7c6d11 593
0531ce1d
XL
594 self.perform_activations_at_location(sets, location);
595 self.kill_loans_out_of_scope_at_location(sets, location);
ff7c6d11 596
3b2f2976 597 match stmt.kind {
ff7c6d11 598 // EndRegion kills any borrows (reservations and active borrows both)
ea8adc8c
XL
599 mir::StatementKind::EndRegion(region_scope) => {
600 if let Some(borrow_indexes) = self.region_map.get(&ReScope(region_scope)) {
abe05a73 601 assert!(self.nonlexical_regioncx.is_none());
ff7c6d11
XL
602 for idx in borrow_indexes {
603 sets.kill(&ReserveOrActivateIndex::reserved(*idx));
0531ce1d 604 sets.kill(&ReserveOrActivateIndex::active(*idx));
ff7c6d11 605 }
ea8adc8c
XL
606 } else {
607 // (if there is no entry, then there are no borrows to be tracked)
608 }
3b2f2976
XL
609 }
610
ff7c6d11
XL
611 mir::StatementKind::Assign(ref lhs, ref rhs) => {
612 // Make sure there are no remaining borrows for variables
613 // that are assigned over.
614 if let Place::Local(ref local) = *lhs {
615 // FIXME: Handle the case in which we're assigning over
616 // a projection (`foo.bar`).
0531ce1d 617 self.kill_borrows_on_local(sets, local);
ff7c6d11
XL
618 }
619
620 // NOTE: if/when the Assign case is revised to inspect
621 // the assigned_place here, make sure to also
622 // re-consider the current implementations of the
623 // propagate_call_return method.
624
625 if let mir::Rvalue::Ref(region, _, ref place) = *rhs {
626 if is_unsafe_place(self.tcx, self.mir, place) { return; }
0531ce1d
XL
627 let index = self.location_map.get(&location).unwrap_or_else(|| {
628 panic!("could not find BorrowIndex for location {:?}", location);
629 });
630
ff7c6d11 631 if let RegionKind::ReEmpty = region {
0531ce1d
XL
632 // If the borrowed value dies before the borrow is used, the region for
633 // the borrow can be empty. Don't track the borrow in that case.
634 sets.kill(&ReserveOrActivateIndex::active(*index));
ff7c6d11
XL
635 return
636 }
637
3b2f2976
XL
638 assert!(self.region_map.get(region).unwrap_or_else(|| {
639 panic!("could not find BorrowIndexs for region {:?}", region);
640 }).contains(&index));
ff7c6d11
XL
641 sets.gen(&ReserveOrActivateIndex::reserved(*index));
642
0531ce1d
XL
643 // Issue #46746: Two-phase borrows handles
644 // stmts of form `Tmp = &mut Borrow` ...
645 match lhs {
646 Place::Local(..) | Place::Static(..) => {} // okay
647 Place::Projection(..) => {
648 // ... can assign into projections,
649 // e.g. `box (&mut _)`. Current
650 // conservative solution: force
651 // immediate activation here.
652 sets.gen(&ReserveOrActivateIndex::active(*index));
ff7c6d11
XL
653 }
654 }
655 }
656 }
657
658 mir::StatementKind::StorageDead(local) => {
659 // Make sure there are no remaining borrows for locals that
660 // are gone out of scope.
0531ce1d 661 self.kill_borrows_on_local(sets, &local)
ff7c6d11
XL
662 }
663
664 mir::StatementKind::InlineAsm { ref outputs, ref asm, .. } => {
665 for (output, kind) in outputs.iter().zip(&asm.outputs) {
666 if !kind.is_indirect && !kind.is_rw {
667 // Make sure there are no remaining borrows for direct
668 // output variables.
669 if let Place::Local(ref local) = *output {
670 // FIXME: Handle the case in which we're assigning over
671 // a projection (`foo.bar`).
0531ce1d 672 self.kill_borrows_on_local(sets, local);
ff7c6d11
XL
673 }
674 }
3b2f2976
XL
675 }
676 }
677
3b2f2976
XL
678 mir::StatementKind::SetDiscriminant { .. } |
679 mir::StatementKind::StorageLive(..) |
3b2f2976 680 mir::StatementKind::Validate(..) |
0531ce1d 681 mir::StatementKind::UserAssertTy(..) |
3b2f2976
XL
682 mir::StatementKind::Nop => {}
683
684 }
ff7c6d11 685 }
abe05a73 686
0531ce1d
XL
687 fn before_terminator_effect(&self,
688 sets: &mut BlockSets<ReserveOrActivateIndex>,
689 location: Location) {
690 debug!("Borrows::before_terminator_effect sets: {:?} location: {:?}", sets, location);
691 self.kill_loans_out_of_scope_at_location(sets, location);
ff7c6d11
XL
692 }
693
0531ce1d
XL
694 fn terminator_effect(&self, sets: &mut BlockSets<ReserveOrActivateIndex>, location: Location) {
695 debug!("Borrows::terminator_effect sets: {:?} location: {:?}", sets, location);
696
ff7c6d11
XL
697 let block = &self.mir.basic_blocks().get(location.block).unwrap_or_else(|| {
698 panic!("could not find block at location {:?}", location);
699 });
700
ff7c6d11 701 let term = block.terminator();
0531ce1d
XL
702 self.perform_activations_at_location(sets, location);
703 self.kill_loans_out_of_scope_at_location(sets, location);
704
ff7c6d11
XL
705
706 match term.kind {
707 mir::TerminatorKind::Resume |
708 mir::TerminatorKind::Return |
709 mir::TerminatorKind::GeneratorDrop => {
710 // When we return from the function, then all `ReScope`-style regions
711 // are guaranteed to have ended.
712 // Normally, there would be `EndRegion` statements that come before,
713 // and hence most of these loans will already be dead -- but, in some cases
714 // like unwind paths, we do not always emit `EndRegion` statements, so we
715 // add some kills here as a "backup" and to avoid spurious error messages.
716 for (borrow_index, borrow_data) in self.borrows.iter_enumerated() {
717 if let ReScope(scope) = borrow_data.region {
718 // Check that the scope is not actually a scope from a function that is
719 // a parent of our closure. Note that the CallSite scope itself is
720 // *outside* of the closure, for some weird reason.
721 if let Some(root_scope) = self.root_scope {
722 if *scope != root_scope &&
723 self.scope_tree.is_subscope_of(*scope, root_scope)
724 {
725 sets.kill(&ReserveOrActivateIndex::reserved(borrow_index));
0531ce1d 726 sets.kill(&ReserveOrActivateIndex::active(borrow_index));
ff7c6d11
XL
727 }
728 }
729 }
730 }
731 }
732 mir::TerminatorKind::Abort |
733 mir::TerminatorKind::SwitchInt {..} |
734 mir::TerminatorKind::Drop {..} |
735 mir::TerminatorKind::DropAndReplace {..} |
736 mir::TerminatorKind::Call {..} |
737 mir::TerminatorKind::Assert {..} |
738 mir::TerminatorKind::Yield {..} |
739 mir::TerminatorKind::Goto {..} |
740 mir::TerminatorKind::FalseEdges {..} |
2c00a5a8 741 mir::TerminatorKind::FalseUnwind {..} |
ff7c6d11
XL
742 mir::TerminatorKind::Unreachable => {}
743 }
744 }
ff7c6d11
XL
745
746 fn propagate_call_return(&self,
747 _in_out: &mut IdxSet<ReserveOrActivateIndex>,
748 _call_bb: mir::BasicBlock,
749 _dest_bb: mir::BasicBlock,
750 _dest_place: &mir::Place) {
751 // there are no effects on borrows from method call return...
752 //
753 // ... but if overwriting a place can affect flow state, then
754 // latter is not true; see NOTE on Assign case in
755 // statement_effect_on_borrows.
756 }
757}
758
0531ce1d 759impl<'a, 'gcx, 'tcx> BitwiseOperator for Borrows<'a, 'gcx, 'tcx> {
ff7c6d11
XL
760 #[inline]
761 fn join(&self, pred1: usize, pred2: usize) -> usize {
762 pred1 | pred2 // union effects of preds when computing reservations
3b2f2976
XL
763 }
764}
765
0531ce1d 766impl<'a, 'gcx, 'tcx> InitialFlow for Borrows<'a, 'gcx, 'tcx> {
3b2f2976
XL
767 #[inline]
768 fn bottom_value() -> bool {
0531ce1d 769 false // bottom = nothing is reserved or activated yet
3b2f2976
XL
770 }
771}
abe05a73 772
ff7c6d11 773fn is_unsafe_place<'a, 'gcx: 'tcx, 'tcx: 'a>(
abe05a73
XL
774 tcx: TyCtxt<'a, 'gcx, 'tcx>,
775 mir: &'a Mir<'tcx>,
ff7c6d11 776 place: &mir::Place<'tcx>
abe05a73 777) -> bool {
ff7c6d11 778 use self::mir::Place::*;
abe05a73
XL
779 use self::mir::ProjectionElem;
780
ff7c6d11 781 match *place {
abe05a73 782 Local(_) => false,
0531ce1d 783 Static(ref static_) => tcx.is_static(static_.def_id) == Some(hir::Mutability::MutMutable),
abe05a73
XL
784 Projection(ref proj) => {
785 match proj.elem {
786 ProjectionElem::Field(..) |
787 ProjectionElem::Downcast(..) |
788 ProjectionElem::Subslice { .. } |
789 ProjectionElem::ConstantIndex { .. } |
790 ProjectionElem::Index(_) => {
ff7c6d11 791 is_unsafe_place(tcx, mir, &proj.base)
abe05a73
XL
792 }
793 ProjectionElem::Deref => {
794 let ty = proj.base.ty(mir, tcx).to_ty(tcx);
795 match ty.sty {
796 ty::TyRawPtr(..) => true,
ff7c6d11 797 _ => is_unsafe_place(tcx, mir, &proj.base),
abe05a73
XL
798 }
799 }
800 }
801 }
802 }
803}