about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEduard-Mihai Burtescu <edy.burt@gmail.com>2017-09-26 21:34:10 +0300
committerEduard-Mihai Burtescu <edy.burt@gmail.com>2017-11-19 02:14:33 +0200
commitced5e04e8bfa80ae297cba6c95ec0948dceb6933 (patch)
treeacb7b944d4fcdbb06ae6ecd786c7505616edc1ea
parentf62e43da2891a65a484a917d84642544ed093ba2 (diff)
downloadrust-ced5e04e8bfa80ae297cba6c95ec0948dceb6933.tar.gz
rust-ced5e04e8bfa80ae297cba6c95ec0948dceb6933.zip
rustc: optimize out uninhabited types and variants.
-rw-r--r--src/librustc/ty/layout.rs151
-rw-r--r--src/librustc_trans/abi.rs3
-rw-r--r--src/librustc_trans/cabi_x86_64.rs2
-rw-r--r--src/librustc_trans/cabi_x86_win64.rs1
-rw-r--r--src/librustc_trans/debuginfo/metadata.rs61
-rw-r--r--src/librustc_trans/mir/block.rs6
-rw-r--r--src/librustc_trans/mir/constant.rs10
-rw-r--r--src/librustc_trans/mir/lvalue.rs25
-rw-r--r--src/librustc_trans/type_of.rs7
-rw-r--r--src/test/ui/print_type_sizes/uninhabited.rs18
-rw-r--r--src/test/ui/print_type_sizes/uninhabited.stdout5
11 files changed, 197 insertions, 92 deletions
diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs
index 899245b22aa..0edd8f44f0c 100644
--- a/src/librustc/ty/layout.rs
+++ b/src/librustc/ty/layout.rs
@@ -755,6 +755,7 @@ impl FieldPlacement {
 /// in terms of categories of C types there are ABI rules for.
 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
 pub enum Abi {
+    Uninhabited,
     Scalar(Scalar),
     Vector,
     Aggregate {
@@ -768,7 +769,7 @@ impl Abi {
     /// Returns true if the layout corresponds to an unsized type.
     pub fn is_unsized(&self) -> bool {
         match *self {
-            Abi::Scalar(_) | Abi::Vector => false,
+            Abi::Uninhabited | Abi::Scalar(_) | Abi::Vector => false,
             Abi::Aggregate { sized, .. } => !sized
         }
     }
@@ -776,7 +777,7 @@ impl Abi {
     /// Returns true if the fields of the layout are packed.
     pub fn is_packed(&self) -> bool {
         match *self {
-            Abi::Scalar(_) | Abi::Vector => false,
+            Abi::Uninhabited | Abi::Scalar(_) | Abi::Vector => false,
             Abi::Aggregate { packed, .. } => packed
         }
     }
@@ -807,6 +808,7 @@ pub enum Variants {
     /// `Some` is the identity function (with a non-null reference).
     NicheFilling {
         dataful_variant: usize,
+        niche_variant: usize,
         niche: Scalar,
         niche_value: u128,
         variants: Vec<CachedLayout>,
@@ -855,6 +857,18 @@ impl CachedLayout {
             primitive_align: align
         }
     }
+
+    fn uninhabited(field_count: usize) -> Self {
+        let align = Align::from_bytes(1, 1).unwrap();
+        CachedLayout {
+            variants: Variants::Single { index: 0 },
+            fields: FieldPlacement::Union(field_count),
+            abi: Abi::Uninhabited,
+            align,
+            primitive_align: align,
+            size: Size::from_bytes(0)
+        }
+    }
 }
 
 fn layout_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
@@ -915,13 +929,14 @@ impl<'a, 'tcx> CachedLayout {
                 bug!("struct cannot be packed and aligned");
             }
 
-            let mut align = if packed {
+            let base_align = if packed {
                 dl.i8_align
             } else {
                 dl.aggregate_align
             };
 
-            let mut primitive_align = align;
+            let mut align = base_align;
+            let mut primitive_align = base_align;
             let mut sized = true;
 
             // Anything with repr(C) or repr(packed) doesn't optimize.
@@ -978,13 +993,17 @@ impl<'a, 'tcx> CachedLayout {
                 }
             }
 
-            for i in inverse_memory_index.iter() {
-                let field = fields[*i as usize];
+            for &i in &inverse_memory_index {
+                let field = fields[i as usize];
                 if !sized {
                     bug!("univariant: field #{} of `{}` comes after unsized field",
                         offsets.len(), ty);
                 }
 
+                if field.abi == Abi::Uninhabited {
+                    return Ok(CachedLayout::uninhabited(fields.len()));
+                }
+
                 if field.is_unsized() {
                     sized = false;
                 }
@@ -997,7 +1016,7 @@ impl<'a, 'tcx> CachedLayout {
                 }
 
                 debug!("univariant offset: {:?} field: {:#?}", offset, field);
-                offsets[*i as usize] = offset;
+                offsets[i as usize] = offset;
 
                 offset = offset.checked_add(field.size, dl)
                     .ok_or(LayoutError::SizeOverflow(ty))?;
@@ -1124,7 +1143,7 @@ impl<'a, 'tcx> CachedLayout {
 
             // The never type.
             ty::TyNever => {
-                univariant(&[], &ReprOptions::default(), StructKind::AlwaysSized)?
+                tcx.intern_layout(CachedLayout::uninhabited(0))
             }
 
             // Potentially-fat pointers.
@@ -1278,11 +1297,15 @@ impl<'a, 'tcx> CachedLayout {
                     }).collect::<Result<Vec<_>, _>>()
                 }).collect::<Result<Vec<_>, _>>()?;
 
-                if variants.is_empty() {
-                    // Uninhabitable; represent as unit
-                    // (Typechecking will reject discriminant-sizing attrs.)
-
-                    return univariant(&[], &def.repr, StructKind::AlwaysSized);
+                let (inh_first, inh_second, inh_third) = {
+                    let mut inh_variants = (0..variants.len()).filter(|&v| {
+                        variants[v].iter().all(|f| f.abi != Abi::Uninhabited)
+                    });
+                    (inh_variants.next(), inh_variants.next(), inh_variants.next())
+                };
+                if inh_first.is_none() {
+                    // Uninhabited because it has no variants, or only uninhabited ones.
+                    return Ok(tcx.intern_layout(CachedLayout::uninhabited(0)));
                 }
 
                 if def.is_union() {
@@ -1329,49 +1352,58 @@ impl<'a, 'tcx> CachedLayout {
                     }));
                 }
 
-                if !def.is_enum() || (variants.len() == 1 &&
-                                      !def.repr.inhibit_enum_layout_opt() &&
-                                      !variants[0].is_empty()) {
-                    // Struct, or union, or univariant enum equivalent to a struct.
+                let is_struct = !def.is_enum() ||
+                    // Only one variant is inhabited.
+                    (inh_second.is_none() &&
+                    // Representation optimizations are allowed.
+                     !def.repr.inhibit_enum_layout_opt() &&
+                    // Inhabited variant either has data ...
+                     (!variants[inh_first.unwrap()].is_empty() ||
+                    // ... or there other, uninhabited, variants.
+                      variants.len() > 1));
+                if is_struct {
+                    // Struct, or univariant enum equivalent to a struct.
                     // (Typechecking will reject discriminant-sizing attrs.)
 
-                    let kind = if def.is_enum() || variants[0].len() == 0 {
+                    let v = inh_first.unwrap();
+                    let kind = if def.is_enum() || variants[v].len() == 0 {
                         StructKind::AlwaysSized
                     } else {
                         let param_env = tcx.param_env(def.did);
-                        let last_field = def.variants[0].fields.last().unwrap();
+                        let last_field = def.variants[v].fields.last().unwrap();
                         let always_sized = tcx.type_of(last_field.did)
                           .is_sized(tcx, param_env, DUMMY_SP);
                         if !always_sized { StructKind::MaybeUnsized }
                         else { StructKind::AlwaysSized }
                     };
 
-                    return univariant(&variants[0], &def.repr, kind);
+                    let mut st = univariant_uninterned(&variants[v], &def.repr, kind)?;
+                    st.variants = Variants::Single { index: v };
+                    return Ok(tcx.intern_layout(st));
                 }
 
                 let no_explicit_discriminants = def.variants.iter().enumerate()
                     .all(|(i, v)| v.discr == ty::VariantDiscr::Relative(i));
 
-                if variants.len() == 2 &&
+                if inh_second.is_some() && inh_third.is_none() &&
                    !def.repr.inhibit_enum_layout_opt() &&
                    no_explicit_discriminants {
                     // Nullable pointer optimization
-                    for i in 0..2 {
-                        if !variants[1 - i].iter().all(|f| f.is_zst()) {
+                    let (a, b) = (inh_first.unwrap(), inh_second.unwrap());
+                    for &(i, other) in &[(a, b), (b, a)] {
+                        if !variants[other].iter().all(|f| f.is_zst()) {
                             continue;
                         }
 
                         for (field_index, field) in variants[i].iter().enumerate() {
                             if let Some((offset, niche, niche_value)) = field.find_niche(cx)? {
-                                let mut st = vec![
-                                    univariant_uninterned(&variants[0],
-                                        &def.repr, StructKind::AlwaysSized)?,
-                                    univariant_uninterned(&variants[1],
-                                        &def.repr, StructKind::AlwaysSized)?
-                                ];
-                                for (i, v) in st.iter_mut().enumerate() {
-                                    v.variants = Variants::Single { index: i };
-                                }
+                                let st = variants.iter().enumerate().map(|(j, v)| {
+                                    let mut st = univariant_uninterned(v,
+                                        &def.repr, StructKind::AlwaysSized)?;
+                                    st.variants = Variants::Single { index: j };
+                                    Ok(st)
+                                }).collect::<Result<Vec<_>, _>>()?;
+
                                 let offset = st[i].fields.offset(field_index) + offset;
                                 let CachedLayout {
                                     size,
@@ -1400,6 +1432,7 @@ impl<'a, 'tcx> CachedLayout {
                                 return Ok(tcx.intern_layout(CachedLayout {
                                     variants: Variants::NicheFilling {
                                         dataful_variant: i,
+                                        niche_variant: other,
                                         niche,
                                         niche_value,
                                         variants: st,
@@ -1419,11 +1452,15 @@ impl<'a, 'tcx> CachedLayout {
                 }
 
                 let (mut min, mut max) = (i128::max_value(), i128::min_value());
-                for discr in def.discriminants(tcx) {
+                for (i, discr) in def.discriminants(tcx).enumerate() {
+                    if variants[i].iter().any(|f| f.abi == Abi::Uninhabited) {
+                        continue;
+                    }
                     let x = discr.to_u128_unchecked() as i128;
                     if x < min { min = x; }
                     if x > max { max = x; }
                 }
+                assert!(min <= max, "discriminant range is {}...{}", min, max);
                 let (min_ity, signed) = Integer::repr_discr(tcx, ty, &def.repr, min, max);
 
                 let mut align = dl.aggregate_align;
@@ -1498,6 +1535,9 @@ impl<'a, 'tcx> CachedLayout {
                     let old_ity_size = min_ity.size();
                     let new_ity_size = ity.size();
                     for variant in &mut variants {
+                        if variant.abi == Abi::Uninhabited {
+                            continue;
+                        }
                         match variant.fields {
                             FieldPlacement::Arbitrary { ref mut offsets, .. } => {
                                 for i in offsets {
@@ -1663,16 +1703,11 @@ impl<'a, 'tcx> CachedLayout {
         };
 
         match layout.variants {
-            Variants::Single { .. } => {
-                let variant_names = || {
-                    adt_def.variants.iter().map(|v|format!("{}", v.name)).collect::<Vec<_>>()
-                };
-                debug!("print-type-size `{:#?}` variants: {:?}",
-                       layout, variant_names());
-                assert!(adt_def.variants.len() <= 1,
-                        "univariant with variants {:?}", variant_names());
-                if adt_def.variants.len() == 1 {
-                    let variant_def = &adt_def.variants[0];
+            Variants::Single { index } => {
+                debug!("print-type-size `{:#?}` variant {}",
+                       layout, adt_def.variants[index].name);
+                if !adt_def.variants.is_empty() {
+                    let variant_def = &adt_def.variants[index];
                     let fields: Vec<_> =
                         variant_def.fields.iter().map(|f| f.name).collect();
                     record(adt_kind.into(),
@@ -1697,7 +1732,7 @@ impl<'a, 'tcx> CachedLayout {
                             variant_def.fields.iter().map(|f| f.name).collect();
                         build_variant_info(Some(variant_def.name),
                                             &fields,
-                                            layout.for_variant(i))
+                                            layout.for_variant(cx, i))
                     })
                     .collect();
                 record(adt_kind.into(), match layout.variants {
@@ -1989,15 +2024,35 @@ impl<'a, 'tcx> LayoutOf<Ty<'tcx>> for (ty::maps::TyCtxtAt<'a, 'tcx, 'tcx>,
 }
 
 impl<'a, 'tcx> TyLayout<'tcx> {
-    pub fn for_variant(&self, variant_index: usize) -> Self {
+    pub fn for_variant<C>(&self, cx: C, variant_index: usize) -> Self
+        where C: LayoutOf<Ty<'tcx>> + HasTyCtxt<'tcx>,
+              C::TyLayout: MaybeResult<TyLayout<'tcx>>
+    {
         let cached = match self.variants {
-            Variants::Single { .. } => self.cached,
+            Variants::Single { index } if index == variant_index => self.cached,
+
+            Variants::Single { index } => {
+                // Deny calling for_variant more than once for non-Single enums.
+                cx.layout_of(self.ty).map_same(|layout| {
+                    assert_eq!(layout.variants, Variants::Single { index });
+                    layout
+                });
+
+                let fields = match self.ty.sty {
+                    ty::TyAdt(def, _) => def.variants[variant_index].fields.len(),
+                    _ => bug!()
+                };
+                let mut cached = CachedLayout::uninhabited(fields);
+                cached.variants = Variants::Single { index: variant_index };
+                cx.tcx().intern_layout(cached)
+            }
 
             Variants::NicheFilling { ref variants, .. } |
             Variants::Tagged { ref variants, .. } => {
                 &variants[variant_index]
             }
         };
+
         assert_eq!(cached.variants, Variants::Single { index: variant_index });
 
         TyLayout {
@@ -2138,6 +2193,7 @@ impl<'a, 'tcx> TyLayout<'tcx> {
     /// Returns true if the type is a ZST and not unsized.
     pub fn is_zst(&self) -> bool {
         match self.abi {
+            Abi::Uninhabited => true,
             Abi::Scalar(_) => false,
             Abi::Vector => self.size.bytes() == 0,
             Abi::Aggregate { sized, .. } => sized && self.size.bytes() == 0
@@ -2241,11 +2297,13 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for Variants {
             }
             NicheFilling {
                 dataful_variant,
+                niche_variant,
                 ref niche,
                 niche_value,
                 ref variants,
             } => {
                 dataful_variant.hash_stable(hcx, hasher);
+                niche_variant.hash_stable(hcx, hasher);
                 niche.hash_stable(hcx, hasher);
                 niche_value.hash_stable(hcx, hasher);
                 variants.hash_stable(hcx, hasher);
@@ -2285,6 +2343,7 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for Abi {
         mem::discriminant(self).hash_stable(hcx, hasher);
 
         match *self {
+            Uninhabited => {}
             Scalar(ref value) => {
                 value.hash_stable(hcx, hasher);
             }
diff --git a/src/librustc_trans/abi.rs b/src/librustc_trans/abi.rs
index c87f856b005..c4b90d94dd4 100644
--- a/src/librustc_trans/abi.rs
+++ b/src/librustc_trans/abi.rs
@@ -278,6 +278,7 @@ pub trait LayoutExt<'tcx> {
 impl<'tcx> LayoutExt<'tcx> for TyLayout<'tcx> {
     fn is_aggregate(&self) -> bool {
         match self.abi {
+            layout::Abi::Uninhabited |
             layout::Abi::Scalar(_) |
             layout::Abi::Vector => false,
             layout::Abi::Aggregate { .. } => true
@@ -286,6 +287,8 @@ impl<'tcx> LayoutExt<'tcx> for TyLayout<'tcx> {
 
     fn homogeneous_aggregate<'a>(&self, ccx: &CrateContext<'a, 'tcx>) -> Option<Reg> {
         match self.abi {
+            layout::Abi::Uninhabited => None,
+
             // The primitive for this algorithm.
             layout::Abi::Scalar(ref scalar) => {
                 let kind = match scalar.value {
diff --git a/src/librustc_trans/cabi_x86_64.rs b/src/librustc_trans/cabi_x86_64.rs
index bc445c7d2a7..62540fac8b5 100644
--- a/src/librustc_trans/cabi_x86_64.rs
+++ b/src/librustc_trans/cabi_x86_64.rs
@@ -65,6 +65,8 @@ fn classify_arg<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &ArgType<'tcx>)
         }
 
         match layout.abi {
+            layout::Abi::Uninhabited => {}
+
             layout::Abi::Scalar(ref scalar) => {
                 let reg = match scalar.value {
                     layout::Int(..) |
diff --git a/src/librustc_trans/cabi_x86_win64.rs b/src/librustc_trans/cabi_x86_win64.rs
index ceb649be197..e93eeb83619 100644
--- a/src/librustc_trans/cabi_x86_win64.rs
+++ b/src/librustc_trans/cabi_x86_win64.rs
@@ -17,6 +17,7 @@ use rustc::ty::layout;
 pub fn compute_abi_info(fty: &mut FnType) {
     let fixup = |a: &mut ArgType| {
         match a.layout.abi {
+            layout::Abi::Uninhabited => {}
             layout::Abi::Aggregate { .. } => {
                 match a.layout.size.bits() {
                     8 => a.cast_to(Reg::i8()),
diff --git a/src/librustc_trans/debuginfo/metadata.rs b/src/librustc_trans/debuginfo/metadata.rs
index e0822b96eeb..25a35274d32 100644
--- a/src/librustc_trans/debuginfo/metadata.rs
+++ b/src/librustc_trans/debuginfo/metadata.rs
@@ -1130,43 +1130,38 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
                                       -> Vec<MemberDescription> {
         let adt = &self.enum_type.ty_adt_def().unwrap();
         match self.layout.variants {
-            layout::Variants::Single { .. } => {
-                assert!(adt.variants.len() <= 1);
-
-                if adt.variants.is_empty() {
-                    vec![]
-                } else {
-                    let (variant_type_metadata, member_description_factory) =
-                        describe_enum_variant(cx,
-                                              self.layout,
-                                              &adt.variants[0],
-                                              NoDiscriminant,
-                                              self.containing_scope,
-                                              self.span);
+            layout::Variants::Single { .. } if adt.variants.is_empty() => vec![],
+            layout::Variants::Single { index } => {
+                let (variant_type_metadata, member_description_factory) =
+                    describe_enum_variant(cx,
+                                          self.layout,
+                                          &adt.variants[index],
+                                          NoDiscriminant,
+                                          self.containing_scope,
+                                          self.span);
 
-                    let member_descriptions =
-                        member_description_factory.create_member_descriptions(cx);
+                let member_descriptions =
+                    member_description_factory.create_member_descriptions(cx);
 
-                    set_members_of_composite_type(cx,
-                                                  variant_type_metadata,
-                                                  &member_descriptions[..]);
-                    vec![
-                        MemberDescription {
-                            name: "".to_string(),
-                            type_metadata: variant_type_metadata,
-                            offset: Size::from_bytes(0),
-                            size: self.layout.size,
-                            align: self.layout.align,
-                            flags: DIFlags::FlagZero
-                        }
-                    ]
-                }
+                set_members_of_composite_type(cx,
+                                              variant_type_metadata,
+                                              &member_descriptions[..]);
+                vec![
+                    MemberDescription {
+                        name: "".to_string(),
+                        type_metadata: variant_type_metadata,
+                        offset: Size::from_bytes(0),
+                        size: self.layout.size,
+                        align: self.layout.align,
+                        flags: DIFlags::FlagZero
+                    }
+                ]
             }
             layout::Variants::Tagged { ref variants, .. } => {
                 let discriminant_info = RegularDiscriminant(self.discriminant_type_metadata
                     .expect(""));
                 (0..variants.len()).map(|i| {
-                    let variant = self.layout.for_variant(i);
+                    let variant = self.layout.for_variant(cx, i);
                     let (variant_type_metadata, member_desc_factory) =
                         describe_enum_variant(cx,
                                               variant,
@@ -1191,8 +1186,8 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
                     }
                 }).collect()
             }
-            layout::Variants::NicheFilling { dataful_variant, .. } => {
-                let variant = self.layout.for_variant(dataful_variant);
+            layout::Variants::NicheFilling { dataful_variant, niche_variant, .. } => {
+                let variant = self.layout.for_variant(cx, dataful_variant);
                 // Create a description of the non-null variant
                 let (variant_type_metadata, member_description_factory) =
                     describe_enum_variant(cx,
@@ -1236,7 +1231,7 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
                                    self.layout,
                                    self.layout.fields.offset(0),
                                    self.layout.field(cx, 0).size);
-                name.push_str(&adt.variants[1 - dataful_variant].name.as_str());
+                name.push_str(&adt.variants[niche_variant].name.as_str());
 
                 // Create the (singleton) list of descriptions of union members.
                 vec![
diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs
index 139c4c656db..d1b6e9073b8 100644
--- a/src/librustc_trans/mir/block.rs
+++ b/src/librustc_trans/mir/block.rs
@@ -710,7 +710,11 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
             Immediate(llval) => {
                 for i in 0..tuple.layout.fields.count() {
                     let field = tuple.layout.field(bcx.ccx, i);
-                    let elem = bcx.extract_value(llval, tuple.layout.llvm_field_index(i));
+                    let elem = if field.is_zst() {
+                        C_undef(field.llvm_type(bcx.ccx))
+                    } else {
+                        bcx.extract_value(llval, tuple.layout.llvm_field_index(i))
+                    };
                     // If the tuple is immediate, the elements are as well
                     let op = OperandRef {
                         val: Immediate(base::to_immediate(bcx, elem, field)),
diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs
index 7e1569c8f8f..542893bd62b 100644
--- a/src/librustc_trans/mir/constant.rs
+++ b/src/librustc_trans/mir/constant.rs
@@ -1099,6 +1099,11 @@ fn trans_const_adt<'a, 'tcx>(
         mir::AggregateKind::Adt(_, index, _, _) => index,
         _ => 0,
     };
+
+    if let layout::Abi::Uninhabited = l.abi {
+        return Const::new(C_undef(l.llvm_type(ccx)), t);
+    }
+
     match l.variants {
         layout::Variants::Single { index } => {
             assert_eq!(variant_index, index);
@@ -1114,7 +1119,6 @@ fn trans_const_adt<'a, 'tcx>(
 
                 Const::new(C_struct(ccx, &contents, l.is_packed()), t)
             } else {
-                assert_eq!(variant_index, 0);
                 build_const_struct(ccx, l, vals, None)
             }
         }
@@ -1132,12 +1136,12 @@ fn trans_const_adt<'a, 'tcx>(
                 Const::new(discr, t)
             } else {
                 let discr = Const::new(discr, discr_field.ty);
-                build_const_struct(ccx, l.for_variant(variant_index), vals, Some(discr))
+                build_const_struct(ccx, l.for_variant(ccx, variant_index), vals, Some(discr))
             }
         }
         layout::Variants::NicheFilling { dataful_variant, niche_value, .. } => {
             if variant_index == dataful_variant {
-                build_const_struct(ccx, l.for_variant(dataful_variant), vals, None)
+                build_const_struct(ccx, l.for_variant(ccx, dataful_variant), vals, None)
             } else {
                 let niche = l.field(ccx, 0);
                 let niche_llty = niche.llvm_type(ccx);
diff --git a/src/librustc_trans/mir/lvalue.rs b/src/librustc_trans/mir/lvalue.rs
index f9a179ee0ee..c6eb822ec87 100644
--- a/src/librustc_trans/mir/lvalue.rs
+++ b/src/librustc_trans/mir/lvalue.rs
@@ -115,7 +115,7 @@ impl<'a, 'tcx> LvalueRef<'tcx> {
                 assert_eq!(count, 0);
                 self.llextra
             } else {
-                common::C_usize(ccx, count)
+                C_usize(ccx, count)
             }
         } else {
             bug!("unexpected layout `{:#?}` in LvalueRef::len", self.layout)
@@ -304,7 +304,12 @@ impl<'a, 'tcx> LvalueRef<'tcx> {
                 };
                 bcx.intcast(lldiscr, cast_to, signed)
             }
-            layout::Variants::NicheFilling { dataful_variant, niche_value, .. } => {
+            layout::Variants::NicheFilling {
+                dataful_variant,
+                niche_variant,
+                niche_value,
+                ..
+            } => {
                 let niche_llty = discr.layout.llvm_type(bcx.ccx);
                 // FIXME(eddyb) Check the actual primitive type here.
                 let niche_llval = if niche_value == 0 {
@@ -313,8 +318,9 @@ impl<'a, 'tcx> LvalueRef<'tcx> {
                 } else {
                     C_uint_big(niche_llty, niche_value)
                 };
-                let cmp = if dataful_variant == 0 { llvm::IntEQ } else { llvm::IntNE };
-                bcx.intcast(bcx.icmp(cmp, lldiscr, niche_llval), cast_to, false)
+                bcx.select(bcx.icmp(llvm::IntEQ, lldiscr, niche_llval),
+                    C_uint(cast_to, niche_variant as u64),
+                    C_uint(cast_to, dataful_variant as u64))
             }
         }
     }
@@ -324,7 +330,12 @@ impl<'a, 'tcx> LvalueRef<'tcx> {
     pub fn trans_set_discr(&self, bcx: &Builder<'a, 'tcx>, variant_index: usize) {
         match self.layout.variants {
             layout::Variants::Single { index } => {
-                assert_eq!(variant_index, index);
+                if index != variant_index {
+                    // If the layout of an enum is `Single`, all
+                    // other variants are necessarily uninhabited.
+                    assert_eq!(self.layout.for_variant(bcx.ccx, variant_index).abi,
+                               layout::Abi::Uninhabited);
+                }
             }
             layout::Variants::Tagged { .. } => {
                 let ptr = self.project_field(bcx, 0);
@@ -366,7 +377,7 @@ impl<'a, 'tcx> LvalueRef<'tcx> {
     pub fn project_index(&self, bcx: &Builder<'a, 'tcx>, llindex: ValueRef)
                          -> LvalueRef<'tcx> {
         LvalueRef {
-            llval: bcx.inbounds_gep(self.llval, &[common::C_usize(bcx.ccx, 0), llindex]),
+            llval: bcx.inbounds_gep(self.llval, &[C_usize(bcx.ccx, 0), llindex]),
             llextra: ptr::null_mut(),
             layout: self.layout.field(bcx.ccx, 0),
             alignment: self.alignment
@@ -376,7 +387,7 @@ impl<'a, 'tcx> LvalueRef<'tcx> {
     pub fn project_downcast(&self, bcx: &Builder<'a, 'tcx>, variant_index: usize)
                             -> LvalueRef<'tcx> {
         let mut downcast = *self;
-        downcast.layout = self.layout.for_variant(variant_index);
+        downcast.layout = self.layout.for_variant(bcx.ccx, variant_index);
 
         // Cast to the appropriate variant struct type.
         let variant_ty = downcast.layout.llvm_type(bcx.ccx);
diff --git a/src/librustc_trans/type_of.rs b/src/librustc_trans/type_of.rs
index eab5cb159de..d2f9ca35468 100644
--- a/src/librustc_trans/type_of.rs
+++ b/src/librustc_trans/type_of.rs
@@ -27,6 +27,7 @@ fn uncached_llvm_type<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
             return Type::vector(&layout.field(ccx, 0).llvm_type(ccx),
                                 layout.fields.count() as u64);
         }
+        layout::Abi::Uninhabited |
         layout::Abi::Aggregate { .. } => {}
     }
 
@@ -158,7 +159,9 @@ pub trait LayoutLlvmExt<'tcx> {
 impl<'tcx> LayoutLlvmExt<'tcx> for TyLayout<'tcx> {
     fn is_llvm_immediate(&self) -> bool {
         match self.abi {
-            layout::Abi::Scalar(_) | layout::Abi::Vector => true,
+            layout::Abi::Uninhabited |
+            layout::Abi::Scalar(_) |
+            layout::Abi::Vector => true,
 
             layout::Abi::Aggregate { .. } => self.is_zst()
         }
@@ -230,7 +233,7 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyLayout<'tcx> {
         let llty = if self.ty != normal_ty {
             let mut layout = ccx.layout_of(normal_ty);
             if let Some(v) = variant_index {
-                layout = layout.for_variant(v);
+                layout = layout.for_variant(ccx, v);
             }
             layout.llvm_type(ccx)
         } else {
diff --git a/src/test/ui/print_type_sizes/uninhabited.rs b/src/test/ui/print_type_sizes/uninhabited.rs
new file mode 100644
index 00000000000..69cc4c93360
--- /dev/null
+++ b/src/test/ui/print_type_sizes/uninhabited.rs
@@ -0,0 +1,18 @@
+// 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.
+
+// compile-flags: -Z print-type-sizes
+
+#![feature(never_type)]
+
+pub fn main() {
+    let _x: Option<!> = None;
+    let _y: Result<u32, !> = Ok(42);
+}
diff --git a/src/test/ui/print_type_sizes/uninhabited.stdout b/src/test/ui/print_type_sizes/uninhabited.stdout
new file mode 100644
index 00000000000..2a8706f7ac5
--- /dev/null
+++ b/src/test/ui/print_type_sizes/uninhabited.stdout
@@ -0,0 +1,5 @@
+print-type-size type: `std::result::Result<u32, !>`: 4 bytes, alignment: 4 bytes
+print-type-size     variant `Ok`: 4 bytes
+print-type-size         field `.0`: 4 bytes
+print-type-size type: `std::option::Option<!>`: 0 bytes, alignment: 1 bytes
+print-type-size     variant `None`: 0 bytes