rustc: add a TyLayout helper for type-related layout queries.

This commit is contained in:
Eduard-Mihai Burtescu 2017-02-28 17:50:28 +02:00
parent a61011761d
commit 49872b859e
3 changed files with 237 additions and 13 deletions

View file

@ -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<usize>)
-> 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<usize>,
}
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<Self, LayoutError<'gcx>> {
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<Self, LayoutError<'gcx>> {
TyLayout::of(infcx, self.field_type(infcx.tcx.global_tcx(), i))
}
}

View file

@ -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()),

View file

@ -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,