diff --git a/mk/crates.mk b/mk/crates.mk
index fec9e985e04cd..48c1e4ad59d5b 100644
--- a/mk/crates.mk
+++ b/mk/crates.mk
@@ -111,7 +111,7 @@ DEPS_rustc_lint := rustc log syntax rustc_const_eval
 DEPS_rustc_llvm := native:rustllvm libc std rustc_bitflags
 DEPS_rustc_metadata := rustc syntax rbml rustc_const_math
 DEPS_rustc_passes := syntax rustc core rustc_const_eval
-DEPS_rustc_mir := rustc syntax rustc_const_math rustc_const_eval
+DEPS_rustc_mir := rustc syntax rustc_const_math rustc_const_eval rustc_bitflags
 DEPS_rustc_resolve := arena rustc log syntax
 DEPS_rustc_platform_intrinsics := std
 DEPS_rustc_plugin := rustc rustc_metadata syntax rustc_mir
diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs
index 85b4b4f59c49a..8378495599ce3 100644
--- a/src/librustc/dep_graph/dep_node.rs
+++ b/src/librustc/dep_graph/dep_node.rs
@@ -64,6 +64,7 @@ pub enum DepNode<D: Clone + Debug> {
     IntrinsicCheck(D),
     MatchCheck(D),
     MirMapConstruction(D),
+    MirPass(D),
     MirTypeck(D),
     BorrowCheck(D),
     RvalueCheck(D),
@@ -186,6 +187,7 @@ impl<D: Clone + Debug> DepNode<D> {
             IntrinsicCheck(ref d) => op(d).map(IntrinsicCheck),
             MatchCheck(ref d) => op(d).map(MatchCheck),
             MirMapConstruction(ref d) => op(d).map(MirMapConstruction),
+            MirPass(ref d) => op(d).map(MirPass),
             MirTypeck(ref d) => op(d).map(MirTypeck),
             BorrowCheck(ref d) => op(d).map(BorrowCheck),
             RvalueCheck(ref d) => op(d).map(RvalueCheck),
diff --git a/src/librustc/hir/map/def_collector.rs b/src/librustc/hir/map/def_collector.rs
index 053d32305be03..640ef48493a2c 100644
--- a/src/librustc/hir/map/def_collector.rs
+++ b/src/librustc/hir/map/def_collector.rs
@@ -97,6 +97,31 @@ impl<'ast> DefCollector<'ast> {
         f(self);
         self.parent_def = parent;
     }
+
+    fn visit_ast_const_integer(&mut self, expr: &'ast Expr) {
+        // Find the node which will be used after lowering.
+        if let ExprKind::Paren(ref inner) = expr.node {
+            return self.visit_ast_const_integer(inner);
+        }
+
+        // FIXME(eddyb) Closures should have separate
+        // function definition IDs and expression IDs.
+        if let ExprKind::Closure(..) = expr.node {
+            return;
+        }
+
+        self.create_def(expr.id, DefPathData::Initializer);
+    }
+
+    fn visit_hir_const_integer(&mut self, expr: &'ast hir::Expr) {
+        // FIXME(eddyb) Closures should have separate
+        // function definition IDs and expression IDs.
+        if let hir::ExprClosure(..) = expr.node {
+            return;
+        }
+
+        self.create_def(expr.id, DefPathData::Initializer);
+    }
 }
 
 impl<'ast> visit::Visitor<'ast> for DefCollector<'ast> {
@@ -126,14 +151,17 @@ impl<'ast> visit::Visitor<'ast> for DefCollector<'ast> {
                         let variant_def_index =
                             this.create_def(v.node.data.id(),
                                             DefPathData::EnumVariant(v.node.name.name));
-
-                        for (index, field) in v.node.data.fields().iter().enumerate() {
-                            let name = field.ident.map(|ident| ident.name)
-                                .unwrap_or(token::intern(&index.to_string()));
-                            this.create_def_with_parent(Some(variant_def_index),
-                                                        field.id,
-                                                        DefPathData::Field(name));
-                        }
+                        this.with_parent(variant_def_index, |this| {
+                            for (index, field) in v.node.data.fields().iter().enumerate() {
+                                let name = field.ident.map(|ident| ident.name)
+                                    .unwrap_or_else(|| token::intern(&index.to_string()));
+                                this.create_def(field.id, DefPathData::Field(name));
+                            }
+
+                            if let Some(ref expr) = v.node.disr_expr {
+                                this.visit_ast_const_integer(expr);
+                            }
+                        });
                     }
                 }
                 ItemKind::Struct(ref struct_def, _) => {
@@ -221,6 +249,10 @@ impl<'ast> visit::Visitor<'ast> for DefCollector<'ast> {
     fn visit_expr(&mut self, expr: &'ast Expr) {
         let parent_def = self.parent_def;
 
+        if let ExprKind::Repeat(_, ref count) = expr.node {
+            self.visit_ast_const_integer(count);
+        }
+
         if let ExprKind::Closure(..) = expr.node {
             let def = self.create_def(expr.id, DefPathData::ClosureExpr);
             self.parent_def = Some(def);
@@ -230,6 +262,13 @@ impl<'ast> visit::Visitor<'ast> for DefCollector<'ast> {
         self.parent_def = parent_def;
     }
 
+    fn visit_ty(&mut self, ty: &'ast Ty) {
+        if let TyKind::FixedLengthVec(_, ref length) = ty.node {
+            self.visit_ast_const_integer(length);
+        }
+        visit::walk_ty(self, ty);
+    }
+
     fn visit_lifetime_def(&mut self, def: &'ast LifetimeDef) {
         self.create_def(def.lifetime.id, DefPathData::LifetimeDef(def.lifetime.name));
     }
@@ -276,11 +315,15 @@ impl<'ast> intravisit::Visitor<'ast> for DefCollector<'ast> {
                             this.create_def(v.node.data.id(),
                                             DefPathData::EnumVariant(v.node.name));
 
-                        for field in v.node.data.fields() {
-                            this.create_def_with_parent(Some(variant_def_index),
-                                                        field.id,
-                                                        DefPathData::Field(field.name));
-                        }
+                        this.with_parent(variant_def_index, |this| {
+                            for field in v.node.data.fields() {
+                                this.create_def(field.id,
+                                                DefPathData::Field(field.name));
+                            }
+                            if let Some(ref expr) = v.node.disr_expr {
+                                this.visit_hir_const_integer(expr);
+                            }
+                        });
                     }
                 }
                 hir::ItemStruct(ref struct_def, _) => {
@@ -365,6 +408,10 @@ impl<'ast> intravisit::Visitor<'ast> for DefCollector<'ast> {
     fn visit_expr(&mut self, expr: &'ast hir::Expr) {
         let parent_def = self.parent_def;
 
+        if let hir::ExprRepeat(_, ref count) = expr.node {
+            self.visit_hir_const_integer(count);
+        }
+
         if let hir::ExprClosure(..) = expr.node {
             let def = self.create_def(expr.id, DefPathData::ClosureExpr);
             self.parent_def = Some(def);
@@ -374,6 +421,13 @@ impl<'ast> intravisit::Visitor<'ast> for DefCollector<'ast> {
         self.parent_def = parent_def;
     }
 
+    fn visit_ty(&mut self, ty: &'ast hir::Ty) {
+        if let hir::TyFixedLengthVec(_, ref length) = ty.node {
+            self.visit_hir_const_integer(length);
+        }
+        intravisit::walk_ty(self, ty);
+    }
+
     fn visit_lifetime_def(&mut self, def: &'ast hir::LifetimeDef) {
         self.create_def(def.lifetime.id, DefPathData::LifetimeDef(def.lifetime.name));
     }
@@ -381,4 +435,4 @@ impl<'ast> intravisit::Visitor<'ast> for DefCollector<'ast> {
     fn visit_macro_def(&mut self, macro_def: &'ast hir::MacroDef) {
         self.create_def(macro_def.id, DefPathData::MacroDef(macro_def.name));
     }
-}
\ No newline at end of file
+}
diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs
index 9ec05a9b2927c..9f8a3dbfa8f39 100644
--- a/src/librustc/mir/repr.rs
+++ b/src/librustc/mir/repr.rs
@@ -36,6 +36,11 @@ pub struct Mir<'tcx> {
     /// used (eventually) for debuginfo. Indexed by a `ScopeId`.
     pub scopes: Vec<ScopeData>,
 
+    /// Rvalues promoted from this function, such as borrows of constants.
+    /// Each of them is the Mir of a constant with the fn's type parameters
+    /// in scope, but no vars or args and a separate set of temps.
+    pub promoted: Vec<Mir<'tcx>>,
+
     /// Return type of the function.
     pub return_ty: FnOutput<'tcx>,
 
@@ -987,6 +992,10 @@ pub enum Literal<'tcx> {
     Value {
         value: ConstVal,
     },
+    Promoted {
+        // Index into the `promoted` vector of `Mir`.
+        index: usize
+    },
 }
 
 impl<'tcx> Debug for Constant<'tcx> {
@@ -1007,6 +1016,9 @@ impl<'tcx> Debug for Literal<'tcx> {
                 write!(fmt, "const ")?;
                 fmt_const_val(fmt, value)
             }
+            Promoted { index } => {
+                write!(fmt, "promoted{}", index)
+            }
         }
     }
 }
diff --git a/src/librustc/mir/transform.rs b/src/librustc/mir/transform.rs
index 410e3f9d06628..520bfbddf9f0a 100644
--- a/src/librustc/mir/transform.rs
+++ b/src/librustc/mir/transform.rs
@@ -8,31 +8,102 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use dep_graph::DepNode;
+use hir;
+use hir::map::DefPathData;
+use hir::def_id::DefId;
 use mir::mir_map::MirMap;
 use mir::repr::Mir;
 use ty::TyCtxt;
 use syntax::ast::NodeId;
 
+/// Where a specific Mir comes from.
+#[derive(Copy, Clone)]
+pub enum MirSource {
+    /// Functions and methods.
+    Fn(NodeId),
+
+    /// Constants and associated constants.
+    Const(NodeId),
+
+    /// Initializer of a `static` item.
+    Static(NodeId, hir::Mutability),
+
+    /// Promoted rvalues within a function.
+    Promoted(NodeId, usize)
+}
+
+impl MirSource {
+    pub fn from_node(tcx: &TyCtxt, id: NodeId) -> MirSource {
+        use hir::*;
+
+        // Handle constants in enum discriminants, types, and repeat expressions.
+        let def_id = tcx.map.local_def_id(id);
+        let def_key = tcx.def_key(def_id);
+        if def_key.disambiguated_data.data == DefPathData::Initializer {
+            return MirSource::Const(id);
+        }
+
+        match tcx.map.get(id) {
+            map::NodeItem(&Item { node: ItemConst(..), .. }) |
+            map::NodeTraitItem(&TraitItem { node: ConstTraitItem(..), .. }) |
+            map::NodeImplItem(&ImplItem { node: ImplItemKind::Const(..), .. }) => {
+                MirSource::Const(id)
+            }
+            map::NodeItem(&Item { node: ItemStatic(_, m, _), .. }) => {
+                MirSource::Static(id, m)
+            }
+            // Default to function if it's not a constant or static.
+            _ => MirSource::Fn(id)
+        }
+    }
+
+    pub fn item_id(&self) -> NodeId {
+        match *self {
+            MirSource::Fn(id) |
+            MirSource::Const(id) |
+            MirSource::Static(id, _) |
+            MirSource::Promoted(id, _) => id
+        }
+    }
+}
+
 /// Various information about pass.
 pub trait Pass {
     // fn name() for printouts of various sorts?
     // fn should_run(Session) to check if pass should run?
+    fn dep_node(&self, def_id: DefId) -> DepNode<DefId> {
+        DepNode::MirPass(def_id)
+    }
 }
 
 /// A pass which inspects the whole MirMap.
 pub trait MirMapPass<'tcx>: Pass {
-    fn run_pass(&mut self, cx: &TyCtxt<'tcx>, map: &mut MirMap<'tcx>);
+    fn run_pass(&mut self, tcx: &TyCtxt<'tcx>, map: &mut MirMap<'tcx>);
 }
 
 /// A pass which inspects Mir of functions in isolation.
 pub trait MirPass<'tcx>: Pass {
-    fn run_pass(&mut self, cx: &TyCtxt<'tcx>, id: NodeId, mir: &mut Mir<'tcx>);
+    fn run_pass_on_promoted(&mut self, tcx: &TyCtxt<'tcx>,
+                            item_id: NodeId, index: usize,
+                            mir: &mut Mir<'tcx>) {
+        self.run_pass(tcx, MirSource::Promoted(item_id, index), mir);
+    }
+    fn run_pass(&mut self, tcx: &TyCtxt<'tcx>, src: MirSource, mir: &mut Mir<'tcx>);
 }
 
 impl<'tcx, T: MirPass<'tcx>> MirMapPass<'tcx> for T {
     fn run_pass(&mut self, tcx: &TyCtxt<'tcx>, map: &mut MirMap<'tcx>) {
         for (&id, mir) in &mut map.map {
-            MirPass::run_pass(self, tcx, id, mir);
+            let def_id = tcx.map.local_def_id(id);
+            let _task = tcx.dep_graph.in_task(self.dep_node(def_id));
+
+            let src = MirSource::from_node(tcx, id);
+            MirPass::run_pass(self, tcx, src, mir);
+
+            for (i, mir) in mir.promoted.iter_mut().enumerate() {
+                self.run_pass_on_promoted(tcx, id, i, mir);
+            }
         }
     }
 }
diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs
index 10afd3dd953d0..f6a241004b37b 100644
--- a/src/librustc/mir/visit.rs
+++ b/src/librustc/mir/visit.rs
@@ -244,6 +244,7 @@ macro_rules! make_mir_visitor {
                 let Mir {
                     ref $($mutability)* basic_blocks,
                     ref $($mutability)* scopes,
+                    promoted: _, // Visited by passes separately.
                     ref $($mutability)* return_ty,
                     ref $($mutability)* var_decls,
                     ref $($mutability)* arg_decls,
@@ -649,10 +650,11 @@ macro_rules! make_mir_visitor {
                                     ref $($mutability)* substs } => {
                         self.visit_def_id(def_id);
                         self.visit_substs(substs);
-                    },
+                    }
                     Literal::Value { ref $($mutability)* value } => {
                         self.visit_const_val(value);
                     }
+                    Literal::Promoted { index: _ } => {}
                 }
             }
 
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index 3e3dae3b3e985..174f626498b1f 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -25,7 +25,7 @@ use middle::cstore::{self, LOCAL_CRATE};
 use hir::def::{self, Def, ExportMap};
 use hir::def_id::DefId;
 use middle::lang_items::{FnTraitLangItem, FnMutTraitLangItem, FnOnceTraitLangItem};
-use middle::region::{CodeExtent};
+use middle::region::{CodeExtent, ROOT_CODE_EXTENT};
 use traits;
 use ty;
 use ty::subst::{Subst, Substs, VecPerParamSpace};
@@ -1376,6 +1376,7 @@ impl<'a, 'tcx> ParameterEnvironment<'a, 'tcx> {
                     }
                     hir::ItemEnum(..) |
                     hir::ItemStruct(..) |
+                    hir::ItemTy(..) |
                     hir::ItemImpl(..) |
                     hir::ItemConst(..) |
                     hir::ItemStatic(..) => {
@@ -1408,6 +1409,15 @@ impl<'a, 'tcx> ParameterEnvironment<'a, 'tcx> {
                 // This is a convenience to allow closures to work.
                 ParameterEnvironment::for_item(cx, cx.map.get_parent(id))
             }
+            Some(ast_map::NodeForeignItem(item)) => {
+                let def_id = cx.map.local_def_id(id);
+                let scheme = cx.lookup_item_type(def_id);
+                let predicates = cx.lookup_predicates(def_id);
+                cx.construct_parameter_environment(item.span,
+                                                   &scheme.generics,
+                                                   &predicates,
+                                                   ROOT_CODE_EXTENT)
+            }
             _ => {
                 bug!("ParameterEnvironment::from_item(): \
                       `{}` is not an item",
diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs
index 2b56366f1c697..1b570933beecc 100644
--- a/src/librustc_driver/driver.rs
+++ b/src/librustc_driver/driver.rs
@@ -37,7 +37,7 @@ use rustc_privacy;
 use rustc_plugin::registry::Registry;
 use rustc_plugin as plugin;
 use rustc::hir::lowering::{lower_crate, LoweringContext};
-use rustc_passes::{no_asm, loops, consts, const_fn, rvalues, static_recursion};
+use rustc_passes::{no_asm, loops, consts, rvalues, static_recursion};
 use rustc_const_eval::check_match;
 use super::Compilation;
 
@@ -726,10 +726,6 @@ pub fn phase_2_configure_and_expand(sess: &Session,
         })
     })?;
 
-    time(time_passes,
-         "const fn bodies and arguments",
-         || const_fn::check_crate(sess, &krate))?;
-
     if sess.opts.debugging_opts.input_stats {
         println!("Post-expansion node count: {}", count_nodes(&krate));
     }
@@ -903,6 +899,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
             let mut passes = sess.mir_passes.borrow_mut();
             // Push all the built-in passes.
             passes.push_pass(box mir::transform::remove_dead_blocks::RemoveDeadBlocks);
+            passes.push_pass(box mir::transform::qualify_consts::QualifyAndPromoteConstants);
             passes.push_pass(box mir::transform::type_check::TypeckMir);
             passes.push_pass(box mir::transform::simplify_cfg::SimplifyCfg);
             passes.push_pass(box mir::transform::remove_dead_blocks::RemoveDeadBlocks);
diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs
index b5b27586a7f76..c911b20c2aba2 100644
--- a/src/librustc_metadata/decoder.rs
+++ b/src/librustc_metadata/decoder.rs
@@ -855,6 +855,9 @@ pub fn maybe_get_item_mir<'tcx>(cdata: Cmd,
         };
 
         def_id_and_span_translator.visit_mir(&mut mir);
+        for promoted in &mut mir.promoted {
+            def_id_and_span_translator.visit_mir(promoted);
+        }
 
         mir
     });
diff --git a/src/librustc_mir/Cargo.toml b/src/librustc_mir/Cargo.toml
index 1c41ca6f416e1..77dccb7e0d483 100644
--- a/src/librustc_mir/Cargo.toml
+++ b/src/librustc_mir/Cargo.toml
@@ -16,4 +16,5 @@ rustc_back = { path = "../librustc_back" }
 rustc_const_eval = { path = "../librustc_const_eval" }
 rustc_const_math = { path = "../librustc_const_math" }
 rustc_data_structures = { path = "../librustc_data_structures" }
+rustc_bitflags = { path = "../librustc_bitflags" }
 syntax = { path = "../libsyntax" }
