about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc/ty/context.rs6
-rw-r--r--src/librustc/ty/layout.rs54
-rw-r--r--src/librustc_lint/types.rs2
-rw-r--r--src/librustc_trans/adt.rs12
-rw-r--r--src/librustc_trans/debuginfo/metadata.rs143
-rw-r--r--src/librustc_trans/mir/constant.rs27
-rw-r--r--src/librustc_trans/mir/lvalue.rs55
-rw-r--r--src/librustc_trans/type_of.rs13
8 files changed, 134 insertions, 178 deletions
diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs
index 22a3edd200c..9ad5e07d8fe 100644
--- a/src/librustc/ty/context.rs
+++ b/src/librustc/ty/context.rs
@@ -78,7 +78,7 @@ use hir;
 /// Internal storage
 pub struct GlobalArenas<'tcx> {
     // internings
-    layout: TypedArena<Layout>,
+    layout: TypedArena<Layout<'tcx>>,
 
     // references
     generics: TypedArena<ty::Generics>,
@@ -918,7 +918,7 @@ pub struct GlobalCtxt<'tcx> {
 
     stability_interner: RefCell<FxHashSet<&'tcx attr::Stability>>,
 
-    layout_interner: RefCell<FxHashSet<&'tcx Layout>>,
+    layout_interner: RefCell<FxHashSet<&'tcx Layout<'tcx>>>,
 
     /// A vector of every trait accessible in the whole crate
     /// (i.e. including those from subcrates). This is used only for
@@ -1016,7 +1016,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
         interned
     }
 
