]> git.lizzy.rs Git - rust.git/commitdiff
Implement layout calculation and add more trans stubs
authorVadim Petrochenkov <vadim.petrochenkov@gmail.com>
Thu, 18 Aug 2016 12:44:00 +0000 (15:44 +0300)
committerVadim Petrochenkov <vadim.petrochenkov@gmail.com>
Sat, 3 Sep 2016 10:39:34 +0000 (13:39 +0300)
src/librustc/ty/layout.rs
src/librustc_mir/hair/cx/expr.rs
src/librustc_trans/adt.rs
src/librustc_trans/debuginfo/metadata.rs
src/test/run-pass/union-basic.rs [new file with mode: 0644]

index ac5e3c6fa700926f05847a991082d0adaaea562b..9270057b54415b8ac8ad7124e652f569461fec56 100644 (file)
@@ -488,7 +488,7 @@ pub fn extend<I>(&mut self, dl: &TargetDataLayout,
 
         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);
             }
 
@@ -623,6 +623,54 @@ pub fn non_zero_field_path<I>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
     }
 }
 
+/// 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.
@@ -690,6 +738,11 @@ pub enum Layout {
         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 {
@@ -896,8 +949,14 @@ pub fn compute_uncached(ty: Ty<'gcx>,
                     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)
@@ -1118,7 +1177,7 @@ pub fn compute_uncached(ty: Ty<'gcx>,
     pub fn is_unsized(&self) -> bool {
         match *self {
             Scalar {..} | Vector {..} | FatPointer {..} |
-            CEnum {..} | General {..} |
+            CEnum {..} | UntaggedUnion {..} | General {..} |
             RawNullablePointer {..} |
             StructWrappedNullablePointer {..} => false,
 
@@ -1152,6 +1211,7 @@ pub fn size(&self, dl: &TargetDataLayout) -> Size {
 
             CEnum { discr, .. } => Int(discr).size(dl),
             Array { size, .. } | General { size, .. } => size,
+            UntaggedUnion { ref variants } => variants.stride(),
 
             Univariant { ref variant, .. } |
             StructWrappedNullablePointer { nonnull: ref variant, .. } => {
@@ -1191,6 +1251,7 @@ pub fn align(&self, dl: &TargetDataLayout) -> Align {
 
             CEnum { discr, .. } => Int(discr).align(dl),
             Array { align, .. } | General { align, .. } => align,
+            UntaggedUnion { ref variants } => variants.align,
 
             Univariant { ref variant, .. } |
             StructWrappedNullablePointer { nonnull: ref variant, .. } => {
@@ -1256,9 +1317,6 @@ pub fn compute(ty: Ty<'gcx>, infcx: &InferCtxt<'a, 'gcx, 'tcx>)
                 }
             }
 
-            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 {
index 0469d44de4ba643419aa48ff394f6852d8a6600d..c8f660a2d9c7c7dbe7aeff37feeff8ff3871ba20 100644 (file)
@@ -459,7 +459,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
 
         hir::ExprStruct(_, ref fields, ref base) => {
             match expr_ty.sty {
-                ty::TyStruct(adt, substs) => {
+                ty::TyStruct(adt, substs) | ty::TyUnion(adt, substs) => {
                     let field_refs = field_refs(&adt.variants[0], fields);
                     ExprKind::Adt {
                         adt_def: adt,
@@ -477,9 +477,6 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
                         })
                     }
                 }
-                ty::TyUnion(..) => {
-                    unimplemented_unions!();
-                }
                 ty::TyEnum(adt, substs) => {
                     match cx.tcx.expect_def(expr.id) {
                         Def::Variant(enum_id, variant_id) => {
index 069eef7895c8478671296d7cf9f768cf1b0cd4e3..abbb9a5d4dbe592ca2cdb0b67ff613dd72ce2451 100644 (file)
@@ -79,6 +79,8 @@ pub enum Repr<'tcx> {
     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>>),
@@ -121,6 +123,15 @@ pub struct 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,
@@ -176,8 +187,12 @@ fn represent_type_uncached<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
 
             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))
@@ -482,6 +497,31 @@ fn mk_struct<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
     }
 }
 
+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,
@@ -646,7 +686,7 @@ pub fn incomplete_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
 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)
@@ -690,6 +730,34 @@ fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                 }
             }
         }
+        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
@@ -762,7 +830,7 @@ pub fn trans_switch<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
         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)
         }
@@ -773,7 +841,7 @@ pub fn is_discr_signed<'tcx>(r: &Repr<'tcx>) -> bool {
     match *r {
         CEnum(ity, _, _) => ity.is_signed(),
         General(ity, _) => ity.is_signed(),
-        Univariant(..) => false,
+        Univariant(..) | UntaggedUnion(..) => false,
         RawNullablePointer { .. } => false,
         StructWrappedNullablePointer { .. } => false,
     }
@@ -794,7 +862,7 @@ pub fn trans_get_discr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr<'tcx>,
             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);
