Auto merge of #62800 - albins:polonius-initialization-1, r=nikomatsakis

Extend Polonius fact generation for (some) move tracking

This PR will extend rustc to emit facts used for tracking moves and initialization in Polonius. It is most likely the final part of my master's thesis work.
This commit is contained in:
bors 2019-09-05 08:51:38 +00:00
commit 9776723881
13 changed files with 322 additions and 272 deletions

View file

@ -2324,9 +2324,9 @@ checksum = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c"
[[package]]
name = "polonius-engine"
version = "0.9.0"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6b8a5defa2aef9ba4999aaa745fbc01c622ecea35964a306adc3e44be4f3b5b"
checksum = "50fa9dbfd0d3d60594da338cfe6f94028433eecae4b11b7e83fd99759227bbfe"
dependencies = [
"datafrog",
"log",

View file

@ -21,7 +21,7 @@ scoped-tls = "1.0"
log = { version = "0.4", features = ["release_max_level_info", "std"] }
rustc-rayon = "0.2.0"
rustc-rayon-core = "0.2.0"
polonius-engine = "0.9.0"
polonius-engine = "0.10.0"
rustc_apfloat = { path = "../librustc_apfloat" }
rustc_target = { path = "../librustc_target" }
rustc_macros = { path = "../librustc_macros" }

View file

@ -15,7 +15,7 @@ either = "1.5.0"
dot = { path = "../libgraphviz", package = "graphviz" }
log = "0.4"
log_settings = "0.1.1"
polonius-engine = "0.9.0"
polonius-engine = "0.10.0"
rustc = { path = "../librustc" }
rustc_target = { path = "../librustc_target" }
rustc_data_structures = { path = "../librustc_data_structures" }

View file

@ -12,7 +12,7 @@ use crate::borrow_check::location::LocationIndex;
use polonius_engine::Output;
use crate::dataflow::indexes::BorrowIndex;
use crate::dataflow::move_paths::HasMoveData;
use crate::dataflow::move_paths::{HasMoveData, MovePathIndex};
use crate::dataflow::Borrows;
use crate::dataflow::EverInitializedPlaces;
use crate::dataflow::MaybeUninitializedPlaces;
@ -21,7 +21,7 @@ use either::Either;
use std::fmt;
use std::rc::Rc;
crate type PoloniusOutput = Output<RegionVid, BorrowIndex, LocationIndex, Local>;
crate type PoloniusOutput = Output<RegionVid, BorrowIndex, LocationIndex, Local, MovePathIndex>;
// (forced to be `pub` due to its use as an associated type below.)
crate struct Flows<'b, 'tcx> {

View file

@ -1,5 +1,5 @@
use crate::borrow_check::location::{LocationIndex, LocationTable};
use crate::dataflow::indexes::BorrowIndex;
use crate::dataflow::indexes::{BorrowIndex, MovePathIndex};
use polonius_engine::AllFacts as PoloniusAllFacts;
use polonius_engine::Atom;
use rustc::mir::Local;
@ -11,7 +11,7 @@ use std::fs::{self, File};
use std::io::Write;
use std::path::Path;
crate type AllFacts = PoloniusAllFacts<RegionVid, BorrowIndex, LocationIndex, Local>;
crate type AllFacts = PoloniusAllFacts<RegionVid, BorrowIndex, LocationIndex, Local, MovePathIndex>;
crate trait AllFactsExt {
/// Returns `true` if there is a need to gather `AllFacts` given the
@ -58,14 +58,17 @@ impl AllFactsExt for AllFacts {
cfg_edge,
killed,
outlives,
region_live_at,
invalidates,
var_used,
var_defined,
var_drop_used,
var_uses_region,
var_drops_region,
var_initialized_on_exit,
child,
path_belongs_to_var,
initialized_at,
moved_out_at,
path_accessed_at,
])
}
Ok(())
@ -84,6 +87,12 @@ impl Atom for LocationIndex {
}
}
impl Atom for MovePathIndex {
fn index(self) -> usize {
Idx::index(self)
}
}
struct FactWriter<'w> {
location_table: &'w LocationTable,
dir: &'w Path,

View file

