diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index a2abaa5e12f55..53b6ccdbd530a 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -1243,7 +1243,7 @@ impl<'a, 'b> GraphSuccessors<'b> for Mir<'a> { type Iter = IntoIter; } -#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Ord, PartialOrd)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] pub struct Location { /// the location is within this block pub block: BasicBlock, @@ -1253,3 +1253,8 @@ pub struct Location { pub statement_index: usize, } +impl fmt::Debug for Location { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "{:?}[{}]", self.block, self.statement_index) + } +} diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index c2d0b2c686e77..16e0b376f4b53 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -774,9 +774,6 @@ pub enum LvalueContext<'tcx> { // Being borrowed Borrow { region: &'tcx Region, kind: BorrowKind }, - // Being sliced -- this should be same as being borrowed, probably - Slice { from_start: usize, from_end: usize }, - // Used as base for another lvalue, e.g. `x` in `x.y` Projection, diff --git a/src/librustc_borrowck/borrowck/gather_loans/gather_moves.rs b/src/librustc_borrowck/borrowck/gather_loans/gather_moves.rs index 3e335dacc8ed9..9bdc6887f6d03 100644 --- a/src/librustc_borrowck/borrowck/gather_loans/gather_moves.rs +++ b/src/librustc_borrowck/borrowck/gather_loans/gather_moves.rs @@ -185,6 +185,7 @@ fn check_and_get_illegal_move_origin<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, check_and_get_illegal_move_origin(bccx, b) } } + ty::TySlice(..) => Some(cmt.clone()), _ => { check_and_get_illegal_move_origin(bccx, b) } diff --git a/src/librustc_borrowck/borrowck/gather_loans/move_error.rs b/src/librustc_borrowck/borrowck/gather_loans/move_error.rs index 3fa7c252b842c..9fbf1492f5d28 100644 --- a/src/librustc_borrowck/borrowck/gather_loans/move_error.rs +++ b/src/librustc_borrowck/borrowck/gather_loans/move_error.rs @@ -16,7 +16,6 @@ use rustc::ty; use syntax::ast; use syntax_pos; use errors::DiagnosticBuilder; -use rustc::hir; pub struct MoveErrorCollector<'tcx> { errors: Vec> @@ -131,17 +130,20 @@ fn report_cannot_move_out_of<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, err } - Categorization::Interior(ref b, mc::InteriorElement(Kind::Index, _)) => { - let expr = bccx.tcx.map.expect_expr(move_from.id); - if let hir::ExprIndex(..) = expr.node { - let mut err = struct_span_err!(bccx, move_from.span, E0508, - "cannot move out of type `{}`, \ - a non-copy fixed-size array", - b.ty); - err.span_label(move_from.span, &format!("cannot move out of here")); - err - } else { - span_bug!(move_from.span, "this path should not cause illegal move"); + Categorization::Interior(ref b, mc::InteriorElement(ik, _)) => { + match (&b.ty.sty, ik) { + (&ty::TySlice(..), _) | + (_, Kind::Index) => { + let mut err = struct_span_err!(bccx, move_from.span, E0508, + "cannot move out of type `{}`, \ + a non-copy array", + b.ty); + err.span_label(move_from.span, &format!("cannot move out of here")); + err + } + (_, Kind::Pattern) => { + span_bug!(move_from.span, "this path should not cause illegal move"); + } } } diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs b/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs index c46daf9c22537..8ac59c60396f6 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs @@ -17,7 +17,7 @@ use super::super::MoveDataParamEnv; use super::super::DropFlagState; use super::super::drop_flag_effects_for_function_entry; use super::super::drop_flag_effects_for_location; -use super::super::on_all_children_bits; +use super::super::on_lookup_result_bits; use super::{BitDenotation, BlockSets, DataflowOperator}; @@ -277,10 +277,9 @@ impl<'a, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'tcx> { dest_lval: &repr::Lvalue) { // when a call returns successfully, that means we need to set // the bits for that dest_lval to 1 (initialized). - let move_path_index = ctxt.move_data.rev_lookup.find(dest_lval); - on_all_children_bits(self.tcx, self.mir, &ctxt.move_data, - move_path_index, - |mpi| { in_out.add(&mpi); }); + on_lookup_result_bits(self.tcx, self.mir, &ctxt.move_data, + ctxt.move_data.rev_lookup.find(dest_lval), + |mpi| { in_out.add(&mpi); }); } } @@ -338,11 +337,10 @@ impl<'a, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'tcx> { _dest_bb: repr::BasicBlock, dest_lval: &repr::Lvalue) { // when a call returns successfully, that means we need to set - // the bits for that dest_lval to 1 (initialized). - let move_path_index = ctxt.move_data.rev_lookup.find(dest_lval); - on_all_children_bits(self.tcx, self.mir, &ctxt.move_data, - move_path_index, - |mpi| { in_out.remove(&mpi); }); + // the bits for that dest_lval to 0 (initialized). + on_lookup_result_bits(self.tcx, self.mir, &ctxt.move_data, + ctxt.move_data.rev_lookup.find(dest_lval), + |mpi| { in_out.remove(&mpi); }); } } @@ -400,10 +398,9 @@ impl<'a, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'tcx> { dest_lval: &repr::Lvalue) { // when a call returns successfully, that means we need to set // the bits for that dest_lval to 1 (initialized). - let move_path_index = ctxt.move_data.rev_lookup.find(dest_lval); - on_all_children_bits(self.tcx, self.mir, &ctxt.move_data, - move_path_index, - |mpi| { in_out.add(&mpi); }); + on_lookup_result_bits(self.tcx, self.mir, &ctxt.move_data, + ctxt.move_data.rev_lookup.find(dest_lval), + |mpi| { in_out.add(&mpi); }); } } @@ -448,11 +445,10 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> { // assigning into this `lvalue` kills all // MoveOuts from it, and *also* all MoveOuts // for children and associated fragment sets. - let move_path_index = rev_lookup.find(lvalue); - on_all_children_bits(tcx, + on_lookup_result_bits(tcx, mir, move_data, - move_path_index, + rev_lookup.find(lvalue), |mpi| for moi in &path_map[mpi] { assert!(moi.index() < bits_per_block); sets.kill_set.add(&moi); @@ -489,18 +485,17 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> { _dest_bb: repr::BasicBlock, dest_lval: &repr::Lvalue) { let move_data = &ctxt.move_data; - let move_path_index = move_data.rev_lookup.find(dest_lval); let bits_per_block = self.bits_per_block(ctxt); let path_map = &move_data.path_map; - on_all_children_bits(self.tcx, - self.mir, - move_data, - move_path_index, - |mpi| for moi in &path_map[mpi] { - assert!(moi.index() < bits_per_block); - in_out.remove(&moi); - }); + on_lookup_result_bits(self.tcx, + self.mir, + move_data, + move_data.rev_lookup.find(dest_lval), + |mpi| for moi in &path_map[mpi] { + assert!(moi.index() < bits_per_block); + in_out.remove(&moi); + }); } } diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs b/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs index 9a4865755e799..88f6d5fef562d 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs @@ -16,7 +16,7 @@ use rustc::ty::{self, TyCtxt}; use rustc::mir::repr::{self, Mir}; use rustc_data_structures::indexed_vec::Idx; -use super::super::gather_moves::{MovePathIndex}; +use super::super::gather_moves::{MovePathIndex, LookupResult}; use super::super::MoveDataParamEnv; use super::BitDenotation; use super::DataflowResults; @@ -116,20 +116,26 @@ fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>, repr::BorrowKind::Shared, ref peeking_at_lval) = *rvalue { // Okay, our search is over. - let peek_mpi = move_data.rev_lookup.find(peeking_at_lval); - let bit_state = sets.on_entry.contains(&peek_mpi); - debug!("rustc_peek({:?} = &{:?}) bit_state: {}", - lvalue, peeking_at_lval, bit_state); - if !bit_state { - tcx.sess.span_err(span, &format!("rustc_peek: bit not set")); + match move_data.rev_lookup.find(peeking_at_lval) { + LookupResult::Exact(peek_mpi) => { + let bit_state = sets.on_entry.contains(&peek_mpi); + debug!("rustc_peek({:?} = &{:?}) bit_state: {}", + lvalue, peeking_at_lval, bit_state); + if !bit_state { + tcx.sess.span_err(span, "rustc_peek: bit not set"); + } + } + LookupResult::Parent(..) => { + tcx.sess.span_err(span, "rustc_peek: argument untracked"); + } } return; } else { // Our search should have been over, but the input // does not match expectations of `rustc_peek` for // this sanity_check. - let msg = &format!("rustc_peek: argument expression \ - must be immediate borrow of form `&expr`"); + let msg = "rustc_peek: argument expression \ + must be immediate borrow of form `&expr`"; tcx.sess.span_err(span, msg); } } diff --git a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs index 71274b7e0218a..96702b209a1f5 100644 --- a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs +++ b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs @@ -9,10 +9,11 @@ // except according to those terms. use indexed_set::IdxSetBuf; -use super::gather_moves::{MoveData, MovePathIndex, MovePathContent}; +use super::gather_moves::{MoveData, MovePathIndex, LookupResult}; use super::dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals}; use super::dataflow::{DataflowResults}; use super::{drop_flag_effects_for_location, on_all_children_bits}; +use super::on_lookup_result_bits; use super::{DropFlagState, MoveDataParamEnv}; use super::patch::MirPatch; use rustc::ty::{self, Ty, TyCtxt}; @@ -42,7 +43,7 @@ impl<'tcx> MirPass<'tcx> for ElaborateDrops { } let id = src.item_id(); let param_env = ty::ParameterEnvironment::for_item(tcx, id); - let move_data = MoveData::gather_moves(mir, tcx); + let move_data = MoveData::gather_moves(mir, tcx, ¶m_env); let elaborate_patch = { let mir = &*mir; let env = MoveDataParamEnv { @@ -184,31 +185,11 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { fn path_needs_drop(&self, path: MovePathIndex) -> bool { - match self.move_data().move_paths[path].content { - MovePathContent::Lvalue(ref lvalue) => { - let ty = lvalue.ty(self.mir, self.tcx).to_ty(self.tcx); - debug!("path_needs_drop({:?}, {:?} : {:?})", path, lvalue, ty); + let lvalue = &self.move_data().move_paths[path].lvalue; + let ty = lvalue.ty(self.mir, self.tcx).to_ty(self.tcx); + debug!("path_needs_drop({:?}, {:?} : {:?})", path, lvalue, ty); - self.tcx.type_needs_drop_given_env(ty, self.param_env()) - } - _ => false - } - } - - /// Returns whether this lvalue is tracked by drop elaboration. This - /// includes all lvalues, except these (1.) behind references or arrays, - /// or (2.) behind ADT's with a Drop impl. - fn lvalue_is_tracked(&self, lv: &Lvalue<'tcx>) -> bool - { - // `lvalue_contents_drop_state_cannot_differ` only compares - // the `lv` to its immediate contents, while this recursively - // follows parent chain formed by `base` of each projection. - if let &Lvalue::Projection(ref data) = lv { - !super::lvalue_contents_drop_state_cannot_differ(self.tcx, self.mir, &data.base) && - self.lvalue_is_tracked(&data.base) - } else { - true - } + self.tcx.type_needs_drop_given_env(ty, self.param_env()) } fn collect_drop_flags(&mut self) @@ -221,19 +202,29 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { _ => continue }; - if !self.lvalue_is_tracked(location) { - continue - } - let init_data = self.initialization_data_at(Location { block: bb, statement_index: data.statements.len() }); let path = self.move_data().rev_lookup.find(location); - debug!("collect_drop_flags: {:?}, lv {:?} (index {:?})", + debug!("collect_drop_flags: {:?}, lv {:?} ({:?})", bb, location, path); + let path = match path { + LookupResult::Exact(e) => e, + LookupResult::Parent(None) => continue, + LookupResult::Parent(Some(parent)) => { + let (_maybe_live, maybe_dead) = init_data.state(parent); + if maybe_dead { + span_bug!(terminator.source_info.span, + "drop of untracked, uninitialized value {:?}, lv {:?} ({:?})", + bb, location, path); + } + continue + } + }; + on_all_children_bits(self.tcx, self.mir, self.move_data(), path, |child| { if self.path_needs_drop(child) { let (maybe_live, maybe_dead) = init_data.state(child); @@ -257,20 +248,27 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { match terminator.kind { TerminatorKind::Drop { ref location, target, unwind } => { let init_data = self.initialization_data_at(loc); - let path = self.move_data().rev_lookup.find(location); - self.elaborate_drop(&DropCtxt { - source_info: terminator.source_info, - is_cleanup: data.is_cleanup, - init_data: &init_data, - lvalue: location, - path: path, - succ: target, - unwind: if data.is_cleanup { - None - } else { - Some(Option::unwrap_or(unwind, resume_block)) + match self.move_data().rev_lookup.find(location) { + LookupResult::Exact(path) => { + self.elaborate_drop(&DropCtxt { + source_info: terminator.source_info, + is_cleanup: data.is_cleanup, + init_data: &init_data, + lvalue: location, + path: path, + succ: target, + unwind: if data.is_cleanup { + None + } else { + Some(Option::unwrap_or(unwind, resume_block)) + } + }, bb); } - }, bb); + LookupResult::Parent(..) => { + span_bug!(terminator.source_info.span, + "drop of untracked value {:?}", bb); + } + } } TerminatorKind::DropAndReplace { ref location, ref value, target, unwind } => @@ -336,35 +334,37 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { is_cleanup: data.is_cleanup, }); - if !self.lvalue_is_tracked(location) { - // drop and replace behind a pointer/array/whatever. The location - // must be initialized. - debug!("elaborate_drop_and_replace({:?}) - untracked", terminator); - self.patch.patch_terminator(bb, TerminatorKind::Drop { - location: location.clone(), - target: target, - unwind: Some(unwind) - }); - } else { - debug!("elaborate_drop_and_replace({:?}) - tracked", terminator); - let init_data = self.initialization_data_at(loc); - let path = self.move_data().rev_lookup.find(location); - - self.elaborate_drop(&DropCtxt { - source_info: terminator.source_info, - is_cleanup: data.is_cleanup, - init_data: &init_data, - lvalue: location, - path: path, - succ: target, - unwind: Some(unwind) - }, bb); - on_all_children_bits(self.tcx, self.mir, self.move_data(), path, |child| { - self.set_drop_flag(Location { block: target, statement_index: 0 }, - child, DropFlagState::Present); - self.set_drop_flag(Location { block: unwind, statement_index: 0 }, - child, DropFlagState::Present); - }); + match self.move_data().rev_lookup.find(location) { + LookupResult::Exact(path) => { + debug!("elaborate_drop_and_replace({:?}) - tracked {:?}", terminator, path); + let init_data = self.initialization_data_at(loc); + + self.elaborate_drop(&DropCtxt { + source_info: terminator.source_info, + is_cleanup: data.is_cleanup, + init_data: &init_data, + lvalue: location, + path: path, + succ: target, + unwind: Some(unwind) + }, bb); + on_all_children_bits(self.tcx, self.mir, self.move_data(), path, |child| { + self.set_drop_flag(Location { block: target, statement_index: 0 }, + child, DropFlagState::Present); + self.set_drop_flag(Location { block: unwind, statement_index: 0 }, + child, DropFlagState::Present); + }); + } + LookupResult::Parent(parent) => { + // drop and replace behind a pointer/array/whatever. The location + // must be initialized. + debug!("elaborate_drop_and_replace({:?}) - untracked {:?}", terminator, parent); + self.patch.patch_terminator(bb, TerminatorKind::Drop { + location: location.clone(), + target: target, + unwind: Some(unwind) + }); + } } } @@ -446,10 +446,9 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { substs: &'tcx Substs<'tcx>) -> Vec<(Lvalue<'tcx>, Option)> { - let move_paths = &self.move_data().move_paths; variant.fields.iter().enumerate().map(|(i, f)| { let subpath = - super::move_path_children_matching(move_paths, variant_path, |p| { + super::move_path_children_matching(self.move_data(), variant_path, |p| { match p { &Projection { elem: ProjectionElem::Field(idx, _), .. @@ -580,7 +579,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { let fields = tys.iter().enumerate().map(|(i, &ty)| { (c.lvalue.clone().field(Field::new(i), ty), super::move_path_children_matching( - &self.move_data().move_paths, c.path, |proj| match proj { + self.move_data(), c.path, |proj| match proj { &Projection { elem: ProjectionElem::Field(f, _), .. } => f.index() == i, @@ -598,7 +597,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { debug!("open_drop_for_box({:?}, {:?})", c, ty); let interior_path = super::move_path_children_matching( - &self.move_data().move_paths, c.path, |proj| match proj { + self.move_data(), c.path, |proj| match proj { &Projection { elem: ProjectionElem::Deref, .. } => true, _ => false }).unwrap(); @@ -625,10 +624,8 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { variant_index: usize) -> BasicBlock { - let move_paths = &self.move_data().move_paths; - let subpath = super::move_path_children_matching( - move_paths, c.path, |proj| match proj { + self.move_data(), c.path, |proj| match proj { &Projection { elem: ProjectionElem::Downcast(_, idx), .. } => idx == variant_index, @@ -942,7 +939,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { let loc = Location { block: tgt, statement_index: 0 }; let path = self.move_data().rev_lookup.find(lv); - on_all_children_bits( + on_lookup_result_bits( self.tcx, self.mir, self.move_data(), path, |child| self.set_drop_flag(loc, child, DropFlagState::Present) ); @@ -1011,7 +1008,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { let loc = Location { block: bb, statement_index: data.statements.len() }; let path = self.move_data().rev_lookup.find(lv); - on_all_children_bits( + on_lookup_result_bits( self.tcx, self.mir, self.move_data(), path, |child| self.set_drop_flag(loc, child, DropFlagState::Present) ); diff --git a/src/librustc_borrowck/borrowck/mir/gather_moves.rs b/src/librustc_borrowck/borrowck/mir/gather_moves.rs index 01bf8ed0e4b57..bd38f554dc9b0 100644 --- a/src/librustc_borrowck/borrowck/mir/gather_moves.rs +++ b/src/librustc_borrowck/borrowck/mir/gather_moves.rs @@ -9,16 +9,17 @@ // except according to those terms. -use rustc::ty::TyCtxt; +use rustc::ty::{self, TyCtxt, ParameterEnvironment}; use rustc::mir::repr::*; use rustc::util::nodemap::FnvHashMap; -use rustc_data_structures::indexed_vec::{Idx, IndexVec}; +use rustc_data_structures::indexed_vec::{IndexVec}; + +use syntax::codemap::DUMMY_SP; -use std::cell::{Cell}; use std::collections::hash_map::Entry; use std::fmt; -use std::iter; -use std::ops::Index; +use std::mem; +use std::ops::{Index, IndexMut}; use super::abs_domain::{AbstractElem, Lift}; @@ -28,17 +29,15 @@ use super::abs_domain::{AbstractElem, Lift}; // ensure that other code does not accidentally access `index.0` // (which is likely to yield a subtle off-by-one error). mod indexes { + use std::fmt; use core::nonzero::NonZero; use rustc_data_structures::indexed_vec::Idx; macro_rules! new_index { - ($Index:ident) => { - #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] + ($Index:ident, $debug_name:expr) => { + #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct $Index(NonZero); - impl $Index { - } - impl Idx for $Index { fn new(idx: usize) -> Self { unsafe { $Index(NonZero::new(idx + 1)) } @@ -47,14 +46,20 @@ mod indexes { *self.0 - 1 } } + + impl fmt::Debug for $Index { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "{}{}", $debug_name, self.index()) + } + } } } /// Index into MovePathData.move_paths - new_index!(MovePathIndex); + new_index!(MovePathIndex, "mp"); /// Index into MoveData.moves. - new_index!(MoveOutIndex); + new_index!(MoveOutIndex, "mo"); } pub use self::indexes::MovePathIndex; @@ -62,7 +67,7 @@ pub use self::indexes::MoveOutIndex; impl self::indexes::MoveOutIndex { pub fn move_path_index(&self, move_data: &MoveData) -> MovePathIndex { - move_data.moves[self.index()].path + move_data.moves[*self].path } } @@ -83,40 +88,7 @@ pub struct MovePath<'tcx> { pub next_sibling: Option, pub first_child: Option, pub parent: Option, - pub content: MovePathContent<'tcx>, -} - -/// MovePaths usually represent a single l-value. The exceptions are -/// forms that arise due to erroneous input code: static data holds -/// l-values that we cannot actually move out of. Therefore we map -/// statics to a special marker value (`MovePathContent::Static`) -/// representing an invalid origin. -#[derive(Clone, Debug)] -pub enum MovePathContent<'tcx> { - Lvalue(Lvalue<'tcx>), - Static, -} - -/// During construction of the MovePath's, we use PreMovePath to -/// represent accumulated state while we are gathering up all the -/// children of each path. -#[derive(Clone)] -struct PreMovePath<'tcx> { - pub next_sibling: Option, - pub first_child: Cell>, - pub parent: Option, - pub content: MovePathContent<'tcx>, -} - -impl<'tcx> PreMovePath<'tcx> { - fn into_move_path(self) -> MovePath<'tcx> { - MovePath { - next_sibling: self.next_sibling, - parent: self.parent, - content: self.content, - first_child: self.first_child.get(), - } - } + pub lvalue: Lvalue<'tcx>, } impl<'tcx> fmt::Debug for MovePath<'tcx> { @@ -131,52 +103,50 @@ impl<'tcx> fmt::Debug for MovePath<'tcx> { if let Some(next_sibling) = self.next_sibling { write!(w, " next_sibling: {:?}", next_sibling)?; } - write!(w, " content: {:?} }}", self.content) + write!(w, " lvalue: {:?} }}", self.lvalue) } } #[derive(Debug)] pub struct MoveData<'tcx> { - pub move_paths: MovePathData<'tcx>, - pub moves: Vec, - pub loc_map: LocMap, - pub path_map: PathMap, + pub move_paths: IndexVec>, + pub moves: IndexVec, + /// Each Location `l` is mapped to the MoveOut's that are effects + /// of executing the code at `l`. (There can be multiple MoveOut's + /// for a given `l` because each MoveOut is associated with one + /// particular path being moved.) + pub loc_map: LocationMap>, + pub path_map: IndexVec>, pub rev_lookup: MovePathLookup<'tcx>, } #[derive(Debug)] -pub struct LocMap { +pub struct LocationMap { /// Location-indexed (BasicBlock for outer index, index within BB - /// for inner index) map to list of MoveOutIndex's. - /// - /// Each Location `l` is mapped to the MoveOut's that are effects - /// of executing the code at `l`. (There can be multiple MoveOut's - /// for a given `l` because each MoveOut is associated with one - /// particular path being moved.) - map: Vec>>, + /// for inner index) map. + map: IndexVec>, } -impl Index for LocMap { - type Output = [MoveOutIndex]; +impl Index for LocationMap { + type Output = T; fn index(&self, index: Location) -> &Self::Output { - assert!(index.block.index() < self.map.len()); - assert!(index.statement_index < self.map[index.block.index()].len()); - &self.map[index.block.index()][index.statement_index] + &self.map[index.block][index.statement_index] } } -#[derive(Debug)] -pub struct PathMap { - /// Path-indexed map to list of MoveOutIndex's. - /// - /// Each Path `p` is mapped to the MoveOut's that move out of `p`. - map: Vec>, +impl IndexMut for LocationMap { + fn index_mut(&mut self, index: Location) -> &mut Self::Output { + &mut self.map[index.block][index.statement_index] + } } -impl Index for PathMap { - type Output = [MoveOutIndex]; - fn index(&self, index: MovePathIndex) -> &Self::Output { - &self.map[index.index()] +impl LocationMap where T: Default + Clone { + fn new(mir: &Mir) -> Self { + LocationMap { + map: mir.basic_blocks().iter().map(|block| { + vec![T::default(); block.statements.len()+1] + }).collect() + } } } @@ -196,583 +166,388 @@ pub struct MoveOut { impl fmt::Debug for MoveOut { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "p{}@{:?}", self.path.index(), self.source) - } -} - -#[derive(Debug)] -pub struct MovePathData<'tcx> { - move_paths: Vec>, -} - -impl<'tcx> MovePathData<'tcx> { - pub fn len(&self) -> usize { self.move_paths.len() } -} - -impl<'tcx> Index for MovePathData<'tcx> { - type Output = MovePath<'tcx>; - fn index(&self, i: MovePathIndex) -> &MovePath<'tcx> { - &self.move_paths[i.index()] + write!(fmt, "{:?}@{:?}", self.path, self.source) } } -struct MovePathDataBuilder<'tcx> { - pre_move_paths: Vec>, - rev_lookup: MovePathLookup<'tcx>, -} - /// Tables mapping from an l-value to its MovePathIndex. #[derive(Debug)] pub struct MovePathLookup<'tcx> { - vars: IndexVec>, - temps: IndexVec>, - args: IndexVec>, + vars: IndexVec, + temps: IndexVec, + args: IndexVec, /// The move path representing the return value is constructed /// lazily when we first encounter it in the input MIR. return_ptr: Option, - /// A single move path (representing any static data referenced) - /// is constructed lazily when we first encounter statics in the - /// input MIR. - statics: Option, - /// projections are made from a base-lvalue and a projection /// elem. The base-lvalue will have a unique MovePathIndex; we use /// the latter as the index into the outer vector (narrowing /// subsequent search so that it is solely relative to that /// base-lvalue). For the remaining lookup, we map the projection /// elem to the associated MovePathIndex. - projections: Vec, MovePathIndex>>, - - /// Tracks the next index to allocate during construction of the - /// MovePathData. Unused after MovePathData is fully constructed. - next_index: MovePathIndex, + projections: FnvHashMap<(MovePathIndex, AbstractElem<'tcx>), MovePathIndex> } -trait FillTo { - type T; - fn fill_to_with(&mut self, idx: usize, x: Self::T); - fn fill_to(&mut self, idx: usize) where Self::T: Default { - self.fill_to_with(idx, Default::default()) - } -} -impl FillTo for Vec { - type T = T; - fn fill_to_with(&mut self, idx: usize, x: T) { - if idx >= self.len() { - let delta = idx + 1 - self.len(); - assert_eq!(idx + 1, self.len() + delta); - self.extend(iter::repeat(x).take(delta)) - } - debug_assert!(idx < self.len()); - } +struct MoveDataBuilder<'a, 'tcx: 'a> { + mir: &'a Mir<'tcx>, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + param_env: &'a ParameterEnvironment<'tcx>, + data: MoveData<'tcx>, } -#[derive(Clone, Debug)] -enum LookupKind { Generate, Reuse } -#[derive(Clone, Debug)] -struct Lookup(LookupKind, T); - -impl Lookup { - fn index(&self) -> usize { (self.1).index() } +pub enum MovePathError { + IllegalMove, + UnionMove { path: MovePathIndex }, } -impl<'tcx> MovePathLookup<'tcx> { - fn new(mir: &Mir) -> Self { - MovePathLookup { - vars: IndexVec::from_elem(None, &mir.var_decls), - temps: IndexVec::from_elem(None, &mir.temp_decls), - args: IndexVec::from_elem(None, &mir.arg_decls), - statics: None, - return_ptr: None, - projections: vec![], - next_index: MovePathIndex::new(0), +impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { + fn new(mir: &'a Mir<'tcx>, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + param_env: &'a ParameterEnvironment<'tcx>) + -> Self { + let mut move_paths = IndexVec::new(); + let mut path_map = IndexVec::new(); + + MoveDataBuilder { + mir: mir, + tcx: tcx, + param_env: param_env, + data: MoveData { + moves: IndexVec::new(), + loc_map: LocationMap::new(mir), + rev_lookup: MovePathLookup { + vars: mir.var_decls.indices().map(Lvalue::Var).map(|v| { + Self::new_move_path(&mut move_paths, &mut path_map, None, v) + }).collect(), + temps: mir.temp_decls.indices().map(Lvalue::Temp).map(|t| { + Self::new_move_path(&mut move_paths, &mut path_map, None, t) + }).collect(), + args: mir.arg_decls.indices().map(Lvalue::Arg).map(|a| { + Self::new_move_path(&mut move_paths, &mut path_map, None, a) + }).collect(), + return_ptr: None, + projections: FnvHashMap(), + }, + move_paths: move_paths, + path_map: path_map, + } } } - fn next_index(next: &mut MovePathIndex) -> MovePathIndex { - let i = *next; - *next = MovePathIndex::new(i.index() + 1); - i - } - - fn lookup_or_generate(vec: &mut IndexVec>, - idx: I, - next_index: &mut MovePathIndex) - -> Lookup { - let entry = &mut vec[idx]; - match *entry { - None => { - let i = Self::next_index(next_index); - *entry = Some(i); - Lookup(LookupKind::Generate, i) - } - Some(entry_idx) => { - Lookup(LookupKind::Reuse, entry_idx) - } + fn new_move_path(move_paths: &mut IndexVec>, + path_map: &mut IndexVec>, + parent: Option, + lvalue: Lvalue<'tcx>) + -> MovePathIndex + { + let move_path = move_paths.push(MovePath { + next_sibling: None, + first_child: None, + parent: parent, + lvalue: lvalue + }); + + if let Some(parent) = parent { + let next_sibling = + mem::replace(&mut move_paths[parent].first_child, Some(move_path)); + move_paths[move_path].next_sibling = next_sibling; } - } - fn lookup_var(&mut self, var_idx: Var) -> Lookup { - Self::lookup_or_generate(&mut self.vars, - var_idx, - &mut self.next_index) + let path_map_ent = path_map.push(vec![]); + assert_eq!(path_map_ent, move_path); + move_path } - fn lookup_temp(&mut self, temp_idx: Temp) -> Lookup { - Self::lookup_or_generate(&mut self.temps, - temp_idx, - &mut self.next_index) + /// This creates a MovePath for a given lvalue, returning an `MovePathError` + /// if that lvalue can't be moved from. + /// + /// NOTE: lvalues behind references *do not* get a move path, which is + /// problematic for borrowck. + /// + /// Maybe we should have seperate "borrowck" and "moveck" modes. + fn move_path_for(&mut self, lval: &Lvalue<'tcx>) + -> Result + { + debug!("lookup({:?})", lval); + match *lval { + Lvalue::Var(var) => Ok(self.data.rev_lookup.vars[var]), + Lvalue::Arg(arg) => Ok(self.data.rev_lookup.args[arg]), + Lvalue::Temp(temp) => Ok(self.data.rev_lookup.temps[temp]), + // error: can't move out of a static + Lvalue::Static(..) => Err(MovePathError::IllegalMove), + Lvalue::ReturnPointer => match self.data.rev_lookup.return_ptr { + Some(ptr) => Ok(ptr), + ref mut ptr @ None => { + let path = Self::new_move_path( + &mut self.data.move_paths, + &mut self.data.path_map, + None, + lval.clone()); + *ptr = Some(path); + Ok(path) + } + }, + Lvalue::Projection(ref proj) => { + self.move_path_for_projection(lval, proj) + } + } } - fn lookup_arg(&mut self, arg_idx: Arg) -> Lookup { - Self::lookup_or_generate(&mut self.args, - arg_idx, - &mut self.next_index) + fn create_move_path(&mut self, lval: &Lvalue<'tcx>) { + // This is an assignment, not a move, so this not being a valid + // move path is OK. + let _ = self.move_path_for(lval); } - fn lookup_static(&mut self) -> Lookup { - match self.statics { - Some(mpi) => { - Lookup(LookupKind::Reuse, mpi) - } - ref mut ret @ None => { - let mpi = Self::next_index(&mut self.next_index); - *ret = Some(mpi); - Lookup(LookupKind::Generate, mpi) + fn move_path_for_projection(&mut self, + lval: &Lvalue<'tcx>, + proj: &LvalueProjection<'tcx>) + -> Result + { + let base = try!(self.move_path_for(&proj.base)); + let lv_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx); + match lv_ty.sty { + // error: can't move out of borrowed content + ty::TyRef(..) | ty::TyRawPtr(..) => return Err(MovePathError::IllegalMove), + // error: can't move out of struct with destructor + ty::TyAdt(adt, _) if adt.has_dtor() => + return Err(MovePathError::IllegalMove), + // move out of union - always move the entire union + ty::TyAdt(adt, _) if adt.is_union() => + return Err(MovePathError::UnionMove { path: base }), + // error: can't move out of a slice + ty::TySlice(..) => + return Err(MovePathError::IllegalMove), + ty::TyArray(..) => match proj.elem { + // error: can't move out of an array + ProjectionElem::Index(..) => return Err(MovePathError::IllegalMove), + _ => { + // FIXME: still badly broken + } + }, + _ => {} + }; + match self.data.rev_lookup.projections.entry((base, proj.elem.lift())) { + Entry::Occupied(ent) => Ok(*ent.get()), + Entry::Vacant(ent) => { + let path = Self::new_move_path( + &mut self.data.move_paths, + &mut self.data.path_map, + Some(base), + lval.clone() + ); + ent.insert(path); + Ok(path) } } } - fn lookup_return_pointer(&mut self) -> Lookup { - match self.return_ptr { - Some(mpi) => { - Lookup(LookupKind::Reuse, mpi) + fn finalize(self) -> MoveData<'tcx> { + debug!("{}", { + debug!("moves for {:?}:", self.mir.span); + for (j, mo) in self.data.moves.iter_enumerated() { + debug!(" {:?} = {:?}", j, mo); } - ref mut ret @ None => { - let mpi = Self::next_index(&mut self.next_index); - *ret = Some(mpi); - Lookup(LookupKind::Generate, mpi) + debug!("move paths for {:?}:", self.mir.span); + for (j, path) in self.data.move_paths.iter_enumerated() { + debug!(" {:?} = {:?}", j, path); } - } + "done dumping moves" + }); + self.data } +} - fn lookup_proj(&mut self, - proj: &LvalueProjection<'tcx>, - base: MovePathIndex) -> Lookup { - let MovePathLookup { ref mut projections, - ref mut next_index, .. } = *self; - projections.fill_to(base.index()); - match projections[base.index()].entry(proj.elem.lift()) { - Entry::Occupied(ent) => { - Lookup(LookupKind::Reuse, *ent.get()) - } - Entry::Vacant(ent) => { - let mpi = Self::next_index(next_index); - ent.insert(mpi); - Lookup(LookupKind::Generate, mpi) - } - } - } +#[derive(Copy, Clone, Debug)] +pub enum LookupResult { + Exact(MovePathIndex), + Parent(Option) } impl<'tcx> MovePathLookup<'tcx> { // Unlike the builder `fn move_path_for` below, this lookup // alternative will *not* create a MovePath on the fly for an - // unknown l-value; it will simply panic. - pub fn find(&self, lval: &Lvalue<'tcx>) -> MovePathIndex { + // unknown l-value, but will rather return the nearest available + // parent. + pub fn find(&self, lval: &Lvalue<'tcx>) -> LookupResult { match *lval { - Lvalue::Var(var) => self.vars[var].unwrap(), - Lvalue::Temp(temp) => self.temps[temp].unwrap(), - Lvalue::Arg(arg) => self.args[arg].unwrap(), - Lvalue::Static(ref _def_id) => self.statics.unwrap(), - Lvalue::ReturnPointer => self.return_ptr.unwrap(), + Lvalue::Var(var) => LookupResult::Exact(self.vars[var]), + Lvalue::Temp(temp) => LookupResult::Exact(self.temps[temp]), + Lvalue::Arg(arg) => LookupResult::Exact(self.args[arg]), + Lvalue::Static(..) => LookupResult::Parent(None), + Lvalue::ReturnPointer => LookupResult::Exact(self.return_ptr.unwrap()), Lvalue::Projection(ref proj) => { - let base_index = self.find(&proj.base); - self.projections[base_index.index()][&proj.elem.lift()] + match self.find(&proj.base) { + LookupResult::Exact(base_path) => { + match self.projections.get(&(base_path, proj.elem.lift())) { + Some(&subpath) => LookupResult::Exact(subpath), + None => LookupResult::Parent(Some(base_path)) + } + } + inexact => inexact + } } } } } -impl<'tcx> MovePathDataBuilder<'tcx> { - fn lookup(&mut self, lval: &Lvalue<'tcx>) -> Lookup { - let proj = match *lval { - Lvalue::Var(var_idx) => - return self.rev_lookup.lookup_var(var_idx), - Lvalue::Temp(temp_idx) => - return self.rev_lookup.lookup_temp(temp_idx), - Lvalue::Arg(arg_idx) => - return self.rev_lookup.lookup_arg(arg_idx), - Lvalue::Static(_def_id) => - return self.rev_lookup.lookup_static(), - Lvalue::ReturnPointer => - return self.rev_lookup.lookup_return_pointer(), - Lvalue::Projection(ref proj) => { - proj - } - }; - - let base_index = self.move_path_for(&proj.base); - self.rev_lookup.lookup_proj(proj, base_index) - } - - fn create_move_path(&mut self, lval: &Lvalue<'tcx>) { - // Create MovePath for `lval`, discarding returned index. - self.move_path_for(lval); +impl<'a, 'tcx> MoveData<'tcx> { + pub fn gather_moves(mir: &Mir<'tcx>, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + param_env: &ParameterEnvironment<'tcx>) + -> Self { + gather_moves(mir, tcx, param_env) } +} - fn move_path_for(&mut self, lval: &Lvalue<'tcx>) -> MovePathIndex { - debug!("move_path_for({:?})", lval); - - let lookup: Lookup = self.lookup(lval); - - // `lookup` is either the previously assigned index or a - // newly-allocated one. - debug_assert!(lookup.index() <= self.pre_move_paths.len()); - - if let Lookup(LookupKind::Generate, mpi) = lookup { - let parent; - let sibling; - // tracks whether content is Some non-static; statics map to None. - let content: Option<&Lvalue<'tcx>>; - - match *lval { - Lvalue::Static(_) => { - content = None; - sibling = None; - parent = None; - } - - Lvalue::Var(_) | Lvalue::Temp(_) | Lvalue::Arg(_) | - Lvalue::ReturnPointer => { - content = Some(lval); - sibling = None; - parent = None; - } - Lvalue::Projection(ref proj) => { - content = Some(lval); - - // Here, install new MovePath as new first_child. - - // Note: `parent` previously allocated (Projection - // case of match above established this). - let idx = self.move_path_for(&proj.base); - parent = Some(idx); - - let parent_move_path = &mut self.pre_move_paths[idx.index()]; - - // At last: Swap in the new first_child. - sibling = parent_move_path.first_child.get(); - parent_move_path.first_child.set(Some(mpi)); - } - }; - - let content = match content { - Some(lval) => MovePathContent::Lvalue(lval.clone()), - None => MovePathContent::Static, - }; - - let move_path = PreMovePath { - next_sibling: sibling, - parent: parent, - content: content, - first_child: Cell::new(None), - }; +fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + param_env: &ParameterEnvironment<'tcx>) + -> MoveData<'tcx> { + let mut builder = MoveDataBuilder::new(mir, tcx, param_env); - self.pre_move_paths.push(move_path); + 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 }; + builder.gather_statement(source, stmt); } - return lookup.1; + let terminator_loc = Location { + block: bb, + statement_index: block.statements.len() + }; + builder.gather_terminator(terminator_loc, block.terminator()); } -} -impl<'a, 'tcx> MoveData<'tcx> { - pub fn gather_moves(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Self { - gather_moves(mir, tcx) - } + builder.finalize() } -#[derive(Debug)] -enum StmtKind { - Use, Repeat, Cast, BinaryOp, UnaryOp, Box, - Aggregate, Drop, CallFn, CallArg, Return, If, -} - -fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveData<'tcx> { - use self::StmtKind as SK; - - let bb_count = mir.basic_blocks().len(); - let mut moves = vec![]; - let mut loc_map: Vec<_> = iter::repeat(Vec::new()).take(bb_count).collect(); - let mut path_map = Vec::new(); - - // this is mutable only because we will move it to and fro' the - // BlockContexts constructed on each iteration. (Moving is more - // straight-forward than mutable borrows in this instance.) - let mut builder = MovePathDataBuilder { - pre_move_paths: Vec::new(), - rev_lookup: MovePathLookup::new(mir), - }; - - // Before we analyze the program text, we create the MovePath's - // for all of the vars, args, and temps. (This enforces a basic - // property that even if the MIR body doesn't contain any - // references to a var/arg/temp, it will still be a valid - // operation to lookup the MovePath associated with it.) - assert!(mir.var_decls.len() <= ::std::u32::MAX as usize); - assert!(mir.arg_decls.len() <= ::std::u32::MAX as usize); - assert!(mir.temp_decls.len() <= ::std::u32::MAX as usize); - for var in mir.var_decls.indices() { - let path_idx = builder.move_path_for(&Lvalue::Var(var)); - path_map.fill_to(path_idx.index()); - } - for arg in mir.arg_decls.indices() { - let path_idx = builder.move_path_for(&Lvalue::Arg(arg)); - path_map.fill_to(path_idx.index()); - } - for temp in mir.temp_decls.indices() { - let path_idx = builder.move_path_for(&Lvalue::Temp(temp)); - path_map.fill_to(path_idx.index()); +impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { + fn gather_statement(&mut self, loc: Location, stmt: &Statement<'tcx>) { + debug!("gather_statement({:?}, {:?})", loc, stmt); + match stmt.kind { + StatementKind::Assign(ref lval, ref rval) => { + self.create_move_path(lval); + self.gather_rvalue(loc, rval); + } + StatementKind::StorageLive(_) | + StatementKind::StorageDead(_) => {} + StatementKind::SetDiscriminant{ .. } => { + span_bug!(stmt.source_info.span, + "SetDiscriminant should not exist during borrowck"); + } + } } - for (bb, bb_data) in mir.basic_blocks().iter_enumerated() { - let loc_map_bb = &mut loc_map[bb.index()]; - - debug_assert!(loc_map_bb.len() == 0); - let len = bb_data.statements.len(); - loc_map_bb.fill_to(len); - debug_assert!(loc_map_bb.len() == len + 1); - - let mut bb_ctxt = BlockContext { - _tcx: tcx, - moves: &mut moves, - builder: builder, - path_map: &mut path_map, - loc_map_bb: loc_map_bb, - }; - - for (i, stmt) in bb_data.statements.iter().enumerate() { - let source = Location { block: bb, statement_index: i }; - match stmt.kind { - StatementKind::Assign(ref lval, ref rval) => { - bb_ctxt.builder.create_move_path(lval); - - // Ensure that the path_map contains entries even - // if the lvalue is assigned and never read. - let assigned_path = bb_ctxt.builder.move_path_for(lval); - bb_ctxt.path_map.fill_to(assigned_path.index()); - - match *rval { - Rvalue::Use(ref operand) => { - bb_ctxt.on_operand(SK::Use, operand, source) - } - Rvalue::Repeat(ref operand, ref _const) => - bb_ctxt.on_operand(SK::Repeat, operand, source), - Rvalue::Cast(ref _kind, ref operand, ref _ty) => - bb_ctxt.on_operand(SK::Cast, operand, source), - Rvalue::BinaryOp(ref _binop, ref operand1, ref operand2) | - Rvalue::CheckedBinaryOp(ref _binop, ref operand1, ref operand2) => { - bb_ctxt.on_operand(SK::BinaryOp, operand1, source); - bb_ctxt.on_operand(SK::BinaryOp, operand2, source); - } - Rvalue::UnaryOp(ref _unop, ref operand) => { - bb_ctxt.on_operand(SK::UnaryOp, operand, source); - } - Rvalue::Box(ref _ty) => { - // this is creating uninitialized - // memory that needs to be initialized. - let deref_lval = Lvalue::Projection(Box::new(Projection { - base: lval.clone(), - elem: ProjectionElem::Deref, - })); - bb_ctxt.on_move_out_lval(SK::Box, &deref_lval, source); - } - Rvalue::Aggregate(ref _kind, ref operands) => { - for operand in operands { - bb_ctxt.on_operand(SK::Aggregate, operand, source); - } - } - Rvalue::Ref(..) | - Rvalue::Len(..) | - Rvalue::InlineAsm { .. } => {} - } - } - StatementKind::StorageLive(_) | - StatementKind::StorageDead(_) => {} - StatementKind::SetDiscriminant{ .. } => { - span_bug!(stmt.source_info.span, - "SetDiscriminant should not exist during borrowck"); + fn gather_rvalue(&mut self, loc: Location, rvalue: &Rvalue<'tcx>) { + match *rvalue { + Rvalue::Use(ref operand) | + Rvalue::Repeat(ref operand, _) | + Rvalue::Cast(_, ref operand, _) | + Rvalue::UnaryOp(_, ref operand) => { + self.gather_operand(loc, operand) + } + Rvalue::BinaryOp(ref _binop, ref lhs, ref rhs) | + Rvalue::CheckedBinaryOp(ref _binop, ref lhs, ref rhs) => { + self.gather_operand(loc, lhs); + self.gather_operand(loc, rhs); + } + Rvalue::Aggregate(ref _kind, ref operands) => { + for operand in operands { + self.gather_operand(loc, operand); } } + Rvalue::Ref(..) | + Rvalue::Len(..) | + Rvalue::InlineAsm { .. } => {} + Rvalue::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 lvalue. + // + // However, this does not matter - MIR building is careful to + // only emit a shallow free for the partially-initialized + // temporary. + // + // In any case, if we want to fix this, we have to register a + // special move and change the `statement_effect` functions. + } } + } - debug!("gather_moves({:?})", bb_data.terminator()); - match bb_data.terminator().kind { + fn gather_terminator(&mut self, loc: Location, term: &Terminator<'tcx>) { + debug!("gather_terminator({:?}, {:?})", loc, term); + match term.kind { TerminatorKind::Goto { target: _ } | TerminatorKind::Resume | TerminatorKind::Unreachable => { } TerminatorKind::Return => { - let source = Location { block: bb, - statement_index: bb_data.statements.len() }; - debug!("gather_moves Return on_move_out_lval return {:?}", source); - bb_ctxt.on_move_out_lval(SK::Return, &Lvalue::ReturnPointer, source); - } - - TerminatorKind::If { ref cond, targets: _ } => { - let source = Location { block: bb, - statement_index: bb_data.statements.len() }; - bb_ctxt.on_operand(SK::If, cond, source); - } - - TerminatorKind::Assert { - ref cond, expected: _, - ref msg, target: _, cleanup: _ - } => { - // The `cond` is always of (copyable) type `bool`, - // so there will never be anything to move. - let _ = cond; - match *msg { - AssertMessage:: BoundsCheck { ref len, ref index } => { - // Same for the usize length and index in bounds-checking. - let _ = (len, index); - } - AssertMessage::Math(_) => {} - } + self.gather_move(loc, &Lvalue::ReturnPointer); } - TerminatorKind::SwitchInt { switch_ty: _, values: _, targets: _, ref discr } | - TerminatorKind::Switch { adt_def: _, targets: _, ref discr } => { - // The `discr` is not consumed; that is instead - // encoded on specific match arms (and for - // SwitchInt`, it is always a copyable integer - // type anyway). - let _ = discr; + TerminatorKind::If { .. } | + TerminatorKind::Assert { .. } | + TerminatorKind::SwitchInt { .. } | + TerminatorKind::Switch { .. } => { + // branching terminators - these don't move anything } TerminatorKind::Drop { ref location, target: _, unwind: _ } => { - let source = Location { block: bb, - statement_index: bb_data.statements.len() }; - bb_ctxt.on_move_out_lval(SK::Drop, location, source); + self.gather_move(loc, location); } TerminatorKind::DropAndReplace { ref location, ref value, .. } => { - let assigned_path = bb_ctxt.builder.move_path_for(location); - bb_ctxt.path_map.fill_to(assigned_path.index()); - - let source = Location { block: bb, - statement_index: bb_data.statements.len() }; - bb_ctxt.on_operand(SK::Use, value, source); + self.create_move_path(location); + self.gather_operand(loc, value); } TerminatorKind::Call { ref func, ref args, ref destination, cleanup: _ } => { - let source = Location { block: bb, - statement_index: bb_data.statements.len() }; - bb_ctxt.on_operand(SK::CallFn, func, source); + self.gather_operand(loc, func); for arg in args { - debug!("gather_moves Call on_operand {:?} {:?}", arg, source); - bb_ctxt.on_operand(SK::CallArg, arg, source); + self.gather_operand(loc, arg); } if let Some((ref destination, _bb)) = *destination { - debug!("gather_moves Call create_move_path {:?} {:?}", destination, source); - - // Ensure that the path_map contains entries even - // if the lvalue is assigned and never read. - let assigned_path = bb_ctxt.builder.move_path_for(destination); - bb_ctxt.path_map.fill_to(assigned_path.index()); - - bb_ctxt.builder.create_move_path(destination); + self.create_move_path(destination); } } } - - builder = bb_ctxt.builder; } - // At this point, we may have created some MovePaths that do not - // have corresponding entries in the path map. - // - // (For example, creating the path `a.b.c` may, as a side-effect, - // create a path for the parent path `a.b`.) - // - // All such paths were not referenced ... - // - // well you know, lets actually try just asserting that the path map *is* complete. - assert_eq!(path_map.len(), builder.pre_move_paths.len()); - - let pre_move_paths = builder.pre_move_paths; - let move_paths: Vec<_> = pre_move_paths.into_iter() - .map(|p| p.into_move_path()) - .collect(); - - debug!("{}", { - let mut seen: Vec<_> = move_paths.iter().map(|_| false).collect(); - for (j, &MoveOut { ref path, ref source }) in moves.iter().enumerate() { - debug!("MovePathData moves[{}]: MoveOut {{ path: {:?} = {:?}, source: {:?} }}", - j, path, move_paths[path.index()], source); - seen[path.index()] = true; - } - for (j, path) in move_paths.iter().enumerate() { - if !seen[j] { - debug!("MovePathData move_paths[{}]: {:?}", j, path); + fn gather_operand(&mut self, loc: Location, operand: &Operand<'tcx>) { + match *operand { + Operand::Constant(..) => {} // not-a-move + Operand::Consume(ref lval) => { // a move + self.gather_move(loc, lval); } } - "done dumping MovePathData" - }); - - MoveData { - move_paths: MovePathData { move_paths: move_paths, }, - moves: moves, - loc_map: LocMap { map: loc_map }, - path_map: PathMap { map: path_map }, - rev_lookup: builder.rev_lookup, } -} -struct BlockContext<'b, 'tcx: 'b> { - _tcx: TyCtxt<'b, 'tcx, 'tcx>, - moves: &'b mut Vec, - builder: MovePathDataBuilder<'tcx>, - path_map: &'b mut Vec>, - loc_map_bb: &'b mut Vec>, -} + fn gather_move(&mut self, loc: Location, lval: &Lvalue<'tcx>) { + debug!("gather_move({:?}, {:?})", loc, lval); -impl<'b, 'tcx: 'b> BlockContext<'b, 'tcx> { - fn on_move_out_lval(&mut self, - stmt_kind: StmtKind, - lval: &Lvalue<'tcx>, - source: Location) { - let i = source.statement_index; - let index = MoveOutIndex::new(self.moves.len()); - - let path = self.builder.move_path_for(lval); - self.moves.push(MoveOut { path: path, source: source.clone() }); - self.path_map.fill_to(path.index()); - - debug!("ctxt: {:?} add consume of lval: {:?} \ - at index: {:?} \ - to path_map for path: {:?} and \ - to loc_map for loc: {:?}", - stmt_kind, lval, index, path, source); - - debug_assert!(path.index() < self.path_map.len()); - // this is actually a questionable assert; at the very - // least, incorrect input code can probably cause it to - // fire. - assert!(self.path_map[path.index()].iter().find(|idx| **idx == index).is_none()); - self.path_map[path.index()].push(index); - - debug_assert!(i < self.loc_map_bb.len()); - debug_assert!(self.loc_map_bb[i].iter().find(|idx| **idx == index).is_none()); - self.loc_map_bb[i].push(index); - } + let lv_ty = lval.ty(self.mir, self.tcx).to_ty(self.tcx); + if !lv_ty.moves_by_default(self.tcx, self.param_env, DUMMY_SP) { + debug!("gather_move({:?}, {:?}) - {:?} is Copy. skipping", loc, lval, lv_ty); + return + } - fn on_operand(&mut self, stmt_kind: StmtKind, operand: &Operand<'tcx>, source: Location) { - match *operand { - Operand::Constant(..) => {} // not-a-move - Operand::Consume(ref lval) => { // a move - self.on_move_out_lval(stmt_kind, lval, source); + let path = match self.move_path_for(lval) { + Ok(path) | Err(MovePathError::UnionMove { path }) => path, + Err(MovePathError::IllegalMove) => { + // Moving out of a bad path. Eventually, this should be a MIR + // borrowck error instead of a bug. + span_bug!(self.mir.span, + "Broken MIR: moving out of lvalue {:?}: {:?} at {:?}", + lval, lv_ty, loc); } - } + }; + let move_out = self.data.moves.push(MoveOut { path: path, source: loc }); + + debug!("gather_move({:?}, {:?}): adding move {:?} of {:?}", + loc, lval, move_out, path); + + self.data.path_map[path].push(move_out); + self.data.loc_map[loc].push(move_out); } } diff --git a/src/librustc_borrowck/borrowck/mir/mod.rs b/src/librustc_borrowck/borrowck/mir/mod.rs index e035e268b1c4c..5b5d782bc83a2 100644 --- a/src/librustc_borrowck/borrowck/mir/mod.rs +++ b/src/librustc_borrowck/borrowck/mir/mod.rs @@ -34,8 +34,7 @@ use self::dataflow::{DataflowOperator}; use self::dataflow::{Dataflow, DataflowAnalysis, DataflowResults}; use self::dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals}; use self::dataflow::{DefinitelyInitializedLvals}; -use self::gather_moves::{MoveData, MovePathIndex}; -use self::gather_moves::{MovePathContent, MovePathData}; +use self::gather_moves::{MoveData, MovePathIndex, LookupResult}; fn has_rustc_mir_with(attrs: &[ast::Attribute], name: &str) -> Option> { for attr in attrs { @@ -78,8 +77,8 @@ pub fn borrowck_mir<'a, 'tcx: 'a>( let tcx = bcx.tcx; - let move_data = MoveData::gather_moves(mir, tcx); let param_env = ty::ParameterEnvironment::for_item(tcx, id); + let move_data = MoveData::gather_moves(mir, tcx, ¶m_env); let mdpe = MoveDataParamEnv { move_data: move_data, param_env: param_env }; let flow_inits = do_dataflow(tcx, mir, id, attributes, &mdpe, MaybeInitializedLvals::new(tcx, mir)); @@ -211,23 +210,23 @@ impl DropFlagState { } } -fn move_path_children_matching<'tcx, F>(move_paths: &MovePathData<'tcx>, +fn move_path_children_matching<'tcx, F>(move_data: &MoveData<'tcx>, path: MovePathIndex, mut cond: F) -> Option where F: FnMut(&repr::LvalueProjection<'tcx>) -> bool { - let mut next_child = move_paths[path].first_child; + let mut next_child = move_data.move_paths[path].first_child; while let Some(child_index) = next_child { - match move_paths[child_index].content { - MovePathContent::Lvalue(repr::Lvalue::Projection(ref proj)) => { + match move_data.move_paths[child_index].lvalue { + repr::Lvalue::Projection(ref proj) => { if cond(proj) { return Some(child_index) } } _ => {} } - next_child = move_paths[child_index].next_sibling; + next_child = move_data.move_paths[child_index].next_sibling; } None @@ -257,12 +256,12 @@ fn lvalue_contents_drop_state_cannot_differ<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx let ty = lv.ty(mir, tcx).to_ty(tcx); match ty.sty { ty::TyArray(..) | ty::TySlice(..) | ty::TyRef(..) | ty::TyRawPtr(..) => { - debug!("lvalue_contents_drop_state_cannot_differ lv: {:?} ty: {:?} refd => false", + debug!("lvalue_contents_drop_state_cannot_differ lv: {:?} ty: {:?} refd => true", lv, ty); true } - ty::TyAdt(def, _) if def.has_dtor() => { - debug!("lvalue_contents_drop_state_cannot_differ lv: {:?} ty: {:?} Drop => false", + ty::TyAdt(def, _) if def.has_dtor() || def.is_union() => { + debug!("lvalue_contents_drop_state_cannot_differ lv: {:?} ty: {:?} Drop => true", lv, ty); true } @@ -272,6 +271,24 @@ fn lvalue_contents_drop_state_cannot_differ<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx } } +fn on_lookup_result_bits<'a, 'tcx, F>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + mir: &Mir<'tcx>, + move_data: &MoveData<'tcx>, + lookup_result: LookupResult, + each_child: F) + where F: FnMut(MovePathIndex) +{ + match lookup_result { + LookupResult::Parent(..) => { + // access to untracked value - do not touch children + } + LookupResult::Exact(e) => { + on_all_children_bits(tcx, mir, move_data, e, each_child) + } + } +} + fn on_all_children_bits<'a, 'tcx, F>( tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &Mir<'tcx>, @@ -286,12 +303,8 @@ fn on_all_children_bits<'a, 'tcx, F>( move_data: &MoveData<'tcx>, path: MovePathIndex) -> bool { - match move_data.move_paths[path].content { - MovePathContent::Lvalue(ref lvalue) => { - lvalue_contents_drop_state_cannot_differ(tcx, mir, lvalue) - } - _ => true - } + lvalue_contents_drop_state_cannot_differ( + tcx, mir, &move_data.move_paths[path].lvalue) } fn on_all_children_bits<'a, 'tcx, F>( @@ -327,10 +340,10 @@ fn drop_flag_effects_for_function_entry<'a, 'tcx, F>( let move_data = &ctxt.move_data; for (arg, _) in mir.arg_decls.iter_enumerated() { let lvalue = repr::Lvalue::Arg(arg); - let move_path_index = move_data.rev_lookup.find(&lvalue); - on_all_children_bits(tcx, mir, move_data, - move_path_index, - |moi| callback(moi, DropFlagState::Present)); + let lookup_result = move_data.rev_lookup.find(&lvalue); + on_lookup_result_bits(tcx, mir, move_data, + lookup_result, + |moi| callback(moi, DropFlagState::Present)); } } @@ -352,11 +365,10 @@ fn drop_flag_effects_for_location<'a, 'tcx, F>( debug!("moving out of path {:?}", move_data.move_paths[path]); // don't move out of non-Copy things - if let MovePathContent::Lvalue(ref lvalue) = move_data.move_paths[path].content { - let ty = lvalue.ty(mir, tcx).to_ty(tcx); - if !ty.moves_by_default(tcx, param_env, DUMMY_SP) { - continue; - } + let lvalue = &move_data.move_paths[path].lvalue; + let ty = lvalue.ty(mir, tcx).to_ty(tcx); + if !ty.moves_by_default(tcx, param_env, DUMMY_SP) { + continue; } on_all_children_bits(tcx, mir, move_data, @@ -372,9 +384,9 @@ fn drop_flag_effects_for_location<'a, 'tcx, F>( } repr::StatementKind::Assign(ref lvalue, _) => { debug!("drop_flag_effects: assignment {:?}", stmt); - on_all_children_bits(tcx, mir, move_data, - move_data.rev_lookup.find(lvalue), - |moi| callback(moi, DropFlagState::Present)) + on_lookup_result_bits(tcx, mir, move_data, + move_data.rev_lookup.find(lvalue), + |moi| callback(moi, DropFlagState::Present)) } repr::StatementKind::StorageLive(_) | repr::StatementKind::StorageDead(_) => {} @@ -383,9 +395,9 @@ fn drop_flag_effects_for_location<'a, 'tcx, F>( debug!("drop_flag_effects: replace {:?}", block.terminator()); match block.terminator().kind { repr::TerminatorKind::DropAndReplace { ref location, .. } => { - on_all_children_bits(tcx, mir, move_data, - move_data.rev_lookup.find(location), - |moi| callback(moi, DropFlagState::Present)) + on_lookup_result_bits(tcx, mir, move_data, + move_data.rev_lookup.find(location), + |moi| callback(moi, DropFlagState::Present)) } _ => { // other terminators do not contain move-ins diff --git a/src/librustc_trans/mir/analyze.rs b/src/librustc_trans/mir/analyze.rs index cfd1ec0996861..e13da2531024f 100644 --- a/src/librustc_trans/mir/analyze.rs +++ b/src/librustc_trans/mir/analyze.rs @@ -180,7 +180,6 @@ impl<'mir, 'bcx, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'bcx, 'tcx> { LvalueContext::Store | LvalueContext::Inspect | LvalueContext::Borrow { .. } | - LvalueContext::Slice { .. } | LvalueContext::Projection => { self.mark_as_lvalue(index); } diff --git a/src/test/compile-fail/borrowck/borrowck-move-out-of-vec-tail.rs b/src/test/compile-fail/borrowck/borrowck-move-out-of-vec-tail.rs index f595d9d81cc6e..51e00a0ad2c3a 100644 --- a/src/test/compile-fail/borrowck/borrowck-move-out-of-vec-tail.rs +++ b/src/test/compile-fail/borrowck/borrowck-move-out-of-vec-tail.rs @@ -28,7 +28,7 @@ pub fn main() { [_, ref tail..] => { match tail { &[Foo { string: a }, - //~^ ERROR cannot move out of borrowed content + //~^ ERROR cannot move out of type `[Foo]` //~| cannot move out //~| to prevent move Foo { string: b }] => { diff --git a/src/test/compile-fail/borrowck/borrowck-vec-pattern-nesting.rs b/src/test/compile-fail/borrowck/borrowck-vec-pattern-nesting.rs index d89b4100789f9..ae001e4e34d16 100644 --- a/src/test/compile-fail/borrowck/borrowck-vec-pattern-nesting.rs +++ b/src/test/compile-fail/borrowck/borrowck-vec-pattern-nesting.rs @@ -40,7 +40,7 @@ fn c() { let mut vec = vec!(box 1, box 2, box 3); let vec: &mut [Box] = &mut vec; match vec { - &mut [_a, //~ ERROR cannot move out of borrowed content + &mut [_a, //~ ERROR cannot move out //~| cannot move out //~| to prevent move .. diff --git a/src/test/compile-fail/issue-12567.rs b/src/test/compile-fail/issue-12567.rs index 32a6ea4f062cb..15d9a318d29ca 100644 --- a/src/test/compile-fail/issue-12567.rs +++ b/src/test/compile-fail/issue-12567.rs @@ -15,12 +15,12 @@ fn match_vecs<'a, T>(l1: &'a [T], l2: &'a [T]) { (&[], &[]) => println!("both empty"), (&[], &[hd, ..]) | (&[hd, ..], &[]) => println!("one empty"), - //~^^ ERROR: cannot move out of borrowed content - //~^^^ ERROR: cannot move out of borrowed content + //~^^ ERROR: cannot move out of type `[T]`, a non-copy array + //~^^^ ERROR: cannot move out of type `[T]`, a non-copy array (&[hd1, ..], &[hd2, ..]) => println!("both nonempty"), - //~^^ ERROR: cannot move out of borrowed content - //~^^^ ERROR: cannot move out of borrowed content + //~^^ ERROR: cannot move out of type `[T]`, a non-copy array + //~^^^ ERROR: cannot move out of type `[T]`, a non-copy array } } diff --git a/src/test/compile-fail/mir-dataflow/uninits-2.rs b/src/test/compile-fail/mir-dataflow/uninits-2.rs index e0bf42534499c..94f812a40a9b5 100644 --- a/src/test/compile-fail/mir-dataflow/uninits-2.rs +++ b/src/test/compile-fail/mir-dataflow/uninits-2.rs @@ -23,7 +23,7 @@ struct S(i32); fn foo(x: &mut S) { // `x` is initialized here, so maybe-uninit bit is 0. - unsafe { *rustc_peek(&x) }; //~ ERROR rustc_peek: bit not set + unsafe { rustc_peek(&x) }; //~ ERROR rustc_peek: bit not set ::std::mem::drop(x); diff --git a/src/test/compile-fail/move-out-of-slice-1.rs b/src/test/compile-fail/move-out-of-slice-1.rs new file mode 100644 index 0000000000000..f3efc68701e94 --- /dev/null +++ b/src/test/compile-fail/move-out-of-slice-1.rs @@ -0,0 +1,21 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(slice_patterns, box_patterns)] + +struct A; + +fn main() { + let a: Box<[A]> = Box::new([A]); + match a { + box [a] => {}, //~ ERROR cannot move out of type `[A]` + _ => {} + } +} diff --git a/src/test/run-pass/dynamic-drop.rs b/src/test/run-pass/dynamic-drop.rs index 2b016dfb33eca..a2cca20640926 100644 --- a/src/test/run-pass/dynamic-drop.rs +++ b/src/test/run-pass/dynamic-drop.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(rustc_attrs)] +#![feature(untagged_unions)] use std::cell::{Cell, RefCell}; use std::panic; @@ -111,6 +111,20 @@ fn assignment1(a: &Allocator, c0: bool) { _v = _w; } +#[allow(unions_with_drop_fields)] +union Boxy { + a: T, + b: T, +} + +fn union1(a: &Allocator) { + unsafe { + let mut u = Boxy { a: a.alloc() }; + u.b = a.alloc(); + drop(u.a); + } +} + fn run_test(mut f: F) where F: FnMut(&Allocator) { @@ -136,6 +150,13 @@ fn run_test(mut f: F) } } +fn run_test_nopanic(mut f: F) + where F: FnMut(&Allocator) +{ + let first_alloc = Allocator::new(usize::MAX); + f(&first_alloc); +} + fn main() { run_test(|a| dynamic_init(a, false)); run_test(|a| dynamic_init(a, true)); @@ -149,4 +170,6 @@ fn main() { run_test(|a| assignment1(a, false)); run_test(|a| assignment1(a, true)); + + run_test_nopanic(|a| union1(a)); }