@@ -856,8 +924,8 @@ pub fn trans_case<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr, discr: Disr)
         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 { .. } => {
@@ -884,6 +952,9 @@ pub fn trans_set_discr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr<'tcx>,
         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);
@@ -939,6 +1010,11 @@ pub fn trans_field_ptr_builder<'blk, 'tcx>(bcx: &BlockAndBuilder<'blk, 'tcx>,
         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.
@@ -1100,6 +1176,9 @@ pub fn trans_const<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, r: &Repr<'tcx>, discr
             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);
@@ -1211,6 +1290,7 @@ pub fn const_get_field(r: &Repr, val: ValueRef, _discr: Disr,
     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);
index f30880ac9beb51e36352be1d6e93aa39ac7570e8..bd67a215d65eb02163f31ca81dcb0c9827128171 100644 (file)
@@ -1302,6 +1302,9 @@ fn create_member_descriptions<'a>(&self, cx: &CrateContext<'a, 'tcx>)
                     ]
                 }
             }
+            adt::UntaggedUnion(..) => {
+                unimplemented_unions!();
+            }
             adt::RawNullablePointer { nndiscr: non_null_variant_index, nnty, .. } => {
                 // As far as debuginfo is concerned, the pointer this enum
                 // represents is still wrapped in a struct. This is to make the
@@ -1616,7 +1619,7 @@ fn prepare_enum_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
         },
         adt::RawNullablePointer { .. }           |
         adt::StructWrappedNullablePointer { .. } |
-        adt::Univariant(..)                      => None,
+        adt::Univariant(..) | adt::UntaggedUnion(..) => None,
         adt::General(inttype, _) => Some(discriminant_type_metadata(inttype)),
     };
 
diff --git a/src/test/run-pass/union-basic.rs b/src/test/run-pass/union-basic.rs
new file mode 100644 (file)
index 0000000..474c8b4
--- /dev/null
@@ -0,0 +1,47 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(untagged_unions)]
+
+use std::mem::{size_of, align_of, zeroed};
+
+union U {
+    a: u8,
+}
+
+union U64 {
+    a: u64,
+}
+
+union W {
+    a: u8,
+    b: u64,
+}
+
+fn main() {
+    assert_eq!(size_of::<U>(), 1);
+    assert_eq!(size_of::<U64>(), 8);
+    assert_eq!(size_of::<W>(), 8);
+    assert_eq!(align_of::<U>(), 1);
+    assert_eq!(align_of::<U64>(), align_of::<u64>());
+    assert_eq!(align_of::<W>(), align_of::<u64>());
+
+    let u = U { a: 10 };
+    assert_eq!(u.a, 10);
+    let U { a } = u;
+    assert_eq!(a, 10);
+
+    let mut w: W = unsafe { zeroed() };
+    assert_eq!(w.a, 0);
+    assert_eq!(w.b, 0);
+    // w.a = 1;
+    // assert_eq!(w.a, 0);
+    // assert_eq!(w.b, 0);
+}