@ -4,14 +4,15 @@ use crate::borrow_check::nll::facts::AllFactsExt;
use crate::borrow_check::nll::type_check::{MirTypeckResults, MirTypeckRegionConstraints};
use crate::borrow_check::nll::region_infer::values::RegionValueElements;
use crate::dataflow::indexes::BorrowIndex;
use crate::dataflow::move_paths::MoveData;
use crate::dataflow::move_paths::{InitLocation, MoveData, MovePathIndex, InitKind};
use crate::dataflow::FlowAtLocation;
use crate::dataflow::MaybeInitializedPlaces;
use crate::transform::MirSource;
use crate::borrow_check::Upvar;
use rustc::hir::def_id::DefId;
use rustc::infer::InferCtxt;
use rustc::mir::{ClosureOutlivesSubject, ClosureRegionRequirements, Local, Body, Promoted};
use rustc::mir::{ClosureOutlivesSubject, ClosureRegionRequirements,
Local, Location, Body, LocalKind, BasicBlock, Promoted};
use rustc::ty::{self, RegionKind, RegionVid};
use rustc_data_structures::indexed_vec::IndexVec;
use rustc_errors::Diagnostic;
@ -69,6 +70,85 @@ pub(in crate::borrow_check) fn replace_regions_in_mir<'cx, 'tcx>(
universal_regions
}
// This function populates an AllFacts instance with base facts related to
// MovePaths and needed for the move analysis.
fn populate_polonius_move_facts(
all_facts: &mut AllFacts,
move_data: &MoveData<'_>,
location_table: &LocationTable,
body: &Body<'_>) {
all_facts
.path_belongs_to_var
.extend(
move_data
.rev_lookup
.iter_locals_enumerated()
.map(|(v, &m)| (m, v)));
for (child, move_path) in move_data.move_paths.iter_enumerated() {
all_facts
.child
.extend(
move_path
.parents(&move_data.move_paths)
.iter()
.map(|&parent| (child, parent)));
}
// initialized_at
for init in move_data.inits.iter() {
match init.location {
InitLocation::Statement(location) => {
let block_data = &body[location.block];
let is_terminator = location.statement_index == block_data.statements.len();
if is_terminator && init.kind == InitKind::NonPanicPathOnly {
// We are at the terminator of an init that has a panic path,
// and where the init should not happen on panic
for &successor in block_data.terminator().successors() {
if body[successor].is_cleanup {
continue;
}
// The initialization happened in (or rather, when arriving at)
// the successors, but not in the unwind block.
let first_statement = Location { block: successor, statement_index: 0};
all_facts
.initialized_at
.push((init.path, location_table.start_index(first_statement)));
}
} else {
// In all other cases, the initialization just happens at the
// midpoint, like any other effect.
all_facts.initialized_at.push((init.path, location_table.mid_index(location)));
}
},
// Arguments are initialized on function entry
InitLocation::Argument(local) => {
assert!(body.local_kind(local) == LocalKind::Arg);
let fn_entry = Location {block: BasicBlock::from_u32(0u32), statement_index: 0 };
all_facts.initialized_at.push((init.path, location_table.start_index(fn_entry)));
}
}
}
// moved_out_at
// deinitialisation is assumed to always happen!
all_facts
.moved_out_at
.extend(
move_data
.moves
.iter()
.map(|mo| (mo.path, location_table.mid_index(mo.source))));
}
/// Computes the (non-lexical) regions from the input MIR.
///
/// This may result in errors being reported.
@ -87,7 +167,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>(
errors_buffer: &mut Vec<Diagnostic>,
) -> (
RegionInferenceContext<'tcx>,
Option<Rc<Output<RegionVid, BorrowIndex, LocationIndex, Local>>>,
Option<Rc<Output<RegionVid, BorrowIndex, LocationIndex, Local, MovePathIndex>>>,
Option<ClosureRegionRequirements<'tcx>>,
) {
let mut all_facts = if AllFacts::enabled(infcx.tcx) {
@ -123,6 +203,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>(
all_facts
.universal_region
.extend(universal_regions.universal_regions());
populate_polonius_move_facts(all_facts, move_data, location_table, body);
}
// Create the region inference context, taking ownership of the

View file

@ -1,7 +1,7 @@
use crate::borrow_check::nll::region_infer::values::{PointIndex, RegionValueElements};
use crate::util::liveness::{categorize, DefUse};
use rustc::mir::visit::{PlaceContext, Visitor};
use rustc::mir::{Local, Location, Body};
use rustc::mir::{Body, Local, Location};
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
use rustc_data_structures::vec_linked_list as vll;
@ -72,16 +72,10 @@ impl LocalUseMap {
let mut locals_with_use_data: IndexVec<Local, bool> =
IndexVec::from_elem_n(false, body.local_decls.len());
live_locals
.iter()
.for_each(|&local| locals_with_use_data[local] = true);
live_locals.iter().for_each(|&local| locals_with_use_data[local] = true);
LocalUseMapBuild {
local_use_map: &mut local_use_map,
elements,
locals_with_use_data,
}
.visit_body(body);
LocalUseMapBuild { local_use_map: &mut local_use_map, elements, locals_with_use_data }
.visit_body(body);
local_use_map
}
@ -151,10 +145,8 @@ impl LocalUseMapBuild<'_> {
location: Location,
) {
let point_index = elements.point_from_location(location);
let appearance_index = appearances.push(Appearance {
point_index,
next: *first_appearance,
});
let appearance_index =
appearances.push(Appearance { point_index, next: *first_appearance });
*first_appearance = Some(appearance_index);
}
}