diff --git a/src/librustc_mir/build/expr/as_temp.rs b/src/librustc_mir/build/expr/as_temp.rs
index a2f7d2c9d725f..a4f4e44b1b139 100644
--- a/src/librustc_mir/build/expr/as_temp.rs
+++ b/src/librustc_mir/build/expr/as_temp.rs
@@ -35,13 +35,12 @@ impl<'a,'tcx> Builder<'a,'tcx> {
 
         let expr_ty = expr.ty.clone();
         let temp = this.temp(expr_ty.clone());
-        let temp_lifetime = match expr.temp_lifetime {
-            Some(t) => t,
-            None => {
-                span_bug!(expr.span, "no temp_lifetime for expr");
-            }
-        };
-        this.schedule_drop(expr.span, temp_lifetime, &temp, expr_ty);
+        // In constants, temp_lifetime is None. We should not need to drop
+        // anything because no values with a destructor can be created in
+        // a constant at this time, even if the type may need dropping.
+        if let Some(temp_lifetime) = expr.temp_lifetime {
+            this.schedule_drop(expr.span, temp_lifetime, &temp, expr_ty);
+        }
 
         // Careful here not to cause an infinite cycle. If we always
         // called `into`, then for lvalues like `x.f`, it would
diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs
index 1ed5fabf274a5..c6765f95d33ca 100644
--- a/src/librustc_mir/build/mod.rs
+++ b/src/librustc_mir/build/mod.rs
@@ -9,13 +9,14 @@
 // except according to those terms.
 
 use hair::cx::Cx;
-use rustc::middle::region::{CodeExtent, CodeExtentData};
-use rustc::ty::{self, FnOutput, Ty};
+use rustc::middle::region::{CodeExtent, CodeExtentData, ROOT_CODE_EXTENT};
+use rustc::ty::{self, Ty};
 use rustc::mir::repr::*;
 use rustc_data_structures::fnv::FnvHashMap;
 use rustc::hir;
 use rustc::hir::pat_util::pat_is_binding;
 use std::ops::{Index, IndexMut};
+use syntax::abi::Abi;
 use syntax::ast;
 use syntax::codemap::Span;
 use syntax::parse::token::keywords;
@@ -83,7 +84,7 @@ pub struct ScopeAuxiliary {
     pub postdoms: Vec<Location>,
 }
 
-#[derive(Copy, Clone, PartialEq, Eq, Hash)]
+#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
 pub struct Location {
     /// the location is within this block
     pub block: BasicBlock,
@@ -159,53 +160,31 @@ macro_rules! unpack {
 ///////////////////////////////////////////////////////////////////////////
 /// the main entry point for building MIR for a function
 
-pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>,
-                          span: Span,
-                          fn_id: ast::NodeId,
-                          body_id: ast::NodeId,
-                          implicit_arguments: Vec<Ty<'tcx>>,
-                          explicit_arguments: Vec<(Ty<'tcx>, &'tcx hir::Pat)>,
-                          return_ty: FnOutput<'tcx>,
-                          ast_block: &'tcx hir::Block)
-                          -> (Mir<'tcx>, ScopeAuxiliaryVec) {
+pub fn construct_fn<'a, 'tcx, A>(hir: Cx<'a,'tcx>,
+                                 fn_id: ast::NodeId,
+                                 arguments: A,
+                                 return_ty: ty::FnOutput<'tcx>,
+                                 ast_block: &'tcx hir::Block)
+                                 -> (Mir<'tcx>, ScopeAuxiliaryVec)
+    where A: Iterator<Item=(Ty<'tcx>, Option<&'tcx hir::Pat>)>
+{
     let tcx = hir.tcx();
-    let cfg = CFG { basic_blocks: vec![] };
-
-    let mut builder = Builder {
-        hir: hir,
-        cfg: cfg,
-        fn_span: span,
-        scopes: vec![],
-        scope_datas: vec![],
-        scope_auxiliary: ScopeAuxiliaryVec { vec: vec![] },
-        loop_scopes: vec![],
-        temp_decls: vec![],
-        var_decls: vec![],
-        var_indices: FnvHashMap(),
-        unit_temp: None,
-        cached_resume_block: None,
-        cached_return_block: None
-    };
-
-    assert_eq!(builder.cfg.start_new_block(), START_BLOCK);
+    let span = tcx.map.span(fn_id);
+    let mut builder = Builder::new(hir, span);
 
-    let mut arg_decls = None; // assigned to `Some` in closures below
+    let body_id = ast_block.id;
     let call_site_extent =
         tcx.region_maps.lookup_code_extent(
             CodeExtentData::CallSiteScope { fn_id: fn_id, body_id: body_id });
-    let _ = builder.in_scope(call_site_extent, START_BLOCK, |builder, call_site_scope_id| {
-        let mut block = START_BLOCK;
-        let arg_extent =
-            tcx.region_maps.lookup_code_extent(
-                CodeExtentData::ParameterScope { fn_id: fn_id, body_id: body_id });
-        unpack!(block = builder.in_scope(arg_extent, block, |builder, arg_scope_id| {
-            arg_decls = Some(unpack!(block = builder.args_and_body(block,
-                                                                   return_ty,
-                                                                   implicit_arguments,
-                                                                   explicit_arguments,
-                                                                   arg_scope_id,
-                                                                   ast_block)));
-            block.unit()
+    let arg_extent =
+        tcx.region_maps.lookup_code_extent(
+            CodeExtentData::ParameterScope { fn_id: fn_id, body_id: body_id });
+    let mut block = START_BLOCK;
+    let mut arg_decls = unpack!(block = builder.in_scope(call_site_extent, block,
+                                                         |builder, call_site_scope_id| {
+        let arg_decls = unpack!(block = builder.in_scope(arg_extent, block,
+                                                         |builder, arg_scope_id| {
+            builder.args_and_body(block, return_ty, arguments, arg_scope_id, ast_block)
         }));
 
         let return_block = builder.return_block();
@@ -213,20 +192,19 @@ pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>,
                               TerminatorKind::Goto { target: return_block });
         builder.cfg.terminate(return_block, call_site_scope_id, span,
                               TerminatorKind::Return);
-        return_block.unit()
-    });
-
-    assert!(
-        builder.cfg.basic_blocks
-                   .iter()
-                   .enumerate()
-                   .all(|(index, block)| {
-                       if block.terminator.is_none() {
-                           bug!("no terminator on block {:?} in fn {:?}",
-                                index, fn_id)
-                       }
-                       true
-                   }));
+        return_block.and(arg_decls)
+    }));
+    assert_eq!(block, builder.return_block());
+
+    match tcx.node_id_to_type(fn_id).sty {
+        ty::TyFnDef(_, _, f) if f.abi == Abi::RustCall => {
+            // RustCall pseudo-ABI untuples the last argument.
+            if let Some(arg_decl) = arg_decls.last_mut() {
+                arg_decl.spread = true;
+            }
+        }
+        _ => {}
+    }
 
     // Gather the upvars of a closure, if any.
     let upvar_decls: Vec<_> = tcx.with_freevars(fn_id, |freevars| {
@@ -251,72 +229,126 @@ pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>,
         }).collect()
     });
 
-    (
-        Mir {
-            basic_blocks: builder.cfg.basic_blocks,
-            scopes: builder.scope_datas,
-            var_decls: builder.var_decls,
-            arg_decls: arg_decls.take().expect("args never built?"),
-            temp_decls: builder.temp_decls,
-            upvar_decls: upvar_decls,
-            return_ty: return_ty,
-            span: span
-        },
-        builder.scope_auxiliary,
-    )
+    builder.finish(upvar_decls, arg_decls, return_ty)
+}
+
+pub fn construct_const<'a, 'tcx>(hir: Cx<'a,'tcx>,
+                                 item_id: ast::NodeId,
+                                 ast_expr: &'tcx hir::Expr)
+                                 -> (Mir<'tcx>, ScopeAuxiliaryVec) {
+    let tcx = hir.tcx();
+    let span = tcx.map.span(item_id);
+    let mut builder = Builder::new(hir, span);
+
+    let extent = ROOT_CODE_EXTENT;
+    let mut block = START_BLOCK;
+    let _ = builder.in_scope(extent, block, |builder, call_site_scope_id| {
+        let expr = builder.hir.mirror(ast_expr);
+        unpack!(block = builder.into(&Lvalue::ReturnPointer, block, expr));
+
+        let return_block = builder.return_block();
+        builder.cfg.terminate(block, call_site_scope_id, span,
+                              TerminatorKind::Goto { target: return_block });
+        builder.cfg.terminate(return_block, call_site_scope_id, span,
+                              TerminatorKind::Return);
+
+        return_block.unit()
+    });
+
+    let ty = tcx.expr_ty_adjusted(ast_expr);
+    builder.finish(vec![], vec![], ty::FnConverging(ty))
 }
 
 impl<'a,'tcx> Builder<'a,'tcx> {
-    fn args_and_body(&mut self,
-                     mut block: BasicBlock,
-                     return_ty: FnOutput<'tcx>,
-                     implicit_arguments: Vec<Ty<'tcx>>,
-                     explicit_arguments: Vec<(Ty<'tcx>, &'tcx hir::Pat)>,
-                     argument_scope_id: ScopeId,
-                     ast_block: &'tcx hir::Block)
-                     -> BlockAnd<Vec<ArgDecl<'tcx>>>
+    fn new(hir: Cx<'a, 'tcx>, span: Span) -> Builder<'a, 'tcx> {
+        let mut builder = Builder {
+            hir: hir,
+            cfg: CFG { basic_blocks: vec![] },
+            fn_span: span,
+            scopes: vec![],
+            scope_datas: vec![],
+            scope_auxiliary: ScopeAuxiliaryVec { vec: vec![] },
+            loop_scopes: vec![],
+            temp_decls: vec![],
+            var_decls: vec![],
+            var_indices: FnvHashMap(),
+            unit_temp: None,
+            cached_resume_block: None,
+            cached_return_block: None
+        };
+
+        assert_eq!(builder.cfg.start_new_block(), START_BLOCK);
+
+        builder
+    }
+
+    fn finish(self,
+              upvar_decls: Vec<UpvarDecl>,
+              arg_decls: Vec<ArgDecl<'tcx>>,
+              return_ty: ty::FnOutput<'tcx>)
+              -> (Mir<'tcx>, ScopeAuxiliaryVec) {
+        for (index, block) in self.cfg.basic_blocks.iter().enumerate() {
+            if block.terminator.is_none() {
+                span_bug!(self.fn_span, "no terminator on block {:?}", index);
+            }
+        }
+
+        (Mir {
+            basic_blocks: self.cfg.basic_blocks,
+            scopes: self.scope_datas,
+            promoted: vec![],
+            var_decls: self.var_decls,
+            arg_decls: arg_decls,
+            temp_decls: self.temp_decls,
+            upvar_decls: upvar_decls,
+            return_ty: return_ty,
+            span: self.fn_span
+        }, self.scope_auxiliary)
+    }
+
+    fn args_and_body<A>(&mut self,
+                        mut block: BasicBlock,
+                        return_ty: ty::FnOutput<'tcx>,
+                        arguments: A,
+                        argument_scope_id: ScopeId,
+                        ast_block: &'tcx hir::Block)
+                        -> BlockAnd<Vec<ArgDecl<'tcx>>>
+        where A: Iterator<Item=(Ty<'tcx>, Option<&'tcx hir::Pat>)>
     {
         // to start, translate the argument patterns and collect the argument types.
-        let implicits = implicit_arguments.into_iter().map(|ty| (ty, None));
-        let explicits = explicit_arguments.into_iter().map(|(ty, pat)| (ty, Some(pat)));
-            let arg_decls =
-            implicits
-            .chain(explicits)
-            .enumerate()
-            .map(|(index, (ty, pattern))| {
-                let lvalue = Lvalue::Arg(index as u32);
-                if let Some(pattern) = pattern {
-                    let pattern = self.hir.irrefutable_pat(pattern);
-                    unpack!(block = self.lvalue_into_pattern(block,
-                                                             argument_scope_id,
-                                                             pattern,
-                                                             &lvalue));
-                }
+        let arg_decls = arguments.enumerate().map(|(index, (ty, pattern))| {
+            let lvalue = Lvalue::Arg(index as u32);
+            if let Some(pattern) = pattern {
+                let pattern = self.hir.irrefutable_pat(pattern);
+                unpack!(block = self.lvalue_into_pattern(block,
+                                                         argument_scope_id,
+                                                         pattern,
+                                                         &lvalue));
+            }
 
-                // Make sure we drop (parts of) the argument even when not matched on.
-                let argument_extent = self.scope_auxiliary[argument_scope_id].extent;
-                self.schedule_drop(pattern.as_ref().map_or(ast_block.span, |pat| pat.span),
-                                   argument_extent, &lvalue, ty);
-
-                let mut name = keywords::Invalid.name();
-                if let Some(pat) = pattern {
-                    if let hir::PatKind::Ident(_, ref ident, _) = pat.node {
-                        if pat_is_binding(&self.hir.tcx().def_map.borrow(), pat) {
-                            name = ident.node.name;
-                        }
+            // Make sure we drop (parts of) the argument even when not matched on.
+            let argument_extent = self.scope_auxiliary[argument_scope_id].extent;
+            self.schedule_drop(pattern.as_ref().map_or(ast_block.span, |pat| pat.span),
+                               argument_extent, &lvalue, ty);
+
+            let mut name = keywords::Invalid.name();
+            if let Some(pat) = pattern {
+                if let hir::PatKind::Ident(_, ref ident, _) = pat.node {
+                    if pat_is_binding(&self.hir.tcx().def_map.borrow(), pat) {
+                        name = ident.node.name;
                     }
                 }
+            }
 
-                ArgDecl {
-                    ty: ty,
-                    spread: false,
-                    debug_name: name
-                }
-            })
-            .collect();
+            ArgDecl {
+                ty: ty,
+                spread: false,
+                debug_name: name
+            }
+        }).collect();
 
         // FIXME(#32959): temporary hack for the issue at hand
-        let return_is_unit = if let FnOutput::FnConverging(t) = return_ty {
+        let return_is_unit = if let ty::FnConverging(t) = return_ty {
             t.is_nil()
         } else {
             false
diff --git a/src/librustc_mir/diagnostics.rs b/src/librustc_mir/diagnostics.rs
new file mode 100644
index 0000000000000..65d51d205285a
--- /dev/null
+++ b/src/librustc_mir/diagnostics.rs
@@ -0,0 +1,387 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![allow(non_snake_case)]
+
+register_long_diagnostics! {
+
+E0010: r##"
+The value of statics and constants must be known at compile time, and they live
+for the entire lifetime of a program. Creating a boxed value allocates memory on
+the heap at runtime, and therefore cannot be done at compile time. Erroneous
+code example:
+
+```compile_fail
+#![feature(box_syntax)]
+
+const CON : Box<i32> = box 0;
+```
+"##,
+
+E0013: r##"
+Static and const variables can refer to other const variables. But a const
+variable cannot refer to a static variable. For example, `Y` cannot refer to
+`X` here:
+
+```compile_fail
+static X: i32 = 42;
+const Y: i32 = X;
+```
+
+To fix this, the value can be extracted as a const and then used:
+
+```
+const A: i32 = 42;
+static X: i32 = A;
+const Y: i32 = A;
+```
+"##,
+
+// FIXME(#24111) Change the language here when const fn stabilizes
+E0015: r##"
+The only functions that can be called in static or constant expressions are
+`const` functions, and struct/enum constructors. `const` functions are only
+available on a nightly compiler. Rust currently does not support more general
+compile-time function execution.
+
+```
+const FOO: Option<u8> = Some(1); // enum constructor
+struct Bar {x: u8}
+const BAR: Bar = Bar {x: 1}; // struct constructor
+```
+
+See [RFC 911] for more details on the design of `const fn`s.
+
+[RFC 911]: https://github.com/rust-lang/rfcs/blob/master/text/0911-const-fn.md
+"##,
+
+E0016: r##"
+Blocks in constants may only contain items (such as constant, function
+definition, etc...) and a tail expression. Erroneous code example:
+
+```compile_fail
+const FOO: i32 = { let x = 0; x }; // 'x' isn't an item!
+```
+
+To avoid it, you have to replace the non-item object:
+
+```
+const FOO: i32 = { const X : i32 = 0; X };
+```
+"##,
+
+E0017: r##"
+References in statics and constants may only refer to immutable values.
+Erroneous code example:
+
+```compile_fail
+static X: i32 = 1;
+const C: i32 = 2;
+
+// these three are not allowed:
+const CR: &'static mut i32 = &mut C;
+static STATIC_REF: &'static mut i32 = &mut X;
+static CONST_REF: &'static mut i32 = &mut C;
+```
+
+Statics are shared everywhere, and if they refer to mutable data one might
+violate memory safety since holding multiple mutable references to shared data
+is not allowed.
+
+If you really want global mutable state, try using `static mut` or a global
+`UnsafeCell`.
+"##,
+
+E0018: r##"
+
+The value of static and constant integers must be known at compile time. You
+can't cast a pointer to an integer because the address of a pointer can
+vary.
+
+For example, if you write:
+
+```compile_fail
+static MY_STATIC: u32 = 42;
+static MY_STATIC_ADDR: usize = &MY_STATIC as *const _ as usize;
+static WHAT: usize = (MY_STATIC_ADDR^17) + MY_STATIC_ADDR;
+```
+
+Then `MY_STATIC_ADDR` would contain the address of `MY_STATIC`. However,
+the address can change when the program is linked, as well as change
+between different executions due to ASLR, and many linkers would
+not be able to calculate the value of `WHAT`.
+
+On the other hand, static and constant pointers can point either to
+a known numeric address or to the address of a symbol.
+
+```
+static MY_STATIC_ADDR: &'static u32 = &MY_STATIC;
+// ... and also
+static MY_STATIC_ADDR2: *const u32 = &MY_STATIC;
+
+const CONST_ADDR: *const u8 = 0x5f3759df as *const u8;
+```
+
+This does not pose a problem by itself because they can't be
+accessed directly.
+"##,
+
+E0019: r##"
+A function call isn't allowed in the const's initialization expression
+because the expression's value must be known at compile-time. Erroneous code
+example:
+
+```compile_fail
+enum Test {
+    V1
+}
+
+impl Test {
+    fn test(&self) -> i32 {
+        12
+    }
+}
+
+fn main() {
+    const FOO: Test = Test::V1;
+
+    const A: i32 = FOO.test(); // You can't call Test::func() here !
+}
+```
+
+Remember: you can't use a function call inside a const's initialization
+expression! However, you can totally use it anywhere else:
+
+```
+fn main() {
+    const FOO: Test = Test::V1;
+
+    FOO.func(); // here is good
+    let x = FOO.func(); // or even here!
+}
+```
+"##,
+
+E0022: r##"
+Constant functions are not allowed to mutate anything. Thus, binding to an
+argument with a mutable pattern is not allowed. For example,
+
+```compile_fail
+const fn foo(mut x: u8) {
+    // do stuff
+}
+```
+
+Is incorrect because the function body may not mutate `x`.
+
+Remove any mutable bindings from the argument list to fix this error. In case
+you need to mutate the argument, try lazily initializing a global variable
+instead of using a `const fn`, or refactoring the code to a functional style to
+avoid mutation if possible.
+"##,
+
+E0394: r##"
+From [RFC 246]:
+
+ > It is invalid for a static to reference another static by value. It is
+ > required that all references be borrowed.
+
+[RFC 246]: https://github.com/rust-lang/rfcs/pull/246
+"##,
+
+
+E0395: r##"
+The value assigned to a constant scalar must be known at compile time,
+which is not the case when comparing raw pointers.
+
+Erroneous code example:
+
+```compile_fail
+static FOO: i32 = 42;
+static BAR: i32 = 42;
+
+static BAZ: bool = { (&FOO as *const i32) == (&BAR as *const i32) };
+// error: raw pointers cannot be compared in statics!
+```
+
+The address assigned by the linker to `FOO` and `BAR` may or may not
+be identical, so the value of `BAZ` can't be determined.
+
+If you want to do the comparison, please do it at run-time.
+
+For example:
+
+```
+static FOO: i32 = 42;
+static BAR: i32 = 42;
+
+let baz: bool = { (&FOO as *const i32) == (&BAR as *const i32) };
+// baz isn't a constant expression so it's ok
+```
+"##,
+
+E0396: r##"
+The value behind a raw pointer can't be determined at compile-time
+(or even link-time), which means it can't be used in a constant
+expression. Erroneous code example:
+
+```compile_fail
+const REG_ADDR: *const u8 = 0x5f3759df as *const u8;
+
+const VALUE: u8 = unsafe { *REG_ADDR };
+// error: raw pointers cannot be dereferenced in constants
+```
+
+A possible fix is to dereference your pointer at some point in run-time.
+
+For example:
+
+```
+const REG_ADDR: *const u8 = 0x5f3759df as *const u8;
+
+let reg_value = unsafe { *REG_ADDR };
+```
+"##,
+
+E0492: r##"
+A borrow of a constant containing interior mutability was attempted. Erroneous
+code example:
+
+```compile_fail
+use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT};
+
+const A: AtomicUsize = ATOMIC_USIZE_INIT;
+static B: &'static AtomicUsize = &A;
+// error: cannot borrow a constant which contains interior mutability, create a
+//        static instead
+```
+
+A `const` represents a constant value that should never change. If one takes
+a `&` reference to the constant, then one is taking a pointer to some memory
+location containing the value. Normally this is perfectly fine: most values
+can't be changed via a shared `&` pointer, but interior mutability would allow
+it. That is, a constant value could be mutated. On the other hand, a `static` is
+explicitly a single memory location, which can be mutated at will.
+
+So, in order to solve this error, either use statics which are `Sync`:
+
+```
+use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT};
+
+static A: AtomicUsize = ATOMIC_USIZE_INIT;
+static B: &'static AtomicUsize = &A; // ok!
+```
+
+You can also have this error while using a cell type:
+
+```compile_fail
+#![feature(const_fn)]
+
+use std::cell::Cell;
+
+const A: Cell<usize> = Cell::new(1);
+const B: &'static Cell<usize> = &A;
+// error: cannot borrow a constant which contains interior mutability, create
+//        a static instead
+
+// or:
+struct C { a: Cell<usize> }
+
+const D: C = C { a: Cell::new(1) };
+const E: &'static Cell<usize> = &D.a; // error
+
+// or:
+const F: &'static C = &D; // error
+```
+
+This is because cell types do operations that are not thread-safe. Due to this,
+they don't implement Sync and thus can't be placed in statics. In this
+case, `StaticMutex` would work just fine, but it isn't stable yet:
+https://doc.rust-lang.org/nightly/std/sync/struct.StaticMutex.html
+
+However, if you still wish to use these types, you can achieve this by an unsafe
+wrapper:
+
+```
+#![feature(const_fn)]
+
+use std::cell::Cell;
+use std::marker::Sync;
+
+struct NotThreadSafe<T> {
+    value: Cell<T>,
+}
+
+unsafe impl<T> Sync for NotThreadSafe<T> {}
+
+static A: NotThreadSafe<usize> = NotThreadSafe { value : Cell::new(1) };
+static B: &'static NotThreadSafe<usize> = &A; // ok!
+```
+
+Remember this solution is unsafe! You will have to ensure that accesses to the
+cell are synchronized.
+"##,
+
+E0493: r##"
+A type with a destructor was assigned to an invalid type of variable. Erroneous
+code example:
+
+```compile_fail
+struct Foo {
+    a: u32
+}
+
+impl Drop for Foo {
+    fn drop(&mut self) {}
+}
+
+const F : Foo = Foo { a : 0 };
+// error: constants are not allowed to have destructors
+static S : Foo = Foo { a : 0 };
+// error: destructors in statics are an unstable feature
+```
+
+To solve this issue, please use a type which does allow the usage of type with
+destructors.
+"##,
+
+E0494: r##"
+A reference of an interior static was assigned to another const/static.
+Erroneous code example:
+
+```compile_fail
+struct Foo {
+    a: u32
+}
+
+static S : Foo = Foo { a : 0 };
+static A : &'static u32 = &S.a;
+// error: cannot refer to the interior of another static, use a
+//        constant instead
+```
+
+The "base" variable has to be a const if you want another static/const variable
+to refer to one of its fields. Example:
+
+```
+struct Foo {
+    a: u32
+}
+
+const S : Foo = Foo { a : 0 };
+static A : &'static u32 = &S.a; // ok!
+```
+"##,
+
+}
+
+register_diagnostics! {
+    E0526, // shuffle indices are not constant
+}
diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs
index 6d527f77800f6..049426db2f4d0 100644
--- a/src/librustc_mir/hair/cx/expr.rs
+++ b/src/librustc_mir/hair/cx/expr.rs
@@ -351,21 +351,39 @@ fn make_mirror_unadjusted<'a, 'tcx>(cx: &mut Cx<'a, 'tcx>, expr: &'tcx hir::Expr
                                     pass_args, lhs.to_ref(), vec![rhs])
             } else {
                 // FIXME overflow
-                match op.node {
-                    hir::BinOp_::BiAnd => {
+                match (op.node, cx.constness) {
+                    // FIXME(eddyb) use logical ops in constants when
+                    // they can handle that kind of control-flow.
+                    (hir::BinOp_::BiAnd, hir::Constness::Const) => {
+                        ExprKind::Binary {
+                            op: BinOp::BitAnd,
+                            lhs: lhs.to_ref(),
+                            rhs: rhs.to_ref(),
+                        }
+                    }
+                    (hir::BinOp_::BiOr, hir::Constness::Const) => {
+                        ExprKind::Binary {
+                            op: BinOp::BitOr,
+                            lhs: lhs.to_ref(),
+                            rhs: rhs.to_ref(),
+                        }
+                    }
+
+                    (hir::BinOp_::BiAnd, hir::Constness::NotConst) => {
                         ExprKind::LogicalOp {
                             op: LogicalOp::And,
                             lhs: lhs.to_ref(),
                             rhs: rhs.to_ref(),
                         }
                     }
-                    hir::BinOp_::BiOr => {
+                    (hir::BinOp_::BiOr, hir::Constness::NotConst) => {
                         ExprKind::LogicalOp {
                             op: LogicalOp::Or,
                             lhs: lhs.to_ref(),
                             rhs: rhs.to_ref(),
                         }
                     }
+
                     _ => {
                         let op = bin_op(op.node);
                         ExprKind::Binary {
diff --git a/src/librustc_mir/hair/cx/mod.rs b/src/librustc_mir/hair/cx/mod.rs
index c3a5fbd967c84..5274b5e9abad8 100644
--- a/src/librustc_mir/hair/cx/mod.rs
+++ b/src/librustc_mir/hair/cx/mod.rs
@@ -32,13 +32,17 @@ use rustc_const_math::{ConstInt, ConstUsize};
 pub struct Cx<'a, 'tcx: 'a> {
     tcx: &'a TyCtxt<'tcx>,
     infcx: &'a InferCtxt<'a, 'tcx>,
+    constness: hir::Constness
 }
 
 impl<'a,'tcx> Cx<'a,'tcx> {
-    pub fn new(infcx: &'a InferCtxt<'a, 'tcx>) -> Cx<'a, 'tcx> {
+    pub fn new(infcx: &'a InferCtxt<'a, 'tcx>,
+               constness: hir::Constness)
+               -> Cx<'a, 'tcx> {
         Cx {
             tcx: infcx.tcx,
             infcx: infcx,
+            constness: constness,
         }
     }
 }
diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs
index ced73f34e0d92..79d11e78bde5a 100644
--- a/src/librustc_mir/lib.rs
+++ b/src/librustc_mir/lib.rs
@@ -20,7 +20,9 @@ Rust MIR: a lowered representation of Rust. Also: an experiment!
 #![cfg_attr(not(stage0), deny(warnings))]
 #![unstable(feature = "rustc_private", issue = "27812")]
 
+#![feature(associated_consts)]
 #![feature(box_patterns)]
+#![feature(rustc_diagnostic_macros)]
 #![feature(rustc_private)]
 #![feature(staged_api)]
 #![feature(question_mark)]
@@ -31,10 +33,16 @@ extern crate graphviz as dot;
 extern crate rustc;
 extern crate rustc_data_structures;
 extern crate rustc_back;
+#[macro_use]
+#[no_link]
+extern crate rustc_bitflags;
+#[macro_use]
 extern crate syntax;
 extern crate rustc_const_math;
 extern crate rustc_const_eval;
 
+pub mod diagnostics;
+
 pub mod build;
 pub mod graphviz;
 mod hair;
diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs
index 40334f652eed3..d771c80438921 100644
--- a/src/librustc_mir/mir_map.rs
+++ b/src/librustc_mir/mir_map.rs
@@ -16,11 +16,10 @@
 //! - `#[rustc_mir(graphviz="file.gv")]`
 //! - `#[rustc_mir(pretty="file.mir")]`
 
-extern crate syntax;
-
 use build;
 use rustc::dep_graph::DepNode;
 use rustc::mir::repr::Mir;
+use rustc::mir::transform::MirSource;
 use pretty;
 use hair::cx::Cx;
 
@@ -28,13 +27,11 @@ use rustc::mir::mir_map::MirMap;
 use rustc::infer;
 use rustc::traits::ProjectionMode;
 use rustc::ty::{self, Ty, TyCtxt};
-use rustc::util::common::ErrorReported;
 use rustc::util::nodemap::NodeMap;
 use rustc::hir;
-use rustc::hir::intravisit::{self, Visitor};
-use syntax::abi::Abi;
+use rustc::hir::intravisit::{self, FnKind, Visitor};
+use rustc::hir::map::blocks::FnLikeNode;
 use syntax::ast;
-use syntax::attr::AttrMetaMethods;
 use syntax::codemap::Span;
 
 pub fn build_mir_for_crate<'tcx>(tcx: &TyCtxt<'tcx>) -> MirMap<'tcx> {
@@ -42,7 +39,7 @@ pub fn build_mir_for_crate<'tcx>(tcx: &TyCtxt<'tcx>) -> MirMap<'tcx> {
         map: NodeMap(),
     };
     {
-        let mut dump = OuterDump {
+        let mut dump = BuildMir {
             tcx: tcx,
             map: &mut map,
         };
@@ -52,161 +49,160 @@ pub fn build_mir_for_crate<'tcx>(tcx: &TyCtxt<'tcx>) -> MirMap<'tcx> {
 }
 
 ///////////////////////////////////////////////////////////////////////////
-// OuterDump -- walks a crate, looking for fn items and methods to build MIR from
+// BuildMir -- walks a crate, looking for fn items and methods to build MIR from
 
-struct OuterDump<'a, 'tcx: 'a> {
+struct BuildMir<'a, 'tcx: 'a> {
     tcx: &'a TyCtxt<'tcx>,
     map: &'a mut MirMap<'tcx>,
 }
 
-impl<'a, 'tcx> OuterDump<'a, 'tcx> {
-    fn visit_mir<OP>(&mut self, attributes: &'a [ast::Attribute], mut walk_op: OP)
-        where OP: for<'m> FnMut(&mut InnerDump<'a, 'm, 'tcx>)
+impl<'a, 'tcx> BuildMir<'a, 'tcx> {
+    fn build<F>(&mut self, src: MirSource, f: F)
+        where F: for<'b> FnOnce(Cx<'b, 'tcx>) -> (Mir<'tcx>, build::ScopeAuxiliaryVec)
     {
-        let mut closure_dump = InnerDump {
-            tcx: self.tcx,
-            attr: None,
-            map: &mut *self.map,
-        };
-        for attr in attributes {
-            if attr.check_name("rustc_mir") {
-                closure_dump.attr = Some(attr);
+        let constness = match src {
+            MirSource::Const(_) |
+            MirSource::Static(..) => hir::Constness::Const,
+            MirSource::Fn(id) => {
+                let fn_like = FnLikeNode::from_node(self.tcx.map.get(id));
+                match fn_like.map(|f| f.kind()) {
+                    Some(FnKind::ItemFn(_, _, _, c, _, _, _)) => c,
+                    Some(FnKind::Method(_, m, _, _)) => m.constness,
+                    _ => hir::Constness::NotConst
+                }
             }
+            MirSource::Promoted(..) => bug!()
+        };
+
+        let param_env = ty::ParameterEnvironment::for_item(self.tcx, src.item_id());
+        let infcx = infer::new_infer_ctxt(self.tcx,
+                                          &self.tcx.tables,
+                                          Some(param_env),
+                                          ProjectionMode::AnyFinal);
+
+        let (mir, scope_auxiliary) = f(Cx::new(&infcx, constness));
+
+        pretty::dump_mir(self.tcx, "mir_map", &0, src, &mir, Some(&scope_auxiliary));
+
+        assert!(self.map.map.insert(src.item_id(), mir).is_none())
+    }
+
+    fn build_const_integer(&mut self, expr: &'tcx hir::Expr) {
+        // FIXME(eddyb) Closures should have separate
+        // function definition IDs and expression IDs.
+        // Type-checking should not let closures get
+        // this far in an integer constant position.
+        if let hir::ExprClosure(..) = expr.node {
+            return;
         }
-        walk_op(&mut closure_dump);
+        self.build(MirSource::Const(expr.id), |cx| {
+            build::construct_const(cx, expr.id, expr)
+        });
     }
 }
 
-
-impl<'a, 'tcx> Visitor<'tcx> for OuterDump<'a, 'tcx> {
+impl<'a, 'tcx> Visitor<'tcx> for BuildMir<'a, 'tcx> {
+    // Const and static items.
     fn visit_item(&mut self, item: &'tcx hir::Item) {
-        self.visit_mir(&item.attrs, |c| intravisit::walk_item(c, item));
+        match item.node {
+            hir::ItemConst(_, ref expr) => {
+                self.build(MirSource::Const(item.id), |cx| {
+                    build::construct_const(cx, item.id, expr)
+                });
+            }
+            hir::ItemStatic(_, m, ref expr) => {
+                self.build(MirSource::Static(item.id, m), |cx| {
+                    build::construct_const(cx, item.id, expr)
+                });
+            }
+            _ => {}
+        }
         intravisit::walk_item(self, item);
     }
 
-    fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem) {
-        match trait_item.node {
-            hir::MethodTraitItem(_, Some(_)) => {
-                self.visit_mir(&trait_item.attrs, |c| intravisit::walk_trait_item(c, trait_item));
-            }
-            hir::MethodTraitItem(_, None) |
-            hir::ConstTraitItem(..) |
-            hir::TypeTraitItem(..) => {}
+    // Trait associated const defaults.
+    fn visit_trait_item(&mut self, item: &'tcx hir::TraitItem) {
+        if let hir::ConstTraitItem(_, Some(ref expr)) = item.node {
+            self.build(MirSource::Const(item.id), |cx| {
+                build::construct_const(cx, item.id, expr)
+            });
         }
-        intravisit::walk_trait_item(self, trait_item);
+        intravisit::walk_trait_item(self, item);
     }
 
-    fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem) {
-        match impl_item.node {
-            hir::ImplItemKind::Method(..) => {
-                self.visit_mir(&impl_item.attrs, |c| intravisit::walk_impl_item(c, impl_item));
-            }
-            hir::ImplItemKind::Const(..) | hir::ImplItemKind::Type(..) => {}
+    // Impl associated const.
+    fn visit_impl_item(&mut self, item: &'tcx hir::ImplItem) {
+        if let hir::ImplItemKind::Const(_, ref expr) = item.node {
+            self.build(MirSource::Const(item.id), |cx| {
+                build::construct_const(cx, item.id, expr)
+            });
         }
-        intravisit::walk_impl_item(self, impl_item);
+        intravisit::walk_impl_item(self, item);
     }
-}
 
-///////////////////////////////////////////////////////////////////////////
-// InnerDump -- dumps MIR for a single fn and its contained closures
-
-struct InnerDump<'a, 'm, 'tcx: 'a + 'm> {
-    tcx: &'a TyCtxt<'tcx>,
-    map: &'m mut MirMap<'tcx>,
-    attr: Option<&'a ast::Attribute>,
-}
+    // Repeat counts, i.e. [expr; constant].
+    fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
+        if let hir::ExprRepeat(_, ref count) = expr.node {
+            self.build_const_integer(count);
+        }
+        intravisit::walk_expr(self, expr);
+    }
 
-impl<'a, 'm, 'tcx> Visitor<'tcx> for InnerDump<'a,'m,'tcx> {
-    fn visit_trait_item(&mut self, _: &'tcx hir::TraitItem) {
-        // ignore methods; the outer dump will call us for them independently
+    // Array lengths, i.e. [T; constant].
+    fn visit_ty(&mut self, ty: &'tcx hir::Ty) {
+        if let hir::TyFixedLengthVec(_, ref length) = ty.node {
+            self.build_const_integer(length);
+        }
+        intravisit::walk_ty(self, ty);
     }
 
-    fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem) {
-        // ignore methods; the outer dump will call us for them independently
+    // Enum variant discriminant values.
+    fn visit_variant(&mut self, v: &'tcx hir::Variant,
+                     g: &'tcx hir::Generics, item_id: ast::NodeId) {
+        if let Some(ref expr) = v.node.disr_expr {
+            self.build_const_integer(expr);
+        }
+        intravisit::walk_variant(self, v, g, item_id);
     }
 
     fn visit_fn(&mut self,
-                fk: intravisit::FnKind<'tcx>,
+                fk: FnKind<'tcx>,
                 decl: &'tcx hir::FnDecl,
                 body: &'tcx hir::Block,
                 span: Span,
                 id: ast::NodeId) {
-        let implicit_arg_tys = if let intravisit::FnKind::Closure(..) = fk {
-            vec![closure_self_ty(&self.tcx, id, body.id)]
+        // fetch the fully liberated fn signature (that is, all bound
+        // types/lifetimes replaced)
+        let fn_sig = match self.tcx.tables.borrow().liberated_fn_sigs.get(&id) {
+            Some(f) => f.clone(),
+            None => {
+                span_bug!(span, "no liberated fn sig for {:?}", id);
+            }
+        };
+
+        let implicit_argument = if let FnKind::Closure(..) = fk {
+            Some((closure_self_ty(&self.tcx, id, body.id), None))
         } else {
-            vec![]
+            None
         };
 
-        let param_env = ty::ParameterEnvironment::for_item(self.tcx, id);
-        let infcx = infer::new_infer_ctxt(self.tcx,
-                                          &self.tcx.tables,
-                                          Some(param_env),
-                                          ProjectionMode::AnyFinal);
+        let explicit_arguments =
+            decl.inputs
+                .iter()
+                .enumerate()
+                .map(|(index, arg)| {
+                    (fn_sig.inputs[index], Some(&*arg.pat))
+                });
 
-        match build_mir(Cx::new(&infcx), implicit_arg_tys, id, span, decl, body) {
-            Ok(mir) => assert!(self.map.map.insert(id, mir).is_none()),
-            Err(ErrorReported) => {}
-        }
+        self.build(MirSource::Fn(id), |cx| {
+            let arguments = implicit_argument.into_iter().chain(explicit_arguments);
+            build::construct_fn(cx, id, arguments, fn_sig.output, body)
+        });
 
         intravisit::walk_fn(self, fk, decl, body, span);
     }
 }
 
-fn build_mir<'a,'tcx:'a>(cx: Cx<'a,'tcx>,
-                         implicit_arg_tys: Vec<Ty<'tcx>>,
-                         fn_id: ast::NodeId,
-                         span: Span,
-                         decl: &'tcx hir::FnDecl,
-                         body: &'tcx hir::Block)
-                         -> Result<Mir<'tcx>, ErrorReported> {
-    // fetch the fully liberated fn signature (that is, all bound
-    // types/lifetimes replaced)
-    let fn_sig = match cx.tcx().tables.borrow().liberated_fn_sigs.get(&fn_id) {
-        Some(f) => f.clone(),
-        None => {
-            span_bug!(span, "no liberated fn sig for {:?}", fn_id);
-        }
-    };
-
-    let arguments =
-        decl.inputs
-            .iter()
-            .enumerate()
-            .map(|(index, arg)| {
-                (fn_sig.inputs[index], &*arg.pat)
-            })
-            .collect();
-
-    let (mut mir, scope_auxiliary) =
-        build::construct(cx,
-                         span,
-                         fn_id,
-                         body.id,
-                         implicit_arg_tys,
-                         arguments,
-                         fn_sig.output,
-                         body);
-
-    match cx.tcx().node_id_to_type(fn_id).sty {
-        ty::TyFnDef(_, _, f) if f.abi == Abi::RustCall => {
-            // RustCall pseudo-ABI untuples the last argument.
-            if let Some(arg_decl) = mir.arg_decls.last_mut() {
-                arg_decl.spread = true;
-            }
-        }
-        _ => {}
-    }
-
-    pretty::dump_mir(cx.tcx(),
-                     "mir_map",
-                     &0,
-                     fn_id,
-                     &mir,
-                     Some(&scope_auxiliary));
-
-    Ok(mir)
-}
-
 fn closure_self_ty<'a, 'tcx>(tcx: &TyCtxt<'tcx>,
                              closure_expr_id: ast::NodeId,
                              body_id: ast::NodeId)
diff --git a/src/librustc_mir/pretty.rs b/src/librustc_mir/pretty.rs
index 77e8e37ef7493..0e082ac262ec1 100644
--- a/src/librustc_mir/pretty.rs
+++ b/src/librustc_mir/pretty.rs
@@ -9,7 +9,9 @@
 // except according to those terms.
 
 use build::{Location, ScopeAuxiliaryVec};
+use rustc::hir;
 use rustc::mir::repr::*;
+use rustc::mir::transform::MirSource;
 use rustc::ty::{self, TyCtxt};
 use rustc_data_structures::fnv::FnvHashMap;
 use std::fmt::Display;
@@ -37,13 +39,14 @@ const INDENT: &'static str = "    ";
 pub fn dump_mir<'a, 'tcx>(tcx: &TyCtxt<'tcx>,
                           pass_name: &str,
                           disambiguator: &Display,
-                          node_id: NodeId,
+                          src: MirSource,
                           mir: &Mir<'tcx>,
                           auxiliary: Option<&ScopeAuxiliaryVec>) {
     let filters = match tcx.sess.opts.debugging_opts.dump_mir {
         None => return,
         Some(ref filters) => filters,
     };
+    let node_id = src.item_id();
     let node_path = tcx.item_path_str(tcx.map.local_def_id(node_id));
     let is_matched =
         filters.split("&")
@@ -64,7 +67,7 @@ pub fn dump_mir<'a, 'tcx>(tcx: &TyCtxt<'tcx>,
         try!(writeln!(file, "// pass_name = {}", pass_name));
         try!(writeln!(file, "// disambiguator = {}", disambiguator));
         try!(writeln!(file, ""));
-        try!(write_mir_fn(tcx, node_id, mir, &mut file, auxiliary));
+        try!(write_mir_fn(tcx, src, mir, &mut file, auxiliary));
         Ok(())
     });
 }
@@ -76,8 +79,13 @@ pub fn write_mir_pretty<'a, 'tcx, I>(tcx: &TyCtxt<'tcx>,
                                      -> io::Result<()>
     where I: Iterator<Item=(&'a NodeId, &'a Mir<'tcx>)>, 'tcx: 'a
 {
-    for (&node_id, mir) in iter {
-        write_mir_fn(tcx, node_id, mir, w, None)?;
+    for (&id, mir) in iter {
+        let src = MirSource::from_node(tcx, id);
+        write_mir_fn(tcx, src, mir, w, None)?;
+
+        for (i, mir) in mir.promoted.iter().enumerate() {
+            write_mir_fn(tcx, MirSource::Promoted(id, i), mir, w, None)?;
+        }
     }
     Ok(())
 }
@@ -88,7 +96,7 @@ enum Annotation {
 }
 
 pub fn write_mir_fn<'tcx>(tcx: &TyCtxt<'tcx>,
-                          node_id: NodeId,
+                          src: MirSource,
                           mir: &Mir<'tcx>,
                           w: &mut Write,
                           auxiliary: Option<&ScopeAuxiliaryVec>)
@@ -111,7 +119,7 @@ pub fn write_mir_fn<'tcx>(tcx: &TyCtxt<'tcx>,
         }
     }
 
-    write_mir_intro(tcx, node_id, mir, w)?;
+    write_mir_intro(tcx, src, mir, w)?;
     for block in mir.all_basic_blocks() {
         write_basic_block(tcx, block, mir, w, &annotations)?;
     }
@@ -214,24 +222,39 @@ fn write_scope_tree(tcx: &TyCtxt,
 
 /// Write out a human-readable textual representation of the MIR's `fn` type and the types of its
 /// local variables (both user-defined bindings and compiler temporaries).
-fn write_mir_intro(tcx: &TyCtxt, nid: NodeId, mir: &Mir, w: &mut Write)
+fn write_mir_intro(tcx: &TyCtxt, src: MirSource, mir: &Mir, w: &mut Write)
                    -> io::Result<()> {
-    write!(w, "fn {}(", tcx.node_path_str(nid))?;
+    match src {
+        MirSource::Fn(_) => write!(w, "fn")?,
+        MirSource::Const(_) => write!(w, "const")?,
+        MirSource::Static(_, hir::MutImmutable) => write!(w, "static")?,
+        MirSource::Static(_, hir::MutMutable) => write!(w, "static mut")?,
+        MirSource::Promoted(_, i) => write!(w, "promoted{} in", i)?
+    }
+
+    write!(w, " {}", tcx.node_path_str(src.item_id()))?;
 
-    // fn argument types.
-    for (i, arg) in mir.arg_decls.iter().enumerate() {
-        if i > 0 {
-            write!(w, ", ")?;
+    if let MirSource::Fn(_) = src {
+        write!(w, "(")?;
+
+        // fn argument types.
+        for (i, arg) in mir.arg_decls.iter().enumerate() {
+            if i > 0 {
+                write!(w, ", ")?;
+            }
+            write!(w, "{:?}: {}", Lvalue::Arg(i as u32), arg.ty)?;
         }
-        write!(w, "{:?}: {}", Lvalue::Arg(i as u32), arg.ty)?;
-    }
 
-    write!(w, ") -> ")?;
+        write!(w, ") -> ")?;
 
-    // fn return type.
-    match mir.return_ty {
-        ty::FnOutput::FnConverging(ty) => write!(w, "{}", ty)?,
-        ty::FnOutput::FnDiverging => write!(w, "!")?,
+        // fn return type.
+        match mir.return_ty {
+            ty::FnOutput::FnConverging(ty) => write!(w, "{}", ty)?,
+            ty::FnOutput::FnDiverging => write!(w, "!")?,
+        }
+    } else {
+        assert!(mir.arg_decls.is_empty());
+        write!(w, ": {} =", mir.return_ty.unwrap())?;
     }
 
     writeln!(w, " {{")?;
diff --git a/src/librustc_mir/transform/break_critical_edges.rs b/src/librustc_mir/transform/break_critical_edges.rs
index e1fb5dfd4374c..ee7c9015baad6 100644
--- a/src/librustc_mir/transform/break_critical_edges.rs
+++ b/src/librustc_mir/transform/break_critical_edges.rs
@@ -10,8 +10,7 @@
 
 use rustc::ty::TyCtxt;
 use rustc::mir::repr::*;
-use rustc::mir::transform::{MirPass, Pass};
-use syntax::ast::NodeId;
+use rustc::mir::transform::{MirPass, MirSource, Pass};
 
 use rustc_data_structures::bitvec::BitVector;
 
@@ -43,7 +42,7 @@ pub struct BreakCriticalEdges;
  */
 
 impl<'tcx> MirPass<'tcx> for BreakCriticalEdges {
-    fn run_pass(&mut self, _: &TyCtxt<'tcx>, _: NodeId, mir: &mut Mir<'tcx>) {
+    fn run_pass(&mut self, _: &TyCtxt<'tcx>, _: MirSource, mir: &mut Mir<'tcx>) {
         break_critical_edges(mir);
     }
 }
diff --git a/src/librustc_mir/transform/erase_regions.rs b/src/librustc_mir/transform/erase_regions.rs
index 12bfa3aebc201..678d2c4614d27 100644
--- a/src/librustc_mir/transform/erase_regions.rs
+++ b/src/librustc_mir/transform/erase_regions.rs
@@ -16,8 +16,7 @@ use rustc::ty::subst::Substs;
 use rustc::ty::{Ty, TyCtxt};
 use rustc::mir::repr::*;
 use rustc::mir::visit::MutVisitor;
-use rustc::mir::transform::{MirPass, Pass};
-use syntax::ast::NodeId;
+use rustc::mir::transform::{MirPass, MirSource, Pass};
 
 struct EraseRegionsVisitor<'a, 'tcx: 'a> {
     tcx: &'a TyCtxt<'tcx>,
@@ -47,7 +46,7 @@ pub struct EraseRegions;
 impl Pass for EraseRegions {}
 
 impl<'tcx> MirPass<'tcx> for EraseRegions {
-    fn run_pass(&mut self, tcx: &TyCtxt<'tcx>, _: NodeId, mir: &mut Mir<'tcx>) {
+    fn run_pass(&mut self, tcx: &TyCtxt<'tcx>, _: MirSource, mir: &mut Mir<'tcx>) {
         EraseRegionsVisitor::new(tcx).visit_mir(mir);
     }
 }
diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs
index a52a8edc21173..51f5c3cd7f53d 100644
--- a/src/librustc_mir/transform/mod.rs
+++ b/src/librustc_mir/transform/mod.rs
@@ -14,3 +14,5 @@ pub mod erase_regions;
 pub mod no_landing_pads;
 pub mod type_check;
 pub mod break_critical_edges;
+pub mod promote_consts;
+pub mod qualify_consts;
diff --git a/src/librustc_mir/transform/no_landing_pads.rs b/src/librustc_mir/transform/no_landing_pads.rs
index edfe75b8430d4..9c9f95e2e630e 100644
--- a/src/librustc_mir/transform/no_landing_pads.rs
+++ b/src/librustc_mir/transform/no_landing_pads.rs
@@ -14,8 +14,7 @@
 use rustc::ty::TyCtxt;
 use rustc::mir::repr::*;
 use rustc::mir::visit::MutVisitor;
-use rustc::mir::transform::{Pass, MirPass};
-use syntax::ast::NodeId;
+use rustc::mir::transform::{Pass, MirPass, MirSource};
 
 pub struct NoLandingPads;
 
@@ -42,7 +41,7 @@ impl<'tcx> MutVisitor<'tcx> for NoLandingPads {
 }
 
 impl<'tcx> MirPass<'tcx> for NoLandingPads {
-    fn run_pass(&mut self, tcx: &TyCtxt<'tcx>, _: NodeId, mir: &mut Mir<'tcx>) {
+    fn run_pass(&mut self, tcx: &TyCtxt<'tcx>, _: MirSource, mir: &mut Mir<'tcx>) {
         if tcx.sess.no_landing_pads() {
             self.visit_mir(mir);
         }
diff --git a/src/librustc_mir/transform/promote_consts.rs b/src/librustc_mir/transform/promote_consts.rs
new file mode 100644
index 0000000000000..c5ebe708eb456
--- /dev/null
+++ b/src/librustc_mir/transform/promote_consts.rs
@@ -0,0 +1,412 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! A pass that promotes borrows of constant rvalues.
+//!
+//! The rvalues considered constant are trees of temps,
+//! each with exactly one initialization, and holding
+//! a constant value with no interior mutability.
+//! They are placed into a new MIR constant body in
+//! `promoted` and the borrow rvalue is replaced with
+//! a `Literal::Promoted` using the index into `promoted`
+//! of that constant MIR.
+//!
+//! This pass assumes that every use is dominated by an
+//! initialization and can otherwise silence errors, if
+//! move analysis runs after promotion on broken MIR.
+
+use rustc::mir::repr::*;
+use rustc::mir::visit::{LvalueContext, MutVisitor, Visitor};
+use rustc::ty::{self, TyCtxt};
+use syntax::codemap::Span;
+
+use build::Location;
+use traversal::ReversePostorder;
+
+use std::mem;
+
+/// State of a temporary during collection and promotion.
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub enum TempState {
+    /// No references to this temp.
+    Undefined,
+    /// One direct assignment and any number of direct uses.
+    /// A borrow of this temp is promotable if the assigned
+    /// value is qualified as constant.
+    Defined {
+        location: Location,
+        uses: usize
+    },
+    /// Any other combination of assignments/uses.
+    Unpromotable,
+    /// This temp was part of an rvalue which got extracted
+    /// during promotion and needs cleanup.
+    PromotedOut
+}
+
+impl TempState {
+    pub fn is_promotable(&self) -> bool {
+        if let TempState::Defined { uses, .. } = *self {
+            uses > 0
+        } else {
+            false
+        }
+    }
+}
+
+/// A "root candidate" for promotion, which will become the
+/// returned value in a promoted MIR, unless it's a subset
+/// of a larger candidate.
+pub enum Candidate {
+    /// Borrow of a constant temporary.
+    Ref(Location),
+
+    /// Array of indices found in the third argument of
+    /// a call to one of the simd_shuffleN intrinsics.
+    ShuffleIndices(BasicBlock)
+}
+
+struct TempCollector {
+    temps: Vec<TempState>,
+    location: Location,
+    span: Span
+}
+
+impl<'tcx> Visitor<'tcx> for TempCollector {
+    fn visit_lvalue(&mut self, lvalue: &Lvalue<'tcx>, context: LvalueContext) {
+        self.super_lvalue(lvalue, context);
+        if let Lvalue::Temp(index) = *lvalue {
+            // Ignore drops, if the temp gets promoted,
+            // then it's constant and thus drop is noop.
+            if let LvalueContext::Drop = context {
+                return;
+            }
+
+            let temp = &mut self.temps[index as usize];
+            if *temp == TempState::Undefined {
+                match context {
+                    LvalueContext::Store |
+                    LvalueContext::Call => {
+                        *temp = TempState::Defined {
+                            location: self.location,
+                            uses: 0
+                        };
+                        return;
+                    }
+                    _ => { /* mark as unpromotable below */ }
+                }
+            } else if let TempState::Defined { ref mut uses, .. } = *temp {
+                match context {
+                    LvalueContext::Borrow {..} |
+                    LvalueContext::Consume |
+                    LvalueContext::Inspect => {
+                        *uses += 1;
+                        return;
+                    }
+                    _ => { /* mark as unpromotable below */ }
+                }
+            }
+            *temp = TempState::Unpromotable;
+        }
+    }
+
+    fn visit_statement(&mut self, bb: BasicBlock, statement: &Statement<'tcx>) {
+        assert_eq!(self.location.block, bb);
+        self.span = statement.span;
+        self.super_statement(bb, statement);
+        self.location.statement_index += 1;
+    }
+
+    fn visit_terminator(&mut self, bb: BasicBlock, terminator: &Terminator<'tcx>) {
+        self.span = terminator.span;
+        self.super_terminator(bb, terminator);
+    }
+
+    fn visit_basic_block_data(&mut self, bb: BasicBlock, data: &BasicBlockData<'tcx>) {
+        self.location.statement_index = 0;
+        self.location.block = bb;
+        self.super_basic_block_data(bb, data);
+    }
+}
+
+pub fn collect_temps(mir: &Mir, rpo: &mut ReversePostorder) -> Vec<TempState> {
+    let mut collector = TempCollector {
+        temps: vec![TempState::Undefined; mir.temp_decls.len()],
+        location: Location {
+            block: START_BLOCK,
+            statement_index: 0
+        },
+        span: mir.span
+    };
+    for (bb, data) in rpo {
+        collector.visit_basic_block_data(bb, data);
+    }
+    collector.temps
+}
+
+struct Promoter<'a, 'tcx: 'a> {
+    source: &'a mut Mir<'tcx>,
+    promoted: Mir<'tcx>,
+    temps: &'a mut Vec<TempState>,
+
+    /// If true, all nested temps are also kept in the
+    /// source MIR, not moved to the promoted MIR.
+    keep_original: bool
+}
+
+impl<'a, 'tcx> Promoter<'a, 'tcx> {
+    fn new_block(&mut self) -> BasicBlock {
+        let index = self.promoted.basic_blocks.len();
+        self.promoted.basic_blocks.push(BasicBlockData {
+            statements: vec![],
+            terminator: Some(Terminator {
+                span: self.promoted.span,
+                scope: ScopeId::new(0),
+                kind: TerminatorKind::Return
+            }),
+            is_cleanup: false
+        });
+        BasicBlock::new(index)
+    }
+
+    fn assign(&mut self, dest: Lvalue<'tcx>, rvalue: Rvalue<'tcx>, span: Span) {
+        let data = self.promoted.basic_blocks.last_mut().unwrap();
+        data.statements.push(Statement {
+            span: span,
+            scope: ScopeId::new(0),
+            kind: StatementKind::Assign(dest, rvalue)
+        });
+    }
+
+    /// Copy the initialization of this temp to the
+    /// promoted MIR, recursing through temps.
+    fn promote_temp(&mut self, index: u32) -> u32 {
+        let index = index as usize;
+        let old_keep_original = self.keep_original;
+        let (bb, stmt_idx) = match self.temps[index] {
+            TempState::Defined {
+                location: Location { block, statement_index },
+                uses
+            } if uses > 0 => {
+                if uses > 1 {
+                    self.keep_original = true;
+                }
+                (block, statement_index)
+            }
+            temp =>  {
+                span_bug!(self.promoted.span, "tmp{} not promotable: {:?}",
+                          index, temp);
+            }
+        };
+        if !self.keep_original {
+            self.temps[index] = TempState::PromotedOut;
+        }
+
+        let no_stmts = self.source[bb].statements.len();
+
+        // First, take the Rvalue or Call out of the source MIR,
+        // or duplicate it, depending on keep_original.
+        let (mut rvalue, mut call) = (None, None);
+        let span = if stmt_idx < no_stmts {
+            let statement = &mut self.source[bb].statements[stmt_idx];
+            let StatementKind::Assign(_, ref mut rhs) = statement.kind;
+            if self.keep_original {
+                rvalue = Some(rhs.clone());
+            } else {
+                let unit = Rvalue::Aggregate(AggregateKind::Tuple, vec![]);
+                rvalue = Some(mem::replace(rhs, unit));
+            }
+            statement.span
+        } else if self.keep_original {
+            let terminator = self.source[bb].terminator().clone();
+            call = Some(terminator.kind);
+            terminator.span
+        } else {
+            let terminator = self.source[bb].terminator_mut();
+            let target = match terminator.kind {
+                TerminatorKind::Call {
+                    destination: ref mut dest @ Some(_),
+                    ref mut cleanup, ..
+                } => {
+                    // No cleanup necessary.
+                    cleanup.take();
+
+                    // We'll put a new destination in later.
+                    dest.take().unwrap().1
+                }
+                ref kind => {
+                    span_bug!(terminator.span, "{:?} not promotable", kind);
+                }
+            };
+            call = Some(mem::replace(&mut terminator.kind, TerminatorKind::Goto {
+                target: target
+            }));
+            terminator.span
+        };
+
+        // Then, recurse for components in the Rvalue or Call.
+        if stmt_idx < no_stmts {
+            self.visit_rvalue(rvalue.as_mut().unwrap());
+        } else {
+            self.visit_terminator_kind(bb, call.as_mut().unwrap());
+        }
+
+        let new_index = self.promoted.temp_decls.len() as u32;
+        let new_temp = Lvalue::Temp(new_index);
+        self.promoted.temp_decls.push(TempDecl {
+            ty: self.source.temp_decls[index].ty
+        });
+
+        // Inject the Rvalue or Call into the promoted MIR.
+        if stmt_idx < no_stmts {
+            self.assign(new_temp, rvalue.unwrap(), span);
+        } else {
+            let last = self.promoted.basic_blocks.len() - 1;
+            let new_target = self.new_block();
+            let mut call = call.unwrap();
+            match call {
+                TerminatorKind::Call { ref mut destination, ..}  => {
+                    *destination = Some((new_temp, new_target));
+                }
+                _ => bug!()
+            }
+            let terminator = &mut self.promoted.basic_blocks[last].terminator_mut();
+            terminator.span = span;
+            terminator.kind = call;
+        }
+
+        // Restore the old duplication state.
+        self.keep_original = old_keep_original;
+
+        new_index
+    }
+
+    fn promote_candidate(mut self, candidate: Candidate) {
+        let span = self.promoted.span;
+        let new_operand = Operand::Constant(Constant {
+            span: span,
+            ty: self.promoted.return_ty.unwrap(),
+            literal: Literal::Promoted {
+                index: self.source.promoted.len()
+            }
+        });
+        let mut rvalue = match candidate {
+            Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => {
+                match self.source[bb].statements[stmt_idx].kind {
+                    StatementKind::Assign(_, ref mut rvalue) => {
+                        mem::replace(rvalue, Rvalue::Use(new_operand))
+                    }
+                }
+            }
+            Candidate::ShuffleIndices(bb) => {
+                match self.source[bb].terminator_mut().kind {
+                    TerminatorKind::Call { ref mut args, .. } => {
+                        Rvalue::Use(mem::replace(&mut args[2], new_operand))
+                    }
+                    _ => bug!()
+                }
+            }
+        };
+        self.visit_rvalue(&mut rvalue);
+        self.assign(Lvalue::ReturnPointer, rvalue, span);
+        self.source.promoted.push(self.promoted);
+    }
+}
+
+/// Replaces all temporaries with their promoted counterparts.
+impl<'a, 'tcx> MutVisitor<'tcx> for Promoter<'a, 'tcx> {
+    fn visit_lvalue(&mut self, lvalue: &mut Lvalue<'tcx>, context: LvalueContext) {
+        if let Lvalue::Temp(ref mut index) = *lvalue {
+            *index = self.promote_temp(*index);
+        }
+        self.super_lvalue(lvalue, context);
+    }
+}
+
+pub fn promote_candidates<'tcx>(mir: &mut Mir<'tcx>,
+                                tcx: &TyCtxt<'tcx>,
+                                mut temps: Vec<TempState>,
+                                candidates: Vec<Candidate>) {
+    // Visit candidates in reverse, in case they're nested.
+    for candidate in candidates.into_iter().rev() {
+        let (span, ty) = match candidate {
+            Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => {
+                let statement = &mir[bb].statements[stmt_idx];
+                let StatementKind::Assign(ref dest, _) = statement.kind;
+                if let Lvalue::Temp(index) = *dest {
+                    if temps[index as usize] == TempState::PromotedOut {
+                        // Already promoted.
+                        continue;
+                    }
+                }
+                (statement.span, mir.lvalue_ty(tcx, dest).to_ty(tcx))
+            }
+            Candidate::ShuffleIndices(bb) => {
+                let terminator = mir[bb].terminator();
+                let ty = match terminator.kind {
+                    TerminatorKind::Call { ref args, .. } => {
+                        mir.operand_ty(tcx, &args[2])
+                    }
+                    _ => {
+                        span_bug!(terminator.span,
+                                  "expected simd_shuffleN call to promote");
+                    }
+                };
+                (terminator.span, ty)
+            }
+        };
+
+        let mut promoter = Promoter {
+            source: mir,
+            promoted: Mir {
+                basic_blocks: vec![],
+                scopes: vec![ScopeData {
+                    span: span,
+                    parent_scope: None
+                }],
+                promoted: vec![],
+                return_ty: ty::FnConverging(ty),
+                var_decls: vec![],
+                arg_decls: vec![],
+                temp_decls: vec![],
+                upvar_decls: vec![],
+                span: span
+            },
+            temps: &mut temps,
+            keep_original: false
+        };
+        assert_eq!(promoter.new_block(), START_BLOCK);
+        promoter.promote_candidate(candidate);
+    }
+
+    // Eliminate assignments to, and drops of promoted temps.
+    let promoted = |index: u32| temps[index as usize] == TempState::PromotedOut;
+    for block in &mut mir.basic_blocks {
+        block.statements.retain(|statement| {
+            match statement.kind {
+                StatementKind::Assign(Lvalue::Temp(index), _) => {
+                    !promoted(index)
+                }
+                _ => true
+            }
+        });
+        let terminator = block.terminator_mut();
+        match terminator.kind {
+            TerminatorKind::Drop { value: Lvalue::Temp(index), target, .. } => {
+                if promoted(index) {
+                    terminator.kind = TerminatorKind::Goto {
+                        target: target
+                    };
+                }
+            }
+            _ => {}
+        }
+    }
+}
diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs
new file mode 100644
index 0000000000000..90823528973b9
--- /dev/null
+++ b/src/librustc_mir/transform/qualify_consts.rs
@@ -0,0 +1,1037 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! A pass that qualifies constness of temporaries in constants,
+//! static initializers and functions and also drives promotion.
+//!
+//! The Qualif flags below can be used to also provide better
+//! diagnostics as to why a constant rvalue wasn't promoted.
+
+use rustc_data_structures::bitvec::BitVector;
+use rustc::hir;
+use rustc::hir::def_id::DefId;
+use rustc::hir::intravisit::FnKind;
+use rustc::hir::map::blocks::FnLikeNode;
+use rustc::infer;
+use rustc::traits::{self, ProjectionMode};
+use rustc::ty::{self, TyCtxt, Ty};
+use rustc::ty::cast::CastTy;
+use rustc::mir::repr::*;
+use rustc::mir::mir_map::MirMap;
+use rustc::mir::transform::{Pass, MirMapPass, MirSource};
+use rustc::mir::visit::{LvalueContext, Visitor};
+use rustc::util::nodemap::DefIdMap;
+use syntax::abi::Abi;
+use syntax::codemap::Span;
+use syntax::feature_gate::UnstableFeatures;
+
+use std::collections::hash_map::Entry;
+use std::fmt;
+
+use build::Location;
+use traversal::{self, ReversePostorder};
+
+use super::promote_consts::{self, Candidate, TempState};
+
+bitflags! {
+    flags Qualif: u8 {
+        // Const item's qualification while recursing.
+        // Recursive consts are an error.
+        const RECURSIVE         = 1 << 0,
+
+        // Constant containing interior mutability (UnsafeCell).
+        const MUTABLE_INTERIOR  = 1 << 1,
+
+        // Constant containing an ADT that implements Drop.
+        const NEEDS_DROP        = 1 << 2,
+
+        // Function argument.
+        const FN_ARGUMENT       = 1 << 3,
+
+        // Static lvalue or move from a static.
+        const STATIC            = 1 << 4,
+
+        // Reference to a static.
+        const STATIC_REF        = 1 << 5,
+
+        // Not constant at all - non-`const fn` calls, asm!,
+        // pointer comparisons, ptr-to-int casts, etc.
+        const NOT_CONST         = 1 << 6,
+
+        // Borrows of temporaries can be promoted only
+        // if they have none of the above qualifications.
+        const UNPROMOTABLE      = !0,
+
+        // Const items can only have MUTABLE_INTERIOR
+        // without producing an error.
+        const CONST_ERROR       = !Qualif::MUTABLE_INTERIOR.bits
+    }
+}
+
+impl Qualif {
+    /// Remove flags which are impossible for the given type.
+    fn restrict<'a, 'tcx>(&mut self, ty: Ty<'tcx>,
+                          param_env: &ty::ParameterEnvironment<'a, 'tcx>) {
+        if !ty.type_contents(param_env.tcx).interior_unsafe() {
+            *self = *self - Qualif::MUTABLE_INTERIOR;
+        }
+        if !param_env.tcx.type_needs_drop_given_env(ty, param_env) {
+            *self = *self - Qualif::NEEDS_DROP;
+        }
+    }
+}
+
+/// What kind of item we are in.
+#[derive(Copy, Clone, PartialEq, Eq)]
+enum Mode {
+    Const,
+    Static,
+    StaticMut,
+    ConstFn,
+    Fn
+}
+
+impl fmt::Display for Mode {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match *self {
+            Mode::Const => write!(f, "constant"),
+            Mode::Static | Mode::StaticMut => write!(f, "static"),
+            Mode::ConstFn => write!(f, "constant function"),
+            Mode::Fn => write!(f, "function")
+        }
+    }
+}
+
+fn is_const_fn(tcx: &TyCtxt, def_id: DefId) -> bool {
+    if let Some(node_id) = tcx.map.as_local_node_id(def_id) {
+        let fn_like = FnLikeNode::from_node(tcx.map.get(node_id));
+        match fn_like.map(|f| f.kind()) {
+            Some(FnKind::ItemFn(_, _, _, c, _, _, _)) => {
+                c == hir::Constness::Const
+            }
+            Some(FnKind::Method(_, m, _, _)) => {
+                m.constness == hir::Constness::Const
+            }
+            _ => false
+        }
+    } else {
+        tcx.sess.cstore.is_const_fn(def_id)
+    }
+}
+
+struct Qualifier<'a, 'tcx: 'a> {
+    mode: Mode,
+    span: Span,
+    def_id: DefId,
+    mir: &'a Mir<'tcx>,
+    rpo: ReversePostorder<'a, 'tcx>,
+    tcx: &'a TyCtxt<'tcx>,
+    param_env: ty::ParameterEnvironment<'a, 'tcx>,
+    qualif_map: &'a mut DefIdMap<Qualif>,
+    mir_map: Option<&'a MirMap<'tcx>>,
+    temp_qualif: Vec<Option<Qualif>>,
+    return_qualif: Option<Qualif>,
+    qualif: Qualif,
+    const_fn_arg_vars: BitVector,
+    location: Location,
+    temp_promotion_state: Vec<TempState>,
+    promotion_candidates: Vec<Candidate>
+}
+
+impl<'a, 'tcx> Qualifier<'a, 'tcx> {
+    fn new(param_env: ty::ParameterEnvironment<'a, 'tcx>,
+           qualif_map: &'a mut DefIdMap<Qualif>,
+           mir_map: Option<&'a MirMap<'tcx>>,
+           def_id: DefId,
+           mir: &'a Mir<'tcx>,
+           mode: Mode)
+           -> Qualifier<'a, 'tcx> {
+        let mut rpo = traversal::reverse_postorder(mir);
+        let temps = promote_consts::collect_temps(mir, &mut rpo);
+        rpo.reset();
+        Qualifier {
+            mode: mode,
+            span: mir.span,
+            def_id: def_id,
+            mir: mir,
+            rpo: rpo,
+            tcx: param_env.tcx,
+            param_env: param_env,
+            qualif_map: qualif_map,
+            mir_map: mir_map,
+            temp_qualif: vec![None; mir.temp_decls.len()],
+            return_qualif: None,
+            qualif: Qualif::empty(),
+            const_fn_arg_vars: BitVector::new(mir.var_decls.len()),
+            location: Location {
+                block: START_BLOCK,
+                statement_index: 0
+            },
+            temp_promotion_state: temps,
+            promotion_candidates: vec![]
+        }
+    }
+
+    // FIXME(eddyb) we could split the errors into meaningful
+    // categories, but enabling full miri would make that
+    // slightly pointless (even with feature-gating).
+    fn not_const(&mut self) {
+        self.add(Qualif::NOT_CONST);
+        if self.mode != Mode::Fn {
+            span_err!(self.tcx.sess, self.span, E0019,
+                      "{} contains unimplemented expression type", self.mode);
+        }
+    }
+
+    /// Error about extra statements in a constant.
+    fn statement_like(&mut self) {
+        self.add(Qualif::NOT_CONST);
+        if self.mode != Mode::Fn {
+            span_err!(self.tcx.sess, self.span, E0016,
+                      "blocks in {}s are limited to items and tail expressions",
+                      self.mode);
+        }
+    }
+
+    /// Add the given qualification to self.qualif.
+    fn add(&mut self, qualif: Qualif) {
+        self.qualif = self.qualif | qualif;
+    }
+
+    /// Add the given type's qualification to self.qualif.
+    fn add_type(&mut self, ty: Ty<'tcx>) {
+        self.add(Qualif::MUTABLE_INTERIOR | Qualif::NEEDS_DROP);
+        self.qualif.restrict(ty, &self.param_env);
+    }
+
+    /// Within the provided closure, self.qualif will start
+    /// out empty, and its value after the closure returns will
+    /// be combined with the value before the call to nest.
+    fn nest<F: FnOnce(&mut Self)>(&mut self, f: F) {
+        let original = self.qualif;
+        self.qualif = Qualif::empty();
+        f(self);
+        self.add(original);
+    }
+
+    /// Check for NEEDS_DROP (from an ADT or const fn call) and
+    /// error, unless we're in a function, or the feature-gate
+    /// for globals with destructors is enabled.
+    fn deny_drop(&self) {
+        if self.mode == Mode::Fn || !self.qualif.intersects(Qualif::NEEDS_DROP) {
+            return;
+        }
+
+        // Static and const fn's allow destructors, but they're feature-gated.
+        let msg = if self.mode != Mode::Const {
+            // Feature-gate for globals with destructors is enabled.
+            if self.tcx.sess.features.borrow().drop_types_in_const {
+                return;
+            }
+
+            // This comes from a macro that has #[allow_internal_unstable].
+            if self.tcx.sess.codemap().span_allows_unstable(self.span) {
+                return;
+            }
+
+            format!("destructors in {}s are an unstable feature",
+                    self.mode)
+        } else {
+            format!("{}s are not allowed to have destructors",
+                    self.mode)
+        };
+
+        let mut err =
+            struct_span_err!(self.tcx.sess, self.span, E0493, "{}", msg);
+        if self.mode != Mode::Const {
+            help!(&mut err,
+                  "in Nightly builds, add `#![feature(drop_types_in_const)]` \
+                   to the crate attributes to enable");
+        }
+        err.emit();
+    }
+
+    /// Check if an Lvalue with the current qualifications could
+    /// be consumed, by either an operand or a Deref projection.
+    fn try_consume(&mut self) -> bool {
+        if self.qualif.intersects(Qualif::STATIC) && self.mode != Mode::Fn {
+            let msg = if self.mode == Mode::Static ||
+                         self.mode == Mode::StaticMut {
+                "cannot refer to other statics by value, use the \
+                 address-of operator or a constant instead"
+            } else {
+                "cannot refer to statics by value, use a constant instead"
+            };
+            span_err!(self.tcx.sess, self.span, E0394, "{}", msg);
+
+            // Replace STATIC with NOT_CONST to avoid further errors.
+            self.qualif = self.qualif - Qualif::STATIC;
+            self.add(Qualif::NOT_CONST);
+
+            false
+        } else {
+            true
+        }
+    }
+
+    /// Assign the current qualification to the given destination.
+    fn assign(&mut self, dest: &Lvalue<'tcx>) {
+        let qualif = self.qualif;
+        let span = self.span;
+        let store = |slot: &mut Option<Qualif>| {
+            if slot.is_some() {
+                span_bug!(span, "multiple assignments to {:?}", dest);
+            }
+            *slot = Some(qualif);
+        };
+
+        // Only handle promotable temps in non-const functions.
+        if self.mode == Mode::Fn {
+            if let Lvalue::Temp(index) = *dest {
+                if self.temp_promotion_state[index as usize].is_promotable() {
+                    store(&mut self.temp_qualif[index as usize]);
+                }
+            }
+            return;
+        }
+
+        match *dest {
+            Lvalue::Temp(index) => store(&mut self.temp_qualif[index as usize]),
+            Lvalue::ReturnPointer => store(&mut self.return_qualif),
+
+            Lvalue::Projection(box Projection {
+                base: Lvalue::Temp(index),
+                elem: ProjectionElem::Deref
+            }) if self.mir.temp_decls[index as usize].ty.is_unique()
+               && self.temp_qualif[index as usize].map_or(false, |qualif| {
+                    qualif.intersects(Qualif::NOT_CONST)
+               }) => {
+                // Part of `box expr`, we should've errored
+                // already for the Box allocation Rvalue.
+            }
+
+            // This must be an explicit assignment.
+            _ => {
+                // Catch more errors in the destination.
+                self.visit_lvalue(dest, LvalueContext::Store);
+                self.statement_like();
+            }
+        }
+    }
+
+    /// Returns true if the block ends in a bounds check branch, i.e.:
+    /// len = Len(array);
+    /// cond = Lt(idx, len);
+    /// if cond {
+    ///     ...
+    /// } else {
+    ///     loc = (...);
+    ///     loc_ref = &loc;
+    ///     panic_bounds_check(loc_ref, idx, len);
+    /// }
+    fn is_bounds_check(&self, bb: BasicBlock,
+                       cond_op: &Operand<'tcx>,
+                       if_else: BasicBlock) -> bool {
+        use rustc::mir::repr::Lvalue::*;
+        use rustc::mir::repr::Operand::Consume;
+        use rustc::mir::repr::Rvalue::*;
+        use rustc::mir::repr::StatementKind::*;
+        use rustc::mir::repr::TerminatorKind::*;
+
+        let stmts = &self.mir[bb].statements;
+        let stmts_panic = &self.mir[if_else].statements;
+        if stmts.len() < 2 || stmts_panic.len() != 2 {
+            return false;
+        }
+
+        let all = (&stmts[stmts.len() - 2].kind,
+                   &stmts[stmts.len() - 1].kind,
+                   cond_op,
+                   &stmts_panic[0].kind,
+                   &stmts_panic[1].kind,
+                   &self.mir[if_else].terminator().kind);
+        match all {
+            (&Assign(Temp(len), Len(_)),
+             &Assign(Temp(cond), BinaryOp(BinOp::Lt, ref idx, Consume(Temp(len2)))),
+             /* if */ &Consume(Temp(cond2)), /* {...} else */
+             &Assign(Temp(loc), Aggregate(..)),
+             &Assign(Temp(loc_ref), Ref(_, _, Temp(loc2))),
+             &Call {
+                func: Operand::Constant(Constant {
+                    literal: Literal::Item { def_id, .. }, ..
+                }),
+                ref args,
+                destination: None,
+                ..
+            }) => {
+                len == len2 && cond == cond2 && loc == loc2 &&
+                args[0] == Consume(Temp(loc_ref)) &&
+                args[1] == *idx &&
+                args[2] == Consume(Temp(len)) &&
+                Some(def_id) == self.tcx.lang_items.panic_bounds_check_fn()
+            }
+            _ => false
+        }
+    }
+
+    /// Qualify a whole const, static initializer or const fn.
+    fn qualify_const(&mut self) -> Qualif {
+        let mir = self.mir;
+
+        let mut seen_blocks = BitVector::new(mir.basic_blocks.len());
+        let mut bb = START_BLOCK;
+        loop {
+            seen_blocks.insert(bb.index());
+
+            self.visit_basic_block_data(bb, &mir[bb]);
+
+            let target = match mir[bb].terminator().kind {
+                TerminatorKind::Goto { target } |
+                // Drops are considered noops.
+                TerminatorKind::Drop { target, .. } |
+                TerminatorKind::Call { destination: Some((_, target)), .. } => {
+                    Some(target)
+                }
+
+                // Non-terminating calls cannot produce any value.
+                TerminatorKind::Call { destination: None, .. } => {
+                    return Qualif::empty();
+                }
+
+                // Need to allow bounds checking branches.
+                TerminatorKind::If { ref cond, targets: (if_true, if_else) } => {
+                    if self.is_bounds_check(bb, cond, if_else) {
+                        Some(if_true)
+                    } else {
+                        None
+                    }
+                }
+
+                TerminatorKind::Switch {..} |
+                TerminatorKind::SwitchInt {..} |
+                TerminatorKind::Resume => None,
+
+                TerminatorKind::Return => {
+                    // Check for unused values. This usually means
+                    // there are extra statements in the AST.
+                    for i in 0..mir.temp_decls.len() {
+                        if self.temp_qualif[i].is_none() {
+                            continue;
+                        }
+
+                        let state = self.temp_promotion_state[i];
+                        if let TempState::Defined { location, uses: 0 } = state {
+                            let data = &mir[location.block];
+                            let stmt_idx = location.statement_index;
+
+                            // Get the span for the initialization.
+                            if stmt_idx < data.statements.len() {
+                                self.span = data.statements[stmt_idx].span;
+                            } else {
+                                self.span = data.terminator().span;
+                            }
+
+                            // Treat this as a statement in the AST.
+                            self.statement_like();
+                        }
+                    }
+
+                    // Make sure there are no extra unassigned variables.
+                    self.qualif = Qualif::NOT_CONST;
+                    for index in 0..mir.var_decls.len() {
+                        if !self.const_fn_arg_vars.contains(index) {
+                            self.assign(&Lvalue::Var(index as u32));
+                        }
+                    }
+
+                    break;
+                }
+            };
+
+            match target {
+                // No loops allowed.
+                Some(target) if !seen_blocks.contains(target.index()) => {
+                    bb = target;
+                }
+                _ => {
+                    self.not_const();
+                    break;
+                }
+            }
+        }
+
+        let return_ty = mir.return_ty.unwrap();
+        self.qualif = self.return_qualif.unwrap_or(Qualif::NOT_CONST);
+
+        match self.mode {
+            Mode::StaticMut => {
+                // Check for destructors in static mut.
+                self.add_type(return_ty);
+                self.deny_drop();
+            }
+            _ => {
+                // Account for errors in consts by using the
+                // conservative type qualification instead.
+                if self.qualif.intersects(Qualif::CONST_ERROR) {
+                    self.qualif = Qualif::empty();
+                    self.add_type(return_ty);
+                }
+            }
+        }
+        self.qualif
+    }
+}
+
+/// Accumulates an Rvalue or Call's effects in self.qualif.
+/// For functions (constant or not), it also records
+/// candidates for promotion in promotion_candidates.
+impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx> {
+    fn visit_lvalue(&mut self, lvalue: &Lvalue<'tcx>, context: LvalueContext) {
+        match *lvalue {
+            Lvalue::Arg(_) => {
+                self.add(Qualif::FN_ARGUMENT);
+            }
+            Lvalue::Var(_) => {
+                self.add(Qualif::NOT_CONST);
+            }
+            Lvalue::Temp(index) => {
+                if let Some(qualif) = self.temp_qualif[index as usize] {
+                    self.add(qualif);
+                } else {
+                    self.not_const();
+                }
+            }
+            Lvalue::Static(_) => {
+                self.add(Qualif::STATIC);
+                if self.mode == Mode::Const || self.mode == Mode::ConstFn {
+                    span_err!(self.tcx.sess, self.span, E0013,
+                              "{}s cannot refer to statics, use \
+                               a constant instead", self.mode);
+                }
+            }
+            Lvalue::ReturnPointer => {
+                self.not_const();
+            }
+            Lvalue::Projection(ref proj) => {
+                self.nest(|this| {
+                    this.super_lvalue(lvalue, context);
+                    match proj.elem {
+                        ProjectionElem::Deref => {
+                            if !this.try_consume() {
+                                return;
+                            }
+
+                            if this.qualif.intersects(Qualif::STATIC_REF) {
+                                this.qualif = this.qualif - Qualif::STATIC_REF;
+                                this.add(Qualif::STATIC);
+                            }
+
+                            let base_ty = this.mir.lvalue_ty(this.tcx, &proj.base)
+                                              .to_ty(this.tcx);
+                            if let ty::TyRawPtr(_) = base_ty.sty {
+                                this.add(Qualif::NOT_CONST);
+                                if this.mode != Mode::Fn {
+                                    span_err!(this.tcx.sess, this.span, E0396,
+                                              "raw pointers cannot be dereferenced in {}s",
+                                              this.mode);
+                                }
+                            }
+                        }
+
+                        ProjectionElem::Field(..) |
+                        ProjectionElem::Index(_) => {
+                            if this.mode != Mode::Fn &&
+                               this.qualif.intersects(Qualif::STATIC) {
+                                span_err!(this.tcx.sess, this.span, E0494,
+                                          "cannot refer to the interior of another \
+                                           static, use a constant instead");
+                            }
+                            let ty = this.mir.lvalue_ty(this.tcx, lvalue)
+                                         .to_ty(this.tcx);
+                            this.qualif.restrict(ty, &this.param_env);
+                        }
+
+                        ProjectionElem::ConstantIndex {..} |
+                        ProjectionElem::Downcast(..) => {
+                            this.not_const()
+                        }
+                    }
+                });
+            }
+        }
+    }
+
+    fn visit_operand(&mut self, operand: &Operand<'tcx>) {
+        match *operand {
+            Operand::Consume(_) => {
+                self.nest(|this| {
+                    this.super_operand(operand);
+                    this.try_consume();
+                });
+            }
+            Operand::Constant(ref constant) => {
+                // Only functions and methods can have these types.
+                if let ty::TyFnDef(..) = constant.ty.sty {
+                    return;
+                }
+
+                if let Literal::Item { def_id, substs } = constant.literal {
+                    // Don't peek inside generic (associated) constants.
+                    if !substs.types.is_empty() {
+                        self.add_type(constant.ty);
+                    } else {
+                        let qualif = qualify_const_item_cached(self.tcx,
+                                                               self.qualif_map,
+                                                               self.mir_map,
+                                                               def_id);
+                        self.add(qualif);
+                    }
+
+                    // FIXME(eddyb) check recursive constants here,
+                    // instead of rustc_passes::static_recursion.
+                    if self.qualif.intersects(Qualif::RECURSIVE) {
+                        span_bug!(constant.span,
+                                  "recursive constant wasn't caught earlier");
+                    }
+
+                    // Let `const fn` transitively have destructors,
+                    // but they do get stopped in `const` or `static`.
+                    if self.mode != Mode::ConstFn {
+                        self.deny_drop();
+                    }
+                }
+            }
+        }
+    }
+
+    fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>) {
+        // Recurse through operands and lvalues.
+        self.super_rvalue(rvalue);
+
+        match *rvalue {
+            Rvalue::Use(_) |
+            Rvalue::Repeat(..) |
+            Rvalue::UnaryOp(..) |
+            Rvalue::Cast(CastKind::ReifyFnPointer, _, _) |
+            Rvalue::Cast(CastKind::UnsafeFnPointer, _, _) |
+            Rvalue::Cast(CastKind::Unsize, _, _) => {}
+
+            Rvalue::Len(_) => {
+                // Static lvalues in consts would have errored already,
+                // don't treat length checks as reads from statics.
+                self.qualif = self.qualif - Qualif::STATIC;
+            }
+
+            Rvalue::Ref(_, kind, ref lvalue) => {
+                // Static lvalues in consts would have errored already,
+                // only keep track of references to them here.
+                if self.qualif.intersects(Qualif::STATIC) {
+                    self.qualif = self.qualif - Qualif::STATIC;
+                    self.add(Qualif::STATIC_REF);
+                }
+
+                let ty = self.mir.lvalue_ty(self.tcx, lvalue).to_ty(self.tcx);
+                if kind == BorrowKind::Mut {
+                    // In theory, any zero-sized value could be borrowed
+                    // mutably without consequences. However, only &mut []
+                    // is allowed right now, and only in functions.
+                    let allow = if let ty::TyArray(_, 0) = ty.sty {
+                        self.mode == Mode::Fn
+                    } else if self.mode == Mode::StaticMut {
+                        // Inside a `static mut`, &mut [...] is also allowed.
+                        match ty.sty {
+                            ty::TyArray(..) | ty::TySlice(_) => {
+                                // Mutating can expose drops, be conservative.
+                                self.add_type(ty);
+                                self.deny_drop();
+                                true
+                            }
+                            _ => false
+                        }
+                    } else {
+                        false
+                    };
+
+                    if !allow {
+                        self.add(Qualif::NOT_CONST);
+                        if self.mode != Mode::Fn {
+                            span_err!(self.tcx.sess, self.span, E0017,
+                                      "references in {}s may only refer \
+                                       to immutable values", self.mode);
+                        }
+                    }
+                } else {
+                    // Constants cannot be borrowed if they contain interior mutability as
+                    // it means that our "silent insertion of statics" could change
+                    // initializer values (very bad).
+                    if self.qualif.intersects(Qualif::MUTABLE_INTERIOR) {
+                        // Replace MUTABLE_INTERIOR with NOT_CONST to avoid
+                        // duplicate errors (from reborrowing, for example).
+                        self.qualif = self.qualif - Qualif::MUTABLE_INTERIOR;
+                        self.add(Qualif::NOT_CONST);
+                        if self.mode != Mode::Fn {
+                            span_err!(self.tcx.sess, self.span, E0492,
+                                      "cannot borrow a constant which contains \
+                                       interior mutability, create a static instead");
+                        }
+                    }
+                }
+
+                // We might have a candidate for promotion.
+                let candidate = Candidate::Ref(self.location);
+                if self.mode == Mode::Fn || self.mode == Mode::ConstFn {
+                    if !self.qualif.intersects(Qualif::UNPROMOTABLE) {
+                        self.promotion_candidates.push(candidate);
+                    }
+                }
+            }
+
+            Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => {
+                let operand_ty = self.mir.operand_ty(self.tcx, operand);
+                let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
+                let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
+                match (cast_in, cast_out) {
+                    (CastTy::Ptr(_), CastTy::Int(_)) |
+                    (CastTy::FnPtr, CastTy::Int(_)) => {
+                        self.add(Qualif::NOT_CONST);
+                        if self.mode != Mode::Fn {
+                            span_err!(self.tcx.sess, self.span, E0018,
+                                      "raw pointers cannot be cast to integers in {}s",
+                                      self.mode);
+                        }
+                    }
+                    _ => {}
+                }
+            }
+
+            Rvalue::BinaryOp(op, ref lhs, _) => {
+                if let ty::TyRawPtr(_) = self.mir.operand_ty(self.tcx, lhs).sty {
+                    assert!(op == BinOp::Eq || op == BinOp::Ne ||
+                            op == BinOp::Le || op == BinOp::Lt ||
+                            op == BinOp::Ge || op == BinOp::Gt);
+
+                    self.add(Qualif::NOT_CONST);
+                    if self.mode != Mode::Fn {
+                        span_err!(self.tcx.sess, self.span, E0395,
+                                  "raw pointers cannot be compared in {}s",
+                                  self.mode);
+                    }
+                }
+            }
+
+            Rvalue::Box(_) => {
+                self.add(Qualif::NOT_CONST);
+                if self.mode != Mode::Fn {
+                    span_err!(self.tcx.sess, self.span, E0010,
+                              "allocations are not allowed in {}s", self.mode);
+                }
+            }
+
+            Rvalue::Aggregate(ref kind, _) => {
+                if let AggregateKind::Adt(def, _, _) = *kind {
+                    if def.has_dtor() {
+                        self.add(Qualif::NEEDS_DROP);
+                        self.deny_drop();
+                    }
+
+                    if Some(def.did) == self.tcx.lang_items.unsafe_cell_type() {
+                        let ty = self.mir.rvalue_ty(self.tcx, rvalue).unwrap();
+                        self.add_type(ty);
+                        assert!(self.qualif.intersects(Qualif::MUTABLE_INTERIOR));
+                        // Even if the value inside may not need dropping,
+                        // mutating it would change that.
+                        if !self.qualif.intersects(Qualif::NOT_CONST) {
+                            self.deny_drop();
+                        }
+                    }
+                }
+            }
+
+            Rvalue::Slice {..} |
+            Rvalue::InlineAsm {..} => {
+                self.not_const();
+            }
+        }
+    }
+
+    fn visit_terminator_kind(&mut self, bb: BasicBlock, kind: &TerminatorKind<'tcx>) {
+        if let TerminatorKind::Call { ref func, ref args, ref destination, .. } = *kind {
+            self.visit_operand(func);
+
+            let fn_ty = self.mir.operand_ty(self.tcx, func);
+            let (is_shuffle, is_const_fn) = match fn_ty.sty {
+                ty::TyFnDef(def_id, _, f) => {
+                    (f.abi == Abi::PlatformIntrinsic &&
+                     self.tcx.item_name(def_id).as_str().starts_with("simd_shuffle"),
+                     is_const_fn(self.tcx, def_id))
+                }
+                _ => (false, false)
+            };
+
+            for (i, arg) in args.iter().enumerate() {
+                self.nest(|this| {
+                    this.visit_operand(arg);
+                    if is_shuffle && i == 2 && this.mode == Mode::Fn {
+                        let candidate = Candidate::ShuffleIndices(bb);
+                        if !this.qualif.intersects(Qualif::UNPROMOTABLE) {
+                            this.promotion_candidates.push(candidate);
+                        } else {
+                            span_err!(this.tcx.sess, this.span, E0526,
+                                      "shuffle indices are not constant");
+                        }
+                    }
+                });
+            }
+
+            // Const fn calls.
+            if is_const_fn {
+                // We are in a const or static initializer,
+                if self.mode != Mode::Fn &&
+
+                    // feature-gate is not enabled,
+                    !self.tcx.sess.features.borrow().const_fn &&
+
+                    // this doesn't come from a crate with the feature-gate enabled,
+                    self.def_id.is_local() &&
+
+                    // this doesn't come from a macro that has #[allow_internal_unstable]
+                    !self.tcx.sess.codemap().span_allows_unstable(self.span)
+                {
+                    let mut err = self.tcx.sess.struct_span_err(self.span,
+                        "const fns are an unstable feature");
+                    help!(&mut err,
+                          "in Nightly builds, add `#![feature(const_fn)]` \
+                           to the crate attributes to enable");
+                    err.emit();
+                }
+            } else {
+                self.qualif = Qualif::NOT_CONST;
+                if self.mode != Mode::Fn {
+                    // FIXME(#24111) Remove this check when const fn stabilizes
+                    let (msg, note) = if let UnstableFeatures::Disallow =
+                            self.tcx.sess.opts.unstable_features {
+                        (format!("calls in {}s are limited to \
+                                  struct and enum constructors",
+                                 self.mode),
+                         Some("a limited form of compile-time function \
+                               evaluation is available on a nightly \
+                               compiler via `const fn`"))
+                    } else {
+                        (format!("calls in {}s are limited \
+                                  to constant functions, \
+                                  struct and enum constructors",
+                                 self.mode),
+                         None)
+                    };
+                    let mut err = struct_span_err!(self.tcx.sess, self.span, E0015, "{}", msg);
+                    if let Some(note) = note {
+                        err.span_note(self.span, note);
+                    }
+                    err.emit();
+                }
+            }
+
+            if let Some((ref dest, _)) = *destination {
+                // Avoid propagating irrelevant callee/argument qualifications.
+                if self.qualif.intersects(Qualif::CONST_ERROR) {
+                    self.qualif = Qualif::NOT_CONST;
+                } else {
+                    // Be conservative about the returned value of a const fn.
+                    let tcx = self.tcx;
+                    let ty = self.mir.lvalue_ty(tcx, dest).to_ty(tcx);
+                    self.qualif = Qualif::empty();
+                    self.add_type(ty);
+
+                    // Let `const fn` transitively have destructors,
+                    // but they do get stopped in `const` or `static`.
+                    if self.mode != Mode::ConstFn {
+                        self.deny_drop();
+                    }
+                }
+                self.assign(dest);
+            }
+        } else {
+            // Qualify any operands inside other terminators.
+            self.super_terminator_kind(bb, kind);
+        }
+    }
+
+    fn visit_assign(&mut self, _: BasicBlock, dest: &Lvalue<'tcx>, rvalue: &Rvalue<'tcx>) {
+        self.visit_rvalue(rvalue);
+
+        // Check the allowed const fn argument forms.
+        if let (Mode::ConstFn, &Lvalue::Var(index)) = (self.mode, dest) {
+            if self.const_fn_arg_vars.insert(index as usize) {
+                // Direct use of an argument is permitted.
+                if let Rvalue::Use(Operand::Consume(Lvalue::Arg(_))) = *rvalue {
+                    return;
+                }
+
+                // Avoid a generic error for other uses of arguments.
+                if self.qualif.intersects(Qualif::FN_ARGUMENT) {
+                    let decl = &self.mir.var_decls[index as usize];
+                    span_err!(self.tcx.sess, decl.span, E0022,
+                              "arguments of constant functions can only \
+                               be immutable by-value bindings");
+                    return;
+                }
+            }
+        }
+
+        self.assign(dest);
+    }
+
+    fn visit_statement(&mut self, bb: BasicBlock, statement: &Statement<'tcx>) {
+        assert_eq!(self.location.block, bb);
+        self.span = statement.span;
+        self.nest(|this| this.super_statement(bb, statement));
+        self.location.statement_index += 1;
+    }
+
+    fn visit_terminator(&mut self, bb: BasicBlock, terminator: &Terminator<'tcx>) {
+        assert_eq!(self.location.block, bb);
+        self.span = terminator.span;
+        self.nest(|this| this.super_terminator(bb, terminator));
+    }
+
+    fn visit_basic_block_data(&mut self, bb: BasicBlock, data: &BasicBlockData<'tcx>) {
+        self.location.statement_index = 0;
+        self.location.block = bb;
+        self.super_basic_block_data(bb, data);
+    }
+}
+
+fn qualify_const_item_cached<'tcx>(tcx: &TyCtxt<'tcx>,
+                                   qualif_map: &mut DefIdMap<Qualif>,
+                                   mir_map: Option<&MirMap<'tcx>>,
+                                   def_id: DefId)
+                                   -> Qualif {
+    match qualif_map.entry(def_id) {
+        Entry::Occupied(entry) => return *entry.get(),
+        Entry::Vacant(entry) => {
+            // Guard against `const` recursion.
+            entry.insert(Qualif::RECURSIVE);
+        }
+    }
+
+    let extern_mir;
+    let param_env_and_mir = if def_id.is_local() {
+        let node_id = tcx.map.as_local_node_id(def_id).unwrap();
+        mir_map.and_then(|map| map.map.get(&node_id)).map(|mir| {
+            (ty::ParameterEnvironment::for_item(tcx, node_id), mir)
+        })
+    } else if let Some(mir) = tcx.sess.cstore.maybe_get_item_mir(tcx, def_id) {
+        // These should only be monomorphic constants.
+        extern_mir = mir;
+        Some((tcx.empty_parameter_environment(), &extern_mir))
+    } else {
+        None
+    };
+
+    let (param_env, mir) = param_env_and_mir.unwrap_or_else(|| {
+        bug!("missing constant MIR for {}", tcx.item_path_str(def_id))
+    });
+
+    let mut qualifier = Qualifier::new(param_env, qualif_map, mir_map,
+                                       def_id, mir, Mode::Const);
+    let qualif = qualifier.qualify_const();
+    qualifier.qualif_map.insert(def_id, qualif);
+    qualif
+}
+
+pub struct QualifyAndPromoteConstants;
+
+impl Pass for QualifyAndPromoteConstants {}
+
+impl<'tcx> MirMapPass<'tcx> for QualifyAndPromoteConstants {
+    fn run_pass(&mut self, tcx: &TyCtxt<'tcx>, map: &mut MirMap<'tcx>) {
+        let mut qualif_map = DefIdMap();
+
+        // First, visit `const` items, potentially recursing, to get
+        // accurate MUTABLE_INTERIOR and NEEDS_DROP qualifications.
+        for &id in map.map.keys() {
+            let def_id = tcx.map.local_def_id(id);
+            let _task = tcx.dep_graph.in_task(self.dep_node(def_id));
+            let src = MirSource::from_node(tcx, id);
+            if let MirSource::Const(_) = src {
+                qualify_const_item_cached(tcx, &mut qualif_map, Some(map), def_id);
+            }
+        }
+
+        // Then, handle everything else, without recursing,
+        // as the MIR map is not shared, since promotion
+        // in functions (including `const fn`) mutates it.
+        for (&id, mir) in &mut map.map {
+            let def_id = tcx.map.local_def_id(id);
+            let _task = tcx.dep_graph.in_task(self.dep_node(def_id));
+            let src = MirSource::from_node(tcx, id);
+            let mode = match src {
+                MirSource::Fn(_) => {
+                    if is_const_fn(tcx, def_id) {
+                        Mode::ConstFn
+                    } else {
+                        Mode::Fn
+                    }
+                }
+                MirSource::Const(_) => continue,
+                MirSource::Static(_, hir::MutImmutable) => Mode::Static,
+                MirSource::Static(_, hir::MutMutable) => Mode::StaticMut,
+                MirSource::Promoted(..) => bug!()
+            };
+            let param_env = ty::ParameterEnvironment::for_item(tcx, id);
+
+            if mode == Mode::Fn || mode == Mode::ConstFn {
+                // This is ugly because Qualifier holds onto mir,
+                // which can't be mutated until its scope ends.
+                let (temps, candidates) = {
+                    let mut qualifier = Qualifier::new(param_env, &mut qualif_map,
+                                                       None, def_id, mir, mode);
+                    if mode == Mode::ConstFn {
+                        // Enforce a constant-like CFG for `const fn`.
+                        qualifier.qualify_const();
+                    } else {
+                        while let Some((bb, data)) = qualifier.rpo.next() {
+                            qualifier.visit_basic_block_data(bb, data);
+                        }
+                    }
+
+                    (qualifier.temp_promotion_state,
+                     qualifier.promotion_candidates)
+                };
+
+                // Do the actual promotion, now that we know what's viable.
+                promote_consts::promote_candidates(mir, tcx, temps, candidates);
+            } else {
+                let mut qualifier = Qualifier::new(param_env, &mut qualif_map,
+                                                   None, def_id, mir, mode);
+                qualifier.qualify_const();
+            }
+
+            // Statics must be Sync.
+            if mode == Mode::Static {
+                let ty = mir.return_ty.unwrap();
+                let infcx = infer::new_infer_ctxt(tcx,
+                                                  &tcx.tables,
+                                                  None,
+                                                  ProjectionMode::AnyFinal);
+                let cause = traits::ObligationCause::new(mir.span, id, traits::SharedStatic);
+                let mut fulfillment_cx = traits::FulfillmentContext::new();
+                fulfillment_cx.register_builtin_bound(&infcx, ty, ty::BoundSync, cause);
+                if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) {
+                    traits::report_fulfillment_errors(&infcx, &err);
+                }
+
+                if let Err(ref errors) = fulfillment_cx.select_rfc1592_obligations(&infcx) {
+                   traits::report_fulfillment_errors_as_warnings(&infcx, errors, id);
+                }
+            }
+        }
+    }
+}
diff --git a/src/librustc_mir/transform/remove_dead_blocks.rs b/src/librustc_mir/transform/remove_dead_blocks.rs
index 2099e9a435a0f..e0d05a17d4376 100644
--- a/src/librustc_mir/transform/remove_dead_blocks.rs
+++ b/src/librustc_mir/transform/remove_dead_blocks.rs
@@ -35,13 +35,12 @@
 use rustc_data_structures::bitvec::BitVector;
 use rustc::ty::TyCtxt;
 use rustc::mir::repr::*;
-use rustc::mir::transform::{Pass, MirPass};
-use syntax::ast::NodeId;
+use rustc::mir::transform::{Pass, MirPass, MirSource};
 
 pub struct RemoveDeadBlocks;
 
 impl<'tcx> MirPass<'tcx> for RemoveDeadBlocks {
-    fn run_pass(&mut self, _: &TyCtxt<'tcx>, _: NodeId, mir: &mut Mir<'tcx>) {
+    fn run_pass(&mut self, _: &TyCtxt<'tcx>, _: MirSource, mir: &mut Mir<'tcx>) {
         let mut seen = BitVector::new(mir.basic_blocks.len());
         // This block is always required.
         seen.insert(START_BLOCK.index());
diff --git a/src/librustc_mir/transform/simplify_cfg.rs b/src/librustc_mir/transform/simplify_cfg.rs
index 00b8f5c093043..a137a812867fa 100644
--- a/src/librustc_mir/transform/simplify_cfg.rs
+++ b/src/librustc_mir/transform/simplify_cfg.rs
@@ -11,9 +11,8 @@
 use rustc::middle::const_val::ConstVal;
 use rustc::ty::TyCtxt;
 use rustc::mir::repr::*;
-use rustc::mir::transform::{MirPass, Pass};
+use rustc::mir::transform::{MirPass, MirSource, Pass};
 use pretty;
-use syntax::ast::NodeId;
 
 use super::remove_dead_blocks::RemoveDeadBlocks;
 
@@ -112,15 +111,15 @@ impl SimplifyCfg {
 }
 
 impl<'tcx> MirPass<'tcx> for SimplifyCfg {
-    fn run_pass(&mut self, tcx: &TyCtxt<'tcx>, id: NodeId, mir: &mut Mir<'tcx>) {
+    fn run_pass(&mut self, tcx: &TyCtxt<'tcx>, src: MirSource, mir: &mut Mir<'tcx>) {
         let mut counter = 0;
         let mut changed = true;
         while changed {
-            pretty::dump_mir(tcx, "simplify_cfg", &counter, id, mir, None);
+            pretty::dump_mir(tcx, "simplify_cfg", &counter, src, mir, None);
             counter += 1;
             changed = self.simplify_branches(mir);
             changed |= self.remove_goto_chains(mir);
-            RemoveDeadBlocks.run_pass(tcx, id, mir);
+            RemoveDeadBlocks.run_pass(tcx, src, mir);
         }
         // FIXME: Should probably be moved into some kind of pass manager
         mir.basic_blocks.shrink_to_fit();
diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs
index 11ac1fa8f82a1..18ebadf42d688 100644
--- a/src/librustc_mir/transform/type_check.rs
+++ b/src/librustc_mir/transform/type_check.rs
@@ -12,16 +12,16 @@
 #![allow(unreachable_code)]
 
 use rustc::dep_graph::DepNode;
+use rustc::hir::def_id::DefId;
 use rustc::infer::{self, InferCtxt, InferOk};
 use rustc::traits::{self, ProjectionMode};
 use rustc::ty::fold::TypeFoldable;
 use rustc::ty::{self, Ty, TyCtxt};
 use rustc::mir::repr::*;
 use rustc::mir::tcx::LvalueTy;
-use rustc::mir::transform::{MirPass, Pass};
+use rustc::mir::transform::{MirPass, MirSource, Pass};
 use rustc::mir::visit::{self, Visitor};
 use std::fmt;
-use syntax::ast::NodeId;
 use syntax::codemap::{Span, DUMMY_SP};
 
 macro_rules! span_mirbug {
@@ -578,15 +578,13 @@ impl TypeckMir {
 }
 
 impl<'tcx> MirPass<'tcx> for TypeckMir {
-    fn run_pass(&mut self, tcx: &TyCtxt<'tcx>, id: NodeId, mir: &mut Mir<'tcx>) {
+    fn run_pass(&mut self, tcx: &TyCtxt<'tcx>, src: MirSource, mir: &mut Mir<'tcx>) {
         if tcx.sess.err_count() > 0 {
             // compiling a broken program can obviously result in a
             // broken MIR, so try not to report duplicate errors.
             return;
         }
-        let def_id = tcx.map.local_def_id(id);
-        let _task = tcx.dep_graph.in_task(DepNode::MirTypeck(def_id));
-        let param_env = ty::ParameterEnvironment::for_item(tcx, id);
+        let param_env = ty::ParameterEnvironment::for_item(tcx, src.item_id());
         let infcx = infer::new_infer_ctxt(tcx,
                                           &tcx.tables,
                                           Some(param_env),
@@ -605,4 +603,8 @@ impl<'tcx> MirPass<'tcx> for TypeckMir {
     }
 }
 
-impl Pass for TypeckMir {}
+impl Pass for TypeckMir {
+    fn dep_node(&self, def_id: DefId) -> DepNode<DefId> {
+        DepNode::MirTypeck(def_id)
+    }
+}
diff --git a/src/librustc_passes/const_fn.rs b/src/librustc_passes/const_fn.rs
deleted file mode 100644
index 97a4c14863d59..0000000000000
--- a/src/librustc_passes/const_fn.rs
+++ /dev/null
@@ -1,118 +0,0 @@
-// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-//! Verifies that const fn arguments are immutable by value bindings
-//! and the const fn body doesn't contain any statements
-
-use rustc::session::{Session, CompileResult};
-
-use syntax::ast::{self, PatKind};
-use syntax::visit::{self, Visitor, FnKind};
-use syntax::codemap::Span;
-
-pub fn check_crate(sess: &Session, krate: &ast::Crate) -> CompileResult {
-    sess.track_errors(|| {
-        visit::walk_crate(&mut CheckConstFn{ sess: sess }, krate);
-    })
-}
-
-struct CheckConstFn<'a> {
-    sess: &'a Session,
-}
-
-struct CheckBlock<'a> {
-    sess: &'a Session,
-    kind: &'static str,
-}
-
-impl<'a, 'v> Visitor<'v> for CheckBlock<'a> {
-    fn visit_block(&mut self, block: &'v ast::Block) {
-        check_block(&self.sess, block, self.kind);
-        CheckConstFn{ sess: self.sess}.visit_block(block);
-    }
-    fn visit_expr(&mut self, e: &'v ast::Expr) {
-        if let ast::ExprKind::Closure(..) = e.node {
-            CheckConstFn{ sess: self.sess}.visit_expr(e);
-        } else {
-            visit::walk_expr(self, e);
-        }
-    }
-    fn visit_item(&mut self, _i: &'v ast::Item) { bug!("should be handled in CheckConstFn") }
-    fn visit_fn(&mut self,
-                _fk: FnKind<'v>,
-                _fd: &'v ast::FnDecl,
-                _b: &'v ast::Block,
-                _s: Span,
-                _fn_id: ast::NodeId) { bug!("should be handled in CheckConstFn") }
-}
-
-fn check_block(sess: &Session, b: &ast::Block, kind: &'static str) {
-    // Check all statements in the block
-    for stmt in &b.stmts {
-        let span = match stmt.node {
-            ast::StmtKind::Decl(ref decl, _) => {
-                match decl.node {
-                    ast::DeclKind::Local(_) => decl.span,
-
-                    // Item statements are allowed
-                    ast::DeclKind::Item(_) => continue,
-                }
-            }
-            ast::StmtKind::Expr(ref expr, _) => expr.span,
-            ast::StmtKind::Semi(ref semi, _) => semi.span,
-            ast::StmtKind::Mac(..) => bug!(),
-        };
-        span_err!(sess, span, E0016,
-                  "blocks in {}s are limited to items and tail expressions", kind);
-    }
-}
-
-impl<'a, 'v> Visitor<'v> for CheckConstFn<'a> {
-    fn visit_item(&mut self, i: &'v ast::Item) {
-        visit::walk_item(self, i);
-        match i.node {
-            ast::ItemKind::Const(_, ref e) => {
-                CheckBlock{ sess: self.sess, kind: "constant"}.visit_expr(e)
-            },
-            ast::ItemKind::Static(_, _, ref e) => {
-                CheckBlock{ sess: self.sess, kind: "static"}.visit_expr(e)
-            },
-            _ => {},
-        }
-    }
-
-    fn visit_fn(&mut self,
-                fk: FnKind<'v>,
-                fd: &'v ast::FnDecl,
-                b: &'v ast::Block,
-                s: Span,
-                _fn_id: ast::NodeId) {
-        visit::walk_fn(self, fk, fd, b, s);
-        match fk {
-            FnKind::ItemFn(_, _, _, ast::Constness::Const, _, _) => {},
-            FnKind::Method(_, m, _) if m.constness == ast::Constness::Const => {},
-            _ => return,
-        }
-
-        // Ensure the arguments are simple, not mutable/by-ref or patterns.
-        for arg in &fd.inputs {
-            match arg.pat.node {
-                PatKind::Wild => {}
-                PatKind::Ident(ast::BindingMode::ByValue(ast::Mutability::Immutable), _, None) => {}
-                _ => {
-                    span_err!(self.sess, arg.pat.span, E0022,
-                              "arguments of constant functions can only \
-                               be immutable by-value bindings");
-                }
-            }
-        }
-        check_block(&self.sess, b, "const function");
-    }
-}
diff --git a/src/librustc_passes/consts.rs b/src/librustc_passes/consts.rs
index 6fb9739fca4f4..c417ec79ff2a8 100644
--- a/src/librustc_passes/consts.rs
+++ b/src/librustc_passes/consts.rs
@@ -40,7 +40,7 @@ use rustc::infer;
 use rustc::middle::mem_categorization as mc;
 use rustc::middle::mem_categorization::Categorization;
 use rustc::ty::{self, Ty, TyCtxt};
-use rustc::traits::{self, ProjectionMode};
+use rustc::traits::ProjectionMode;
 use rustc::util::nodemap::NodeMap;
 use rustc::middle::const_qualif::ConstQualif;
 use rustc::lint::builtin::CONST_ERR;
@@ -48,7 +48,6 @@ use rustc::lint::builtin::CONST_ERR;
 use rustc::hir::{self, PatKind};
 use syntax::ast;
 use syntax::codemap::Span;
-use syntax::feature_gate::UnstableFeatures;
 use rustc::hir::intravisit::{self, FnKind, Visitor};
 
 use std::collections::hash_map::Entry;
@@ -180,31 +179,11 @@ impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> {
 
     /// Returns true if the call is to a const fn or method.
     fn handle_const_fn_call(&mut self,
-                            expr: &hir::Expr,
+                            _expr: &hir::Expr,
                             def_id: DefId,
                             ret_ty: Ty<'tcx>)
                             -> bool {
         if let Some(fn_like) = lookup_const_fn_by_id(self.tcx, def_id) {
-            if
-                // we are in a static/const initializer
-                self.mode != Mode::Var &&
-
-                // feature-gate is not enabled
-                !self.tcx.sess.features.borrow().const_fn &&
-
-                // this doesn't come from a macro that has #[allow_internal_unstable]
-                !self.tcx.sess.codemap().span_allows_unstable(expr.span)
-            {
-                let mut err = self.tcx.sess.struct_span_err(
-                    expr.span,
-                    "const fns are an unstable feature");
-                help!(
-                    &mut err,
-                    "in Nightly builds, add `#![feature(const_fn)]` to the crate \
-                     attributes to enable");
-                err.emit();
-            }
-
             let qualif = self.fn_like(fn_like.kind(),
                                       fn_like.decl(),
                                       fn_like.body(),
@@ -245,42 +224,6 @@ impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> {
             Mode::Var => bug!(),
         }
     }
-
-    fn check_static_mut_type(&self, e: &hir::Expr) {
-        let node_ty = self.tcx.node_id_to_type(e.id);
-        let tcontents = node_ty.type_contents(self.tcx);
-
-        let suffix = if tcontents.has_dtor() {
-            "destructors"
-        } else if tcontents.owns_owned() {
-            "boxes"
-        } else {
-            return
-        };
-
-        span_err!(self.tcx.sess, e.span, E0397,
-                 "mutable statics are not allowed to have {}", suffix);
-    }
-
-    fn check_static_type(&self, e: &hir::Expr) {
-        let ty = self.tcx.node_id_to_type(e.id);
-        let infcx = infer::new_infer_ctxt(self.tcx,
-                                          &self.tcx.tables,
-                                          None,
-                                          ProjectionMode::AnyFinal);
-        let cause = traits::ObligationCause::new(e.span, e.id, traits::SharedStatic);
-        let mut fulfillment_cx = traits::FulfillmentContext::new();
-        fulfillment_cx.register_builtin_bound(&infcx, ty, ty::BoundSync, cause);
-        match fulfillment_cx.select_all_or_error(&infcx) {
-            Ok(()) => { },
-            Err(ref errors) => {
-                traits::report_fulfillment_errors(&infcx, errors);
-            }
-        }
-        if let Err(ref errors) = fulfillment_cx.select_rfc1592_obligations(&infcx) {
-            traits::report_fulfillment_errors_as_warnings(&infcx, errors, e.id);
-        }
-    }
 }
 
 impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
@@ -289,11 +232,9 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
         assert_eq!(self.mode, Mode::Var);
         match i.node {
             hir::ItemStatic(_, hir::MutImmutable, ref expr) => {
-                self.check_static_type(&expr);
                 self.global_expr(Mode::Static, &expr);
             }
             hir::ItemStatic(_, hir::MutMutable, ref expr) => {
-                self.check_static_mut_type(&expr);
                 self.global_expr(Mode::StaticMut, &expr);
             }
             hir::ItemConst(_, ref expr) => {
@@ -360,8 +301,9 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
                             "lower range bound must be less than or equal to upper");
                     }
                     None => {
-                        self.tcx.sess.delay_span_bug(start.span,
-                                                     "non-constant path in constant expr");
+                        span_err!(self.tcx.sess, p.span, E0014,
+                                  "paths in {}s may only refer to constants",
+                                  self.msg());
                     }
                 }
             }
@@ -384,8 +326,6 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
                 hir::StmtSemi(_, _) => {},
             }
             self.add_qualif(ConstQualif::NOT_CONST);
-            // anything else should have been caught by check_const_fn
-            assert_eq!(self.mode, Mode::Var);
         }
         intravisit::walk_block(self, block);
     }
@@ -455,11 +395,6 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
                 let tc = node_ty.type_contents(self.tcx);
                 if self.qualif.intersects(ConstQualif::MUTABLE_MEM) && tc.interior_unsafe() {
                     outer = outer | ConstQualif::NOT_CONST;
-                    if self.mode != Mode::Var {
-                        span_err!(self.tcx.sess, ex.span, E0492,
-                                  "cannot borrow a constant which contains \
-                                   interior mutability, create a static instead");
-                    }
                 }
                 // If the reference has to be 'static, avoid in-place initialization
                 // as that will end up pointing to the stack instead.
@@ -474,10 +409,6 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
                     if self.mode == Mode::Var {
                         outer = outer | ConstQualif::NOT_CONST;
                         self.add_qualif(ConstQualif::MUTABLE_MEM);
-                    } else {
-                        span_err!(self.tcx.sess, ex.span, E0017,
-                            "references in {}s may only refer \
-                             to immutable values", self.msg())
                     }
                 }
                 if !self.qualif.intersects(ConstQualif::NON_STATIC_BORROWS) {
@@ -525,11 +456,6 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
         ty::TyStruct(def, _) |
         ty::TyEnum(def, _) if def.has_dtor() => {
             v.add_qualif(ConstQualif::NEEDS_DROP);
-            if v.mode != Mode::Var {
-                span_err!(v.tcx.sess, e.span, E0493,
-                          "{}s are not allowed to have destructors",
-                          v.msg());
-            }
         }
         _ => {}
     }
@@ -540,17 +466,9 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
         hir::ExprBinary(..) |
         hir::ExprIndex(..) if v.tcx.tables.borrow().method_map.contains_key(&method_call) => {
             v.add_qualif(ConstQualif::NOT_CONST);
-            if v.mode != Mode::Var {
-                span_err!(v.tcx.sess, e.span, E0011,
-                            "user-defined operators are not allowed in {}s", v.msg());
-            }
         }
         hir::ExprBox(_) => {
             v.add_qualif(ConstQualif::NOT_CONST);
-            if v.mode != Mode::Var {
-                span_err!(v.tcx.sess, e.span, E0010,
-                          "allocations are not allowed in {}s", v.msg());
-            }
         }
         hir::ExprUnary(op, ref inner) => {
             match v.tcx.node_id_to_type(inner.id).sty {
@@ -558,10 +476,6 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
                     assert!(op == hir::UnDeref);
 
                     v.add_qualif(ConstQualif::NOT_CONST);
-                    if v.mode != Mode::Var {
-                        span_err!(v.tcx.sess, e.span, E0396,
-                                  "raw pointers cannot be dereferenced in {}s", v.msg());
-                    }
                 }
                 _ => {}
             }
@@ -574,10 +488,6 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
                             op.node == hir::BiGe || op.node == hir::BiGt);
 
                     v.add_qualif(ConstQualif::NOT_CONST);
-                    if v.mode != Mode::Var {
-                        span_err!(v.tcx.sess, e.span, E0395,
-                                  "raw pointers cannot be compared in {}s", v.msg());
-                    }
                 }
                 _ => {}
             }
@@ -588,10 +498,6 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
                 None => span_bug!(e.span, "no kind for cast"),
                 Some(&CastKind::PtrAddrCast) | Some(&CastKind::FnPtrAddrCast) => {
                     v.add_qualif(ConstQualif::NOT_CONST);
-                    if v.mode != Mode::Var {
-                        span_err!(v.tcx.sess, e.span, E0018,
-                                  "raw pointers cannot be cast to integers in {}s", v.msg());
-                    }
                 }
                 _ => {}
             }
@@ -616,11 +522,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
                 Some(Def::Static(..)) => {
                     match v.mode {
                         Mode::Static | Mode::StaticMut => {}
-                        Mode::Const | Mode::ConstFn => {
-                            span_err!(v.tcx.sess, e.span, E0013,
-                                "{}s cannot refer to other statics, insert \
-                                 an intermediate constant instead", v.msg());
-                        }
+                        Mode::Const | Mode::ConstFn => {}
                         Mode::Var => v.add_qualif(ConstQualif::NOT_CONST)
                     }
                 }
@@ -636,14 +538,8 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
                     // Sadly, we can't determine whether the types are zero-sized.
                     v.add_qualif(ConstQualif::NOT_CONST | ConstQualif::NON_ZERO_SIZED);
                 }
-                def => {
+                _ => {
                     v.add_qualif(ConstQualif::NOT_CONST);
-                    if v.mode != Mode::Var {
-                        debug!("(checking const) found bad def: {:?}", def);
-                        span_err!(v.tcx.sess, e.span, E0014,
-                                  "paths in {}s may only refer to constants \
-                                   or functions", v.msg());
-                    }
                 }
             }
         }
@@ -681,29 +577,6 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
             };
             if !is_const {
                 v.add_qualif(ConstQualif::NOT_CONST);
-                if v.mode != Mode::Var {
-                    // FIXME(#24111) Remove this check when const fn stabilizes
-                    let (msg, note) =
-                        if let UnstableFeatures::Disallow = v.tcx.sess.opts.unstable_features {
-                        (format!("function calls in {}s are limited to \
-                                  struct and enum constructors",
-                                 v.msg()),
-                         Some("a limited form of compile-time function \
-                               evaluation is available on a nightly \
-                               compiler via `const fn`"))
-                    } else {
-                        (format!("function calls in {}s are limited \
-                                  to constant functions, \
-                                  struct and enum constructors",
-                                 v.msg()),
-                         None)
-                    };
-                    let mut err = struct_span_err!(v.tcx.sess, e.span, E0015, "{}", msg);
-                    if let Some(note) = note {
-                        err.span_note(e.span, note);
-                    }
-                    err.emit();
-                }
             }
         }
         hir::ExprMethodCall(..) => {
@@ -714,11 +587,6 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
             };
             if !is_const {
                 v.add_qualif(ConstQualif::NOT_CONST);
-                if v.mode != Mode::Var {
-                    span_err!(v.tcx.sess, e.span, E0378,
-                              "method calls in {}s are limited to \
-                               constant inherent methods", v.msg());
-                }
             }
         }
         hir::ExprStruct(..) => {
@@ -773,10 +641,6 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
         hir::ExprAssignOp(..) |
         hir::ExprInlineAsm(..) => {
             v.add_qualif(ConstQualif::NOT_CONST);
-            if v.mode != Mode::Var {
-                span_err!(v.tcx.sess, e.span, E0019,
-                          "{} contains unimplemented expression type", v.msg());
-            }
         }
     }
 }
@@ -796,11 +660,6 @@ fn check_adjustments<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Exp
                     v.tcx.is_overloaded_autoderef(e.id, autoderef)
             }) {
                 v.add_qualif(ConstQualif::NOT_CONST);
-                if v.mode != Mode::Var {
-                    span_err!(v.tcx.sess, e.span, E0400,
-                              "user-defined dereference operators are not allowed in {}s",
-                              v.msg());
-                }
             }
         }
     }
@@ -819,21 +678,13 @@ pub fn check_crate(tcx: &TyCtxt) {
 impl<'a, 'tcx> euv::Delegate<'tcx> for CheckCrateVisitor<'a, 'tcx> {
     fn consume(&mut self,
                _consume_id: ast::NodeId,
-               consume_span: Span,
+               _consume_span: Span,
                cmt: mc::cmt,
                _mode: euv::ConsumeMode) {
         let mut cur = &cmt;
         loop {
             match cur.cat {
                 Categorization::StaticItem => {
-                    if self.mode != Mode::Var {
-                        // statics cannot be consumed by value at any time, that would imply
-                        // that they're an initializer (what a const is for) or kept in sync
-                        // over time (not feasible), so deny it outright.
-                        span_err!(self.tcx.sess, consume_span, E0394,
-                                  "cannot refer to other statics by value, use the \
-                                   address-of operator or a constant instead");
-                    }
                     break;
                 }
                 Categorization::Deref(ref cmt, _, _) |
@@ -848,7 +699,7 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for CheckCrateVisitor<'a, 'tcx> {
     }
     fn borrow(&mut self,
               borrow_id: ast::NodeId,
-              borrow_span: Span,
+              _borrow_span: Span,
               cmt: mc::cmt<'tcx>,
               _loan_region: ty::Region,
               bk: ty::BorrowKind,
@@ -866,7 +717,6 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for CheckCrateVisitor<'a, 'tcx> {
         }
 
         let mut cur = &cmt;
-        let mut is_interior = false;
         loop {
             match cur.cat {
                 Categorization::Rvalue(..) => {
@@ -891,20 +741,11 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for CheckCrateVisitor<'a, 'tcx> {
                     break;
                 }
                 Categorization::StaticItem => {
-                    if is_interior && self.mode != Mode::Var {
-                        // Borrowed statics can specifically *only* have their address taken,
-                        // not any number of other borrows such as borrowing fields, reading
-                        // elements of an array, etc.
-                        span_err!(self.tcx.sess, borrow_span, E0494,
-                                  "cannot refer to the interior of another \
-                                   static, use a constant instead");
-                    }
                     break;
                 }
                 Categorization::Deref(ref cmt, _, _) |
                 Categorization::Downcast(ref cmt, _) |
                 Categorization::Interior(ref cmt, _) => {
-                    is_interior = true;
                     cur = cmt;
                 }
 
diff --git a/src/librustc_passes/diagnostics.rs b/src/librustc_passes/diagnostics.rs
index c89e9bb19595e..77f896e011b93 100644
--- a/src/librustc_passes/diagnostics.rs
+++ b/src/librustc_passes/diagnostics.rs
@@ -12,70 +12,6 @@
 
 register_long_diagnostics! {
 
-E0010: r##"
-The value of statics and constants must be known at compile time, and they live
-for the entire lifetime of a program. Creating a boxed value allocates memory on
-the heap at runtime, and therefore cannot be done at compile time. Erroneous
-code example:
-
-```compile_fail
-#![feature(box_syntax)]
-
-const CON : Box<i32> = box 0;
-```
-"##,
-
-E0011: r##"
-Initializers for constants and statics are evaluated at compile time.
-User-defined operators rely on user-defined functions, which cannot be evaluated
-at compile time.
-
-Erroneous code example:
-
-```compile_fail
-use std::ops::Index;
-
-struct Foo { a: u8 }
-
-impl Index<u8> for Foo {
-    type Output = u8;
-
-    fn index<'a>(&'a self, idx: u8) -> &'a u8 { &self.a }
-}
-
-const a: Foo = Foo { a: 0u8 };
-const b: u8 = a[0]; // Index trait is defined by the user, bad!
-```
-
-Only operators on builtin types are allowed.
-
-Example:
-
-```
-const a: &'static [i32] = &[1, 2, 3];
-const b: i32 = a[0]; // Ok!
-```
-"##,
-
-E0013: r##"
-Static and const variables can refer to other const variables. But a const
-variable cannot refer to a static variable. For example, `Y` cannot refer to
-`X` here:
-
-```compile_fail
-static X: i32 = 42;
-const Y: i32 = X;
-```
-
-To fix this, the value can be extracted as a const and then used:
-
-```
-const A: i32 = 42;
-static X: i32 = A;
-const Y: i32 = A;
-```
-"##,
-
 E0014: r##"
 Constants can only be initialized by a constant value or, in a future
 version of Rust, a call to a const function. This error indicates the use
@@ -95,149 +31,6 @@ const FOO2: i32 = { 0 }; // but brackets are useless here
 ```
 "##,
 
-// FIXME(#24111) Change the language here when const fn stabilizes
-E0015: r##"
-The only functions that can be called in static or constant expressions are
-`const` functions, and struct/enum constructors. `const` functions are only
-available on a nightly compiler. Rust currently does not support more general
-compile-time function execution.
-
-```
-const FOO: Option<u8> = Some(1); // enum constructor
-struct Bar {x: u8}
-const BAR: Bar = Bar {x: 1}; // struct constructor
-```
-
-See [RFC 911] for more details on the design of `const fn`s.
-
-[RFC 911]: https://github.com/rust-lang/rfcs/blob/master/text/0911-const-fn.md
-"##,
-
-E0016: r##"
-Blocks in constants may only contain items (such as constant, function
-definition, etc...) and a tail expression. Erroneous code example:
-
-```compile_fail
-const FOO: i32 = { let x = 0; x }; // 'x' isn't an item!
-```
-
-To avoid it, you have to replace the non-item object:
-
-```
-const FOO: i32 = { const X : i32 = 0; X };
-```
-"##,
-
-E0017: r##"
-References in statics and constants may only refer to immutable values.
-Erroneous code example:
-
-```compile_fail
-static X: i32 = 1;
-const C: i32 = 2;
-
-// these three are not allowed:
-const CR: &'static mut i32 = &mut C;
-static STATIC_REF: &'static mut i32 = &mut X;
-static CONST_REF: &'static mut i32 = &mut C;
-```
-
-Statics are shared everywhere, and if they refer to mutable data one might
-violate memory safety since holding multiple mutable references to shared data
-is not allowed.
-
-If you really want global mutable state, try using `static mut` or a global
-`UnsafeCell`.
-"##,
-
-E0018: r##"
-
-The value of static and constant integers must be known at compile time. You
-can't cast a pointer to an integer because the address of a pointer can
-vary.
-
-For example, if you write:
-
-```compile_fail
-static MY_STATIC: u32 = 42;
-static MY_STATIC_ADDR: usize = &MY_STATIC as *const _ as usize;
-static WHAT: usize = (MY_STATIC_ADDR^17) + MY_STATIC_ADDR;
-```
-
-Then `MY_STATIC_ADDR` would contain the address of `MY_STATIC`. However,
-the address can change when the program is linked, as well as change
-between different executions due to ASLR, and many linkers would
-not be able to calculate the value of `WHAT`.
-
-On the other hand, static and constant pointers can point either to
-a known numeric address or to the address of a symbol.
-
-```
-static MY_STATIC_ADDR: &'static u32 = &MY_STATIC;
-// ... and also
-static MY_STATIC_ADDR2: *const u32 = &MY_STATIC;
-
-const CONST_ADDR: *const u8 = 0x5f3759df as *const u8;
-```
-
-This does not pose a problem by itself because they can't be
-accessed directly.
-"##,
-
-E0019: r##"
-A function call isn't allowed in the const's initialization expression
-because the expression's value must be known at compile-time. Erroneous code
-example:
-
-```compile_fail
-enum Test {
-    V1
-}
-
-impl Test {
-    fn test(&self) -> i32 {
-        12
-    }
-}
-
-fn main() {
-    const FOO: Test = Test::V1;
-
-    const A: i32 = FOO.test(); // You can't call Test::func() here !
-}
-```
-
-Remember: you can't use a function call inside a const's initialization
-expression! However, you can totally use it anywhere else:
-
-```
-fn main() {
-    const FOO: Test = Test::V1;
-
-    FOO.func(); // here is good
-    let x = FOO.func(); // or even here!
-}
-```
-"##,
-
-E0022: r##"
-Constant functions are not allowed to mutate anything. Thus, binding to an
-argument with a mutable pattern is not allowed. For example,
-
-```compile_fail
-const fn foo(mut x: u8) {
-    // do stuff
-}
-```
-
-Is incorrect because the function body may not mutate `x`.
-
-Remove any mutable bindings from the argument list to fix this error. In case
-you need to mutate the argument, try lazily initializing a global variable
-instead of using a `const fn`, or refactoring the code to a functional style to
-avoid mutation if possible.
-"##,
-
 E0030: r##"
 When matching against a range, the compiler verifies that the range is
 non-empty.  Range patterns include both end-points, so this is equivalent to
@@ -325,281 +118,6 @@ fn some_func() {
 ```
 "##,
 
-E0378: r##"
-Method calls that aren't calls to inherent `const` methods are disallowed
-in statics, constants, and constant functions.
-
-For example:
-
-```compile_fail
-const BAZ: i32 = Foo(25).bar(); // error, `bar` isn't `const`
-
-struct Foo(i32);
-
-impl Foo {
-    const fn foo(&self) -> i32 {
-        self.bar() // error, `bar` isn't `const`
-    }
-
-    fn bar(&self) -> i32 { self.0 }
-}
-```
-
-For more information about `const fn`'s, see [RFC 911].
-
-[RFC 911]: https://github.com/rust-lang/rfcs/blob/master/text/0911-const-fn.md
-"##,
-
-E0394: r##"
-From [RFC 246]:
-
- > It is invalid for a static to reference another static by value. It is
- > required that all references be borrowed.
-
-[RFC 246]: https://github.com/rust-lang/rfcs/pull/246
-"##,
-
-
-E0395: r##"
-The value assigned to a constant scalar must be known at compile time,
-which is not the case when comparing raw pointers.
-
-Erroneous code example:
-
-```compile_fail
-static FOO: i32 = 42;
-static BAR: i32 = 42;
-
-static BAZ: bool = { (&FOO as *const i32) == (&BAR as *const i32) };
-// error: raw pointers cannot be compared in statics!
-```
-
-The address assigned by the linker to `FOO` and `BAR` may or may not
-be identical, so the value of `BAZ` can't be determined.
-
-If you want to do the comparison, please do it at run-time.
-
-For example:
-
-```
-static FOO: i32 = 42;
-static BAR: i32 = 42;
-
-let baz: bool = { (&FOO as *const i32) == (&BAR as *const i32) };
-// baz isn't a constant expression so it's ok
-```
-"##,
-
-E0396: r##"
-The value behind a raw pointer can't be determined at compile-time
-(or even link-time), which means it can't be used in a constant
-expression. Erroneous code example:
-
-```compile_fail
-const REG_ADDR: *const u8 = 0x5f3759df as *const u8;
-
-const VALUE: u8 = unsafe { *REG_ADDR };
-// error: raw pointers cannot be dereferenced in constants
-```
-
-A possible fix is to dereference your pointer at some point in run-time.
-
-For example:
-
-```
-const REG_ADDR: *const u8 = 0x5f3759df as *const u8;
-
-let reg_value = unsafe { *REG_ADDR };
-```
-"##,
-
-E0397: r##"
-It is not allowed for a mutable static to allocate or have destructors. For
-example:
-
-```compile_fail
-// error: mutable statics are not allowed to have boxes
-static mut FOO: Option<Box<usize>> = None;
-
-// error: mutable statics are not allowed to have destructors
-static mut BAR: Option<Vec<i32>> = None;
-```
-"##,
-
-E0400: r##"
-A user-defined dereference was attempted in an invalid context. Erroneous
-code example:
-
-```compile_fail
-use std::ops::Deref;
-
-struct A;
-
-impl Deref for A {
-    type Target = str;
-
-    fn deref(&self)-> &str { "foo" }
-}
-
-const S: &'static str = &A;
-// error: user-defined dereference operators are not allowed in constants
-
-fn main() {
-    let foo = S;
-}
-```
-
-You cannot directly use a dereference operation whilst initializing a constant
-or a static. To fix this error, restructure your code to avoid this dereference,
-perhaps moving it inline:
-
-```
-use std::ops::Deref;
-
-struct A;
-
-impl Deref for A {
-    type Target = str;
-
-    fn deref(&self)-> &str { "foo" }
-}
-
-fn main() {
-    let foo : &str = &A;
-}
-```
-"##,
-
-E0492: r##"
-A borrow of a constant containing interior mutability was attempted. Erroneous
-code example:
-
-```compile_fail
-use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT};
-
-const A: AtomicUsize = ATOMIC_USIZE_INIT;
-static B: &'static AtomicUsize = &A;
-// error: cannot borrow a constant which contains interior mutability, create a
-//        static instead
-```
-
-A `const` represents a constant value that should never change. If one takes
-a `&` reference to the constant, then one is taking a pointer to some memory
-location containing the value. Normally this is perfectly fine: most values
-can't be changed via a shared `&` pointer, but interior mutability would allow
-it. That is, a constant value could be mutated. On the other hand, a `static` is
-explicitly a single memory location, which can be mutated at will.
-
-So, in order to solve this error, either use statics which are `Sync`:
-
-```
-use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT};
-
-static A: AtomicUsize = ATOMIC_USIZE_INIT;
-static B: &'static AtomicUsize = &A; // ok!
-```
-
-You can also have this error while using a cell type:
-
-```compile_fail
-#![feature(const_fn)]
-
-use std::cell::Cell;
-
-const A: Cell<usize> = Cell::new(1);
-const B: &'static Cell<usize> = &A;
-// error: cannot borrow a constant which contains interior mutability, create
-//        a static instead
-
-// or:
-struct C { a: Cell<usize> }
-
-const D: C = C { a: Cell::new(1) };
-const E: &'static Cell<usize> = &D.a; // error
-
-// or:
-const F: &'static C = &D; // error
-```
-
-This is because cell types do operations that are not thread-safe. Due to this,
-they don't implement Sync and thus can't be placed in statics. In this
-case, `StaticMutex` would work just fine, but it isn't stable yet:
-https://doc.rust-lang.org/nightly/std/sync/struct.StaticMutex.html
-
-However, if you still wish to use these types, you can achieve this by an unsafe
-wrapper:
-
-```
-#![feature(const_fn)]
-
-use std::cell::Cell;
-use std::marker::Sync;
-
-struct NotThreadSafe<T> {
-    value: Cell<T>,
-}
-
-unsafe impl<T> Sync for NotThreadSafe<T> {}
-
-static A: NotThreadSafe<usize> = NotThreadSafe { value : Cell::new(1) };
-static B: &'static NotThreadSafe<usize> = &A; // ok!
-```
-
-Remember this solution is unsafe! You will have to ensure that accesses to the
-cell are synchronized.
-"##,
-
-E0493: r##"
-A type with a destructor was assigned to an invalid type of variable. Erroneous
-code example:
-
-```compile_fail
-struct Foo {
-    a: u32
-}
-
-impl Drop for Foo {
-    fn drop(&mut self) {}
-}
-
-const F : Foo = Foo { a : 0 };
-// error: constants are not allowed to have destructors
-static S : Foo = Foo { a : 0 };
-// error: statics are not allowed to have destructors
-```
-
-To solve this issue, please use a type which does allow the usage of type with
-destructors.
-"##,
-
-E0494: r##"
-A reference of an interior static was assigned to another const/static.
-Erroneous code example:
-
-```compile_fail
-struct Foo {
-    a: u32
-}
-
-static S : Foo = Foo { a : 0 };
-static A : &'static u32 = &S.a;
-// error: cannot refer to the interior of another static, use a
-//        constant instead
-```
-
-The "base" variable has to be a const if you want another static/const variable
-to refer to one of its fields. Example:
-
-```
-struct Foo {
-    a: u32
-}
-
-const S : Foo = Foo { a : 0 };
-static A : &'static u32 = &S.a; // ok!
-```
-"##,
-
 }
 
 register_diagnostics! {
diff --git a/src/librustc_passes/lib.rs b/src/librustc_passes/lib.rs
index b235962eb9a3d..67a9c2fd17e9f 100644
--- a/src/librustc_passes/lib.rs
+++ b/src/librustc_passes/lib.rs
@@ -37,7 +37,6 @@ extern crate rustc_const_math;
 
 pub mod diagnostics;
 
-pub mod const_fn;
 pub mod consts;
 pub mod loops;
 pub mod no_asm;
diff --git a/src/librustc_trans/collector.rs b/src/librustc_trans/collector.rs
index e8181579911d2..a0311ec606627 100644
--- a/src/librustc_trans/collector.rs
+++ b/src/librustc_trans/collector.rs
@@ -414,6 +414,9 @@ fn collect_items_rec<'a, 'tcx: 'a>(ccx: &CrateContext<'a, 'tcx>,
             };
 
             visitor.visit_mir(&mir);
+            for promoted in &mir.promoted {
+                visitor.visit_mir(promoted);
+            }
         }
     }
 
diff --git a/src/librustc_trans/consts.rs b/src/librustc_trans/consts.rs
index b9af0bbe3d123..09d38e8946bbd 100644
--- a/src/librustc_trans/consts.rs
+++ b/src/librustc_trans/consts.rs
@@ -39,7 +39,7 @@ use rustc::ty::adjustment::{AdjustUnsafeFnPointer, AdjustMutToConstPointer};
 use rustc::ty::{self, Ty, TyCtxt};
 use rustc::ty::cast::{CastTy,IntTy};
 use util::nodemap::NodeMap;
-use rustc_const_math::{ConstInt, ConstMathErr, ConstUsize, ConstIsize};
+use rustc_const_math::{ConstInt, ConstUsize, ConstIsize};
 
 use rustc::hir;
 
@@ -48,6 +48,7 @@ use std::borrow::Cow;
 use libc::c_uint;
 use syntax::ast::{self, LitKind};
 use syntax::attr::{self, AttrMetaMethods};
+use syntax::codemap::Span;
 use syntax::parse::token;
 use syntax::ptr::P;
 
@@ -110,11 +111,11 @@ pub fn ptrcast(val: ValueRef, ty: Type) -> ValueRef {
     }
 }
 
-fn addr_of_mut(ccx: &CrateContext,
-               cv: ValueRef,
-               align: machine::llalign,
-               kind: &str)
-               -> ValueRef {
+pub fn addr_of_mut(ccx: &CrateContext,
+                   cv: ValueRef,
+                   align: machine::llalign,
+                   kind: &str)
+                    -> ValueRef {
     unsafe {
         // FIXME: this totally needs a better name generation scheme, perhaps a simple global
         // counter? Also most other uses of gensym in trans.
@@ -158,13 +159,13 @@ pub fn addr_of(ccx: &CrateContext,
 }
 
 /// Deref a constant pointer
-fn load_const(cx: &CrateContext, v: ValueRef, t: Ty) -> ValueRef {
+pub fn load_const(cx: &CrateContext, v: ValueRef, t: Ty) -> ValueRef {
     let v = match cx.const_unsized().borrow().get(&v) {
         Some(&v) => v,
         None => v
     };
     let d = unsafe { llvm::LLVMGetInitializer(v) };
-    if t.is_bool() {
+    if !d.is_null() && t.is_bool() {
         unsafe { llvm::LLVMConstTrunc(d, Type::i1(cx).to_ref()) }
     } else {
         d
@@ -466,16 +467,12 @@ fn check_unary_expr_validity(cx: &CrateContext, e: &hir::Expr, t: Ty,
             Some(v) => v,
             None => return Ok(()),
         };
-        match -cval {
-            Ok(_) => return Ok(()),
-            Err(err) => const_err(cx, e, Err(err), trueconst),
-        }
-    } else {
-        Ok(())
+        const_err(cx, e.span, (-cval).map_err(ErrKind::Math), trueconst)?;
     }
+    Ok(())
 }
 
-fn to_const_int(value: ValueRef, t: Ty, tcx: &TyCtxt) -> Option<ConstInt> {
+pub fn to_const_int(value: ValueRef, t: Ty, tcx: &TyCtxt) -> Option<ConstInt> {
     match t.sty {
         ty::TyInt(int_type) => const_to_opt_int(value).and_then(|input| match int_type {
             ast::IntTy::I8 => {
@@ -523,24 +520,21 @@ fn to_const_int(value: ValueRef, t: Ty, tcx: &TyCtxt) -> Option<ConstInt> {
     }
 }
 
-fn const_err(cx: &CrateContext,
-             e: &hir::Expr,
-             result: Result<ConstInt, ConstMathErr>,
-             trueconst: TrueConst)
-             -> Result<(), ConstEvalFailure> {
+pub fn const_err<T>(cx: &CrateContext,
+                    span: Span,
+                    result: Result<T, ErrKind>,
+                    trueconst: TrueConst)
+                    -> Result<T, ConstEvalFailure> {
     match (result, trueconst) {
-        (Ok(_), _) => {
-            // We do not actually care about a successful result.
-            Ok(())
-        },
+        (Ok(x), _) => Ok(x),
         (Err(err), TrueConst::Yes) => {
-            let err = ConstEvalErr{ span: e.span, kind: ErrKind::Math(err) };
-            cx.tcx().sess.span_err(e.span, &err.description());
+            let err = ConstEvalErr{ span: span, kind: err };
+            cx.tcx().sess.span_err(span, &err.description());
             Err(Compiletime(err))
         },
         (Err(err), TrueConst::No) => {
-            let err = ConstEvalErr{ span: e.span, kind: ErrKind::Math(err) };
-            cx.tcx().sess.span_warn(e.span, &err.description());
+            let err = ConstEvalErr{ span: span, kind: err };
+            cx.tcx().sess.span_warn(span, &err.description());
             Err(Runtime(err))
         },
     }
@@ -564,7 +558,8 @@ fn check_binary_expr_validity(cx: &CrateContext, e: &hir::Expr, t: Ty,
         hir::BiShr => lhs >> rhs,
         _ => return Ok(()),
     };
-    const_err(cx, e, result, trueconst)
+    const_err(cx, e.span, result.map_err(ErrKind::Math), trueconst)?;
+    Ok(())
 }
 
 fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
@@ -719,8 +714,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
             if iv >= len {
                 // FIXME #3170: report this earlier on in the const-eval
                 // pass. Reporting here is a bit late.
-                span_err!(cx.sess(), e.span, E0515,
-                          "const index-expr is out of bounds");
+                const_err(cx, e.span, Err(ErrKind::IndexOutOfBounds), trueconst)?;
                 C_undef(val_ty(arr).element_type())
             } else {
                 const_get_elt(arr, &[iv as c_uint])
@@ -1128,6 +1122,7 @@ pub fn get_static<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, def_id: DefId)
     };
 
     ccx.instances().borrow_mut().insert(instance, g);
+    ccx.statics().borrow_mut().insert(g, def_id);
     Datum::new(g, ty, Lvalue::new("static"))
 }
 
@@ -1147,14 +1142,20 @@ pub fn trans_static(ccx: &CrateContext,
         let def_id = ccx.tcx().map.local_def_id(id);
         let datum = get_static(ccx, def_id);
 
-        let empty_substs = ccx.tcx().mk_substs(Substs::empty());
-        let (v, _) = const_expr(
-            ccx,
-            expr,
-            empty_substs,
-            None,
-            TrueConst::Yes,
-        ).map_err(|e| e.into_inner())?;
+        let check_attrs = |attrs: &[ast::Attribute]| {
+            let default_to_mir = ccx.sess().opts.debugging_opts.orbit;
+            let invert = if default_to_mir { "rustc_no_mir" } else { "rustc_mir" };
+            default_to_mir ^ attrs.iter().any(|item| item.check_name(invert))
+        };
+        let use_mir = check_attrs(ccx.tcx().map.attrs(id));
+
+        let v = if use_mir {
+            ::mir::trans_static_initializer(ccx, def_id)
+        } else {
+            let empty_substs = ccx.tcx().mk_substs(Substs::empty());
+            const_expr(ccx, expr, empty_substs, None, TrueConst::Yes)
+                .map(|(v, _)| v)
+        }.map_err(|e| e.into_inner())?;
 
         // boolean SSA values are i1, but they have to be stored in i8 slots,
         // otherwise some LLVM optimization passes don't work as expected
diff --git a/src/librustc_trans/context.rs b/src/librustc_trans/context.rs
index 1217b2b5a1b17..2ee880fa3d850 100644
--- a/src/librustc_trans/context.rs
+++ b/src/librustc_trans/context.rs
@@ -131,6 +131,9 @@ pub struct LocalCrateContext<'tcx> {
     /// Cache of external const values
     extern_const_values: RefCell<DefIdMap<ValueRef>>,
 
+    /// Mapping from static definitions to their DefId's.
+    statics: RefCell<FnvHashMap<ValueRef, DefId>>,
+
     impl_method_cache: RefCell<FnvHashMap<(DefId, ast::Name), DefId>>,
 
     /// Cache of closure wrappers for bare fn's.
@@ -495,6 +498,7 @@ impl<'tcx> LocalCrateContext<'tcx> {
                 const_globals: RefCell::new(FnvHashMap()),
                 const_values: RefCell::new(FnvHashMap()),
                 extern_const_values: RefCell::new(DefIdMap()),
+                statics: RefCell::new(FnvHashMap()),
                 impl_method_cache: RefCell::new(FnvHashMap()),
                 closure_bare_wrapper_cache: RefCell::new(FnvHashMap()),
                 statics_to_rauw: RefCell::new(Vec::new()),
@@ -699,6 +703,10 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> {
         &self.local.extern_const_values
     }
 
+    pub fn statics<'a>(&'a self) -> &'a RefCell<FnvHashMap<ValueRef, DefId>> {
+        &self.local.statics
+    }
+
     pub fn impl_method_cache<'a>(&'a self)
             -> &'a RefCell<FnvHashMap<(DefId, ast::Name), DefId>> {
         &self.local.impl_method_cache
diff --git a/src/librustc_trans/diagnostics.rs b/src/librustc_trans/diagnostics.rs
index 5e4902cf3caf2..d9de673db27ca 100644
--- a/src/librustc_trans/diagnostics.rs
+++ b/src/librustc_trans/diagnostics.rs
@@ -82,19 +82,4 @@ extern "platform-intrinsic" {
 unsafe { simd_add(i32x1(0), i32x1(1)); } // ok!
 ```
 "##,
-
-E0515: r##"
-A constant index expression was out of bounds. Erroneous code example:
-
-```compile_fail
-let x = &[0, 1, 2][7]; // error: const index-expr is out of bounds
-```
-
-Please specify a valid index (not inferior to 0 or superior to array length).
-Example:
-
-```
-let x = &[0, 1, 2][2]; // ok
-```
-"##,
 }
diff --git a/src/librustc_trans/intrinsic.rs b/src/librustc_trans/intrinsic.rs
index 1220fbafa29c9..1824055fcf94d 100644
--- a/src/librustc_trans/intrinsic.rs
+++ b/src/librustc_trans/intrinsic.rs
@@ -1482,28 +1482,23 @@ fn generic_simd_intrinsic<'blk, 'tcx, 'a>
 
         let total_len = in_len as u64 * 2;
 
-        let (vector, indirect) = match args {
+        let vector = match args {
             Some(args) => {
                 match consts::const_expr(bcx.ccx(), &args[2], substs, None,
                                          // this should probably help simd error reporting
                                          consts::TrueConst::Yes) {
-                    Ok((vector, _)) => (vector, false),
+                    Ok((vector, _)) => vector,
                     Err(err) => bcx.sess().span_fatal(span, &err.description()),
                 }
             }
-            None => (llargs[2], !type_is_immediate(bcx.ccx(), arg_tys[2]))
+            None => llargs[2]
         };
 
         let indices: Option<Vec<_>> = (0..n)
             .map(|i| {
                 let arg_idx = i;
-                let val = if indirect {
-                    Load(bcx, StructGEP(bcx, vector, i))
-                } else {
-                    const_get_elt(vector, &[i as libc::c_uint])
-                };
-                let c = const_to_opt_uint(val);
-                match c {
+                let val = const_get_elt(vector, &[i as libc::c_uint]);
+                match const_to_opt_uint(val) {
                     None => {
                         emit_error!("shuffle index #{} is not a constant", arg_idx);
                         None
diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs
index d0b47934bcf16..e605ef81c587f 100644
--- a/src/librustc_trans/mir/block.rs
+++ b/src/librustc_trans/mir/block.rs
@@ -26,6 +26,7 @@ use glue;
 use type_::Type;
 
 use super::{MirContext, TempRef, drop};
+use super::constant::Const;
 use super::lvalue::{LvalueRef, load_fat_ptr};
 use super::operand::OperandRef;
 use super::operand::OperandValue::{self, FatPtr, Immediate, Ref};
@@ -114,9 +115,9 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                 let discr = bcx.with_block(|bcx| base::to_immediate(bcx, discr, switch_ty));
                 let switch = bcx.switch(discr, self.llblock(*otherwise), values.len());
                 for (value, target) in values.iter().zip(targets) {
-                    let llval = self.trans_constval(&bcx, value, switch_ty).immediate();
+                    let val = Const::from_constval(bcx.ccx(), value.clone(), switch_ty);
                     let llbb = self.llblock(*target);
-                    build::AddCase(switch, llval, llbb)
+                    build::AddCase(switch, val.llval, llbb)
                 }
             }
 
@@ -240,8 +241,30 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                     (&args[..], None)
                 };
 
+                let is_shuffle = intrinsic.map_or(false, |name| {
+                    name.starts_with("simd_shuffle")
+                });
                 let mut idx = 0;
                 for arg in first_args {
+                    // The indices passed to simd_shuffle* in the
+                    // third argument must be constant. This is
+                    // checked by const-qualification, which also
+                    // promotes any complex rvalues to constants.
+                    if is_shuffle && idx == 2 {
+                        match *arg {
+                            mir::Operand::Consume(_) => {
+                                span_bug!(terminator.span,
+                                          "shuffle indices must be constant");
+                            }
+                            mir::Operand::Constant(ref constant) => {
+                                let val = self.trans_constant(&bcx, constant);
+                                llargs.push(val.llval);
+                                idx += 1;
+                                continue;
+                            }
+                        }
+                    }
+
                     let val = self.trans_operand(&bcx, arg).val;
                     self.trans_argument(&bcx, val, &mut llargs, &fn_ty,
                                         &mut idx, &mut callee.data);
diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs
index cf85595c08e96..9320617983772 100644
--- a/src/librustc_trans/mir/constant.rs
+++ b/src/librustc_trans/mir/constant.rs
@@ -8,62 +8,59 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use llvm::ValueRef;
-use rustc::ty::{Ty, TypeFoldable};
+use llvm::{self, ValueRef};
 use rustc::middle::const_val::ConstVal;
+use rustc_const_eval::ErrKind;
 use rustc_const_math::ConstInt::*;
-use rustc_const_eval::lookup_const_by_id;
+use rustc::hir::def_id::DefId;
 use rustc::mir::repr as mir;
-use abi;
-use common::{self, BlockAndBuilder, C_bool, C_bytes, C_floating_f64, C_integral,
-                    C_str_slice, C_undef};
-use consts;
-use datum;
-use expr;
+use rustc::mir::tcx::LvalueTy;
+use rustc::traits;
+use rustc::ty::{self, Ty, TypeFoldable};
+use rustc::ty::cast::{CastTy, IntTy};
+use rustc::ty::subst::Substs;
+use {abi, adt, base, Disr};
+use callee::Callee;
+use common::{self, BlockAndBuilder, CrateContext, const_get_elt, val_ty};
+use common::{C_array, C_bool, C_bytes, C_floating_f64, C_integral};
+use common::{C_null, C_struct, C_str_slice, C_undef, C_uint};
+use consts::{self, ConstEvalFailure, TrueConst, to_const_int};
+use monomorphize::{self, Instance};
 use type_of;
 use type_::Type;
+use value::Value;
 
-use super::operand::{OperandRef, OperandValue};
-use super::MirContext;
+use syntax::codemap::{Span, DUMMY_SP};
 
+use std::ptr;
 
-impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
-    pub fn trans_constval(&mut self,
-                          bcx: &BlockAndBuilder<'bcx, 'tcx>,
-                          cv: &ConstVal,
-                          ty: Ty<'tcx>)
-                          -> OperandRef<'tcx>
-    {
-        let ccx = bcx.ccx();
-        let val = self.trans_constval_inner(bcx, cv, ty);
-        let val = if common::type_is_immediate(ccx, ty) {
-            OperandValue::Immediate(val)
-        } else if common::type_is_fat_ptr(bcx.tcx(), ty) {
-            let data = common::const_get_elt(val, &[abi::FAT_PTR_ADDR as u32]);
-            let extra = common::const_get_elt(val, &[abi::FAT_PTR_EXTRA as u32]);
-            OperandValue::FatPtr(data, extra)
-        } else {
-            OperandValue::Ref(val)
-        };
+use super::operand::{OperandRef, OperandValue};
+use super::MirContext;
 
-        assert!(!ty.has_erasable_regions());
+/// A sized constant rvalue.
+/// The LLVM type might not be the same for a single Rust type,
+/// e.g. each enum variant would have its own LLVM struct type.
+#[derive(Copy, Clone)]
+pub struct Const<'tcx> {
+    pub llval: ValueRef,
+    pub ty: Ty<'tcx>
+}
 
-        OperandRef {
-            ty: ty,
-            val: val
+impl<'tcx> Const<'tcx> {
+    pub fn new(llval: ValueRef, ty: Ty<'tcx>) -> Const<'tcx> {
+        Const {
+            llval: llval,
+            ty: ty
         }
     }
 
-    /// Translate ConstVal into a bare LLVM ValueRef.
-    fn trans_constval_inner(&mut self,
-                            bcx: &BlockAndBuilder<'bcx, 'tcx>,
-                            cv: &ConstVal,
-                            ty: Ty<'tcx>)
-                            -> ValueRef
-    {
-        let ccx = bcx.ccx();
+    /// Translate ConstVal into a LLVM constant value.
+    pub fn from_constval<'a>(ccx: &CrateContext<'a, 'tcx>,
+                             cv: ConstVal,
+                             ty: Ty<'tcx>)
+                             -> Const<'tcx> {
         let llty = type_of::type_of(ccx, ty);
-        match *cv {
+        let val = match cv {
             ConstVal::Float(v) => C_floating_f64(v, llty),
             ConstVal::Bool(v) => C_bool(ccx, v),
             ConstVal::Integral(I8(v)) => C_integral(Type::i8(ccx), v as u64, true),
@@ -93,51 +90,769 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
             }
             ConstVal::Char(c) => C_integral(Type::char(ccx), c as u64, false),
             ConstVal::Dummy => bug!(),
+        };
+
+        assert!(!ty.has_erasable_regions());
+
+        Const::new(val, ty)
+    }
+
+    fn get_fat_ptr(&self) -> (ValueRef, ValueRef) {
+        (const_get_elt(self.llval, &[abi::FAT_PTR_ADDR as u32]),
+         const_get_elt(self.llval, &[abi::FAT_PTR_EXTRA as u32]))
+    }
+
+    fn as_lvalue(&self) -> ConstLvalue<'tcx> {
+        ConstLvalue {
+            base: Base::Value(self.llval),
+            llextra: ptr::null_mut(),
+            ty: self.ty
+        }
+    }
+
+    pub fn to_operand<'a>(&self, ccx: &CrateContext<'a, 'tcx>) -> OperandRef<'tcx> {
+        let llty = type_of::immediate_type_of(ccx, self.ty);
+        let llvalty = val_ty(self.llval);
+
+        let val = if common::type_is_fat_ptr(ccx.tcx(), self.ty) {
+            let (data, extra) = self.get_fat_ptr();
+            OperandValue::FatPtr(data, extra)
+        } else if common::type_is_immediate(ccx, self.ty) && llty == llvalty {
+            // If the types match, we can use the value directly.
+            OperandValue::Immediate(self.llval)
+        } else {
+            // Otherwise, or if the value is not immediate, we create
+            // a constant LLVM global and cast its address if necessary.
+            let align = type_of::align_of(ccx, self.ty);
+            let ptr = consts::addr_of(ccx, self.llval, align, "const");
+            OperandValue::Ref(consts::ptrcast(ptr, llty.ptr_to()))
+        };
+
+        OperandRef {
+            val: val,
+            ty: self.ty
+        }
+    }
+}
+
+#[derive(Copy, Clone)]
+enum Base {
+    /// A constant value without an unique address.
+    Value(ValueRef),
+
+    /// String literal base pointer (cast from array).
+    Str(ValueRef),
+
+    /// The address of a static.
+    Static(ValueRef)
+}
+
+/// An lvalue as seen from a constant.
+#[derive(Copy, Clone)]
+struct ConstLvalue<'tcx> {
+    base: Base,
+    llextra: ValueRef,
+    ty: Ty<'tcx>
+}
+
+impl<'tcx> ConstLvalue<'tcx> {
+    fn to_const(&self, span: Span) -> Const<'tcx> {
+        match self.base {
+            Base::Value(val) => Const::new(val, self.ty),
+            Base::Str(ptr) => {
+                span_bug!(span, "loading from `str` ({:?}) in constant",
+                          Value(ptr))
+            }
+            Base::Static(val) => {
+                span_bug!(span, "loading from `static` ({:?}) in constant",
+                          Value(val))
+            }
+        }
+    }
+
+    pub fn len<'a>(&self, ccx: &CrateContext<'a, 'tcx>) -> ValueRef {
+        match self.ty.sty {
+            ty::TyArray(_, n) => C_uint(ccx, n),
+            ty::TySlice(_) | ty::TyStr => {
+                assert!(self.llextra != ptr::null_mut());
+                self.llextra
+            }
+            _ => bug!("unexpected type `{}` in ConstLvalue::len", self.ty)
+        }
+    }
+}
+
+/// Machinery for translating a constant's MIR to LLVM values.
+/// FIXME(eddyb) use miri and lower its allocations to LLVM.
+struct MirConstContext<'a, 'tcx: 'a> {
+    ccx: &'a CrateContext<'a, 'tcx>,
+    mir: &'a mir::Mir<'tcx>,
+
+    /// Type parameters for const fn and associated constants.
+    substs: &'tcx Substs<'tcx>,
+
+    /// Arguments passed to a const fn.
+    args: Vec<Const<'tcx>>,
+
+    /// Variable values - specifically, argument bindings of a const fn.
+    vars: Vec<Option<Const<'tcx>>>,
+
+    /// Temp values.
+    temps: Vec<Option<Const<'tcx>>>,
+
+    /// Value assigned to Return, which is the resulting constant.
+    return_value: Option<Const<'tcx>>
+}
+
+
+impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
+    fn new(ccx: &'a CrateContext<'a, 'tcx>,
+           mir: &'a mir::Mir<'tcx>,
+           substs: &'tcx Substs<'tcx>,
+           args: Vec<Const<'tcx>>)
+           -> MirConstContext<'a, 'tcx> {
+        MirConstContext {
+            ccx: ccx,
+            mir: mir,
+            substs: substs,
+            args: args,
+            vars: vec![None; mir.var_decls.len()],
+            temps: vec![None; mir.temp_decls.len()],
+            return_value: None
         }
     }
 
+    fn trans_def(ccx: &'a CrateContext<'a, 'tcx>,
+                 mut instance: Instance<'tcx>,
+                 args: Vec<Const<'tcx>>)
+                 -> Result<Const<'tcx>, ConstEvalFailure> {
+        // Try to resolve associated constants.
+        if instance.substs.self_ty().is_some() {
+            // Only trait items can have a Self parameter.
+            let trait_item = ccx.tcx().impl_or_trait_item(instance.def);
+            let trait_id = trait_item.container().id();
+            let substs = instance.substs;
+            let trait_ref = ty::Binder(substs.to_trait_ref(ccx.tcx(), trait_id));
+            let vtable = common::fulfill_obligation(ccx, DUMMY_SP, trait_ref);
+            if let traits::VtableImpl(vtable_impl) = vtable {
+                let name = ccx.tcx().item_name(instance.def);
+                for ac in ccx.tcx().associated_consts(vtable_impl.impl_def_id) {
+                    if ac.name == name {
+                        instance = Instance::new(ac.def_id, vtable_impl.substs);
+                        break;
+                    }
+                }
+            }
+        }
+
+        let mir = ccx.get_mir(instance.def).unwrap_or_else(|| {
+            bug!("missing constant MIR for {}", instance)
+        });
+        MirConstContext::new(ccx, &mir, instance.substs, args).trans()
+    }
+
+    fn monomorphize<T>(&self, value: &T) -> T
+        where T : TypeFoldable<'tcx>
+    {
+        monomorphize::apply_param_substs(self.ccx.tcx(),
+                                         self.substs,
+                                         value)
+    }
+
+    fn trans(&mut self) -> Result<Const<'tcx>, ConstEvalFailure> {
+        let tcx = self.ccx.tcx();
+        let mut bb = mir::START_BLOCK;
+        loop {
+            let data = self.mir.basic_block_data(bb);
+            for statement in &data.statements {
+                match statement.kind {
+                    mir::StatementKind::Assign(ref dest, ref rvalue) => {
+                        let ty = self.mir.lvalue_ty(tcx, dest);
+                        let ty = self.monomorphize(&ty).to_ty(tcx);
+                        let value = self.const_rvalue(rvalue, ty, statement.span)?;
+                        self.store(dest, value, statement.span);
+                    }
+                }
+            }
+
+            let terminator = data.terminator();
+            let span = terminator.span;
+            bb = match terminator.kind {
+                mir::TerminatorKind::Drop { target, .. } | // No dropping.
+                mir::TerminatorKind::Goto { target } => target,
+                mir::TerminatorKind::Return => {
+                    return Ok(self.return_value.unwrap_or_else(|| {
+                        span_bug!(span, "no returned value in constant");
+                    }))
+                }
+
+                // This is only supported to make bounds checking work.
+                mir::TerminatorKind::If { ref cond, targets: (true_bb, false_bb) } => {
+                    let cond = self.const_operand(cond, span)?;
+                    if common::const_to_uint(cond.llval) != 0 {
+                        true_bb
+                    } else {
+                        false_bb
+                    }
+                }
+
+                mir::TerminatorKind::Call { ref func, ref args, ref destination, .. } => {
+                    let fn_ty = self.mir.operand_ty(tcx, func);
+                    let fn_ty = self.monomorphize(&fn_ty);
+                    let instance = match fn_ty.sty {
+                        ty::TyFnDef(def_id, substs, _) => {
+                            Instance::new(def_id, substs)
+                        }
+                        _ => span_bug!(span, "calling {:?} (of type {}) in constant",
+                                       func, fn_ty)
+                    };
+
+                    // Indexing OOB doesn't call a const fn, handle it.
+                    if Some(instance.def) == tcx.lang_items.panic_bounds_check_fn() {
+                        consts::const_err(self.ccx, span,
+                                          Err(ErrKind::IndexOutOfBounds),
+                                          TrueConst::Yes)?;
+                    }
+
+                    let args = args.iter().map(|arg| {
+                        self.const_operand(arg, span)
+                    }).collect::<Result<Vec<_>, _>>()?;
+                    let value = MirConstContext::trans_def(self.ccx, instance, args)?;
+                    if let Some((ref dest, target)) = *destination {
+                        self.store(dest, value, span);
+                        target
+                    } else {
+                        span_bug!(span, "diverging {:?} in constant", terminator.kind)
+                    }
+                }
+                _ => span_bug!(span, "{:?} in constant", terminator.kind)
+            };
+        }
+    }
+
+    fn store(&mut self, dest: &mir::Lvalue<'tcx>, value: Const<'tcx>, span: Span) {
+        let dest = match *dest {
+            mir::Lvalue::Var(index) => &mut self.vars[index as usize],
+            mir::Lvalue::Temp(index) => &mut self.temps[index as usize],
+            mir::Lvalue::ReturnPointer => &mut self.return_value,
+            _ => span_bug!(span, "assignment to {:?} in constant", dest)
+        };
+        *dest = Some(value);
+    }
+
+    fn const_lvalue(&self, lvalue: &mir::Lvalue<'tcx>, span: Span)
+                    -> Result<ConstLvalue<'tcx>, ConstEvalFailure> {
+        let tcx = self.ccx.tcx();
+        let lvalue = match *lvalue {
+            mir::Lvalue::Var(index) => {
+                self.vars[index as usize].unwrap_or_else(|| {
+                    span_bug!(span, "var{} not initialized", index)
+                }).as_lvalue()
+            }
+            mir::Lvalue::Temp(index) => {
+                self.temps[index as usize].unwrap_or_else(|| {
+                    span_bug!(span, "tmp{} not initialized", index)
+                }).as_lvalue()
+            }
+            mir::Lvalue::Arg(index) => self.args[index as usize].as_lvalue(),
+            mir::Lvalue::Static(def_id) => {
+                ConstLvalue {
+                    base: Base::Static(consts::get_static(self.ccx, def_id).val),
+                    llextra: ptr::null_mut(),
+                    ty: self.mir.lvalue_ty(tcx, lvalue).to_ty(tcx)
+                }
+            }
+            mir::Lvalue::ReturnPointer => {
+                span_bug!(span, "accessing Lvalue::ReturnPointer in constant")
+            }
+            mir::Lvalue::Projection(ref projection) => {
+                let tr_base = self.const_lvalue(&projection.base, span)?;
+                let projected_ty = LvalueTy::Ty { ty: tr_base.ty }
+                    .projection_ty(tcx, &projection.elem);
+                let base = tr_base.to_const(span);
+                let projected_ty = self.monomorphize(&projected_ty).to_ty(tcx);
+                let is_sized = common::type_is_sized(tcx, projected_ty);
+
+                let (projected, llextra) = match projection.elem {
+                    mir::ProjectionElem::Deref => {
+                        let (base, extra) = if is_sized {
+                            (base.llval, ptr::null_mut())
+                        } else {
+                            base.get_fat_ptr()
+                        };
+                        if self.ccx.statics().borrow().contains_key(&base) {
+                            (Base::Static(base), extra)
+                        } else if let ty::TyStr = projected_ty.sty {
+                            (Base::Str(base), extra)
+                        } else {
+                            let val = consts::load_const(self.ccx, base, projected_ty);
+                            if val.is_null() {
+                                span_bug!(span, "dereference of non-constant pointer `{:?}`",
+                                          Value(base));
+                            }
+                            (Base::Value(val), extra)
+                        }
+                    }
+                    mir::ProjectionElem::Field(ref field, _) => {
+                        let base_repr = adt::represent_type(self.ccx, tr_base.ty);
+                        let llprojected = adt::const_get_field(&base_repr, base.llval,
+                                                               Disr(0), field.index());
+                        let llextra = if is_sized {
+                            ptr::null_mut()
+                        } else {
+                            tr_base.llextra
+                        };
+                        (Base::Value(llprojected), llextra)
+                    }
+                    mir::ProjectionElem::Index(ref index) => {
+                        let llindex = self.const_operand(index, span)?.llval;
+
+                        let iv = if let Some(iv) = common::const_to_opt_uint(llindex) {
+                            iv
+                        } else {
+                            span_bug!(span, "index is not an integer-constant expression")
+                        };
+                        (Base::Value(const_get_elt(base.llval, &[iv as u32])),
+                         ptr::null_mut())
+                    }
+                    _ => span_bug!(span, "{:?} in constant", projection.elem)
+                };
+                ConstLvalue {
+                    base: projected,
+                    llextra: llextra,
+                    ty: projected_ty
+                }
+            }
+        };
+        Ok(lvalue)
+    }
+
+    fn const_operand(&self, operand: &mir::Operand<'tcx>, span: Span)
+                     -> Result<Const<'tcx>, ConstEvalFailure> {
+        match *operand {
+            mir::Operand::Consume(ref lvalue) => {
+                Ok(self.const_lvalue(lvalue, span)?.to_const(span))
+            }
+
+            mir::Operand::Constant(ref constant) => {
+                let ty = self.monomorphize(&constant.ty);
+                match constant.literal.clone() {
+                    mir::Literal::Item { def_id, substs } => {
+                        // Shortcut for zero-sized types, including function item
+                        // types, which would not work with MirConstContext.
+                        if common::type_is_zero_size(self.ccx, ty) {
+                            let llty = type_of::type_of(self.ccx, ty);
+                            return Ok(Const::new(C_null(llty), ty));
+                        }
+
+                        let substs = self.ccx.tcx().mk_substs(self.monomorphize(substs));
+                        let instance = Instance::new(def_id, substs);
+                        MirConstContext::trans_def(self.ccx, instance, vec![])
+                    }
+                    mir::Literal::Promoted { index } => {
+                        let mir = &self.mir.promoted[index];
+                        MirConstContext::new(self.ccx, mir, self.substs, vec![]).trans()
+                    }
+                    mir::Literal::Value { value } => {
+                        Ok(Const::from_constval(self.ccx, value, ty))
+                    }
+                }
+            }
+        }
+    }
+
+    fn const_rvalue(&self, rvalue: &mir::Rvalue<'tcx>,
+                    dest_ty: Ty<'tcx>, span: Span)
+                    -> Result<Const<'tcx>, ConstEvalFailure> {
+        let tcx = self.ccx.tcx();
+        let val = match *rvalue {
+            mir::Rvalue::Use(ref operand) => self.const_operand(operand, span)?,
+
+            mir::Rvalue::Repeat(ref elem, ref count) => {
+                let elem = self.const_operand(elem, span)?;
+                let size = count.value.as_u64(tcx.sess.target.uint_type);
+                let fields = vec![elem.llval; size as usize];
+
+                let llunitty = type_of::type_of(self.ccx, elem.ty);
+                // If the array contains enums, an LLVM array won't work.
+                let val = if val_ty(elem.llval) == llunitty {
+                    C_array(llunitty, &fields)
+                } else {
+                    C_struct(self.ccx, &fields, false)
+                };
+                Const::new(val, dest_ty)
+            }
+
+            mir::Rvalue::Aggregate(ref kind, ref operands) => {
+                let fields = operands.iter().map(|operand| {
+                    Ok(self.const_operand(operand, span)?.llval)
+                }).collect::<Result<Vec<_>, _>>()?;
+
+                // FIXME Shouldn't need to manually trigger closure instantiations.
+                if let mir::AggregateKind::Closure(def_id, substs) = *kind {
+                    use rustc::hir;
+                    use syntax::ast::DUMMY_NODE_ID;
+                    use syntax::ptr::P;
+                    use closure;
+
+                    closure::trans_closure_expr(closure::Dest::Ignore(self.ccx),
+                                                &hir::FnDecl {
+                                                    inputs: P::new(),
+                                                    output: hir::NoReturn(DUMMY_SP),
+                                                    variadic: false
+                                                },
+                                                &hir::Block {
+                                                    stmts: P::new(),
+                                                    expr: None,
+                                                    id: DUMMY_NODE_ID,
+                                                    rules: hir::DefaultBlock,
+                                                    span: DUMMY_SP
+                                                },
+                                                DUMMY_NODE_ID, def_id,
+                                                &self.monomorphize(substs));
+                }
+
+                let val = if let mir::AggregateKind::Adt(adt_def, index, _) = *kind {
+                    let repr = adt::represent_type(self.ccx, dest_ty);
+                    let disr = Disr::from(adt_def.variants[index].disr_val);
+                    adt::trans_const(self.ccx, &repr, disr, &fields)
+                } else if let ty::TyArray(elem_ty, _) = dest_ty.sty {
+                    let llunitty = type_of::type_of(self.ccx, elem_ty);
+                    // If the array contains enums, an LLVM array won't work.
+                    if fields.iter().all(|&f| val_ty(f) == llunitty) {
+                        C_array(llunitty, &fields)
+                    } else {
+                        C_struct(self.ccx, &fields, false)
+                    }
+                } else {
+                    C_struct(self.ccx, &fields, false)
+                };
+                Const::new(val, dest_ty)
+            }
+
+            mir::Rvalue::Cast(ref kind, ref source, cast_ty) => {
+                let operand = self.const_operand(source, span)?;
+                let cast_ty = self.monomorphize(&cast_ty);
+
+                let val = match *kind {
+                    mir::CastKind::ReifyFnPointer => {
+                        match operand.ty.sty {
+                            ty::TyFnDef(def_id, substs, _) => {
+                                Callee::def(self.ccx, def_id, substs)
+                                    .reify(self.ccx).val
+                            }
+                            _ => {
+                                span_bug!(span, "{} cannot be reified to a fn ptr",
+                                          operand.ty)
+                            }
+                        }
+                    }
+                    mir::CastKind::UnsafeFnPointer => {
+                        // this is a no-op at the LLVM level
+                        operand.llval
+                    }
+                    mir::CastKind::Unsize => {
+                        // unsize targets other than to a fat pointer currently
+                        // can't be in constants.
+                        assert!(common::type_is_fat_ptr(tcx, cast_ty));
+
+                        let pointee_ty = operand.ty.builtin_deref(true, ty::NoPreference)
+                            .expect("consts: unsizing got non-pointer type").ty;
+                        let (base, old_info) = if !common::type_is_sized(tcx, pointee_ty) {
+                            // Normally, the source is a thin pointer and we are
+                            // adding extra info to make a fat pointer. The exception
+                            // is when we are upcasting an existing object fat pointer
+                            // to use a different vtable. In that case, we want to
+                            // load out the original data pointer so we can repackage
+                            // it.
+                            let (base, extra) = operand.get_fat_ptr();
+                            (base, Some(extra))
+                        } else {
+                            (operand.llval, None)
+                        };
+
+                        let unsized_ty = cast_ty.builtin_deref(true, ty::NoPreference)
+                            .expect("consts: unsizing got non-pointer target type").ty;
+                        let ptr_ty = type_of::in_memory_type_of(self.ccx, unsized_ty).ptr_to();
+                        let base = consts::ptrcast(base, ptr_ty);
+                        let info = base::unsized_info(self.ccx, pointee_ty,
+                                                      unsized_ty, old_info);
+
+                        if old_info.is_none() {
+                            let prev_const = self.ccx.const_unsized().borrow_mut()
+                                                     .insert(base, operand.llval);
+                            assert!(prev_const.is_none() || prev_const == Some(operand.llval));
+                        }
+                        assert_eq!(abi::FAT_PTR_ADDR, 0);
+                        assert_eq!(abi::FAT_PTR_EXTRA, 1);
+                        C_struct(self.ccx, &[base, info], false)
+                    }
+                    mir::CastKind::Misc if common::type_is_immediate(self.ccx, operand.ty) => {
+                        debug_assert!(common::type_is_immediate(self.ccx, cast_ty));
+                        let r_t_in = CastTy::from_ty(operand.ty).expect("bad input type for cast");
+                        let r_t_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
+                        let ll_t_out = type_of::immediate_type_of(self.ccx, cast_ty);
+                        let llval = operand.llval;
+                        let signed = if let CastTy::Int(IntTy::CEnum) = r_t_in {
+                            let repr = adt::represent_type(self.ccx, operand.ty);
+                            adt::is_discr_signed(&repr)
+                        } else {
+                            operand.ty.is_signed()
+                        };
+
+                        unsafe {
+                            match (r_t_in, r_t_out) {
+                                (CastTy::Int(_), CastTy::Int(_)) => {
+                                    let s = signed as llvm::Bool;
+                                    llvm::LLVMConstIntCast(llval, ll_t_out.to_ref(), s)
+                                }
+                                (CastTy::Int(_), CastTy::Float) => {
+                                    if signed {
+                                        llvm::LLVMConstSIToFP(llval, ll_t_out.to_ref())
+                                    } else {
+                                        llvm::LLVMConstUIToFP(llval, ll_t_out.to_ref())
+                                    }
+                                }
+                                (CastTy::Float, CastTy::Float) => {
+                                    llvm::LLVMConstFPCast(llval, ll_t_out.to_ref())
+                                }
+                                (CastTy::Float, CastTy::Int(IntTy::I)) => {
+                                    llvm::LLVMConstFPToSI(llval, ll_t_out.to_ref())
+                                }
+                                (CastTy::Float, CastTy::Int(_)) => {
+                                    llvm::LLVMConstFPToUI(llval, ll_t_out.to_ref())
+                                }
+                                (CastTy::Ptr(_), CastTy::Ptr(_)) |
+                                (CastTy::FnPtr, CastTy::Ptr(_)) |
+                                (CastTy::RPtr(_), CastTy::Ptr(_)) => {
+                                    consts::ptrcast(llval, ll_t_out)
+                                }
+                                (CastTy::Int(_), CastTy::Ptr(_)) => {
+                                    llvm::LLVMConstIntToPtr(llval, ll_t_out.to_ref())
+                                }
+                                (CastTy::Ptr(_), CastTy::Int(_)) |
+                                (CastTy::FnPtr, CastTy::Int(_)) => {
+                                    llvm::LLVMConstPtrToInt(llval, ll_t_out.to_ref())
+                                }
+                                _ => bug!("unsupported cast: {:?} to {:?}", operand.ty, cast_ty)
+                            }
+                        }
+                    }
+                    mir::CastKind::Misc => { // Casts from a fat-ptr.
+                        let ll_cast_ty = type_of::immediate_type_of(self.ccx, cast_ty);
+                        let ll_from_ty = type_of::immediate_type_of(self.ccx, operand.ty);
+                        if common::type_is_fat_ptr(tcx, operand.ty) {
+                            let (data_ptr, meta_ptr) = operand.get_fat_ptr();
+                            if common::type_is_fat_ptr(tcx, cast_ty) {
+                                let ll_cft = ll_cast_ty.field_types();
+                                let ll_fft = ll_from_ty.field_types();
+                                let data_cast = consts::ptrcast(data_ptr, ll_cft[0]);
+                                assert_eq!(ll_cft[1].kind(), ll_fft[1].kind());
+                                C_struct(self.ccx, &[data_cast, meta_ptr], false)
+                            } else { // cast to thin-ptr
+                                // Cast of fat-ptr to thin-ptr is an extraction of data-ptr and
+                                // pointer-cast of that pointer to desired pointer type.
+                                consts::ptrcast(data_ptr, ll_cast_ty)
+                            }
+                        } else {
+                            bug!("Unexpected non-FatPtr operand")
+                        }
+                    }
+                };
+                Const::new(val, cast_ty)
+            }
+
+            mir::Rvalue::Ref(_, bk, ref lvalue) => {
+                let tr_lvalue = self.const_lvalue(lvalue, span)?;
+
+                let ty = tr_lvalue.ty;
+                let ref_ty = tcx.mk_ref(tcx.mk_region(ty::ReStatic),
+                    ty::TypeAndMut { ty: ty, mutbl: bk.to_mutbl_lossy() });
+
+                let base = match tr_lvalue.base {
+                    Base::Value(llval) => {
+                        let align = type_of::align_of(self.ccx, ty);
+                        if bk == mir::BorrowKind::Mut {
+                            consts::addr_of_mut(self.ccx, llval, align, "ref_mut")
+                        } else {
+                            consts::addr_of(self.ccx, llval, align, "ref")
+                        }
+                    }
+                    Base::Str(llval) |
+                    Base::Static(llval) => llval
+                };
+
+                let ptr = if common::type_is_sized(tcx, ty) {
+                    base
+                } else {
+                    C_struct(self.ccx, &[base, tr_lvalue.llextra], false)
+                };
+                Const::new(ptr, ref_ty)
+            }
+
+            mir::Rvalue::Len(ref lvalue) => {
+                let tr_lvalue = self.const_lvalue(lvalue, span)?;
+                Const::new(tr_lvalue.len(self.ccx), tcx.types.usize)
+            }
+
+            mir::Rvalue::BinaryOp(op, ref lhs, ref rhs) => {
+                let lhs = self.const_operand(lhs, span)?;
+                let rhs = self.const_operand(rhs, span)?;
+                let ty = lhs.ty;
+                let binop_ty = self.mir.binop_ty(tcx, op, lhs.ty, rhs.ty);
+                let (lhs, rhs) = (lhs.llval, rhs.llval);
+                assert!(!ty.is_simd());
+                let is_float = ty.is_fp();
+                let signed = ty.is_signed();
+
+                if let (Some(lhs), Some(rhs)) = (to_const_int(lhs, ty, tcx),
+                                                 to_const_int(rhs, ty, tcx)) {
+                    let result = match op {
+                        mir::BinOp::Add => lhs + rhs,
+                        mir::BinOp::Sub => lhs - rhs,
+                        mir::BinOp::Mul => lhs * rhs,
+                        mir::BinOp::Div => lhs / rhs,
+                        mir::BinOp::Rem => lhs % rhs,
+                        mir::BinOp::Shl => lhs << rhs,
+                        mir::BinOp::Shr => lhs >> rhs,
+                        _ => Ok(lhs)
+                    };
+                    consts::const_err(self.ccx, span,
+                                      result.map_err(ErrKind::Math),
+                                      TrueConst::Yes)?;
+                }
+
+                let llval = unsafe {
+                    match op {
+                        mir::BinOp::Add if is_float => llvm::LLVMConstFAdd(lhs, rhs),
+                        mir::BinOp::Add             => llvm::LLVMConstAdd(lhs, rhs),
+
+                        mir::BinOp::Sub if is_float => llvm::LLVMConstFSub(lhs, rhs),
+                        mir::BinOp::Sub             => llvm::LLVMConstSub(lhs, rhs),
+
+                        mir::BinOp::Mul if is_float => llvm::LLVMConstFMul(lhs, rhs),
+                        mir::BinOp::Mul             => llvm::LLVMConstMul(lhs, rhs),
+
+                        mir::BinOp::Div if is_float => llvm::LLVMConstFDiv(lhs, rhs),
+                        mir::BinOp::Div if signed   => llvm::LLVMConstSDiv(lhs, rhs),
+                        mir::BinOp::Div             => llvm::LLVMConstUDiv(lhs, rhs),
+
+                        mir::BinOp::Rem if is_float => llvm::LLVMConstFRem(lhs, rhs),
+                        mir::BinOp::Rem if signed   => llvm::LLVMConstSRem(lhs, rhs),
+                        mir::BinOp::Rem             => llvm::LLVMConstURem(lhs, rhs),
+
+                        mir::BinOp::BitXor => llvm::LLVMConstXor(lhs, rhs),
+                        mir::BinOp::BitAnd => llvm::LLVMConstAnd(lhs, rhs),
+                        mir::BinOp::BitOr  => llvm::LLVMConstOr(lhs, rhs),
+                        mir::BinOp::Shl    => {
+                            let rhs = base::cast_shift_const_rhs(op.to_hir_binop(), lhs, rhs);
+                            llvm::LLVMConstShl(lhs, rhs)
+                        }
+                        mir::BinOp::Shr    => {
+                            let rhs = base::cast_shift_const_rhs(op.to_hir_binop(), lhs, rhs);
+                            if signed { llvm::LLVMConstAShr(lhs, rhs) }
+                            else      { llvm::LLVMConstLShr(lhs, rhs) }
+                        }
+                        mir::BinOp::Eq | mir::BinOp::Ne |
+                        mir::BinOp::Lt | mir::BinOp::Le |
+                        mir::BinOp::Gt | mir::BinOp::Ge => {
+                            if is_float {
+                                let cmp = base::bin_op_to_fcmp_predicate(op.to_hir_binop());
+                                llvm::ConstFCmp(cmp, lhs, rhs)
+                            } else {
+                                let cmp = base::bin_op_to_icmp_predicate(op.to_hir_binop(),
+                                                                         signed);
+                                llvm::ConstICmp(cmp, lhs, rhs)
+                            }
+                        }
+                    }
+                };
+                Const::new(llval, binop_ty)
+            }
+
+            mir::Rvalue::UnaryOp(op, ref operand) => {
+                let operand = self.const_operand(operand, span)?;
+                let lloperand = operand.llval;
+                let llval = match op {
+                    mir::UnOp::Not => {
+                        unsafe {
+                            llvm::LLVMConstNot(lloperand)
+                        }
+                    }
+                    mir::UnOp::Neg => {
+                        if let Some(cval) = to_const_int(lloperand, operand.ty, tcx) {
+                            consts::const_err(self.ccx, span,
+                                              (-cval).map_err(ErrKind::Math),
+                                              TrueConst::Yes)?;
+                        }
+                        let is_float = operand.ty.is_fp();
+                        unsafe {
+                            if is_float {
+                                llvm::LLVMConstFNeg(lloperand)
+                            } else {
+                                llvm::LLVMConstNeg(lloperand)
+                            }
+                        }
+                    }
+                };
+                Const::new(llval, operand.ty)
+            }
+
+            _ => span_bug!(span, "{:?} in constant", rvalue)
+        };
+
+        Ok(val)
+    }
+}
+
+impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
     pub fn trans_constant(&mut self,
                           bcx: &BlockAndBuilder<'bcx, 'tcx>,
                           constant: &mir::Constant<'tcx>)
-                          -> OperandRef<'tcx>
+                          -> Const<'tcx>
     {
         let ty = bcx.monomorphize(&constant.ty);
-        match constant.literal {
+        let result = match constant.literal.clone() {
             mir::Literal::Item { def_id, substs } => {
                 // Shortcut for zero-sized types, including function item
-                // types, which would not work with lookup_const_by_id.
+                // types, which would not work with MirConstContext.
                 if common::type_is_zero_size(bcx.ccx(), ty) {
                     let llty = type_of::type_of(bcx.ccx(), ty);
-                    return OperandRef {
-                        val: OperandValue::Immediate(C_undef(llty)),
-                        ty: ty
-                    };
+                    return Const::new(C_null(llty), ty);
                 }
 
-                let substs = Some(bcx.monomorphize(substs));
-                let expr = lookup_const_by_id(bcx.tcx(), def_id, substs)
-                            .expect("def was const, but lookup_const_by_id failed").0;
-                // FIXME: this is falling back to translating from HIR. This is not easy to fix,
-                // because we would have somehow adapt const_eval to work on MIR rather than HIR.
-                let d = bcx.with_block(|bcx| {
-                    expr::trans(bcx, expr)
-                });
-
-                let datum = d.datum.to_rvalue_datum(d.bcx, "").datum;
-
-                match datum.kind.mode {
-                    datum::RvalueMode::ByValue => {
-                        OperandRef {
-                            ty: datum.ty,
-                            val: OperandValue::Immediate(datum.val)
-                        }
-                    }
-                    datum::RvalueMode::ByRef => self.trans_load(bcx, datum.val, datum.ty)
-                }
+                let substs = bcx.tcx().mk_substs(bcx.monomorphize(substs));
+                let instance = Instance::new(def_id, substs);
+                MirConstContext::trans_def(bcx.ccx(), instance, vec![])
+            }
+            mir::Literal::Promoted { index } => {
+                let mir = &self.mir.promoted[index];
+                MirConstContext::new(bcx.ccx(), mir, bcx.fcx().param_substs, vec![]).trans()
+            }
+            mir::Literal::Value { value } => {
+                Ok(Const::from_constval(bcx.ccx(), value, ty))
             }
-            mir::Literal::Value { ref value } => {
-                self.trans_constval(bcx, value, ty)
+        };
+
+        match result {
+            Ok(v) => v,
+            Err(ConstEvalFailure::Compiletime(_)) => {
+                // We've errored, so we don't have to produce working code.
+                let llty = type_of::type_of(bcx.ccx(), ty);
+                Const::new(C_undef(llty), ty)
+            }
+            Err(ConstEvalFailure::Runtime(err)) => {
+                span_bug!(constant.span,
+                          "MIR constant {:?} results in runtime panic: {}",
+                          constant, err.description())
             }
         }
     }
 }
+
+
+pub fn trans_static_initializer(ccx: &CrateContext, def_id: DefId)
+                                -> Result<ValueRef, ConstEvalFailure> {
+    let instance = Instance::mono(ccx.tcx(), def_id);
+    MirConstContext::trans_def(ccx, instance, vec![]).map(|c| c.llval)
+}
diff --git a/src/librustc_trans/mir/lvalue.rs b/src/librustc_trans/mir/lvalue.rs
index 695806aa82cee..e57a5cbc5b8b9 100644
--- a/src/librustc_trans/mir/lvalue.rs
+++ b/src/librustc_trans/mir/lvalue.rs
@@ -16,7 +16,7 @@ use abi;
 use adt;
 use base;
 use builder::Builder;
-use common::{self, BlockAndBuilder, C_uint};
+use common::{self, BlockAndBuilder, CrateContext, C_uint};
 use consts;
 use machine;
 use mir::drop;
@@ -56,6 +56,18 @@ impl<'tcx> LvalueRef<'tcx> {
         }
         LvalueRef::new_sized(lltemp, LvalueTy::from_ty(ty))
     }
+
+    pub fn len<'a>(&self, ccx: &CrateContext<'a, 'tcx>) -> ValueRef {
+        let ty = self.ty.to_ty(ccx.tcx());
+        match ty.sty {
+            ty::TyArray(_, n) => common::C_uint(ccx, n),
+            ty::TySlice(_) | ty::TyStr => {
+                assert!(self.llextra != ptr::null_mut());
+                self.llextra
+            }
+            _ => bug!("unexpected type `{}` in LvalueRef::len", ty)
+        }
+    }
 }
 
 pub fn get_meta(b: &Builder, fat_ptr: ValueRef) -> ValueRef {
@@ -71,20 +83,6 @@ pub fn load_fat_ptr(b: &Builder, fat_ptr: ValueRef) -> (ValueRef, ValueRef) {
 }
 
 impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
-    pub fn lvalue_len(&mut self,
-                      bcx: &BlockAndBuilder<'bcx, 'tcx>,
-                      lvalue: LvalueRef<'tcx>)
-                      -> ValueRef {
-        match lvalue.ty.to_ty(bcx.tcx()).sty {
-            ty::TyArray(_, n) => common::C_uint(bcx.ccx(), n),
-            ty::TySlice(_) | ty::TyStr => {
-                assert!(lvalue.llextra != ptr::null_mut());
-                lvalue.llextra
-            }
-            _ => bug!("unexpected type in lvalue_len"),
-        }
-    }
-
     pub fn trans_lvalue(&mut self,
                         bcx: &BlockAndBuilder<'bcx, 'tcx>,
                         lvalue: &mir::Lvalue<'tcx>)
@@ -190,7 +188,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                                                          from_end: true,
                                                          min_length: _ } => {
                         let lloffset = C_uint(bcx.ccx(), offset);
-                        let lllen = self.lvalue_len(bcx, tr_base);
+                        let lllen = tr_base.len(bcx.ccx());
                         let llindex = bcx.sub(lllen, lloffset);
                         project_index(self.prepare_index(bcx, llindex))
                     }
diff --git a/src/librustc_trans/mir/mod.rs b/src/librustc_trans/mir/mod.rs
index 75d9ca32a21de..b98e04e51c007 100644
--- a/src/librustc_trans/mir/mod.rs
+++ b/src/librustc_trans/mir/mod.rs
@@ -31,6 +31,8 @@ use basic_block::BasicBlock;
 
 use rustc_data_structures::bitvec::BitVector;
 
+pub use self::constant::trans_static_initializer;
+
 use self::lvalue::{LvalueRef, get_dataptr, get_meta};
 use rustc_mir::traversal;
 
diff --git a/src/librustc_trans/mir/operand.rs b/src/librustc_trans/mir/operand.rs
index c15d6cd5b2440..fc726a3474f7e 100644
--- a/src/librustc_trans/mir/operand.rs
+++ b/src/librustc_trans/mir/operand.rs
@@ -140,7 +140,14 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
             }
 
             mir::Operand::Constant(ref constant) => {
-                self.trans_constant(bcx, constant)
+                let val = self.trans_constant(bcx, constant);
+                let operand = val.to_operand(bcx.ccx());
+                if let OperandValue::Ref(ptr) = operand.val {
+                    // If this is a OperandValue::Ref to an immediate constant, load it.
+                    self.trans_load(bcx, ptr, operand.ty)
+                } else {
+                    operand
+                }
             }
         }
     }
diff --git a/src/librustc_trans/mir/rvalue.rs b/src/librustc_trans/mir/rvalue.rs
index 67d7f44cbbf41..1236100a4d51c 100644
--- a/src/librustc_trans/mir/rvalue.rs
+++ b/src/librustc_trans/mir/rvalue.rs
@@ -11,8 +11,6 @@
 use llvm::ValueRef;
 use rustc::ty::{self, Ty};
 use rustc::ty::cast::{CastTy, IntTy};
-use middle::const_val::ConstVal;
-use rustc_const_math::ConstInt;
 use rustc::mir::repr as mir;
 
 use asm;
@@ -100,8 +98,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
 
             mir::Rvalue::Repeat(ref elem, ref count) => {
                 let tr_elem = self.trans_operand(&bcx, elem);
-                let count = ConstVal::Integral(ConstInt::Usize(count.value));
-                let size = self.trans_constval(&bcx, &count, bcx.tcx().types.usize).immediate();
+                let size = count.value.as_u64(bcx.tcx().sess.target.uint_type);
+                let size = C_uint(bcx.ccx(), size);
                 let base = get_dataptr(&bcx, dest.llval);
                 let bcx = bcx.map_block(|block| {
                     tvec::iter_vec_raw(block, base, tr_elem.ty, size, |block, llslot, _| {
@@ -405,7 +403,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
             mir::Rvalue::Len(ref lvalue) => {
                 let tr_lvalue = self.trans_lvalue(&bcx, lvalue);
                 let operand = OperandRef {
-                    val: OperandValue::Immediate(self.lvalue_len(&bcx, tr_lvalue)),
+                    val: OperandValue::Immediate(tr_lvalue.len(bcx.ccx())),
                     ty: bcx.tcx().types.usize,
                 };
                 (bcx, operand)
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index e269475d1e2c5..7f01821b00433 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -266,7 +266,10 @@ declare_features! (
     (active, specialization, "1.7.0", Some(31844)),
 
     // pub(restricted) visibilities (RFC 1422)
-    (active, pub_restricted, "1.9.0", Some(32409))
+    (active, pub_restricted, "1.9.0", Some(32409)),
+
+     // Allow Drop types in statics/const functions (RFC 1440)
+    (active, drop_types_in_const, "1.9.0", Some(33156))
 );
 
 declare_features! (
diff --git a/src/rustc/Cargo.lock b/src/rustc/Cargo.lock
index 1fa4d5398f489..9dc1899e5a092 100644
--- a/src/rustc/Cargo.lock
+++ b/src/rustc/Cargo.lock
@@ -220,6 +220,7 @@ dependencies = [
  "log 0.0.0",
  "rustc 0.0.0",
  "rustc_back 0.0.0",
+ "rustc_bitflags 0.0.0",
  "rustc_const_eval 0.0.0",
  "rustc_const_math 0.0.0",
  "rustc_data_structures 0.0.0",
@@ -233,6 +234,7 @@ dependencies = [
  "log 0.0.0",
  "rustc 0.0.0",
  "rustc_const_eval 0.0.0",
+ "rustc_const_math 0.0.0",
  "syntax 0.0.0",
 ]
 
@@ -278,6 +280,7 @@ version = "0.0.0"
 dependencies = [
  "log 0.0.0",
  "rustc 0.0.0",
+ "serialize 0.0.0",
  "syntax 0.0.0",
 ]
 
diff --git a/src/test/compile-fail/array_const_index-0.rs b/src/test/compile-fail/array_const_index-0.rs
index 63a5cf65e3674..1134dbfd1c46c 100644
--- a/src/test/compile-fail/array_const_index-0.rs
+++ b/src/test/compile-fail/array_const_index-0.rs
@@ -8,8 +8,9 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-static A: &'static [i32] = &[];
-static B: i32 = (&A)[1]; //~ ERROR: const index-expr is out of bounds
+const A: &'static [i32] = &[];
+const B: i32 = (&A)[1];
+//~^ ERROR: array index out of bounds
 
 fn main() {
     let _ = B;
diff --git a/src/test/compile-fail/array_const_index-1.rs b/src/test/compile-fail/array_const_index-1.rs
index 1f56cd8e87512..e59895cda442a 100644
--- a/src/test/compile-fail/array_const_index-1.rs
+++ b/src/test/compile-fail/array_const_index-1.rs
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 const A: [i32; 0] = [];
-const B: i32 = A[1]; //~ ERROR: const index-expr is out of bounds
+const B: i32 = A[1]; //~ ERROR: array index out of bounds
 
 fn main() {
     let _ = B;
diff --git a/src/test/compile-fail/auxiliary/pub_static_array.rs b/src/test/compile-fail/auxiliary/pub_static_array.rs
index 4419a5ae83cfa..7248d0e543bdb 100644
--- a/src/test/compile-fail/auxiliary/pub_static_array.rs
+++ b/src/test/compile-fail/auxiliary/pub_static_array.rs
@@ -8,4 +8,4 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-pub static ARRAY: &'static [u8] = &[1];
+pub static ARRAY: [u8; 1] = [1];
diff --git a/src/test/compile-fail/check-static-immutable-mut-slices.rs b/src/test/compile-fail/check-static-immutable-mut-slices.rs
index 1804b9e04c2c9..370cfe9d55012 100644
--- a/src/test/compile-fail/check-static-immutable-mut-slices.rs
+++ b/src/test/compile-fail/check-static-immutable-mut-slices.rs
@@ -12,5 +12,6 @@
 
 static TEST: &'static mut [isize] = &mut [];
 //~^ ERROR references in statics may only refer to immutable values
+//~^^ ERROR references in statics may only refer to immutable values
 
 pub fn main() { }
diff --git a/src/test/compile-fail/check-static-values-constraints.rs b/src/test/compile-fail/check-static-values-constraints.rs
index c3a1de11752f8..df22e2ea4de13 100644
--- a/src/test/compile-fail/check-static-values-constraints.rs
+++ b/src/test/compile-fail/check-static-values-constraints.rs
@@ -37,7 +37,7 @@ static STATIC2: SafeEnum = SafeEnum::Variant2(0);
 
 // This one should fail
 static STATIC3: SafeEnum = SafeEnum::Variant3(WithDtor);
-//~^ ERROR statics are not allowed to have destructors
+//~^ ERROR destructors in statics are an unstable feature
 
 
 // This enum will be used to test that variants
@@ -54,9 +54,9 @@ impl Drop for UnsafeEnum {
 
 
 static STATIC4: UnsafeEnum = UnsafeEnum::Variant5;
-//~^ ERROR statics are not allowed to have destructors
+//~^ ERROR destructors in statics are an unstable feature
 static STATIC5: UnsafeEnum = UnsafeEnum::Variant6(0);
-//~^ ERROR statics are not allowed to have destructors
+//~^ ERROR destructors in statics are an unstable feature
 
 
 struct SafeStruct {
@@ -71,7 +71,7 @@ static STATIC6: SafeStruct = SafeStruct{field1: SafeEnum::Variant1, field2: Safe
 // field2 has an unsafe value, hence this should fail
 static STATIC7: SafeStruct = SafeStruct{field1: SafeEnum::Variant1,
                                         field2: SafeEnum::Variant3(WithDtor)};
-//~^ ERROR statics are not allowed to have destructors
+//~^ ERROR destructors in statics are an unstable feature
 
 // Test variadic constructor for structs. The base struct should be examined
 // as well as every field present in the constructor.
@@ -84,7 +84,7 @@ static STATIC8: SafeStruct = SafeStruct{field1: SafeEnum::Variant1,
 static STATIC9: SafeStruct = SafeStruct{field1: SafeEnum::Variant1,
                                         ..SafeStruct{field1: SafeEnum::Variant3(WithDtor),
                                                      field2: SafeEnum::Variant1}};
-//~^^ ERROR statics are not allowed to have destructors
+//~^^ ERROR destructors in statics are an unstable feature
 
 struct UnsafeStruct;
 
@@ -94,7 +94,7 @@ impl Drop for UnsafeStruct {
 
 // Types with destructors are not allowed for statics
 static STATIC10: UnsafeStruct = UnsafeStruct;
-//~^ ERROR statics are not allowed to have destructor
+//~^ ERROR destructors in statics are an unstable feature
 
 struct MyOwned;
 
@@ -105,19 +105,19 @@ static STATIC11: Box<MyOwned> = box MyOwned;
 // to have types with destructors
 // These should fail
 static mut STATIC12: UnsafeStruct = UnsafeStruct;
-//~^ ERROR mutable statics are not allowed to have destructors
-//~^^ ERROR statics are not allowed to have destructors
+//~^ ERROR destructors in statics are an unstable feature
+//~^^ ERROR destructors in statics are an unstable feature
 
 static mut STATIC13: SafeStruct = SafeStruct{field1: SafeEnum::Variant1,
-//~^ ERROR mutable statics are not allowed to have destructors
+//~^ ERROR destructors in statics are an unstable feature
                                              field2: SafeEnum::Variant3(WithDtor)};
-//~^ ERROR: statics are not allowed to have destructors
+//~^ ERROR: destructors in statics are an unstable feature
 
 static mut STATIC14: SafeStruct = SafeStruct {
-//~^ ERROR mutable statics are not allowed to have destructors
+//~^ ERROR destructors in statics are an unstable feature
     field1: SafeEnum::Variant1,
     field2: SafeEnum::Variant4("str".to_string())
-//~^ ERROR method calls in statics are limited to constant inherent methods
+//~^ ERROR calls in statics are limited to constant functions
 };
 
 static STATIC15: &'static [Box<MyOwned>] = &[
@@ -131,7 +131,7 @@ static STATIC16: (&'static Box<MyOwned>, &'static Box<MyOwned>) = (
 );
 
 static mut STATIC17: SafeEnum = SafeEnum::Variant1;
-//~^ ERROR mutable statics are not allowed to have destructors
+//~^ ERROR destructors in statics are an unstable feature
 
 static STATIC19: Box<isize> =
     box 3;
@@ -140,4 +140,5 @@ static STATIC19: Box<isize> =
 pub fn main() {
     let y = { static x: Box<isize> = box 3; x };
     //~^ ERROR allocations are not allowed in statics
+    //~^^ ERROR cannot move out of static item
 }
diff --git a/src/test/compile-fail/const-block-non-item-statement.rs b/src/test/compile-fail/const-block-non-item-statement.rs
index 5ccfb1ddec78a..edb85023c9ba7 100644
--- a/src/test/compile-fail/const-block-non-item-statement.rs
+++ b/src/test/compile-fail/const-block-non-item-statement.rs
@@ -21,6 +21,20 @@ const C: usize = { foo!(); 2 };
 
 const D: usize = { let x = 4; 2 };
 //~^ ERROR: blocks in constants are limited to items and tail expressions
+//~^^ ERROR: blocks in constants are limited to items and tail expressions
+
+enum Foo {
+    Bar = { let x = 1; 3 }
+    //~^ ERROR: blocks in constants are limited to items and tail expressions
+    //~^^ ERROR: blocks in constants are limited to items and tail expressions
+}
+
+type Array = [u32; {  let x = 2; 5 }];
+//~^ ERROR: blocks in constants are limited to items and tail expressions
+//~^^ ERROR: blocks in constants are limited to items and tail expressions
 
 pub fn main() {
+    let _: Array = [0; { let x = 3; 5 }];
+    //~^ ERROR: blocks in constants are limited to items and tail expressions
+    //~^^ ERROR: blocks in constants are limited to items and tail expressions
 }
diff --git a/src/test/compile-fail/const-err.rs b/src/test/compile-fail/const-err.rs
index 3fb9a3f236ced..a25255c010caf 100644
--- a/src/test/compile-fail/const-err.rs
+++ b/src/test/compile-fail/const-err.rs
@@ -19,6 +19,11 @@ fn black_box<T>(_: T) {
     unimplemented!()
 }
 
+// Make sure that the two uses get two errors.
+const FOO: u8 = [5u8][1];
+//~^ ERROR array index out of bounds
+//~^^ ERROR array index out of bounds
+
 #[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
 fn main() {
     let a = -std::i8::MIN;
@@ -31,9 +36,11 @@ fn main() {
     let d = 42u8 - (42u8 + 1);
     //~^ WARN attempted to subtract with overflow
     let _e = [5u8][1];
-    //~^ ERROR const index-expr is out of bounds
+    //~^ WARN array index out of bounds
     black_box(a);
     black_box(b);
     black_box(c);
     black_box(d);
+
+    black_box((FOO, FOO));
 }
diff --git a/src/test/compile-fail/const-fn-destructuring-arg.rs b/src/test/compile-fail/const-fn-destructuring-arg.rs
index 1642c04106723..c3d5975fe01f6 100644
--- a/src/test/compile-fail/const-fn-destructuring-arg.rs
+++ b/src/test/compile-fail/const-fn-destructuring-arg.rs
@@ -13,6 +13,11 @@
 #![feature(const_fn)]
 
 // no destructuring
-const fn i((a, b): (u32, u32)) -> u32 { a + b } //~ ERROR: E0022
+const fn i((
+            a, //~ ERROR: E0022
+            b  //~ ERROR: E0022
+           ): (u32, u32)) -> u32 {
+    a + b
+}
 
 fn main() {}
diff --git a/src/test/compile-fail/const-fn-error.rs b/src/test/compile-fail/const-fn-error.rs
index cb6f2d0215fdb..45a00de48e712 100644
--- a/src/test/compile-fail/const-fn-error.rs
+++ b/src/test/compile-fail/const-fn-error.rs
@@ -8,19 +8,16 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// test that const fn signature and body errors are checked
-// even in array lengths, which are evaluated before check_const
-
 #![feature(const_fn)]
 
 const X : usize = 2;
 
 const fn f(x: usize) -> usize {
-    let mut sum = 0; //~ ERROR: E0016
-    for i in 0..x { //~ ERROR: E0016
+    let mut sum = 0;
+    for i in 0..x {
         sum += i;
     }
-    sum
+    sum //~ ERROR: E0250
 }
 
 #[allow(unused_variables)]
diff --git a/src/test/compile-fail/const-fn-not-safe-for-const.rs b/src/test/compile-fail/const-fn-not-safe-for-const.rs
index f8381978dc7bd..48877a60d2504 100644
--- a/src/test/compile-fail/const-fn-not-safe-for-const.rs
+++ b/src/test/compile-fail/const-fn-not-safe-for-const.rs
@@ -29,7 +29,7 @@ static Y: u32 = 0;
 const fn get_Y() -> u32 {
     Y
         //~^ ERROR E0013
-        //~| ERROR cannot refer to other statics by value
+        //~| ERROR cannot refer to statics by value
 }
 
 const fn get_Y_addr() -> &'static u32 {
@@ -37,5 +37,11 @@ const fn get_Y_addr() -> &'static u32 {
         //~^ ERROR E0013
 }
 
+const fn get() -> u32 {
+    let x = 22; //~ ERROR E0016
+    let y = 44; //~ ERROR E0016
+    x + y
+}
+
 fn main() {
 }
diff --git a/src/test/compile-fail/const-fn-not-safe-for-const2.rs b/src/test/compile-fail/const-fn-not-safe-for-const2.rs
deleted file mode 100644
index a053847e88294..0000000000000
--- a/src/test/compile-fail/const-fn-not-safe-for-const2.rs
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// Test that we can't call random fns in a const fn or do other bad things.
-
-#![feature(const_fn)]
-
-use std::mem::transmute;
-
-fn random() -> u32 { 0 }
-
-const fn sub(x: &u32) -> usize {
-    unsafe { transmute(x) }
-}
-
-const fn sub1() -> u32 {
-    random()
-}
-
-static Y: u32 = 0;
-
-const fn get_Y() -> u32 {
-    Y
-}
-
-const fn get_Y_addr() -> &'static u32 {
-    &Y
-}
-
-const fn get() -> u32 {
-    let x = 22; //~ ERROR E0016
-    let y = 44; //~ ERROR E0016
-    x + y
-}
-
-fn main() {
-}
diff --git a/src/test/compile-fail/const-slice-oob.rs b/src/test/compile-fail/const-slice-oob.rs
index 519c4917c71b3..b50468c33fd8b 100644
--- a/src/test/compile-fail/const-slice-oob.rs
+++ b/src/test/compile-fail/const-slice-oob.rs
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 const FOO: &'static[u32] = &[1, 2, 3];
-const BAR: u32 = FOO[5]; //~ ERROR const index-expr is out of bounds
+const BAR: u32 = FOO[5]; //~ ERROR array index out of bounds
 
 fn main() {
     let _ = BAR;
diff --git a/src/test/compile-fail/feature-gate-negate-unsigned.rs b/src/test/compile-fail/feature-gate-negate-unsigned.rs
index 93e09c6d8d2d2..98cc2fc0c3e0c 100644
--- a/src/test/compile-fail/feature-gate-negate-unsigned.rs
+++ b/src/test/compile-fail/feature-gate-negate-unsigned.rs
@@ -16,7 +16,9 @@ impl std::ops::Neg for S {
     fn neg(self) -> u32 { 0 }
 }
 
-const _MAX: usize = -1;
+// FIXME(eddyb) move this back to a `-1` literal when
+// MIR building stops eagerly erroring in that case.
+const _MAX: usize = -(2 - 1);
 //~^ WARN unary negation of unsigned integer
 //~| ERROR unary negation of unsigned integer
 //~| HELP use a cast or the `!` operator
diff --git a/src/test/compile-fail/issue-17718-borrow-interior.rs b/src/test/compile-fail/issue-17718-borrow-interior.rs
index d33c12668f288..31352c57f1bc3 100644
--- a/src/test/compile-fail/issue-17718-borrow-interior.rs
+++ b/src/test/compile-fail/issue-17718-borrow-interior.rs
@@ -17,7 +17,8 @@ static C: &'static usize = &(A.a);
 
 static D: [usize; 1] = [1];
 static E: usize = D[0];
-//~^ ERROR: cannot refer to other statics by value
+//~^ ERROR: cannot refer to the interior of another static
+//~^^ ERROR: cannot refer to other statics by value
 static F: &'static usize = &D[0];
 //~^ ERROR: cannot refer to the interior of another static
 
diff --git a/src/test/compile-fail/issue-17718-const-bad-values.rs b/src/test/compile-fail/issue-17718-const-bad-values.rs
index 6ee869d65a8f2..af356588ed9e7 100644
--- a/src/test/compile-fail/issue-17718-const-bad-values.rs
+++ b/src/test/compile-fail/issue-17718-const-bad-values.rs
@@ -10,10 +10,13 @@
 
 const C1: &'static mut [usize] = &mut [];
 //~^ ERROR: references in constants may only refer to immutable values
+//~| ERROR: references in constants may only refer to immutable values
 
 static mut S: usize = 3;
-const C2: &'static mut usize = &mut S;
-//~^ ERROR: constants cannot refer to other statics
-//~^^ ERROR: references in constants may only refer to immutable values
+const C2: &'static mut usize = unsafe { &mut S };
+//~^ ERROR: constants cannot refer to statics
+//~| ERROR: references in constants may only refer to immutable values
+//~| ERROR: references in constants may only refer to immutable values
+//~| ERROR: references in constants may only refer to immutable values
 
 fn main() {}
diff --git a/src/test/compile-fail/issue-17718-references.rs b/src/test/compile-fail/issue-17718-references.rs
index 9d8b116f56982..c159168030b8c 100644
--- a/src/test/compile-fail/issue-17718-references.rs
+++ b/src/test/compile-fail/issue-17718-references.rs
@@ -14,19 +14,19 @@ const C: usize = 1;
 static S: usize = 1;
 
 const T1: &'static usize = &C;
-const T2: &'static usize = &S; //~ ERROR: constants cannot refer to other statics
+const T2: &'static usize = &S; //~ ERROR: constants cannot refer to statics
 static T3: &'static usize = &C;
 static T4: &'static usize = &S;
 
 const T5: usize = C;
-const T6: usize = S; //~ ERROR: constants cannot refer to other statics
-//~^ cannot refer to other statics
+const T6: usize = S; //~ ERROR: constants cannot refer to statics
+//~^ cannot refer to statics
 static T7: usize = C;
 static T8: usize = S; //~ ERROR: cannot refer to other statics by value
 
 const T9: Struct = Struct { a: C };
-const T10: Struct = Struct { a: S }; //~ ERROR: cannot refer to other statics by value
-//~^ ERROR: constants cannot refer to other statics
+const T10: Struct = Struct { a: S }; //~ ERROR: cannot refer to statics by value
+//~^ ERROR: constants cannot refer to statics
 static T11: Struct = Struct { a: C };
 static T12: Struct = Struct { a: S }; //~ ERROR: cannot refer to other statics by value
 
diff --git a/src/test/compile-fail/issue-18118-2.rs b/src/test/compile-fail/issue-18118-2.rs
index 1fbf48f5b2125..6efe532b5fdf6 100644
--- a/src/test/compile-fail/issue-18118-2.rs
+++ b/src/test/compile-fail/issue-18118-2.rs
@@ -12,6 +12,6 @@ pub fn main() {
     const z: &'static isize = {
         static p: isize = 3;
         &p
-        //~^ ERROR constants cannot refer to other statics, insert an intermediate constant instead
+        //~^ ERROR constants cannot refer to statics, use a constant instead
     };
 }
diff --git a/src/test/compile-fail/issue-18118.rs b/src/test/compile-fail/issue-18118.rs
index 9c8ed314d22d3..3afb34f037bcc 100644
--- a/src/test/compile-fail/issue-18118.rs
+++ b/src/test/compile-fail/issue-18118.rs
@@ -10,6 +10,7 @@
 
 pub fn main() {
     const z: &'static isize = {
+        //~^ ERROR blocks in constants are limited to items and tail expressions
         let p = 3;
         //~^ ERROR blocks in constants are limited to items and tail expressions
         &p
diff --git a/src/test/compile-fail/issue-25901.rs b/src/test/compile-fail/issue-25901.rs
index 3254f0b2aa9bd..72fb2a682ebe2 100644
--- a/src/test/compile-fail/issue-25901.rs
+++ b/src/test/compile-fail/issue-25901.rs
@@ -11,7 +11,8 @@
 struct A;
 struct B;
 
-static S: &'static B = &A; //~ ERROR user-defined dereference operators
+static S: &'static B = &A;
+//~^ ERROR calls in statics are limited to constant functions
 
 use std::ops::Deref;
 
diff --git a/src/test/compile-fail/issue-27895.rs b/src/test/compile-fail/issue-27895.rs
index 959818b49c928..3b3abc94a4900 100644
--- a/src/test/compile-fail/issue-27895.rs
+++ b/src/test/compile-fail/issue-27895.rs
@@ -14,8 +14,7 @@ fn main() {
 
     match i {
         0...index => println!("winner"),
-        //~^ ERROR paths in constants may only refer to constants or functions
-        //~| ERROR non-constant path in constant expression
+        //~^ ERROR non-constant path in constant expression
         _ => println!("hello"),
     }
 }
diff --git a/src/test/compile-fail/issue-28113.rs b/src/test/compile-fail/issue-28113.rs
index c5c4fb070178a..5c697b69c80b1 100644
--- a/src/test/compile-fail/issue-28113.rs
+++ b/src/test/compile-fail/issue-28113.rs
@@ -9,7 +9,8 @@
 // except according to those terms.
 
 const X: u8 =
-    || -> u8 { 5 }() //~ ERROR function calls in constants are limited
+    || -> u8 { 5 }()
+    //~^ ERROR calls in constants are limited to constant functions
 ;
 
 fn main() {}
diff --git a/src/test/compile-fail/issue-9243.rs b/src/test/compile-fail/issue-9243.rs
index 7424a45d04405..58bdff6c0414b 100644
--- a/src/test/compile-fail/issue-9243.rs
+++ b/src/test/compile-fail/issue-9243.rs
@@ -10,11 +10,12 @@
 
 // Regression test for issue 9243
 
-struct Test {
+pub struct Test {
     mem: isize,
 }
 
-pub static g_test: Test = Test {mem: 0}; //~ ERROR statics are not allowed to have destructors
+pub static g_test: Test = Test {mem: 0};
+//~^ ERROR destructors in statics are an unstable feature
 
 impl Drop for Test {
     fn drop(&mut self) {}
diff --git a/src/test/compile-fail/non-constant-in-const-path.rs b/src/test/compile-fail/non-constant-in-const-path.rs
index 124a2ffc18541..ee88168515d39 100644
--- a/src/test/compile-fail/non-constant-in-const-path.rs
+++ b/src/test/compile-fail/non-constant-in-const-path.rs
@@ -12,7 +12,6 @@ fn main() {
     let x = 0;
     match 1 {
         0 ... x => {}
-        //~^ ERROR non-constant path in constant expr
-        //~| ERROR paths in constants may only refer to constants or functions
+        //~^ ERROR non-constant path in constant expression
     };
 }
diff --git a/src/test/compile-fail/static-array-across-crate.rs b/src/test/compile-fail/static-array-across-crate.rs
index 04a731e847ad3..d101432f6d124 100644
--- a/src/test/compile-fail/static-array-across-crate.rs
+++ b/src/test/compile-fail/static-array-across-crate.rs
@@ -17,4 +17,11 @@ use array::ARRAY;
 static X: &'static u8 = &ARRAY[0];
 //~^ ERROR: cannot refer to the interior of another static, use a constant
 
+static Y: &'static u8 = &(&ARRAY)[0];
+//~^ ERROR: cannot refer to the interior of another static, use a constant
+
+static Z: u8 = (&ARRAY)[0];
+//~^ ERROR: cannot refer to the interior of another static, use a constant
+//~^^ ERROR: cannot refer to other statics by value
+
 pub fn main() {}
diff --git a/src/test/compile-fail/static-mut-not-constant.rs b/src/test/compile-fail/static-mut-not-constant.rs
index e3bb01e697078..9b83c42609aa5 100644
--- a/src/test/compile-fail/static-mut-not-constant.rs
+++ b/src/test/compile-fail/static-mut-not-constant.rs
@@ -12,6 +12,6 @@
 
 static mut a: Box<isize> = box 3;
 //~^ ERROR allocations are not allowed in statics
-//~^^ ERROR mutable statics are not allowed to have boxes
+//~^^ ERROR destructors in statics are an unstable feature
 
 fn main() {}
diff --git a/src/test/run-pass-fulldeps/auxiliary/dummy_mir_pass.rs b/src/test/run-pass-fulldeps/auxiliary/dummy_mir_pass.rs
index b5234af937b0c..600df1c778bef 100644
--- a/src/test/run-pass-fulldeps/auxiliary/dummy_mir_pass.rs
+++ b/src/test/run-pass-fulldeps/auxiliary/dummy_mir_pass.rs
@@ -18,7 +18,7 @@ extern crate rustc_plugin;
 extern crate rustc_const_math;
 extern crate syntax;
 
-use rustc::mir::transform::{self, MirPass};
+use rustc::mir::transform::{self, MirPass, MirSource};
 use rustc::mir::repr::{Mir, Literal};
 use rustc::mir::visit::MutVisitor;
 use rustc::ty;
@@ -26,13 +26,11 @@ use rustc::middle::const_val::ConstVal;
 use rustc_const_math::ConstInt;
 use rustc_plugin::Registry;
 
-use syntax::ast::NodeId;
-
 struct Pass;
 
 impl transform::Pass for Pass {}
 impl<'tcx> MirPass<'tcx> for Pass {
-    fn run_pass(&mut self, _: &ty::TyCtxt<'tcx>, _: NodeId, mir: &mut Mir<'tcx>) {
+    fn run_pass(&mut self, _: &ty::TyCtxt<'tcx>, _: MirSource, mir: &mut Mir<'tcx>) {
         Visitor.visit_mir(mir)
     }
 }
diff --git a/src/test/run-pass/const-autoderef.rs b/src/test/run-pass/const-autoderef.rs
index 69173e35e2ce3..6b3970e4f1997 100644
--- a/src/test/run-pass/const-autoderef.rs
+++ b/src/test/run-pass/const-autoderef.rs
@@ -9,10 +9,10 @@
 // except according to those terms.
 
 
-static A: [u8; 1] = ['h' as u8];
-static B: u8 = (&A)[0];
-static C: &'static &'static &'static &'static [u8; 1] = & & & &A;
-static D: u8 = (&C)[0];
+const A: [u8; 1] = ['h' as u8];
+const B: u8 = (&A)[0];
+const C: &'static &'static &'static &'static [u8; 1] = & & & &A;
+const D: u8 = (&C)[0];
 
 pub fn main() {
     assert_eq!(B, A[0]);
diff --git a/src/test/run-pass/const-str-ptr.rs b/src/test/run-pass/const-str-ptr.rs
index 1736ab5bb82c8..f58bf4fc39fec 100644
--- a/src/test/run-pass/const-str-ptr.rs
+++ b/src/test/run-pass/const-str-ptr.rs
@@ -8,17 +8,12 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(rustc_attrs)]
-
-// ignore-pretty : (#23623) problems when  ending with // comments
-
 use std::{str, string};
 
 const A: [u8; 2] = ['h' as u8, 'i' as u8];
 const B: &'static [u8; 2] = &A;
 const C: *const u8 = B as *const u8;
 
-#[rustc_no_mir] // FIXME #27840 MIR can't do rvalue promotion yet.
 pub fn main() {
     unsafe {
         let foo = &A as *const u8;
diff --git a/src/test/run-pass/mir_constval_adts.rs b/src/test/run-pass/mir_constval_adts.rs
index 8a1f68dbea3ee..4e9c0bce646e6 100644
--- a/src/test/run-pass/mir_constval_adts.rs
+++ b/src/test/run-pass/mir_constval_adts.rs
@@ -14,6 +14,7 @@ struct Point {
     _x: i32,
     _y: i32,
 }
+
 const STRUCT: Point = Point { _x: 42, _y: 42 };
 const TUPLE1: (i32, i32) = (42, 42);
 const TUPLE2: (&'static str, &'static str) = ("hello","world");
@@ -26,7 +27,19 @@ fn mir() -> (Point, (i32, i32), (&'static str, &'static str)){
     (struct1, tuple1, tuple2)
 }
 
+#[derive(PartialEq, Eq, Debug)]
+struct Newtype<T>(T);
+
+const NEWTYPE: Newtype<&'static str> = Newtype("foobar");
+
+#[rustc_mir]
+fn test_promoted_newtype_str_ref() {
+    let x = &NEWTYPE;
+    assert_eq!(x, &Newtype("foobar"));
+}
+
 fn main(){
     assert_eq!(mir(), (STRUCT, TUPLE1, TUPLE2));
+    test_promoted_newtype_str_ref();
 }
 
diff --git a/src/test/run-pass/mir_raw_fat_ptr.rs b/src/test/run-pass/mir_raw_fat_ptr.rs
index c0ba7a76dba48..a632f00d9ee5f 100644
--- a/src/test/run-pass/mir_raw_fat_ptr.rs
+++ b/src/test/run-pass/mir_raw_fat_ptr.rs
@@ -121,7 +121,6 @@ impl<T> Foo for T {
 
 struct S<T:?Sized>(u32, T);
 
-#[rustc_no_mir] // FIXME #27840 MIR can't do rvalue promotion yet.
 fn main() {
     let array = [0,1,2,3,4];
     let array2 = [5,6,7,8,9];
diff --git a/src/test/run-pass/simd-intrinsic-generic-elements.rs b/src/test/run-pass/simd-intrinsic-generic-elements.rs
index ffb9e6072dfe3..5cb57b63ada2c 100644
--- a/src/test/run-pass/simd-intrinsic-generic-elements.rs
+++ b/src/test/run-pass/simd-intrinsic-generic-elements.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(repr_simd, rustc_attrs, platform_intrinsics)]
+#![feature(repr_simd, platform_intrinsics)]
 
 // ignore-pretty : (#23623) problems when  ending with // comments
 
@@ -52,7 +52,6 @@ macro_rules! all_eq {
     }}
 }
 
-#[rustc_no_mir] // FIXME #27840 MIR doesn't handle shuffle constants.
 fn main() {
     let x2 = i32x2(20, 21);
     let x3 = i32x3(30, 31, 32);
diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs
index 245c3992bee0c..a9e6c454ffa6f 100644
--- a/src/tools/compiletest/src/main.rs
+++ b/src/tools/compiletest/src/main.rs
@@ -261,6 +261,11 @@ pub fn run_tests(config: &Config) {
         _ => { /* proceed */ }
     }
 
+    // FIXME(#33435) Avoid spurious failures in codegen-units/partitioning tests.
+    if let Mode::CodegenUnits = config.mode {
+        let _ = fs::remove_dir_all("tmp/partitioning-tests");
+    }
+
     let opts = test_opts(config);
     let tests = make_tests(config);
     // sadly osx needs some file descriptor limits raised for running tests in