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
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
|
use borrow_check::place_ext::PlaceExt;
|
||||||
use dataflow::indexes::BorrowIndex;
|
use dataflow::indexes::BorrowIndex;
|
||||||
use rustc::mir::{self, Location};
|
use rustc::mir::traversal;
|
||||||
use rustc::ty::{Region, RegionKind};
|
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::util::nodemap::{FxHashMap, FxHashSet};
|
||||||
use rustc_data_structures::indexed_vec::IndexVec;
|
use rustc_data_structures::indexed_vec::IndexVec;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::hash::Hash;
|
||||||
use syntax_pos::Span;
|
use syntax_pos::Span;
|
||||||
|
|
||||||
crate struct BorrowSet<'tcx> {
|
crate struct BorrowSet<'tcx> {
|
||||||
|
@ -71,8 +75,250 @@ impl<'tcx> fmt::Display for BorrowData<'tcx> {
|
||||||
mir::BorrowKind::Mut { .. } => "mut ",
|
mir::BorrowKind::Mut { .. } => "mut ",
|
||||||
};
|
};
|
||||||
let region = format!("{}", self.region);
|
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)
|
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;
|
crate mod borrow_set;
|
||||||
mod error_reporting;
|
mod error_reporting;
|
||||||
mod flows;
|
mod flows;
|
||||||
|
crate mod place_ext;
|
||||||
mod prefixes;
|
mod prefixes;
|
||||||
|
|
||||||
pub(crate) mod nll;
|
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.
|
// except according to those terms.
|
||||||
|
|
||||||
use borrow_check::borrow_set::{BorrowSet, BorrowData};
|
use borrow_check::borrow_set::{BorrowSet, BorrowData};
|
||||||
|
use borrow_check::place_ext::PlaceExt;
|
||||||
|
|
||||||
use rustc;
|
use rustc;
|
||||||
use rustc::hir;
|
use rustc::hir;
|
||||||
use rustc::hir::def_id::DefId;
|
use rustc::hir::def_id::DefId;
|
||||||
use rustc::middle::region;
|
use rustc::middle::region;
|
||||||
use rustc::mir::{self, Location, Place, Mir};
|
use rustc::mir::{self, Location, Place, Mir};
|
||||||
use rustc::mir::traversal;
|
use rustc::ty::{Region, TyCtxt};
|
||||||
use rustc::mir::visit::{PlaceContext, Visitor};
|
|
||||||
use rustc::ty::{self, Region, TyCtxt};
|
|
||||||
use rustc::ty::RegionKind;
|
use rustc::ty::RegionKind;
|
||||||
use rustc::ty::RegionKind::ReScope;
|
use rustc::ty::RegionKind::ReScope;
|
||||||
use rustc::util::nodemap::{FxHashMap, FxHashSet};
|
|
||||||
|
|
||||||
use rustc_data_structures::bitslice::{BitwiseOperator};
|
use rustc_data_structures::bitslice::{BitwiseOperator};
|
||||||
use rustc_data_structures::indexed_set::{IdxSet};
|
use rustc_data_structures::indexed_set::{IdxSet};
|
||||||
|
@ -34,7 +32,6 @@ use borrow_check::nll::ToRegionVid;
|
||||||
|
|
||||||
use syntax_pos::Span;
|
use syntax_pos::Span;
|
||||||
|
|
||||||
use std::hash::Hash;
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
/// `Borrows` stores the data used in the analyses that track the flow
|
/// `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| {
|
let root_scope = body_id.map(|body_id| {
|
||||||
region::Scope::CallSite(tcx.hir.body(body_id).value.hir_id.local_id)
|
region::Scope::CallSite(tcx.hir.body(body_id).value.hir_id.local_id)
|
||||||
});
|
});
|
||||||
let mut visitor = GatherBorrows {
|
let borrow_set = BorrowSet::build(tcx, mir);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Double check: We should have found an activation for every pending
|
Borrows {
|
||||||
// activation.
|
tcx: tcx,
|
||||||
assert_eq!(
|
mir: mir,
|
||||||
visitor
|
borrow_set,
|
||||||
.pending_activations
|
scope_tree,
|
||||||
.iter()
|
root_scope,
|
||||||
.find(|&(_local, &borrow_index)| {
|
nonlexical_regioncx,
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -597,7 +236,7 @@ impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> {
|
||||||
// propagate_call_return method.
|
// propagate_call_return method.
|
||||||
|
|
||||||
if let mir::Rvalue::Ref(region, _, ref place) = *rhs {
|
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(|| {
|
let index = self.borrow_set.location_map.get(&location).unwrap_or_else(|| {
|
||||||
panic!("could not find BorrowIndex for location {:?}", location);
|
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