Auto merge of #36353 - arielb1:union-drops, r=pnkfelix
a few move-checker improvements This fixes moves out of unions and prohibits moves out of slices (see the individual commits). r? @pnkfelix
This commit is contained in:
commit
c6673db58d
@ -1243,7 +1243,7 @@ impl<'a, 'b> GraphSuccessors<'b> for Mir<'a> {
|
||||
type Iter = IntoIter<BasicBlock>;
|
||||
}
|
||||
|
||||
#[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)
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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<MoveError<'tcx>>
|
||||
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
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);
|
||||
});
|
||||
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<MovePathIndex>)>
|
||||
{
|
||||
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)
|
||||
);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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<P<MetaItem>> {
|
||||
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<MovePathIndex>
|
||||
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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 }] => {
|
||||
|
@ -40,7 +40,7 @@ fn c() {
|
||||
let mut vec = vec!(box 1, box 2, box 3);
|
||||
let vec: &mut [Box<isize>] = &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
|
||||
..
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
21
src/test/compile-fail/move-out-of-slice-1.rs
Normal file
21
src/test/compile-fail/move-out-of-slice-1.rs
Normal file
@ -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 <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.
|
||||
|
||||
#![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]`
|
||||
_ => {}
|
||||
}
|
||||
}
|
@ -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<T> {
|
||||
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<F>(mut f: F)
|
||||
where F: FnMut(&Allocator)
|
||||
{
|
||||
@ -136,6 +150,13 @@ fn run_test<F>(mut f: F)
|
||||
}
|
||||
}
|
||||
|
||||
fn run_test_nopanic<F>(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));
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user