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:
parent
7b25e88602
commit
eeedc144be
3 changed files with 60 additions and 23 deletions
|
@ -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) {
|
||||||
// Moving out of a bad path. Eventually, this should be a MIR
|
Ok(path) | Err(MovePathError::UnionMove { path }) => path,
|
||||||
// borrowck error instead of a bug.
|
Err(MovePathError::IllegalMove) => {
|
||||||
span_bug!(self.mir.span,
|
// Moving out of a bad path. Eventually, this should be a MIR
|
||||||
"Broken MIR: moving out of lvalue {:?}: {:?} at {:?}",
|
// borrowck error instead of a bug.
|
||||||
lval, lv_ty, loc);
|
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 });
|
let move_out = self.data.moves.push(MoveOut { path: path, source: loc });
|
||||||
|
|
||||||
debug!("gather_move({:?}, {:?}): adding move {:?} of {:?}",
|
debug!("gather_move({:?}, {:?}): adding move {:?} of {:?}",
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue