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.
This commit is contained in:
parent
70592664b6
commit
e1123674b1
4 changed files with 321 additions and 406 deletions
|
@ -8,12 +8,16 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use borrow_check::place_ext::PlaceExt;
|
||||
use dataflow::indexes::BorrowIndex;
|
||||
use rustc::mir::{self, Location};
|
||||
use rustc::ty::{Region, RegionKind};
|
||||
use rustc::mir::traversal;
|
||||
use rustc::mir::visit::{PlaceContext, Visitor};
|
||||
use rustc::mir::{self, Location, Mir, Place};
|
||||
use rustc::ty::{self, Region, RegionKind, TyCtxt};
|
||||
use rustc::util::nodemap::{FxHashMap, FxHashSet};
|
||||
use rustc_data_structures::indexed_vec::IndexVec;
|
||||
use std::fmt;
|
||||
use std::hash::Hash;
|
||||
use syntax_pos::Span;
|
||||
|
||||
crate struct BorrowSet<'tcx> {
|
||||
|
@ -71,8 +75,250 @@ impl<'tcx> fmt::Display for BorrowData<'tcx> {
|
|||
mir::BorrowKind::Mut { .. } => "mut ",
|
||||
};
|
||||
let region = format!("{}", self.region);
|
||||
let region = if region.len() > 0 { format!("{} ", region) } else { region };
|
||||
let region = if region.len() > 0 {
|
||||
format!("{} ", region)
|
||||
} else {
|
||||
region
|
||||
};
|
||||
write!(w, "&{}{}{:?}", region, kind, self.borrowed_place)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> BorrowSet<'tcx> {
|
||||
pub fn build(tcx: TyCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>) -> Self {
|
||||
let mut visitor = GatherBorrows {
|
||||
tcx,
|
||||
mir,
|
||||
idx_vec: IndexVec::new(),
|
||||
location_map: FxHashMap(),
|
||||
activation_map: FxHashMap(),
|
||||
region_map: FxHashMap(),
|
||||
local_map: FxHashMap(),
|
||||
region_span_map: FxHashMap(),
|
||||
pending_activations: FxHashMap(),
|
||||
};
|
||||
|
||||
for (block, block_data) in traversal::preorder(mir) {
|
||||
visitor.visit_basic_block_data(block, block_data);
|
||||
}
|
||||
|
||||
// Double check: We should have found an activation for every pending
|
||||
// activation.
|
||||
assert_eq!(
|
||||
visitor
|
||||
.pending_activations
|
||||
.iter()
|
||||
.find(|&(_local, &borrow_index)| visitor.idx_vec[borrow_index]
|
||||
.activation_location
|
||||
.is_none()),
|
||||
None,
|
||||
"never found an activation for this borrow!",
|
||||
);
|
||||
|
||||
BorrowSet {
|
||||
borrows: visitor.idx_vec,
|
||||
location_map: visitor.location_map,
|
||||
activation_map: visitor.activation_map,
|
||||
region_map: visitor.region_map,
|
||||
local_map: visitor.local_map,
|
||||
region_span_map: visitor.region_span_map,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct GatherBorrows<'a, 'gcx: 'tcx, 'tcx: 'a> {
|
||||
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||
mir: &'a Mir<'tcx>,
|
||||
idx_vec: IndexVec<BorrowIndex, BorrowData<'tcx>>,
|
||||
location_map: FxHashMap<Location, BorrowIndex>,
|
||||
activation_map: FxHashMap<Location, FxHashSet<BorrowIndex>>,
|
||||
region_map: FxHashMap<Region<'tcx>, FxHashSet<BorrowIndex>>,
|
||||
local_map: FxHashMap<mir::Local, FxHashSet<BorrowIndex>>,
|
||||
region_span_map: FxHashMap<RegionKind, Span>,
|
||||
|
||||
/// When we encounter a 2-phase borrow statement, it will always
|
||||
/// be assigning into a temporary TEMP:
|
||||
///
|
||||
/// TEMP = &foo
|
||||
///
|
||||
/// We add TEMP into this map with `b`, where `b` is the index of
|
||||
/// the borrow. When we find a later use of this activation, we
|
||||
/// remove from the map (and add to the "tombstone" set below).
|
||||
pending_activations: FxHashMap<mir::Local, BorrowIndex>,
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'gcx, 'tcx> {
|
||||
fn visit_assign(
|
||||
&mut self,
|
||||
block: mir::BasicBlock,
|
||||
assigned_place: &mir::Place<'tcx>,
|
||||
rvalue: &mir::Rvalue<'tcx>,
|
||||
location: mir::Location,
|
||||
) {
|
||||
if let mir::Rvalue::Ref(region, kind, ref borrowed_place) = *rvalue {
|
||||
if borrowed_place.is_unsafe_place(self.tcx, self.mir) {
|
||||
return;
|
||||
}
|
||||
|
||||
let borrow = BorrowData {
|
||||
kind,
|
||||
region,
|
||||
reserve_location: location,
|
||||
activation_location: None,
|
||||
borrowed_place: borrowed_place.clone(),
|
||||
assigned_place: assigned_place.clone(),
|
||||
};
|
||||
let idx = self.idx_vec.push(borrow);
|
||||
self.location_map.insert(location, idx);
|
||||
|
||||
self.insert_as_pending_if_two_phase(location, &assigned_place, region, kind, idx);
|
||||
|
||||
insert(&mut self.region_map, ®ion, idx);
|
||||
if let Some(local) = borrowed_place.root_local() {
|
||||
insert(&mut self.local_map, &local, idx);
|
||||
}
|
||||
}
|
||||
|
||||
return self.super_assign(block, assigned_place, rvalue, location);
|
||||
|
||||
fn insert<'a, K, V>(map: &'a mut FxHashMap<K, FxHashSet<V>>, k: &K, v: V)
|
||||
where
|
||||
K: Clone + Eq + Hash,
|
||||
V: Eq + Hash,
|
||||
{
|
||||
map.entry(k.clone()).or_insert(FxHashSet()).insert(v);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_place(
|
||||
&mut self,
|
||||
place: &mir::Place<'tcx>,
|
||||
context: PlaceContext<'tcx>,
|
||||
location: Location,
|
||||
) {
|
||||
self.super_place(place, context, location);
|
||||
|
||||
// We found a use of some temporary TEMP...
|
||||
if let Place::Local(temp) = place {
|
||||
// ... check whether we (earlier) saw a 2-phase borrow like
|
||||
//
|
||||
// TMP = &mut place
|
||||
match self.pending_activations.get(temp) {
|
||||
Some(&borrow_index) => {
|
||||
let borrow_data = &mut self.idx_vec[borrow_index];
|
||||
|
||||
// Watch out: the use of TMP in the borrow
|
||||
// itself doesn't count as an
|
||||
// activation. =)
|
||||
if borrow_data.reserve_location == location && context == PlaceContext::Store {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(other_activation) = borrow_data.activation_location {
|
||||
span_bug!(
|
||||
self.mir.source_info(location).span,
|
||||
"found two activations for 2-phase borrow temporary {:?}: \
|
||||
{:?} and {:?}",
|
||||
temp,
|
||||
location,
|
||||
other_activation,
|
||||
);
|
||||
}
|
||||
|
||||
// Otherwise, this is the unique later use
|
||||
// that we expect.
|
||||
borrow_data.activation_location = Some(location);
|
||||
self.activation_map
|
||||
.entry(location)
|
||||
.or_insert(FxHashSet())
|
||||
.insert(borrow_index);
|
||||
}
|
||||
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: mir::Location) {
|
||||
if let mir::Rvalue::Ref(region, kind, ref place) = *rvalue {
|
||||
// double-check that we already registered a BorrowData for this
|
||||
|
||||
let borrow_index = self.location_map[&location];
|
||||
let borrow_data = &self.idx_vec[borrow_index];
|
||||
assert_eq!(borrow_data.reserve_location, location);
|
||||
assert_eq!(borrow_data.kind, kind);
|
||||
assert_eq!(borrow_data.region, region);
|
||||
assert_eq!(borrow_data.borrowed_place, *place);
|
||||
}
|
||||
|
||||
return self.super_rvalue(rvalue, location);
|
||||
}
|
||||
|
||||
fn visit_statement(
|
||||
&mut self,
|
||||
block: mir::BasicBlock,
|
||||
statement: &mir::Statement<'tcx>,
|
||||
location: Location,
|
||||
) {
|
||||
if let mir::StatementKind::EndRegion(region_scope) = statement.kind {
|
||||
self.region_span_map
|
||||
.insert(ty::ReScope(region_scope), statement.source_info.span);
|
||||
}
|
||||
return self.super_statement(block, statement, location);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> GatherBorrows<'a, 'gcx, 'tcx> {
|
||||
/// Returns true if the borrow represented by `kind` is
|
||||
/// allowed to be split into separate Reservation and
|
||||
/// Activation phases.
|
||||
fn allow_two_phase_borrow(&self, kind: mir::BorrowKind) -> bool {
|
||||
self.tcx.two_phase_borrows()
|
||||
&& (kind.allows_two_phase_borrow()
|
||||
|| self.tcx.sess.opts.debugging_opts.two_phase_beyond_autoref)
|
||||
}
|
||||
|
||||
/// If this is a two-phase borrow, then we will record it
|
||||
/// as "pending" until we find the activating use.
|
||||
fn insert_as_pending_if_two_phase(
|
||||
&mut self,
|
||||
start_location: Location,
|
||||
assigned_place: &mir::Place<'tcx>,
|
||||
region: Region<'tcx>,
|
||||
kind: mir::BorrowKind,
|
||||
borrow_index: BorrowIndex,
|
||||
) {
|
||||
debug!(
|
||||
"Borrows::insert_as_pending_if_two_phase({:?}, {:?}, {:?}, {:?})",
|
||||
start_location, assigned_place, region, borrow_index,
|
||||
);
|
||||
|
||||
if !self.allow_two_phase_borrow(kind) {
|
||||
debug!(" -> {:?}", start_location);
|
||||
return;
|
||||
}
|
||||
|
||||
// When we encounter a 2-phase borrow statement, it will always
|
||||
// be assigning into a temporary TEMP:
|
||||
//
|
||||
// TEMP = &foo
|
||||
//
|
||||
// so extract `temp`.
|
||||
let temp = if let &mir::Place::Local(temp) = assigned_place {
|
||||
temp
|
||||
} else {
|
||||
span_bug!(
|
||||
self.mir.source_info(start_location).span,
|
||||
"expected 2-phase borrow to assign to a local, not `{:?}`",
|
||||
assigned_place,
|
||||
);
|
||||
};
|
||||
|
||||
// Insert `temp` into the list of pending activations. From
|
||||
// now on, we'll be on the lookout for a use of it. Note that
|
||||
// we are guaranteed that this use will come after the
|
||||
// assignment.
|
||||
let old_value = self.pending_activations.insert(temp, borrow_index);
|
||||
assert!(old_value.is_none());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,6 +54,7 @@ use self::MutateMode::{JustWrite, WriteAndRead};
|
|||
crate mod borrow_set;
|
||||
mod error_reporting;
|
||||
mod flows;
|
||||
crate mod place_ext;
|
||||
mod prefixes;
|
||||
|
||||
pub(crate) mod nll;
|
||||
|
|
60
src/librustc_mir/borrow_check/place_ext.rs
Normal file
60
src/librustc_mir/borrow_check/place_ext.rs
Normal file
|
@ -0,0 +1,60 @@
|
|||
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use rustc::hir;
|
||||
use rustc::mir::ProjectionElem;
|
||||
use rustc::mir::{Local, Mir, Place};
|
||||
use rustc::ty::{self, TyCtxt};
|
||||
|
||||
/// Extension methods for the `Place` type.
|
||||
crate trait PlaceExt<'tcx> {
|
||||
/// True if this is a deref of a raw pointer.
|
||||
fn is_unsafe_place(&self, tcx: TyCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>) -> bool;
|
||||
|
||||
/// If this is a place like `x.f.g`, returns the local
|
||||
/// `x`. Returns `None` if this is based in a static.
|
||||
fn root_local(&self) -> Option<Local>;
|
||||
}
|
||||
|
||||
impl<'tcx> PlaceExt<'tcx> for Place<'tcx> {
|
||||
fn is_unsafe_place(&self, tcx: TyCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>) -> bool {
|
||||
match self {
|
||||
Place::Local(_) => false,
|
||||
Place::Static(static_) => {
|
||||
tcx.is_static(static_.def_id) == Some(hir::Mutability::MutMutable)
|
||||
}
|
||||
Place::Projection(proj) => match proj.elem {
|
||||
ProjectionElem::Field(..)
|
||||
| ProjectionElem::Downcast(..)
|
||||
| ProjectionElem::Subslice { .. }
|
||||
| ProjectionElem::ConstantIndex { .. }
|
||||
| ProjectionElem::Index(_) => proj.base.is_unsafe_place(tcx, mir),
|
||||
ProjectionElem::Deref => {
|
||||
let ty = proj.base.ty(mir, tcx).to_ty(tcx);
|
||||
match ty.sty {
|
||||
ty::TyRawPtr(..) => true,
|
||||
_ => proj.base.is_unsafe_place(tcx, mir),
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn root_local(&self) -> Option<Local> {
|
||||
let mut p = self;
|
||||
loop {
|
||||
match p {
|
||||
Place::Projection(pi) => p = &pi.base,
|
||||
Place::Static(_) => return None,
|
||||
Place::Local(l) => return Some(*l),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,18 +9,16 @@
|
|||
// except according to those terms.
|
||||
|
||||
use borrow_check::borrow_set::{BorrowSet, BorrowData};
|
||||
use borrow_check::place_ext::PlaceExt;
|
||||
|
||||
use rustc;
|
||||
use rustc::hir;
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::middle::region;
|
||||
use rustc::mir::{self, Location, Place, Mir};
|
||||
use rustc::mir::traversal;
|
||||
use rustc::mir::visit::{PlaceContext, Visitor};
|
||||
use rustc::ty::{self, Region, TyCtxt};
|
||||
use rustc::ty::{Region, TyCtxt};
|
||||
use rustc::ty::RegionKind;
|
||||
use rustc::ty::RegionKind::ReScope;
|
||||
use rustc::util::nodemap::{FxHashMap, FxHashSet};
|
||||
|
||||
use rustc_data_structures::bitslice::{BitwiseOperator};
|
||||
use rustc_data_structures::indexed_set::{IdxSet};
|
||||
|
@ -34,7 +32,6 @@ use borrow_check::nll::ToRegionVid;
|
|||
|
||||
use syntax_pos::Span;
|
||||
|
||||
use std::hash::Hash;
|
||||
use std::rc::Rc;
|
||||
|
||||
/// `Borrows` stores the data used in the analyses that track the flow
|
||||
|
@ -82,373 +79,15 @@ impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
|
|||
let root_scope = body_id.map(|body_id| {
|
||||
region::Scope::CallSite(tcx.hir.body(body_id).value.hir_id.local_id)
|
||||
});
|
||||
let mut visitor = GatherBorrows {
|
||||
tcx,
|
||||
mir,
|
||||
idx_vec: IndexVec::new(),
|
||||
location_map: FxHashMap(),
|
||||
activation_map: FxHashMap(),
|
||||
region_map: FxHashMap(),
|
||||
local_map: FxHashMap(),
|
||||
region_span_map: FxHashMap(),
|
||||
nonlexical_regioncx: nonlexical_regioncx.clone(),
|
||||
pending_activations: FxHashMap(),
|
||||
};
|
||||
for (block, block_data) in traversal::preorder(mir) {
|
||||
visitor.visit_basic_block_data(block, block_data);
|
||||
}
|
||||
let borrow_set = BorrowSet::build(tcx, mir);
|
||||
|
||||
// Double check: We should have found an activation for every pending
|
||||
// activation.
|
||||
assert_eq!(
|
||||
visitor
|
||||
.pending_activations
|
||||
.iter()
|
||||
.find(|&(_local, &borrow_index)| {
|
||||
visitor.idx_vec[borrow_index].activation_location.is_none()
|
||||
}),
|
||||
None,
|
||||
"never found an activation for this borrow!",
|
||||
);
|
||||
|
||||
return Borrows { tcx: tcx,
|
||||
mir: mir,
|
||||
borrow_set: BorrowSet {
|
||||
borrows: visitor.idx_vec,
|
||||
location_map: visitor.location_map,
|
||||
activation_map: visitor.activation_map,
|
||||
region_map: visitor.region_map,
|
||||
local_map: visitor.local_map,
|
||||
region_span_map: visitor.region_span_map,
|
||||
},
|
||||
scope_tree,
|
||||
root_scope,
|
||||
nonlexical_regioncx };
|
||||
|
||||
struct GatherBorrows<'a, 'gcx: 'tcx, 'tcx: 'a> {
|
||||
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||
mir: &'a Mir<'tcx>,
|
||||
idx_vec: IndexVec<BorrowIndex, BorrowData<'tcx>>,
|
||||
location_map: FxHashMap<Location, BorrowIndex>,
|
||||
activation_map: FxHashMap<Location, FxHashSet<BorrowIndex>>,
|
||||
region_map: FxHashMap<Region<'tcx>, FxHashSet<BorrowIndex>>,
|
||||
local_map: FxHashMap<mir::Local, FxHashSet<BorrowIndex>>,
|
||||
region_span_map: FxHashMap<RegionKind, Span>,
|
||||
nonlexical_regioncx: Option<Rc<RegionInferenceContext<'tcx>>>,
|
||||
|
||||
/// When we encounter a 2-phase borrow statement, it will always
|
||||
/// be assigning into a temporary TEMP:
|
||||
///
|
||||
/// TEMP = &foo
|
||||
///
|
||||
/// We add TEMP into this map with `b`, where `b` is the index of
|
||||
/// the borrow. When we find a later use of this activation, we
|
||||
/// remove from the map (and add to the "tombstone" set below).
|
||||
pending_activations: FxHashMap<mir::Local, BorrowIndex>,
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'gcx, 'tcx> {
|
||||
fn visit_assign(&mut self,
|
||||
block: mir::BasicBlock,
|
||||
assigned_place: &mir::Place<'tcx>,
|
||||
rvalue: &mir::Rvalue<'tcx>,
|
||||
location: mir::Location) {
|
||||
fn root_local(mut p: &mir::Place<'_>) -> Option<mir::Local> {
|
||||
loop { match p {
|
||||
mir::Place::Projection(pi) => p = &pi.base,
|
||||
mir::Place::Static(_) => return None,
|
||||
mir::Place::Local(l) => return Some(*l)
|
||||
}}
|
||||
}
|
||||
|
||||
if let mir::Rvalue::Ref(region, kind, ref borrowed_place) = *rvalue {
|
||||
if is_unsafe_place(self.tcx, self.mir, borrowed_place) { return; }
|
||||
|
||||
let borrow = BorrowData {
|
||||
kind,
|
||||
region,
|
||||
reserve_location: location,
|
||||
activation_location: None,
|
||||
borrowed_place: borrowed_place.clone(),
|
||||
assigned_place: assigned_place.clone(),
|
||||
};
|
||||
let idx = self.idx_vec.push(borrow);
|
||||
self.location_map.insert(location, idx);
|
||||
|
||||
self.insert_as_pending_if_two_phase(
|
||||
location,
|
||||
&assigned_place,
|
||||
region,
|
||||
kind,
|
||||
idx,
|
||||
);
|
||||
|
||||
insert(&mut self.region_map, ®ion, idx);
|
||||
if let Some(local) = root_local(borrowed_place) {
|
||||
insert(&mut self.local_map, &local, idx);
|
||||
}
|
||||
}
|
||||
|
||||
return self.super_assign(block, assigned_place, rvalue, location);
|
||||
|
||||
fn insert<'a, K, V>(map: &'a mut FxHashMap<K, FxHashSet<V>>,
|
||||
k: &K,
|
||||
v: V)
|
||||
where K: Clone+Eq+Hash, V: Eq+Hash
|
||||
{
|
||||
map.entry(k.clone())
|
||||
.or_insert(FxHashSet())
|
||||
.insert(v);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_place(
|
||||
&mut self,
|
||||
place: &mir::Place<'tcx>,
|
||||
context: PlaceContext<'tcx>,
|
||||
location: Location,
|
||||
) {
|
||||
self.super_place(place, context, location);
|
||||
|
||||
// We found a use of some temporary TEMP...
|
||||
if let Place::Local(temp) = place {
|
||||
// ... check whether we (earlier) saw a 2-phase borrow like
|
||||
//
|
||||
// TMP = &mut place
|
||||
match self.pending_activations.get(temp) {
|
||||
Some(&borrow_index) => {
|
||||
let borrow_data = &mut self.idx_vec[borrow_index];
|
||||
|
||||
// Watch out: the use of TMP in the borrow
|
||||
// itself doesn't count as an
|
||||
// activation. =)
|
||||
if borrow_data.reserve_location == location
|
||||
&& context == PlaceContext::Store
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(other_activation) = borrow_data.activation_location {
|
||||
span_bug!(
|
||||
self.mir.source_info(location).span,
|
||||
"found two activations for 2-phase borrow temporary {:?}: \
|
||||
{:?} and {:?}",
|
||||
temp,
|
||||
location,
|
||||
other_activation,
|
||||
);
|
||||
}
|
||||
|
||||
// Otherwise, this is the unique later use
|
||||
// that we expect.
|
||||
borrow_data.activation_location = Some(location);
|
||||
self.activation_map
|
||||
.entry(location)
|
||||
.or_insert(FxHashSet())
|
||||
.insert(borrow_index);
|
||||
}
|
||||
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_rvalue(&mut self,
|
||||
rvalue: &mir::Rvalue<'tcx>,
|
||||
location: mir::Location) {
|
||||
if let mir::Rvalue::Ref(region, kind, ref place) = *rvalue {
|
||||
// double-check that we already registered a BorrowData for this
|
||||
|
||||
let borrow_index = self.location_map[&location];
|
||||
let borrow_data = &self.idx_vec[borrow_index];
|
||||
assert_eq!(borrow_data.reserve_location, location);
|
||||
assert_eq!(borrow_data.kind, kind);
|
||||
assert_eq!(borrow_data.region, region);
|
||||
assert_eq!(borrow_data.borrowed_place, *place);
|
||||
}
|
||||
|
||||
return self.super_rvalue(rvalue, location);
|
||||
}
|
||||
|
||||
fn visit_statement(&mut self,
|
||||
block: mir::BasicBlock,
|
||||
statement: &mir::Statement<'tcx>,
|
||||
location: Location) {
|
||||
if let mir::StatementKind::EndRegion(region_scope) = statement.kind {
|
||||
self.region_span_map.insert(ReScope(region_scope), statement.source_info.span);
|
||||
}
|
||||
return self.super_statement(block, statement, location);
|
||||
}
|
||||
}
|
||||
|
||||
/// A MIR visitor that determines if a specific place is used in a two-phase activating
|
||||
/// manner in a given chunk of MIR.
|
||||
struct ContainsUseOfPlace<'b, 'tcx: 'b> {
|
||||
target: &'b Place<'tcx>,
|
||||
use_found: bool,
|
||||
}
|
||||
|
||||
impl<'b, 'tcx: 'b> ContainsUseOfPlace<'b, 'tcx> {
|
||||
fn new(place: &'b Place<'tcx>) -> Self {
|
||||
Self { target: place, use_found: false }
|
||||
}
|
||||
|
||||
/// return whether `context` should be considered a "use" of a
|
||||
/// place found in that context. "Uses" activate associated
|
||||
/// borrows (at least when such uses occur while the borrow also
|
||||
/// has a reservation at the time).
|
||||
fn is_potential_use(context: PlaceContext) -> bool {
|
||||
match context {
|
||||
// storage effects on a place do not activate it
|
||||
PlaceContext::StorageLive | PlaceContext::StorageDead => false,
|
||||
|
||||
// validation effects do not activate a place
|
||||
//
|
||||
// FIXME: Should they? Is it just another read? Or can we
|
||||
// guarantee it won't dereference the stored address? How
|
||||
// "deep" does validation go?
|
||||
PlaceContext::Validate => false,
|
||||
|
||||
// FIXME: This is here to not change behaviour from before
|
||||
// AsmOutput existed, but it's not necessarily a pure overwrite.
|
||||
// so it's possible this should activate the place.
|
||||
PlaceContext::AsmOutput |
|
||||
// pure overwrites of a place do not activate it. (note
|
||||
// PlaceContext::Call is solely about dest place)
|
||||
PlaceContext::Store | PlaceContext::Call => false,
|
||||
|
||||
// reads of a place *do* activate it
|
||||
PlaceContext::Move |
|
||||
PlaceContext::Copy |
|
||||
PlaceContext::Drop |
|
||||
PlaceContext::Inspect |
|
||||
PlaceContext::Borrow { .. } |
|
||||
PlaceContext::Projection(..) => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b, 'tcx: 'b> Visitor<'tcx> for ContainsUseOfPlace<'b, 'tcx> {
|
||||
fn visit_place(&mut self,
|
||||
place: &mir::Place<'tcx>,
|
||||
context: PlaceContext<'tcx>,
|
||||
location: Location) {
|
||||
if Self::is_potential_use(context) && place == self.target {
|
||||
self.use_found = true;
|
||||
return;
|
||||
// There is no need to keep checking the statement, we already found a use
|
||||
}
|
||||
|
||||
self.super_place(place, context, location);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> GatherBorrows<'a, 'gcx, 'tcx> {
|
||||
/// Returns true if the borrow represented by `kind` is
|
||||
/// allowed to be split into separate Reservation and
|
||||
/// Activation phases.
|
||||
fn allow_two_phase_borrow(&self, kind: mir::BorrowKind) -> bool {
|
||||
self.tcx.two_phase_borrows() &&
|
||||
(kind.allows_two_phase_borrow() ||
|
||||
self.tcx.sess.opts.debugging_opts.two_phase_beyond_autoref)
|
||||
}
|
||||
|
||||
/// Returns true if the given location contains an NLL-activating use of the given place
|
||||
fn location_contains_use(&self, location: Location, place: &Place) -> bool {
|
||||
let mut use_checker = ContainsUseOfPlace::new(place);
|
||||
let block = &self.mir.basic_blocks().get(location.block).unwrap_or_else(|| {
|
||||
panic!("could not find block at location {:?}", location);
|
||||
});
|
||||
if location.statement_index != block.statements.len() {
|
||||
// This is a statement
|
||||
let stmt = block.statements.get(location.statement_index).unwrap_or_else(|| {
|
||||
panic!("could not find statement at location {:?}");
|
||||
});
|
||||
use_checker.visit_statement(location.block, stmt, location);
|
||||
} else {
|
||||
// This is a terminator
|
||||
match block.terminator {
|
||||
Some(ref term) => {
|
||||
use_checker.visit_terminator(location.block, term, location);
|
||||
}
|
||||
None => {
|
||||
// There is no way for Place to be used by the terminator if there is no
|
||||
// terminator
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use_checker.use_found
|
||||
}
|
||||
|
||||
/// Determines if the provided region is terminated after the provided location.
|
||||
/// EndRegion statements terminate their enclosed region::Scope.
|
||||
/// We also consult with the NLL region inference engine, should one be available
|
||||
fn region_terminated_after(&self, region: Region<'tcx>, location: Location) -> bool {
|
||||
let block_data = &self.mir[location.block];
|
||||
if location.statement_index != block_data.statements.len() {
|
||||
let stmt = &block_data.statements[location.statement_index];
|
||||
if let mir::StatementKind::EndRegion(region_scope) = stmt.kind {
|
||||
if &ReScope(region_scope) == region {
|
||||
// We encountered an EndRegion statement that terminates the provided
|
||||
// region
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(ref regioncx) = self.nonlexical_regioncx {
|
||||
if !regioncx.region_contains_point(region, location) {
|
||||
// NLL says the region has ended already
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// If this is a two-phase borrow, then we will record it
|
||||
/// as "pending" until we find the activating use.
|
||||
fn insert_as_pending_if_two_phase(
|
||||
&mut self,
|
||||
start_location: Location,
|
||||
assigned_place: &mir::Place<'tcx>,
|
||||
region: Region<'tcx>,
|
||||
kind: mir::BorrowKind,
|
||||
borrow_index: BorrowIndex,
|
||||
) {
|
||||
debug!(
|
||||
"Borrows::insert_as_pending_if_two_phase({:?}, {:?}, {:?}, {:?})",
|
||||
start_location, assigned_place, region, borrow_index,
|
||||
);
|
||||
|
||||
if !self.allow_two_phase_borrow(kind) {
|
||||
debug!(" -> {:?}", start_location);
|
||||
return;
|
||||
}
|
||||
|
||||
// When we encounter a 2-phase borrow statement, it will always
|
||||
// be assigning into a temporary TEMP:
|
||||
//
|
||||
// TEMP = &foo
|
||||
//
|
||||
// so extract `temp`.
|
||||
let temp = if let &mir::Place::Local(temp) = assigned_place {
|
||||
temp
|
||||
} else {
|
||||
span_bug!(
|
||||
self.mir.source_info(start_location).span,
|
||||
"expected 2-phase borrow to assign to a local, not `{:?}`",
|
||||
assigned_place,
|
||||
);
|
||||
};
|
||||
|
||||
// Insert `temp` into the list of pending activations. From
|
||||
// now on, we'll be on the lookout for a use of it. Note that
|
||||
// we are guaranteed that this use will come after the
|
||||
// assignment.
|
||||
let old_value = self.pending_activations.insert(temp, borrow_index);
|
||||
assert!(old_value.is_none());
|
||||
}
|
||||
Borrows {
|
||||
tcx: tcx,
|
||||
mir: mir,
|
||||
borrow_set,
|
||||
scope_tree,
|
||||
root_scope,
|
||||
nonlexical_regioncx,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -597,7 +236,7 @@ impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> {
|
|||
// propagate_call_return method.
|
||||
|
||||
if let mir::Rvalue::Ref(region, _, ref place) = *rhs {
|
||||
if is_unsafe_place(self.tcx, self.mir, place) { return; }
|
||||
if place.is_unsafe_place(self.tcx, self.mir) { return; }
|
||||
let index = self.borrow_set.location_map.get(&location).unwrap_or_else(|| {
|
||||
panic!("could not find BorrowIndex for location {:?}", location);
|
||||
});
|
||||
|
@ -744,34 +383,3 @@ impl<'a, 'gcx, 'tcx> InitialFlow for Borrows<'a, 'gcx, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn is_unsafe_place<'a, 'gcx: 'tcx, 'tcx: 'a>(
|
||||
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||
mir: &'a Mir<'tcx>,
|
||||
place: &mir::Place<'tcx>
|
||||
) -> bool {
|
||||
use self::mir::Place::*;
|
||||
use self::mir::ProjectionElem;
|
||||
|
||||
match *place {
|
||||
Local(_) => false,
|
||||
Static(ref static_) => tcx.is_static(static_.def_id) == Some(hir::Mutability::MutMutable),
|
||||
Projection(ref proj) => {
|
||||
match proj.elem {
|
||||
ProjectionElem::Field(..) |
|
||||
ProjectionElem::Downcast(..) |
|
||||
ProjectionElem::Subslice { .. } |
|
||||
ProjectionElem::ConstantIndex { .. } |
|
||||
ProjectionElem::Index(_) => {
|
||||
is_unsafe_place(tcx, mir, &proj.base)
|
||||
}
|
||||
ProjectionElem::Deref => {
|
||||
let ty = proj.base.ty(mir, tcx).to_ty(tcx);
|
||||
match ty.sty {
|
||||
ty::TyRawPtr(..) => true,
|
||||
_ => is_unsafe_place(tcx, mir, &proj.base),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue