Merge pull request #136 from oli-obk/master
refactor drop glue and fix Box<Box<Struct>> as Box<Trait>
This commit is contained in:
commit
c342cb5fe8
6 changed files with 335 additions and 241 deletions
|
|
@ -120,9 +120,11 @@ pub enum Function<'tcx> {
|
|||
Concrete(FunctionDefinition<'tcx>),
|
||||
/// Glue required to call a regular function through a Fn(Mut|Once) trait object
|
||||
FnDefAsTraitObject(FunctionDefinition<'tcx>),
|
||||
/// Glue required to call the actual drop impl's `drop` method.
|
||||
/// Drop glue takes the `self` by value, while `Drop::drop` take `&mut self`
|
||||
DropGlue(FunctionDefinition<'tcx>),
|
||||
/// A drop glue function only needs to know the real type, and then miri can extract
|
||||
/// that type from a vtable's drop pointer.
|
||||
/// Instead of storing some drop function, we act as if there are no trait objects, by
|
||||
/// mapping trait objects to their real types before acting on them.
|
||||
DropGlue(ty::Ty<'tcx>),
|
||||
/// Glue required to treat the ptr part of a fat pointer
|
||||
/// as a function pointer
|
||||
FnPtrAsTraitObject(&'tcx ty::FnSig<'tcx>),
|
||||
|
|
@ -137,9 +139,9 @@ impl<'tcx> Function<'tcx> {
|
|||
other => Err(EvalError::ExpectedConcreteFunction(other)),
|
||||
}
|
||||
}
|
||||
pub fn expect_drop_glue(self) -> EvalResult<'tcx, FunctionDefinition<'tcx>> {
|
||||
pub fn expect_drop_glue_real_ty(self) -> EvalResult<'tcx, ty::Ty<'tcx>> {
|
||||
match self {
|
||||
Function::DropGlue(fn_def) => Ok(fn_def),
|
||||
Function::DropGlue(real_ty) => Ok(real_ty),
|
||||
other => Err(EvalError::ExpectedDropGlue(other)),
|
||||
}
|
||||
}
|
||||
|
|
@ -234,15 +236,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
|
|||
self.create_fn_alloc(Function::FnPtrAsTraitObject(fn_ty.sig.skip_binder()))
|
||||
}
|
||||
|
||||
pub fn create_drop_glue(&mut self, _tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, substs: &'tcx Substs<'tcx>, fn_ty: &'tcx BareFnTy<'tcx>) -> Pointer {
|
||||
self.create_fn_alloc(Function::DropGlue(FunctionDefinition {
|
||||
def_id,
|
||||
substs,
|
||||
abi: fn_ty.abi,
|
||||
// FIXME: why doesn't this compile?
|
||||
//sig: tcx.erase_late_bound_regions(&fn_ty.sig),
|
||||
sig: fn_ty.sig.skip_binder(),
|
||||
}))
|
||||
pub fn create_drop_glue(&mut self, ty: ty::Ty<'tcx>) -> Pointer {
|
||||
self.create_fn_alloc(Function::DropGlue(ty))
|
||||
}
|
||||
|
||||
pub fn create_fn_ptr(&mut self, _tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, substs: &'tcx Substs<'tcx>, fn_ty: &'tcx BareFnTy<'tcx>) -> Pointer {
|
||||
|
|
@ -495,8 +490,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
|
|||
trace!("{} {}", msg, dump_fn_def(fn_def));
|
||||
continue;
|
||||
},
|
||||
(None, Some(&Function::DropGlue(fn_def))) => {
|
||||
trace!("{} drop glue for {}", msg, dump_fn_def(fn_def));
|
||||
(None, Some(&Function::DropGlue(real_ty))) => {
|
||||
trace!("{} drop glue for {}", msg, real_ty);
|
||||
continue;
|
||||
},
|
||||
(None, Some(&Function::FnDefAsTraitObject(fn_def))) => {
|
||||
|
|
|
|||
236
src/terminator/drop.rs
Normal file
236
src/terminator/drop.rs
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
use rustc::hir::def_id::DefId;
|
||||
use rustc::ty::layout::Layout;
|
||||
use rustc::ty::subst::{Substs, Kind};
|
||||
use rustc::ty::{self, Ty};
|
||||
use rustc::mir;
|
||||
use syntax::codemap::Span;
|
||||
|
||||
use error::{EvalError, EvalResult};
|
||||
use eval_context::{EvalContext, monomorphize_field_ty, StackPopCleanup};
|
||||
use lvalue::{Lvalue, LvalueExtra};
|
||||
use memory::Pointer;
|
||||
use value::PrimVal;
|
||||
use value::Value;
|
||||
|
||||
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||
|
||||
/// Creates stack frames for all drop impls. See `drop` for the actual content.
|
||||
pub fn eval_drop_impls(&mut self, drops: Vec<(DefId, Value, &'tcx Substs<'tcx>)>, span: Span) -> EvalResult<'tcx> {
|
||||
// add them to the stack in reverse order, because the impl that needs to run the last
|
||||
// is the one that needs to be at the bottom of the stack
|
||||
for (drop_def_id, self_arg, substs) in drops.into_iter().rev() {
|
||||
let mir = self.load_mir(drop_def_id)?;
|
||||
trace!("substs for drop glue: {:?}", substs);
|
||||
self.push_stack_frame(
|
||||
drop_def_id,
|
||||
span,
|
||||
mir,
|
||||
substs,
|
||||
Lvalue::from_ptr(Pointer::zst_ptr()),
|
||||
StackPopCleanup::None,
|
||||
Vec::new(),
|
||||
)?;
|
||||
let mut arg_locals = self.frame().mir.args_iter();
|
||||
let first = arg_locals.next().expect("drop impl has self arg");
|
||||
assert!(arg_locals.next().is_none(), "drop impl should have only one arg");
|
||||
let dest = self.eval_lvalue(&mir::Lvalue::Local(first))?;
|
||||
let ty = self.frame().mir.local_decls[first].ty;
|
||||
self.write_value(self_arg, dest, ty)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// push DefIds of drop impls and their argument on the given vector
|
||||
pub fn drop(
|
||||
&mut self,
|
||||
lval: Lvalue<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
drop: &mut Vec<(DefId, Value, &'tcx Substs<'tcx>)>,
|
||||
) -> EvalResult<'tcx> {
|
||||
if !self.type_needs_drop(ty) {
|
||||
debug!("no need to drop {:?}", ty);
|
||||
return Ok(());
|
||||
}
|
||||
trace!("-need to drop {:?} at {:?}", ty, lval);
|
||||
|
||||
match ty.sty {
|
||||
// special case `Box` to deallocate the inner allocation
|
||||
ty::TyAdt(ref def, _) if def.is_box() => {
|
||||
let contents_ty = ty.boxed_ty();
|
||||
let val = self.read_lvalue(lval);
|
||||
// we are going through the read_value path, because that already does all the
|
||||
// checks for the trait object types. We'd only be repeating ourselves here.
|
||||
let val = self.follow_by_ref_value(val, ty)?;
|
||||
trace!("box dealloc on {:?}", val);
|
||||
match val {
|
||||
Value::ByRef(_) => bug!("follow_by_ref_value can't result in ByRef"),
|
||||
Value::ByVal(ptr) => {
|
||||
assert!(self.type_is_sized(contents_ty));
|
||||
let contents_ptr = ptr.to_ptr()?;
|
||||
self.drop(Lvalue::from_ptr(contents_ptr), contents_ty, drop)?;
|
||||
},
|
||||
Value::ByValPair(prim_ptr, extra) => {
|
||||
let ptr = prim_ptr.to_ptr()?;
|
||||
let extra = match self.tcx.struct_tail(contents_ty).sty {
|
||||
ty::TyDynamic(..) => LvalueExtra::Vtable(extra.to_ptr()?),
|
||||
ty::TyStr | ty::TySlice(_) => LvalueExtra::Length(extra.to_u64()?),
|
||||
_ => bug!("invalid fat pointer type: {}", ty),
|
||||
};
|
||||
self.drop(Lvalue::Ptr { ptr, extra }, contents_ty, drop)?;
|
||||
},
|
||||
}
|
||||
// We cannot use Box's destructor, because it is a no-op and only exists to reduce
|
||||
// the number of hacks required in the compiler around the Box type.
|
||||
let box_free_fn = self.tcx.lang_items.box_free_fn().expect("no box_free lang item");
|
||||
let substs = self.tcx.intern_substs(&[Kind::from(contents_ty)]);
|
||||
// this is somewhat hacky, but hey, there's no representation difference between
|
||||
// pointers, `Box`es and references, so
|
||||
// #[lang = "box_free"] unsafe fn box_free<T>(ptr: *mut T)
|
||||
// is the same as
|
||||
// fn drop(&mut self) if Self is Box<T>
|
||||
drop.push((box_free_fn, val, substs));
|
||||
},
|
||||
|
||||
ty::TyAdt(adt_def, substs) => {
|
||||
// FIXME: some structs are represented as ByValPair
|
||||
let lval = self.force_allocation(lval)?;
|
||||
let adt_ptr = match lval {
|
||||
Lvalue::Ptr { ptr, .. } => ptr,
|
||||
_ => bug!("force allocation can only yield Lvalue::Ptr"),
|
||||
};
|
||||
// run drop impl before the fields' drop impls
|
||||
if let Some(drop_def_id) = adt_def.destructor() {
|
||||
drop.push((drop_def_id, Value::ByVal(PrimVal::Ptr(adt_ptr)), substs));
|
||||
}
|
||||
let layout = self.type_layout(ty)?;
|
||||
let fields = match *layout {
|
||||
Layout::Univariant { ref variant, .. } => {
|
||||
adt_def.struct_variant().fields.iter().zip(&variant.offsets)
|
||||
},
|
||||
Layout::General { ref variants, .. } => {
|
||||
let discr_val = self.read_discriminant_value(adt_ptr, ty)? as u128;
|
||||
match adt_def.variants.iter().position(|v| discr_val == v.disr_val.to_u128_unchecked()) {
|
||||
// start at offset 1, to skip over the discriminant
|
||||
Some(i) => adt_def.variants[i].fields.iter().zip(&variants[i].offsets[1..]),
|
||||
None => return Err(EvalError::InvalidDiscriminant),
|
||||
}
|
||||
},
|
||||
Layout::StructWrappedNullablePointer { nndiscr, ref nonnull, .. } => {
|
||||
let discr = self.read_discriminant_value(adt_ptr, ty)?;
|
||||
if discr == nndiscr as u128 {
|
||||
assert_eq!(discr as usize as u128, discr);
|
||||
adt_def.variants[discr as usize].fields.iter().zip(&nonnull.offsets)
|
||||
} else {
|
||||
// FIXME: the zst variant might contain zst types that impl Drop
|
||||
return Ok(()); // nothing to do, this is zero sized (e.g. `None`)
|
||||
}
|
||||
},
|
||||
Layout::RawNullablePointer { nndiscr, .. } => {
|
||||
let discr = self.read_discriminant_value(adt_ptr, ty)?;
|
||||
if discr == nndiscr as u128 {
|
||||
assert_eq!(discr as usize as u128, discr);
|
||||
assert_eq!(adt_def.variants[discr as usize].fields.len(), 1);
|
||||
let field_ty = &adt_def.variants[discr as usize].fields[0];
|
||||
let field_ty = monomorphize_field_ty(self.tcx, field_ty, substs);
|
||||
// FIXME: once read_discriminant_value works with lvalue, don't force
|
||||
// alloc in the RawNullablePointer case
|
||||
self.drop(lval, field_ty, drop)?;
|
||||
return Ok(());
|
||||
} else {
|
||||
// FIXME: the zst variant might contain zst types that impl Drop
|
||||
return Ok(()); // nothing to do, this is zero sized (e.g. `None`)
|
||||
}
|
||||
},
|
||||
Layout::CEnum { .. } => return Ok(()),
|
||||
_ => bug!("{:?} is not an adt layout", layout),
|
||||
};
|
||||
let tcx = self.tcx;
|
||||
self.drop_fields(
|
||||
fields.map(|(ty, &offset)| (monomorphize_field_ty(tcx, ty, substs), offset)),
|
||||
lval,
|
||||
drop,
|
||||
)?;
|
||||
},
|
||||
ty::TyTuple(fields, _) => {
|
||||
let offsets = match *self.type_layout(ty)? {
|
||||
Layout::Univariant { ref variant, .. } => &variant.offsets,
|
||||
_ => bug!("tuples must be univariant"),
|
||||
};
|
||||
self.drop_fields(fields.iter().cloned().zip(offsets.iter().cloned()), lval, drop)?;
|
||||
},
|
||||
ty::TyDynamic(..) => {
|
||||
let (ptr, vtable) = match lval {
|
||||
Lvalue::Ptr { ptr, extra: LvalueExtra::Vtable(vtable) } => (ptr, vtable),
|
||||
_ => bug!("expected an lvalue with a vtable"),
|
||||
};
|
||||
let drop_fn = self.memory.read_ptr(vtable)?;
|
||||
// some values don't need to call a drop impl, so the value is null
|
||||
if drop_fn != Pointer::from_int(0) {
|
||||
let real_ty = self.memory.get_fn(drop_fn.alloc_id)?.expect_drop_glue_real_ty()?;
|
||||
self.drop(Lvalue::from_ptr(ptr), real_ty, drop)?;
|
||||
} else {
|
||||
// just a sanity check
|
||||
assert_eq!(drop_fn.offset, 0);
|
||||
}
|
||||
},
|
||||
ty::TySlice(elem_ty) => {
|
||||
let (ptr, len) = match lval {
|
||||
Lvalue::Ptr { ptr, extra: LvalueExtra::Length(len) } => (ptr, len),
|
||||
_ => bug!("expected an lvalue with a length"),
|
||||
};
|
||||
let size = self.type_size(elem_ty)?.expect("slice element must be sized");
|
||||
// FIXME: this creates a lot of stack frames if the element type has
|
||||
// a drop impl
|
||||
for i in 0..len {
|
||||
self.drop(Lvalue::from_ptr(ptr.offset(i * size)), elem_ty, drop)?;
|
||||
}
|
||||
},
|
||||
ty::TyArray(elem_ty, len) => {
|
||||
let lval = self.force_allocation(lval)?;
|
||||
let (ptr, extra) = match lval {
|
||||
Lvalue::Ptr { ptr, extra } => (ptr, extra),
|
||||
_ => bug!("expected an lvalue with optional extra data"),
|
||||
};
|
||||
let size = self.type_size(elem_ty)?.expect("array element cannot be unsized");
|
||||
// FIXME: this creates a lot of stack frames if the element type has
|
||||
// a drop impl
|
||||
for i in 0..(len as u64) {
|
||||
self.drop(Lvalue::Ptr { ptr: ptr.offset(i * size), extra }, elem_ty, drop)?;
|
||||
}
|
||||
},
|
||||
// FIXME: what about TyClosure and TyAnon?
|
||||
// other types do not need to process drop
|
||||
_ => {},
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn drop_fields<I>(
|
||||
&mut self,
|
||||
mut fields: I,
|
||||
lval: Lvalue<'tcx>,
|
||||
drop: &mut Vec<(DefId, Value, &'tcx Substs<'tcx>)>,
|
||||
) -> EvalResult<'tcx>
|
||||
where I: Iterator<Item = (Ty<'tcx>, ty::layout::Size)>,
|
||||
{
|
||||
// FIXME: some aggregates may be represented by Value::ByValPair
|
||||
let (adt_ptr, extra) = self.force_allocation(lval)?.to_ptr_and_extra();
|
||||
// manual iteration, because we need to be careful about the last field if it is unsized
|
||||
while let Some((field_ty, offset)) = fields.next() {
|
||||
let ptr = adt_ptr.offset(offset.bytes());
|
||||
if self.type_is_sized(field_ty) {
|
||||
self.drop(Lvalue::from_ptr(ptr), field_ty, drop)?;
|
||||
} else {
|
||||
self.drop(Lvalue::Ptr { ptr, extra }, field_ty, drop)?;
|
||||
break; // if it is not sized, then this is the last field anyway
|
||||
}
|
||||
}
|
||||
assert!(fields.next().is_none());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn type_needs_drop(&self, ty: Ty<'tcx>) -> bool {
|
||||
self.tcx.type_needs_drop_given_env(ty, &self.tcx.empty_parameter_environment())
|
||||
}
|
||||
}
|
||||
|
|
@ -3,19 +3,20 @@ use rustc::mir;
|
|||
use rustc::traits::{self, Reveal};
|
||||
use rustc::ty::fold::TypeFoldable;
|
||||
use rustc::ty::layout::{Layout, Size};
|
||||
use rustc::ty::subst::{Substs, Kind};
|
||||
use rustc::ty::subst::Substs;
|
||||
use rustc::ty::{self, Ty, TyCtxt, BareFnTy};
|
||||
use syntax::codemap::{DUMMY_SP, Span};
|
||||
use syntax::{ast, attr, abi};
|
||||
|
||||
use error::{EvalError, EvalResult};
|
||||
use eval_context::{EvalContext, IntegerExt, StackPopCleanup, monomorphize_field_ty, is_inhabited};
|
||||
use lvalue::{Lvalue, LvalueExtra};
|
||||
use eval_context::{EvalContext, IntegerExt, StackPopCleanup, is_inhabited};
|
||||
use lvalue::Lvalue;
|
||||
use memory::{Pointer, FunctionDefinition, Function};
|
||||
use value::PrimVal;
|
||||
use value::Value;
|
||||
|
||||
mod intrinsic;
|
||||
mod drop;
|
||||
|
||||
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||
|
||||
|
|
@ -161,31 +162,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn eval_drop_impls(&mut self, drops: Vec<(DefId, Value, &'tcx Substs<'tcx>)>, span: Span) -> EvalResult<'tcx> {
|
||||
// add them to the stack in reverse order, because the impl that needs to run the last
|
||||
// is the one that needs to be at the bottom of the stack
|
||||
for (drop_def_id, self_arg, substs) in drops.into_iter().rev() {
|
||||
let mir = self.load_mir(drop_def_id)?;
|
||||
trace!("substs for drop glue: {:?}", substs);
|
||||
self.push_stack_frame(
|
||||
drop_def_id,
|
||||
span,
|
||||
mir,
|
||||
substs,
|
||||
Lvalue::from_ptr(Pointer::zst_ptr()),
|
||||
StackPopCleanup::None,
|
||||
Vec::new(),
|
||||
)?;
|
||||
let mut arg_locals = self.frame().mir.args_iter();
|
||||
let first = arg_locals.next().expect("drop impl has self arg");
|
||||
assert!(arg_locals.next().is_none(), "drop impl should have only one arg");
|
||||
let dest = self.eval_lvalue(&mir::Lvalue::Local(first))?;
|
||||
let ty = self.frame().mir.local_decls[first].ty;
|
||||
self.write_value(self_arg, dest, ty)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn eval_fn_call(
|
||||
&mut self,
|
||||
def_id: DefId,
|
||||
|
|
@ -699,200 +675,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
vtable => bug!("resolved vtable bad vtable {:?} in trans", vtable),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn type_needs_drop(&self, ty: Ty<'tcx>) -> bool {
|
||||
self.tcx.type_needs_drop_given_env(ty, &self.tcx.empty_parameter_environment())
|
||||
}
|
||||
|
||||
/// push DefIds of drop impls and their argument on the given vector
|
||||
pub fn drop(
|
||||
&mut self,
|
||||
lval: Lvalue<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
drop: &mut Vec<(DefId, Value, &'tcx Substs<'tcx>)>,
|
||||
) -> EvalResult<'tcx> {
|
||||
if !self.type_needs_drop(ty) {
|
||||
debug!("no need to drop {:?}", ty);
|
||||
return Ok(());
|
||||
}
|
||||
trace!("-need to drop {:?} at {:?}", ty, lval);
|
||||
|
||||
match ty.sty {
|
||||
// special case `Box` to deallocate the inner allocation
|
||||
ty::TyAdt(ref def, _) if def.is_box() => {
|
||||
let contents_ty = ty.boxed_ty();
|
||||
let val = self.read_lvalue(lval);
|
||||
// we are going through the read_value path, because that already does all the
|
||||
// checks for the trait object types. We'd only be repeating ourselves here.
|
||||
let val = self.follow_by_ref_value(val, ty)?;
|
||||
trace!("box dealloc on {:?}", val);
|
||||
match val {
|
||||
Value::ByRef(_) => bug!("follow_by_ref_value can't result in ByRef"),
|
||||
Value::ByVal(ptr) => {
|
||||
assert!(self.type_is_sized(contents_ty));
|
||||
let contents_ptr = ptr.to_ptr()?;
|
||||
self.drop(Lvalue::from_ptr(contents_ptr), contents_ty, drop)?;
|
||||
},
|
||||
Value::ByValPair(prim_ptr, extra) => {
|
||||
let ptr = prim_ptr.to_ptr()?;
|
||||
let extra = match self.tcx.struct_tail(contents_ty).sty {
|
||||
ty::TyDynamic(..) => LvalueExtra::Vtable(extra.to_ptr()?),
|
||||
ty::TyStr | ty::TySlice(_) => LvalueExtra::Length(extra.to_u64()?),
|
||||
_ => bug!("invalid fat pointer type: {}", ty),
|
||||
};
|
||||
self.drop(Lvalue::Ptr { ptr, extra }, contents_ty, drop)?;
|
||||
},
|
||||
}
|
||||
let box_free_fn = self.tcx.lang_items.box_free_fn().expect("no box_free lang item");
|
||||
let substs = self.tcx.intern_substs(&[Kind::from(contents_ty)]);
|
||||
// this is somewhat hacky, but hey, there's no representation difference between
|
||||
// pointers and references, so
|
||||
// #[lang = "box_free"] unsafe fn box_free<T>(ptr: *mut T)
|
||||
// is the same as
|
||||
// fn drop(&mut self) if Self is Box<T>
|
||||
drop.push((box_free_fn, val, substs));
|
||||
},
|
||||
|
||||
ty::TyAdt(adt_def, substs) => {
|
||||
// FIXME: some structs are represented as ByValPair
|
||||
let lval = self.force_allocation(lval)?;
|
||||
let adt_ptr = match lval {
|
||||
Lvalue::Ptr { ptr, .. } => ptr,
|
||||
_ => bug!("force allocation can only yield Lvalue::Ptr"),
|
||||
};
|
||||
// run drop impl before the fields' drop impls
|
||||
if let Some(drop_def_id) = adt_def.destructor() {
|
||||
drop.push((drop_def_id, Value::ByVal(PrimVal::Ptr(adt_ptr)), substs));
|
||||
}
|
||||
let layout = self.type_layout(ty)?;
|
||||
let fields = match *layout {
|
||||
Layout::Univariant { ref variant, .. } => {
|
||||
adt_def.struct_variant().fields.iter().zip(&variant.offsets)
|
||||
},
|
||||
Layout::General { ref variants, .. } => {
|
||||
let discr_val = self.read_discriminant_value(adt_ptr, ty)? as u128;
|
||||
match adt_def.variants.iter().position(|v| discr_val == v.disr_val.to_u128_unchecked()) {
|
||||
// start at offset 1, to skip over the discriminant
|
||||
Some(i) => adt_def.variants[i].fields.iter().zip(&variants[i].offsets[1..]),
|
||||
None => return Err(EvalError::InvalidDiscriminant),
|
||||
}
|
||||
},
|
||||
Layout::StructWrappedNullablePointer { nndiscr, ref nonnull, .. } => {
|
||||
let discr = self.read_discriminant_value(adt_ptr, ty)?;
|
||||
if discr == nndiscr as u128 {
|
||||
assert_eq!(discr as usize as u128, discr);
|
||||
adt_def.variants[discr as usize].fields.iter().zip(&nonnull.offsets)
|
||||
} else {
|
||||
// FIXME: the zst variant might contain zst types that impl Drop
|
||||
return Ok(()); // nothing to do, this is zero sized (e.g. `None`)
|
||||
}
|
||||
},
|
||||
Layout::RawNullablePointer { nndiscr, .. } => {
|
||||
let discr = self.read_discriminant_value(adt_ptr, ty)?;
|
||||
if discr == nndiscr as u128 {
|
||||
assert_eq!(discr as usize as u128, discr);
|
||||
assert_eq!(adt_def.variants[discr as usize].fields.len(), 1);
|
||||
let field_ty = &adt_def.variants[discr as usize].fields[0];
|
||||
let field_ty = monomorphize_field_ty(self.tcx, field_ty, substs);
|
||||
// FIXME: once read_discriminant_value works with lvalue, don't force
|
||||
// alloc in the RawNullablePointer case
|
||||
self.drop(lval, field_ty, drop)?;
|
||||
return Ok(());
|
||||
} else {
|
||||
// FIXME: the zst variant might contain zst types that impl Drop
|
||||
return Ok(()); // nothing to do, this is zero sized (e.g. `None`)
|
||||
}
|
||||
},
|
||||
Layout::CEnum { .. } => return Ok(()),
|
||||
_ => bug!("{:?} is not an adt layout", layout),
|
||||
};
|
||||
let tcx = self.tcx;
|
||||
self.drop_fields(
|
||||
fields.map(|(ty, &offset)| (monomorphize_field_ty(tcx, ty, substs), offset)),
|
||||
lval,
|
||||
drop,
|
||||
)?;
|
||||
},
|
||||
ty::TyTuple(fields, _) => {
|
||||
let offsets = match *self.type_layout(ty)? {
|
||||
Layout::Univariant { ref variant, .. } => &variant.offsets,
|
||||
_ => bug!("tuples must be univariant"),
|
||||
};
|
||||
self.drop_fields(fields.iter().cloned().zip(offsets.iter().cloned()), lval, drop)?;
|
||||
},
|
||||
ty::TyDynamic(..) => {
|
||||
let (ptr, vtable) = match lval {
|
||||
Lvalue::Ptr { ptr, extra: LvalueExtra::Vtable(vtable) } => (ptr, vtable),
|
||||
_ => bug!("expected an lvalue with a vtable"),
|
||||
};
|
||||
let drop_fn = self.memory.read_ptr(vtable)?;
|
||||
// some values don't need to call a drop impl, so the value is null
|
||||
if drop_fn != Pointer::from_int(0) {
|
||||
let FunctionDefinition {def_id, substs, sig, ..} = self.memory.get_fn(drop_fn.alloc_id)?.expect_drop_glue()?;
|
||||
let real_ty = sig.inputs()[0];
|
||||
self.drop(Lvalue::from_ptr(ptr), real_ty, drop)?;
|
||||
drop.push((def_id, Value::ByVal(PrimVal::Ptr(ptr)), substs));
|
||||
} else {
|
||||
// just a sanity check
|
||||
assert_eq!(drop_fn.offset, 0);
|
||||
}
|
||||
},
|
||||
ty::TySlice(elem_ty) => {
|
||||
let (ptr, len) = match lval {
|
||||
Lvalue::Ptr { ptr, extra: LvalueExtra::Length(len) } => (ptr, len),
|
||||
_ => bug!("expected an lvalue with a length"),
|
||||
};
|
||||
let size = self.type_size(elem_ty)?.expect("slice element must be sized");
|
||||
// FIXME: this creates a lot of stack frames if the element type has
|
||||
// a drop impl
|
||||
for i in 0..len {
|
||||
self.drop(Lvalue::from_ptr(ptr.offset(i * size)), elem_ty, drop)?;
|
||||
}
|
||||
},
|
||||
ty::TyArray(elem_ty, len) => {
|
||||
let lval = self.force_allocation(lval)?;
|
||||
let (ptr, extra) = match lval {
|
||||
Lvalue::Ptr { ptr, extra } => (ptr, extra),
|
||||
_ => bug!("expected an lvalue with optional extra data"),
|
||||
};
|
||||
let size = self.type_size(elem_ty)?.expect("array element cannot be unsized");
|
||||
// FIXME: this creates a lot of stack frames if the element type has
|
||||
// a drop impl
|
||||
for i in 0..(len as u64) {
|
||||
self.drop(Lvalue::Ptr { ptr: ptr.offset(i * size), extra }, elem_ty, drop)?;
|
||||
}
|
||||
},
|
||||
// FIXME: what about TyClosure and TyAnon?
|
||||
// other types do not need to process drop
|
||||
_ => {},
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn drop_fields<I>(
|
||||
&mut self,
|
||||
mut fields: I,
|
||||
lval: Lvalue<'tcx>,
|
||||
drop: &mut Vec<(DefId, Value, &'tcx Substs<'tcx>)>,
|
||||
) -> EvalResult<'tcx>
|
||||
where I: Iterator<Item = (Ty<'tcx>, ty::layout::Size)>,
|
||||
{
|
||||
// FIXME: some aggregates may be represented by Value::ByValPair
|
||||
let (adt_ptr, extra) = self.force_allocation(lval)?.to_ptr_and_extra();
|
||||
// manual iteration, because we need to be careful about the last field if it is unsized
|
||||
while let Some((field_ty, offset)) = fields.next() {
|
||||
let ptr = adt_ptr.offset(offset.bytes());
|
||||
if self.type_is_sized(field_ty) {
|
||||
self.drop(Lvalue::from_ptr(ptr), field_ty, drop)?;
|
||||
} else {
|
||||
self.drop(Lvalue::Ptr { ptr, extra }, field_ty, drop)?;
|
||||
break; // if it is not sized, then this is the last field anyway
|
||||
}
|
||||
}
|
||||
assert!(fields.next().is_none());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
|||
|
|
@ -94,11 +94,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
self.memory.write_usize(vtable, 0)?;
|
||||
if let ty::TyAdt(adt_def, substs) = trait_ref.self_ty().sty {
|
||||
if let Some(drop_def_id) = adt_def.destructor() {
|
||||
let fn_ty = match self.tcx.item_type(drop_def_id).sty {
|
||||
let fn_ty = match self.tcx.item_type(drop_def_id).sty {
|
||||
ty::TyFnDef(_, _, fn_ty) => self.tcx.erase_regions(&fn_ty),
|
||||
_ => bug!("drop method is not a TyFnDef"),
|
||||
};
|
||||
let fn_ptr = self.memory.create_drop_glue(self.tcx, drop_def_id, substs, fn_ty);
|
||||
// The real type is taken from the self argument in `fn drop(&mut self)`
|
||||
let real_ty = match fn_ty.sig.skip_binder().inputs()[0].sty {
|
||||
ty::TyRef(_, mt) => self.monomorphize(mt.ty, substs),
|
||||
_ => bug!("first argument of Drop::drop must be &mut T"),
|
||||
};
|
||||
let fn_ptr = self.memory.create_drop_glue(real_ty);
|
||||
self.memory.write_ptr(vtable, fn_ptr)?;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
29
tests/run-pass/box_box_trait.rs
Normal file
29
tests/run-pass/box_box_trait.rs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#![feature(box_syntax)]
|
||||
|
||||
struct DroppableStruct;
|
||||
|
||||
static mut DROPPED: bool = false;
|
||||
|
||||
impl Drop for DroppableStruct {
|
||||
fn drop(&mut self) {
|
||||
unsafe { DROPPED = true; }
|
||||
}
|
||||
}
|
||||
|
||||
trait MyTrait { fn dummy(&self) { } }
|
||||
impl MyTrait for Box<DroppableStruct> {}
|
||||
|
||||
struct Whatever { w: Box<MyTrait+'static> }
|
||||
impl Whatever {
|
||||
fn new(w: Box<MyTrait+'static>) -> Whatever {
|
||||
Whatever { w: w }
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
{
|
||||
let f: Box<_> = box DroppableStruct;
|
||||
let _a = Whatever::new(box f as Box<MyTrait>);
|
||||
}
|
||||
assert!(unsafe { DROPPED });
|
||||
}
|
||||
|
|
@ -23,8 +23,55 @@ impl Bar for usize {
|
|||
fn get(&self) -> usize { *self }
|
||||
}
|
||||
|
||||
struct Baz<T: ?Sized> {
|
||||
a: T
|
||||
}
|
||||
|
||||
struct HasDrop<T: ?Sized> {
|
||||
ptr: Box<usize>,
|
||||
data: T
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Test that zero-offset works properly
|
||||
let b : Baz<usize> = Baz { a: 7 };
|
||||
assert_eq!(b.a.get(), 7);
|
||||
let b : &Baz<Bar> = &b;
|
||||
assert_eq!(b.a.get(), 7);
|
||||
|
||||
// Test that the field is aligned properly
|
||||
let f : Foo<usize> = Foo { a: 0, b: 11 };
|
||||
assert_eq!(f.b.get(), 11);
|
||||
let ptr1 : *const u8 = &f.b as *const _ as *const u8;
|
||||
|
||||
let f : &Foo<Bar> = &f;
|
||||
let ptr2 : *const u8 = &f.b as *const _ as *const u8;
|
||||
assert_eq!(f.b.get(), 11);
|
||||
|
||||
// The pointers should be the same
|
||||
assert_eq!(ptr1, ptr2);
|
||||
|
||||
// Test that nested DSTs work properly
|
||||
let f : Foo<Foo<usize>> = Foo { a: 0, b: Foo { a: 1, b: 17 }};
|
||||
assert_eq!(f.b.b.get(), 17);
|
||||
let f : &Foo<Foo<Bar>> = &f;
|
||||
assert_eq!(f.b.b.get(), 17);
|
||||
|
||||
// Test that get the pointer via destructuring works
|
||||
|
||||
let f : Foo<usize> = Foo { a: 0, b: 11 };
|
||||
let f : &Foo<Bar> = &f;
|
||||
assert_eq!(f.b.get(), 11);
|
||||
let &Foo { a: _, b: ref bar } = f;
|
||||
assert_eq!(bar.get(), 11);
|
||||
|
||||
// Make sure that drop flags don't screw things up
|
||||
|
||||
let d : HasDrop<Baz<[i32; 4]>> = HasDrop {
|
||||
ptr: Box::new(0),
|
||||
data: Baz { a: [1,2,3,4] }
|
||||
};
|
||||
assert_eq!([1,2,3,4], d.data.a);
|
||||
|
||||
let d : &HasDrop<Baz<[i32]>> = &d;
|
||||
assert_eq!(&[1,2,3,4], &d.data.a);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue