From: Eduard-Mihai Burtescu Date: Tue, 28 Feb 2017 15:50:28 +0000 (+0200) Subject: rustc: add a TyLayout helper for type-related layout queries. X-Git-Url: https://git.lizzy.rs/?a=commitdiff_plain;h=49872b859ec71e53266a717f4ec865c88e2bdefe;p=rust.git rustc: add a TyLayout helper for type-related layout queries. --- diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index 571ef30b6b9..8d68eff9251 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -25,6 +25,7 @@ use std::fmt; use std::i64; use std::iter; +use std::ops::Deref; /// Parsed [Data layout](http://llvm.org/docs/LangRef.html#data-layout) /// for a target, which contains everything needed to compute layouts. @@ -904,7 +905,8 @@ pub enum Layout { /// If true, the size is exact, otherwise it's only a lower bound. sized: bool, align: Align, - size: Size + element_size: Size, + count: u64 }, /// TyRawPtr or TyRef with a !Sized pointee. @@ -1087,25 +1089,35 @@ pub fn compute_uncached(ty: Ty<'gcx>, // Arrays and slices. ty::TyArray(element, count) => { let element = element.layout(infcx)?; + let element_size = element.size(dl); + // FIXME(eddyb) Don't use host `usize` for array lengths. + let usize_count: usize = count; + let count = usize_count as u64; + if element_size.checked_mul(count, dl).is_none() { + return Err(LayoutError::SizeOverflow(ty)); + } Array { sized: true, align: element.align(dl), - size: element.size(dl).checked_mul(count as u64, dl) - .map_or(Err(LayoutError::SizeOverflow(ty)), Ok)? + element_size: element_size, + count: count } } ty::TySlice(element) => { + let element = element.layout(infcx)?; Array { sized: false, - align: element.layout(infcx)?.align(dl), - size: Size::from_bytes(0) + align: element.align(dl), + element_size: element.size(dl), + count: 0 } } ty::TyStr => { Array { sized: false, align: dl.i8_align, - size: Size::from_bytes(0) + element_size: Size::from_bytes(1), + count: 0 } } @@ -1447,15 +1459,23 @@ pub fn size(&self, dl: &TargetDataLayout) -> Size { } Vector { element, count } => { - let elem_size = element.size(dl); - let vec_size = match elem_size.checked_mul(count, dl) { + let element_size = element.size(dl); + let vec_size = match element_size.checked_mul(count, dl) { Some(size) => size, None => bug!("Layout::size({:?}): {} * {} overflowed", - self, elem_size.bytes(), count) + self, element_size.bytes(), count) }; vec_size.abi_align(self.align(dl)) } + Array { element_size, count, .. } => { + match element_size.checked_mul(count, dl) { + Some(size) => size, + None => bug!("Layout::size({:?}): {} * {} overflowed", + self, element_size.bytes(), count) + } + } + FatPointer { metadata, .. } => { // Effectively a (ptr, meta) tuple. Pointer.size(dl).abi_align(metadata.align(dl)) @@ -1464,7 +1484,7 @@ pub fn size(&self, dl: &TargetDataLayout) -> Size { } CEnum { discr, .. } => Int(discr).size(dl), - Array { size, .. } | General { size, .. } => size, + General { size, .. } => size, UntaggedUnion { ref variants } => variants.stride(), Univariant { ref variant, .. } | @@ -1513,6 +1533,59 @@ pub fn align(&self, dl: &TargetDataLayout) -> Align { } } } + + pub fn field_offset(&self, + dl: &TargetDataLayout, + i: usize, + variant_index: Option) + -> Size { + match *self { + Scalar { .. } | + CEnum { .. } | + UntaggedUnion { .. } | + RawNullablePointer { .. } => { + Size::from_bytes(0) + } + + Vector { element, count } => { + let element_size = element.size(dl); + let i = i as u64; + assert!(i < count); + Size::from_bytes(element_size.bytes() * count) + } + + Array { element_size, count, .. } => { + let i = i as u64; + assert!(i < count); + Size::from_bytes(element_size.bytes() * count) + } + + FatPointer { metadata, .. } => { + // Effectively a (ptr, meta) tuple. + assert!(i < 2); + if i == 0 { + Size::from_bytes(0) + } else { + Pointer.size(dl).abi_align(metadata.align(dl)) + } + } + + Univariant { ref variant, .. } => variant.offsets[i], + + General { ref variants, .. } => { + let v = variant_index.expect("variant index required"); + variants[v].offsets[i + 1] + } + + StructWrappedNullablePointer { nndiscr, ref nonnull, .. } => { + if Some(nndiscr as usize) == variant_index { + nonnull.offsets[i] + } else { + Size::from_bytes(0) + } + } + } + } } /// Type size "skeleton", i.e. the only information determining a type's size. @@ -1658,3 +1731,154 @@ pub fn same_size(self, other: SizeSkeleton) -> bool { } } } + +/// A pair of a type and its layout. Implements various +/// type traversal APIs (e.g. recursing into fields). +#[derive(Copy, Clone, Debug)] +pub struct TyLayout<'tcx> { + pub ty: Ty<'tcx>, + pub layout: &'tcx Layout, + pub variant_index: Option, +} + +impl<'tcx> Deref for TyLayout<'tcx> { + type Target = Layout; + fn deref(&self) -> &Layout { + self.layout + } +} + +impl<'a, 'gcx, 'tcx> TyLayout<'gcx> { + pub fn of(infcx: &InferCtxt<'a, 'gcx, 'tcx>, ty: Ty<'gcx>) + -> Result> { + let ty = normalize_associated_type(infcx, ty); + + Ok(TyLayout { + ty: ty, + layout: ty.layout(infcx)?, + variant_index: None + }) + } + + pub fn for_variant(&self, variant_index: usize) -> Self { + TyLayout { + variant_index: Some(variant_index), + ..*self + } + } + + pub fn field_offset(&self, dl: &TargetDataLayout, i: usize) -> Size { + self.layout.field_offset(dl, i, self.variant_index) + } + + pub fn field_count(&self) -> usize { + // Handle enum/union through the type rather than Layout. + if let ty::TyAdt(def, _) = self.ty.sty { + let v = self.variant_index.unwrap_or(0); + if def.variants.is_empty() { + assert_eq!(v, 0); + return 0; + } else { + return def.variants[v].fields.len(); + } + } + + match *self.layout { + Scalar { .. } => { + bug!("TyLayout::field_count({:?}): not applicable", self) + } + + // Handled above (the TyAdt case). + CEnum { .. } | + General { .. } | + UntaggedUnion { .. } | + RawNullablePointer { .. } | + StructWrappedNullablePointer { .. } => bug!(), + + FatPointer { .. } => 2, + + Vector { count, .. } | + Array { count, .. } => { + let usize_count = count as usize; + assert_eq!(usize_count as u64, count); + usize_count + } + + Univariant { ref variant, .. } => variant.offsets.len(), + } + } + + pub fn field_type(&self, tcx: TyCtxt<'a, 'gcx, 'gcx>, i: usize) -> Ty<'gcx> { + let ptr_field_type = |pointee: Ty<'gcx>| { + let slice = |element: Ty<'gcx>| { + assert!(i < 2); + if i == 0 { + tcx.mk_mut_ptr(element) + } else { + tcx.types.usize + } + }; + match tcx.struct_tail(pointee).sty { + ty::TySlice(element) => slice(element), + ty::TyStr => slice(tcx.types.u8), + ty::TyDynamic(..) => tcx.mk_mut_ptr(tcx.mk_nil()), + _ => bug!("TyLayout::field_type({:?}): not applicable", self) + } + }; + + match self.ty.sty { + ty::TyBool | + ty::TyChar | + ty::TyInt(_) | + ty::TyUint(_) | + ty::TyFloat(_) | + ty::TyFnPtr(_) | + ty::TyNever | + ty::TyFnDef(..) | + ty::TyDynamic(..) => { + bug!("TyLayout::field_type({:?}): not applicable", self) + } + + // Potentially-fat pointers. + ty::TyRef(_, ty::TypeAndMut { ty: pointee, .. }) | + ty::TyRawPtr(ty::TypeAndMut { ty: pointee, .. }) => { + ptr_field_type(pointee) + } + ty::TyAdt(def, _) if def.is_box() => { + ptr_field_type(self.ty.boxed_ty()) + } + + // Arrays and slices. + ty::TyArray(element, _) | + ty::TySlice(element) => element, + ty::TyStr => tcx.types.u8, + + // Tuples and closures. + ty::TyClosure(def_id, ref substs) => { + substs.upvar_tys(def_id, tcx).nth(i).unwrap() + } + + ty::TyTuple(tys, _) => tys[i], + + // SIMD vector types. + ty::TyAdt(def, ..) if def.repr.simd => { + self.ty.simd_type(tcx) + } + + // ADTs. + ty::TyAdt(def, substs) => { + def.variants[self.variant_index.unwrap_or(0)].fields[i].ty(tcx, substs) + } + + ty::TyProjection(_) | ty::TyAnon(..) | ty::TyParam(_) | + ty::TyInfer(_) | ty::TyError => { + bug!("TyLayout::field_type: unexpected type `{}`", self.ty) + } + } + } + + pub fn field(&self, infcx: &InferCtxt<'a, 'gcx, 'tcx>, i: usize) + -> Result> { + TyLayout::of(infcx, self.field_type(infcx.tcx.global_tcx(), i)) + } +} diff --git a/src/librustc_trans/context.rs b/src/librustc_trans/context.rs index afb94f546ab..3649216eaa8 100644 --- a/src/librustc_trans/context.rs +++ b/src/librustc_trans/context.rs @@ -828,9 +828,9 @@ pub fn enter_type_of(&self, ty: Ty<'tcx>) -> TypeOfDepthLock<'b, 'tcx> { TypeOfDepthLock(self.local()) } - pub fn layout_of(&self, ty: Ty<'tcx>) -> &'tcx ty::layout::Layout { + pub fn layout_of(&self, ty: Ty<'tcx>) -> ty::layout::TyLayout<'tcx> { self.tcx().infer_ctxt((), traits::Reveal::All).enter(|infcx| { - ty.layout(&infcx).unwrap_or_else(|e| { + ty::layout::TyLayout::of(&infcx, ty).unwrap_or_else(|e| { match e { ty::layout::LayoutError::SizeOverflow(_) => self.sess().fatal(&e.to_string()), diff --git a/src/librustc_trans/debuginfo/metadata.rs b/src/librustc_trans/debuginfo/metadata.rs index 049178a2575..93798e7bd33 100644 --- a/src/librustc_trans/debuginfo/metadata.rs +++ b/src/librustc_trans/debuginfo/metadata.rs @@ -1564,7 +1564,7 @@ fn prepare_enum_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, enum_llvm_type, EnumMDF(EnumMemberDescriptionFactory { enum_type: enum_type, - type_rep: type_rep, + type_rep: type_rep.layout, discriminant_type_metadata: discriminant_type_metadata, containing_scope: containing_scope, file_metadata: file_metadata,