about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc/ty/layout.rs82
-rw-r--r--src/librustc_trans/adt.rs26
-rw-r--r--src/librustc_trans/debuginfo/metadata.rs10
-rw-r--r--src/librustc_trans/mir/constant.rs21
-rw-r--r--src/librustc_trans/mir/lvalue.rs13
-rw-r--r--src/test/codegen/consts.rs4
-rw-r--r--src/test/run-pass/enum-discrim-manual-sizing.rs3
7 files changed, 70 insertions, 89 deletions
diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs
index d51c25ba6d3..4fea7ee082c 100644
--- a/src/librustc/ty/layout.rs
+++ b/src/librustc/ty/layout.rs
@@ -651,14 +651,14 @@ pub struct Struct {
 }
 
 /// Info required to optimize struct layout.
-#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
+#[derive(Copy, Clone, Debug)]
 enum StructKind {
     /// A tuple, closure, or univariant which cannot be coerced to unsized.
     AlwaysSizedUnivariant,
     /// A univariant, the last field of which may be coerced to unsized.
     MaybeUnsizedUnivariant,
     /// A univariant, but part of an enum.
-    EnumVariant,
+    EnumVariant(Integer),
 }
 
 impl<'a, 'tcx> Struct {
@@ -692,30 +692,27 @@ impl<'a, 'tcx> Struct {
         // Neither do  1-member and 2-member structs.
         // In addition, code in trans assume that 2-element structs can become pairs.
         // It's easier to just short-circuit here.
-        let can_optimize = (fields.len() > 2 || StructKind::EnumVariant == kind)
-            && (repr.flags & ReprFlags::IS_UNOPTIMISABLE).is_empty();
-
-        let (optimize, sort_ascending) = match kind {
-            StructKind::AlwaysSizedUnivariant => (can_optimize, false),
-            StructKind::MaybeUnsizedUnivariant => (can_optimize, false),
-            StructKind::EnumVariant => {
-                assert!(fields.len() >= 1, "Enum variants must have discriminants.");
-                (can_optimize && fields[0].size(dl).bytes() == 1, true)
+        let (mut optimize, sort_ascending) = match kind {
+            StructKind::AlwaysSizedUnivariant |
+            StructKind::MaybeUnsizedUnivariant => (fields.len() > 2, false),
+            StructKind::EnumVariant(discr) => {
+                (discr.size().bytes() == 1, true)
             }
         };
 
+        optimize &= (repr.flags & ReprFlags::IS_UNOPTIMISABLE).is_empty();
+
         ret.offsets = vec![Size::from_bytes(0); fields.len()];
         let mut inverse_memory_index: Vec<u32> = (0..fields.len() as u32).collect();
 
         if optimize {
-            let start = if let StructKind::EnumVariant = kind { 1 } else { 0 };
             let end = if let StructKind::MaybeUnsizedUnivariant = kind {
                 fields.len() - 1
             } else {
                 fields.len()
             };
-            if end > start {
-                let optimizing  = &mut inverse_memory_index[start..end];
+            if end > 0 {
+                let optimizing  = &mut inverse_memory_index[..end];
                 if sort_ascending {
                     optimizing.sort_by_key(|&x| fields[x as usize].align(dl).abi());
                 } else {
@@ -734,13 +731,17 @@ impl<'a, 'tcx> Struct {
         // field 5 with offset 0 puts 0 in offsets[5].
         // At the bottom of this function, we use inverse_memory_index to produce memory_index.
 
-        if let StructKind::EnumVariant = kind {
-            assert_eq!(inverse_memory_index[0], 0,
-              "Enum variant discriminants must have the lowest offset.");
-        }
-
         let mut offset = Size::from_bytes(0);
 
+        if let StructKind::EnumVariant(discr) = kind {
+            offset = discr.size();
+            if !ret.packed {
+                let align = discr.align(dl);
+                ret.align = ret.align.max(align);
+                ret.primitive_align = ret.primitive_align.max(align);
+            }
+        }
+
         for i in inverse_memory_index.iter() {
             let field = fields[*i as usize];
             if !ret.sized {
@@ -1112,8 +1113,9 @@ pub enum Layout {
         variants: Union,
     },
 
-    /// General-case enums: for each case there is a struct, and they
-    /// all start with a field for the discriminant.
+    /// General-case enums: for each case there is a struct, and they all have
+    /// all space reserved for the discriminant, and their first field starts
+    /// at a non-0 offset, after where the discriminant would go.
     General {
         discr: Integer,
         variants: Vec<Struct>,
@@ -1495,21 +1497,17 @@ impl<'a, 'tcx> Layout {
                 // We're interested in the smallest alignment, so start large.
                 let mut start_align = Align::from_bytes(256, 256).unwrap();
 
-                // Create the set of structs that represent each variant
-                // Use the minimum integer type we figured out above
-                let discr = Scalar { value: Int(min_ity), non_zero: false };
+                // Create the set of structs that represent each variant.
                 let mut variants = variants.into_iter().map(|fields| {
-                    let mut fields = fields.into_iter().map(|field| {
+                    let fields = fields.into_iter().map(|field| {
                         field.layout(tcx, param_env)
                     }).collect::<Result<Vec<_>, _>>()?;
-                    fields.insert(0, &discr);
                     let st = Struct::new(dl,
                         &fields,
-                        &def.repr, StructKind::EnumVariant, ty)?;
+                        &def.repr, StructKind::EnumVariant(min_ity), ty)?;
                     // Find the first field we can't move later
                     // to make room for a larger discriminant.
-                    // It is important to skip the first field.
-                    for i in st.field_index_by_increasing_offset().skip(1) {
+                    for i in st.field_index_by_increasing_offset() {
                         let field = fields[i];
                         let field_align = field.align(dl);
                         if field.size(dl).bytes() != 0 || field_align.abi() != 1 {
@@ -1569,9 +1567,8 @@ impl<'a, 'tcx> Layout {
                     let new_ity_size = Int(ity).size(dl);
                     for variant in &mut variants {
                         for i in variant.offsets.iter_mut() {
-                            // The first field is the discrimminant, at offset 0.
-                            // These aren't in order, and we need to skip it.
-                            if *i <= old_ity_size && *i > Size::from_bytes(0) {
+                            if *i <= old_ity_size {
+                                assert_eq!(*i, old_ity_size);
                                 *i = new_ity_size;
                             }
                         }
@@ -1759,7 +1756,7 @@ impl<'a, 'tcx> Layout {
 
             General { ref variants, .. } => {
                 let v = variant_index.expect("variant index required");
-                variants[v].offsets[i + 1]
+                variants[v].offsets[i]
             }
 
             StructWrappedNullablePointer { nndiscr, ref nonnull, .. } => {
@@ -1857,21 +1854,12 @@ impl<'a, 'tcx> Layout {
             }
         };
 
-        enum Fields<'a> {
-            WithDiscrim(&'a Struct),
-            NoDiscrim(&'a Struct),
-        }
-
         let build_variant_info = |n: Option<ast::Name>,
                                   flds: &[(ast::Name, Ty<'tcx>)],
-                                  layout: Fields| {
-            let (s, field_offsets) = match layout {
-                Fields::WithDiscrim(s) => (s, &s.offsets[1..]),
-                Fields::NoDiscrim(s) => (s, &s.offsets[0..]),
-            };
+                                  s: &Struct| {
             let field_info: Vec<_> =
                 flds.iter()
-                    .zip(field_offsets.iter())
+                    .zip(&s.offsets)
                     .map(|(&field_name_ty, offset)| build_field_info(field_name_ty, offset))
                     .collect();
 
@@ -1904,7 +1892,7 @@ impl<'a, 'tcx> Layout {
                        None,
                        vec![build_variant_info(Some(variant_def.name),
                                                &fields,
-                                               Fields::NoDiscrim(variant_layout))]);
+                                               variant_layout)]);
             }
             Layout::RawNullablePointer { nndiscr, value } => {
                 debug!("print-type-size t: `{:?}` adt raw nullable nndiscr {} is {:?}",
@@ -1931,7 +1919,7 @@ impl<'a, 'tcx> Layout {
                            None,
                            vec![build_variant_info(Some(variant_def.name),
                                                    &fields,
-                                                   Fields::NoDiscrim(variant_layout))]);
+                                                   variant_layout)]);
                 } else {
                     // (This case arises for *empty* enums; so give it
                     // zero variants.)
@@ -1953,7 +1941,7 @@ impl<'a, 'tcx> Layout {
                                                        .collect();
                                         build_variant_info(Some(variant_def.name),
                                                            &fields,
-                                                           Fields::WithDiscrim(variant_layout))
+                                                           variant_layout)
                                     })
                                     .collect();
                 record(adt_kind.into(), Some(discr.size()), variant_infos);
diff --git a/src/librustc_trans/adt.rs b/src/librustc_trans/adt.rs
index c1242f57139..9c492ca52d2 100644
--- a/src/librustc_trans/adt.rs
+++ b/src/librustc_trans/adt.rs
@@ -90,8 +90,7 @@ pub fn finish_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                     (l.for_variant(nndiscr as usize), nonnull),
                 _ => unreachable!()
             };
-            llty.set_struct_body(&struct_llfields(cx, variant_layout, variant, None),
-                                 variant.packed)
+            llty.set_struct_body(&struct_llfields(cx, variant_layout, variant), variant.packed)
         },
         _ => bug!("This function cannot handle {} with layout {:#?}", t, l)
     }
@@ -116,7 +115,7 @@ fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
             match name {
                 None => {
                     Type::struct_(cx, &struct_llfields(cx, l.for_variant(nndiscr as usize),
-                                                       nonnull, None),
+                                                       nonnull),
                                   nonnull.packed)
                 }
                 Some(name) => {
@@ -127,7 +126,7 @@ fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
         layout::Univariant { ref variant, .. } => {
             match name {
                 None => {
-                    Type::struct_(cx, &struct_llfields(cx, l, &variant, None),
+                    Type::struct_(cx, &struct_llfields(cx, l, &variant),
                                   variant.packed)
                 }
                 Some(name) => {
@@ -209,23 +208,16 @@ pub fn memory_index_to_gep(index: u64) -> u64 {
 
 pub fn struct_llfields<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                                  layout: TyLayout<'tcx>,
-                                 variant: &layout::Struct,
-                                 discr: Option<Ty<'tcx>>) -> Vec<Type> {
-    let field_count = (discr.is_some() as usize) + layout.field_count();
+                                 variant: &layout::Struct) -> Vec<Type> {
+    let field_count = layout.field_count();
     debug!("struct_llfields: variant: {:?}", variant);
     let mut offset = Size::from_bytes(0);
     let mut result: Vec<Type> = Vec::with_capacity(1 + field_count * 2);
-    let field_iter = variant.field_index_by_increasing_offset().map(|i| {
-        let ty = if i == 0 && discr.is_some() {
-            cx.layout_of(discr.unwrap())
-        } else {
-            layout.field(cx, i - discr.is_some() as usize)
-        };
-        (i, ty, variant.offsets[i as usize])
-    });
-    for (index, field, target_offset) in field_iter {
+    for i in variant.field_index_by_increasing_offset() {
+        let field = layout.field(cx, i);
+        let target_offset = variant.offsets[i as usize];
         debug!("struct_llfields: {}: {:?} offset: {:?} target_offset: {:?}",
-            index, field, offset, target_offset);
+            i, field, offset, target_offset);
         assert!(target_offset >= offset);
         let padding = target_offset - offset;
         result.push(Type::array(&Type::i8(cx), padding.bytes()));
diff --git a/src/librustc_trans/debuginfo/metadata.rs b/src/librustc_trans/debuginfo/metadata.rs
index 2869ddb6e22..879a7467857 100644
--- a/src/librustc_trans/debuginfo/metadata.rs
+++ b/src/librustc_trans/debuginfo/metadata.rs
@@ -1340,7 +1340,7 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
 // Creates MemberDescriptions for the fields of a single enum variant.
 struct VariantMemberDescriptionFactory<'tcx> {
     // Cloned from the layout::Struct describing the variant.
-    offsets: &'tcx [layout::Size],
+    offsets: Vec<layout::Size>,
     args: Vec<(String, Ty<'tcx>)>,
     discriminant_type_metadata: Option<DIType>,
     span: Span,
@@ -1436,8 +1436,12 @@ fn describe_enum_variant<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
     };
 
     // If this is not a univariant enum, there is also the discriminant field.
+    let mut offsets = struct_def.offsets.clone();
     match discriminant_info {
-        RegularDiscriminant(_) => arg_names.insert(0, "RUST$ENUM$DISR".to_string()),
+        RegularDiscriminant(_) => {
+            arg_names.insert(0, "RUST$ENUM$DISR".to_string());
+            offsets.insert(0, Size::from_bytes(0));
+        }
         _ => { /* do nothing */ }
     };
 
@@ -1449,7 +1453,7 @@ fn describe_enum_variant<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
 
     let member_description_factory =
         VariantMDF(VariantMemberDescriptionFactory {
-            offsets: &struct_def.offsets[..],
+            offsets,
             args,
             discriminant_type_metadata: match discriminant_info {
                 RegularDiscriminant(discriminant_type_metadata) => {
diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs
index 1b3559a50e3..ab8749f5611 100644
--- a/src/librustc_trans/mir/constant.rs
+++ b/src/librustc_trans/mir/constant.rs
@@ -1108,11 +1108,8 @@ fn trans_const_adt<'a, 'tcx>(
         layout::General { discr: d, ref variants, .. } => {
             let variant = &variants[variant_index];
             let lldiscr = C_int(Type::from_integer(ccx, d), variant_index as i64);
-            let mut vals_with_discr = vec![
-                Const::new(lldiscr, d.to_ty(ccx.tcx(), false))
-            ];
-            vals_with_discr.extend_from_slice(vals);
-            build_const_struct(ccx, l, &variant, &vals_with_discr)
+            build_const_struct(ccx, l, &variant, vals,
+                Some(Const::new(lldiscr, d.to_ty(ccx.tcx(), false))))
         }
         layout::UntaggedUnion { ref variants, .. }=> {
             assert_eq!(variant_index, 0);
@@ -1125,7 +1122,7 @@ fn trans_const_adt<'a, 'tcx>(
         }
         layout::Univariant { ref variant, .. } => {
             assert_eq!(variant_index, 0);
-            build_const_struct(ccx, l, &variant, vals)
+            build_const_struct(ccx, l, &variant, vals, None)
         }
         layout::Vector { .. } => {
             Const::new(C_vector(&vals.iter().map(|x| x.llval).collect::<Vec<_>>()), t)
@@ -1140,7 +1137,7 @@ fn trans_const_adt<'a, 'tcx>(
         }
         layout::StructWrappedNullablePointer { ref nonnull, nndiscr, .. } => {
             if variant_index as u64 == nndiscr {
-                build_const_struct(ccx, l, &nonnull, vals)
+                build_const_struct(ccx, l, &nonnull, vals, None)
             } else {
                 // Always use null even if it's not the `discrfield`th
                 // field; see #8506.
@@ -1162,14 +1159,20 @@ fn trans_const_adt<'a, 'tcx>(
 fn build_const_struct<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                                 layout: layout::TyLayout<'tcx>,
                                 st: &layout::Struct,
-                                vals: &[Const<'tcx>])
+                                vals: &[Const<'tcx>],
+                                discr: Option<Const<'tcx>>)
                                 -> Const<'tcx> {
     assert_eq!(vals.len(), st.offsets.len());
 
     // offset of current value
     let mut offset = Size::from_bytes(0);
     let mut cfields = Vec::new();
-    cfields.reserve(st.offsets.len()*2);
+    cfields.reserve(discr.is_some() as usize + 1 + st.offsets.len() * 2);
+
+    if let Some(discr) = discr {
+        cfields.push(discr.llval);
+        offset = ccx.size_of(discr.ty);
+    }
 
     let parts = st.field_index_by_increasing_offset().map(|i| {
         (vals[i], st.offsets[i])
diff --git a/src/librustc_trans/mir/lvalue.rs b/src/librustc_trans/mir/lvalue.rs
index 8bd4142f2c1..c11596b0140 100644
--- a/src/librustc_trans/mir/lvalue.rs
+++ b/src/librustc_trans/mir/lvalue.rs
@@ -236,14 +236,6 @@ impl<'a, 'tcx> LvalueRef<'tcx> {
             _ => {}
         }
 
-        // Adjust the index to account for enum discriminants in variants.
-        let mut ix = ix;
-        if let layout::General { .. } = *l {
-            if l.variant_index.is_some() {
-                ix += 1;
-            }
-        }
-
         let simple = || {
             LvalueRef {
                 llval: bcx.struct_gep(self.llval, l.llvm_field_index(ix)),
@@ -474,11 +466,10 @@ impl<'a, 'tcx> LvalueRef<'tcx> {
 
             // If this is an enum, cast to the appropriate variant struct type.
             let layout = bcx.ccx.layout_of(ty).for_variant(variant_index);
-            if let layout::General { discr, ref variants, .. } = *layout {
+            if let layout::General { ref variants, .. } = *layout {
                 let st = &variants[variant_index];
                 let variant_ty = Type::struct_(bcx.ccx,
-                    &adt::struct_llfields(bcx.ccx, layout, st,
-                        Some(discr.to_ty(bcx.tcx(), false))), st.packed);
+                    &adt::struct_llfields(bcx.ccx, layout, st), st.packed);
                 downcast.llval = bcx.pointercast(downcast.llval, variant_ty.ptr_to());
             }
 
diff --git a/src/test/codegen/consts.rs b/src/test/codegen/consts.rs
index 705488b7757..a75b8f3992d 100644
--- a/src/test/codegen/consts.rs
+++ b/src/test/codegen/consts.rs
@@ -54,7 +54,7 @@ pub fn inline_enum_const() -> E<i8, i16> {
 #[no_mangle]
 pub fn low_align_const() -> E<i16, [i16; 3]> {
 // Check that low_align_const and high_align_const use the same constant
-// CHECK: load {{.*}} bitcast ({ [0 x i8], i16, [0 x i8], i16, [4 x i8] }** [[LOW_HIGH_REF]]
+// CHECK: load {{.*}} bitcast ({ i16, [0 x i8], i16, [4 x i8] }** [[LOW_HIGH_REF]]
     *&E::A(0)
 }
 
@@ -62,6 +62,6 @@ pub fn low_align_const() -> E<i16, [i16; 3]> {
 #[no_mangle]
 pub fn high_align_const() -> E<i16, i32> {
 // Check that low_align_const and high_align_const use the same constant
-// CHECK: load {{.*}} bitcast ({ [0 x i8], i16, [0 x i8], i16, [4 x i8] }** [[LOW_HIGH_REF]]
+// CHECK: load {{.*}} bitcast ({ i16, [0 x i8], i16, [4 x i8] }** [[LOW_HIGH_REF]]
     *&E::A(0)
 }
diff --git a/src/test/run-pass/enum-discrim-manual-sizing.rs b/src/test/run-pass/enum-discrim-manual-sizing.rs
index 3bbc107e0b9..8557c065dc6 100644
--- a/src/test/run-pass/enum-discrim-manual-sizing.rs
+++ b/src/test/run-pass/enum-discrim-manual-sizing.rs
@@ -108,6 +108,9 @@ pub fn main() {
     let array_expected_size = round_up(28, align_of::<Eu64NonCLike<[u32; 5]>>());
     assert_eq!(size_of::<Eu64NonCLike<[u32; 5]>>(), array_expected_size);
     assert_eq!(size_of::<Eu64NonCLike<[u32; 6]>>(), 32);
+
+    assert_eq!(align_of::<Eu32>(), align_of::<u32>());
+    assert_eq!(align_of::<Eu64NonCLike<u8>>(), align_of::<u64>());
 }
 
 // Rounds x up to the next multiple of a