1
Fork 0

fix dynamic drop for unions

Moving out of a union is now treated like moving out of its parent type.

Fixes #36246
This commit is contained in:
Ariel Ben-Yehuda 2016-09-08 20:12:53 +03:00 committed by Ariel Ben-Yehuda
parent 7b25e88602
commit eeedc144be
3 changed files with 60 additions and 23 deletions

View file

@ -12,7 +12,6 @@
use rustc::ty::{self, TyCtxt, ParameterEnvironment}; use rustc::ty::{self, TyCtxt, ParameterEnvironment};
use rustc::mir::repr::*; use rustc::mir::repr::*;
use rustc::util::nodemap::FnvHashMap; use rustc::util::nodemap::FnvHashMap;
use rustc::util::common::ErrorReported;
use rustc_data_structures::indexed_vec::{IndexVec}; use rustc_data_structures::indexed_vec::{IndexVec};
use syntax::codemap::DUMMY_SP; use syntax::codemap::DUMMY_SP;
@ -198,6 +197,11 @@ struct MoveDataBuilder<'a, 'tcx: 'a> {
data: MoveData<'tcx>, data: MoveData<'tcx>,
} }
pub enum MovePathError {
IllegalMove,
UnionMove { path: MovePathIndex },
}
impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
fn new(mir: &'a Mir<'tcx>, fn new(mir: &'a Mir<'tcx>,
tcx: TyCtxt<'a, 'tcx, 'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>,
@ -256,7 +260,7 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
move_path move_path
} }
/// This creates a MovePath for a given lvalue, returning an `ErrorReported` /// This creates a MovePath for a given lvalue, returning an `MovePathError`
/// if that lvalue can't be moved from. /// if that lvalue can't be moved from.
/// ///
/// NOTE: lvalues behind references *do not* get a move path, which is /// NOTE: lvalues behind references *do not* get a move path, which is
@ -264,7 +268,7 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
/// ///
/// Maybe we should have seperate "borrowck" and "moveck" modes. /// Maybe we should have seperate "borrowck" and "moveck" modes.
fn move_path_for(&mut self, lval: &Lvalue<'tcx>) fn move_path_for(&mut self, lval: &Lvalue<'tcx>)
-> Result<MovePathIndex, ErrorReported> -> Result<MovePathIndex, MovePathError>
{ {
debug!("lookup({:?})", lval); debug!("lookup({:?})", lval);
match *lval { match *lval {
@ -272,7 +276,7 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
Lvalue::Arg(arg) => Ok(self.data.rev_lookup.args[arg]), Lvalue::Arg(arg) => Ok(self.data.rev_lookup.args[arg]),
Lvalue::Temp(temp) => Ok(self.data.rev_lookup.temps[temp]), Lvalue::Temp(temp) => Ok(self.data.rev_lookup.temps[temp]),
// error: can't move out of a static // error: can't move out of a static
Lvalue::Static(..) => Err(ErrorReported), Lvalue::Static(..) => Err(MovePathError::IllegalMove),
Lvalue::ReturnPointer => match self.data.rev_lookup.return_ptr { Lvalue::ReturnPointer => match self.data.rev_lookup.return_ptr {
Some(ptr) => Ok(ptr), Some(ptr) => Ok(ptr),
ref mut ptr @ None => { ref mut ptr @ None => {
@ -300,21 +304,28 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
fn move_path_for_projection(&mut self, fn move_path_for_projection(&mut self,
lval: &Lvalue<'tcx>, lval: &Lvalue<'tcx>,
proj: &LvalueProjection<'tcx>) proj: &LvalueProjection<'tcx>)
-> Result<MovePathIndex, ErrorReported> -> Result<MovePathIndex, MovePathError>
{ {
let base = try!(self.move_path_for(&proj.base)); let base = try!(self.move_path_for(&proj.base));
let lv_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx); let lv_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
match lv_ty.sty { match lv_ty.sty {
// error: can't move out of borrowed content // error: can't move out of borrowed content
ty::TyRef(..) | ty::TyRawPtr(..) => return Err(ErrorReported), ty::TyRef(..) | ty::TyRawPtr(..) => return Err(MovePathError::IllegalMove),
// error: can't move out of struct with destructor // error: can't move out of struct with destructor
ty::TyStruct(adt, _) | ty::TyEnum(adt, _) if adt.has_dtor() => ty::TyAdt(adt, _) if adt.has_dtor() =>
return Err(ErrorReported), return Err(MovePathError::IllegalMove),
// move out of union - always move the entire union
ty::TyArray(..) | ty::TySlice(..) => match proj.elem { 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 // error: can't move out of an array
ProjectionElem::Index(..) => return Err(ErrorReported), ProjectionElem::Index(..) => return Err(MovePathError::IllegalMove),
_ => {} _ => {
// FIXME: still badly broken
}
}, },
_ => {} _ => {}
}; };
@ -521,13 +532,16 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
return return
} }
let path = self.move_path_for(lval).unwrap_or_else(|_| { 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 // Moving out of a bad path. Eventually, this should be a MIR
// borrowck error instead of a bug. // borrowck error instead of a bug.
span_bug!(self.mir.span, span_bug!(self.mir.span,
"Broken MIR: moving out of lvalue {:?}: {:?} at {:?}", "Broken MIR: moving out of lvalue {:?}: {:?} at {:?}",
lval, lv_ty, loc); lval, lv_ty, loc);
}); }
};
let move_out = self.data.moves.push(MoveOut { path: path, source: loc }); let move_out = self.data.moves.push(MoveOut { path: path, source: loc });
debug!("gather_move({:?}, {:?}): adding move {:?} of {:?}", debug!("gather_move({:?}, {:?}): adding move {:?} of {:?}",

View file

@ -256,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); let ty = lv.ty(mir, tcx).to_ty(tcx);
match ty.sty { match ty.sty {
ty::TyArray(..) | ty::TySlice(..) | ty::TyRef(..) | ty::TyRawPtr(..) => { 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); lv, ty);
true true
} }
ty::TyAdt(def, _) if def.has_dtor() => { ty::TyAdt(def, _) if def.has_dtor() || def.is_union() => {
debug!("lvalue_contents_drop_state_cannot_differ lv: {:?} ty: {:?} Drop => false", debug!("lvalue_contents_drop_state_cannot_differ lv: {:?} ty: {:?} Drop => true",
lv, ty); lv, ty);
true true
} }

View file

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![feature(rustc_attrs)] #![feature(untagged_unions)]
use std::cell::{Cell, RefCell}; use std::cell::{Cell, RefCell};
use std::panic; use std::panic;
@ -111,6 +111,20 @@ fn assignment1(a: &Allocator, c0: bool) {
_v = _w; _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) fn run_test<F>(mut f: F)
where F: FnMut(&Allocator) 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() { fn main() {
run_test(|a| dynamic_init(a, false)); run_test(|a| dynamic_init(a, false));
run_test(|a| dynamic_init(a, true)); run_test(|a| dynamic_init(a, true));
@ -149,4 +170,6 @@ fn main() {
run_test(|a| assignment1(a, false)); run_test(|a| assignment1(a, false));
run_test(|a| assignment1(a, true)); run_test(|a| assignment1(a, true));
run_test_nopanic(|a| union1(a));
} }