-    pub fn intern_layout(self, layout: Layout) -> &'gcx Layout {
+    pub fn intern_layout(self, layout: Layout<'gcx>) -> &'gcx Layout<'gcx> {
         if let Some(layout) = self.layout_interner.borrow().get(&layout) {
             return layout;
         }
diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs
index eb22b6f2ce9..096c74a6163 100644
--- a/src/librustc/ty/layout.rs
+++ b/src/librustc/ty/layout.rs
@@ -1004,7 +1004,7 @@ pub const FAT_PTR_ADDR: usize = 0;
 pub const FAT_PTR_EXTRA: usize = 1;
 
 /// Describes how the fields of a type are located in memory.
-#[derive(Copy, Clone, Debug)]
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
 pub enum FieldPlacement<'a> {
     /// Array-like placement. Can also express
     /// unions, by using a stride of zero bytes.
@@ -1058,7 +1058,7 @@ impl<'a> FieldPlacement<'a> {
 
 /// Describes how values of the type are passed by target ABIs,
 /// in terms of categories of C types there are ABI rules for.
-#[derive(Copy, Clone, Debug)]
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
 pub enum Abi {
     Scalar(Primitive),
     Vector {
@@ -1141,8 +1141,8 @@ impl Abi {
 /// For ADTs, it also includes field placement and enum optimizations.
 /// NOTE: Because Layout is interned, redundant information should be
 /// kept to a minimum, e.g. it includes no sub-component Ty or Layout.
-#[derive(Debug, PartialEq, Eq, Hash)]
-pub enum Layout {
+#[derive(PartialEq, Eq, Hash, Debug)]
+pub enum Layout<'a> {
     /// TyBool, TyChar, TyInt, TyUint, TyFloat, TyRawPtr, TyRef or TyFnPtr.
     Scalar(Primitive),
 
@@ -1184,7 +1184,7 @@ pub enum Layout {
         // the largest space between two consecutive discriminants and
         // taking everything else as the (shortest) discriminant range.
         discr_range: RangeInclusive<u64>,
-        variants: Vec<Struct>,
+        variants: Vec<CachedLayout<'a>>,
         size: Size,
         align: Align,
         primitive_align: Align,
@@ -1202,7 +1202,7 @@ pub enum Layout {
         nndiscr: u64,
         discr: Primitive,
         discr_offset: Size,
-        variants: Vec<Struct>,
+        variants: Vec<CachedLayout<'a>>,
         size: Size,
         align: Align,
         primitive_align: Align,
@@ -1228,9 +1228,9 @@ impl<'tcx> fmt::Display for LayoutError<'tcx> {
     }
 }
 
-#[derive(Copy, Clone, Debug)]
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
 pub struct CachedLayout<'tcx> {
-    pub layout: &'tcx Layout,
+    pub layout: &'tcx Layout<'tcx>,
     pub fields: FieldPlacement<'tcx>,
     pub abi: Abi,
 }
@@ -1262,7 +1262,7 @@ pub fn provide(providers: &mut ty::maps::Providers) {
     };
 }
 
-impl<'a, 'tcx> Layout {
+impl<'a, 'tcx> Layout<'tcx> {
     fn compute_uncached(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                         param_env: ty::ParamEnv<'tcx>,
                         ty: Ty<'tcx>)
@@ -1624,7 +1624,9 @@ impl<'a, 'tcx> Layout {
                             size: st[discr].stride(),
                             align,
                             primitive_align,
-                            variants: st,
+                            variants: st.into_iter().map(|variant| {
+                                success(Univariant(variant))
+                            }).collect::<Result<Vec<_>, _>>()?,
                         });
                     }
                 }
@@ -1730,7 +1732,9 @@ impl<'a, 'tcx> Layout {
 
                     // FIXME: should be u128?
                     discr_range: (min as u64)..=(max as u64),
-                    variants,
+                    variants: variants.into_iter().map(|variant| {
+                        success(Univariant(variant))
+                    }).collect::<Result<Vec<_>, _>>()?,
                     size,
                     align,
                     primitive_align,
@@ -1897,6 +1901,10 @@ impl<'a, 'tcx> Layout {
                                                        .iter()
                                                        .map(|f| (f.name, f.ty(tcx, substs)))
                                                        .collect();
+                                        let variant_layout = match *variant_layout.layout {
+                                            Univariant(ref variant) => variant,
+                                            _ => bug!()
+                                        };
                                         build_variant_info(Some(variant_def.name),
                                                            &fields,
                                                            variant_layout)
@@ -2084,7 +2092,7 @@ impl<'a, 'tcx> SizeSkeleton<'tcx> {
 pub struct FullLayout<'tcx> {
     pub ty: Ty<'tcx>,
     pub variant_index: Option<usize>,
-    pub layout: &'tcx Layout,
+    pub layout: &'tcx Layout<'tcx>,
     pub fields: FieldPlacement<'tcx>,
     pub abi: Abi,
 }
@@ -2198,27 +2206,22 @@ impl<'a, 'tcx> FullLayout<'tcx> {
             variants[variant_index].fields.len()
         };
 
-        let (fields, abi) = match *self.layout {
-            Univariant(_) => (self.fields, self.abi),
+        let (layout, fields, abi) = match *self.layout {
+            Univariant(_) => (self.layout, self.fields, self.abi),
 
             NullablePointer { ref variants, .. } |
             General { ref variants, .. } => {
-                let variant = &variants[variant_index];
-                (FieldPlacement::Arbitrary {
-                    offsets: &variant.offsets
-                }, Abi::Aggregate {
-                    sized: true,
-                    align: variant.align,
-                    primitive_align: variant.primitive_align,
-                    size: variant.stride(),
-                })
+                let variant = variants[variant_index];
+                (variant.layout, variant.fields, variant.abi)
             }
 
-            _ => (FieldPlacement::union(count), self.abi)
+            _ => bug!()
         };
+        assert_eq!(fields.count(), count);
 
         FullLayout {
             variant_index: Some(variant_index),
+            layout,
             fields,
             abi,
             ..*self
@@ -2348,8 +2351,7 @@ impl<'a, 'tcx> FullLayout<'tcx> {
     }
 }
 
-impl<'gcx> HashStable<StableHashingContext<'gcx>> for Layout
-{
+impl<'gcx> HashStable<StableHashingContext<'gcx>> for Layout<'gcx> {
     fn hash_stable<W: StableHasherResult>(&self,
                                           hcx: &mut StableHashingContext<'gcx>,
                                           hasher: &mut StableHasher<W>) {
diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs
index b993b161877..f59b372e7d5 100644
--- a/src/librustc_lint/types.rs
+++ b/src/librustc_lint/types.rs
@@ -764,7 +764,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for VariantSizeDifferences {
                         .zip(variants)
                         .map(|(variant, variant_layout)| {
                             // Subtract the size of the enum discriminant
-                            let bytes = variant_layout.min_size
+                            let bytes = variant_layout.abi.size(cx.tcx)
                                 .bytes()
                                 .saturating_sub(discr_size);
 
diff --git a/src/librustc_trans/adt.rs b/src/librustc_trans/adt.rs
index 314d929fe8c..ff66090dc8c 100644
--- a/src/librustc_trans/adt.rs
+++ b/src/librustc_trans/adt.rs
@@ -87,7 +87,7 @@ pub fn finish_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
             } else {
                 l
             };
-            llty.set_struct_body(&struct_llfields(cx, variant_layout, variant), variant.packed)
+            llty.set_struct_body(&struct_llfields(cx, variant_layout), variant.packed)
         },
         _ => bug!("This function cannot handle {} with layout {:#?}", t, l)
     }
@@ -105,8 +105,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),
-                                  variant.packed)
+                    Type::struct_(cx, &struct_llfields(cx, l), variant.packed)
                 }
                 Some(name) => {
                     Type::named_struct(cx, name)
@@ -166,8 +165,11 @@ pub fn memory_index_to_gep(index: u64) -> u64 {
 }
 
 pub fn struct_llfields<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
-                                 layout: FullLayout<'tcx>,
-                                 variant: &layout::Struct) -> Vec<Type> {
+                                 layout: FullLayout<'tcx>) -> Vec<Type> {
+    let variant = match *layout.layout {
+        layout::Univariant(ref variant) => variant,
+        _ => bug!("unexpected {:#?}", layout)
+    };
     let field_count = layout.fields.count();
     debug!("struct_llfields: variant: {:?}", variant);
     let mut offset = Size::from_bytes(0);
diff --git a/src/librustc_trans/debuginfo/metadata.rs b/src/librustc_trans/debuginfo/metadata.rs
index 6ab89f5c3eb..89f1bb6fd51 100644
--- a/src/librustc_trans/debuginfo/metadata.rs
+++ b/src/librustc_trans/debuginfo/metadata.rs
@@ -1153,37 +1153,33 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
             layout::General { ref variants, .. } => {
                 let discriminant_info = RegularDiscriminant(self.discriminant_type_metadata
                     .expect(""));
-                variants
-                    .iter()
-                    .enumerate()
-                    .map(|(i, struct_def)| {
-                        let (variant_type_metadata, member_desc_factory) =
-                            describe_enum_variant(cx,
-                                                  self.enum_type,
-                                                  struct_def,
-                                                  i,
-                                                  &adt.variants[i],
-                                                  discriminant_info,
-                                                  self.containing_scope,
-                                                  self.span);
-
-                        let member_descriptions = member_desc_factory
-                            .create_member_descriptions(cx);
-
-                        set_members_of_composite_type(cx,
-                                                      variant_type_metadata,
-                                                      &member_descriptions);
-                        MemberDescription {
-                            name: "".to_string(),
-                            type_metadata: variant_type_metadata,
-                            offset: Size::from_bytes(0),
-                            size: struct_def.stride(),
-                            align: struct_def.align,
-                            flags: DIFlags::FlagZero
-                        }
-                    }).collect()
+                (0..variants.len()).map(|i| {
+                    let variant = self.type_rep.for_variant(i);
+                    let (variant_type_metadata, member_desc_factory) =
+                        describe_enum_variant(cx,
+                                              variant,
+                                              &adt.variants[i],
+                                              discriminant_info,
+                                              self.containing_scope,
+                                              self.span);
+
+                    let member_descriptions = member_desc_factory
+                        .create_member_descriptions(cx);
+
+                    set_members_of_composite_type(cx,
+                                                  variant_type_metadata,
+                                                  &member_descriptions);
+                    MemberDescription {
+                        name: "".to_string(),
+                        type_metadata: variant_type_metadata,
+                        offset: Size::from_bytes(0),
+                        size: variant.size(cx),
+                        align: variant.align(cx),
+                        flags: DIFlags::FlagZero
+                    }
+                }).collect()
             },
-            layout::Univariant(ref variant) => {
+            layout::Univariant(_) => {
                 assert!(adt.variants.len() <= 1);
 
                 if adt.variants.is_empty() {
@@ -1191,9 +1187,7 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
                 } else {
                     let (variant_type_metadata, member_description_factory) =
                         describe_enum_variant(cx,
-                                              self.enum_type,
-                                              variant,
-                                              0,
+                                              self.type_rep,
                                               &adt.variants[0],
                                               NoDiscriminant,
                                               self.containing_scope,
@@ -1210,8 +1204,8 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
                             name: "".to_string(),
                             type_metadata: variant_type_metadata,
                             offset: Size::from_bytes(0),
-                            size: variant.stride(),
-                            align: variant.align,
+                            size: self.type_rep.size(cx),
+                            align: self.type_rep.align(cx),
                             flags: DIFlags::FlagZero
                         }
                     ]
@@ -1221,16 +1215,13 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
                 nndiscr,
                 discr,
                 discr_offset,
-                ref variants,
                 ..
             } => {
-                let struct_def = &variants[nndiscr as usize];
+                let variant = self.type_rep.for_variant(nndiscr as usize);
                 // Create a description of the non-null variant
                 let (variant_type_metadata, member_description_factory) =
                     describe_enum_variant(cx,
-                                          self.enum_type,
-                                          struct_def,
-                                          nndiscr as usize,
+                                          variant,
                                           &adt.variants[nndiscr as usize],
                                           OptimizedDiscriminant,
                                           self.containing_scope,
@@ -1278,8 +1269,8 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
                         name,
                         type_metadata: variant_type_metadata,
                         offset: Size::from_bytes(0),
-                        size: struct_def.stride(),
-                        align: struct_def.align,
+                        size: variant.size(cx),
+                        align: variant.align(cx),
                         flags: DIFlags::FlagZero
                     }
                 ]
@@ -1330,78 +1321,48 @@ enum EnumDiscriminantInfo {
 // descriptions of the fields of the variant. This is a rudimentary version of a
 // full RecursiveTypeDescription.
 fn describe_enum_variant<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
-                                   enum_type: Ty<'tcx>,
-                                   struct_def: &'tcx layout::Struct,
-                                   variant_index: usize,
+                                   layout: layout::FullLayout<'tcx>,
                                    variant: &'tcx ty::VariantDef,
                                    discriminant_info: EnumDiscriminantInfo,
                                    containing_scope: DIScope,
                                    span: Span)
                                    -> (DICompositeType, MemberDescriptionFactory<'tcx>) {
-    let layout = cx.layout_of(enum_type);
-    let maybe_discr = match *layout.layout {
-        layout::General { .. } => Some(layout.field(cx, 0).ty),
-        _ => None,
-    };
-
-    let layout = layout.for_variant(variant_index);
-    let mut field_tys = (0..layout.fields.count()).map(|i| {
-        layout.field(cx, i).ty
-    }).collect::<Vec<_>>();
-
-    if let Some(discr) = maybe_discr {
-        field_tys.insert(0, discr);
-    }
-
-    // Could do some consistency checks here: size, align, field count, discr type
-
     let variant_name = variant.name.as_str();
     let unique_type_id = debug_context(cx).type_map
                                           .borrow_mut()
                                           .get_unique_type_id_of_enum_variant(
                                               cx,
-                                              enum_type,
+                                              layout.ty,
                                               &variant_name);
 
     let metadata_stub = create_struct_stub(cx,
-                                           enum_type,
+                                           layout.ty,
                                            &variant_name,
                                            unique_type_id,
                                            containing_scope);
 
-    // Get the argument names from the enum variant info
-    let mut arg_names: Vec<_> = match variant.ctor_kind {
-        CtorKind::Const => vec![],
-        CtorKind::Fn => {
-            variant.fields
-                   .iter()
-                   .enumerate()
-                   .map(|(i, _)| format!("__{}", i))
-                   .collect()
-        }
-        CtorKind::Fictive => {
-            variant.fields
-                   .iter()
-                   .map(|f| f.name.to_string())
-                   .collect()
-        }
-    };
-
     // If this is not a univariant enum, there is also the discriminant field.
-    let mut offsets = struct_def.offsets.clone();
-    match discriminant_info {
+    let (discr_offset, discr_arg) = match discriminant_info {
         RegularDiscriminant(_) => {
-            arg_names.insert(0, "RUST$ENUM$DISR".to_string());
-            offsets.insert(0, Size::from_bytes(0));
+            let enum_layout = cx.layout_of(layout.ty);
+            (Some(enum_layout.fields.offset(0)),
+             Some(("RUST$ENUM$DISR".to_string(), enum_layout.field(cx, 0).ty)))
         }
-        _ => { /* do nothing */ }
+        _ => (None, None),
     };
+    let offsets = discr_offset.into_iter().chain((0..layout.fields.count()).map(|i| {
+        layout.fields.offset(i)
+    })).collect();
 
     // Build an array of (field name, field type) pairs to be captured in the factory closure.
-    let args: Vec<(String, Ty)> = arg_names.iter()
-        .zip(field_tys.iter())
-        .map(|(s, &t)| (s.to_string(), t))
-        .collect();
+    let args = discr_arg.into_iter().chain((0..layout.fields.count()).map(|i| {
+        let name = if variant.ctor_kind == CtorKind::Fn {
+            format!("__{}", i)
+        } else {
+            variant.fields[i].name.to_string()
+        };
+        (name, layout.field(cx, i).ty)
+    })).collect();
 
     let member_description_factory =
         VariantMDF(VariantMemberDescriptionFactory {
diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs
index c64333fc044..8924fc3b5ac 100644
--- a/src/librustc_trans/mir/constant.rs
+++ b/src/librustc_trans/mir/constant.rs
@@ -1091,7 +1091,7 @@ fn trans_const_adt<'a, 'tcx>(
         _ => 0,
     };
     match *l.layout {
-        layout::General { ref variants, .. } => {
+        layout::General { .. } => {
             let discr = match *kind {
                 mir::AggregateKind::Adt(adt_def, _, _, _) => {
                     adt_def.discriminant_for_variant(ccx.tcx(), variant_index)
@@ -1105,7 +1105,7 @@ fn trans_const_adt<'a, 'tcx>(
             if let layout::Abi::Scalar(_) = l.abi {
                 discr
             } else {
-                build_const_struct(ccx, l, &variants[variant_index], vals, Some(discr))
+                build_const_struct(ccx, l.for_variant(variant_index), vals, Some(discr))
             }
         }
         layout::UntaggedUnion(ref un) => {
@@ -1117,16 +1117,16 @@ fn trans_const_adt<'a, 'tcx>(
 
             Const::new(C_struct(ccx, &contents, un.packed), t)
         }
-        layout::Univariant(ref variant) => {
+        layout::Univariant(_) => {
             assert_eq!(variant_index, 0);
-            build_const_struct(ccx, l, &variant, vals, None)
+            build_const_struct(ccx, l, vals, None)
         }
         layout::Vector { .. } => {
             Const::new(C_vector(&vals.iter().map(|x| x.llval).collect::<Vec<_>>()), t)
         }
-        layout::NullablePointer { ref variants, nndiscr, .. } => {
+        layout::NullablePointer { nndiscr, .. } => {
             if variant_index as u64 == nndiscr {
-                build_const_struct(ccx, l, &variants[variant_index], vals, None)
+                build_const_struct(ccx, l.for_variant(variant_index), vals, None)
             } else {
                 // Always use null even if it's not the `discrfield`th
                 // field; see #8506.
@@ -1147,24 +1147,27 @@ fn trans_const_adt<'a, 'tcx>(
 /// will read the wrong memory.
 fn build_const_struct<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                                 layout: layout::FullLayout<'tcx>,
-                                st: &layout::Struct,
                                 vals: &[Const<'tcx>],
                                 discr: Option<Const<'tcx>>)
                                 -> Const<'tcx> {
-    assert_eq!(vals.len(), st.offsets.len());
+    assert_eq!(vals.len(), layout.fields.count());
 
     // offset of current value
     let mut offset = Size::from_bytes(0);
     let mut cfields = Vec::new();
-    cfields.reserve(discr.is_some() as usize + 1 + st.offsets.len() * 2);
+    cfields.reserve(discr.is_some() as usize + 1 + layout.fields.count() * 2);
 
     if let Some(discr) = discr {
         cfields.push(discr.llval);
         offset = ccx.size_of(discr.ty);
     }
 
+    let st = match *layout.layout {
+        layout::Univariant(ref variant) => variant,
+        _ => bug!("unexpected {:#?}", layout)
+    };
     let parts = st.field_index_by_increasing_offset().map(|i| {
-        (vals[i], st.offsets[i])
+        (vals[i], layout.fields.offset(i))
     });
     for (val, target_offset) in parts {
         cfields.push(padding(ccx, target_offset - offset));
@@ -1172,8 +1175,8 @@ fn build_const_struct<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
         offset = target_offset + ccx.size_of(val.ty);
     }
 
-    let size = layout.size(ccx);
-    cfields.push(padding(ccx, size - offset));
+    // Pad to the size of the whole type, not e.g. the variant.
+    cfields.push(padding(ccx, ccx.size_of(layout.ty) - offset));
 
     Const::new(C_struct(ccx, &cfields, st.packed), layout.ty)
 }
diff --git a/src/librustc_trans/mir/lvalue.rs b/src/librustc_trans/mir/lvalue.rs
index 00c76bee1a8..ab31bcde52a 100644
--- a/src/librustc_trans/mir/lvalue.rs
+++ b/src/librustc_trans/mir/lvalue.rs
@@ -55,7 +55,7 @@ impl ops::BitOr for Alignment {
     }
 }
 
-impl<'a> From<&'a Layout> for Alignment {
+impl<'a> From<&'a Layout<'a>> for Alignment {
     fn from(layout: &Layout) -> Self {
         let (packed, align) = match *layout {
             Layout::UntaggedUnion(ref un) => (un.packed, un.align),
@@ -234,27 +234,24 @@ impl<'a, 'tcx> LvalueRef<'tcx> {
         }
 
         // Discriminant field of enums.
-        match *l.layout {
-            layout::NullablePointer { .. } if l.variant_index.is_none() => {
-                let ty = ccx.llvm_type_of(field.ty);
-                let size = field.size(ccx).bytes();
-
-                // If the discriminant is not on a multiple of the primitive's size,
-                // we need to go through i8*. Also assume the worst alignment.
-                if offset % size != 0 {
-                    let byte_ptr = bcx.pointercast(self.llval, Type::i8p(ccx));
-                    let byte_ptr = bcx.inbounds_gep(byte_ptr, &[C_usize(ccx, offset)]);
-                    let byte_align = Alignment::Packed(Align::from_bytes(1, 1).unwrap());
-                    return LvalueRef::new_sized(
-                        bcx.pointercast(byte_ptr, ty.ptr_to()), field.ty, byte_align);
-                }
-
-                let discr_ptr = bcx.pointercast(self.llval, ty.ptr_to());
+        if let layout::NullablePointer { .. } = *l.layout {
+            let ty = ccx.llvm_type_of(field.ty);
+            let size = field.size(ccx).bytes();
+
+            // If the discriminant is not on a multiple of the primitive's size,
+            // we need to go through i8*. Also assume the worst alignment.
+            if offset % size != 0 {
+                let byte_ptr = bcx.pointercast(self.llval, Type::i8p(ccx));
+                let byte_ptr = bcx.inbounds_gep(byte_ptr, &[C_usize(ccx, offset)]);
+                let byte_align = Alignment::Packed(Align::from_bytes(1, 1).unwrap());
                 return LvalueRef::new_sized(
-                    bcx.inbounds_gep(discr_ptr, &[C_usize(ccx, offset / size)]),
-                    field.ty, alignment);
+                    bcx.pointercast(byte_ptr, ty.ptr_to()), field.ty, byte_align);
             }
-            _ => {}
+
+            let discr_ptr = bcx.pointercast(self.llval, ty.ptr_to());
+            return LvalueRef::new_sized(
+                bcx.inbounds_gep(discr_ptr, &[C_usize(ccx, offset / size)]),
+                field.ty, alignment);
         }
 
         let simple = || {
@@ -271,10 +268,8 @@ impl<'a, 'tcx> LvalueRef<'tcx> {
         };
 
         // Check whether the variant being used is packed, if applicable.
-        let is_packed = match (l.layout, l.variant_index) {
-            (&layout::Univariant(ref variant), _) => variant.packed,
-            (&layout::NullablePointer { ref variants, .. }, Some(v)) |
-            (&layout::General { ref variants, .. }, Some(v)) => variants[v].packed,
+        let is_packed = match *l.layout {
+            layout::Univariant(ref variant) => variant.packed,
             _ => return simple()
         };
 
@@ -470,13 +465,13 @@ 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);
-            match *layout.layout {
-                layout::NullablePointer { ref variants, .. } |
-                layout::General { ref variants, .. } => {
-                    let st = &variants[variant_index];
+            let layout = bcx.ccx.layout_of(ty);
+            let variant_layout = layout.for_variant(variant_index);
+            match (layout.layout, variant_layout.layout) {
+                (&layout::NullablePointer { .. }, &layout::Univariant(ref st)) |
+                (&layout::General { .. }, &layout::Univariant(ref st)) => {
                     let variant_ty = Type::struct_(bcx.ccx,
-                        &adt::struct_llfields(bcx.ccx, layout, st), st.packed);
+                        &adt::struct_llfields(bcx.ccx, variant_layout), st.packed);
                     downcast.llval = bcx.pointercast(downcast.llval, variant_ty.ptr_to());
                 }
                 _ => {}
diff --git a/src/librustc_trans/type_of.rs b/src/librustc_trans/type_of.rs
index b829d33600c..679632d9113 100644
--- a/src/librustc_trans/type_of.rs
+++ b/src/librustc_trans/type_of.rs
@@ -242,7 +242,9 @@ impl<'tcx> LayoutLlvmExt for FullLayout<'tcx> {
         }
         match *self.layout {
             Layout::Scalar { .. } |
-            Layout::UntaggedUnion { .. } => {
+            Layout::UntaggedUnion { .. } |
+            Layout::NullablePointer { .. } |
+            Layout::General { .. } => {
                 bug!("FullLayout::llvm_field_index({:?}): not applicable", self)
             }
 
@@ -258,15 +260,6 @@ impl<'tcx> LayoutLlvmExt for FullLayout<'tcx> {
             Layout::Univariant(ref variant) => {
                 adt::memory_index_to_gep(variant.memory_index[index] as u64)
             }
-
-            Layout::NullablePointer { ref variants, .. } |
-            Layout::General { ref variants, .. } => {
-                if let Some(v) = self.variant_index {
-                    adt::memory_index_to_gep(variants[v].memory_index[index] as u64)
-                } else {
-                    bug!("FullLayout::llvm_field_index({:?}): not applicable", self)
-                }
-            }
         }
     }
 }