diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index 88f9af75f95..2b2e1f30c88 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -7,7 +7,7 @@ use rustc::middle::const_val::ConstVal; use rustc::middle::region::CodeExtent; use rustc::mir; use rustc::traits::Reveal; -use rustc::ty::layout::{self, Layout, Size}; +use rustc::ty::layout::{self, Layout, Size, Align}; use rustc::ty::subst::{Subst, Substs, Kind}; use rustc::ty::{self, Ty, TyCtxt, TypeFoldable, Binder}; use rustc::traits; @@ -277,6 +277,98 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { self.tcx.erase_regions(&value) } + pub fn size_and_align_of_dst( + &mut self, + ty: ty::Ty<'tcx>, + value: Value, + ) -> EvalResult<'tcx, (u64, u64)> { + if let Some(size) = self.type_size(ty)? { + Ok((size as u64, self.type_align(ty)? as u64)) + } else { + match ty.sty { + ty::TyAdt(def, substs) => { + // First get the size of all statically known fields. + // Don't use type_of::sizing_type_of because that expects t to be sized, + // and it also rounds up to alignment, which we want to avoid, + // as the unsized field's alignment could be smaller. + assert!(!ty.is_simd()); + let layout = self.type_layout(ty)?; + debug!("DST {} layout: {:?}", ty, layout); + + let (sized_size, sized_align) = match *layout { + ty::layout::Layout::Univariant { ref variant, .. } => { + (variant.offsets.last().map_or(0, |o| o.bytes()), variant.align) + } + _ => { + bug!("size_and_align_of_dst: expcted Univariant for `{}`, found {:#?}", + ty, layout); + } + }; + debug!("DST {} statically sized prefix size: {} align: {:?}", + ty, sized_size, sized_align); + + // Recurse to get the size of the dynamically sized field (must be + // the last field). + let last_field = def.struct_variant().fields.last().unwrap(); + let field_ty = self.field_ty(substs, last_field); + let (unsized_size, unsized_align) = self.size_and_align_of_dst(field_ty, value)?; + + // FIXME (#26403, #27023): We should be adding padding + // to `sized_size` (to accommodate the `unsized_align` + // required of the unsized field that follows) before + // summing it with `sized_size`. (Note that since #26403 + // is unfixed, we do not yet add the necessary padding + // here. But this is where the add would go.) + + // Return the sum of sizes and max of aligns. + let size = sized_size + unsized_size; + + // Choose max of two known alignments (combined value must + // be aligned according to more restrictive of the two). + let align = sized_align.max(Align::from_bytes(unsized_align, unsized_align).unwrap()); + + // Issue #27023: must add any necessary padding to `size` + // (to make it a multiple of `align`) before returning it. + // + // Namely, the returned size should be, in C notation: + // + // `size + ((size & (align-1)) ? align : 0)` + // + // emulated via the semi-standard fast bit trick: + // + // `(size + (align-1)) & -align` + + let size = Size::from_bytes(size).abi_align(align).bytes(); + Ok((size, align.abi())) + } + ty::TyDynamic(..) => { + let (_, vtable) = value.into_ptr_vtable_pair(&mut self.memory)?; + // the second entry in the vtable is the dynamic size of the object. + self.read_size_and_align_from_vtable(vtable) + } + + ty::TySlice(_) | ty::TyStr => { + let elem_ty = ty.sequence_element_type(self.tcx); + let elem_size = self.type_size(elem_ty)?.expect("slice element must be sized") as u64; + let (_, len) = value.into_slice(&mut self.memory)?; + let align = self.type_align(elem_ty)?; + Ok((len * elem_size, align as u64)) + } + + _ => bug!("size_of_val::<{:?}>", ty), + } + } + } + + /// Returns the normalized type of a struct field + fn field_ty( + &self, + param_substs: &Substs<'tcx>, + f: &ty::FieldDef, + ) -> ty::Ty<'tcx> { + self.tcx.normalize_associated_type(&f.ty(self.tcx, param_substs)) + } + pub(super) fn type_size(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, Option> { self.type_size_with_substs(ty, self.substs()) } @@ -1556,8 +1648,8 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { let dest = self.force_allocation(dest)?.to_ptr()?; let iter = src_fields.zip(dst_fields).enumerate(); for (i, (src_f, dst_f)) in iter { - let src_fty = monomorphize_field_ty(self.tcx, src_f, substs_a); - let dst_fty = monomorphize_field_ty(self.tcx, dst_f, substs_b); + let src_fty = self.field_ty(substs_a, src_f); + let dst_fty = self.field_ty(substs_b, dst_f); if self.type_size(dst_fty)? == Some(0) { continue; } @@ -1726,12 +1818,6 @@ impl IntegerExt for layout::Integer { } } - -pub fn monomorphize_field_ty<'a, 'tcx:'a >(tcx: TyCtxt<'a, 'tcx, 'tcx>, f: &ty::FieldDef, substs: &'tcx Substs<'tcx>) -> Ty<'tcx> { - let substituted = f.ty(tcx, substs); - tcx.normalize_associated_type(&substituted) -} - pub fn is_inhabited<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> bool { ty.uninhabited_from(&mut HashMap::default(), tcx).is_empty() } diff --git a/src/librustc_mir/interpret/terminator/intrinsic.rs b/src/librustc_mir/interpret/terminator/intrinsic.rs index 11638705104..7c81b76ba41 100644 --- a/src/librustc_mir/interpret/terminator/intrinsic.rs +++ b/src/librustc_mir/interpret/terminator/intrinsic.rs @@ -1,7 +1,6 @@ use rustc::mir; use rustc::traits::Reveal; -use rustc::ty::layout::{Layout, Size, Align}; -use rustc::ty::subst::Substs; +use rustc::ty::layout::Layout; use rustc::ty::{self, Ty}; use interpret::{ @@ -487,97 +486,6 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { // current frame. Ok(()) } - - pub fn size_and_align_of_dst( - &mut self, - ty: ty::Ty<'tcx>, - value: Value, - ) -> EvalResult<'tcx, (u64, u64)> { - if let Some(size) = self.type_size(ty)? { - Ok((size as u64, self.type_align(ty)? as u64)) - } else { - match ty.sty { - ty::TyAdt(def, substs) => { - // First get the size of all statically known fields. - // Don't use type_of::sizing_type_of because that expects t to be sized, - // and it also rounds up to alignment, which we want to avoid, - // as the unsized field's alignment could be smaller. - assert!(!ty.is_simd()); - let layout = self.type_layout(ty)?; - debug!("DST {} layout: {:?}", ty, layout); - - let (sized_size, sized_align) = match *layout { - ty::layout::Layout::Univariant { ref variant, .. } => { - (variant.offsets.last().map_or(0, |o| o.bytes()), variant.align) - } - _ => { - bug!("size_and_align_of_dst: expcted Univariant for `{}`, found {:#?}", - ty, layout); - } - }; - debug!("DST {} statically sized prefix size: {} align: {:?}", - ty, sized_size, sized_align); - - // Recurse to get the size of the dynamically sized field (must be - // the last field). - let last_field = def.struct_variant().fields.last().unwrap(); - let field_ty = self.field_ty(substs, last_field); - let (unsized_size, unsized_align) = self.size_and_align_of_dst(field_ty, value)?; - - // FIXME (#26403, #27023): We should be adding padding - // to `sized_size` (to accommodate the `unsized_align` - // required of the unsized field that follows) before - // summing it with `sized_size`. (Note that since #26403 - // is unfixed, we do not yet add the necessary padding - // here. But this is where the add would go.) - - // Return the sum of sizes and max of aligns. - let size = sized_size + unsized_size; - - // Choose max of two known alignments (combined value must - // be aligned according to more restrictive of the two). - let align = sized_align.max(Align::from_bytes(unsized_align, unsized_align).unwrap()); - - // Issue #27023: must add any necessary padding to `size` - // (to make it a multiple of `align`) before returning it. - // - // Namely, the returned size should be, in C notation: - // - // `size + ((size & (align-1)) ? align : 0)` - // - // emulated via the semi-standard fast bit trick: - // - // `(size + (align-1)) & -align` - - let size = Size::from_bytes(size).abi_align(align).bytes(); - Ok((size, align.abi())) - } - ty::TyDynamic(..) => { - let (_, vtable) = value.into_ptr_vtable_pair(&self.memory)?; - // the second entry in the vtable is the dynamic size of the object. - self.read_size_and_align_from_vtable(vtable) - } - - ty::TySlice(_) | ty::TyStr => { - let elem_ty = ty.sequence_element_type(self.tcx); - let elem_size = self.type_size(elem_ty)?.expect("slice element must be sized") as u64; - let (_, len) = value.into_slice(&self.memory)?; - let align = self.type_align(elem_ty)?; - Ok((len * elem_size, align as u64)) - } - - _ => bug!("size_of_val::<{:?}>", ty), - } - } - } - /// Returns the normalized type of a struct field - fn field_ty( - &self, - param_substs: &Substs<'tcx>, - f: &ty::FieldDef, - ) -> ty::Ty<'tcx> { - self.tcx.normalize_associated_type(&f.ty(self.tcx, param_substs)) - } } fn numeric_intrinsic<'tcx>(