about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2017-02-13 02:32:09 +0000
committerbors <bors@rust-lang.org>2017-02-13 02:32:09 +0000
commit05a7f25cc42d08aa541f50876915489bdc0eb4bb (patch)
tree60ed2486c674db2267aa679157ee1ea96686c845
parent956e2bcbaa00c05e051718b1375375915064f1c3 (diff)
parentb663d9d5e85c06ca980e75c545fadc51226ab38c (diff)
downloadrust-05a7f25cc42d08aa541f50876915489bdc0eb4bb.tar.gz
rust-05a7f25cc42d08aa541f50876915489bdc0eb4bb.zip
Auto merge of #39456 - nagisa:mir-switchint-everywhere, r=nikomatsakis
[MIR] SwitchInt Everywhere

Something I've been meaning to do for a very long while. This PR essentially gets rid of 3 kinds of conditional branching and only keeps the most general one - `SwitchInt`. Primary benefits are such that dealing with MIR now does not involve dealing with 3 different ways to do conditional control flow. On the other hand, constructing a `SwitchInt` currently requires more code than what previously was necessary to build an equivalent `If` terminator. Something trivially "fixable" with some constructor methods somewhere (MIR needs stuff like that badly in general).

Some timings (tl;dr: slightly faster^1 (unexpected), but also uses slightly more memory at peak (expected)):

^1: Not sure if the speed benefits are because of LLVM liking the generated code better or the compiler itself getting compiled better. Either way, its a net benefit. The CORE and SYNTAX timings done for compilation without optimisation.

```
AFTER:
Building stage1 std artifacts (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu)
    Finished release [optimized] target(s) in 31.50 secs
    Finished release [optimized] target(s) in 31.42 secs
Building stage1 compiler artifacts (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu)
    Finished release [optimized] target(s) in 439.56 secs
    Finished release [optimized] target(s) in 435.15 secs

CORE: 99% (24.81 real, 0.13 kernel, 24.57 user); 358536k resident
CORE: 99% (24.56 real, 0.15 kernel, 24.36 user); 359168k resident
SYNTAX: 99% (49.98 real, 0.48 kernel, 49.42 user); 653416k resident
SYNTAX: 99% (50.07 real, 0.58 kernel, 49.43 user); 653604k resident

BEFORE:
Building stage1 std artifacts (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu)
    Finished release [optimized] target(s) in 31.84 secs
Building stage1 compiler artifacts (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu)
    Finished release [optimized] target(s) in 451.17 secs

CORE: 99% (24.66 real, 0.20 kernel, 24.38 user); 351096k resident
CORE: 99% (24.36 real, 0.17 kernel, 24.18 user); 352284k resident
SYNTAX: 99% (52.24 real, 0.56 kernel, 51.66 user); 645544k resident
SYNTAX: 99% (51.55 real, 0.48 kernel, 50.99 user); 646428k resident
```

cc @nikomatsakis @eddyb
-rw-r--r--src/librustc/middle/const_val.rs11
-rw-r--r--src/librustc/mir/mod.rs67
-rw-r--r--src/librustc/mir/tcx.rs35
-rw-r--r--src/librustc/mir/visit.rs43
-rw-r--r--src/librustc/ty/context.rs4
-rw-r--r--src/librustc/ty/layout.rs31
-rw-r--r--src/librustc/ty/mod.rs11
-rw-r--r--src/librustc/ty/util.rs81
-rw-r--r--src/librustc_borrowck/borrowck/mir/dataflow/mod.rs5
-rw-r--r--src/librustc_borrowck/borrowck/mir/elaborate_drops.rs118
-rw-r--r--src/librustc_borrowck/borrowck/mir/gather_moves.rs5
-rw-r--r--src/librustc_const_math/int.rs8
-rw-r--r--src/librustc_data_structures/bitvec.rs4
-rw-r--r--src/librustc_llvm/ffi.rs10
-rw-r--r--src/librustc_metadata/decoder.rs18
-rw-r--r--src/librustc_metadata/encoder.rs11
-rw-r--r--src/librustc_metadata/schema.rs2
-rw-r--r--src/librustc_mir/build/expr/into.rs24
-rw-r--r--src/librustc_mir/build/matches/mod.rs3
-rw-r--r--src/librustc_mir/build/matches/test.rs152
-rw-r--r--src/librustc_mir/hair/mod.rs3
-rw-r--r--src/librustc_mir/lib.rs2
-rw-r--r--src/librustc_mir/transform/no_landing_pads.rs2
-rw-r--r--src/librustc_mir/transform/qualify_consts.rs10
-rw-r--r--src/librustc_mir/transform/simplify.rs2
-rw-r--r--src/librustc_mir/transform/simplify_branches.rs28
-rw-r--r--src/librustc_mir/transform/type_check.rs30
-rw-r--r--src/librustc_passes/mir_stats.rs3
-rw-r--r--src/librustc_trans/adt.rs2
-rw-r--r--src/librustc_trans/base.rs2
-rw-r--r--src/librustc_trans/builder.rs4
-rw-r--r--src/librustc_trans/debuginfo/metadata.rs2
-rw-r--r--src/librustc_trans/disr.rs2
-rw-r--r--src/librustc_trans/mir/analyze.rs4
-rw-r--r--src/librustc_trans/mir/block.rs72
-rw-r--r--src/librustc_trans/mir/constant.rs48
-rw-r--r--src/librustc_trans/mir/rvalue.rs26
-rw-r--r--src/librustc_typeck/collect.rs122
-rw-r--r--src/libserialize/serialize.rs28
-rw-r--r--src/rustllvm/RustWrapper.cpp6
-rw-r--r--src/test/codegen/match.rs10
-rw-r--r--src/test/compile-fail/E0081.rs6
-rw-r--r--src/test/compile-fail/issue-15524.rs18
-rw-r--r--src/test/mir-opt/simplify_if.rs2
-rw-r--r--src/test/ui/custom-derive/issue-36935.rs1
-rw-r--r--src/test/ui/custom-derive/issue-36935.stderr4
46 files changed, 531 insertions, 551 deletions
diff --git a/src/librustc/middle/const_val.rs b/src/librustc/middle/const_val.rs
index f583f601726..11919db479c 100644
--- a/src/librustc/middle/const_val.rs
+++ b/src/librustc/middle/const_val.rs
@@ -14,6 +14,7 @@ use std::rc::Rc;
 use hir::def_id::DefId;
 use rustc_const_math::*;
 use self::ConstVal::*;
+pub use rustc_const_math::ConstInt;
 
 use std::collections::BTreeMap;
 
@@ -48,4 +49,14 @@ impl ConstVal {
             Char(..) => "char",
         }
     }
+
+    pub fn to_const_int(&self) -> Option<ConstInt> {
+        match *self {
+            ConstVal::Integral(i) => Some(i),
+            ConstVal::Bool(true) => Some(ConstInt::Infer(1)),
+            ConstVal::Bool(false) => Some(ConstInt::Infer(0)),
+            ConstVal::Char(ch) => Some(ConstInt::U32(ch as u32)),
+            _ => None
+        }
+    }
 }
diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs
index cbb7b2710f5..d8212807eb2 100644
--- a/src/librustc/mir/mod.rs
+++ b/src/librustc/mir/mod.rs
@@ -453,36 +453,30 @@ pub enum TerminatorKind<'tcx> {
         target: BasicBlock,
     },
 
-    /// jump to branch 0 if this lvalue evaluates to true
-    If {
-        cond: Operand<'tcx>,
-        targets: (BasicBlock, BasicBlock),
-    },
-
-    /// lvalue evaluates to some enum; jump depending on the branch
-    Switch {
-        discr: Lvalue<'tcx>,
-        adt_def: &'tcx AdtDef,
-        targets: Vec<BasicBlock>,
-    },
-
     /// operand evaluates to an integer; jump depending on its value
     /// to one of the targets, and otherwise fallback to `otherwise`
     SwitchInt {
         /// discriminant value being tested
-        discr: Lvalue<'tcx>,
+        discr: Operand<'tcx>,
 
         /// type of value being tested
         switch_ty: Ty<'tcx>,
 
         /// Possible values. The locations to branch to in each case
         /// are found in the corresponding indices from the `targets` vector.
-        values: Vec<ConstVal>,
-
-        /// Possible branch sites. The length of this vector should be
-        /// equal to the length of the `values` vector plus 1 -- the
-        /// extra item is the block to branch to if none of the values
-        /// fit.
+        values: Cow<'tcx, [ConstInt]>,
+
+        /// Possible branch sites. The last element of this vector is used
+        /// for the otherwise branch, so values.len() == targets.len() + 1
+        /// should hold.
+        // This invariant is quite non-obvious and also could be improved.
+        // One way to make this invariant is to have something like this instead:
+        //
+        // branches: Vec<(ConstInt, BasicBlock)>,
+        // otherwise: Option<BasicBlock> // exhaustive if None
+        //
+        // However we’ve decided to keep this as-is until we figure a case
+        // where some other approach seems to be strictly better than other.
         targets: Vec<BasicBlock>,
     },
 
