}
impl Default for TargetDataLayout {
+ /// Creates an instance of `TargetDataLayout`.
fn default() -> TargetDataLayout {
TargetDataLayout {
endian: Endian::Big,
}
impl Integer {
+ pub fn size(&self) -> Size {
+ match *self {
+ I1 => Size::from_bits(1),
+ I8 => Size::from_bytes(1),
+ I16 => Size::from_bytes(2),
+ I32 => Size::from_bytes(4),
+ I64 => Size::from_bytes(8),
+ }
+ }
+
+ pub fn align(&self, dl: &TargetDataLayout)-> Align {
+ match *self {
+ I1 => dl.i1_align,
+ I8 => dl.i8_align,
+ I16 => dl.i16_align,
+ I32 => dl.i32_align,
+ I64 => dl.i64_align,
+ }
+ }
+
+ pub fn to_ty<'a, 'tcx>(&self, tcx: &ty::TyCtxt<'a, 'tcx, 'tcx>,
+ signed: bool) -> Ty<'tcx> {
+ match (*self, signed) {
+ (I1, false) => tcx.types.u8,
+ (I8, false) => tcx.types.u8,
+ (I16, false) => tcx.types.u16,
+ (I32, false) => tcx.types.u32,
+ (I64, false) => tcx.types.u64,
+ (I1, true) => tcx.types.i8,
+ (I8, true) => tcx.types.i8,
+ (I16, true) => tcx.types.i16,
+ (I32, true) => tcx.types.i32,
+ (I64, true) => tcx.types.i64,
+ }
+ }
+
/// Find the smallest Integer type which can represent the signed value.
pub fn fit_signed(x: i64) -> Integer {
match x {
}
}
+ /// Find the smallest integer with the given alignment.
+ pub fn for_abi_align(dl: &TargetDataLayout, align: Align) -> Option<Integer> {
+ let wanted = align.abi();
+ for &candidate in &[I8, I16, I32, I64] {
+ let ty = Int(candidate);
+ if wanted == ty.align(dl).abi() && wanted == ty.size(dl).bytes() {
+ return Some(candidate);
+ }
+ }
+ None
+ }
+
/// Get the Integer type from an attr::IntType.
pub fn from_attr(dl: &TargetDataLayout, ity: attr::IntType) -> Integer {
match ity {
/// signed discriminant range and #[repr] attribute.
/// N.B.: u64 values above i64::MAX will be treated as signed, but
/// that shouldn't affect anything, other than maybe debuginfo.
- pub fn repr_discr(tcx: TyCtxt, hint: attr::ReprAttr, min: i64, max: i64)
+ pub fn repr_discr(tcx: TyCtxt, ty: Ty, hint: attr::ReprAttr, min: i64, max: i64)
-> (Integer, bool) {
// Theoretically, negative values could be larger in unsigned representation
// than the unsigned representation of the signed minimum. However, if there
let signed_fit = cmp::max(Integer::fit_signed(min), Integer::fit_signed(max));
let at_least = match hint {
- attr::ReprInt(span, ity) => {
+ attr::ReprInt(ity) => {
let discr = Integer::from_attr(&tcx.data_layout, ity);
let fit = if ity.is_signed() { signed_fit } else { unsigned_fit };
if discr < fit {
- span_bug!(span, "representation hint insufficient for discriminant range")
+ bug!("Integer::repr_discr: `#[repr]` hint too small for \
+ discriminant range of enum `{}", ty)
}
return (discr, ity.is_signed());
}
}
attr::ReprAny => I8,
attr::ReprPacked => {
- bug!("Integer::repr_discr: found #[repr(packed)] on an enum");
+ bug!("Integer::repr_discr: found #[repr(packed)] on enum `{}", ty);
}
attr::ReprSimd => {
- bug!("Integer::repr_discr: found #[repr(simd)] on an enum");
+ bug!("Integer::repr_discr: found #[repr(simd)] on enum `{}", ty);
}
};
for field in fields {
if !self.sized {
- bug!("Struct::compute: field #{} of `{}` comes after unsized field",
+ bug!("Struct::extend: field #{} of `{}` comes after unsized field",
self.offset_after_field.len(), scapegoat);
}
}
// Is this the NonZero lang item wrapping a pointer or integer type?
- (&Univariant { non_zero: true, .. }, &ty::TyStruct(def, substs)) => {
+ (&Univariant { non_zero: true, .. }, &ty::TyAdt(def, substs)) => {
let fields = &def.struct_variant().fields;
assert_eq!(fields.len(), 1);
match *fields[0].ty(tcx, substs).layout(infcx)? {
// Perhaps one of the fields of this struct is non-zero
// let's recurse and find out
- (_, &ty::TyStruct(def, substs)) => {
+ (_, &ty::TyAdt(def, substs)) if def.is_struct() => {
Struct::non_zero_field_path(infcx, def.struct_variant().fields
.iter().map(|field| {
field.ty(tcx, substs)
}
Ok(None)
}
+
+ pub fn offset_of_field(&self, index: usize) -> Size {
+ assert!(index < self.offset_after_field.len());
+ if index == 0 {
+ Size::from_bytes(0)
+ } else {
+ self.offset_after_field[index-1]
+ }
+ }
+}
+
+/// An untagged union.
+#[derive(PartialEq, Eq, Hash, Debug)]
+pub struct Union {
+ pub align: Align,
+
+ pub min_size: Size,
+
+ /// If true, no alignment padding is used.
+ pub packed: bool,
+}
+
+impl<'a, 'gcx, 'tcx> Union {
+ pub fn new(dl: &TargetDataLayout, packed: bool) -> Union {
+ Union {
+ align: if packed { dl.i8_align } else { dl.aggregate_align },
+ min_size: Size::from_bytes(0),
+ packed: packed,
+ }
+ }
+
+ /// Extend the Struct with more fields.
+ pub fn extend<I>(&mut self, dl: &TargetDataLayout,
+ fields: I,
+ scapegoat: Ty<'gcx>)
+ -> Result<(), LayoutError<'gcx>>
+ where I: Iterator<Item=Result<&'a Layout, LayoutError<'gcx>>> {
+ for (index, field) in fields.enumerate() {
+ let field = field?;
+ if field.is_unsized() {
+ bug!("Union::extend: field #{} of `{}` is unsized",
+ index, scapegoat);
+ }
+
+ if !self.packed {
+ self.align = self.align.max(field.align(dl));
+ }
+ self.min_size = cmp::max(self.min_size, field.size(dl));
+ }
+
+ Ok(())
+ }
+
+ /// Get the size with trailing aligment padding.
+ pub fn stride(&self) -> Size {
+ self.min_size.abi_align(self.align)
+ }
}
/// The first half of a fat pointer.
non_zero: bool
},
- /// SIMD vectors, from TyStruct marked with #[repr(simd)].
+ /// SIMD vectors, from structs marked with #[repr(simd)].
Vector {
element: Primitive,
count: u64
non_zero: bool
},
- // Remaining variants are all ADTs such as TyStruct, TyEnum or TyTuple.
+ // Remaining variants are all ADTs such as structs, enums or tuples.
/// C-like enums; basically an integer.
CEnum {
non_zero: bool
},
+ /// Untagged unions.
+ UntaggedUnion {
+ variants: Union,
+ },
+
/// General-case enums: for each case there is a struct, and they
/// all start with a field for the discriminant.
General {
Univariant { variant: unit, non_zero: false }
}
- // Tuples.
+ // Tuples and closures.
ty::TyClosure(_, ty::ClosureSubsts { upvar_tys: tys, .. }) |
ty::TyTuple(tys) => {
let mut st = Struct::new(dl, false);
Univariant { variant: st, non_zero: false }
}
- // ADTs.
- ty::TyStruct(def, substs) => {
- if ty.is_simd() {
- // SIMD vector types.
- let element = ty.simd_type(tcx);
- match *element.layout(infcx)? {
- Scalar { value, .. } => {
- return success(Vector {
- element: value,
- count: ty.simd_size(tcx) as u64
- });
- }
- _ => {
- tcx.sess.fatal(&format!("monomorphising SIMD type `{}` with \
- a non-machine element type `{}`",
- ty, element));
- }
+ // SIMD vector types.
+ ty::TyAdt(def, ..) if def.is_simd() => {
+ let element = ty.simd_type(tcx);
+ match *element.layout(infcx)? {
+ Scalar { value, .. } => {
+ return success(Vector {
+ element: value,
+ count: ty.simd_size(tcx) as u64
+ });
+ }
+ _ => {
+ tcx.sess.fatal(&format!("monomorphising SIMD type `{}` with \
+ a non-machine element type `{}`",
+ ty, element));
}
- }
- let fields = def.struct_variant().fields.iter().map(|field| {
- field.ty(tcx, substs).layout(infcx)
- });
- let packed = tcx.lookup_packed(def.did);
- let mut st = Struct::new(dl, packed);
- st.extend(dl, fields, ty)?;
-
- // FIXME(16758) don't add a drop flag to unsized structs, as it
- // won't actually be in the location we say it is because it'll be after
- // the unsized field. Several other pieces of code assume that the unsized
- // field is definitely the last one.
- if def.dtor_kind().has_drop_flag() &&
- ty.is_sized(tcx, &infcx.parameter_environment, DUMMY_SP) {
- st.extend(dl, Some(Ok(&Scalar {
- value: Int(I8),
- non_zero: false
- })).into_iter(), ty)?;
- }
- Univariant {
- variant: st,
- non_zero: Some(def.did) == tcx.lang_items.non_zero()
}
}
- ty::TyEnum(def, substs) => {
+
+ // ADTs.
+ ty::TyAdt(def, substs) => {
let hint = *tcx.lookup_repr_hints(def.did).get(0)
.unwrap_or(&attr::ReprAny);
- let dtor = def.dtor_kind().has_drop_flag();
- let drop_flag = if dtor {
- Some(Scalar { value: Int(I8), non_zero: false })
- } else {
- None
- };
-
if def.variants.is_empty() {
// Uninhabitable; represent as unit
// (Typechecking will reject discriminant-sizing attrs.)
assert_eq!(hint, attr::ReprAny);
- let mut st = Struct::new(dl, false);
- st.extend(dl, drop_flag.iter().map(Ok), ty)?;
- return success(Univariant { variant: st, non_zero: false });
+ return success(Univariant {
+ variant: Struct::new(dl, false),
+ non_zero: false
+ });
}
- if !dtor && def.variants.iter().all(|v| v.fields.is_empty()) {
+ if def.is_enum() && def.variants.iter().all(|v| v.fields.is_empty()) {
// All bodies empty -> intlike
let (mut min, mut max) = (i64::MAX, i64::MIN);
for v in &def.variants {
if x > max { max = x; }
}
- let (discr, signed) = Integer::repr_discr(tcx, hint, min, max);
+ let (discr, signed) = Integer::repr_discr(tcx, ty, hint, min, max);
return success(CEnum {
discr: discr,
signed: signed,
});
}
+ if !def.is_enum() || def.variants.len() == 1 && hint == attr::ReprAny {
+ // Struct, or union, or univariant enum equivalent to a struct.
+ // (Typechecking will reject discriminant-sizing attrs.)
+
+ let fields = def.variants[0].fields.iter().map(|field| {
+ field.ty(tcx, substs).layout(infcx)
+ });
+ let packed = tcx.lookup_packed(def.did);
+ let layout = if def.is_union() {
+ let mut un = Union::new(dl, packed);
+ un.extend(dl, fields, ty)?;
+ UntaggedUnion { variants: un }
+ } else {
+ let mut st = Struct::new(dl, packed);
+ st.extend(dl, fields, ty)?;
+ let non_zero = Some(def.did) == tcx.lang_items.non_zero();
+ Univariant { variant: st, non_zero: non_zero }
+ };
+ return success(layout);
+ }
+
// Since there's at least one
// non-empty body, explicit discriminants should have
// been rejected by a checker before this point.
for (i, v) in def.variants.iter().enumerate() {
if i as u64 != v.disr_val.to_u64_unchecked() {
bug!("non-C-like enum {} with specified discriminants",
- tcx.item_path_str(def.did));
+ tcx.item_path_str(def.did));
}
}
- if def.variants.len() == 1 {
- // Equivalent to a struct/tuple/newtype.
- // (Typechecking will reject discriminant-sizing attrs.)
- assert_eq!(hint, attr::ReprAny);
- let fields = def.variants[0].fields.iter().map(|field| {
- field.ty(tcx, substs).layout(infcx)
- });
- let mut st = Struct::new(dl, false);
- st.extend(dl, fields.chain(drop_flag.iter().map(Ok)), ty)?;
- return success(Univariant { variant: st, non_zero: false });
- }
-
// Cache the substituted and normalized variant field types.
let variants = def.variants.iter().map(|v| {
v.fields.iter().map(|field| field.ty(tcx, substs)).collect::<Vec<_>>()
}).collect::<Vec<_>>();
- if !dtor && variants.len() == 2 && hint == attr::ReprAny {
+ if variants.len() == 2 && hint == attr::ReprAny {
// Nullable pointer optimization
for discr in 0..2 {
let other_fields = variants[1 - discr].iter().map(|ty| {
}
_ => {
bug!("Layout::compute: `{}`'s non-zero \
- `{}` field not scalar?!",
- ty, variants[discr][0])
+ `{}` field not scalar?!",
+ ty, variants[discr][0])
}
}
}
// The general case.
let discr_max = (variants.len() - 1) as i64;
assert!(discr_max >= 0);
- let (min_ity, _) = Integer::repr_discr(tcx, hint, 0, discr_max);
+ let (min_ity, _) = Integer::repr_discr(tcx, ty, hint, 0, discr_max);
let mut align = dl.aggregate_align;
let mut size = Size::from_bytes(0);
Ok(field)
});
let mut st = Struct::new(dl, false);
- st.extend(dl, discr.iter().map(Ok).chain(fields)
- .chain(drop_flag.iter().map(Ok)), ty)?;
+ st.extend(dl, discr.iter().map(Ok).chain(fields), ty)?;
size = cmp::max(size, st.min_size());
align = align.max(st.align);
Ok(st)
// won't be so conservative.
// Use the initial field alignment
- let wanted = start_align.abi();
- let mut ity = min_ity;
- for &candidate in &[I16, I32, I64] {
- let ty = Int(candidate);
- if wanted == ty.align(dl).abi() && wanted == ty.size(dl).bytes() {
- ity = candidate;
- break;
- }
- }
-
- // FIXME(eddyb) conservative only to avoid diverging from trans::adt.
- if align.abi() != start_align.abi() {
- ity = min_ity;
- }
+ let mut ity = Integer::for_abi_align(dl, start_align).unwrap_or(min_ity);
// If the alignment is not larger than the chosen discriminant size,
// don't use the alignment as the final size.
pub fn is_unsized(&self) -> bool {
match *self {
Scalar {..} | Vector {..} | FatPointer {..} |
- CEnum {..} | General {..} |
+ CEnum {..} | UntaggedUnion {..} | General {..} |
RawNullablePointer {..} |
StructWrappedNullablePointer {..} => false,
CEnum { discr, .. } => Int(discr).size(dl),
Array { size, .. } | General { size, .. } => size,
+ UntaggedUnion { ref variants } => variants.stride(),
Univariant { ref variant, .. } |
StructWrappedNullablePointer { nonnull: ref variant, .. } => {
CEnum { discr, .. } => Int(discr).align(dl),
Array { align, .. } | General { align, .. } => align,
+ UntaggedUnion { ref variants } => variants.align,
Univariant { ref variant, .. } |
StructWrappedNullablePointer { nonnull: ref variant, .. } => {
}
}
- ty::TyStruct(def, substs) | ty::TyEnum(def, substs) => {
+ ty::TyAdt(def, substs) => {
// Only newtypes and enums w/ nullable pointer optimization.
- if def.variants.is_empty() || def.variants.len() > 2 {
- return Err(err);
- }
-
- // If there's a drop flag, it can't be just a pointer.
- if def.dtor_kind().has_drop_flag() {
+ if def.is_union() || def.variants.is_empty() || def.variants.len() > 2 {
return Err(err);
}