View file

@ -58,9 +58,9 @@ pub(super) fn generate<'tcx>(
};
if !live_locals.is_empty() {
trace::trace(typeck, body, elements, flow_inits, move_data, live_locals, location_table);
trace::trace(typeck, body, elements, flow_inits, move_data, live_locals);
polonius::populate_var_liveness_facts(typeck, body, location_table);
polonius::populate_access_facts(typeck, body, location_table, move_data);
}
}

View file

@ -1,22 +1,28 @@
use crate::borrow_check::location::{LocationIndex, LocationTable};
use crate::dataflow::indexes::MovePathIndex;
use crate::dataflow::move_paths::{LookupResult, MoveData};
use crate::util::liveness::{categorize, DefUse};
use rustc::mir::visit::{PlaceContext, Visitor};
use rustc::mir::{Body, Local, Location};
use rustc::mir::visit::{MutatingUseContext, PlaceContext, Visitor};
use rustc::mir::{Body, Local, Location, Place};
use rustc::ty::subst::Kind;
use rustc::ty::Ty;
use super::TypeChecker;
type VarPointRelations = Vec<(Local, LocationIndex)>;
type MovePathPointRelations = Vec<(MovePathIndex, LocationIndex)>;
struct LivenessPointFactsExtractor<'me> {
struct UseFactsExtractor<'me> {
var_defined: &'me mut VarPointRelations,
var_used: &'me mut VarPointRelations,
location_table: &'me LocationTable,
var_drop_used: &'me mut VarPointRelations,
move_data: &'me MoveData<'me>,
path_accessed_at: &'me mut MovePathPointRelations,
}
// A Visitor to walk through the MIR and extract point-wise facts
impl LivenessPointFactsExtractor<'_> {
impl UseFactsExtractor<'_> {
fn location_to_index(&self, location: Location) -> LocationIndex {
self.location_table.mid_index(location)
}
@ -30,15 +36,50 @@ impl LivenessPointFactsExtractor<'_> {
debug!("LivenessFactsExtractor::insert_use()");
self.var_used.push((local, self.location_to_index(location)));
}
fn insert_drop_use(&mut self, local: Local, location: Location) {
debug!("LivenessFactsExtractor::insert_drop_use()");
self.var_drop_used.push((local, self.location_to_index(location)));
}
fn insert_path_access(&mut self, path: MovePathIndex, location: Location) {
debug!("LivenessFactsExtractor::insert_path_access({:?}, {:?})", path, location);
self.path_accessed_at.push((path, self.location_to_index(location)));
}
fn place_to_mpi(&self, place: &Place<'_>) -> Option<MovePathIndex> {
match self.move_data.rev_lookup.find(place.as_ref()) {
LookupResult::Exact(mpi) => Some(mpi),
LookupResult::Parent(mmpi) => mmpi,
}
}
}
impl Visitor<'tcx> for LivenessPointFactsExtractor<'_> {
impl Visitor<'tcx> for UseFactsExtractor<'_> {
fn visit_local(&mut self, &local: &Local, context: PlaceContext, location: Location) {
match categorize(context) {
Some(DefUse::Def) => self.insert_def(local, location),
Some(DefUse::Use) => self.insert_use(local, location),
Some(DefUse::Drop) => self.insert_drop_use(local, location),
_ => (),
}
}
fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) {
self.super_place(place, context, location);
match context {
PlaceContext::NonMutatingUse(_) => {
if let Some(mpi) = self.place_to_mpi(place) {
self.insert_path_access(mpi, location);
}
}
PlaceContext::MutatingUse(MutatingUseContext::Borrow) => {
if let Some(mpi) = self.place_to_mpi(place) {
self.insert_path_access(mpi, location);
}
}
_ => (),
// NOTE: Drop handling is now done in trace()
}
}
}
@ -54,23 +95,27 @@ fn add_var_uses_regions(typeck: &mut TypeChecker<'_, 'tcx>, local: Local, ty: Ty
});
}
pub(super) fn populate_var_liveness_facts(
pub(super) fn populate_access_facts(
typeck: &mut TypeChecker<'_, 'tcx>,
mir: &Body<'tcx>,
body: &Body<'tcx>,
location_table: &LocationTable,
move_data: &MoveData<'_>,
) {
debug!("populate_var_liveness_facts()");
if let Some(facts) = typeck.borrowck_context.all_facts.as_mut() {
LivenessPointFactsExtractor {
UseFactsExtractor {
var_defined: &mut facts.var_defined,
var_used: &mut facts.var_used,
var_drop_used: &mut facts.var_drop_used,
path_accessed_at: &mut facts.path_accessed_at,
location_table,
move_data,
}
.visit_body(mir);
.visit_body(body);
}
for (local, local_decl) in mir.local_decls.iter_enumerated() {
for (local, local_decl) in body.local_decls.iter_enumerated() {
add_var_uses_regions(typeck, local, local_decl.ty);
}
}

View file

@ -1,4 +1,3 @@
use crate::borrow_check::location::LocationTable;
use crate::borrow_check::nll::region_infer::values::{self, PointIndex, RegionValueElements};
use crate::borrow_check::nll::type_check::liveness::local_use_map::LocalUseMap;
use crate::borrow_check::nll::type_check::liveness::polonius;
@ -38,7 +37,6 @@ pub(super) fn trace(
flow_inits: &mut FlowAtLocation<'tcx, MaybeInitializedPlaces<'_, 'tcx>>,
move_data: &MoveData<'tcx>,
live_locals: Vec<Local>,
location_table: &LocationTable,
) {
debug!("trace()");
@ -52,7 +50,6 @@ pub(super) fn trace(
local_use_map,
move_data,
drop_data: FxHashMap::default(),
location_table,
};
LivenessResults::new(cx).compute_for_all_locals(live_locals);
@ -82,9 +79,6 @@ struct LivenessContext<'me, 'typeck, 'flow, 'tcx> {
/// Index indicating where each variable is assigned, used, or
/// dropped.
local_use_map: &'me LocalUseMap,
/// Maps between a MIR Location and a LocationIndex
location_table: &'me LocationTable,
}
struct DropData<'tcx> {
@ -131,12 +125,6 @@ impl LivenessResults<'me, 'typeck, 'flow, 'tcx> {
for local in live_locals {
self.reset_local_state();
self.add_defs_for(local);
// FIXME: this is temporary until we can generate our own initialization
if self.cx.typeck.borrowck_context.all_facts.is_some() {
self.add_polonius_var_initialized_on_exit_for(local)
}
self.compute_use_live_points_for(local);
self.compute_drop_live_points_for(local);
@ -157,63 +145,6 @@ impl LivenessResults<'me, 'typeck, 'flow, 'tcx> {
}
}
// WARNING: panics if self.cx.typeck.borrowck_context.all_facts != None
//
// FIXME: this analysis (the initialization tracking) should be
// done in Polonius, but isn't yet.
fn add_polonius_var_initialized_on_exit_for(&mut self, local: Local) {
let move_path = self.cx.move_data.rev_lookup.find_local(local);
let facts = self.cx.typeck.borrowck_context.all_facts.as_mut().unwrap();
for block in self.cx.body.basic_blocks().indices() {
debug!("polonius: generating initialization facts for {:?} in {:?}", local, block);
// iterate through the block, applying the effects of each statement
// up to and including location, and populate `var_initialized_on_exit`
self.cx.flow_inits.reset_to_entry_of(block);
let start_location = Location { block, statement_index: 0 };
self.cx.flow_inits.apply_local_effect(start_location);
for statement_index in 0..self.cx.body[block].statements.len() {
let current_location = Location { block, statement_index };
self.cx.flow_inits.reconstruct_statement_effect(current_location);
// statement has not yet taken effect:
if self.cx.flow_inits.has_any_child_of(move_path).is_some() {
facts
.var_initialized_on_exit
.push((local, self.cx.location_table.start_index(current_location)));
}
// statement has now taken effect
self.cx.flow_inits.apply_local_effect(current_location);
if self.cx.flow_inits.has_any_child_of(move_path).is_some() {
facts
.var_initialized_on_exit
.push((local, self.cx.location_table.mid_index(current_location)));
}
}
let terminator_location = self.cx.body.terminator_loc(block);
if self.cx.flow_inits.has_any_child_of(move_path).is_some() {
facts
.var_initialized_on_exit
.push((local, self.cx.location_table.start_index(terminator_location)));
}
// apply the effects of the terminator and push it if needed
self.cx.flow_inits.reset_to_exit_of(block);
if self.cx.flow_inits.has_any_child_of(move_path).is_some() {
facts
.var_initialized_on_exit
.push((local, self.cx.location_table.mid_index(terminator_location)));
}
}
}
/// Clear the value of fields that are "per local variable".
fn reset_local_state(&mut self) {
self.defs.clear();
@ -273,11 +204,6 @@ impl LivenessResults<'me, 'typeck, 'flow, 'tcx> {
debug_assert_eq!(self.cx.body.terminator_loc(location.block), location,);
if self.cx.initialized_at_terminator(location.block, mpi) {
// FIXME: this analysis (the initialization tracking) should be
// done in Polonius, but isn't yet.
if let Some(facts) = self.cx.typeck.borrowck_context.all_facts {
facts.var_drop_used.push((local, self.cx.location_table.mid_index(location)));
}
if self.drop_live_at.insert(drop_point) {
self.drop_locations.push(location);
self.stack.push(drop_point);
@ -468,13 +394,7 @@ impl LivenessContext<'_, '_, '_, 'tcx> {
) {
debug!("add_use_live_facts_for(value={:?})", value);
Self::make_all_regions_live(
self.elements,
&mut self.typeck,
value,
live_at,
self.location_table,
)
Self::make_all_regions_live(self.elements, &mut self.typeck, value, live_at)
}
/// Some variable with type `live_ty` is "drop live" at `location`
@ -525,13 +445,7 @@ impl LivenessContext<'_, '_, '_, 'tcx> {
// All things in the `outlives` array may be touched by
// the destructor and must be live at this point.
for &kind in &drop_data.dropck_result.kinds {
Self::make_all_regions_live(
self.elements,
&mut self.typeck,
kind,
live_at,
self.location_table,
);
Self::make_all_regions_live(self.elements, &mut self.typeck, kind, live_at);
polonius::add_var_drops_regions(&mut self.typeck, dropped_local, &kind);
}
@ -542,7 +456,6 @@ impl LivenessContext<'_, '_, '_, 'tcx> {
typeck: &mut TypeChecker<'_, 'tcx>,
value: impl TypeFoldable<'tcx>,
live_at: &HybridBitSet<PointIndex>,
location_table: &LocationTable,
) {
debug!("make_all_regions_live(value={:?})", value);
debug!(
@ -559,15 +472,6 @@ impl LivenessContext<'_, '_, '_, 'tcx> {
.constraints
.liveness_constraints
.add_elements(live_region_vid, live_at);
// FIXME: remove this when we can generate our own region-live-at reliably
if let Some(facts) = typeck.borrowck_context.all_facts {
for point in live_at.iter() {
let loc = elements.to_location(point);
facts.region_live_at.push((live_region_vid, location_table.start_index(loc)));
facts.region_live_at.push((live_region_vid, location_table.mid_index(loc)));
}
}
});
}

View file

@ -11,7 +11,7 @@
//! `a[x]` would still overlap them both. But that is not this
//! representation does today.)
use rustc::mir::{Local, PlaceElem, Operand, ProjectionElem};
use rustc::mir::{Local, Operand, PlaceElem, ProjectionElem};
use rustc::ty::Ty;
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
@ -26,36 +26,36 @@ pub trait Lift {
}
impl<'tcx> Lift for Operand<'tcx> {
type Abstract = AbstractOperand;
fn lift(&self) -> Self::Abstract { AbstractOperand }
fn lift(&self) -> Self::Abstract {
AbstractOperand
}
}
impl Lift for Local {
type Abstract = AbstractOperand;
fn lift(&self) -> Self::Abstract { AbstractOperand }
fn lift(&self) -> Self::Abstract {
AbstractOperand
}
}
impl<'tcx> Lift for Ty<'tcx> {
type Abstract = AbstractType;
fn lift(&self) -> Self::Abstract { AbstractType }
fn lift(&self) -> Self::Abstract {
AbstractType
}
}
impl<'tcx> Lift for PlaceElem<'tcx> {
type Abstract = AbstractElem;
fn lift(&self) -> Self::Abstract {
match *self {
ProjectionElem::Deref =>
ProjectionElem::Deref,
ProjectionElem::Field(ref f, ty) =>
ProjectionElem::Field(f.clone(), ty.lift()),
ProjectionElem::Index(ref i) =>
ProjectionElem::Index(i.lift()),
ProjectionElem::Subslice {from, to} =>
ProjectionElem::Subslice { from: from, to: to },
ProjectionElem::ConstantIndex {offset,min_length,from_end} =>
ProjectionElem::ConstantIndex {
offset,
min_length,
from_end,
},
ProjectionElem::Downcast(a, u) =>
ProjectionElem::Downcast(a, u.clone()),
ProjectionElem::Deref => ProjectionElem::Deref,
ProjectionElem::Field(ref f, ty) => ProjectionElem::Field(f.clone(), ty.lift()),
ProjectionElem::Index(ref i) => ProjectionElem::Index(i.lift()),
ProjectionElem::Subslice { from, to } => {
ProjectionElem::Subslice { from: from, to: to }
}
ProjectionElem::ConstantIndex { offset, min_length, from_end } => {
ProjectionElem::ConstantIndex { offset, min_length, from_end }
}
ProjectionElem::Downcast(a, u) => ProjectionElem::Downcast(a, u.clone()),
}
}
}

View file

@ -1,16 +1,18 @@
use rustc::ty::{self, TyCtxt};
use rustc::mir::*;
use rustc::mir::tcx::RvalueInitializationState;
use rustc_data_structures::indexed_vec::{IndexVec};
use smallvec::{SmallVec, smallvec};
use rustc::mir::*;
use rustc::ty::{self, TyCtxt};
use rustc_data_structures::indexed_vec::IndexVec;
use smallvec::{smallvec, SmallVec};
use std::collections::hash_map::Entry;
use std::mem;
use super::abs_domain::Lift;
use super::{LocationMap, MoveData, MovePath, MovePathLookup, MovePathIndex, MoveOut, MoveOutIndex};
use super::{MoveError, InitIndex, Init, InitLocation, LookupResult, InitKind};
use super::IllegalMoveOriginKind::*;
use super::{Init, InitIndex, InitKind, InitLocation, LookupResult, MoveError};
use super::{
LocationMap, MoveData, MoveOut, MoveOutIndex, MovePath, MovePathIndex, MovePathLookup,
};
struct MoveDataBuilder<'a, 'tcx> {
body: &'a Body<'tcx>,
@ -33,15 +35,19 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
moves: IndexVec::new(),
loc_map: LocationMap::new(body),
rev_lookup: MovePathLookup {
locals: body.local_decls.indices().map(|i| {
Self::new_move_path(
&mut move_paths,
&mut path_map,
&mut init_path_map,
None,
Place::from(i),
)
}).collect(),
locals: body
.local_decls
.indices()
.map(|i| {
Self::new_move_path(
&mut move_paths,
&mut path_map,
&mut init_path_map,
None,
Place::from(i),
)
})
.collect(),
projections: Default::default(),
},
move_paths,
@ -49,27 +55,22 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
inits: IndexVec::new(),
init_loc_map: LocationMap::new(body),
init_path_map,
}
},
}
}
fn new_move_path(move_paths: &mut IndexVec<MovePathIndex, MovePath<'tcx>>,
path_map: &mut IndexVec<MovePathIndex, SmallVec<[MoveOutIndex; 4]>>,
init_path_map: &mut IndexVec<MovePathIndex, SmallVec<[InitIndex; 4]>>,
parent: Option<MovePathIndex>,
place: Place<'tcx>)
-> MovePathIndex
{
let move_path = move_paths.push(MovePath {
next_sibling: None,
first_child: None,
parent,
place,
});
fn new_move_path(
move_paths: &mut IndexVec<MovePathIndex, MovePath<'tcx>>,
path_map: &mut IndexVec<MovePathIndex, SmallVec<[MoveOutIndex; 4]>>,
init_path_map: &mut IndexVec<MovePathIndex, SmallVec<[InitIndex; 4]>>,
parent: Option<MovePathIndex>,
place: Place<'tcx>,
) -> MovePathIndex {
let move_path =
move_paths.push(MovePath { next_sibling: None, first_child: None, parent, place });
if let Some(parent) = parent {
let next_sibling =
mem::replace(&mut move_paths[parent].first_child, Some(move_path));
let next_sibling = mem::replace(&mut move_paths[parent].first_child, Some(move_path));
move_paths[move_path].next_sibling = next_sibling;
}
@ -91,9 +92,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
/// problematic for borrowck.
///
/// Maybe we should have separate "borrowck" and "moveck" modes.
fn move_path_for(&mut self, place: &Place<'tcx>)
-> Result<MovePathIndex, MoveError<'tcx>>
{
fn move_path_for(&mut self, place: &Place<'tcx>) -> Result<MovePathIndex, MoveError<'tcx>> {
debug!("lookup({:?})", place);
place.iterate(|place_base, place_projection| {
let mut base = match place_base {
@ -108,39 +107,46 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
let tcx = self.builder.tcx;
let place_ty = Place::ty_from(place_base, &proj.base, body, tcx).ty;
match place_ty.sty {
ty::Ref(..) | ty::RawPtr(..) =>
ty::Ref(..) | ty::RawPtr(..) => {
return Err(MoveError::cannot_move_out_of(
self.loc,
BorrowedContent {
target_place: Place {
base: place_base.clone(),
projection: Some(Box::new(proj.clone())),
}
})),
ty::Adt(adt, _) if adt.has_dtor(tcx) && !adt.is_box() =>
return Err(MoveError::cannot_move_out_of(self.loc,
InteriorOfTypeWithDestructor {
container_ty: place_ty
})),
},
},
));
}
ty::Adt(adt, _) if adt.has_dtor(tcx) && !adt.is_box() => {
return Err(MoveError::cannot_move_out_of(
self.loc,
InteriorOfTypeWithDestructor { container_ty: place_ty },
));
}
// move out of union - always move the entire union
ty::Adt(adt, _) if adt.is_union() =>
return Err(MoveError::UnionMove { path: base }),
ty::Slice(_) =>
ty::Adt(adt, _) if adt.is_union() => {
return Err(MoveError::UnionMove { path: base });
}
ty::Slice(_) => {
return Err(MoveError::cannot_move_out_of(
self.loc,
InteriorOfSliceOrArray {
ty: place_ty, is_index: match proj.elem {
ty: place_ty,
is_index: match proj.elem {
ProjectionElem::Index(..) => true,
_ => false
_ => false,
},
})),
},
));
}
ty::Array(..) => match proj.elem {
ProjectionElem::Index(..) =>
ProjectionElem::Index(..) => {
return Err(MoveError::cannot_move_out_of(
self.loc,
InteriorOfSliceOrArray {
ty: place_ty, is_index: true
})),
InteriorOfSliceOrArray { ty: place_ty, is_index: true },
));
}
_ => {
// FIXME: still badly broken
}
@ -186,7 +192,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
fn finalize(
self
self,
) -> Result<MoveData<'tcx>, (MoveData<'tcx>, Vec<(Place<'tcx>, MoveError<'tcx>)>)> {
debug!("{}", {
debug!("moves for {:?}:", self.body.span);
@ -200,11 +206,7 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
"done dumping moves"
});
if !self.errors.is_empty() {
Err((self.data, self.errors))
} else {
Ok(self.data)
}
if !self.errors.is_empty() { Err((self.data, self.errors)) } else { Ok(self.data) }
}
}
@ -222,10 +224,7 @@ pub(super) fn gather_moves<'tcx>(
builder.gather_statement(source, stmt);
}
let terminator_loc = Location {
block: bb,
statement_index: block.statements.len()
};
let terminator_loc = Location { block: bb, statement_index: block.statements.len() };
builder.gather_terminator(terminator_loc, block.terminator());
}
@ -238,11 +237,12 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
let path = self.data.rev_lookup.locals[arg];
let init = self.data.inits.push(Init {
path, kind: InitKind::Deep, location: InitLocation::Argument(arg),
path,
kind: InitKind::Deep,
location: InitLocation::Argument(arg),
});
debug!("gather_args: adding init {:?} of {:?} for argument {:?}",
init, path, arg);
debug!("gather_args: adding init {:?} of {:?} for argument {:?}", init, path, arg);
self.data.init_path_map[path].push(init);
}
@ -297,26 +297,26 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
StatementKind::StorageDead(local) => {
self.gather_move(&Place::from(local));
}
StatementKind::SetDiscriminant{ .. } => {
span_bug!(stmt.source_info.span,
"SetDiscriminant should not exist during borrowck");
StatementKind::SetDiscriminant { .. } => {
span_bug!(
stmt.source_info.span,
"SetDiscriminant should not exist during borrowck"
);
}
StatementKind::Retag { .. } |
StatementKind::AscribeUserType(..) |
StatementKind::Nop => {}
StatementKind::Retag { .. }
| StatementKind::AscribeUserType(..)
| StatementKind::Nop => {}
}
}
fn gather_rvalue(&mut self, rvalue: &Rvalue<'tcx>) {
match *rvalue {
Rvalue::Use(ref operand) |
Rvalue::Repeat(ref operand, _) |
Rvalue::Cast(_, ref operand, _) |
Rvalue::UnaryOp(_, ref operand) => {
self.gather_operand(operand)
}
Rvalue::BinaryOp(ref _binop, ref lhs, ref rhs) |
Rvalue::CheckedBinaryOp(ref _binop, ref lhs, ref rhs) => {
Rvalue::Use(ref operand)
| Rvalue::Repeat(ref operand, _)
| Rvalue::Cast(_, ref operand, _)
| Rvalue::UnaryOp(_, ref operand) => self.gather_operand(operand),
Rvalue::BinaryOp(ref _binop, ref lhs, ref rhs)
| Rvalue::CheckedBinaryOp(ref _binop, ref lhs, ref rhs) => {
self.gather_operand(lhs);
self.gather_operand(rhs);
}
@ -325,11 +325,11 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
self.gather_operand(operand);
}
}
Rvalue::Ref(..) |
Rvalue::Discriminant(..) |
Rvalue::Len(..) |
Rvalue::NullaryOp(NullOp::SizeOf, _) |
Rvalue::NullaryOp(NullOp::Box, _) => {
Rvalue::Ref(..)
| Rvalue::Discriminant(..)
| Rvalue::Len(..)
| Rvalue::NullaryOp(NullOp::SizeOf, _)
| Rvalue::NullaryOp(NullOp::Box, _) => {
// This returns an rvalue with uninitialized contents. We can't
// move out of it here because it is an rvalue - assignments always
// completely initialize their place.
@ -346,13 +346,13 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
fn gather_terminator(&mut self, term: &Terminator<'tcx>) {
match term.kind {
TerminatorKind::Goto { target: _ } |
TerminatorKind::Resume |
TerminatorKind::Abort |
TerminatorKind::GeneratorDrop |
TerminatorKind::FalseEdges { .. } |
TerminatorKind::FalseUnwind { .. } |
TerminatorKind::Unreachable => { }
TerminatorKind::Goto { target: _ }
| TerminatorKind::Resume
| TerminatorKind::Abort
| TerminatorKind::GeneratorDrop
| TerminatorKind::FalseEdges { .. }
| TerminatorKind::FalseUnwind { .. }
| TerminatorKind::Unreachable => {}
TerminatorKind::Return => {
self.gather_move(&Place::RETURN_PLACE);
@ -399,9 +399,9 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
fn gather_operand(&mut self, operand: &Operand<'tcx>) {
match *operand {
Operand::Constant(..) |
Operand::Copy(..) => {} // not-a-move
Operand::Move(ref place) => { // a move
Operand::Constant(..) | Operand::Copy(..) => {} // not-a-move
Operand::Move(ref place) => {
// a move
self.gather_move(place);
}
}
@ -419,8 +419,10 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
};
let move_out = self.builder.data.moves.push(MoveOut { path: path, source: self.loc });
debug!("gather_move({:?}, {:?}): adding move {:?} of {:?}",
self.loc, place, move_out, path);
debug!(
"gather_move({:?}, {:?}): adding move {:?} of {:?}",
self.loc, place, move_out, path
);
self.builder.data.path_map[path].push(move_out);
self.builder.data.loc_map[self.loc].push(move_out);
@ -452,8 +454,10 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
kind,
});
debug!("gather_init({:?}, {:?}): adding init {:?} of {:?}",
self.loc, place, init, path);
debug!(
"gather_init({:?}, {:?}): adding init {:?} of {:?}",
self.loc, place, init, path
);
self.builder.data.init_path_map[path].push(init);
self.builder.data.init_loc_map[self.loc].push(init);

View file

@ -1,9 +1,10 @@
use rustc::ty::{Ty, TyCtxt};
use core::slice::Iter;
use rustc::mir::*;
use rustc::ty::{Ty, TyCtxt};
use rustc::util::nodemap::FxHashMap;
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
use rustc_data_structures::indexed_vec::{Enumerated, Idx, IndexVec};
use smallvec::SmallVec;
use syntax_pos::{Span};
use syntax_pos::Span;
use std::fmt;
use std::ops::{Index, IndexMut};
@ -137,12 +138,17 @@ impl<T> IndexMut<Location> for LocationMap<T> {
}
}
impl<T> LocationMap<T> where T: Default + Clone {
impl<T> LocationMap<T>
where
T: Default + Clone,
{
fn new(body: &Body<'_>) -> Self {
LocationMap {
map: body.basic_blocks().iter().map(|block| {
vec![T::default(); block.statements.len()+1]
}).collect()
map: body
.basic_blocks()
.iter()
.map(|block| vec![T::default(); block.statements.len() + 1])
.collect(),
}
}
}
@ -178,7 +184,6 @@ pub struct Init {
pub kind: InitKind,
}
/// Initializations can be from an argument or from a statement. Arguments
/// do not have locations, in those cases the `Local` is kept..
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
@ -224,7 +229,7 @@ pub struct MovePathLookup {
/// subsequent search so that it is solely relative to that
/// base-place). For the remaining lookup, we map the projection
/// elem to the associated MovePathIndex.
projections: FxHashMap<(MovePathIndex, AbstractElem), MovePathIndex>
projections: FxHashMap<(MovePathIndex, AbstractElem), MovePathIndex>,
}
mod builder;
@ -232,7 +237,7 @@ mod builder;
#[derive(Copy, Clone, Debug)]
pub enum LookupResult {
Exact(MovePathIndex),
Parent(Option<MovePathIndex>)
Parent(Option<MovePathIndex>),
}
impl MovePathLookup {
@ -262,6 +267,12 @@ impl MovePathLookup {
pub fn find_local(&self, local: Local) -> MovePathIndex {
self.locals[local]
}
/// An enumerated iterator of `local`s and their associated
/// `MovePathIndex`es.
pub fn iter_locals_enumerated(&self) -> Enumerated<Local, Iter<'_, MovePathIndex>> {
self.locals.iter_enumerated()
}
}
#[derive(Debug)]
@ -289,7 +300,7 @@ pub(crate) enum IllegalMoveOriginKind<'tcx> {
InteriorOfTypeWithDestructor { container_ty: Ty<'tcx> },
/// Illegal move due to attempt to move out of a slice or array.
InteriorOfSliceOrArray { ty: Ty<'tcx>, is_index: bool, },
InteriorOfSliceOrArray { ty: Ty<'tcx>, is_index: bool },
}
#[derive(Debug)]
@ -318,11 +329,15 @@ impl<'tcx> MoveData<'tcx> {
pub fn base_local(&self, mut mpi: MovePathIndex) -> Option<Local> {
loop {
let path = &self.move_paths[mpi];
if let Place {
base: PlaceBase::Local(l),
projection: None,
} = path.place { return Some(l); }
if let Some(parent) = path.parent { mpi = parent; continue } else { return None }
if let Place { base: PlaceBase::Local(l), projection: None } = path.place {
return Some(l);
}
if let Some(parent) = path.parent {
mpi = parent;
continue;
} else {
return None;
}
}
}
}