@@ -546,12 +540,21 @@ impl<'tcx> Terminator<'tcx> {
 }
 
 impl<'tcx> TerminatorKind<'tcx> {
+    pub fn if_<'a, 'gcx>(tcx: ty::TyCtxt<'a, 'gcx, 'tcx>, cond: Operand<'tcx>,
+                         t: BasicBlock, f: BasicBlock) -> TerminatorKind<'tcx> {
+        static BOOL_SWITCH_FALSE: &'static [ConstInt] = &[ConstInt::Infer(0)];
+        TerminatorKind::SwitchInt {
+            discr: cond,
+            switch_ty: tcx.types.bool,
+            values: From::from(BOOL_SWITCH_FALSE),
+            targets: vec![f, t],
+        }
+    }
+
     pub fn successors(&self) -> Cow<[BasicBlock]> {
         use self::TerminatorKind::*;
         match *self {
             Goto { target: ref b } => slice::ref_slice(b).into_cow(),
-            If { targets: (b1, b2), .. } => vec![b1, b2].into_cow(),
-            Switch { targets: ref b, .. } => b[..].into_cow(),
             SwitchInt { targets: ref b, .. } => b[..].into_cow(),
             Resume => (&[]).into_cow(),
             Return => (&[]).into_cow(),
@@ -580,8 +583,6 @@ impl<'tcx> TerminatorKind<'tcx> {
         use self::TerminatorKind::*;
         match *self {
             Goto { target: ref mut b } => vec![b],
-            If { targets: (ref mut b1, ref mut b2), .. } => vec![b1, b2],
-            Switch { targets: ref mut b, .. } => b.iter_mut().collect(),
             SwitchInt { targets: ref mut b, .. } => b.iter_mut().collect(),
             Resume => Vec::new(),
             Return => Vec::new(),
@@ -659,8 +660,6 @@ impl<'tcx> TerminatorKind<'tcx> {
         use self::TerminatorKind::*;
         match *self {
             Goto { .. } => write!(fmt, "goto"),
-            If { cond: ref lv, .. } => write!(fmt, "if({:?})", lv),
-            Switch { discr: ref lv, .. } => write!(fmt, "switch({:?})", lv),
             SwitchInt { discr: ref lv, .. } => write!(fmt, "switchInt({:?})", lv),
             Return => write!(fmt, "return"),
             Resume => write!(fmt, "resume"),
@@ -710,18 +709,11 @@ impl<'tcx> TerminatorKind<'tcx> {
         match *self {
             Return | Resume | Unreachable => vec![],
             Goto { .. } => vec!["".into()],
-            If { .. } => vec!["true".into(), "false".into()],
-            Switch { ref adt_def, .. } => {
-                adt_def.variants
-                       .iter()
-                       .map(|variant| variant.name.to_string().into())
-                       .collect()
-            }
             SwitchInt { ref values, .. } => {
                 values.iter()
                       .map(|const_val| {
                           let mut buf = String::new();
-                          fmt_const_val(&mut buf, const_val).unwrap();
+                          fmt_const_val(&mut buf, &ConstVal::Integral(*const_val)).unwrap();
                           buf.into()
                       })
                       .chain(iter::once(String::from("otherwise").into()))
@@ -997,6 +989,12 @@ pub enum Rvalue<'tcx> {
 
     UnaryOp(UnOp, Operand<'tcx>),
 
+    /// Read the discriminant of an ADT.
+    ///
+    /// Undefined (i.e. no effort is made to make it defined, but there’s no reason why it cannot
+    /// be defined to return, say, a 0) if ADT is not an enum.
+    Discriminant(Lvalue<'tcx>),
+
     /// Creates an *uninitialized* Box
     Box(Ty<'tcx>),
 
@@ -1111,6 +1109,7 @@ impl<'tcx> Debug for Rvalue<'tcx> {
                 write!(fmt, "Checked{:?}({:?}, {:?})", op, a, b)
             }
             UnaryOp(ref op, ref a) => write!(fmt, "{:?}({:?})", op, a),
+            Discriminant(ref lval) => write!(fmt, "discriminant({:?})", lval),
             Box(ref t) => write!(fmt, "Box({:?})", t),
             InlineAsm { ref asm, ref outputs, ref inputs } => {
                 write!(fmt, "asm!({:?} : {:?} : {:?})", asm, outputs, inputs)
diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs
index f1268521d67..7b0863b4c42 100644
--- a/src/librustc/mir/tcx.rs
+++ b/src/librustc/mir/tcx.rs
@@ -18,6 +18,7 @@ use ty::subst::{Subst, Substs};
 use ty::{self, AdtDef, Ty, TyCtxt};
 use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
 use hir;
+use ty::util::IntTypeExt;
 
 #[derive(Copy, Clone, Debug)]
 pub enum LvalueTy<'tcx> {
@@ -135,15 +136,15 @@ impl<'tcx> Lvalue<'tcx> {
 impl<'tcx> Rvalue<'tcx> {
     pub fn ty<'a, 'gcx>(&self, mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Option<Ty<'tcx>>
     {
-        match self {
-            &Rvalue::Use(ref operand) => Some(operand.ty(mir, tcx)),
-            &Rvalue::Repeat(ref operand, ref count) => {
+        match *self {
+            Rvalue::Use(ref operand) => Some(operand.ty(mir, tcx)),
+            Rvalue::Repeat(ref operand, ref count) => {
                 let op_ty = operand.ty(mir, tcx);
                 let count = count.value.as_u64(tcx.sess.target.uint_type);
                 assert_eq!(count as usize as u64, count);
                 Some(tcx.mk_array(op_ty, count as usize))
             }
-            &Rvalue::Ref(reg, bk, ref lv) => {
+            Rvalue::Ref(reg, bk, ref lv) => {
                 let lv_ty = lv.ty(mir, tcx).to_ty(tcx);
                 Some(tcx.mk_ref(reg,
                     ty::TypeAndMut {
@@ -152,27 +153,37 @@ impl<'tcx> Rvalue<'tcx> {
                     }
                 ))
             }
-            &Rvalue::Len(..) => Some(tcx.types.usize),
-            &Rvalue::Cast(.., ty) => Some(ty),
-            &Rvalue::BinaryOp(op, ref lhs, ref rhs) => {
+            Rvalue::Len(..) => Some(tcx.types.usize),
+            Rvalue::Cast(.., ty) => Some(ty),
+            Rvalue::BinaryOp(op, ref lhs, ref rhs) => {
                 let lhs_ty = lhs.ty(mir, tcx);
                 let rhs_ty = rhs.ty(mir, tcx);
                 Some(op.ty(tcx, lhs_ty, rhs_ty))
             }
-            &Rvalue::CheckedBinaryOp(op, ref lhs, ref rhs) => {
+            Rvalue::CheckedBinaryOp(op, ref lhs, ref rhs) => {
                 let lhs_ty = lhs.ty(mir, tcx);
                 let rhs_ty = rhs.ty(mir, tcx);
                 let ty = op.ty(tcx, lhs_ty, rhs_ty);
                 let ty = tcx.intern_tup(&[ty, tcx.types.bool], false);
                 Some(ty)
             }
-            &Rvalue::UnaryOp(_, ref operand) => {
+            Rvalue::UnaryOp(_, ref operand) => {
                 Some(operand.ty(mir, tcx))
             }
-            &Rvalue::Box(t) => {
+            Rvalue::Discriminant(ref lval) => {
+                let ty = lval.ty(mir, tcx).to_ty(tcx);
+                if let ty::TyAdt(adt_def, _) = ty.sty {
+                    Some(adt_def.discr_ty.to_ty(tcx))
+                } else {
+                    // Undefined behaviour, bug for now; may want to return something for
+                    // the `discriminant` intrinsic later.
+                    bug!("Rvalue::Discriminant on Lvalue of type {:?}", ty);
+                }
+            }
+            Rvalue::Box(t) => {
                 Some(tcx.mk_box(t))
             }
-            &Rvalue::Aggregate(ref ak, ref ops) => {
+            Rvalue::Aggregate(ref ak, ref ops) => {
                 match *ak {
                     AggregateKind::Array => {
                         if let Some(operand) = ops.get(0) {
@@ -196,7 +207,7 @@ impl<'tcx> Rvalue<'tcx> {
                     }
                 }
             }
-            &Rvalue::InlineAsm { .. } => None
+            Rvalue::InlineAsm { .. } => None
         }
     }
 }
diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs
index b5da304a109..be3c43db7ba 100644
--- a/src/librustc/mir/visit.rs
+++ b/src/librustc/mir/visit.rs
@@ -14,7 +14,6 @@ use ty::subst::Substs;
 use ty::{ClosureSubsts, Region, Ty};
 use mir::*;
 use rustc_const_math::ConstUsize;
-use rustc_data_structures::tuple_slice::TupleSlice;
 use rustc_data_structures::indexed_vec::Idx;
 use syntax_pos::Span;
 
@@ -224,6 +223,12 @@ macro_rules! make_mir_visitor {
                 self.super_const_val(const_val);
             }
 
+            fn visit_const_int(&mut self,
+                               const_int: &ConstInt,
+                               _: Location) {
+                self.super_const_int(const_int);
+            }
+
             fn visit_const_usize(&mut self,
                                  const_usize: & $($mutability)* ConstUsize,
                                  _: Location) {
@@ -363,31 +368,14 @@ macro_rules! make_mir_visitor {
                         self.visit_branch(block, target);
                     }
 
-                    TerminatorKind::If { ref $($mutability)* cond,
-                                         ref $($mutability)* targets } => {
-                        self.visit_operand(cond, source_location);
-                        for &target in targets.as_slice() {
-                            self.visit_branch(block, target);
-                        }
-                    }
-
-                    TerminatorKind::Switch { ref $($mutability)* discr,
-                                             adt_def: _,
-                                             ref targets } => {
-                        self.visit_lvalue(discr, LvalueContext::Inspect, source_location);
-                        for &target in targets {
-                            self.visit_branch(block, target);
-                        }
-                    }
-
                     TerminatorKind::SwitchInt { ref $($mutability)* discr,
                                                 ref $($mutability)* switch_ty,
-                                                ref $($mutability)* values,
+                                                ref values,
                                                 ref targets } => {
-                        self.visit_lvalue(discr, LvalueContext::Inspect, source_location);
+                        self.visit_operand(discr, source_location);
                         self.visit_ty(switch_ty);
-                        for value in values {
-                            self.visit_const_val(value, source_location);
+                        for value in &values[..] {
+                            self.visit_const_int(value, source_location);
                         }
                         for &target in targets {
                             self.visit_branch(block, target);
@@ -506,6 +494,10 @@ macro_rules! make_mir_visitor {
                         self.visit_operand(op, location);
                     }
 
+                    Rvalue::Discriminant(ref $($mutability)* lvalue) => {
+                        self.visit_lvalue(lvalue, LvalueContext::Inspect, location);
+                    }
+
                     Rvalue::Box(ref $($mutability)* ty) => {
                         self.visit_ty(ty);
                     }
@@ -712,10 +704,13 @@ macro_rules! make_mir_visitor {
                                     _substs: & $($mutability)* ClosureSubsts<'tcx>) {
             }
 
-            fn super_const_val(&mut self, _substs: & $($mutability)* ConstVal) {
+            fn super_const_val(&mut self, _const_val: & $($mutability)* ConstVal) {
+            }
+
+            fn super_const_int(&mut self, _const_int: &ConstInt) {
             }
 
-            fn super_const_usize(&mut self, _substs: & $($mutability)* ConstUsize) {
+            fn super_const_usize(&mut self, _const_usize: & $($mutability)* ConstUsize) {
             }
 
             // Convenience methods
diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs
index 6203679a510..19bb8a63aa2 100644
--- a/src/librustc/ty/context.rs
+++ b/src/librustc/ty/context.rs
@@ -673,10 +673,12 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
     pub fn alloc_adt_def(self,
                          did: DefId,
                          kind: AdtKind,
+                         discr_ty: Option<attr::IntType>,
                          variants: Vec<ty::VariantDef>,
                          repr: ReprOptions)
                          -> &'gcx ty::AdtDef {
-        let def = ty::AdtDef::new(self, did, kind, variants, repr);
+        let discr_ty = discr_ty.unwrap_or(attr::UnsignedInt(ast::UintTy::U8));
+        let def = ty::AdtDef::new(self, did, kind, discr_ty, variants, repr);
         self.global_arenas.adt_def.alloc(def)
     }
 
diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs
index 3a463e981a6..f429053d8bb 100644
--- a/src/librustc/ty/layout.rs
+++ b/src/librustc/ty/layout.rs
@@ -20,7 +20,6 @@ use ty::{self, Ty, TyCtxt, TypeFoldable, ReprOptions};
 use syntax::ast::{FloatTy, IntTy, UintTy};
 use syntax::attr;
 use syntax_pos::DUMMY_SP;
-use rustc_const_math::ConstInt;
 
 use std::cmp;
 use std::fmt;
@@ -1183,11 +1182,7 @@ impl<'a, 'gcx, 'tcx> Layout {
                                                             i64::min_value(),
                                                             true);
                     for v in &def.variants {
-                        let x = match v.disr_val.erase_type() {
-                            ConstInt::InferSigned(i) => i as i64,
-                            ConstInt::Infer(i) => i as u64 as i64,
-                            _ => bug!()
-                        };
+                        let x = v.disr_val as i128 as i64;
                         if x == 0 { non_zero = false; }
                         if x < min { min = x; }
                         if x > max { max = x; }
@@ -1195,9 +1190,7 @@ impl<'a, 'gcx, 'tcx> Layout {
 
                     // FIXME: should handle i128? signed-value based impl is weird and hard to
                     // grok.
-                    let (discr, signed) = Integer::repr_discr(tcx, ty, &def.repr,
-                                                              min,
-                                                              max);
+                    let (discr, signed) = Integer::repr_discr(tcx, ty, &def.repr, min, max);
                     return success(CEnum {
                         discr: discr,
                         signed: signed,
@@ -1247,7 +1240,7 @@ impl<'a, 'gcx, 'tcx> Layout {
                 // non-empty body, explicit discriminants should have
                 // been rejected by a checker before this point.
                 for (i, v) in def.variants.iter().enumerate() {
-                    if i as u128 != v.disr_val.to_u128_unchecked() {
+                    if i as u128 != v.disr_val {
                         bug!("non-C-like enum {} with specified discriminants",
                             tcx.item_path_str(def.did));
                     }
@@ -1315,7 +1308,6 @@ impl<'a, 'gcx, 'tcx> Layout {
                 let discr_max = (variants.len() - 1) as i64;
                 assert!(discr_max >= 0);
                 let (min_ity, _) = Integer::repr_discr(tcx, ty, &def.repr, 0, discr_max);
-
                 let mut align = dl.aggregate_align;
                 let mut size = Size::from_bytes(0);
 
@@ -1356,6 +1348,23 @@ impl<'a, 'gcx, 'tcx> Layout {
                     return Err(LayoutError::SizeOverflow(ty));
                 }
 
+                let typeck_ity = Integer::from_attr(dl, def.discr_ty);
+                if typeck_ity < min_ity {
+                    // It is a bug if Layout decided on a greater discriminant size than typeck for
+                    // some reason at this point (based on values discriminant can take on). Mostly
+                    // because this discriminant will be loaded, and then stored into variable of
+                    // type calculated by typeck. Consider such case (a bug): typeck decided on
+                    // byte-sized discriminant, but layout thinks we need a 16-bit to store all
+                    // discriminant values. That would be a bug, because then, in trans, in order
+                    // to store this 16-bit discriminant into 8-bit sized temporary some of the
+                    // space necessary to represent would have to be discarded (or layout is wrong
+                    // on thinking it needs 16 bits)
+                    bug!("layout decided on a larger discriminant type ({:?}) than typeck ({:?})",
+                         min_ity, typeck_ity);
+                    // However, it is fine to make discr type however large (as an optimisation)
+                    // after this point – we’ll just truncate the value we load in trans.
+                }
+
                 // Check to see if we should use a different type for the
                 // discriminant. We can safely use a type with the same size
                 // as the alignment of the first field of each variant.
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index beb286108a0..411e14531fa 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -45,7 +45,6 @@ use syntax::attr;
 use syntax::symbol::{Symbol, InternedString};
 use syntax_pos::{DUMMY_SP, Span};
 
-use rustc_const_math::ConstInt;
 use rustc_data_structures::accumulate_vec::IntoIter as AccIntoIter;
 
 use hir;
@@ -96,7 +95,7 @@ mod flags;
 mod structural_impls;
 mod sty;
 
-pub type Disr = ConstInt;
+pub type Disr = u128;
 
 // Data types
 
@@ -1325,6 +1324,12 @@ pub struct FieldDef {
 /// table.
 pub struct AdtDef {
     pub did: DefId,
+    /// Type of the discriminant
+    ///
+    /// Note, that this is the type specified in `repr()` or a default type of some sort, and might
+    /// not match the actual type that layout algorithm decides to use when translating this type
+    /// into LLVM. That being said, layout algorithm may not use a type larger than specified here.
+    pub discr_ty: attr::IntType,
     pub variants: Vec<VariantDef>,
     destructor: Cell<Option<DefId>>,
     flags: Cell<AdtFlags>,
@@ -1387,6 +1392,7 @@ impl<'a, 'gcx, 'tcx> AdtDef {
     fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
            did: DefId,
            kind: AdtKind,
+           discr_ty: attr::IntType,
            variants: Vec<VariantDef>,
            repr: ReprOptions) -> Self {
         let mut flags = AdtFlags::NO_ADT_FLAGS;
@@ -1410,6 +1416,7 @@ impl<'a, 'gcx, 'tcx> AdtDef {
         }
         AdtDef {
             did: did,
+            discr_ty: discr_ty,
             variants: variants,
             flags: Cell::new(flags),
             destructor: Cell::new(None),
diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs
index b01b77bbcf8..16492de6c3d 100644
--- a/src/librustc/ty/util.rs
+++ b/src/librustc/ty/util.rs
@@ -23,7 +23,6 @@ use ty::TypeVariants::*;
 use util::nodemap::FxHashMap;
 use middle::lang_items;
 
-use rustc_const_math::{ConstInt, ConstIsize, ConstUsize};
 use rustc_data_structures::stable_hasher::{StableHasher, StableHasherResult};
 
 use std::cell::RefCell;
@@ -37,84 +36,20 @@ use syntax_pos::Span;
 use hir;
 
 pub trait IntTypeExt {
-    fn to_ty<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Ty<'tcx>;
-    fn disr_incr<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, val: Option<Disr>)
-                           -> Option<Disr>;
-    fn assert_ty_matches(&self, val: Disr);
-    fn initial_discriminant<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Disr;
+    fn to_ty<'a, 'gcx: 'a+'tcx, 'tcx: 'a>(self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx>;
+    fn initial_discriminant<'a, 'tcx>(&self, _: TyCtxt<'a, 'tcx, 'tcx>) -> Disr;
 }
 
 impl IntTypeExt for attr::IntType {
-    fn to_ty<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Ty<'tcx> {
-        match *self {
-            SignedInt(ast::IntTy::I8)      => tcx.types.i8,
-            SignedInt(ast::IntTy::I16)     => tcx.types.i16,
-            SignedInt(ast::IntTy::I32)     => tcx.types.i32,
-            SignedInt(ast::IntTy::I64)     => tcx.types.i64,
-            SignedInt(ast::IntTy::I128)     => tcx.types.i128,
-            SignedInt(ast::IntTy::Is)   => tcx.types.isize,
-            UnsignedInt(ast::UintTy::U8)    => tcx.types.u8,
-            UnsignedInt(ast::UintTy::U16)   => tcx.types.u16,
-            UnsignedInt(ast::UintTy::U32)   => tcx.types.u32,
-            UnsignedInt(ast::UintTy::U64)   => tcx.types.u64,
-            UnsignedInt(ast::UintTy::U128)   => tcx.types.u128,
-            UnsignedInt(ast::UintTy::Us) => tcx.types.usize,
+    fn to_ty<'a, 'gcx: 'a+'tcx, 'tcx: 'a>(self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx> {
+        match self {
+            SignedInt(i) => tcx.mk_mach_int(i),
+            UnsignedInt(i) => tcx.mk_mach_uint(i),
         }
     }
 
-    fn initial_discriminant<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Disr {
-        match *self {
-            SignedInt(ast::IntTy::I8)    => ConstInt::I8(0),
-            SignedInt(ast::IntTy::I16)   => ConstInt::I16(0),
-            SignedInt(ast::IntTy::I32)   => ConstInt::I32(0),
-            SignedInt(ast::IntTy::I64)   => ConstInt::I64(0),
-            SignedInt(ast::IntTy::I128)   => ConstInt::I128(0),
-            SignedInt(ast::IntTy::Is) => match tcx.sess.target.int_type {
-                ast::IntTy::I16 => ConstInt::Isize(ConstIsize::Is16(0)),
-                ast::IntTy::I32 => ConstInt::Isize(ConstIsize::Is32(0)),
-                ast::IntTy::I64 => ConstInt::Isize(ConstIsize::Is64(0)),
-                _ => bug!(),
-            },
-            UnsignedInt(ast::UintTy::U8)  => ConstInt::U8(0),
-            UnsignedInt(ast::UintTy::U16) => ConstInt::U16(0),
-            UnsignedInt(ast::UintTy::U32) => ConstInt::U32(0),
-            UnsignedInt(ast::UintTy::U64) => ConstInt::U64(0),
-            UnsignedInt(ast::UintTy::U128) => ConstInt::U128(0),
-            UnsignedInt(ast::UintTy::Us) => match tcx.sess.target.uint_type {
-                ast::UintTy::U16 => ConstInt::Usize(ConstUsize::Us16(0)),
-                ast::UintTy::U32 => ConstInt::Usize(ConstUsize::Us32(0)),
-                ast::UintTy::U64 => ConstInt::Usize(ConstUsize::Us64(0)),
-                _ => bug!(),
-            },
-        }
-    }
-
-    fn assert_ty_matches(&self, val: Disr) {
-        match (*self, val) {
-            (SignedInt(ast::IntTy::I8), ConstInt::I8(_)) => {},
-            (SignedInt(ast::IntTy::I16), ConstInt::I16(_)) => {},
-            (SignedInt(ast::IntTy::I32), ConstInt::I32(_)) => {},
-            (SignedInt(ast::IntTy::I64), ConstInt::I64(_)) => {},
-            (SignedInt(ast::IntTy::I128), ConstInt::I128(_)) => {},
-            (SignedInt(ast::IntTy::Is), ConstInt::Isize(_)) => {},
-            (UnsignedInt(ast::UintTy::U8), ConstInt::U8(_)) => {},
-            (UnsignedInt(ast::UintTy::U16), ConstInt::U16(_)) => {},
-            (UnsignedInt(ast::UintTy::U32), ConstInt::U32(_)) => {},
-            (UnsignedInt(ast::UintTy::U64), ConstInt::U64(_)) => {},
-            (UnsignedInt(ast::UintTy::U128), ConstInt::U128(_)) => {},
-            (UnsignedInt(ast::UintTy::Us), ConstInt::Usize(_)) => {},
-            _ => bug!("disr type mismatch: {:?} vs {:?}", self, val),
-        }
-    }
-
-    fn disr_incr<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, val: Option<Disr>)
-                           -> Option<Disr> {
-        if let Some(val) = val {
-            self.assert_ty_matches(val);
-            (val + ConstInt::Infer(1)).ok()
-        } else {
-            Some(self.initial_discriminant(tcx))
-        }
+    fn initial_discriminant<'a, 'tcx>(&self, _: TyCtxt<'a, 'tcx, 'tcx>) -> Disr {
+        0
     }
 }
 
diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs
index f11cf90834d..8b246105f61 100644
--- a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs
+++ b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs
@@ -454,11 +454,6 @@ impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D>
                 self.propagate_bits_into_entry_set_for(in_out, changed, target);
                 self.propagate_bits_into_entry_set_for(in_out, changed, unwind);
             }
-            mir::TerminatorKind::If { ref targets, .. } => {
-                self.propagate_bits_into_entry_set_for(in_out, changed, &targets.0);
-                self.propagate_bits_into_entry_set_for(in_out, changed, &targets.1);
-            }
-            mir::TerminatorKind::Switch { ref targets, .. } |
             mir::TerminatorKind::SwitchInt { ref targets, .. } => {
                 for target in targets {
                     self.propagate_bits_into_entry_set_for(in_out, changed, target);
diff --git a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs
index d2f744bde2d..5899c9f31d1 100644
--- a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs
+++ b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs
@@ -17,9 +17,10 @@ use super::{DropFlagState, MoveDataParamEnv};
 use super::patch::MirPatch;
 use rustc::ty::{self, Ty, TyCtxt};
 use rustc::ty::subst::{Kind, Subst, Substs};
+use rustc::ty::util::IntTypeExt;
 use rustc::mir::*;
 use rustc::mir::transform::{Pass, MirPass, MirSource};
-use rustc::middle::const_val::ConstVal;
+use rustc::middle::const_val::{ConstVal, ConstInt};
 use rustc::middle::lang_items;
 use rustc::util::nodemap::FxHashMap;
 use rustc_data_structures::indexed_set::IdxSetBuf;
@@ -619,48 +620,11 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
         self.elaborated_drop_block(&inner_c)
     }
 
-    fn open_drop_for_variant<'a>(&mut self,
-                                 c: &DropCtxt<'a, 'tcx>,
-                                 drop_block: &mut Option<BasicBlock>,
-                                 adt: &'tcx ty::AdtDef,
-                                 substs: &'tcx Substs<'tcx>,
-                                 variant_index: usize)
-                                 -> BasicBlock
-    {
-        let subpath = super::move_path_children_matching(
-            self.move_data(), c.path, |proj| match proj {
-                &Projection {
-                    elem: ProjectionElem::Downcast(_, idx), ..
-                } => idx == variant_index,
-                _ => false
-            });
-
-        if let Some(variant_path) = subpath {
-            let base_lv = c.lvalue.clone().elem(
-                ProjectionElem::Downcast(adt, variant_index)
-            );
-            let fields = self.move_paths_for_fields(
-                &base_lv,
-                variant_path,
-                &adt.variants[variant_index],
-                substs);
-            self.drop_ladder(c, fields)
-        } else {
-            // variant not found - drop the entire enum
-            if let None = *drop_block {
-                *drop_block = Some(self.complete_drop(c, true));
-            }
-            return drop_block.unwrap();
-        }
-    }
-
     fn open_drop_for_adt<'a>(&mut self, c: &DropCtxt<'a, 'tcx>,
                              adt: &'tcx ty::AdtDef, substs: &'tcx Substs<'tcx>)
                              -> BasicBlock {
         debug!("open_drop_for_adt({:?}, {:?}, {:?})", c, adt, substs);
 
-        let mut drop_block = None;
-
         match adt.variants.len() {
             1 => {
                 let fields = self.move_paths_for_fields(
@@ -672,12 +636,43 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
                 self.drop_ladder(c, fields)
             }
             _ => {
-                let variant_drops : Vec<BasicBlock> =
-                    (0..adt.variants.len()).map(|i| {
-                        self.open_drop_for_variant(c, &mut drop_block,
-                                                   adt, substs, i)
-                    }).collect();
-
+                let mut values = Vec::with_capacity(adt.variants.len());
+                let mut blocks = Vec::with_capacity(adt.variants.len());
+                let mut otherwise = None;
+                for (variant_index, variant) in adt.variants.iter().enumerate() {
+                    let discr = ConstInt::new_inttype(variant.disr_val, adt.discr_ty,
+                                                      self.tcx.sess.target.uint_type,
+                                                      self.tcx.sess.target.int_type).unwrap();
+                    let subpath = super::move_path_children_matching(
+                        self.move_data(), c.path, |proj| match proj {
+                            &Projection {
+                                elem: ProjectionElem::Downcast(_, idx), ..
+                            } => idx == variant_index,
+                            _ => false
+                        });
+                    if let Some(variant_path) = subpath {
+                        let base_lv = c.lvalue.clone().elem(
+                            ProjectionElem::Downcast(adt, variant_index)
+                        );
+                        let fields = self.move_paths_for_fields(
+                            &base_lv,
+                            variant_path,
+                            &adt.variants[variant_index],
+                            substs);
+                        values.push(discr);
+                        blocks.push(self.drop_ladder(c, fields));
+                    } else {
+                        // variant not found - drop the entire enum
+                        if let None = otherwise {
+                            otherwise = Some(self.complete_drop(c, true));
+                        }
+                    }
+                }
+                if let Some(block) = otherwise {
+                    blocks.push(block);
+                } else {
+                    values.pop();
+                }
                 // If there are multiple variants, then if something
                 // is present within the enum the discriminant, tracked
                 // by the rest path, must be initialized.
@@ -685,14 +680,27 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
                 // Additionally, we do not want to switch on the
                 // discriminant after it is free-ed, because that
                 // way lies only trouble.
-
-                let switch_block = self.new_block(
-                    c, c.is_cleanup, TerminatorKind::Switch {
-                        discr: c.lvalue.clone(),
-                        adt_def: adt,
-                        targets: variant_drops
-                    });
-
+                let discr_ty = adt.discr_ty.to_ty(self.tcx);
+                let discr = Lvalue::Local(self.patch.new_temp(discr_ty));
+                let switch_block = self.patch.new_block(BasicBlockData {
+                    statements: vec![
+                        Statement {
+                            source_info: c.source_info,
+                            kind: StatementKind::Assign(discr.clone(),
+                                                        Rvalue::Discriminant(c.lvalue.clone()))
+                        }
+                    ],
+                    terminator: Some(Terminator {
+                        source_info: c.source_info,
+                        kind: TerminatorKind::SwitchInt {
+                            discr: Operand::Consume(discr),
+                            switch_ty: discr_ty,
+                            values: From::from(values),
+                            targets: blocks,
+                        }
+                    }),
+                    is_cleanup: c.is_cleanup,
+                });
                 self.drop_flag_test_block(c, switch_block)
             }
         }
@@ -813,10 +821,8 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
             (true, false) => on_set,
             (true, true) => {
                 let flag = self.drop_flag(c.path).unwrap();
-                self.new_block(c, is_cleanup, TerminatorKind::If {
-                    cond: Operand::Consume(flag),
-                    targets: (on_set, on_unset)
-                })
+                let term = TerminatorKind::if_(self.tcx, Operand::Consume(flag), on_set, on_unset);
+                self.new_block(c, is_cleanup, term)
             }
         }
     }
diff --git a/src/librustc_borrowck/borrowck/mir/gather_moves.rs b/src/librustc_borrowck/borrowck/mir/gather_moves.rs
index 7cf6ab2999c..0c7e922c48a 100644
--- a/src/librustc_borrowck/borrowck/mir/gather_moves.rs
+++ b/src/librustc_borrowck/borrowck/mir/gather_moves.rs
@@ -435,6 +435,7 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
                 }
             }
             Rvalue::Ref(..) |
+            Rvalue::Discriminant(..) |
             Rvalue::Len(..) |
             Rvalue::InlineAsm { .. } => {}
             Rvalue::Box(..) => {
@@ -463,10 +464,8 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
                 self.gather_move(loc, &Lvalue::Local(RETURN_POINTER));
             }
 
-            TerminatorKind::If { .. } |
             TerminatorKind::Assert { .. } |
-            TerminatorKind::SwitchInt { .. } |
-            TerminatorKind::Switch { .. } => {
+            TerminatorKind::SwitchInt { .. } => {
                 // branching terminators - these don't move anything
             }
 
diff --git a/src/librustc_const_math/int.rs b/src/librustc_const_math/int.rs
index 17714f2fb2d..bc3809db1c6 100644
--- a/src/librustc_const_math/int.rs
+++ b/src/librustc_const_math/int.rs
@@ -77,6 +77,14 @@ mod ibounds {
 }
 
 impl ConstInt {
+    pub fn new_inttype(val: u128, ty: IntType, usize_ty: UintTy, isize_ty: IntTy)
+    -> Option<ConstInt> {
+        match ty {
+            IntType::SignedInt(i) => ConstInt::new_signed(val as i128, i, isize_ty),
+            IntType::UnsignedInt(i) => ConstInt::new_unsigned(val, i, usize_ty),
+        }
+    }
+
     /// Creates a new unsigned ConstInt with matching type while also checking that overflow does
     /// not happen.
     pub fn new_unsigned(val: u128, ty: UintTy, usize_ty: UintTy) -> Option<ConstInt> {
diff --git a/src/librustc_data_structures/bitvec.rs b/src/librustc_data_structures/bitvec.rs
index 3700d46c346..ffcd25a4cdd 100644
--- a/src/librustc_data_structures/bitvec.rs
+++ b/src/librustc_data_structures/bitvec.rs
@@ -30,6 +30,10 @@ impl BitVector {
         }
     }
 
+    pub fn count(&self) -> usize {
+        self.data.iter().map(|e| e.count_ones() as usize).sum()
+    }
+
     #[inline]
     pub fn contains(&self, bit: usize) -> bool {
         let (word, mask) = word_mask(bit);
diff --git a/src/librustc_llvm/ffi.rs b/src/librustc_llvm/ffi.rs
index 8a958cf1ac1..adf8067987e 100644
--- a/src/librustc_llvm/ffi.rs
+++ b/src/librustc_llvm/ffi.rs
@@ -1084,11 +1084,11 @@ extern "C" {
                                 DestTy: TypeRef,
                                 Name: *const c_char)
                                 -> ValueRef;
-    pub fn LLVMBuildIntCast(B: BuilderRef,
-                            Val: ValueRef,
-                            DestTy: TypeRef,
-                            Name: *const c_char)
-                            -> ValueRef;
+    pub fn LLVMRustBuildIntCast(B: BuilderRef,
+                                Val: ValueRef,
+                                DestTy: TypeRef,
+                                IsSized: bool)
+                                -> ValueRef;
     pub fn LLVMBuildFPCast(B: BuilderRef,
                            Val: ValueRef,
                            DestTy: TypeRef,
diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs
index bb99be20f64..abc3ffcf86b 100644
--- a/src/librustc_metadata/decoder.rs
+++ b/src/librustc_metadata/decoder.rs
@@ -25,8 +25,6 @@ use rustc::session::Session;
 use rustc::ty::{self, Ty, TyCtxt};
 use rustc::ty::subst::Substs;
 
-use rustc_const_math::ConstInt;
-
 use rustc::mir::Mir;
 
 use std::borrow::Cow;
@@ -435,7 +433,7 @@ impl<'tcx> EntryKind<'tcx> {
             EntryKind::Mod(_) => Def::Mod(did),
             EntryKind::Variant(_) => Def::Variant(did),
             EntryKind::Trait(_) => Def::Trait(did),
-            EntryKind::Enum(_) => Def::Enum(did),
+            EntryKind::Enum(..) => Def::Enum(did),
             EntryKind::MacroDef(_) => Def::Macro(did),
 
             EntryKind::ForeignMod |
@@ -535,7 +533,7 @@ impl<'a, 'tcx> CrateMetadata {
                     vis: f.visibility.decode(self)
                 }
             }).collect(),
-            disr_val: ConstInt::Infer(data.disr),
+            disr_val: data.disr,
             ctor_kind: data.ctor_kind,
         }, data.struct_ctor)
     }
@@ -546,8 +544,14 @@ impl<'a, 'tcx> CrateMetadata {
                        -> &'tcx ty::AdtDef {
         let item = self.entry(item_id);
         let did = self.local_def_id(item_id);
+        let (kind, ty) = match item.kind {
+            EntryKind::Enum(dt, _) => (ty::AdtKind::Enum, Some(dt.decode(self))),
+            EntryKind::Struct(_, _) => (ty::AdtKind::Struct, None),
+            EntryKind::Union(_, _) => (ty::AdtKind::Union, None),
+            _ => bug!("get_adt_def called on a non-ADT {:?}", did),
+        };
         let mut ctor_index = None;
-        let variants = if let EntryKind::Enum(_) = item.kind {
+        let variants = if let ty::AdtKind::Enum = kind {
             item.children
                 .decode(self)
                 .map(|index| {
@@ -562,13 +566,13 @@ impl<'a, 'tcx> CrateMetadata {
             vec![variant]
         };
         let (kind, repr) = match item.kind {
-            EntryKind::Enum(repr) => (ty::AdtKind::Enum, repr),
+            EntryKind::Enum(_, repr) => (ty::AdtKind::Enum, repr),
             EntryKind::Struct(_, repr) => (ty::AdtKind::Struct, repr),
             EntryKind::Union(_, repr) => (ty::AdtKind::Union, repr),
             _ => bug!("get_adt_def called on a non-ADT {:?}", did),
         };
 
-        let adt = tcx.alloc_adt_def(did, kind, variants, repr);
+        let adt = tcx.alloc_adt_def(did, kind, ty, variants, repr);
         if let Some(ctor_index) = ctor_index {
             // Make adt definition available through constructor id as well.
             tcx.adt_defs.borrow_mut().insert(self.local_def_id(ctor_index), adt);
diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs
index f4ff5f4626f..0f9491aaf15 100644
--- a/src/librustc_metadata/encoder.rs
+++ b/src/librustc_metadata/encoder.rs
@@ -261,7 +261,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
 
         let data = VariantData {
             ctor_kind: variant.ctor_kind,
-            disr: variant.disr_val.to_u128_unchecked(),
+            disr: variant.disr_val,
             struct_ctor: None,
         };
 
@@ -388,7 +388,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
 
         let data = VariantData {
             ctor_kind: variant.ctor_kind,
-            disr: variant.disr_val.to_u128_unchecked(),
+            disr: variant.disr_val,
             struct_ctor: Some(def_id.index),
         };
 
@@ -661,7 +661,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
             }
             hir::ItemForeignMod(_) => EntryKind::ForeignMod,
             hir::ItemTy(..) => EntryKind::Type,
-            hir::ItemEnum(..) => EntryKind::Enum(get_repr_options(&tcx, def_id)),
+            hir::ItemEnum(..) => EntryKind::Enum(self.lazy(&tcx.lookup_adt_def(def_id).discr_ty),
+                                                 get_repr_options(&tcx, def_id)),
             hir::ItemStruct(ref struct_def, _) => {
                 let variant = tcx.lookup_adt_def(def_id).struct_variant();
 
@@ -678,7 +679,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
 
                 EntryKind::Struct(self.lazy(&VariantData {
                     ctor_kind: variant.ctor_kind,
-                    disr: variant.disr_val.to_u128_unchecked(),
+                    disr: variant.disr_val,
                     struct_ctor: struct_ctor,
                 }), repr_options)
             }
@@ -688,7 +689,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
 
                 EntryKind::Union(self.lazy(&VariantData {
                     ctor_kind: variant.ctor_kind,
-                    disr: variant.disr_val.to_u128_unchecked(),
+                    disr: variant.disr_val,
                     struct_ctor: None,
                 }), repr_options)
             }
diff --git a/src/librustc_metadata/schema.rs b/src/librustc_metadata/schema.rs
index 10aa4784aa2..777af02772e 100644
--- a/src/librustc_metadata/schema.rs
+++ b/src/librustc_metadata/schema.rs
@@ -228,7 +228,7 @@ pub enum EntryKind<'tcx> {
     ForeignMutStatic,
     ForeignMod,
     Type,
-    Enum(ReprOptions),
+    Enum(Lazy<attr::IntType>, ReprOptions),
     Field,
     Variant(Lazy<VariantData>),
     Struct(Lazy<VariantData>, ReprOptions),
diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs
index 3d4af259ec9..35841c2cbdf 100644
--- a/src/librustc_mir/build/expr/into.rs
+++ b/src/librustc_mir/build/expr/into.rs
@@ -69,10 +69,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
 
                 let mut then_block = this.cfg.start_new_block();
                 let mut else_block = this.cfg.start_new_block();
-                this.cfg.terminate(block, source_info, TerminatorKind::If {
-                    cond: operand,
-                    targets: (then_block, else_block)
-                });
+                let term = TerminatorKind::if_(this.hir.tcx(), operand, then_block, else_block);
+                this.cfg.terminate(block, source_info, term);
 
                 unpack!(then_block = this.into(destination, then_block, then_expr));
                 else_block = if let Some(else_expr) = else_expr {
@@ -114,14 +112,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                     LogicalOp::And => (else_block, false_block),
                     LogicalOp::Or => (true_block, else_block),
                 };
-                this.cfg.terminate(block, source_info,
-                                   TerminatorKind::If { cond: lhs, targets: blocks });
+                let term = TerminatorKind::if_(this.hir.tcx(), lhs, blocks.0, blocks.1);
+                this.cfg.terminate(block, source_info, term);
 
                 let rhs = unpack!(else_block = this.as_operand(else_block, rhs));
-                this.cfg.terminate(else_block, source_info, TerminatorKind::If {
-                    cond: rhs,
-                    targets: (true_block, false_block)
-                });
+                let term = TerminatorKind::if_(this.hir.tcx(), rhs, true_block, false_block);
+                this.cfg.terminate(else_block, source_info, term);
 
                 this.cfg.push_assign_constant(
                     true_block, source_info, destination,
@@ -179,11 +175,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                             let cond = unpack!(
                                 loop_block_end = this.as_operand(loop_block, cond_expr));
                             body_block = this.cfg.start_new_block();
-                            this.cfg.terminate(loop_block_end, source_info,
-                                               TerminatorKind::If {
-                                                   cond: cond,
-                                                   targets: (body_block, exit_block)
-                                               });
+                            let term = TerminatorKind::if_(this.hir.tcx(), cond,
+                                                           body_block, exit_block);
+                            this.cfg.terminate(loop_block_end, source_info, term);
 
                             // if the test is false, there's no `break` to assign `destination`, so
                             // we have to do it; this overwrites any `break`-assigned value but it's
diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs
index 5c3d93ffda9..a28bc5d6ce3 100644
--- a/src/librustc_mir/build/matches/mod.rs
+++ b/src/librustc_mir/build/matches/mod.rs
@@ -673,8 +673,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             let cond = unpack!(block = self.as_operand(block, guard));
             let otherwise = self.cfg.start_new_block();
             self.cfg.terminate(block, source_info,
-                               TerminatorKind::If { cond: cond,
-                                                    targets: (arm_block, otherwise)});
+                               TerminatorKind::if_(self.hir.tcx(), cond, arm_block, otherwise));
             Some(otherwise)
         } else {
             let source_info = self.source_info(candidate.span);
diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs
index a35b2925ae2..01c0433112b 100644
--- a/src/librustc_mir/build/matches/test.rs
+++ b/src/librustc_mir/build/matches/test.rs
@@ -20,8 +20,9 @@ use build::matches::{Candidate, MatchPair, Test, TestKind};
 use hair::*;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::bitvec::BitVector;
-use rustc::middle::const_val::ConstVal;
+use rustc::middle::const_val::{ConstVal, ConstInt};
 use rustc::ty::{self, Ty};
+use rustc::ty::util::IntTypeExt;
 use rustc::mir::*;
 use rustc::hir::RangeEnd;
 use syntax_pos::Span;
@@ -182,74 +183,82 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         let source_info = self.source_info(test.span);
         match test.kind {
             TestKind::Switch { adt_def, ref variants } => {
+                // Variants is a BitVec of indexes into adt_def.variants.
                 let num_enum_variants = self.hir.num_variants(adt_def);
+                let used_variants = variants.count();
                 let mut otherwise_block = None;
-                let target_blocks: Vec<_> = (0..num_enum_variants).map(|i| {
-                    if variants.contains(i) {
-                        self.cfg.start_new_block()
+                let mut target_blocks = Vec::with_capacity(num_enum_variants);
+                let mut targets = Vec::with_capacity(used_variants + 1);
+                let mut values = Vec::with_capacity(used_variants);
+                let tcx = self.hir.tcx();
+                for (idx, variant) in adt_def.variants.iter().enumerate() {
+                    target_blocks.place_back() <- if variants.contains(idx) {
+                        let discr = ConstInt::new_inttype(variant.disr_val, adt_def.discr_ty,
+                                                          tcx.sess.target.uint_type,
+                                                          tcx.sess.target.int_type).unwrap();
+                        values.push(discr);
+                        *(targets.place_back() <- self.cfg.start_new_block())
                     } else {
                         if otherwise_block.is_none() {
                             otherwise_block = Some(self.cfg.start_new_block());
                         }
                         otherwise_block.unwrap()
-                    }
-                }).collect();
-                debug!("num_enum_variants: {}, num tested variants: {}, variants: {:?}",
-                       num_enum_variants, variants.iter().count(), variants);
-                self.cfg.terminate(block, source_info, TerminatorKind::Switch {
-                    discr: lvalue.clone(),
-                    adt_def: adt_def,
-                    targets: target_blocks.clone()
+                    };
+                }
+                if let Some(otherwise_block) = otherwise_block {
+                    targets.push(otherwise_block);
+                } else {
+                    values.pop();
+                }
+                debug!("num_enum_variants: {}, tested variants: {:?}, variants: {:?}",
+                       num_enum_variants, values, variants);
+                let discr_ty = adt_def.discr_ty.to_ty(tcx);
+                let discr = self.temp(discr_ty);
+                self.cfg.push_assign(block, source_info, &discr,
+                                     Rvalue::Discriminant(lvalue.clone()));
+                assert_eq!(values.len() + 1, targets.len());
+                self.cfg.terminate(block, source_info, TerminatorKind::SwitchInt {
+                    discr: Operand::Consume(discr),
+                    switch_ty: discr_ty,
+                    values: From::from(values),
+                    targets: targets
                 });
                 target_blocks
             }
 
             TestKind::SwitchInt { switch_ty, ref options, indices: _ } => {
-                let (targets, term) = match switch_ty.sty {
-                    // If we're matching on boolean we can
-                    // use the If TerminatorKind instead
-                    ty::TyBool => {
-                        assert!(options.len() > 0 && options.len() <= 2);
-
-                        let (true_bb, else_bb) =
-                            (self.cfg.start_new_block(),
-                             self.cfg.start_new_block());
-
-                        let targets = match &options[0] {
-                            &ConstVal::Bool(true) => vec![true_bb, else_bb],
-                            &ConstVal::Bool(false) => vec![else_bb, true_bb],
-                            v => span_bug!(test.span, "expected boolean value but got {:?}", v)
-                        };
-
-                        (targets,
-                         TerminatorKind::If {
-                             cond: Operand::Consume(lvalue.clone()),
-                             targets: (true_bb, else_bb)
-                         })
-
-                    }
-                    _ => {
-                        // The switch may be inexhaustive so we
-                        // add a catch all block
-                        let otherwise = self.cfg.start_new_block();
-                        let targets: Vec<_> =
-                            options.iter()
-                                   .map(|_| self.cfg.start_new_block())
-                                   .chain(Some(otherwise))
-                                   .collect();
-
-                        (targets.clone(),
-                         TerminatorKind::SwitchInt {
-                             discr: lvalue.clone(),
-                             switch_ty: switch_ty,
-                             values: options.clone(),
-                             targets: targets
-                         })
-                    }
+                let (ret, terminator) = if switch_ty.sty == ty::TyBool {
+                    assert!(options.len() > 0 && options.len() <= 2);
+                    let (true_bb, false_bb) = (self.cfg.start_new_block(),
+                                               self.cfg.start_new_block());
+                    let ret = match &options[0] {
+                        &ConstVal::Bool(true) => vec![true_bb, false_bb],
+                        &ConstVal::Bool(false) => vec![false_bb, true_bb],
+                        v => span_bug!(test.span, "expected boolean value but got {:?}", v)
+                    };
+                    (ret, TerminatorKind::if_(self.hir.tcx(), Operand::Consume(lvalue.clone()),
+                                              true_bb, false_bb))
+                } else {
+                    // The switch may be inexhaustive so we
+                    // add a catch all block
+                    let otherwise = self.cfg.start_new_block();
+                    let targets: Vec<_> =
+                        options.iter()
+                               .map(|_| self.cfg.start_new_block())
+                               .chain(Some(otherwise))
+                               .collect();
+                    let values: Vec<_> = options.iter().map(|v|
+                        v.to_const_int().expect("switching on integral")
+                    ).collect();
+                    (targets.clone(), TerminatorKind::SwitchInt {
+                        discr: Operand::Consume(lvalue.clone()),
+                        switch_ty: switch_ty,
+                        values: From::from(values),
+                        targets: targets,
+                    })
                 };
-
-                self.cfg.terminate(block, source_info, term);
-                targets
+                self.cfg.terminate(block, source_info, terminator);
+                ret
             }
 
             TestKind::Eq { ref value, mut ty } => {
@@ -314,11 +323,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
 
                     // check the result
                     let block = self.cfg.start_new_block();
-                    self.cfg.terminate(eq_block, source_info, TerminatorKind::If {
-                        cond: Operand::Consume(eq_result),
-                        targets: (block, fail),
-                    });
-
+                    self.cfg.terminate(eq_block, source_info,
+                                       TerminatorKind::if_(self.hir.tcx(),
+                                                           Operand::Consume(eq_result),
+                                                           block, fail));
                     vec![block, fail]
                 } else {
                     let block = self.compare(block, fail, test.span, BinOp::Eq, expect, val);
@@ -360,14 +368,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                                                       Operand::Consume(expected)));
 
                 // branch based on result
-                let target_blocks: Vec<_> = vec![self.cfg.start_new_block(),
-                                                 self.cfg.start_new_block()];
-                self.cfg.terminate(block, source_info, TerminatorKind::If {
-                    cond: Operand::Consume(result),
-                    targets: (target_blocks[0], target_blocks[1])
-                });
-
-                target_blocks
+                let (false_bb, true_bb) = (self.cfg.start_new_block(),
+                                           self.cfg.start_new_block());
+                self.cfg.terminate(block, source_info,
+                                   TerminatorKind::if_(self.hir.tcx(), Operand::Consume(result),
+                                                       true_bb, false_bb));
+                vec![true_bb, false_bb]
             }
         }
     }
@@ -389,11 +395,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
 
         // branch based on result
         let target_block = self.cfg.start_new_block();
-        self.cfg.terminate(block, source_info, TerminatorKind::If {
-            cond: Operand::Consume(result),
-            targets: (target_block, fail_block)
-        });
-
+        self.cfg.terminate(block, source_info,
+                           TerminatorKind::if_(self.hir.tcx(), Operand::Consume(result),
+                                               target_block, fail_block));
         target_block
     }
 
diff --git a/src/librustc_mir/hair/mod.rs b/src/librustc_mir/hair/mod.rs
index 01dc01c5ecf..4ac67cfb2fc 100644
--- a/src/librustc_mir/hair/mod.rs
+++ b/src/librustc_mir/hair/mod.rs
@@ -134,7 +134,8 @@ pub enum ExprKind<'tcx> {
         op: LogicalOp,
         lhs: ExprRef<'tcx>,
         rhs: ExprRef<'tcx>,
-    },
+    }, // NOT overloaded!
+       // LogicalOp is distinct from BinaryOp because of lazy evaluation of the operands.
     Unary {
         op: UnOp,
         arg: ExprRef<'tcx>,
diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs
index 61ba9d90fef..9a8fb1099d0 100644
--- a/src/librustc_mir/lib.rs
+++ b/src/librustc_mir/lib.rs
@@ -26,6 +26,8 @@ Rust MIR: a lowered representation of Rust. Also: an experiment!
 #![feature(rustc_diagnostic_macros)]
 #![feature(rustc_private)]
 #![feature(staged_api)]
+#![feature(placement_in_syntax)]
+#![feature(collection_placement)]
 
 #[macro_use] extern crate log;
 extern crate graphviz as dot;
diff --git a/src/librustc_mir/transform/no_landing_pads.rs b/src/librustc_mir/transform/no_landing_pads.rs
index 6ef5720b330..55a26f4b37f 100644
--- a/src/librustc_mir/transform/no_landing_pads.rs
+++ b/src/librustc_mir/transform/no_landing_pads.rs
@@ -28,8 +28,6 @@ impl<'tcx> MutVisitor<'tcx> for NoLandingPads {
             TerminatorKind::Resume |
             TerminatorKind::Return |
             TerminatorKind::Unreachable |
-            TerminatorKind::If { .. } |
-            TerminatorKind::Switch { .. } |
             TerminatorKind::SwitchInt { .. } => {
                 /* nothing to do */
             },
diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs
index 9c1107344f2..922521726c6 100644
--- a/src/librustc_mir/transform/qualify_consts.rs
+++ b/src/librustc_mir/transform/qualify_consts.rs
@@ -394,8 +394,6 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
                     return Qualif::empty();
                 }
 
-                TerminatorKind::If {..} |
-                TerminatorKind::Switch {..} |
                 TerminatorKind::SwitchInt {..} |
                 TerminatorKind::DropAndReplace { .. } |
                 TerminatorKind::Resume |
@@ -739,6 +737,14 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
                 }
             }
 
+            Rvalue::Discriminant(..) => {
+                // FIXME discriminant
+                self.add(Qualif::NOT_CONST);
+                if self.mode != Mode::Fn {
+                    bug!("implement discriminant const qualify");
+                }
+            }
+
             Rvalue::Box(_) => {
                 self.add(Qualif::NOT_CONST);
                 if self.mode != Mode::Fn {
diff --git a/src/librustc_mir/transform/simplify.rs b/src/librustc_mir/transform/simplify.rs
index d5fc90289e2..e93a412dc74 100644
--- a/src/librustc_mir/transform/simplify.rs
+++ b/src/librustc_mir/transform/simplify.rs
@@ -209,8 +209,6 @@ impl<'a, 'tcx: 'a> CfgSimplifier<'a, 'tcx> {
     // turn a branch with all successors identical to a goto
     fn simplify_branch(&mut self, terminator: &mut Terminator<'tcx>) -> bool {
         match terminator.kind {
-            TerminatorKind::If { .. } |
-            TerminatorKind::Switch { .. } |
             TerminatorKind::SwitchInt { .. } => {},
             _ => return false
         };
diff --git a/src/librustc_mir/transform/simplify_branches.rs b/src/librustc_mir/transform/simplify_branches.rs
index 8759a340d7e..3d5106c4b06 100644
--- a/src/librustc_mir/transform/simplify_branches.rs
+++ b/src/librustc_mir/transform/simplify_branches.rs
@@ -30,26 +30,30 @@ impl<'l, 'tcx> MirPass<'tcx> for SimplifyBranches<'l> {
         for block in mir.basic_blocks_mut() {
             let terminator = block.terminator_mut();
             terminator.kind = match terminator.kind {
-                TerminatorKind::If { ref targets, cond: Operand::Constant(Constant {
-                    literal: Literal::Value {
-                        value: ConstVal::Bool(cond)
-                    }, ..
-                }) } => {
-                    if cond {
-                        TerminatorKind::Goto { target: targets.0 }
+                TerminatorKind::SwitchInt { discr: Operand::Constant(Constant {
+                    literal: Literal::Value { ref value }, ..
+                }), ref values, ref targets, .. } => {
+                    if let Some(ref constint) = value.to_const_int() {
+                        let (otherwise, targets) = targets.split_last().unwrap();
+                        let mut ret = TerminatorKind::Goto { target: *otherwise };
+                        for (v, t) in values.iter().zip(targets.iter()) {
+                            if v == constint {
+                                ret = TerminatorKind::Goto { target: *t };
+                                break;
+                            }
+                        }
+                        ret
                     } else {
-                        TerminatorKind::Goto { target: targets.1 }
+                        continue
                     }
-                }
-
+                },
                 TerminatorKind::Assert { target, cond: Operand::Constant(Constant {
                     literal: Literal::Value {
                         value: ConstVal::Bool(cond)
                     }, ..
                 }), expected, .. } if cond == expected => {
                     TerminatorKind::Goto { target: target }
-                }
-
+                },
                 _ => continue
             };
         }
diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs
index 529fe564af0..8ede7aaab5f 100644
--- a/src/librustc_mir/transform/type_check.rs
+++ b/src/librustc_mir/transform/type_check.rs
@@ -423,18 +423,8 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                                  lv_ty, rv_ty, terr);
                 }
             }
-
-            TerminatorKind::If { ref cond, .. } => {
-                let cond_ty = cond.ty(mir, tcx);
-                match cond_ty.sty {
-                    ty::TyBool => {}
-                    _ => {
-                        span_mirbug!(self, term, "bad If ({:?}, not bool", cond_ty);
-                    }
-                }
-            }
             TerminatorKind::SwitchInt { ref discr, switch_ty, .. } => {
-                let discr_ty = discr.ty(mir, tcx).to_ty(tcx);
+                let discr_ty = discr.ty(mir, tcx);
                 if let Err(terr) = self.sub_types(discr_ty, switch_ty) {
                     span_mirbug!(self, term, "bad SwitchInt ({:?} on {:?}): {:?}",
                                  switch_ty, discr_ty, terr);
@@ -446,19 +436,6 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                 }
                 // FIXME: check the values
             }
-            TerminatorKind::Switch { ref discr, adt_def, ref targets } => {
-                let discr_ty = discr.ty(mir, tcx).to_ty(tcx);
-                match discr_ty.sty {
-                    ty::TyAdt(def, _) if def.is_enum() &&
-                                         def == adt_def &&
-                                         adt_def.variants.len() == targets.len()
-                      => {},
-                    _ => {
-                        span_mirbug!(self, term, "bad Switch ({:?} on {:?})",
-                                     adt_def, discr_ty);
-                    }
-                }
-            }
             TerminatorKind::Call { ref func, ref args, ref destination, .. } => {
                 let func_ty = func.ty(mir, tcx);
                 debug!("check_terminator: call, func_ty={:?}", func_ty);
@@ -603,11 +580,6 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
         match block.terminator().kind {
             TerminatorKind::Goto { target } =>
                 self.assert_iscleanup(mir, block, target, is_cleanup),
-            TerminatorKind::If { targets: (on_true, on_false), .. } => {
-                self.assert_iscleanup(mir, block, on_true, is_cleanup);
-                self.assert_iscleanup(mir, block, on_false, is_cleanup);
-            }
-            TerminatorKind::Switch { ref targets, .. } |
             TerminatorKind::SwitchInt { ref targets, .. } => {
                 for target in targets {
                     self.assert_iscleanup(mir, block, *target, is_cleanup);
diff --git a/src/librustc_passes/mir_stats.rs b/src/librustc_passes/mir_stats.rs
index cec1c20519b..517a4720563 100644
--- a/src/librustc_passes/mir_stats.rs
+++ b/src/librustc_passes/mir_stats.rs
@@ -148,8 +148,6 @@ impl<'a, 'tcx> mir_visit::Visitor<'tcx> for StatCollector<'a, 'tcx> {
         self.record("TerminatorKind", kind);
         self.record(match *kind {
             TerminatorKind::Goto { .. } => "TerminatorKind::Goto",
-            TerminatorKind::If { .. } => "TerminatorKind::If",
-            TerminatorKind::Switch { .. } => "TerminatorKind::Switch",
             TerminatorKind::SwitchInt { .. } => "TerminatorKind::SwitchInt",
             TerminatorKind::Resume => "TerminatorKind::Resume",
             TerminatorKind::Return => "TerminatorKind::Return",
@@ -186,6 +184,7 @@ impl<'a, 'tcx> mir_visit::Visitor<'tcx> for StatCollector<'a, 'tcx> {
             Rvalue::BinaryOp(..) => "Rvalue::BinaryOp",
             Rvalue::CheckedBinaryOp(..) => "Rvalue::CheckedBinaryOp",
             Rvalue::UnaryOp(..) => "Rvalue::UnaryOp",
+            Rvalue::Discriminant(..) => "Rvalue::Discriminant",
             Rvalue::Box(..) => "Rvalue::Box",
             Rvalue::Aggregate(ref kind, ref _operands) => {
                 // AggregateKind is not distinguished by visit API, so
diff --git a/src/librustc_trans/adt.rs b/src/librustc_trans/adt.rs
index 59f2104ec14..11d3fae8238 100644
--- a/src/librustc_trans/adt.rs
+++ b/src/librustc_trans/adt.rs
@@ -318,7 +318,7 @@ pub fn trans_get_discr<'a, 'tcx>(
     };
     match cast_to {
         None => val,
-        Some(llty) => if is_discr_signed(&l) { bcx.sext(val, llty) } else { bcx.zext(val, llty) }
+        Some(llty) => bcx.intcast(val, llty, is_discr_signed(&l))
     }
 }
 
diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs
index ce02c9725d1..41c0eaa52a7 100644
--- a/src/librustc_trans/base.rs
+++ b/src/librustc_trans/base.rs
@@ -536,7 +536,7 @@ pub fn call_memcpy<'a, 'tcx>(b: &Builder<'a, 'tcx>,
     let memcpy = ccx.get_intrinsic(&key);
     let src_ptr = b.pointercast(src, Type::i8p(ccx));
     let dst_ptr = b.pointercast(dst, Type::i8p(ccx));
-    let size = b.intcast(n_bytes, ccx.int_type());
+    let size = b.intcast(n_bytes, ccx.int_type(), false);
     let align = C_i32(ccx, align as i32);
     let volatile = C_bool(ccx, false);
     b.call(memcpy, &[dst_ptr, src_ptr, size, align, volatile], None);
diff --git a/src/librustc_trans/builder.rs b/src/librustc_trans/builder.rs
index 89aaa8b6630..f64e581c177 100644
--- a/src/librustc_trans/builder.rs
+++ b/src/librustc_trans/builder.rs
@@ -780,10 +780,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         }
     }
 
-    pub fn intcast(&self, val: ValueRef, dest_ty: Type) -> ValueRef {
+    pub fn intcast(&self, val: ValueRef, dest_ty: Type, is_signed: bool) -> ValueRef {
         self.count_insn("intcast");
         unsafe {
-            llvm::LLVMBuildIntCast(self.llbuilder, val, dest_ty.to_ref(), noname())
+            llvm::LLVMRustBuildIntCast(self.llbuilder, val, dest_ty.to_ref(), is_signed)
         }
     }
 
diff --git a/src/librustc_trans/debuginfo/metadata.rs b/src/librustc_trans/debuginfo/metadata.rs
index c453d9bbd05..11c0bf852f7 100644
--- a/src/librustc_trans/debuginfo/metadata.rs
+++ b/src/librustc_trans/debuginfo/metadata.rs
@@ -1476,7 +1476,7 @@ fn prepare_enum_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                     DIB(cx),
                     name.as_ptr(),
                     // FIXME: what if enumeration has i128 discriminant?
-                    v.disr_val.to_u128_unchecked() as u64)
+                    v.disr_val as u64)
             }
         })
         .collect();
diff --git a/src/librustc_trans/disr.rs b/src/librustc_trans/disr.rs
index c5737c6e5f1..f3a62bc85b8 100644
--- a/src/librustc_trans/disr.rs
+++ b/src/librustc_trans/disr.rs
@@ -27,7 +27,7 @@ impl ::std::ops::BitAnd for Disr {
 impl From<::rustc::ty::Disr> for Disr {
     fn from(i: ::rustc::ty::Disr) -> Disr {
         // FIXME: what if discr has 128 bit discr?
-        Disr(i.to_u128_unchecked() as u64)
+        Disr(i as u64)
     }
 }
 
diff --git a/src/librustc_trans/mir/analyze.rs b/src/librustc_trans/mir/analyze.rs
index 2a1ab10d74e..2c3b479c7dd 100644
--- a/src/librustc_trans/mir/analyze.rs
+++ b/src/librustc_trans/mir/analyze.rs
@@ -156,10 +156,10 @@ impl<'mir, 'a, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'tcx> {
 
                 LvalueContext::StorageLive |
                 LvalueContext::StorageDead |
+                LvalueContext::Inspect |
                 LvalueContext::Consume => {}
 
                 LvalueContext::Store |
-                LvalueContext::Inspect |
                 LvalueContext::Borrow { .. } |
                 LvalueContext::Projection(..) => {
                     self.mark_as_lvalue(index);
@@ -204,8 +204,6 @@ pub fn cleanup_kinds<'a, 'tcx>(mir: &mir::Mir<'tcx>) -> IndexVec<mir::BasicBlock
                 TerminatorKind::Resume |
                 TerminatorKind::Return |
                 TerminatorKind::Unreachable |
-                TerminatorKind::If { .. } |
-                TerminatorKind::Switch { .. } |
                 TerminatorKind::SwitchInt { .. } => {
                     /* nothing to do */
                 }
diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs
index f04f7ab9ed1..651d0066b12 100644
--- a/src/librustc_trans/mir/block.rs
+++ b/src/librustc_trans/mir/block.rs
@@ -11,17 +11,16 @@
 use llvm::{self, ValueRef, BasicBlockRef};
 use rustc_const_eval::{ErrKind, ConstEvalErr, note_const_eval_err};
 use rustc::middle::lang_items;
+use rustc::middle::const_val::ConstInt;
 use rustc::ty::{self, layout, TypeFoldable};
 use rustc::mir;
 use abi::{Abi, FnType, ArgType};
-use adt;
 use base::{self, Lifetime};
 use callee::{Callee, CalleeData, Fn, Intrinsic, NamedTupleConstructor, Virtual};
 use builder::Builder;
 use common::{self, Funclet};
 use common::{C_bool, C_str_slice, C_struct, C_u32, C_undef};
 use consts;
-use Disr;
 use machine::{llalign_of_min, llbitsize_of_real};
 use meth;
 use type_of::{self, align_of};
@@ -29,7 +28,6 @@ use glue;
 use type_::Type;
 
 use rustc_data_structures::indexed_vec::IndexVec;
-use rustc_data_structures::fx::FxHashMap;
 use syntax::symbol::Symbol;
 
 use std::cmp;
@@ -136,62 +134,28 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
                 funclet_br(self, bcx, target);
             }
 
-            mir::TerminatorKind::If { ref cond, targets: (true_bb, false_bb) } => {
-                let cond = self.trans_operand(&bcx, cond);
-
-                let lltrue = llblock(self, true_bb);
-                let llfalse = llblock(self, false_bb);
-                bcx.cond_br(cond.immediate(), lltrue, llfalse);
-            }
-
-            mir::TerminatorKind::Switch { ref discr, ref adt_def, ref targets } => {
-                let discr_lvalue = self.trans_lvalue(&bcx, discr);
-                let ty = discr_lvalue.ty.to_ty(bcx.tcx());
-                let discr = adt::trans_get_discr(
-                    &bcx, ty, discr_lvalue.llval, discr_lvalue.alignment,
-                    None, true);
-
-                let mut bb_hist = FxHashMap();
-                for target in targets {
-                    *bb_hist.entry(target).or_insert(0) += 1;
-                }
-                let (default_bb, default_blk) = match bb_hist.iter().max_by_key(|&(_, c)| c) {
-                    // If a single target basic blocks is predominant, promote that to be the
-                    // default case for the switch instruction to reduce the size of the generated
-                    // code. This is especially helpful in cases like an if-let on a huge enum.
-                    // Note: This optimization is only valid for exhaustive matches.
-                    Some((&&bb, &c)) if c > targets.len() / 2 => {
-                        (Some(bb), llblock(self, bb))
+            mir::TerminatorKind::SwitchInt { ref discr, switch_ty, ref values, ref targets } => {
+                let discr = self.trans_operand(&bcx, discr);
+                if switch_ty == bcx.tcx().types.bool {
+                    let lltrue = llblock(self, targets[0]);
+                    let llfalse = llblock(self, targets[1]);
+                    if let [ConstInt::Infer(0)] = values[..] {
+                        bcx.cond_br(discr.immediate(), llfalse, lltrue);
+                    } else {
+                        bcx.cond_br(discr.immediate(), lltrue, llfalse);
                     }
-                    // We're generating an exhaustive switch, so the else branch
-                    // can't be hit.  Branching to an unreachable instruction
-                    // lets LLVM know this
-                    _ => (None, self.unreachable_block())
-                };
-                let switch = bcx.switch(discr, default_blk, targets.len());
-                assert_eq!(adt_def.variants.len(), targets.len());
-                for (adt_variant, &target) in adt_def.variants.iter().zip(targets) {
-                    if default_bb != Some(target) {
-                        let llbb = llblock(self, target);
-                        let llval = adt::trans_case(&bcx, ty, Disr::from(adt_variant.disr_val));
-                        bcx.add_case(switch, llval, llbb)
+                } else {
+                    let (otherwise, targets) = targets.split_last().unwrap();
+                    let switch = bcx.switch(discr.immediate(),
+                                            llblock(self, *otherwise), values.len());
+                    for (value, target) in values.iter().zip(targets) {
+                        let val = Const::from_constint(bcx.ccx, value);
+                        let llbb = llblock(self, *target);
+                        bcx.add_case(switch, val.llval, llbb)
                     }
                 }
             }
 
-            mir::TerminatorKind::SwitchInt { ref discr, switch_ty, ref values, ref targets } => {
-                let (otherwise, targets) = targets.split_last().unwrap();
-                let lv = self.trans_lvalue(&bcx, discr);
-                let discr = bcx.load(lv.llval, lv.alignment.to_align());
-                let discr = base::to_immediate(&bcx, discr, switch_ty);
-                let switch = bcx.switch(discr, llblock(self, *otherwise), values.len());
-                for (value, target) in values.iter().zip(targets) {
-                    let val = Const::from_constval(bcx.ccx, value.clone(), switch_ty);
-                    let llbb = llblock(self, *target);
-                    bcx.add_case(switch, val.llval, llbb)
-                }
-            }
-
             mir::TerminatorKind::Return => {
                 let ret = self.fn_ty.ret;
                 if ret.is_ignore() || ret.is_indirect() {
diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs
index 11668e792e3..19139301bb0 100644
--- a/src/librustc_trans/mir/constant.rs
+++ b/src/librustc_trans/mir/constant.rs
@@ -61,6 +61,33 @@ impl<'tcx> Const<'tcx> {
         }
     }
 
+    pub fn from_constint<'a>(ccx: &CrateContext<'a, 'tcx>, ci: &ConstInt)
+    -> Const<'tcx> {
+        let tcx = ccx.tcx();
+        let (llval, ty) = match *ci {
+            I8(v) => (C_integral(Type::i8(ccx), v as u64, true), tcx.types.i8),
+            I16(v) => (C_integral(Type::i16(ccx), v as u64, true), tcx.types.i16),
+            I32(v) => (C_integral(Type::i32(ccx), v as u64, true), tcx.types.i32),
+            I64(v) => (C_integral(Type::i64(ccx), v as u64, true), tcx.types.i64),
+            I128(v) => (C_big_integral(Type::i128(ccx), v as u128), tcx.types.i128),
+            Isize(v) => {
+                let i = v.as_i64(ccx.tcx().sess.target.int_type);
+                (C_integral(Type::int(ccx), i as u64, true), tcx.types.isize)
+            },
+            U8(v) => (C_integral(Type::i8(ccx), v as u64, false), tcx.types.u8),
+            U16(v) => (C_integral(Type::i16(ccx), v as u64, false), tcx.types.u16),
+            U32(v) => (C_integral(Type::i32(ccx), v as u64, false), tcx.types.u32),
+            U64(v) => (C_integral(Type::i64(ccx), v, false), tcx.types.u64),
+            U128(v) => (C_big_integral(Type::i128(ccx), v), tcx.types.u128),
+            Usize(v) => {
+                let u = v.as_u64(ccx.tcx().sess.target.uint_type);
+                (C_integral(Type::int(ccx), u, false), tcx.types.usize)
+            },
+            Infer(_) | InferSigned(_) => bug!("MIR must not use `{:?}`", ci),
+        };
+        Const { llval: llval, ty: ty }
+    }
+
     /// Translate ConstVal into a LLVM constant value.
     pub fn from_constval<'a>(ccx: &CrateContext<'a, 'tcx>,
                              cv: ConstVal,
@@ -72,26 +99,7 @@ impl<'tcx> Const<'tcx> {
             ConstVal::Float(F64(v)) => C_floating_f64(v, llty),
             ConstVal::Float(FInfer {..}) => bug!("MIR must not use `{:?}`", cv),
             ConstVal::Bool(v) => C_bool(ccx, v),
-            ConstVal::Integral(I8(v)) => C_integral(Type::i8(ccx), v as u64, true),
-            ConstVal::Integral(I16(v)) => C_integral(Type::i16(ccx), v as u64, true),
-            ConstVal::Integral(I32(v)) => C_integral(Type::i32(ccx), v as u64, true),
-            ConstVal::Integral(I64(v)) => C_integral(Type::i64(ccx), v as u64, true),
-            ConstVal::Integral(I128(v)) => C_big_integral(Type::i128(ccx), v as u128),
-            ConstVal::Integral(Isize(v)) => {
-                let i = v.as_i64(ccx.tcx().sess.target.int_type);
-                C_integral(Type::int(ccx), i as u64, true)
-            },
-            ConstVal::Integral(U8(v)) => C_integral(Type::i8(ccx), v as u64, false),
-            ConstVal::Integral(U16(v)) => C_integral(Type::i16(ccx), v as u64, false),
-            ConstVal::Integral(U32(v)) => C_integral(Type::i32(ccx), v as u64, false),
-            ConstVal::Integral(U64(v)) => C_integral(Type::i64(ccx), v, false),
-            ConstVal::Integral(U128(v)) => C_big_integral(Type::i128(ccx), v),
-            ConstVal::Integral(Usize(v)) => {
-                let u = v.as_u64(ccx.tcx().sess.target.uint_type);
-                C_integral(Type::int(ccx), u, false)
-            },
-            ConstVal::Integral(Infer(_)) |
-            ConstVal::Integral(InferSigned(_)) => bug!("MIR must not use `{:?}`", cv),
+            ConstVal::Integral(ref i) => return Const::from_constint(ccx, i),
             ConstVal::Str(ref v) => C_str_slice(ccx, v.clone()),
             ConstVal::ByteStr(ref v) => consts::addr_of(ccx, C_bytes(ccx, v), 1, "byte_str"),
             ConstVal::Struct(_) | ConstVal::Tuple(_) |
diff --git a/src/librustc_trans/mir/rvalue.rs b/src/librustc_trans/mir/rvalue.rs
index 0a8e676b078..38ee67796c6 100644
--- a/src/librustc_trans/mir/rvalue.rs
+++ b/src/librustc_trans/mir/rvalue.rs
@@ -286,17 +286,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
 
                         let newval = match (r_t_in, r_t_out) {
                             (CastTy::Int(_), CastTy::Int(_)) => {
-                                let srcsz = ll_t_in.int_width();
-                                let dstsz = ll_t_out.int_width();
-                                if srcsz == dstsz {
-                                    bcx.bitcast(llval, ll_t_out)
-                                } else if srcsz > dstsz {
-                                    bcx.trunc(llval, ll_t_out)
-                                } else if signed {
-                                    bcx.sext(llval, ll_t_out)
-                                } else {
-                                    bcx.zext(llval, ll_t_out)
-                                }
+                                bcx.intcast(llval, ll_t_out, signed)
                             }
                             (CastTy::Float, CastTy::Float) => {
                                 let srcsz = ll_t_in.float_width();
@@ -433,6 +423,19 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
                 })
             }
 
+            mir::Rvalue::Discriminant(ref lvalue) => {
+                let discr_lvalue = self.trans_lvalue(&bcx, lvalue);
+                let enum_ty = discr_lvalue.ty.to_ty(bcx.tcx());
+                let discr_ty = rvalue.ty(&*self.mir, bcx.tcx()).unwrap();
+                let discr_type = type_of::immediate_type_of(bcx.ccx, discr_ty);
+                let discr = adt::trans_get_discr(&bcx, enum_ty, discr_lvalue.llval,
+                                                  discr_lvalue.alignment, Some(discr_type), true);
+                (bcx, OperandRef {
+                    val: OperandValue::Immediate(discr),
+                    ty: discr_ty
+                })
+            }
+
             mir::Rvalue::Box(content_ty) => {
                 let content_ty: Ty<'tcx> = self.monomorphize(&content_ty);
                 let llty = type_of::type_of(bcx.ccx, content_ty);
@@ -661,6 +664,7 @@ pub fn rvalue_creates_operand(rvalue: &mir::Rvalue) -> bool {
         mir::Rvalue::BinaryOp(..) |
         mir::Rvalue::CheckedBinaryOp(..) |
         mir::Rvalue::UnaryOp(..) |
+        mir::Rvalue::Discriminant(..) |
         mir::Rvalue::Box(..) |
         mir::Rvalue::Use(..) =>
             true,
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index 7936db65c44..217405a81ec 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -1004,9 +1004,8 @@ fn convert_struct_def<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
     let did = ccx.tcx.hir.local_def_id(it.id);
     // Use separate constructor id for unit/tuple structs and reuse did for braced structs.
     let ctor_id = if !def.is_struct() { Some(ccx.tcx.hir.local_def_id(def.id())) } else { None };
-    let variants = vec![convert_struct_variant(ccx, ctor_id.unwrap_or(did), it.name,
-                                               ConstInt::Infer(0), def)];
-    let adt = ccx.tcx.alloc_adt_def(did, AdtKind::Struct, variants,
+    let variants = vec![convert_struct_variant(ccx, ctor_id.unwrap_or(did), it.name, 0, def)];
+    let adt = ccx.tcx.alloc_adt_def(did, AdtKind::Struct, None, variants,
         ReprOptions::new(&ccx.tcx, did));
     if let Some(ctor_id) = ctor_id {
         // Make adt definition available through constructor id as well.
@@ -1023,63 +1022,65 @@ fn convert_union_def<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
                                 -> &'tcx ty::AdtDef
 {
     let did = ccx.tcx.hir.local_def_id(it.id);
-    let variants = vec![convert_struct_variant(ccx, did, it.name, ConstInt::Infer(0), def)];
-
-    let adt = ccx.tcx.alloc_adt_def(did, AdtKind::Union, variants, ReprOptions::new(&ccx.tcx, did));
+    let variants = vec![convert_struct_variant(ccx, did, it.name, 0, def)];
+    let adt = ccx.tcx.alloc_adt_def(did, AdtKind::Union, None, variants,
+                                    ReprOptions::new(&ccx.tcx, did));
     ccx.tcx.adt_defs.borrow_mut().insert(did, adt);
     adt
 }
 
-    fn evaluate_disr_expr(ccx: &CrateCtxt, repr_ty: attr::IntType, body: hir::BodyId)
-                          -> Option<ty::Disr> {
-        let e = &ccx.tcx.hir.body(body).value;
-        debug!("disr expr, checking {}", ccx.tcx.hir.node_to_pretty_string(e.id));
-
-        let ty_hint = repr_ty.to_ty(ccx.tcx);
-        let print_err = |cv: ConstVal| {
-            struct_span_err!(ccx.tcx.sess, e.span, E0079, "mismatched types")
-                .note_expected_found(&"type", &ty_hint, &format!("{}", cv.description()))
-                .span_label(e.span, &format!("expected '{}' type", ty_hint))
-                .emit();
-        };
+fn evaluate_disr_expr(ccx: &CrateCtxt, repr_ty: attr::IntType, body: hir::BodyId)
+                      -> Option<ConstInt> {
+    let e = &ccx.tcx.hir.body(body).value;
+    debug!("disr expr, checking {}", ccx.tcx.hir.node_to_pretty_string(e.id));
+
+    let ty_hint = repr_ty.to_ty(ccx.tcx);
+    let print_err = |cv: ConstVal| {
+        struct_span_err!(ccx.tcx.sess, e.span, E0079, "mismatched types")
+            .note_expected_found(&"type", &ty_hint, &format!("{}", cv.description()))
+            .span_label(e.span, &format!("expected '{}' type", ty_hint))
+            .emit();
+    };
 
-        let hint = UncheckedExprHint(ty_hint);
-        match ConstContext::new(ccx.tcx, body).eval(e, hint) {
-            Ok(ConstVal::Integral(i)) => {
-                // FIXME: eval should return an error if the hint is wrong
-                match (repr_ty, i) {
-                    (attr::SignedInt(ast::IntTy::I8), ConstInt::I8(_)) |
-                    (attr::SignedInt(ast::IntTy::I16), ConstInt::I16(_)) |
-                    (attr::SignedInt(ast::IntTy::I32), ConstInt::I32(_)) |
-                    (attr::SignedInt(ast::IntTy::I64), ConstInt::I64(_)) |
-                    (attr::SignedInt(ast::IntTy::I128), ConstInt::I128(_)) |
-                    (attr::SignedInt(ast::IntTy::Is), ConstInt::Isize(_)) |
-                    (attr::UnsignedInt(ast::UintTy::U8), ConstInt::U8(_)) |
-                    (attr::UnsignedInt(ast::UintTy::U16), ConstInt::U16(_)) |
-                    (attr::UnsignedInt(ast::UintTy::U32), ConstInt::U32(_)) |
-                    (attr::UnsignedInt(ast::UintTy::U64), ConstInt::U64(_)) |
-                    (attr::UnsignedInt(ast::UintTy::U128), ConstInt::U128(_)) |
-                    (attr::UnsignedInt(ast::UintTy::Us), ConstInt::Usize(_)) => Some(i),
-                    (_, i) => {
-                        print_err(ConstVal::Integral(i));
-                        None
-                    },
-                }
-            },
-            Ok(cv) => {
-                print_err(cv);
-                None
-            },
-            // enum variant evaluation happens before the global constant check
-            // so we need to report the real error
-            Err(err) => {
-                let mut diag = report_const_eval_err(
-                    ccx.tcx, &err, e.span, "enum discriminant");
-                diag.emit();
-                None
+    let hint = UncheckedExprHint(ty_hint);
+    match ConstContext::new(ccx.tcx, body).eval(e, hint) {
+        Ok(ConstVal::Integral(i)) => {
+            // FIXME: eval should return an error if the hint does not match the type of the body.
+            // i.e. eventually the match below would not exist.
+            match (repr_ty, i) {
+                (attr::SignedInt(ast::IntTy::I8), ConstInt::I8(_)) |
+                (attr::SignedInt(ast::IntTy::I16), ConstInt::I16(_)) |
+                (attr::SignedInt(ast::IntTy::I32), ConstInt::I32(_)) |
+                (attr::SignedInt(ast::IntTy::I64), ConstInt::I64(_)) |
+                (attr::SignedInt(ast::IntTy::I128), ConstInt::I128(_)) |
+                (attr::SignedInt(ast::IntTy::Is), ConstInt::Isize(_)) |
+                (attr::UnsignedInt(ast::UintTy::U8), ConstInt::U8(_)) |
+                (attr::UnsignedInt(ast::UintTy::U16), ConstInt::U16(_)) |
+                (attr::UnsignedInt(ast::UintTy::U32), ConstInt::U32(_)) |
+                (attr::UnsignedInt(ast::UintTy::U64), ConstInt::U64(_)) |
+                (attr::UnsignedInt(ast::UintTy::U128), ConstInt::U128(_)) |
+                (attr::UnsignedInt(ast::UintTy::Us), ConstInt::Usize(_)) =>
+                    Some(i),
+                (_, i) => {
+                    print_err(ConstVal::Integral(i));
+                    None
+                },
             }
+        },
+        Ok(cv) => {
+            print_err(cv);
+            None
+        },
+        // enum variant evaluation happens before the global constant check
+        // so we need to report the real error
+        Err(err) => {
+            let mut diag = report_const_eval_err(
+                ccx.tcx, &err, e.span, "enum discriminant");
+            diag.emit();
+            None
         }
     }
+}
 
 fn convert_enum_def<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
                               it: &hir::Item,
@@ -1090,13 +1091,17 @@ fn convert_enum_def<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
     let did = tcx.hir.local_def_id(it.id);
     let repr_hints = tcx.lookup_repr_hints(did);
     let repr_type = tcx.enum_repr_type(repr_hints.get(0));
-    let initial = repr_type.initial_discriminant(tcx);
-    let mut prev_disr = None::<ty::Disr>;
+    let initial = ConstInt::new_inttype(repr_type.initial_discriminant(tcx), repr_type,
+                                        tcx.sess.target.uint_type, tcx.sess.target.int_type)
+        .unwrap();
+    let mut prev_disr = None::<ConstInt>;
     let variants = def.variants.iter().map(|v| {
         let wrapped_disr = prev_disr.map_or(initial, |d| d.wrap_incr());
         let disr = if let Some(e) = v.node.disr_expr {
+            // FIXME: i128 discriminants
             evaluate_disr_expr(ccx, repr_type, e)
-        } else if let Some(disr) = repr_type.disr_incr(tcx, prev_disr) {
+        } else if let Some(disr) = prev_disr.map_or(Some(initial),
+                                                    |v| (v + ConstInt::Infer(1)).ok()) {
             Some(disr)
         } else {
             struct_span_err!(tcx.sess, v.span, E0370,
@@ -1108,12 +1113,11 @@ fn convert_enum_def<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
             None
         }.unwrap_or(wrapped_disr);
         prev_disr = Some(disr);
-
         let did = tcx.hir.local_def_id(v.node.data.id());
-        convert_struct_variant(ccx, did, v.node.name, disr, &v.node.data)
+        convert_struct_variant(ccx, did, v.node.name, disr.to_u128_unchecked(), &v.node.data)
     }).collect();
-
-    let adt = tcx.alloc_adt_def(did, AdtKind::Enum, variants, ReprOptions::new(&ccx.tcx, did));
+    let adt = tcx.alloc_adt_def(did, AdtKind::Enum, Some(repr_type), variants,
+                                ReprOptions::new(&ccx.tcx, did));
     tcx.adt_defs.borrow_mut().insert(did, adt);
     adt
 }
diff --git a/src/libserialize/serialize.rs b/src/libserialize/serialize.rs
index ba39fcdec6f..c6847249803 100644
--- a/src/libserialize/serialize.rs
+++ b/src/libserialize/serialize.rs
@@ -567,6 +567,34 @@ impl<T:Decodable> Decodable for Vec<T> {
     }
 }
 
+impl<'a, T:Encodable> Encodable for Cow<'a, [T]>
+where [T]: ToOwned<Owned = Vec<T>>
+{
+    fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
+        s.emit_seq(self.len(), |s| {
+            for (i, e) in self.iter().enumerate() {
+                s.emit_seq_elt(i, |s| e.encode(s))?
+            }
+            Ok(())
+        })
+    }
+}
+
+impl<T:Decodable+ToOwned> Decodable for Cow<'static, [T]>
+where [T]: ToOwned<Owned = Vec<T>>
+{
+    fn decode<D: Decoder>(d: &mut D) -> Result<Cow<'static, [T]>, D::Error> {
+        d.read_seq(|d, len| {
+            let mut v = Vec::with_capacity(len);
+            for i in 0..len {
+                v.push(d.read_seq_elt(i, |d| Decodable::decode(d))?);
+            }
+            Ok(Cow::Owned(v))
+        })
+    }
+}
+
+
 impl<T:Encodable> Encodable for Option<T> {
     fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
         s.emit_option(|s| {
diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp
index 40432735911..e89f48b4105 100644
--- a/src/rustllvm/RustWrapper.cpp
+++ b/src/rustllvm/RustWrapper.cpp
@@ -1316,6 +1316,12 @@ extern "C" LLVMRustVisibility LLVMRustGetVisibility(LLVMValueRef V) {
   return toRust(LLVMGetVisibility(V));
 }
 
+// Oh hey, a binding that makes sense for once? (because LLVM’s own do not)
+extern "C" LLVMValueRef LLVMRustBuildIntCast(LLVMBuilderRef B, LLVMValueRef Val,
+                                             LLVMTypeRef DestTy, bool isSigned) {
+  return wrap(unwrap(B)->CreateIntCast(unwrap(Val), unwrap(DestTy), isSigned, ""));
+}
+
 extern "C" void LLVMRustSetVisibility(LLVMValueRef V,
                                       LLVMRustVisibility RustVisibility) {
   LLVMSetVisibility(V, fromRust(RustVisibility));
diff --git a/src/test/codegen/match.rs b/src/test/codegen/match.rs
index ac47f6082e3..aa100da6013 100644
--- a/src/test/codegen/match.rs
+++ b/src/test/codegen/match.rs
@@ -20,9 +20,13 @@ pub enum E {
 // CHECK-LABEL: @exhaustive_match
 #[no_mangle]
 pub fn exhaustive_match(e: E) {
-// CHECK: switch{{.*}}, label %[[DEFAULT:[a-zA-Z0-9_]+]]
-// CHECK: [[DEFAULT]]:
-// CHECK-NEXT: unreachable
+// CHECK: switch{{.*}}, label %[[OTHERWISE:[a-zA-Z0-9_]+]] [
+// CHECK-NEXT: i[[TY:[0-9]+]] [[DISCR:[0-9]+]], label %[[TRUE:[a-zA-Z0-9_]+]]
+// CHECK-NEXT: ]
+// CHECK: [[TRUE]]:
+// CHECK-NEXT: br label %[[EXIT:[a-zA-Z0-9_]+]]
+// CHECK: [[OTHERWISE]]:
+// CHECK-NEXT: br label %[[EXIT:[a-zA-Z0-9_]+]]
     match e {
         E::A => (),
         E::B => (),
diff --git a/src/test/compile-fail/E0081.rs b/src/test/compile-fail/E0081.rs
index 9911e093a89..e12eff72c7f 100644
--- a/src/test/compile-fail/E0081.rs
+++ b/src/test/compile-fail/E0081.rs
@@ -9,10 +9,10 @@
 // except according to those terms.
 
 enum Enum {
-    P = 3, //~ NOTE first use of `3isize`
+    P = 3, //~ NOTE first use of `3`
     X = 3,
-    //~^ ERROR discriminant value `3isize` already exists
-    //~| NOTE enum already has `3isize`
+    //~^ ERROR discriminant value `3` already exists
+    //~| NOTE enum already has `3`
     Y = 5
 }
 
diff --git a/src/test/compile-fail/issue-15524.rs b/src/test/compile-fail/issue-15524.rs
index 658a0c1546b..0d5f5fd75eb 100644
--- a/src/test/compile-fail/issue-15524.rs
+++ b/src/test/compile-fail/issue-15524.rs
@@ -12,20 +12,20 @@ const N: isize = 1;
 
 enum Foo {
     A = 1,
-    //~^ NOTE first use of `1isize`
-    //~| NOTE first use of `1isize`
-    //~| NOTE first use of `1isize`
+    //~^ NOTE first use of `1`
+    //~| NOTE first use of `1`
+    //~| NOTE first use of `1`
     B = 1,
-    //~^ ERROR discriminant value `1isize` already exists
-    //~| NOTE enum already has `1isize`
+    //~^ ERROR discriminant value `1` already exists
+    //~| NOTE enum already has `1`
     C = 0,
     D,
-    //~^ ERROR discriminant value `1isize` already exists
-    //~| NOTE enum already has `1isize`
+    //~^ ERROR discriminant value `1` already exists
+    //~| NOTE enum already has `1`
 
     E = N,
-    //~^ ERROR discriminant value `1isize` already exists
-    //~| NOTE enum already has `1isize`
+    //~^ ERROR discriminant value `1` already exists
+    //~| NOTE enum already has `1`
 
 }
 
diff --git a/src/test/mir-opt/simplify_if.rs b/src/test/mir-opt/simplify_if.rs
index 7239e32357b..0e8971269b0 100644
--- a/src/test/mir-opt/simplify_if.rs
+++ b/src/test/mir-opt/simplify_if.rs
@@ -17,7 +17,7 @@ fn main() {
 // END RUST SOURCE
 // START rustc.node4.SimplifyBranches.initial-before.mir
 // bb0: {
-//     if(const false) -> [true: bb1, false: bb2];
+//     switchInt(const false) -> [0: bb2, otherwise: bb1];
 // }
 // END rustc.node4.SimplifyBranches.initial-before.mir
 // START rustc.node4.SimplifyBranches.initial-after.mir
diff --git a/src/test/ui/custom-derive/issue-36935.rs b/src/test/ui/custom-derive/issue-36935.rs
index 22d603563de..2231c3c2422 100644
--- a/src/test/ui/custom-derive/issue-36935.rs
+++ b/src/test/ui/custom-derive/issue-36935.rs
@@ -9,6 +9,7 @@
 // except according to those terms.
 
 // aux-build:plugin.rs
+// ignore-stage1
 
 #![feature(proc_macro)]
 
diff --git a/src/test/ui/custom-derive/issue-36935.stderr b/src/test/ui/custom-derive/issue-36935.stderr
index 9a5e2de14e3..46cc7a42b04 100644
--- a/src/test/ui/custom-derive/issue-36935.stderr
+++ b/src/test/ui/custom-derive/issue-36935.stderr
@@ -1,7 +1,7 @@
 error: proc-macro derive panicked
-  --> $DIR/issue-36935.rs:17:15
+  --> $DIR/issue-36935.rs:18:15
    |
-17 | #[derive(Foo, Bar)]
+18 | #[derive(Foo, Bar)]
    |               ^^^
    |
    = help: message: lolnope