提交 d409dbf9 编写于 作者: M Matthew Jasper

Add initialization info to `MoveData`

* Used for new dataflow to track if a variable has every been initialized
* Used for other dataflows that need to be updated for initializations
上级 827cb0d6
......@@ -31,7 +31,7 @@
use dataflow::{MoveDataParamEnv};
use dataflow::{BitDenotation, BlockSets, DataflowResults, DataflowResultsConsumer};
use dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals};
use dataflow::{MovingOutStatements};
use dataflow::{MovingOutStatements, EverInitializedLvals};
use dataflow::{Borrows, BorrowData, BorrowIndex};
use dataflow::move_paths::{MoveError, IllegalMoveOriginKind};
use dataflow::move_paths::{HasMoveData, MoveData, MovePathIndex, LookupResult, MoveOutIndex};
......@@ -130,6 +130,9 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
let flow_move_outs = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds,
MovingOutStatements::new(tcx, mir, &mdpe),
|bd, i| &bd.move_data().moves[i]);
let flow_ever_inits = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds,
EverInitializedLvals::new(tcx, mir, &mdpe),
|bd, i| &bd.move_data().inits[i]);
let mut mbcx = MirBorrowckCtxt {
tcx: tcx,
......@@ -143,7 +146,8 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
let mut state = InProgress::new(flow_borrows,
flow_inits,
flow_uninits,
flow_move_outs);
flow_move_outs,
flow_ever_inits);
mbcx.analyze_results(&mut state); // entry point for DataflowResultsConsumer
}
......@@ -167,6 +171,7 @@ pub struct InProgress<'b, 'gcx: 'tcx, 'tcx: 'b> {
inits: FlowInProgress<MaybeInitializedLvals<'b, 'gcx, 'tcx>>,
uninits: FlowInProgress<MaybeUninitializedLvals<'b, 'gcx, 'tcx>>,
move_outs: FlowInProgress<MovingOutStatements<'b, 'gcx, 'tcx>>,
ever_inits: FlowInProgress<EverInitializedLvals<'b, 'gcx, 'tcx>>,
}
struct FlowInProgress<BD> where BD: BitDenotation {
......@@ -190,7 +195,8 @@ fn reset_to_entry_of(&mut self, bb: BasicBlock, flow_state: &mut Self::FlowState
flow_state.each_flow(|b| b.reset_to_entry_of(bb),
|i| i.reset_to_entry_of(bb),
|u| u.reset_to_entry_of(bb),
|m| m.reset_to_entry_of(bb));
|m| m.reset_to_entry_of(bb),
|e| e.reset_to_entry_of(bb));
}
fn reconstruct_statement_effect(&mut self,
......@@ -199,7 +205,8 @@ fn reconstruct_statement_effect(&mut self,
flow_state.each_flow(|b| b.reconstruct_statement_effect(location),
|i| i.reconstruct_statement_effect(location),
|u| u.reconstruct_statement_effect(location),
|m| m.reconstruct_statement_effect(location));
|m| m.reconstruct_statement_effect(location),
|e| e.reconstruct_statement_effect(location));
}
fn apply_local_effect(&mut self,
......@@ -208,7 +215,8 @@ fn apply_local_effect(&mut self,
flow_state.each_flow(|b| b.apply_local_effect(),
|i| i.apply_local_effect(),
|u| u.apply_local_effect(),
|m| m.apply_local_effect());
|m| m.apply_local_effect(),
|e| e.apply_local_effect());
}
fn reconstruct_terminator_effect(&mut self,
......@@ -217,7 +225,8 @@ fn reconstruct_terminator_effect(&mut self,
flow_state.each_flow(|b| b.reconstruct_terminator_effect(location),
|i| i.reconstruct_terminator_effect(location),
|u| u.reconstruct_terminator_effect(location),
|m| m.reconstruct_terminator_effect(location));
|m| m.reconstruct_terminator_effect(location),
|e| e.reconstruct_terminator_effect(location));
}
fn visit_block_entry(&mut self,
......@@ -750,22 +759,13 @@ fn check_if_reassignment_to_immutable_state(&mut self,
}
if let Some(mpi) = self.move_path_for_lvalue(lvalue) {
if flow_state.inits.curr_state.contains(&mpi) {
// may already be assigned before reaching this statement;
// report error.
// FIXME: Not ideal, it only finds the assignment that lexically comes first
let assigned_lvalue = &move_data.move_paths[mpi].lvalue;
let assignment_stmt = self.mir.basic_blocks().iter().filter_map(|bb| {
bb.statements.iter().find(|stmt| {
if let StatementKind::Assign(ref lv, _) = stmt.kind {
*lv == *assigned_lvalue
} else {
false
}
})
}).next().unwrap();
self.report_illegal_reassignment(
context, (lvalue, span), assignment_stmt.source_info.span);
for ii in &move_data.init_path_map[mpi] {
if flow_state.ever_inits.curr_state.contains(ii) {
let first_assign_span = self.move_data.inits[*ii].span;
self.report_illegal_reassignment(
context, (lvalue, span), first_assign_span);
break;
}
}
}
}
......@@ -1852,30 +1852,35 @@ impl<'b, 'gcx, 'tcx> InProgress<'b, 'gcx, 'tcx> {
pub(super) fn new(borrows: DataflowResults<Borrows<'b, 'gcx, 'tcx>>,
inits: DataflowResults<MaybeInitializedLvals<'b, 'gcx, 'tcx>>,
uninits: DataflowResults<MaybeUninitializedLvals<'b, 'gcx, 'tcx>>,
move_out: DataflowResults<MovingOutStatements<'b, 'gcx, 'tcx>>)
move_out: DataflowResults<MovingOutStatements<'b, 'gcx, 'tcx>>,
ever_inits: DataflowResults<EverInitializedLvals<'b, 'gcx, 'tcx>>)
-> Self {
InProgress {
borrows: FlowInProgress::new(borrows),
inits: FlowInProgress::new(inits),
uninits: FlowInProgress::new(uninits),
move_outs: FlowInProgress::new(move_out)
move_outs: FlowInProgress::new(move_out),
ever_inits: FlowInProgress::new(ever_inits)
}
}
fn each_flow<XB, XI, XU, XM>(&mut self,
fn each_flow<XB, XI, XU, XM, XE>(&mut self,
mut xform_borrows: XB,
mut xform_inits: XI,
mut xform_uninits: XU,
mut xform_move_outs: XM) where
mut xform_move_outs: XM,
mut xform_ever_inits: XE) where
XB: FnMut(&mut FlowInProgress<Borrows<'b, 'gcx, 'tcx>>),
XI: FnMut(&mut FlowInProgress<MaybeInitializedLvals<'b, 'gcx, 'tcx>>),
XU: FnMut(&mut FlowInProgress<MaybeUninitializedLvals<'b, 'gcx, 'tcx>>),
XM: FnMut(&mut FlowInProgress<MovingOutStatements<'b, 'gcx, 'tcx>>),
XE: FnMut(&mut FlowInProgress<EverInitializedLvals<'b, 'gcx, 'tcx>>),
{
xform_borrows(&mut self.borrows);
xform_inits(&mut self.inits);
xform_uninits(&mut self.uninits);
xform_move_outs(&mut self.move_outs);
xform_ever_inits(&mut self.ever_inits);
}
fn summary(&self) -> String {
......@@ -1932,6 +1937,17 @@ fn summary(&self) -> String {
&self.move_outs.base_results.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| {
if saw_one { s.push_str(", "); };
saw_one = true;
let ever_init =
&self.ever_inits.base_results.operator().move_data().inits[mpi_ever_init];
s.push_str(&format!("{:?}", ever_init));
});
s.push_str("]");
return s;
......
......@@ -14,7 +14,7 @@
use super::{MoveDataParamEnv};
use super::indexes::MovePathIndex;
use super::move_paths::{MoveData, LookupResult};
use super::move_paths::{MoveData, LookupResult, InitKind};
pub fn move_path_children_matching<'tcx, F>(move_data: &MoveData<'tcx>,
path: MovePathIndex,
......@@ -197,47 +197,40 @@ pub(crate) fn drop_flag_effects_for_location<'a, 'gcx, 'tcx, F>(
|mpi| callback(mpi, DropFlagState::Absent))
}
let block = &mir[loc.block];
match block.statements.get(loc.statement_index) {
Some(stmt) => match stmt.kind {
mir::StatementKind::SetDiscriminant{ .. } => {
span_bug!(stmt.source_info.span, "SetDiscrimant should not exist during borrowck");
}
mir::StatementKind::Assign(ref lvalue, ref rvalue) => {
match rvalue.initialization_state() {
mir::tcx::RvalueInitializationState::Shallow => {
debug!("drop_flag_effects: box assignment {:?}", stmt);
if let LookupResult::Exact(mpi) = move_data.rev_lookup.find(lvalue) {
callback(mpi, DropFlagState::Present);
}
}
mir::tcx::RvalueInitializationState::Deep => {
debug!("drop_flag_effects: assignment {:?}", stmt);
on_lookup_result_bits(tcx, mir, move_data,
move_data.rev_lookup.find(lvalue),
|mpi| callback(mpi, DropFlagState::Present))
}
}
}
mir::StatementKind::StorageLive(_) |
mir::StatementKind::StorageDead(_) |
mir::StatementKind::InlineAsm { .. } |
mir::StatementKind::EndRegion(_) |
mir::StatementKind::Validate(..) |
mir::StatementKind::Nop => {}
},
None => {
debug!("drop_flag_effects: replace {:?}", block.terminator());
match block.terminator().kind {
mir::TerminatorKind::DropAndReplace { ref location, .. } => {
on_lookup_result_bits(tcx, mir, move_data,
move_data.rev_lookup.find(location),
|mpi| callback(mpi, DropFlagState::Present))
}
_ => {
// other terminators do not contain move-ins
}
debug!("drop_flag_effects: assignment for location({:?})", loc);
for_location_inits(
tcx,
mir,
move_data,
loc,
|mpi| callback(mpi, DropFlagState::Present)
);
}
pub(crate) fn for_location_inits<'a, 'gcx, 'tcx, F>(
tcx: TyCtxt<'a, 'gcx, 'tcx>,
mir: &Mir<'tcx>,
move_data: &MoveData<'tcx>,
loc: Location,
mut callback: F)
where F: FnMut(MovePathIndex)
{
for ii in &move_data.init_loc_map[loc] {
let init = move_data.inits[*ii];
match init.kind {
InitKind::Deep => {
let path = init.path;
on_all_children_bits(tcx, mir, move_data,
path,
&mut callback)
},
InitKind::Shallow => {
let mpi = init.path;
callback(mpi);
}
InitKind::NonPanicPathOnly => (),
}
}
}
......@@ -22,13 +22,13 @@
use super::MoveDataParamEnv;
use util::elaborate_drops::DropFlagState;
use super::move_paths::{HasMoveData, MoveData, MoveOutIndex, MovePathIndex};
use super::move_paths::LookupResult;
use super::move_paths::{HasMoveData, MoveData, MoveOutIndex, MovePathIndex, InitIndex};
use super::move_paths::{LookupResult, InitKind};
use super::{BitDenotation, BlockSets, DataflowOperator};
use super::drop_flag_effects_for_function_entry;
use super::drop_flag_effects_for_location;
use super::on_lookup_result_bits;
use super::{on_lookup_result_bits, for_location_inits};
mod storage_liveness;
......@@ -242,6 +242,56 @@ impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for MovingOutStatements<'a, 'gcx, 'tcx> {
fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
}
/// `EverInitializedLvals` tracks all l-values that might have ever been
/// initialized upon reaching a particular point in the control flow
/// for a function, without an intervening `Storage Dead`.
///
/// This dataflow is used to determine if an immutable local variable may
/// be assigned to.
///
/// For example, in code like the following, we have corresponding
/// dataflow information shown in the right-hand comments.
///
/// ```rust
/// struct S;
/// fn foo(pred: bool) { // ever-init:
/// // { }
/// let a = S; let b = S; let c; let d; // {a, b }
///
/// if pred {
/// drop(a); // {a, b, }
/// b = S; // {a, b, }
///
/// } else {
/// drop(b); // {a, b, }
/// d = S; // {a, b, d }
///
/// } // {a, b, d }
///
/// c = S; // {a, b, c, d }
/// }
/// ```
pub struct EverInitializedLvals<'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> EverInitializedLvals<'a, 'gcx, 'tcx> {
pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
mir: &'a Mir<'tcx>,
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>)
-> Self
{
EverInitializedLvals { tcx: tcx, mir: mir, mdpe: mdpe }
}
}
impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for EverInitializedLvals<'a, 'gcx, 'tcx> {
fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
}
impl<'a, 'gcx, 'tcx> MaybeInitializedLvals<'a, 'gcx, 'tcx> {
fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
state: DropFlagState)
......@@ -454,7 +504,7 @@ fn statement_effect(&self,
let stmt = &mir[location.block].statements[location.statement_index];
let loc_map = &move_data.loc_map;
let path_map = &move_data.path_map;
let rev_lookup = &move_data.rev_lookup;
let bits_per_block = self.bits_per_block();
match stmt.kind {
// this analysis only tries to find moves explicitly
......@@ -474,52 +524,23 @@ fn statement_effect(&self,
}
}
let bits_per_block = self.bits_per_block();
match stmt.kind {
mir::StatementKind::SetDiscriminant { .. } => {
span_bug!(stmt.source_info.span, "SetDiscriminant should not exist in borrowck");
for_location_inits(tcx, mir, move_data, location,
|mpi| for moi in &path_map[mpi] {
assert!(moi.index() < bits_per_block);
sets.kill_set.add(&moi);
}
mir::StatementKind::Assign(ref lvalue, ref rvalue) => {
// assigning into this `lvalue` kills all
// MoveOuts from it, and *also* all MoveOuts
// for children and associated fragment sets.
match rvalue.initialization_state() {
mir::tcx::RvalueInitializationState::Shallow => {
if let LookupResult::Exact(mpi) = rev_lookup.find(lvalue) {
for moi in &path_map[mpi] {
assert!(moi.index() < bits_per_block);
sets.kill_set.add(&moi);
}
}
}
mir::tcx::RvalueInitializationState::Deep => {
on_lookup_result_bits(tcx,
mir,
move_data,
rev_lookup.find(lvalue),
|mpi| for moi in &path_map[mpi] {
assert!(moi.index() < bits_per_block);
sets.kill_set.add(&moi);
});
}
}
}
mir::StatementKind::StorageLive(_) |
mir::StatementKind::StorageDead(_) |
mir::StatementKind::InlineAsm { .. } |
mir::StatementKind::EndRegion(_) |
mir::StatementKind::Validate(..) |
mir::StatementKind::Nop => {}
}
);
}
fn terminator_effect(&self,
sets: &mut BlockSets<MoveOutIndex>,
location: Location)
{
let (mir, move_data) = (self.mir, self.move_data());
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]);
let bits_per_block = self.bits_per_block();
......@@ -527,19 +548,13 @@ fn terminator_effect(&self,
assert!(move_index.index() < bits_per_block);
zero_to_one(sets.gen_set.words_mut(), *move_index);
}
match term.kind {
mir::TerminatorKind::DropAndReplace { ref location, .. } => {
on_lookup_result_bits(self.tcx,
mir,
move_data,
move_data.rev_lookup.find(location),
|mpi| for moi in &move_data.path_map[mpi] {
assert!(moi.index() < bits_per_block);
sets.kill_set.add(&moi);
});
for_location_inits(tcx, mir, move_data, location,
|mpi| for moi in &path_map[mpi] {
assert!(moi.index() < bits_per_block);
sets.kill_set.add(&moi);
}
_ => {}
}
);
}
fn propagate_call_return(&self,
......@@ -562,12 +577,97 @@ fn propagate_call_return(&self,
}
}
impl<'a, 'gcx, 'tcx> BitDenotation for EverInitializedLvals<'a, 'gcx, 'tcx> {
type Idx = InitIndex;
fn name() -> &'static str { "ever_init" }
fn bits_per_block(&self) -> usize {
self.move_data().inits.len()
}
fn start_block_effect(&self, sets: &mut BlockSets<InitIndex>) {
let bits_per_block = self.bits_per_block();
for init_index in (0..self.mir.arg_count).map(InitIndex::new) {
assert!(init_index.index() < bits_per_block);
sets.gen_set.add(&init_index);
}
}
fn statement_effect(&self,
sets: &mut BlockSets<InitIndex>,
location: Location) {
let (_, mir, move_data) = (self.tcx, self.mir, self.move_data());
let stmt = &mir[location.block].statements[location.statement_index];
let init_path_map = &move_data.init_path_map;
let init_loc_map = &move_data.init_loc_map;
let rev_lookup = &move_data.rev_lookup;
let bits_per_block = self.bits_per_block();
debug!("statement {:?} at loc {:?} initializes move_indexes {:?}",
stmt, location, &init_loc_map[location]);
for init_index in &init_loc_map[location] {
assert!(init_index.index() < bits_per_block);
sets.gen_set.add(init_index);
}
match stmt.kind {
mir::StatementKind::StorageDead(local) => {
// End inits for StorageDead, so that an immutable variable can
// be reinitialized on the next iteration of the loop.
if let LookupResult::Exact(mpi) = rev_lookup.find(&mir::Lvalue::Local(local)) {
debug!("stmt {:?} at loc {:?} clears the ever initialized status of {:?}",
stmt, location, &init_path_map[mpi]);
for ii in &init_path_map[mpi] {
assert!(ii.index() < bits_per_block);
sets.kill_set.add(&ii);
}
}
}
_ => {}
}
}
fn terminator_effect(&self,
sets: &mut BlockSets<InitIndex>,
location: Location)
{
let (mir, move_data) = (self.mir, self.move_data());
let term = mir[location.block].terminator();
let init_loc_map = &move_data.init_loc_map;
debug!("terminator {:?} at loc {:?} initializes move_indexes {:?}",
term, location, &init_loc_map[location]);
let bits_per_block = self.bits_per_block();
for init_index in &init_loc_map[location] {
if move_data.inits[*init_index].kind != InitKind::NonPanicPathOnly {
assert!(init_index.index() < bits_per_block);
sets.gen_set.add(init_index);
}
}
}
fn propagate_call_return(&self,
in_out: &mut IdxSet<InitIndex>,
call_bb: mir::BasicBlock,
_dest_bb: mir::BasicBlock,
_dest_lval: &mir::Lvalue) {
let move_data = self.move_data();
let bits_per_block = self.bits_per_block();
let init_loc_map = &move_data.init_loc_map;
let call_loc = Location {
block: call_bb,
statement_index: self.mir[call_bb].statements.len(),
};
for init_index in &init_loc_map[call_loc] {
assert!(init_index.index() < bits_per_block);
in_out.add(init_index);
}
}
}
fn zero_to_one(bitvec: &mut [usize], move_index: MoveOutIndex) {
let retval = bitvec.set_bit(move_index.index());
assert!(retval);
}
impl<'a, 'gcx, 'tcx> BitwiseOperator for MaybeInitializedLvals<'a, 'gcx, 'tcx> {
#[inline]
fn join(&self, pred1: usize, pred2: usize) -> usize {
......@@ -596,6 +696,13 @@ fn join(&self, pred1: usize, pred2: usize) -> usize {
}
}
impl<'a, 'gcx, 'tcx> BitwiseOperator for EverInitializedLvals<'a, 'gcx, 'tcx> {
#[inline]
fn join(&self, pred1: usize, pred2: usize) -> usize {
pred1 | pred2 // inits from both preds are in scope
}
}
// The way that dataflow fixed point iteration works, you want to
// start at bottom and work your way to a fixed point. Control-flow
// merges will apply the `join` operator to each block entry's current
......@@ -633,3 +740,10 @@ fn bottom_value() -> bool {
false // bottom = no loans in scope by default
}
}
impl<'a, 'gcx, 'tcx> DataflowOperator for EverInitializedLvals<'a, 'gcx, 'tcx> {
#[inline]
fn bottom_value() -> bool {
false // bottom = no initialized variables by default
}
}
......@@ -27,6 +27,7 @@
pub use self::impls::{MaybeStorageLive};
pub use self::impls::{MaybeInitializedLvals, MaybeUninitializedLvals};
pub use self::impls::{DefinitelyInitializedLvals, MovingOutStatements};
pub use self::impls::EverInitializedLvals;
pub use self::impls::borrows::{Borrows, BorrowData, BorrowIndex};
pub(crate) use self::drop_flag_effects::*;
......
......@@ -22,7 +22,7 @@
use super::abs_domain::Lift;
use super::{LocationMap, MoveData, MovePath, MovePathLookup, MovePathIndex, MoveOut, MoveOutIndex};
use super::{MoveError};
use super::{MoveError, InitIndex, Init, LookupResult, InitKind};
use super::IllegalMoveOriginKind::*;
struct MoveDataBuilder<'a, 'gcx: 'tcx, 'tcx: 'a> {
......@@ -40,6 +40,7 @@ fn new(mir: &'a Mir<'tcx>,
-> Self {
let mut move_paths = IndexVec::new();
let mut path_map = IndexVec::new();
let mut init_path_map = IndexVec::new();
MoveDataBuilder {
mir,
......@@ -51,18 +52,28 @@ fn new(mir: &'a Mir<'tcx>,
loc_map: LocationMap::new(mir),
rev_lookup: MovePathLookup {
locals: mir.local_decls.indices().map(Lvalue::Local).map(|v| {
Self::new_move_path(&mut move_paths, &mut path_map, None, v)
Self::new_move_path(
&mut move_paths,
&mut path_map,
&mut init_path_map,
None,
v,
)
}).collect(),
projections: FxHashMap(),
},
move_paths,
path_map,
inits: IndexVec::new(),
init_loc_map: LocationMap::new(mir),
init_path_map,
}
}
}
fn new_move_path(move_paths: &mut IndexVec<MovePathIndex, MovePath<'tcx>>,
path_map: &mut IndexVec<MovePathIndex, Vec<MoveOutIndex>>,
init_path_map: &mut IndexVec<MovePathIndex, Vec<InitIndex>>,
parent: Option<MovePathIndex>,
lvalue: Lvalue<'tcx>)
-> MovePathIndex
......@@ -82,6 +93,10 @@ fn new_move_path(move_paths: &mut IndexVec<MovePathIndex, MovePath<'tcx>>,
let path_map_ent = path_map.push(vec![]);
assert_eq!(path_map_ent, move_path);
let init_path_map_ent = init_path_map.push(vec![]);
assert_eq!(init_path_map_ent, move_path);
move_path
}
}
......@@ -165,6 +180,7 @@ fn move_path_for_projection(&mut self,
let path = MoveDataBuilder::new_move_path(
&mut self.builder.data.move_paths,
&mut self.builder.data.path_map,
&mut self.builder.data.init_path_map,
Some(base),
lval.clone()
);
......@@ -204,6 +220,8 @@ pub(super) fn gather_moves<'a, 'gcx, 'tcx>(mir: &Mir<'tcx>,
(MoveData<'tcx>, Vec<MoveError<'tcx>>)> {
let mut builder = MoveDataBuilder::new(mir, tcx, param_env);
builder.gather_args();
for (bb, block) in mir.basic_blocks().iter_enumerated() {
for (i, stmt) in block.statements.iter().enumerate() {
let source = Location { block: bb, statement_index: i };
......@@ -221,6 +239,22 @@ pub(super) fn gather_moves<'a, 'gcx, 'tcx>(mir: &Mir<'tcx>,
}
impl<'a, 'gcx, 'tcx> MoveDataBuilder<'a, 'gcx, 'tcx> {
fn gather_args(&mut self) {
for arg in self.mir.args_iter() {
let path = self.data.rev_lookup.locals[arg];
let span = self.mir.local_decls[arg].source_info.span;
let init = self.data.inits.push(Init {
path, span, kind: InitKind::Deep
});
debug!("gather_args: adding init {:?} of {:?} for argument {:?}",
init, path, arg);
self.data.init_path_map[path].push(init);
}
}
fn gather_statement(&mut self, loc: Location, stmt: &Statement<'tcx>) {
debug!("gather_statement({:?}, {:?})", loc, stmt);
(Gatherer { builder: self, loc }).gather_statement(stmt);
......@@ -247,6 +281,9 @@ fn gather_statement(&mut self, stmt: &Statement<'tcx>) {
// move-path for the interior so it will be separate from
// the exterior.
self.create_move_path(&lval.clone().deref());
self.gather_init(lval, InitKind::Shallow);
} else {
self.gather_init(lval, InitKind::Deep);
}
self.gather_rvalue(rval);
}
......@@ -329,6 +366,7 @@ fn gather_terminator(&mut self, term: &Terminator<'tcx>) {
TerminatorKind::DropAndReplace { ref location, ref value, .. } => {
self.create_move_path(location);
self.gather_operand(value);
self.gather_init(location, InitKind::Deep);
}
TerminatorKind::Call { ref func, ref args, ref destination, cleanup: _ } => {
self.gather_operand(func);
......@@ -337,6 +375,7 @@ fn gather_terminator(&mut self, term: &Terminator<'tcx>) {
}
if let Some((ref destination, _bb)) = *destination {
self.create_move_path(destination);
self.gather_init(destination, InitKind::NonPanicPathOnly);
}
}
}
......@@ -378,4 +417,22 @@ fn gather_move(&mut self, lval: &Lvalue<'tcx>, force: bool) {
self.builder.data.path_map[path].push(move_out);
self.builder.data.loc_map[self.loc].push(move_out);
}
fn gather_init(&mut self, lval: &Lvalue<'tcx>, kind: InitKind) {
debug!("gather_init({:?}, {:?})", self.loc, lval);
if let LookupResult::Exact(path) = self.builder.data.rev_lookup.find(lval) {
let init = self.builder.data.inits.push(Init {
span: self.builder.mir.source_info(self.loc).span,
path,
kind,
});
debug!("gather_init({:?}, {:?}): adding init {:?} of {:?}",
self.loc, lval, init, path);
self.builder.data.init_path_map[path].push(init);
self.builder.data.init_loc_map[self.loc].push(init);
}
}
}
......@@ -60,12 +60,16 @@ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
/// Index into MoveData.moves.
new_index!(MoveOutIndex, "mo");
/// Index into MoveData.inits.
new_index!(InitIndex, "in");
/// Index into Borrows.locations
new_index!(BorrowIndex, "bw");
}
pub use self::indexes::MovePathIndex;
pub use self::indexes::MoveOutIndex;
pub use self::indexes::InitIndex;
impl MoveOutIndex {
pub fn move_path_index(&self, move_data: &MoveData) -> MovePathIndex {
......@@ -126,6 +130,11 @@ pub struct MoveData<'tcx> {
pub loc_map: LocationMap<Vec<MoveOutIndex>>,
pub path_map: IndexVec<MovePathIndex, Vec<MoveOutIndex>>,
pub rev_lookup: MovePathLookup<'tcx>,
pub inits: IndexVec<InitIndex, Init>,
/// Each Location `l` is mapped to the Inits that are effects
/// of executing the code at `l`.
pub init_loc_map: LocationMap<Vec<InitIndex>>,
pub init_path_map: IndexVec<MovePathIndex, Vec<InitIndex>>,
}
pub trait HasMoveData<'tcx> {
......@@ -182,6 +191,34 @@ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
}
}
/// `Init` represents a point in a program that initializes some L-value;
#[derive(Copy, Clone)]
pub struct Init {
/// path being initialized
pub path: MovePathIndex,
/// span of initialization
pub span: Span,
/// Extra information about this initialization
pub kind: InitKind,
}
/// Additional information about the initialization.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum InitKind {
/// Deep init, even on panic
Deep,
/// Only does a shallow init
Shallow,
/// This doesn't initialize the variabe on panic (and a panic is possible).
NonPanicPathOnly,
}
impl fmt::Debug for Init {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "{:?}@{:?} ({:?})", self.path, self.span, self.kind)
}
}
/// Tables mapping from an l-value to its MovePathIndex.
#[derive(Debug)]
pub struct MovePathLookup<'tcx> {
......
......@@ -16,6 +16,12 @@ fn ok() {
}
}
fn also_ok() {
loop {
let _x = String::new();
}
}
fn fail() {
loop {
let x: i32;
......@@ -26,5 +32,6 @@ fn fail() {
fn main() {
ok();
also_ok();
fail();
}
// Copyright 2012 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.
// revisions: ast mir
//[mir]compile-flags: -Zborrowck=mir
fn test_drop_replace() {
let b: Box<isize>;
b = Box::new(1); //[ast]~ NOTE first assignment
//[mir]~^ NOTE first assignment
b = Box::new(2); //[ast]~ ERROR cannot assign twice to immutable variable
//[mir]~^ ERROR cannot assign twice to immutable variable `b`
//[ast]~| NOTE cannot assign twice to immutable
//[mir]~| NOTE cannot assign twice to immutable
}
fn test_call() {
let b = Box::new(1); //[ast]~ NOTE first assignment
//[mir]~^ NOTE first assignment
b = Box::new(2); //[ast]~ ERROR cannot assign twice to immutable variable
//[mir]~^ ERROR cannot assign twice to immutable variable `b`
//[ast]~| NOTE cannot assign twice to immutable
//[mir]~| NOTE cannot assign twice to immutable
}
fn test_args(b: Box<i32>) { //[ast]~ NOTE first assignment
//[mir]~^ NOTE first assignment
b = Box::new(2); //[ast]~ ERROR cannot assign twice to immutable variable
//[mir]~^ ERROR cannot assign twice to immutable variable `b`
//[ast]~| NOTE cannot assign twice to immutable
//[mir]~| NOTE cannot assign twice to immutable
}
fn main() {}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册