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);
}
}
}
+/// 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.
/// - For a trait object, this is the address of the box.
/// - For a slice, this is the base address.
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 {
non_zero: Some(def.did) == tcx.lang_items.non_zero()
}
}
- ty::TyUnion(..) => {
- unimplemented_unions!();
+ ty::TyUnion(def, substs) => {
+ let fields = def.struct_variant().fields.iter().map(|field| {
+ field.ty(tcx, substs).layout(infcx)
+ });
+ let packed = tcx.lookup_packed(def.did);
+ let mut un = Union::new(dl, packed);
+ un.extend(dl, fields, ty)?;
+ UntaggedUnion { variants: un }
}
ty::TyEnum(def, substs) => {
let hint = *tcx.lookup_repr_hints(def.did).get(0)
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::TyUnion(..) => {
- unimplemented_unions!();
- }
ty::TyStruct(def, substs) | ty::TyEnum(def, substs) => {
// Only newtypes and enums w/ nullable pointer optimization.
if def.variants.is_empty() || def.variants.len() > 2 {
CEnum(IntType, Disr, Disr), // discriminant range (signedness based on the IntType)
/// Single-case variants, and structs/tuples/records.
Univariant(Struct<'tcx>),
+ /// Untagged unions.
+ UntaggedUnion(Union<'tcx>),
/// General-case enums: for each case there is a struct, and they
/// all start with a field for the discriminant.
General(IntType, Vec<Struct<'tcx>>),
pub fields: Vec<Ty<'tcx>>,
}
+/// For untagged unions.
+#[derive(Eq, PartialEq, Debug)]
+pub struct Union<'tcx> {
+ pub min_size: u64,
+ pub align: u32,
+ pub packed: bool,
+ pub fields: Vec<Ty<'tcx>>,
+}
+
#[derive(Copy, Clone)]
pub struct MaybeSizedValue {
pub value: ValueRef,
Univariant(mk_struct(cx, &ftys[..], packed, t))
}
- ty::TyUnion(..) => {
- unimplemented_unions!();
+ ty::TyUnion(def, substs) => {
+ let ftys = def.struct_variant().fields.iter().map(|field| {
+ monomorphize::field_ty(cx.tcx(), substs, field)
+ }).collect::<Vec<_>>();
+ let packed = cx.tcx().lookup_packed(def.did);
+ UntaggedUnion(mk_union(cx, &ftys[..], packed, t))
}
ty::TyClosure(_, ref substs) => {
Univariant(mk_struct(cx, &substs.upvar_tys, false, t))
}
}
+fn mk_union<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
+ tys: &[Ty<'tcx>], packed: bool,
+ _scapegoat: Ty<'tcx>)
+ -> Union<'tcx> {
+ let mut min_size = 0;
+ let mut align = 0;
+ for llty in tys.iter().map(|&ty| type_of::sizing_type_of(cx, ty)) {
+ let field_size = machine::llsize_of_alloc(cx, llty);
+ if min_size < field_size {
+ min_size = field_size;
+ }
+ let field_align = machine::llalign_of_min(cx, llty);
+ if align < field_align {
+ align = field_align;
+ }
+ }
+
+ Union {
+ min_size: min_size,
+ align: align,
+ packed: packed,
+ fields: tys.to_vec(),
+ }
+}
+
#[derive(Debug)]
struct IntBounds {
slo: i64,
pub fn finish_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
r: &Repr<'tcx>, llty: &mut Type) {
match *r {
- CEnum(..) | General(..) | RawNullablePointer { .. } => { }
+ CEnum(..) | General(..) | UntaggedUnion(..) | RawNullablePointer { .. } => { }
Univariant(ref st) | StructWrappedNullablePointer { nonnull: ref st, .. } =>
llty.set_struct_body(&struct_llfields(cx, st, false, false),
st.packed)
}
}
}
+ UntaggedUnion(ref un) => {
+ // Use alignment-sized ints to fill all the union storage.
+ let (size, align) = (roundup(un.min_size, un.align), un.align);
+
+ let align_s = align as u64;
+ assert_eq!(size % align_s, 0); // Ensure division in align_units comes out evenly
+ let align_units = size / align_s;
+ let fill_ty = match align_s {
+ 1 => Type::array(&Type::i8(cx), align_units),
+ 2 => Type::array(&Type::i16(cx), align_units),
+ 4 => Type::array(&Type::i32(cx), align_units),
+ 8 if machine::llalign_of_min(cx, Type::i64(cx)) == 8 =>
+ Type::array(&Type::i64(cx), align_units),
+ a if a.count_ones() == 1 => Type::array(&Type::vector(&Type::i32(cx), a / 4),
+ align_units),
+ _ => bug!("unsupported union alignment: {}", align)
+ };
+ match name {
+ None => {
+ TypeContext::direct(Type::struct_(cx, &[fill_ty], un.packed))
+ }
+ Some(name) => {
+ let mut llty = Type::named_struct(cx, name);
+ llty.set_struct_body(&[fill_ty], un.packed);
+ TypeContext::direct(llty)
+ }
+ }
+ }
General(ity, ref sts) => {
// We need a representation that has:
// * The alignment of the most-aligned field
RawNullablePointer { .. } | StructWrappedNullablePointer { .. } => {
(BranchKind::Switch, Some(trans_get_discr(bcx, r, scrutinee, None, range_assert)))
}
- Univariant(..) => {
+ Univariant(..) | UntaggedUnion(..) => {
// N.B.: Univariant means <= 1 enum variants (*not* == 1 variants).
(BranchKind::Single, None)
}
match *r {
CEnum(ity, _, _) => ity.is_signed(),
General(ity, _) => ity.is_signed(),
- Univariant(..) => false,
+ Univariant(..) | UntaggedUnion(..) => false,
RawNullablePointer { .. } => false,
StructWrappedNullablePointer { .. } => false,
}
load_discr(bcx, ity, ptr, Disr(0), Disr(cases.len() as u64 - 1),
range_assert)
}
- Univariant(..) => C_u8(bcx.ccx(), 0),
+ Univariant(..) | UntaggedUnion(..) => C_u8(bcx.ccx(), 0),
RawNullablePointer { nndiscr, nnty, .. } => {
let cmp = if nndiscr == Disr(0) { IntEQ } else { IntNE };
let llptrty = type_of::sizing_type_of(bcx.ccx(), nnty);
General(ity, _) => {
C_integral(ll_inttype(bcx.ccx(), ity), discr.0, true)
}
- Univariant(..) => {
- bug!("no cases for univariants or structs")
+ Univariant(..) | UntaggedUnion(..) => {
+ bug!("no cases for univariants, structs or unions")
}
RawNullablePointer { .. } |
StructWrappedNullablePointer { .. } => {
Univariant(_) => {
assert_eq!(discr, Disr(0));
}
+ UntaggedUnion(..) => {
+ assert_eq!(discr, Disr(0));
+ }
RawNullablePointer { nndiscr, nnty, ..} => {
if discr != nndiscr {
let llptrty = type_of::sizing_type_of(bcx.ccx(), nnty);
General(_, ref cases) => {
struct_field_ptr(bcx, &cases[discr.0 as usize], val, ix + 1, true)
}
+ UntaggedUnion(ref un) => {
+ let ty = type_of::in_memory_type_of(bcx.ccx(), un.fields[ix]);
+ if bcx.is_unreachable() { return C_undef(ty.ptr_to()); }
+ bcx.pointercast(val.value, ty.ptr_to())
+ }
RawNullablePointer { nndiscr, ref nullfields, .. } |
StructWrappedNullablePointer { nndiscr, ref nullfields, .. } if discr != nndiscr => {
// The unit-like case might have a nonzero number of unit-like fields.
contents.extend_from_slice(&[padding(ccx, max_sz - case.size)]);
C_struct(ccx, &contents[..], false)
}
+ UntaggedUnion(..) => {
+ unimplemented_unions!();
+ }
Univariant(ref st) => {
assert_eq!(discr, Disr(0));
let contents = build_const_struct(ccx, st, vals);
match *r {
CEnum(..) => bug!("element access in C-like enum const"),
Univariant(..) => const_struct_field(val, ix),
+ UntaggedUnion(..) => const_struct_field(val, 0),
General(..) => const_struct_field(val, ix + 1),
RawNullablePointer { .. } => {
assert_eq!(ix, 0);