Make move out computation lazy

This commit is contained in:
Santiago Pastorino 2018-08-30 18:54:32 -03:00
parent 685fb54317
commit 373fc932aa
No known key found for this signature in database
GPG Key ID: 88C941CDA1D46432
7 changed files with 110 additions and 189 deletions

View File

@ -203,6 +203,28 @@ impl<'tcx> Mir<'tcx> {
ReadGuard::map(self.predecessors(), |p| &p[bb])
}
#[inline]
pub fn predecessor_locations(&self, loc: Location) -> impl Iterator<Item = Location> + '_ {
let if_zero_locations = if loc.statement_index == 0 {
let predecessor_blocks = self.predecessors_for(loc.block);
let num_predecessor_blocks = predecessor_blocks.len();
Some((0 .. num_predecessor_blocks)
.map(move |i| predecessor_blocks[i])
.map(move |bb| self.terminator_loc(bb))
)
} else {
None
};
let if_not_zero_locations = if loc.statement_index == 0 {
None
} else {
Some(Location { block: loc.block, statement_index: loc.statement_index - 1 })
};
if_zero_locations.into_iter().flatten().chain(if_not_zero_locations)
}
#[inline]
pub fn dominators(&self) -> Dominators<BasicBlock> {
dominators(self)

View File

@ -15,6 +15,7 @@ use rustc::mir::{BindingForm, BorrowKind, ClearCrossCrate, Field, Local};
use rustc::mir::{LocalDecl, LocalKind, Location, Operand, Place};
use rustc::mir::{ProjectionElem, Rvalue, Statement, StatementKind};
use rustc::ty;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::indexed_vec::Idx;
use rustc_data_structures::sync::Lrc;
use rustc_errors::DiagnosticBuilder;
@ -24,8 +25,9 @@ use super::borrow_set::BorrowData;
use super::{Context, MirBorrowckCtxt};
use super::{InitializationRequiringAction, PrefixSet};
use dataflow::drop_flag_effects;
use dataflow::move_paths::MovePathIndex;
use dataflow::{FlowAtLocation, MovingOutStatements};
use dataflow::move_paths::indexes::MoveOutIndex;
use util::borrowck_errors::{BorrowckErrors, Origin};
impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
@ -35,17 +37,14 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
desired_action: InitializationRequiringAction,
(place, span): (&Place<'tcx>, Span),
mpi: MovePathIndex,
curr_move_out: &FlowAtLocation<MovingOutStatements<'_, 'gcx, 'tcx>>,
) {
let use_spans = self
.move_spans(place, context.loc)
.or_else(|| self.borrow_spans(span, context.loc));
let span = use_spans.args_or_use();
let mois = self.move_data.path_map[mpi]
.iter()
.filter(|moi| curr_move_out.contains(moi))
.collect::<Vec<_>>();
let mois = self.get_moved_indexes(context, mpi);
debug!("report_use_of_moved_or_uninitialized: mois={:?}", mois);
if mois.is_empty() {
let root_place = self.prefixes(&place, PrefixSet::All).last().unwrap();
@ -93,7 +92,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
let mut is_loop_move = false;
for moi in &mois {
let move_out = self.move_data.moves[**moi];
let move_out = self.move_data.moves[*moi];
let moved_place = &self.move_data.move_paths[move_out.path].place;
let move_spans = self.move_spans(moved_place, move_out.source);
@ -148,7 +147,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
};
if needs_note {
let mpi = self.move_data.moves[*mois[0]].path;
let mpi = self.move_data.moves[mois[0]].path;
let place = &self.move_data.move_paths[mpi].place;
if let Some(ty) = self.retrieve_type_for_place(place) {
@ -521,6 +520,81 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
err
}
fn get_moved_indexes(
&mut self,
context: Context,
mpi: MovePathIndex,
) -> Vec<MoveOutIndex> {
let mir = self.mir;
let mut stack = Vec::new();
stack.extend(mir.predecessor_locations(context.loc));
let mut visited = FxHashSet();
let mut result = vec![];
'dfs:
while let Some(l) = stack.pop() {
debug!("report_use_of_moved_or_uninitialized: current_location={:?}", l);
if !visited.insert(l) {
continue;
}
// check for moves
let stmt_kind = mir[l.block].statements.get(l.statement_index).map(|s| &s.kind);
if let Some(StatementKind::StorageDead(..)) = stmt_kind {
// this analysis only tries to find moves explicitly
// written by the user, so we ignore the move-outs
// created by `StorageDead` and at the beginning
// of a function.
} else {
for moi in &self.move_data.loc_map[l] {
debug!("report_use_of_moved_or_uninitialized: moi={:?}", moi);
if self.move_data.moves[*moi].path == mpi {
debug!("report_use_of_moved_or_uninitialized: found");
result.push(*moi);
// Strictly speaking, we could continue our DFS here. There may be
// other moves that can reach the point of error. But it is kind of
// confusing to highlight them.
//
// Example:
//
// ```
// let a = vec![];
// let b = a;
// let c = a;
// drop(a); // <-- current point of error
// ```
//
// Because we stop the DFS here, we only highlight `let c = a`,
// and not `let b = a`. We will of course also report an error at
// `let c = a` which highlights `let b = a` as the move.
continue 'dfs;
}
}
}
// check for inits
let mut any_match = false;
drop_flag_effects::for_location_inits(
self.tcx,
self.mir,
self.move_data,
l,
|m| if m == mpi { any_match = true; },
);
if any_match {
continue 'dfs;
}
stack.extend(mir.predecessor_locations(l));
}
result
}
pub(super) fn report_illegal_mutation_of_borrowed(
&mut self,
context: Context,

View File

@ -24,7 +24,7 @@ use polonius_engine::Output;
use dataflow::move_paths::indexes::BorrowIndex;
use dataflow::move_paths::HasMoveData;
use dataflow::Borrows;
use dataflow::{EverInitializedPlaces, MovingOutStatements};
use dataflow::EverInitializedPlaces;
use dataflow::{FlowAtLocation, FlowsAtLocation};
use dataflow::MaybeUninitializedPlaces;
use either::Either;
@ -35,7 +35,6 @@ use std::rc::Rc;
crate struct Flows<'b, 'gcx: 'tcx, 'tcx: 'b> {
borrows: FlowAtLocation<Borrows<'b, 'gcx, 'tcx>>,
pub uninits: FlowAtLocation<MaybeUninitializedPlaces<'b, 'gcx, 'tcx>>,
pub move_outs: FlowAtLocation<MovingOutStatements<'b, 'gcx, 'tcx>>,
pub ever_inits: FlowAtLocation<EverInitializedPlaces<'b, 'gcx, 'tcx>>,
/// Polonius Output
@ -46,14 +45,12 @@ impl<'b, 'gcx, 'tcx> Flows<'b, 'gcx, 'tcx> {
crate fn new(
borrows: FlowAtLocation<Borrows<'b, 'gcx, 'tcx>>,
uninits: FlowAtLocation<MaybeUninitializedPlaces<'b, 'gcx, 'tcx>>,
move_outs: FlowAtLocation<MovingOutStatements<'b, 'gcx, 'tcx>>,
ever_inits: FlowAtLocation<EverInitializedPlaces<'b, 'gcx, 'tcx>>,
polonius_output: Option<Rc<Output<RegionVid, BorrowIndex, LocationIndex>>>,
) -> Self {
Flows {
borrows,
uninits,
move_outs,
ever_inits,
polonius_output,
}
@ -79,7 +76,6 @@ macro_rules! each_flow {
($this:ident, $meth:ident($arg:ident)) => {
FlowAtLocation::$meth(&mut $this.borrows, $arg);
FlowAtLocation::$meth(&mut $this.uninits, $arg);
FlowAtLocation::$meth(&mut $this.move_outs, $arg);
FlowAtLocation::$meth(&mut $this.ever_inits, $arg);
};
}
@ -146,18 +142,6 @@ impl<'b, 'gcx, 'tcx> fmt::Display for Flows<'b, 'gcx, 'tcx> {
});
s.push_str("] ");
s.push_str("move_out: [");
let mut saw_one = false;
self.move_outs.each_state_bit(|mpi_move_out| {
if saw_one {
s.push_str(", ");
};
saw_one = true;
let move_out = &self.move_outs.operator().move_data().moves[mpi_move_out];
s.push_str(&format!("{:?}", move_out));
});
s.push_str("] ");
s.push_str("ever_init: [");
let mut saw_one = false;
self.ever_inits.each_state_bit(|mpi_ever_init| {

View File

@ -43,7 +43,7 @@ use dataflow::DataflowResultsConsumer;
use dataflow::FlowAtLocation;
use dataflow::MoveDataParamEnv;
use dataflow::{do_dataflow, DebugFormatted};
use dataflow::{EverInitializedPlaces, MovingOutStatements};
use dataflow::EverInitializedPlaces;
use dataflow::{MaybeInitializedPlaces, MaybeUninitializedPlaces};
use util::borrowck_errors::{BorrowckErrors, Origin};
@ -186,15 +186,6 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
MaybeUninitializedPlaces::new(tcx, mir, &mdpe),
|bd, i| DebugFormatted::new(&bd.move_data().move_paths[i]),
));
let flow_move_outs = FlowAtLocation::new(do_dataflow(
tcx,
mir,
id,
&attributes,
&dead_unwinds,
MovingOutStatements::new(tcx, mir, &mdpe),
|bd, i| DebugFormatted::new(&bd.move_data().moves[i]),
));
let flow_ever_inits = FlowAtLocation::new(do_dataflow(
tcx,
mir,
@ -268,7 +259,6 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
let mut state = Flows::new(
flow_borrows,
flow_uninits,
flow_move_outs,
flow_ever_inits,
polonius_output,
);
@ -1617,7 +1607,6 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
let place = self.base_path(place_span.0);
let maybe_uninits = &flow_state.uninits;
let curr_move_outs = &flow_state.move_outs;
// Bad scenarios:
//
@ -1663,7 +1652,6 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
desired_action,
place_span,
mpi,
curr_move_outs,
);
return; // don't bother finding other problems.
}
@ -1691,7 +1679,6 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
let place = self.base_path(place_span.0);
let maybe_uninits = &flow_state.uninits;
let curr_move_outs = &flow_state.move_outs;
// Bad scenarios:
//
@ -1727,7 +1714,6 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
desired_action,
place_span,
child_mpi,
curr_move_outs,
);
return; // don't bother finding other problems.
}

View File

@ -22,13 +22,13 @@ use super::MoveDataParamEnv;
use util::elaborate_drops::DropFlagState;
use super::move_paths::{HasMoveData, MoveData, MoveOutIndex, MovePathIndex, InitIndex};
use super::move_paths::{HasMoveData, MoveData, MovePathIndex, InitIndex};
use super::move_paths::{LookupResult, InitKind};
use super::{BitDenotation, BlockSets, InitialFlow};
use super::drop_flag_effects_for_function_entry;
use super::drop_flag_effects_for_location;
use super::{on_lookup_result_bits, for_location_inits};
use super::on_lookup_result_bits;
mod storage_liveness;
@ -211,40 +211,6 @@ impl<'a, 'gcx, 'tcx: 'a> HasMoveData<'tcx> for DefinitelyInitializedPlaces<'a, '
fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
}
/// `MovingOutStatements` tracks the statements that perform moves out
/// of particular places. More precisely, it tracks whether the
/// *effect* of such moves (namely, the uninitialization of the
/// place in question) can reach some point in the control-flow of
/// the function, or if that effect is "killed" by some intervening
/// operation reinitializing that place.
///
/// The resulting dataflow is a more enriched version of
/// `MaybeUninitializedPlaces`. Both structures on their own only tell
/// you if a place *might* be uninitialized at a given point in the
/// control flow. But `MovingOutStatements` also includes the added
/// data of *which* particular statement causing the deinitialization
/// that the borrow checker's error message may need to report.
#[allow(dead_code)]
pub struct MovingOutStatements<'a, 'gcx: 'tcx, 'tcx: 'a> {
tcx: TyCtxt<'a, 'gcx, 'tcx>,
mir: &'a Mir<'tcx>,
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>,
}
impl<'a, 'gcx: 'tcx, 'tcx: 'a> MovingOutStatements<'a, 'gcx, 'tcx> {
pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
mir: &'a Mir<'tcx>,
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>)
-> Self
{
MovingOutStatements { tcx: tcx, mir: mir, mdpe: mdpe }
}
}
impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for MovingOutStatements<'a, 'gcx, 'tcx> {
fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
}
/// `EverInitializedPlaces` tracks all places that might have ever been
/// initialized upon reaching a particular point in the control flow
/// for a function, without an intervening `Storage Dead`.
@ -488,83 +454,6 @@ impl<'a, 'gcx, 'tcx> BitDenotation for DefinitelyInitializedPlaces<'a, 'gcx, 'tc
}
}
impl<'a, 'gcx, 'tcx> BitDenotation for MovingOutStatements<'a, 'gcx, 'tcx> {
type Idx = MoveOutIndex;
fn name() -> &'static str { "moving_out" }
fn bits_per_block(&self) -> usize {
self.move_data().moves.len()
}
fn start_block_effect(&self, _sets: &mut IdxSet<MoveOutIndex>) {
// no move-statements have been executed prior to function
// execution, so this method has no effect on `_sets`.
}
fn statement_effect(&self,
sets: &mut BlockSets<MoveOutIndex>,
location: Location) {
let (tcx, mir, move_data) = (self.tcx, self.mir, self.move_data());
let stmt = &mir[location.block].statements[location.statement_index];
let loc_map = &move_data.loc_map;
let path_map = &move_data.path_map;
match stmt.kind {
// this analysis only tries to find moves explicitly
// written by the user, so we ignore the move-outs
// created by `StorageDead` and at the beginning
// of a function.
mir::StatementKind::StorageDead(_) => {}
_ => {
debug!("stmt {:?} at loc {:?} moves out of move_indexes {:?}",
stmt, location, &loc_map[location]);
// Every path deinitialized by a *particular move*
// has corresponding bit, "gen'ed" (i.e. set)
// here, in dataflow vector
sets.gen_all_and_assert_dead(&loc_map[location]);
}
}
for_location_inits(tcx, mir, move_data, location,
|mpi| sets.kill_all(&path_map[mpi]));
}
fn terminator_effect(&self,
sets: &mut BlockSets<MoveOutIndex>,
location: Location)
{
let (tcx, mir, move_data) = (self.tcx, self.mir, self.move_data());
let term = mir[location.block].terminator();
let loc_map = &move_data.loc_map;
let path_map = &move_data.path_map;
debug!("terminator {:?} at loc {:?} moves out of move_indexes {:?}",
term, location, &loc_map[location]);
sets.gen_all_and_assert_dead(&loc_map[location]);
for_location_inits(tcx, mir, move_data, location,
|mpi| sets.kill_all(&path_map[mpi]));
}
fn propagate_call_return(&self,
in_out: &mut IdxSet<MoveOutIndex>,
_call_bb: mir::BasicBlock,
_dest_bb: mir::BasicBlock,
dest_place: &mir::Place) {
let move_data = self.move_data();
let bits_per_block = self.bits_per_block();
let path_map = &move_data.path_map;
on_lookup_result_bits(self.tcx,
self.mir,
move_data,
move_data.rev_lookup.find(dest_place),
|mpi| for moi in &path_map[mpi] {
assert!(moi.index() < bits_per_block);
in_out.remove(&moi);
});
}
}
impl<'a, 'gcx, 'tcx> BitDenotation for EverInitializedPlaces<'a, 'gcx, 'tcx> {
type Idx = InitIndex;
fn name() -> &'static str { "ever_init" }
@ -682,13 +571,6 @@ impl<'a, 'gcx, 'tcx> BitwiseOperator for DefinitelyInitializedPlaces<'a, 'gcx, '
}
}
impl<'a, 'gcx, 'tcx> BitwiseOperator for MovingOutStatements<'a, 'gcx, 'tcx> {
#[inline]
fn join(&self, pred1: Word, pred2: Word) -> Word {
pred1 | pred2 // moves from both preds are in scope
}
}
impl<'a, 'gcx, 'tcx> BitwiseOperator for EverInitializedPlaces<'a, 'gcx, 'tcx> {
#[inline]
fn join(&self, pred1: Word, pred2: Word) -> Word {
@ -727,13 +609,6 @@ impl<'a, 'gcx, 'tcx> InitialFlow for DefinitelyInitializedPlaces<'a, 'gcx, 'tcx>
}
}
impl<'a, 'gcx, 'tcx> InitialFlow for MovingOutStatements<'a, 'gcx, 'tcx> {
#[inline]
fn bottom_value() -> bool {
false // bottom = no loans in scope by default
}
}
impl<'a, 'gcx, 'tcx> InitialFlow for EverInitializedPlaces<'a, 'gcx, 'tcx> {
#[inline]
fn bottom_value() -> bool {

View File

@ -28,7 +28,7 @@ use std::usize;
pub use self::impls::{MaybeStorageLive};
pub use self::impls::{MaybeInitializedPlaces, MaybeUninitializedPlaces};
pub use self::impls::{DefinitelyInitializedPlaces, MovingOutStatements};
pub use self::impls::DefinitelyInitializedPlaces;
pub use self::impls::EverInitializedPlaces;
pub use self::impls::borrows::Borrows;
pub use self::impls::HaveBeenBorrowedLocals;
@ -38,7 +38,7 @@ pub(crate) use self::drop_flag_effects::*;
use self::move_paths::MoveData;
mod at_location;
mod drop_flag_effects;
pub mod drop_flag_effects;
mod graphviz;
mod impls;
pub mod move_paths;
@ -511,18 +511,6 @@ impl<'a, E:Idx> BlockSets<'a, E> {
}
}
fn gen_all_and_assert_dead<I>(&mut self, i: I)
where I: IntoIterator,
I::Item: Borrow<E>
{
for j in i {
let j = j.borrow();
let retval = self.gen_set.add(j);
self.kill_set.remove(j);
assert!(retval);
}
}
fn kill(&mut self, e: &E) {
self.gen_set.remove(e);
self.kill_set.add(e);

View File

@ -1,9 +1,6 @@
error[E0382]: use of moved value: `foo.x`
--> $DIR/fields-move.rs:28:9
|
LL | $foo.x
| ------ value moved here
...
LL | $foo.x //~ ERROR use of moved value: `foo.x`
| ^^^^^^ value used here after move
...
@ -28,14 +25,9 @@ LL | assert_two_copies(copy_modern!(foo), foo.x); //~ ERROR use of moved val
error[E0382]: use of moved value: `foo.x`
--> $DIR/fields-move.rs:39:42
|
LL | $foo.x
| ------ value moved here
...
LL | $foo.x //~ ERROR use of moved value: `foo.x`
| ------ value moved here
...
LL | assert_two_copies(copy_modern!(foo), foo.x); //~ ERROR use of moved value: `foo.x`
| ----- value moved here
LL | assert_two_copies(copy_legacy!(foo), foo.x); //~ ERROR use of moved value: `foo.x`
| ^^^^^ value used here after move
|