about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorEduard-Mihai Burtescu <edy.burt@gmail.com>2017-09-26 14:41:06 +0300
committerEduard-Mihai Burtescu <edy.burt@gmail.com>2017-11-19 02:14:33 +0200
commitf62e43da2891a65a484a917d84642544ed093ba2 (patch)
tree5b0338f9070e2cbe50dd8bde7b25d74e1b412e4f /src
parent5df25c4aed68a4f761645f63e6ce34ec8c30a75e (diff)
rustc: track validity ranges for layout::Abi::Scalar values.
Diffstat (limited to 'src')
-rw-r--r--src/librustc/ty/layout.rs301
-rw-r--r--src/librustc_lint/types.rs4
-rw-r--r--src/librustc_trans/abi.rs19
-rw-r--r--src/librustc_trans/base.rs9
-rw-r--r--src/librustc_trans/cabi_s390x.rs8
-rw-r--r--src/librustc_trans/cabi_x86.rs8
-rw-r--r--src/librustc_trans/cabi_x86_64.rs4
-rw-r--r--src/librustc_trans/debuginfo/metadata.rs8
-rw-r--r--src/librustc_trans/lib.rs1
-rw-r--r--src/librustc_trans/mir/block.rs13
-rw-r--r--src/librustc_trans/mir/constant.rs16
-rw-r--r--src/librustc_trans/mir/lvalue.rs71
-rw-r--r--src/librustc_trans/mir/rvalue.rs36
-rw-r--r--src/librustc_trans/type_.rs1
-rw-r--r--src/librustc_trans/type_of.rs14
15 files changed, 294 insertions, 219 deletions
diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs
index 9d8736338f1..899245b22aa 100644
--- a/src/librustc/ty/layout.rs
+++ b/src/librustc/ty/layout.rs
@@ -416,7 +416,6 @@ impl Align {
 /// Integers, also used for enum discriminants.
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
 pub enum Integer {
-    I1,
     I8,
     I16,
     I32,
@@ -427,7 +426,6 @@ pub enum Integer {
 impl<'a, 'tcx> Integer {
     pub fn size(&self) -> Size {
         match *self {
-            I1 => Size::from_bits(1),
             I8 => Size::from_bytes(1),
             I16 => Size::from_bytes(2),
             I32 => Size::from_bytes(4),
@@ -440,7 +438,6 @@ impl<'a, 'tcx> Integer {
         let dl = cx.data_layout();
 
         match *self {
-            I1 => dl.i1_align,
             I8 => dl.i8_align,
             I16 => dl.i16_align,
             I32 => dl.i32_align,
@@ -451,13 +448,11 @@ impl<'a, 'tcx> Integer {
 
     pub fn to_ty(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, signed: bool) -> Ty<'tcx> {
         match (*self, signed) {
-            (I1, false) => tcx.types.u8,
             (I8, false) => tcx.types.u8,
             (I16, false) => tcx.types.u16,
             (I32, false) => tcx.types.u32,
             (I64, false) => tcx.types.u64,
             (I128, false) => tcx.types.u128,
-            (I1, true) => tcx.types.i8,
             (I8, true) => tcx.types.i8,
             (I16, true) => tcx.types.i16,
             (I32, true) => tcx.types.i32,
@@ -469,7 +464,6 @@ impl<'a, 'tcx> Integer {
     /// Find the smallest Integer type which can represent the signed value.
     pub fn fit_signed(x: i128) -> Integer {
         match x {
-            -0x0000_0000_0000_0001...0x0000_0000_0000_0000 => I1,
             -0x0000_0000_0000_0080...0x0000_0000_0000_007f => I8,
             -0x0000_0000_0000_8000...0x0000_0000_0000_7fff => I16,
             -0x0000_0000_8000_0000...0x0000_0000_7fff_ffff => I32,
@@ -481,7 +475,6 @@ impl<'a, 'tcx> Integer {
     /// Find the smallest Integer type which can represent the unsigned value.
     pub fn fit_unsigned(x: u128) -> Integer {
         match x {
-            0...0x0000_0000_0000_0001 => I1,
             0...0x0000_0000_0000_00ff => I8,
             0...0x0000_0000_0000_ffff => I16,
             0...0x0000_0000_ffff_ffff => I32,
@@ -621,6 +614,29 @@ impl<'a, 'tcx> Primitive {
     }
 }
 
+/// Information about one scalar component of a Rust type.
+#[derive(Clone, PartialEq, Eq, Hash, Debug)]
+pub struct Scalar {
+    pub value: Primitive,
+
+    /// Inclusive wrap-around range of valid values, that is, if
+    /// min > max, it represents min..=u128::MAX followed by 0..=max.
+    // FIXME(eddyb) always use the shortest range, e.g. by finding
+    // the largest space between two consecutive valid values and
+    // taking everything else as the (shortest) valid range.
+    pub valid_range: RangeInclusive<u128>,
+}
+
+impl Scalar {
+    pub fn is_bool(&self) -> bool {
+        if let Int(I8, _) = self.value {
+            self.valid_range == (0..=1)
+        } else {
+            false
+        }
+    }
+}
+
 /// 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.
@@ -737,9 +753,9 @@ impl FieldPlacement {
 
 /// 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, PartialEq, Eq, Hash, Debug)]
+#[derive(Clone, PartialEq, Eq, Hash, Debug)]
 pub enum Abi {
-    Scalar(Primitive),
+    Scalar(Scalar),
     Vector,
     Aggregate {
         /// If true, the size is exact, otherwise it's only a lower bound.
@@ -777,13 +793,7 @@ pub enum Variants {
     /// all space reserved for the discriminant, and their first field starts
     /// at a non-0 offset, after where the discriminant would go.
     Tagged {
-        discr: Primitive,
-        /// Inclusive wrap-around range of discriminant values, that is,
-        /// if min > max, it represents min..=u128::MAX followed by 0..=max.
-        // FIXME(eddyb) always use the shortest range, e.g. by finding
-        // the largest space between two consecutive discriminants and
-        // taking everything else as the (shortest) discriminant range.
-        discr_range: RangeInclusive<u128>,
+        discr: Scalar,
         variants: Vec<CachedLayout>,
     },
 
@@ -797,7 +807,7 @@ pub enum Variants {
     /// `Some` is the identity function (with a non-null reference).
     NicheFilling {
         dataful_variant: usize,
-        niche: Primitive,
+        niche: Scalar,
         niche_value: u128,
         variants: Vec<CachedLayout>,
     }
@@ -832,6 +842,21 @@ pub struct CachedLayout {
     pub size: Size
 }
 
+impl CachedLayout {
+    fn scalar<C: HasDataLayout>(cx: C, scalar: Scalar) -> Self {
+        let size = scalar.value.size(cx);
+        let align = scalar.value.align(cx);
+        CachedLayout {
+            variants: Variants::Single { index: 0 },
+            fields: FieldPlacement::Union(0),
+            abi: Abi::Scalar(scalar),
+            size,
+            align,
+            primitive_align: align
+        }
+    }
+}
+
 fn layout_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                         query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>)
                         -> Result<&'tcx CachedLayout, LayoutError<'tcx>>
@@ -867,16 +892,14 @@ impl<'a, 'tcx> CachedLayout {
         let cx = (tcx, param_env);
         let dl = cx.data_layout();
         let scalar = |value: Primitive| {
-            let align = value.align(dl);
-            tcx.intern_layout(CachedLayout {
-                variants: Variants::Single { index: 0 },
-                fields: FieldPlacement::Union(0),
-                abi: Abi::Scalar(value),
-                size: value.size(dl),
-                align,
-                primitive_align: align
-            })
+            let bits = value.size(dl).bits();
+            assert!(bits <= 128);
+            tcx.intern_layout(CachedLayout::scalar(cx, Scalar {
+                value,
+                valid_range: 0..=(!0 >> (128 - bits))
+            }))
         };
+
         #[derive(Copy, Clone, Debug)]
         enum StructKind {
             /// A tuple, closure, or univariant which cannot be coerced to unsized.
@@ -1030,7 +1053,12 @@ impl<'a, 'tcx> CachedLayout {
         let ptr_layout = |pointee: Ty<'tcx>| {
             let pointee = tcx.normalize_associated_type_in_env(&pointee, param_env);
             if pointee.is_sized(tcx, param_env, DUMMY_SP) {
-                return Ok(scalar(Pointer));
+                let non_zero = !ty.is_unsafe_ptr();
+                let bits = Pointer.size(dl).bits();
+                return Ok(tcx.intern_layout(CachedLayout::scalar(cx, Scalar {
+                    value: Pointer,
+                    valid_range: (non_zero as u128)..=(!0 >> (128 - bits))
+                })));
             }
 
             let unsized_part = tcx.struct_tail(pointee);
@@ -1066,8 +1094,18 @@ impl<'a, 'tcx> CachedLayout {
 
         Ok(match ty.sty {
             // Basic scalars.
-            ty::TyBool => scalar(Int(I1, false)),
-            ty::TyChar => scalar(Int(I32, false)),
+            ty::TyBool => {
+                tcx.intern_layout(CachedLayout::scalar(cx, Scalar {
+                    value: Int(I8, false),
+                    valid_range: 0..=1
+                }))
+            }
+            ty::TyChar => {
+                tcx.intern_layout(CachedLayout::scalar(cx, Scalar {
+                    value: Int(I32, false),
+                    valid_range: 0..=0x10FFFF
+                }))
+            }
             ty::TyInt(ity) => {
                 scalar(Int(Integer::from_attr(dl, attr::SignedInt(ity)), true))
             }
@@ -1076,7 +1114,13 @@ impl<'a, 'tcx> CachedLayout {
             }
             ty::TyFloat(FloatTy::F32) => scalar(F32),
             ty::TyFloat(FloatTy::F64) => scalar(F64),
-            ty::TyFnPtr(_) => scalar(Pointer),
+            ty::TyFnPtr(_) => {
+                let bits = Pointer.size(dl).bits();
+                tcx.intern_layout(CachedLayout::scalar(cx, Scalar {
+                    value: Pointer,
+                    valid_range: 1..=(!0 >> (128 - bits))
+                }))
+            }
 
             // The never type.
             ty::TyNever => {
@@ -1330,22 +1374,26 @@ impl<'a, 'tcx> CachedLayout {
                                 }
                                 let offset = st[i].fields.offset(field_index) + offset;
                                 let CachedLayout {
-                                    mut abi,
                                     size,
                                     mut align,
                                     mut primitive_align,
                                     ..
                                 } = st[i];
 
-                                let mut niche_align = niche.align(dl);
-                                if offset.bytes() == 0 && niche.size(dl) == size {
-                                    abi = Abi::Scalar(niche);
-                                } else if let Abi::Aggregate { ref mut packed, .. } = abi {
+                                let mut niche_align = niche.value.align(dl);
+                                let abi = if offset.bytes() == 0 && niche.value.size(dl) == size {
+                                    Abi::Scalar(niche.clone())
+                                } else {
+                                    let mut packed = st[i].abi.is_packed();
                                     if offset.abi_align(niche_align) != offset {
-                                        *packed = true;
+                                        packed = true;
                                         niche_align = dl.i8_align;
                                     }
-                                }
+                                    Abi::Aggregate {
+                                       sized: true,
+                                       packed
+                                    }
+                                };
                                 align = align.max(niche_align);
                                 primitive_align = primitive_align.max(niche_align);
 
@@ -1468,25 +1516,28 @@ impl<'a, 'tcx> CachedLayout {
                     }
                 }
 
-                let discr = Int(ity, signed);
+                let discr = Scalar {
+                    value: Int(ity, signed),
+                    valid_range: (min as u128)..=(max as u128)
+                };
+                let abi = if discr.value.size(dl) == size {
+                    Abi::Scalar(discr.clone())
+                } else {
+                    Abi::Aggregate {
+                        sized: true,
+                        packed: false
+                    }
+                };
                 tcx.intern_layout(CachedLayout {
                     variants: Variants::Tagged {
                         discr,
-                        discr_range: (min as u128)..=(max as u128),
                         variants
                     },
                     // FIXME(eddyb): using `FieldPlacement::Arbitrary` here results
                     // in lost optimizations, specifically around allocations, see
                     // `test/codegen/{alloc-optimisation,vec-optimizes-away}.rs`.
                     fields: FieldPlacement::Union(1),
-                    abi: if discr.size(dl) == size {
-                        Abi::Scalar(discr)
-                    } else {
-                        Abi::Aggregate {
-                            sized: true,
-                            packed: false
-                        }
-                    },
+                    abi,
                     align,
                     primitive_align,
                     size
@@ -1650,7 +1701,7 @@ impl<'a, 'tcx> CachedLayout {
                     })
                     .collect();
                 record(adt_kind.into(), match layout.variants {
-                    Variants::Tagged { discr, .. } => Some(discr.size(tcx)),
+                    Variants::Tagged { ref discr, .. } => Some(discr.value.size(tcx)),
                     _ => None
                 }, variant_infos);
             }
@@ -1852,16 +1903,23 @@ impl<'a, 'gcx, 'tcx, T: Copy> HasTyCtxt<'gcx> for (TyCtxt<'a, 'gcx, 'tcx>, T) {
 }
 
 pub trait MaybeResult<T> {
+    fn from_ok(x: T) -> Self;
     fn map_same<F: FnOnce(T) -> T>(self, f: F) -> Self;
 }
 
 impl<T> MaybeResult<T> for T {
+    fn from_ok(x: T) -> Self {
+        x
+    }
     fn map_same<F: FnOnce(T) -> T>(self, f: F) -> Self {
         f(self)
     }
 }
 
 impl<T, E> MaybeResult<T> for Result<T, E> {
+    fn from_ok(x: T) -> Self {
+        Ok(x)
+    }
     fn map_same<F: FnOnce(T) -> T>(self, f: F) -> Self {
         self.map(f)
     }
@@ -1961,7 +2019,13 @@ impl<'a, 'tcx> TyLayout<'tcx> {
             // (which may have no non-DST form), and will work as long
             // as the `Abi` or `FieldPlacement` is checked by users.
             if i == 0 {
-                return cx.layout_of(Pointer.to_ty(tcx)).map_same(|mut ptr_layout| {
+                let nil = tcx.mk_nil();
+                let ptr_ty = if self.ty.is_unsafe_ptr() {
+                    tcx.mk_mut_ptr(nil)
+                } else {
+                    tcx.mk_mut_ref(tcx.types.re_static, nil)
+                };
+                return cx.layout_of(ptr_ty).map_same(|mut ptr_layout| {
                     ptr_layout.ty = self.ty;
                     ptr_layout
                 });
@@ -2042,9 +2106,14 @@ impl<'a, 'tcx> TyLayout<'tcx> {
                     }
 
                     // Discriminant field for enums (where applicable).
-                    Variants::Tagged { discr, .. } |
-                    Variants::NicheFilling { niche: discr, .. } => {
-                        return cx.layout_of([discr.to_ty(tcx)][i]);
+                    Variants::Tagged { ref discr, .. } |
+                    Variants::NicheFilling { niche: ref discr, .. } => {
+                        assert_eq!(i, 0);
+                        let layout = CachedLayout::scalar(tcx, discr.clone());
+                        return MaybeResult::from_ok(TyLayout {
+                            cached: tcx.intern_layout(layout),
+                            ty: discr.value.to_ty(tcx)
+                        });
                     }
                 }
             }
@@ -2081,79 +2150,74 @@ impl<'a, 'tcx> TyLayout<'tcx> {
 
     /// Find the offset of a niche leaf field, starting from
     /// the given type and recursing through aggregates.
-    /// The tuple is `(offset, primitive, niche_value)`.
-    // FIXME(eddyb) track value ranges and traverse already optimized enums.
+    /// The tuple is `(offset, scalar, niche_value)`.
+    // FIXME(eddyb) traverse already optimized enums.
     fn find_niche<C>(&self, cx: C)
-        -> Result<Option<(Size, Primitive, u128)>, LayoutError<'tcx>>
+        -> Result<Option<(Size, Scalar, u128)>, LayoutError<'tcx>>
         where C: LayoutOf<Ty<'tcx>, TyLayout = Result<Self, LayoutError<'tcx>>> +
                  HasTyCtxt<'tcx>
     {
-        let tcx = cx.tcx();
-        match (&self.variants, self.abi, &self.ty.sty) {
-            // FIXME(eddyb) check this via value ranges on scalars.
-            (_, Abi::Scalar(Int(I1, _)), _) => {
-                Ok(Some((Size::from_bytes(0), Int(I8, false), 2)))
-            }
-            (_, Abi::Scalar(Int(I32, _)), &ty::TyChar) => {
-                Ok(Some((Size::from_bytes(0), Int(I32, false), 0x10FFFF+1)))
-            }
-            (_, Abi::Scalar(Pointer), &ty::TyRef(..)) |
-            (_, Abi::Scalar(Pointer), &ty::TyFnPtr(..)) => {
-                Ok(Some((Size::from_bytes(0), Pointer, 0)))
-            }
-            (_, Abi::Scalar(Pointer), &ty::TyAdt(def, _)) if def.is_box() => {
-                Ok(Some((Size::from_bytes(0), Pointer, 0)))
-            }
-
-            // FIXME(eddyb) check this via value ranges on scalars.
-            (&Variants::Tagged { discr, ref discr_range, .. }, _, _) => {
-                // FIXME(eddyb) support negative/wrap-around discriminant ranges.
-                if discr_range.start < discr_range.end {
-                    if discr_range.start > 0 {
-                        Ok(Some((self.fields.offset(0), discr, 0)))
-                    } else {
-                        let bits = discr.size(tcx).bits();
-                        assert!(bits <= 128);
-                        let max_value = !0u128 >> (128 - bits);
-                        if discr_range.end < max_value {
-                            Ok(Some((self.fields.offset(0), discr, discr_range.end + 1)))
-                        } else {
-                            Ok(None)
-                        }
-                    }
+        if let Abi::Scalar(Scalar { value, ref valid_range }) = self.abi {
+            // FIXME(eddyb) support negative/wrap-around discriminant ranges.
+            return if valid_range.start < valid_range.end {
+                let bits = value.size(cx).bits();
+                assert!(bits <= 128);
+                let max_value = !0u128 >> (128 - bits);
+                if valid_range.start > 0 {
+                    let niche = valid_range.start - 1;
+                    Ok(Some((self.fields.offset(0), Scalar {
+                        value,
+                        valid_range: niche..=valid_range.end
+                    }, niche)))
+                } else if valid_range.end < max_value {
+                    let niche = valid_range.end + 1;
+                    Ok(Some((self.fields.offset(0), Scalar {
+                        value,
+                        valid_range: valid_range.start..=niche
+                    }, niche)))
                 } else {
                     Ok(None)
                 }
-            }
+            } else {
+                Ok(None)
+            };
+        }
 
-            // Is this the NonZero lang item wrapping a pointer or integer type?
-            (_, _, &ty::TyAdt(def, _)) if Some(def.did) == tcx.lang_items().non_zero() => {
+        // Is this the NonZero lang item wrapping a pointer or integer type?
+        if let ty::TyAdt(def, _) = self.ty.sty {
+            if Some(def.did) == cx.tcx().lang_items().non_zero() {
                 let field = self.field(cx, 0)?;
                 let offset = self.fields.offset(0);
-                if let Abi::Scalar(value) = field.abi {
-                    Ok(Some((offset, value, 0)))
-                } else {
-                    Ok(None)
+                if let Abi::Scalar(Scalar { value, ref valid_range }) = field.abi {
+                    return Ok(Some((offset, Scalar {
+                        value,
+                        valid_range: 0..=valid_range.end
+                    }, 0)));
                 }
             }
+        }
 
-            // Perhaps one of the fields is non-zero, let's recurse and find out.
-            _ => {
-                if let FieldPlacement::Array { count, .. } = self.fields {
-                    if count > 0 {
-                        return self.field(cx, 0)?.find_niche(cx);
-                    }
-                }
-                for i in 0..self.fields.count() {
-                    let r = self.field(cx, i)?.find_niche(cx)?;
-                    if let Some((offset, primitive, niche_value)) = r {
-                        let offset = self.fields.offset(i) + offset;
-                        return Ok(Some((offset, primitive, niche_value)));
-                    }
-                }
-                Ok(None)
+        // Perhaps one of the fields is non-zero, let's recurse and find out.
+        if let FieldPlacement::Union(_) = self.fields {
+            // Only Rust enums have safe-to-inspect fields
+            // (a discriminant), other unions are unsafe.
+            if let Variants::Single { .. } = self.variants {
+                return Ok(None);
+            }
+        }
+        if let FieldPlacement::Array { count, .. } = self.fields {
+            if count > 0 {
+                return self.field(cx, 0)?.find_niche(cx);
             }
         }
+        for i in 0..self.fields.count() {
+            let r = self.field(cx, i)?.find_niche(cx)?;
+            if let Some((offset, scalar, niche_value)) = r {
+                let offset = self.fields.offset(i) + offset;
+                return Ok(Some((offset, scalar, niche_value)));
+            }
+        }
+        Ok(None)
     }
 }
 
@@ -2169,13 +2233,10 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for Variants {
                 index.hash_stable(hcx, hasher);
             }
             Tagged {
-                discr,
-                discr_range: RangeInclusive { start, end },
+                ref discr,
                 ref variants,
             } => {
                 discr.hash_stable(hcx, hasher);
-                start.hash_stable(hcx, hasher);
-                end.hash_stable(hcx, hasher);
                 variants.hash_stable(hcx, hasher);
             }
             NicheFilling {
@@ -2236,6 +2297,17 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for Abi {
     }
 }
 
+impl<'gcx> HashStable<StableHashingContext<'gcx>> for Scalar {
+    fn hash_stable<W: StableHasherResult>(&self,
+                                          hcx: &mut StableHashingContext<'gcx>,
+                                          hasher: &mut StableHasher<W>) {
+        let Scalar { value, valid_range: RangeInclusive { start, end } } = *self;
+        value.hash_stable(hcx, hasher);
+        start.hash_stable(hcx, hasher);
+        end.hash_stable(hcx, hasher);
+    }
+}
+
 impl_stable_hash_for!(struct ::ty::layout::CachedLayout {
     variants,
     fields,
@@ -2246,7 +2318,6 @@ impl_stable_hash_for!(struct ::ty::layout::CachedLayout {
 });
 
 impl_stable_hash_for!(enum ::ty::layout::Integer {
-    I1,
     I8,
     I16,
     I32,
diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs
index 46debcce958..1356574f646 100644
--- a/src/librustc_lint/types.rs
+++ b/src/librustc_lint/types.rs
@@ -753,8 +753,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for VariantSizeDifferences {
                     bug!("failed to get layout for `{}`: {}", t, e)
                 });
 
-                if let layout::Variants::Tagged { ref variants, discr, .. } = layout.variants {
-                    let discr_size = discr.size(cx.tcx).bytes();
+                if let layout::Variants::Tagged { ref variants, ref discr, .. } = layout.variants {
+                    let discr_size = discr.value.size(cx.tcx).bytes();
 
                     debug!("enum `{}` is {} bytes large with layout:\n{:#?}",
                       t, layout.size.bytes(), layout);
diff --git a/src/librustc_trans/abi.rs b/src/librustc_trans/abi.rs
index 688fa8fe02d..c87f856b005 100644
--- a/src/librustc_trans/abi.rs
+++ b/src/librustc_trans/abi.rs
@@ -287,8 +287,8 @@ impl<'tcx> LayoutExt<'tcx> for TyLayout<'tcx> {
     fn homogeneous_aggregate<'a>(&self, ccx: &CrateContext<'a, 'tcx>) -> Option<Reg> {
         match self.abi {
             // The primitive for this algorithm.
-            layout::Abi::Scalar(value) => {
-                let kind = match value {
+            layout::Abi::Scalar(ref scalar) => {
+                let kind = match scalar.value {
                     layout::Int(..) |
                     layout::Pointer => RegKind::Integer,
                     layout::F32 |
@@ -471,8 +471,8 @@ impl<'a, 'tcx> ArgType<'tcx> {
 
     pub fn extend_integer_width_to(&mut self, bits: u64) {
         // Only integers have signedness
-        match self.layout.abi {
-            layout::Abi::Scalar(layout::Int(i, signed)) => {
+        if let layout::Abi::Scalar(ref scalar) = self.layout.abi {
+            if let layout::Int(i, signed) = scalar.value {
                 if i.size().bits() < bits {
                     self.attrs.set(if signed {
                         ArgAttribute::SExt
@@ -481,8 +481,6 @@ impl<'a, 'tcx> ArgType<'tcx> {
                     });
                 }
             }
-
-            _ => {}
         }
     }
 
@@ -695,9 +693,12 @@ impl<'a, 'tcx> FnType<'tcx> {
 
         let arg_of = |ty: Ty<'tcx>, is_return: bool| {
             let mut arg = ArgType::new(ccx.layout_of(ty));
-            if let layout::Abi::Scalar(layout::Int(layout::I1, _)) = arg.layout.abi {
-                arg.attrs.set(ArgAttribute::ZExt);
-            } else if arg.layout.is_zst() {
+            if let layout::Abi::Scalar(ref scalar) = arg.layout.abi {
+                if scalar.is_bool() {
+                    arg.attrs.set(ArgAttribute::ZExt);
+                }
+            }
+            if arg.layout.is_zst() {
                 // For some forsaken reason, x86_64-pc-windows-gnu
                 // doesn't ignore zero-sized struct arguments.
                 // The same is true for s390x-unknown-linux-gnu.
diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs
index 3c6626cfa7f..ff70184b262 100644
--- a/src/librustc_trans/base.rs
+++ b/src/librustc_trans/base.rs
@@ -375,11 +375,12 @@ pub fn from_immediate(bcx: &Builder, val: ValueRef) -> ValueRef {
 }
 
 pub fn to_immediate(bcx: &Builder, val: ValueRef, layout: layout::TyLayout) -> ValueRef {
-    if let layout::Abi::Scalar(layout::Int(layout::I1, _)) = layout.abi {
-        bcx.trunc(val, Type::i1(bcx.ccx))
-    } else {
-        val
+    if let layout::Abi::Scalar(ref scalar) = layout.abi {
+        if scalar.is_bool() {
+            return bcx.trunc(val, Type::i1(bcx.ccx));
+        }
     }
+    val
 }
 
 pub fn call_memcpy(b: &Builder,
diff --git a/src/librustc_trans/cabi_s390x.rs b/src/librustc_trans/cabi_s390x.rs
index 9c24b637efd..9fb460043ae 100644
--- a/src/librustc_trans/cabi_s390x.rs
+++ b/src/librustc_trans/cabi_s390x.rs
@@ -27,8 +27,12 @@ fn classify_ret_ty(ret: &mut ArgType) {
 fn is_single_fp_element<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                                   layout: TyLayout<'tcx>) -> bool {
     match layout.abi {
-        layout::Abi::Scalar(layout::F32) |
-        layout::Abi::Scalar(layout::F64) => true,
+        layout::Abi::Scalar(ref scalar) => {
+            match scalar.value {
+                layout::F32 | layout::F64 => true,
+                _ => false
+            }
+        }
         layout::Abi::Aggregate { .. } => {
             if layout.fields.count() == 1 && layout.fields.offset(0).bytes() == 0 {
                 is_single_fp_element(ccx, layout.field(ccx, 0))
diff --git a/src/librustc_trans/cabi_x86.rs b/src/librustc_trans/cabi_x86.rs
index 401e75387c4..dc9f681af52 100644
--- a/src/librustc_trans/cabi_x86.rs
+++ b/src/librustc_trans/cabi_x86.rs
@@ -22,8 +22,12 @@ pub enum Flavor {
 fn is_single_fp_element<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                                   layout: TyLayout<'tcx>) -> bool {
     match layout.abi {
-        layout::Abi::Scalar(layout::F32) |
-        layout::Abi::Scalar(layout::F64) => true,
+        layout::Abi::Scalar(ref scalar) => {
+            match scalar.value {
+                layout::F32 | layout::F64 => true,
+                _ => false
+            }
+        }
         layout::Abi::Aggregate { .. } => {
             if layout.fields.count() == 1 && layout.fields.offset(0).bytes() == 0 {
                 is_single_fp_element(ccx, layout.field(ccx, 0))
diff --git a/src/librustc_trans/cabi_x86_64.rs b/src/librustc_trans/cabi_x86_64.rs
index b799a7690bd..bc445c7d2a7 100644
--- a/src/librustc_trans/cabi_x86_64.rs
+++ b/src/librustc_trans/cabi_x86_64.rs
@@ -65,8 +65,8 @@ fn classify_arg<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &ArgType<'tcx>)
         }
 
         match layout.abi {
-            layout::Abi::Scalar(value) => {
-                let reg = match value {
+            layout::Abi::Scalar(ref scalar) => {
+                let reg = match scalar.value {
                     layout::Int(..) |
                     layout::Pointer => Class::Int,
                     layout::F32 |
diff --git a/src/librustc_trans/debuginfo/metadata.rs b/src/librustc_trans/debuginfo/metadata.rs
index 2768c7fb577..e0822b96eeb 100644
--- a/src/librustc_trans/debuginfo/metadata.rs
+++ b/src/librustc_trans/debuginfo/metadata.rs
@@ -1429,11 +1429,13 @@ fn prepare_enum_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
     let discriminant_type_metadata = match layout.variants {
         layout::Variants::Single { .. } |
         layout::Variants::NicheFilling { .. } => None,
-        layout::Variants::Tagged { discr, .. } => Some(discriminant_type_metadata(discr)),
+        layout::Variants::Tagged { ref discr, .. } => {
+            Some(discriminant_type_metadata(discr.value))
+        }
     };
 
-    match (layout.abi, discriminant_type_metadata) {
-        (layout::Abi::Scalar(_), Some(discr)) => return FinalMetadata(discr),
+    match (&layout.abi, discriminant_type_metadata) {
+        (&layout::Abi::Scalar(_), Some(discr)) => return FinalMetadata(discr),
         _ => {}
     }
 
diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs
index 83fc1017316..f6c4153c183 100644
--- a/src/librustc_trans/lib.rs
+++ b/src/librustc_trans/lib.rs
@@ -26,6 +26,7 @@
 #![feature(i128_type)]
 #![feature(i128)]
 #![feature(inclusive_range)]
+#![feature(inclusive_range_syntax)]
 #![feature(libc)]
 #![feature(quote)]
 #![feature(rustc_diagnostic_macros)]
diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs
index cd152a391b8..139c4c656db 100644
--- a/src/librustc_trans/mir/block.rs
+++ b/src/librustc_trans/mir/block.rs
@@ -671,10 +671,17 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
                                  (align | Alignment::Packed(arg.layout.align))
                                     .non_abi());
             } else {
+                // We can't use `LvalueRef::load` here because the argument
+                // may have a type we don't treat as immediate, but the ABI
+                // used for this call is passing it by-value. In that case,
+                // the load would just produce `OperandValue::Ref` instead
+                // of the `OperandValue::Immediate` we need for the call.
                 llval = bcx.load(llval, align.non_abi());
-            }
-            if let layout::Abi::Scalar(layout::Int(layout::I1, _)) = arg.layout.abi {
-                bcx.range_metadata(llval, 0..2);
+                if let layout::Abi::Scalar(ref scalar) = arg.layout.abi {
+                    if scalar.is_bool() {
+                        bcx.range_metadata(llval, 0..2);
+                    }
+                }
                 // We store bools as i8 so we need to truncate to i1.
                 llval = base::to_immediate(bcx, llval, arg.layout);
             }
diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs
index d782ffe1f9d..7e1569c8f8f 100644
--- a/src/librustc_trans/mir/constant.rs
+++ b/src/librustc_trans/mir/constant.rs
@@ -455,9 +455,9 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
                                           Value(base));
                             }
                             let layout = self.ccx.layout_of(projected_ty);
-                            if let layout::Abi::Scalar(layout::Int(layout::I1, _)) = layout.abi {
+                            if let layout::Abi::Scalar(ref scalar) = layout.abi {
                                 let i1_type = Type::i1(self.ccx);
-                                if val_ty(val) != i1_type {
+                                if scalar.is_bool() && val_ty(val) != i1_type {
                                     unsafe {
                                         val = llvm::LLVMConstTrunc(val, i1_type.to_ref());
                                     }
@@ -685,10 +685,14 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
                         assert!(cast_layout.is_llvm_immediate());
                         let ll_t_out = cast_layout.immediate_llvm_type(self.ccx);
                         let llval = operand.llval;
-                        let signed = match self.ccx.layout_of(operand.ty).abi {
-                            layout::Abi::Scalar(layout::Int(_, signed)) => signed,
-                            _ => false
-                        };
+
+                        let mut signed = false;
+                        let l = self.ccx.layout_of(operand.ty);
+                        if let layout::Abi::Scalar(ref scalar) = l.abi {
+                            if let layout::Int(_, true) = scalar.value {
+                                signed = true;
+                            }
+                        }
 
                         unsafe {
                             match (r_t_in, r_t_out) {
diff --git a/src/librustc_trans/mir/lvalue.rs b/src/librustc_trans/mir/lvalue.rs
index 1f8209c7066..f9a179ee0ee 100644
--- a/src/librustc_trans/mir/lvalue.rs
+++ b/src/librustc_trans/mir/lvalue.rs
@@ -148,16 +148,29 @@ impl<'a, 'tcx> LvalueRef<'tcx> {
                 const_llval
             } else {
                 let load = bcx.load(self.llval, self.alignment.non_abi());
-                if self.layout.ty.is_bool() {
-                    bcx.range_metadata(load, 0..2);
-                } else if self.layout.ty.is_char() {
-                    // a char is a Unicode codepoint, and so takes values from 0
-                    // to 0x10FFFF inclusive only.
-                    bcx.range_metadata(load, 0..0x10FFFF+1);
-                } else if self.layout.ty.is_region_ptr() ||
-                        self.layout.ty.is_box() ||
-                        self.layout.ty.is_fn() {
-                    bcx.nonnull_metadata(load);
+                if let layout::Abi::Scalar(ref scalar) = self.layout.abi {
+                    let (min, max) = (scalar.valid_range.start, scalar.valid_range.end);
+                    let max_next = max.wrapping_add(1);
+                    let bits = scalar.value.size(bcx.ccx).bits();
+                    assert!(bits <= 128);
+                    let mask = !0u128 >> (128 - bits);
+                    // For a (max) value of -1, max will be `-1 as usize`, which overflows.
+                    // However, that is fine here (it would still represent the full range),
+                    // i.e., if the range is everything.  The lo==hi case would be
+                    // rejected by the LLVM verifier (it would mean either an
+                    // empty set, which is impossible, or the entire range of the
+                    // type, which is pointless).
+                    match scalar.value {
+                        layout::Int(..) if max_next & mask != min & mask => {
+                            // llvm::ConstantRange can deal with ranges that wrap around,
+                            // so an overflow on (max + 1) is fine.
+                            bcx.range_metadata(load, min..max_next);
+                        }
+                        layout::Pointer if 0 < min && min < max => {
+                            bcx.nonnull_metadata(load);
+                        }
+                        _ => {}
+                    }
                 }
                 load
             };
@@ -274,48 +287,18 @@ impl<'a, 'tcx> LvalueRef<'tcx> {
         let cast_to = bcx.ccx.layout_of(cast_to).immediate_llvm_type(bcx.ccx);
         match self.layout.variants {
             layout::Variants::Single { index } => {
-                assert_eq!(index, 0);
-                return C_uint(cast_to, 0);
+                return C_uint(cast_to, index as u64);
             }
             layout::Variants::Tagged { .. } |
             layout::Variants::NicheFilling { .. } => {},
         }
 
         let discr = self.project_field(bcx, 0);
-        let discr_scalar = match discr.layout.abi {
-            layout::Abi::Scalar(discr) => discr,
-            _ => bug!("discriminant not scalar: {:#?}", discr.layout)
-        };
-        let (min, max) = match self.layout.variants {
-            layout::Variants::Tagged { ref discr_range, .. } => {
-                (discr_range.start, discr_range.end)
-            }
-            _ => (0, !0),
-        };
-        let max_next = max.wrapping_add(1);
-        let bits = discr_scalar.size(bcx.ccx).bits();
-        assert!(bits <= 128);
-        let mask = !0u128 >> (128 - bits);
-        let lldiscr = bcx.load(discr.llval, discr.alignment.non_abi());
-        match discr_scalar {
-            // For a (max) discr of -1, max will be `-1 as usize`, which overflows.
-            // However, that is fine here (it would still represent the full range),
-            layout::Int(..) if max_next & mask != min & mask => {
-                // llvm::ConstantRange can deal with ranges that wrap around,
-                // so an overflow on (max + 1) is fine.
-                bcx.range_metadata(lldiscr, min..max_next);
-            }
-            _ => {
-                // i.e., if the range is everything.  The lo==hi case would be
-                // rejected by the LLVM verifier (it would mean either an
-                // empty set, which is impossible, or the entire range of the
-                // type, which is pointless).
-            }
-        };
+        let lldiscr = discr.load(bcx).immediate();
         match self.layout.variants {
             layout::Variants::Single { .. } => bug!(),
-            layout::Variants::Tagged { .. } => {
-                let signed = match discr_scalar {
+            layout::Variants::Tagged { ref discr, .. } => {
+                let signed = match discr.value {
                     layout::Int(_, signed) => signed,
                     _ => false
                 };
diff --git a/src/librustc_trans/mir/rvalue.rs b/src/librustc_trans/mir/rvalue.rs
index f584c6a653e..e52dcd07562 100644
--- a/src/librustc_trans/mir/rvalue.rs
+++ b/src/librustc_trans/mir/rvalue.rs
@@ -119,6 +119,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
                     }
 
                     // Use llvm.memset.p0i8.* to initialize byte arrays
+                    let v = base::from_immediate(&bcx, v);
                     if common::val_ty(v) == Type::i8(bcx.ccx) {
                         base::call_memset(&bcx, start, v, size, align, false);
                         return bcx;
@@ -278,28 +279,25 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
                         let ll_t_out = cast.immediate_llvm_type(bcx.ccx);
                         let llval = operand.immediate();
 
-                        match operand.layout.variants {
-                            layout::Variants::Tagged {
-                                ref discr_range, ..
-                            } if discr_range.end > discr_range.start => {
-                                // We want `table[e as usize]` to not
-                                // have bound checks, and this is the most
-                                // convenient place to put the `assume`.
-
-                                base::call_assume(&bcx, bcx.icmp(
-                                    llvm::IntULE,
-                                    llval,
-                                    C_uint_big(ll_t_in, discr_range.end)
-                                ));
+                        let mut signed = false;
+                        if let layout::Abi::Scalar(ref scalar) = operand.layout.abi {
+                            if let layout::Int(_, s) = scalar.value {
+                                signed = s;
+
+                                if scalar.valid_range.end > scalar.valid_range.start {
+                                    // We want `table[e as usize]` to not
+                                    // have bound checks, and this is the most
+                                    // convenient place to put the `assume`.
+
+                                    base::call_assume(&bcx, bcx.icmp(
+                                        llvm::IntULE,
+                                        llval,
+                                        C_uint_big(ll_t_in, scalar.valid_range.end)
+                                    ));
+                                }
                             }
-                            _ => {}
                         }
 
-                        let signed = match operand.layout.abi {
-                            layout::Abi::Scalar(layout::Int(_, signed)) => signed,
-                            _ => false
-                        };
-
                         let newval = match (r_t_in, r_t_out) {
                             (CastTy::Int(_), CastTy::Int(_)) => {
                                 bcx.intcast(llval, ll_t_out, signed)
diff --git a/src/librustc_trans/type_.rs b/src/librustc_trans/type_.rs
index 53aaed15783..2774359c994 100644
--- a/src/librustc_trans/type_.rs
+++ b/src/librustc_trans/type_.rs
@@ -268,7 +268,6 @@ impl Type {
     pub fn from_integer(cx: &CrateContext, i: layout::Integer) -> Type {
         use rustc::ty::layout::Integer::*;
         match i {
-            I1 => Type::i1(cx),
             I8 => Type::i8(cx),
             I16 => Type::i16(cx),
             I32 => Type::i32(cx),
diff --git a/src/librustc_trans/type_of.rs b/src/librustc_trans/type_of.rs
index 6fec1a675cd..eab5cb159de 100644
--- a/src/librustc_trans/type_of.rs
+++ b/src/librustc_trans/type_of.rs
@@ -176,14 +176,13 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyLayout<'tcx> {
     /// of that field's type - this is useful for taking the address of
     /// that field and ensuring the struct has the right alignment.
     fn llvm_type<'a>(&self, ccx: &CrateContext<'a, 'tcx>) -> Type {
-        if let layout::Abi::Scalar(value) = self.abi {
+        if let layout::Abi::Scalar(ref scalar) = self.abi {
             // Use a different cache for scalars because pointers to DSTs
             // can be either fat or thin (data pointers of fat pointers).
             if let Some(&llty) = ccx.scalar_lltypes().borrow().get(&self.ty) {
                 return llty;
             }
-            let llty = match value {
-                layout::Int(layout::I1, _) => Type::i8(ccx),
+            let llty = match scalar.value {
                 layout::Int(i, _) => Type::from_integer(ccx, i),
                 layout::F32 => Type::f32(ccx),
                 layout::F64 => Type::f64(ccx),
@@ -249,11 +248,12 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyLayout<'tcx> {
     }
 
     fn immediate_llvm_type<'a>(&self, ccx: &CrateContext<'a, 'tcx>) -> Type {
-        if let layout::Abi::Scalar(layout::Int(layout::I1, _)) = self.abi {
-            Type::i1(ccx)
-        } else {
-            self.llvm_type(ccx)
+        if let layout::Abi::Scalar(ref scalar) = self.abi {
+            if scalar.is_bool() {
+                return Type::i1(ccx);
+            }
         }
+        self.llvm_type(ccx)
     }
 
     fn over_align(&self) -> Option<Align> {