about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2016-02-20 12:38:18 +0000
committerbors <bors@rust-lang.org>2016-02-20 12:38:18 +0000
commit6c751e045642376eafe7ec8a2cae6d92995a46b6 (patch)
treeb5096f050a0278e3bc7cc294d0bd43779af71dc8 /src
parentcfabd173724e5e1bae204126261a03d850d86107 (diff)
parentd84658e317ac5b881a46e27204a1cb9f4ac1b691 (diff)
downloadrust-6c751e045642376eafe7ec8a2cae6d92995a46b6.tar.gz
rust-6c751e045642376eafe7ec8a2cae6d92995a46b6.zip
Auto merge of #31474 - arielb1:mir-typeck, r=nikomatsakis
This should stop broken MIR from annoying us when we try to implement things
Diffstat (limited to 'src')
-rw-r--r--src/librustc/middle/expr_use_visitor.rs5
-rw-r--r--src/librustc/middle/mem_categorization.rs1
-rw-r--r--src/librustc/middle/ty/adjustment.rs31
-rw-r--r--src/librustc/middle/ty/context.rs7
-rw-r--r--src/librustc/mir/mir_map.rs14
-rw-r--r--src/librustc/mir/repr.rs19
-rw-r--r--src/librustc/mir/tcx.rs88
-rw-r--r--src/librustc/mir/transform.rs4
-rw-r--r--src/librustc/session/config.rs6
-rw-r--r--src/librustc/util/ppaux.rs3
-rw-r--r--src/librustc_driver/driver.rs20
-rw-r--r--src/librustc_metadata/astencode.rs16
-rw-r--r--src/librustc_mir/build/expr/as_lvalue.rs2
-rw-r--r--src/librustc_mir/build/expr/as_rvalue.rs35
-rw-r--r--src/librustc_mir/build/matches/test.rs3
-rw-r--r--src/librustc_mir/build/matches/util.rs3
-rw-r--r--src/librustc_mir/build/mod.rs5
-rw-r--r--src/librustc_mir/build/scope.rs40
-rw-r--r--src/librustc_mir/hair/cx/expr.rs120
-rw-r--r--src/librustc_mir/hair/cx/pattern.rs17
-rw-r--r--src/librustc_mir/hair/mod.rs8
-rw-r--r--src/librustc_mir/lib.rs1
-rw-r--r--src/librustc_mir/mir_map.rs12
-rw-r--r--src/librustc_mir/transform/clear_dead_blocks.rs81
-rw-r--r--src/librustc_mir/transform/erase_regions.rs13
-rw-r--r--src/librustc_mir/transform/mod.rs2
-rw-r--r--src/librustc_mir/transform/no_landing_pads.rs7
-rw-r--r--src/librustc_mir/transform/simplify_cfg.rs3
-rw-r--r--src/librustc_mir/transform/type_check.rs598
-rw-r--r--src/librustc_passes/consts.rs3
-rw-r--r--src/librustc_trans/trans/consts.rs4
-rw-r--r--src/librustc_trans/trans/expr.rs7
-rw-r--r--src/librustc_trans/trans/mir/block.rs61
-rw-r--r--src/librustc_trans/trans/mir/lvalue.rs2
-rw-r--r--src/librustc_trans/trans/mir/operand.rs98
-rw-r--r--src/librustc_typeck/check/coercion.rs4
-rw-r--r--src/librustc_typeck/check/mod.rs23
-rw-r--r--src/librustc_typeck/check/writeback.rs21
-rw-r--r--src/test/auxiliary/dummy_mir_pass.rs7
-rw-r--r--src/test/run-pass/mir_augmented_assignments.rs187
-rw-r--r--src/test/run-pass/mir_autoderef.rs41
-rw-r--r--src/test/run-pass/mir_struct_with_assoc_ty.rs41
-rw-r--r--src/test/run-pass/mir_trans_calls.rs36
43 files changed, 1509 insertions, 190 deletions
diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs
index 9b315aa46e5..93cc158cd12 100644
--- a/src/librustc/middle/expr_use_visitor.rs
+++ b/src/librustc/middle/expr_use_visitor.rs
@@ -721,10 +721,11 @@ impl<'d,'t,'a,'tcx> ExprUseVisitor<'d,'t,'a,'tcx> {
         if let Some(adjustment) = adj {
             match adjustment {
                 adjustment::AdjustReifyFnPointer |
-                adjustment::AdjustUnsafeFnPointer => {
+                adjustment::AdjustUnsafeFnPointer |
+                adjustment::AdjustMutToConstPointer => {
                     // Creating a closure/fn-pointer or unsizing consumes
                     // the input and stores it into the resulting rvalue.
-                    debug!("walk_adjustment(AdjustReifyFnPointer|AdjustUnsafeFnPointer)");
+                    debug!("walk_adjustment: trivial adjustment");
                     let cmt_unadjusted =
                         return_if_err!(self.mc.cat_expr_unadjusted(expr));
                     self.delegate_consume(expr.id, expr.span, cmt_unadjusted);
diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs
index c16997157bd..fef35764e1c 100644
--- a/src/librustc/middle/mem_categorization.rs
+++ b/src/librustc/middle/mem_categorization.rs
@@ -430,6 +430,7 @@ impl<'t, 'a,'tcx> MemCategorizationContext<'t, 'a, 'tcx> {
 
                     adjustment::AdjustReifyFnPointer |
                     adjustment::AdjustUnsafeFnPointer |
+                    adjustment::AdjustMutToConstPointer |
                     adjustment::AdjustDerefRef(_) => {
                         debug!("cat_expr({:?}): {:?}",
                                adjustment,
diff --git a/src/librustc/middle/ty/adjustment.rs b/src/librustc/middle/ty/adjustment.rs
index 6cab0baa553..afe177fbdcd 100644
--- a/src/librustc/middle/ty/adjustment.rs
+++ b/src/librustc/middle/ty/adjustment.rs
@@ -21,8 +21,9 @@ use rustc_front::hir;
 
 #[derive(Copy, Clone)]
 pub enum AutoAdjustment<'tcx> {
-    AdjustReifyFnPointer,   // go from a fn-item type to a fn-pointer type
-    AdjustUnsafeFnPointer,  // go from a safe fn pointer to an unsafe fn pointer
+    AdjustReifyFnPointer,    // go from a fn-item type to a fn-pointer type
+    AdjustUnsafeFnPointer,   // go from a safe fn pointer to an unsafe fn pointer
+    AdjustMutToConstPointer, // go from a mut raw pointer to a const raw pointer
     AdjustDerefRef(AutoDerefRef<'tcx>),
 }
 
@@ -106,7 +107,8 @@ impl<'tcx> AutoAdjustment<'tcx> {
     pub fn is_identity(&self) -> bool {
         match *self {
             AdjustReifyFnPointer |
-            AdjustUnsafeFnPointer => false,
+            AdjustUnsafeFnPointer |
+            AdjustMutToConstPointer => false,
             AdjustDerefRef(ref r) => r.is_identity(),
         }
     }
@@ -151,7 +153,7 @@ impl<'tcx> ty::TyS<'tcx> {
         return match adjustment {
             Some(adjustment) => {
                 match *adjustment {
-                   AdjustReifyFnPointer => {
+                    AdjustReifyFnPointer => {
                         match self.sty {
                             ty::TyBareFn(Some(_), b) => {
                                 cx.mk_fn(None, b)
@@ -164,17 +166,32 @@ impl<'tcx> ty::TyS<'tcx> {
                         }
                     }
 
-                   AdjustUnsafeFnPointer => {
+                    AdjustUnsafeFnPointer => {
                         match self.sty {
                             ty::TyBareFn(None, b) => cx.safe_to_unsafe_fn_ty(b),
                             ref b => {
                                 cx.sess.bug(
-                                    &format!("AdjustReifyFnPointer adjustment on non-fn-item: \
+                                    &format!("AdjustUnsafeFnPointer adjustment on non-fn-ptr: \
                                              {:?}",
                                             b));
                             }
                         }
-                   }
+                    }
+
+                    AdjustMutToConstPointer => {
+                        match self.sty {
+                            ty::TyRawPtr(mt) => cx.mk_ptr(ty::TypeAndMut {
+                                ty: mt.ty,
+                                mutbl: hir::MutImmutable
+                            }),
+                            ref b => {
+                                cx.sess.bug(
+                                    &format!("AdjustMutToConstPointer on non-raw-ptr: \
+                                             {:?}",
+                                            b));
+                            }
+                        }
+                    }
 
                     AdjustDerefRef(ref adj) => {
                         let mut adjusted_ty = self;
diff --git a/src/librustc/middle/ty/context.rs b/src/librustc/middle/ty/context.rs
index 1d071cd604d..ea6b4df8104 100644
--- a/src/librustc/middle/ty/context.rs
+++ b/src/librustc/middle/ty/context.rs
@@ -131,6 +131,12 @@ pub struct Tables<'tcx> {
     /// equivalents. This table is not used in trans (since regions
     /// are erased there) and hence is not serialized to metadata.
     pub liberated_fn_sigs: NodeMap<ty::FnSig<'tcx>>,
+
+    /// For each FRU expression, record the normalized types of the fields
+    /// of the struct - this is needed because it is non-trivial to
+    /// normalize while preserving regions. This table is used only in
+    /// MIR construction and hence is not serialized to metadata.
+    pub fru_field_types: NodeMap<Vec<Ty<'tcx>>>
 }
 
 impl<'tcx> Tables<'tcx> {
@@ -144,6 +150,7 @@ impl<'tcx> Tables<'tcx> {
             closure_tys: DefIdMap(),
             closure_kinds: DefIdMap(),
             liberated_fn_sigs: NodeMap(),
+            fru_field_types: NodeMap()
         }
     }
 
diff --git a/src/librustc/mir/mir_map.rs b/src/librustc/mir/mir_map.rs
index aa24f6d8979..32e78b04676 100644
--- a/src/librustc/mir/mir_map.rs
+++ b/src/librustc/mir/mir_map.rs
@@ -8,10 +8,12 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use dep_graph::DepNode;
 use util::nodemap::NodeMap;
 use mir::repr::Mir;
 use mir::transform::MirPass;
 use middle::ty;
+use middle::infer;
 
 pub struct MirMap<'tcx> {
     pub map: NodeMap<Mir<'tcx>>,
@@ -19,9 +21,17 @@ pub struct MirMap<'tcx> {
 
 impl<'tcx> MirMap<'tcx> {
     pub fn run_passes(&mut self, passes: &mut [Box<MirPass>], tcx: &ty::ctxt<'tcx>) {
-        for (_, ref mut mir) in &mut self.map {
+        if passes.is_empty() { return; }
+
+        for (&id, mir) in &mut self.map {
+            let did = tcx.map.local_def_id(id);
+            let _task = tcx.dep_graph.in_task(DepNode::MirMapConstruction(did));
+
+            let param_env = ty::ParameterEnvironment::for_item(tcx, id);
+            let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(param_env));
+
             for pass in &mut *passes {
-                pass.run_on_mir(mir, tcx)
+                pass.run_on_mir(mir, &infcx)
             }
         }
     }
diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs
index 783c58469a1..ce7b1ceb355 100644
--- a/src/librustc/mir/repr.rs
+++ b/src/librustc/mir/repr.rs
@@ -45,6 +45,9 @@ pub struct Mir<'tcx> {
     /// values in that it is possible to borrow them and mutate them
     /// through the resulting reference.
     pub temp_decls: Vec<TempDecl<'tcx>>,
+
+    /// A span representing this MIR, for error reporting
+    pub span: Span,
 }
 
 /// where execution begins
@@ -145,7 +148,7 @@ pub enum BorrowKind {
 
 /// A "variable" is a binding declared by the user as part of the fn
 /// decl, a let, etc.
-#[derive(Clone, RustcEncodable, RustcDecodable)]
+#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
 pub struct VarDecl<'tcx> {
     pub mutability: Mutability,
     pub name: Name,
@@ -154,7 +157,7 @@ pub struct VarDecl<'tcx> {
 
 /// A "temp" is a temporary that we place on the stack. They are
 /// anonymous, always mutable, and have only a type.
-#[derive(Clone, RustcEncodable, RustcDecodable)]
+#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
 pub struct TempDecl<'tcx> {
     pub ty: Ty<'tcx>,
 }
@@ -170,7 +173,7 @@ pub struct TempDecl<'tcx> {
 ///
 /// there is only one argument, of type `(i32, u32)`, but two bindings
 /// (`x` and `y`).
-#[derive(Clone, RustcEncodable, RustcDecodable)]
+#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
 pub struct ArgDecl<'tcx> {
     pub ty: Ty<'tcx>,
 }
@@ -499,7 +502,7 @@ pub struct Projection<'tcx, B, V> {
 #[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable)]
 pub enum ProjectionElem<'tcx, V> {
     Deref,
-    Field(Field),
+    Field(Field, Ty<'tcx>),
     Index(V),
 
     /// These indices are generated by slice patterns. Easiest to explain
@@ -550,8 +553,8 @@ impl Field {
 }
 
 impl<'tcx> Lvalue<'tcx> {
-    pub fn field(self, f: Field) -> Lvalue<'tcx> {
-        self.elem(ProjectionElem::Field(f))
+    pub fn field(self, f: Field, ty: Ty<'tcx>) -> Lvalue<'tcx> {
+        self.elem(ProjectionElem::Field(f, ty))
     }
 
     pub fn deref(self) -> Lvalue<'tcx> {
@@ -591,8 +594,8 @@ impl<'tcx> Debug for Lvalue<'tcx> {
                         write!(fmt, "({:?} as {})", data.base, adt_def.variants[index].name),
                     ProjectionElem::Deref =>
                         write!(fmt, "(*{:?})", data.base),
-                    ProjectionElem::Field(field) =>
-                        write!(fmt, "{:?}.{:?}", data.base, field.index()),
+                    ProjectionElem::Field(field, ty) =>
+                        write!(fmt, "({:?}.{:?}: {:?})", data.base, field.index(), ty),
                     ProjectionElem::Index(ref index) =>
                         write!(fmt, "{:?}[{:?}]", data.base, index),
                     ProjectionElem::ConstantIndex { offset, min_length, from_end: false } =>
diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs
index 45cc0b8b413..20e083f840f 100644
--- a/src/librustc/mir/tcx.rs
+++ b/src/librustc/mir/tcx.rs
@@ -14,7 +14,8 @@
  */
 
 use mir::repr::*;
-use middle::subst::Substs;
+use middle::const_eval::ConstVal;
+use middle::subst::{Subst, Substs};
 use middle::ty::{self, AdtDef, Ty};
 use rustc_front::hir;
 
@@ -72,23 +73,7 @@ impl<'tcx> LvalueTy<'tcx> {
                         tcx.sess.bug(&format!("cannot downcast non-enum type: `{:?}`", self))
                     }
                 },
-            ProjectionElem::Field(field) => {
-                let field_ty = match self {
-                    LvalueTy::Ty { ty } => match ty.sty {
-                        ty::TyStruct(adt_def, substs) =>
-                            adt_def.struct_variant().fields[field.index()].ty(tcx, substs),
-                        ty::TyTuple(ref tys) =>
-                            tys[field.index()],
-                        ty::TyClosure(_, ref closure_substs) =>
-                            closure_substs.upvar_tys[field.index()],
-                        _ =>
-                            tcx.sess.bug(&format!("cannot get field of type: `{:?}`", ty)),
-                    },
-                    LvalueTy::Downcast { adt_def, substs, variant_index } =>
-                        adt_def.variants[variant_index].fields[field.index()].ty(tcx, substs),
-                };
-                LvalueTy::Ty { ty: field_ty }
-            }
+            ProjectionElem::Field(_, fty) => LvalueTy::Ty { ty: fty }
         }
     }
 }
@@ -150,6 +135,73 @@ impl<'tcx> Mir<'tcx> {
                 self.lvalue_ty(tcx, &proj.base).projection_ty(tcx, &proj.elem)
         }
     }
+
+    pub fn rvalue_ty(&self,
+                     tcx: &ty::ctxt<'tcx>,
+                     rvalue: &Rvalue<'tcx>)
+                     -> Option<Ty<'tcx>>
+    {
+        match *rvalue {
+            Rvalue::Use(ref operand) => Some(self.operand_ty(tcx, operand)),
+            Rvalue::Repeat(ref operand, ref count) => {
+                if let ConstVal::Uint(u) = count.value {
+                    let op_ty = self.operand_ty(tcx, operand);
+                    Some(tcx.mk_array(op_ty, u as usize))
+                } else {
+                    None
+                }
+            }
+            Rvalue::Ref(reg, bk, ref lv) => {
+                let lv_ty = self.lvalue_ty(tcx, lv).to_ty(tcx);
+                Some(tcx.mk_ref(
+                    tcx.mk_region(reg),
+                    ty::TypeAndMut {
+                        ty: lv_ty,
+                        mutbl: bk.to_mutbl_lossy()
+                    }
+                ))
+            }
+            Rvalue::Len(..) => Some(tcx.types.usize),
+            Rvalue::Cast(_, _, ty) => Some(ty),
+            Rvalue::BinaryOp(op, ref lhs, ref rhs) => {
+                let lhs_ty = self.operand_ty(tcx, lhs);
+                let rhs_ty = self.operand_ty(tcx, rhs);
+                Some(self.binop_ty(tcx, op, lhs_ty, rhs_ty))
+            }
+            Rvalue::UnaryOp(_, ref operand) => {
+                Some(self.operand_ty(tcx, operand))
+            }
+            Rvalue::Box(t) => {
+                Some(tcx.mk_box(t))
+            }
+            Rvalue::Aggregate(ref ak, ref ops) => {
+                match *ak {
+                    AggregateKind::Vec => {
+                        if let Some(operand) = ops.get(0) {
+                            let ty = self.operand_ty(tcx, operand);
+                            Some(tcx.mk_array(ty, ops.len()))
+                        } else {
+                            None
+                        }
+                    }
+                    AggregateKind::Tuple => {
+                        Some(tcx.mk_tup(
+                            ops.iter().map(|op| self.operand_ty(tcx, op)).collect()
+                        ))
+                    }
+                    AggregateKind::Adt(def, _, substs) => {
+                        Some(def.type_scheme(tcx).ty.subst(tcx, substs))
+                    }
+                    AggregateKind::Closure(did, substs) => {
+                        Some(tcx.mk_closure_from_closure_substs(
+                            did, Box::new(substs.clone())))
+                    }
+                }
+            }
+            Rvalue::Slice { .. } => None,
+            Rvalue::InlineAsm(..) => None
+        }
+    }
 }
 
 impl BorrowKind {
diff --git a/src/librustc/mir/transform.rs b/src/librustc/mir/transform.rs
index 0f18d8d2821..cc417f5a99e 100644
--- a/src/librustc/mir/transform.rs
+++ b/src/librustc/mir/transform.rs
@@ -9,8 +9,8 @@
 // except according to those terms.
 
 use mir::repr::Mir;
-use middle::ty::ctxt;
+use middle::infer::InferCtxt;
 
 pub trait MirPass {
-    fn run_on_mir<'tcx>(&mut self, mir: &mut Mir<'tcx>, tcx: &ctxt<'tcx>);
+    fn run_on_mir<'a, 'tcx>(&mut self, mir: &mut Mir<'tcx>, infcx: &InferCtxt<'a, 'tcx>);
 }
diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs
index 864ff40fe10..f835613cfcb 100644
--- a/src/librustc/session/config.rs
+++ b/src/librustc/session/config.rs
@@ -138,6 +138,7 @@ pub struct Options {
     pub no_trans: bool,
     pub error_format: ErrorOutputType,
     pub treat_err_as_bug: bool,
+    pub mir_opt_level: usize,
 
     /// if true, build up the dep-graph
     pub build_dep_graph: bool,
@@ -254,6 +255,7 @@ pub fn basic_options() -> Options {
         parse_only: false,
         no_trans: false,
         treat_err_as_bug: false,
+        mir_opt_level: 1,
         build_dep_graph: false,
         dump_dep_graph: false,
         no_analysis: false,
@@ -655,6 +657,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
           "show spans for compiler debugging (expr|pat|ty)"),
     print_trans_items: Option<String> = (None, parse_opt_string,
           "print the result of the translation item collection pass"),
+    mir_opt_level: Option<usize> = (None, parse_opt_uint,
+          "set the MIR optimization level (0-3)"),
 }
 
 pub fn default_lib_output() -> CrateType {
@@ -988,6 +992,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
     let parse_only = debugging_opts.parse_only;
     let no_trans = debugging_opts.no_trans;
     let treat_err_as_bug = debugging_opts.treat_err_as_bug;
+    let mir_opt_level = debugging_opts.mir_opt_level.unwrap_or(1);
     let incremental_compilation = debugging_opts.incr_comp;
     let dump_dep_graph = debugging_opts.dump_dep_graph;
     let no_analysis = debugging_opts.no_analysis;
@@ -1166,6 +1171,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
         parse_only: parse_only,
         no_trans: no_trans,
         treat_err_as_bug: treat_err_as_bug,
+        mir_opt_level: mir_opt_level,
         build_dep_graph: incremental_compilation || dump_dep_graph,
         dump_dep_graph: dump_dep_graph,
         no_analysis: no_analysis,
diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs
index a0939dc53df..5868f233776 100644
--- a/src/librustc/util/ppaux.rs
+++ b/src/librustc/util/ppaux.rs
@@ -396,6 +396,9 @@ impl<'tcx> fmt::Debug for ty::adjustment::AutoAdjustment<'tcx> {
             ty::adjustment::AdjustUnsafeFnPointer => {
                 write!(f, "AdjustUnsafeFnPointer")
             }
+            ty::adjustment::AdjustMutToConstPointer => {
+                write!(f, "AdjustMutToConstPointer")
+            }
             ty::adjustment::AdjustDerefRef(ref data) => {
                 write!(f, "{:?}", data)
             }
diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs
index 6eede3070b2..b446dec96fb 100644
--- a/src/librustc_driver/driver.rs
+++ b/src/librustc_driver/driver.rs
@@ -844,6 +844,18 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
              "match checking",
              || middle::check_match::check_crate(tcx));
 
+        // this must run before MIR dump, because
+        // "not all control paths return a value" is reported here.
+        //
+        // maybe move the check to a MIR pass?
+        time(time_passes,
+             "liveness checking",
+             || middle::liveness::check_crate(tcx));
+
+        time(time_passes,
+             "rvalue checking",
+             || rvalues::check_crate(tcx));
+
         let mut mir_map =
             time(time_passes,
                  "MIR dump",
@@ -854,17 +866,9 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
              || mir_map.run_passes(&mut sess.plugin_mir_passes.borrow_mut(), tcx));
 
         time(time_passes,
-             "liveness checking",
-             || middle::liveness::check_crate(tcx));
-
-        time(time_passes,
              "borrow checking",
              || borrowck::check_crate(tcx));
 
-        time(time_passes,
-             "rvalue checking",
-             || rvalues::check_crate(tcx));
-
         // Avoid overwhelming user with errors if type checking failed.
         // I'm not sure how helpful this is, to be honest, but it avoids
         // a
diff --git a/src/librustc_metadata/astencode.rs b/src/librustc_metadata/astencode.rs
index 3a0bd3aa205..fe4df865a0e 100644
--- a/src/librustc_metadata/astencode.rs
+++ b/src/librustc_metadata/astencode.rs
@@ -610,7 +610,7 @@ impl<'a, 'tcx> rbml_writer_helpers<'tcx> for Encoder<'a> {
 
         self.emit_enum("AutoAdjustment", |this| {
             match *adj {
-                adjustment::AdjustReifyFnPointer=> {
+                adjustment::AdjustReifyFnPointer => {
                     this.emit_enum_variant("AdjustReifyFnPointer", 1, 0, |_| Ok(()))
                 }
 
@@ -620,8 +620,14 @@ impl<'a, 'tcx> rbml_writer_helpers<'tcx> for Encoder<'a> {
                     })
                 }
 
+                adjustment::AdjustMutToConstPointer => {
+                    this.emit_enum_variant("AdjustMutToConstPointer", 3, 0, |_| {
+                        Ok(())
+                    })
+                }
+
                 adjustment::AdjustDerefRef(ref auto_deref_ref) => {
-                    this.emit_enum_variant("AdjustDerefRef", 3, 2, |this| {
+                    this.emit_enum_variant("AdjustDerefRef", 4, 2, |this| {
                         this.emit_enum_variant_arg(0,
                             |this| Ok(this.emit_auto_deref_ref(ecx, auto_deref_ref)))
                     })
@@ -1002,12 +1008,14 @@ impl<'a, 'tcx> rbml_decoder_decoder_helpers<'tcx> for reader::Decoder<'a> {
     fn read_auto_adjustment<'b, 'c>(&mut self, dcx: &DecodeContext<'b, 'c, 'tcx>)
                                     -> adjustment::AutoAdjustment<'tcx> {
         self.read_enum("AutoAdjustment", |this| {
-            let variants = ["AdjustReifyFnPointer", "AdjustUnsafeFnPointer", "AdjustDerefRef"];
+            let variants = ["AdjustReifyFnPointer", "AdjustUnsafeFnPointer",
+                            "AdjustMutToConstPointer", "AdjustDerefRef"];
             this.read_enum_variant(&variants, |this, i| {
                 Ok(match i {
                     1 => adjustment::AdjustReifyFnPointer,
                     2 => adjustment::AdjustUnsafeFnPointer,
-                    3 => {
+                    3 => adjustment::AdjustMutToConstPointer,
+                    4 => {
                         let auto_deref_ref: adjustment::AutoDerefRef =
                             this.read_enum_variant_arg(0,
                                 |this| Ok(this.read_auto_deref_ref(dcx))).unwrap();
diff --git a/src/librustc_mir/build/expr/as_lvalue.rs b/src/librustc_mir/build/expr/as_lvalue.rs
index 4e03ed489eb..b2c7507ed7b 100644
--- a/src/librustc_mir/build/expr/as_lvalue.rs
+++ b/src/librustc_mir/build/expr/as_lvalue.rs
@@ -41,7 +41,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
             }
             ExprKind::Field { lhs, name } => {
                 let lvalue = unpack!(block = this.as_lvalue(block, lhs));
-                let lvalue = lvalue.field(name);
+                let lvalue = lvalue.field(name, expr.ty);
                 block.and(lvalue)
             }
             ExprKind::Deref { arg } => {
diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs
index dea2d750b98..2338d7df01a 100644
--- a/src/librustc_mir/build/expr/as_rvalue.rs
+++ b/src/librustc_mir/build/expr/as_rvalue.rs
@@ -139,7 +139,9 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                           .collect();
                 block.and(Rvalue::Aggregate(AggregateKind::Closure(closure_id, substs), upvars))
             }
-            ExprKind::Adt { adt_def, variant_index, substs, fields, base } => { // see (*) above
+            ExprKind::Adt {
+                adt_def, variant_index, substs, fields, base
+            } => { // see (*) above
                 // first process the set of fields that were provided
                 // (evaluating them in order given by user)
                 let fields_map: FnvHashMap<_, _> =
@@ -147,25 +149,24 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                           .map(|f| (f.name, unpack!(block = this.as_operand(block, f.expr))))
                           .collect();
 
-                // if base expression is given, evaluate it now
-                let base = base.map(|base| unpack!(block = this.as_lvalue(block, base)));
-
-                // get list of all fields that we will need
                 let field_names = this.hir.all_fields(adt_def, variant_index);
 
-                // for the actual values we use, take either the
-                // expr the user specified or, if they didn't
-                // specify something for this field name, create a
-                // path relative to the base (which must have been
-                // supplied, or the IR is internally
-                // inconsistent).
-                let fields: Vec<_> =
+                let fields = if let Some(FruInfo { base, field_types }) = base {
+                    let base = unpack!(block = this.as_lvalue(block, base));
+
+                    // MIR does not natively support FRU, so for each
+                    // base-supplied field, generate an operand that
+                    // reads it from the base.
                     field_names.into_iter()
-                               .map(|n| match fields_map.get(&n) {
-                                   Some(v) => v.clone(),
-                                   None => Operand::Consume(base.clone().unwrap().field(n)),
-                               })
-                               .collect();
+                        .zip(field_types.into_iter())
+                        .map(|(n, ty)| match fields_map.get(&n) {
+                            Some(v) => v.clone(),
+                            None => Operand::Consume(base.clone().field(n, ty))
+                        })
+                        .collect()
+                } else {
+                    field_names.iter().map(|n| fields_map[n].clone()).collect()
+                };
 
                 block.and(Rvalue::Aggregate(AggregateKind::Adt(adt_def, variant_index, substs),
                                             fields))
diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs
index ec67429379f..02f32da2b83 100644
--- a/src/librustc_mir/build/matches/test.rs
+++ b/src/librustc_mir/build/matches/test.rs
@@ -404,7 +404,8 @@ impl<'a,'tcx> Builder<'a,'tcx> {
             subpatterns.iter()
                        .map(|subpattern| {
                            // e.g., `(x as Variant).0`
-                           let lvalue = downcast_lvalue.clone().field(subpattern.field);
+                           let lvalue = downcast_lvalue.clone().field(subpattern.field,
+                                                                      subpattern.field_ty());
                            // e.g., `(x as Variant).0 @ P1`
                            MatchPair::new(lvalue, &subpattern.pattern)
                        });
diff --git a/src/librustc_mir/build/matches/util.rs b/src/librustc_mir/build/matches/util.rs
index dbb00a13cd3..c295ed168ba 100644
--- a/src/librustc_mir/build/matches/util.rs
+++ b/src/librustc_mir/build/matches/util.rs
@@ -21,7 +21,8 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                                    -> Vec<MatchPair<'pat, 'tcx>> {
         subpatterns.iter()
                    .map(|fieldpat| {
-                       let lvalue = lvalue.clone().field(fieldpat.field);
+                       let lvalue = lvalue.clone().field(fieldpat.field,
+                                                         fieldpat.field_ty());
                        MatchPair::new(lvalue, &fieldpat.pattern)
                    })
                    .collect()
diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs
index 86b34858189..b83d0f6a980 100644
--- a/src/librustc_mir/build/mod.rs
+++ b/src/librustc_mir/build/mod.rs
@@ -80,7 +80,7 @@ macro_rules! unpack {
 /// the main entry point for building MIR for a function
 
 pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>,
-                          _span: Span,
+                          span: Span,
                           implicit_arguments: Vec<Ty<'tcx>>,
                           explicit_arguments: Vec<(Ty<'tcx>, &'tcx hir::Pat)>,
                           argument_extent: CodeExtent,
@@ -97,7 +97,7 @@ pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>,
         temp_decls: vec![],
         var_decls: vec![],
         var_indices: FnvHashMap(),
-        unit_temp: None
+        unit_temp: None,
     };
 
     assert_eq!(builder.cfg.start_new_block(), START_BLOCK);
@@ -119,6 +119,7 @@ pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>,
         arg_decls: arg_decls,
         temp_decls: builder.temp_decls,
         return_ty: return_ty,
+        span: span
     }
 }
 
diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs
index 167317f426b..7be70867796 100644
--- a/src/librustc_mir/build/scope.rs
+++ b/src/librustc_mir/build/scope.rs
@@ -418,25 +418,28 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                              len: Operand<'tcx>,
                              span: Span) {
         // fn(&(filename: &'static str, line: u32), index: usize, length: usize) -> !
+        let region = ty::ReStatic; // FIXME(mir-borrowck): use a better region?
         let func = self.lang_function(lang_items::PanicBoundsCheckFnLangItem);
-        let args = func.ty.fn_args();
-        let ref_ty = args.skip_binder()[0];
-        let (region, tup_ty) = if let ty::TyRef(region, tyandmut) = ref_ty.sty {
-            (region, tyandmut.ty)
+        let args = self.hir.tcx().replace_late_bound_regions(&func.ty.fn_args(), |_| region).0;
+
+        let ref_ty = args[0];
+        let tup_ty = if let ty::TyRef(_, tyandmut) = ref_ty.sty {
+            tyandmut.ty
         } else {
             self.hir.span_bug(span, &format!("unexpected panic_bound_check type: {:?}", func.ty));
         };
+
         let (tuple, tuple_ref) = (self.temp(tup_ty), self.temp(ref_ty));
         let (file, line) = self.span_to_fileline_args(span);
         let elems = vec![Operand::Constant(file), Operand::Constant(line)];
         // FIXME: We should have this as a constant, rather than a stack variable (to not pollute
         // icache with cold branch code), however to achieve that we either have to rely on rvalue
         // promotion or have some way, in MIR, to create constants.
-        self.cfg.push_assign(block, DUMMY_SP, &tuple, // tuple = (file_arg, line_arg);
+        self.cfg.push_assign(block, span, &tuple, // tuple = (file_arg, line_arg);
                              Rvalue::Aggregate(AggregateKind::Tuple, elems));
         // FIXME: is this region really correct here?
-        self.cfg.push_assign(block, DUMMY_SP, &tuple_ref, // tuple_ref = &tuple;
-                             Rvalue::Ref(*region, BorrowKind::Unique, tuple));
+        self.cfg.push_assign(block, span, &tuple_ref, // tuple_ref = &tuple;
+                             Rvalue::Ref(region, BorrowKind::Shared, tuple));
         let cleanup = self.diverge_cleanup();
         self.cfg.terminate(block, Terminator::Call {
             func: Operand::Constant(func),
@@ -449,18 +452,21 @@ impl<'a,'tcx> Builder<'a,'tcx> {
     /// Create diverge cleanup and branch to it from `block`.
     pub fn panic(&mut self, block: BasicBlock, msg: &'static str, span: Span) {
         // fn(&(msg: &'static str filename: &'static str, line: u32)) -> !
+        let region = ty::ReStatic; // FIXME(mir-borrowck): use a better region?
         let func = self.lang_function(lang_items::PanicFnLangItem);
-        let args = func.ty.fn_args();
-        let ref_ty = args.skip_binder()[0];
-        let (region, tup_ty) = if let ty::TyRef(region, tyandmut) = ref_ty.sty {
-            (region, tyandmut.ty)
+        let args = self.hir.tcx().replace_late_bound_regions(&func.ty.fn_args(), |_| region).0;
+
+        let ref_ty = args[0];
+        let tup_ty = if let ty::TyRef(_, tyandmut) = ref_ty.sty {
+            tyandmut.ty
         } else {
             self.hir.span_bug(span, &format!("unexpected panic type: {:?}", func.ty));
         };
+
         let (tuple, tuple_ref) = (self.temp(tup_ty), self.temp(ref_ty));
         let (file, line) = self.span_to_fileline_args(span);
         let message = Constant {
-            span: DUMMY_SP,
+            span: span,
             ty: self.hir.tcx().mk_static_str(),
             literal: self.hir.str_literal(intern_and_get_ident(msg))
         };
@@ -470,11 +476,11 @@ impl<'a,'tcx> Builder<'a,'tcx> {
         // FIXME: We should have this as a constant, rather than a stack variable (to not pollute
         // icache with cold branch code), however to achieve that we either have to rely on rvalue
         // promotion or have some way, in MIR, to create constants.
-        self.cfg.push_assign(block, DUMMY_SP, &tuple, // tuple = (message_arg, file_arg, line_arg);
+        self.cfg.push_assign(block, span, &tuple, // tuple = (message_arg, file_arg, line_arg);
                              Rvalue::Aggregate(AggregateKind::Tuple, elems));
         // FIXME: is this region really correct here?
-        self.cfg.push_assign(block, DUMMY_SP, &tuple_ref, // tuple_ref = &tuple;
-                             Rvalue::Ref(*region, BorrowKind::Unique, tuple));
+        self.cfg.push_assign(block, span, &tuple_ref, // tuple_ref = &tuple;
+                             Rvalue::Ref(region, BorrowKind::Shared, tuple));
         let cleanup = self.diverge_cleanup();
         self.cfg.terminate(block, Terminator::Call {
             func: Operand::Constant(func),
@@ -505,11 +511,11 @@ impl<'a,'tcx> Builder<'a,'tcx> {
     fn span_to_fileline_args(&mut self, span: Span) -> (Constant<'tcx>, Constant<'tcx>) {
         let span_lines = self.hir.tcx().sess.codemap().lookup_char_pos(span.lo);
         (Constant {
-            span: DUMMY_SP,
+            span: span,
             ty: self.hir.tcx().mk_static_str(),
             literal: self.hir.str_literal(intern_and_get_ident(&span_lines.file.name))
         }, Constant {
-            span: DUMMY_SP,
+            span: span,
             ty: self.hir.tcx().types.u32,
             literal: self.hir.usize_literal(span_lines.line)
         })
diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs
index 90d049cdc73..ac1cff527fe 100644
--- a/src/librustc_mir/hair/cx/expr.rs
+++ b/src/librustc_mir/hair/cx/expr.rs
@@ -32,6 +32,8 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
         debug!("Expr::make_mirror(): id={}, span={:?}", self.id, self.span);
 
         let expr_ty = cx.tcx.expr_ty(self); // note: no adjustments (yet)!
+        let temp_lifetime = cx.tcx.region_maps.temporary_scope(self.id);
+        let expr_extent = cx.tcx.region_maps.node_extent(self.id);
 
         let kind = match self.node {
             // Here comes the interesting stuff:
@@ -54,14 +56,35 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
                     // Find the actual method implementation being called and
                     // build the appropriate UFCS call expression with the
                     // callee-object as self parameter.
+
+                    // rewrite f(u, v) into FnOnce::call_once(f, (u, v))
+
                     let method = method_callee(cx, self, ty::MethodCall::expr(self.id));
-                    let mut argrefs = vec![fun.to_ref()];
-                    argrefs.extend(args.iter().map(|a| a.to_ref()));
+
+                    let sig = match method.ty.sty {
+                        ty::TyBareFn(_, fn_ty) => &fn_ty.sig,
+                        _ => cx.tcx.sess.span_bug(self.span, "type of method is not an fn")
+                    };
+
+                    let sig = cx.tcx.no_late_bound_regions(sig).unwrap_or_else(|| {
+                        cx.tcx.sess.span_bug(self.span, "method call has late-bound regions")
+                    });
+
+                    assert_eq!(sig.inputs.len(), 2);
+
+                    let tupled_args = Expr {
+                        ty: sig.inputs[1],
+                        temp_lifetime: temp_lifetime,
+                        span: self.span,
+                        kind: ExprKind::Tuple {
+                            fields: args.iter().map(ToRef::to_ref).collect()
+                        }
+                    };
 
                     ExprKind::Call {
                         ty: method.ty,
                         fun: method.to_ref(),
-                        args: argrefs,
+                        args: vec![fun.to_ref(), tupled_args.to_ref()]
                     }
                 } else {
                     let adt_data = if let hir::ExprPath(..) = fun.node {
@@ -125,11 +148,20 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
             }
 
             hir::ExprAssignOp(op, ref lhs, ref rhs) => {
-                let op = bin_op(op.node);
-                ExprKind::AssignOp {
-                    op: op,
-                    lhs: lhs.to_ref(),
-                    rhs: rhs.to_ref(),
+                if cx.tcx.is_method_call(self.id) {
+                    let pass_args = if hir_util::is_by_value_binop(op.node) {
+                        PassArgs::ByValue
+                    } else {
+                        PassArgs::ByRef
+                    };
+                    overloaded_operator(cx, self, ty::MethodCall::expr(self.id),
+                                        pass_args, lhs.to_ref(), vec![rhs])
+                } else {
+                    ExprKind::AssignOp {
+                        op: bin_op(op.node),
+                        lhs: lhs.to_ref(),
+                        rhs: rhs.to_ref(),
+                    }
                 }
             }
 
@@ -227,13 +259,23 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
                             variant_index: 0,
                             substs: substs,
                             fields: field_refs,
-                            base: base.to_ref(),
+                            base: base.as_ref().map(|base| {
+                                FruInfo {
+                                    base: base.to_ref(),
+                                    field_types: cx.tcx.tables
+                                        .borrow()
+                                        .fru_field_types[&self.id]
+                                        .clone()
+                                }
+                            })
                         }
                     }
                     ty::TyEnum(adt, substs) => {
                         match cx.tcx.def_map.borrow()[&self.id].full_def() {
                             Def::Variant(enum_id, variant_id) => {
                                 debug_assert!(adt.did == enum_id);
+                                assert!(base.is_none());
+
                                 let index = adt.variant_index_with_id(variant_id);
                                 let field_refs = field_refs(&adt.variants[index], fields);
                                 ExprKind::Adt {
@@ -241,7 +283,7 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
                                     variant_index: index,
                                     substs: substs,
                                     fields: field_refs,
-                                    base: base.to_ref(),
+                                    base: None
                                 }
                             }
                             ref def => {
@@ -385,9 +427,6 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
                 ExprKind::Tuple { fields: fields.to_ref() },
         };
 
-        let temp_lifetime = cx.tcx.region_maps.temporary_scope(self.id);
-        let expr_extent = cx.tcx.region_maps.node_extent(self.id);
-
         let mut expr = Expr {
             temp_lifetime: temp_lifetime,
             ty: expr_ty,
@@ -395,6 +434,9 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
             kind: kind,
         };
 
+        debug!("make_mirror: unadjusted-expr={:?} applying adjustments={:?}",
+               expr, cx.tcx.tables.borrow().adjustments.get(&self.id));
+
         // Now apply adjustments, if any.
         match cx.tcx.tables.borrow().adjustments.get(&self.id) {
             None => {}
@@ -416,6 +458,15 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
                     kind: ExprKind::UnsafeFnPointer { source: expr.to_ref() },
                 };
             }
+            Some(&ty::adjustment::AdjustMutToConstPointer) => {
+                let adjusted_ty = cx.tcx.expr_ty_adjusted(self);
+                expr = Expr {
+                    temp_lifetime: temp_lifetime,
+                    ty: adjusted_ty,
+                    span: self.span,
+                    kind: ExprKind::Cast { source: expr.to_ref() },
+                };
+            }
             Some(&ty::adjustment::AdjustDerefRef(ref adj)) => {
                 for i in 0..adj.autoderefs {
                     let i = i as u32;
@@ -426,10 +477,38 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
                             self.span,
                             i,
                             |mc| cx.tcx.tables.borrow().method_map.get(&mc).map(|m| m.ty));
-                    let kind = if cx.tcx.is_overloaded_autoderef(self.id, i) {
-                        overloaded_lvalue(cx, self, ty::MethodCall::autoderef(self.id, i),
-                                          PassArgs::ByValue, expr.to_ref(), vec![])
+                    debug!("make_mirror: autoderef #{}, adjusted_ty={:?}", i, adjusted_ty);
+                    let method_key = ty::MethodCall::autoderef(self.id, i);
+                    let meth_ty =
+                        cx.tcx.tables.borrow().method_map.get(&method_key).map(|m| m.ty);
+                    let kind = if let Some(meth_ty) = meth_ty {
+                        debug!("make_mirror: overloaded autoderef (meth_ty={:?})", meth_ty);
+
+                        let ref_ty = cx.tcx.no_late_bound_regions(&meth_ty.fn_ret());
+                        let (region, mutbl) = match ref_ty {
+                            Some(ty::FnConverging(&ty::TyS {
+                                sty: ty::TyRef(region, mt), ..
+                            })) => (region, mt.mutbl),
+                            _ => cx.tcx.sess.span_bug(
+                                expr.span, "autoderef returned bad type")
+                        };
+
+                        expr = Expr {
+                            temp_lifetime: temp_lifetime,
+                            ty: cx.tcx.mk_ref(
+                                region, ty::TypeAndMut { ty: expr.ty, mutbl: mutbl }),
+                            span: expr.span,
+                            kind: ExprKind::Borrow {
+                                region: *region,
+                                borrow_kind: to_borrow_kind(mutbl),
+                                arg: expr.to_ref()
+                            }
+                        };
+
+                        overloaded_lvalue(cx, self, method_key,
+                                          PassArgs::ByRef, expr.to_ref(), vec![])
                     } else {
+                        debug!("make_mirror: built-in autoderef");
                         ExprKind::Deref { arg: expr.to_ref() }
                     };
                     expr = Expr {
@@ -749,11 +828,16 @@ fn convert_var<'a, 'tcx: 'a>(cx: &mut Cx<'a, 'tcx>,
             };
             match upvar_capture {
                 ty::UpvarCapture::ByValue => field_kind,
-                ty::UpvarCapture::ByRef(_) => {
+                ty::UpvarCapture::ByRef(borrow) => {
                     ExprKind::Deref {
                         arg: Expr {
                             temp_lifetime: temp_lifetime,
-                            ty: var_ty,
+                            ty: cx.tcx.mk_ref(
+                                cx.tcx.mk_region(borrow.region),
+                                ty::TypeAndMut {
+                                    ty: var_ty,
+                                    mutbl: borrow.kind.to_mutbl_lossy()
+                                }),
                             span: expr.span,
                             kind: field_kind,
                         }.to_ref()
diff --git a/src/librustc_mir/hair/cx/pattern.rs b/src/librustc_mir/hair/cx/pattern.rs
index c5b34d92466..6f4375d53ec 100644
--- a/src/librustc_mir/hair/cx/pattern.rs
+++ b/src/librustc_mir/hair/cx/pattern.rs
@@ -314,3 +314,20 @@ impl<'patcx, 'cx, 'tcx> PatCx<'patcx, 'cx, 'tcx> {
         }
     }
 }
+
+impl<'tcx> FieldPattern<'tcx> {
+    pub fn field_ty(&self) -> Ty<'tcx> {
+        debug!("field_ty({:?},ty={:?})", self, self.pattern.ty);
+        let r = match *self.pattern.kind {
+            PatternKind::Binding { mode: BindingMode::ByRef(..), ..} => {
+                match self.pattern.ty.sty {
+                    ty::TyRef(_, mt) => mt.ty,
+                    _ => unreachable!()
+                }
+            }
+            _ => self.pattern.ty
+        };
+        debug!("field_ty -> {:?}", r);
+        r
+    }
+}
diff --git a/src/librustc_mir/hair/mod.rs b/src/librustc_mir/hair/mod.rs
index 0891c2845b0..e8edd4067e2 100644
--- a/src/librustc_mir/hair/mod.rs
+++ b/src/librustc_mir/hair/mod.rs
@@ -229,7 +229,7 @@ pub enum ExprKind<'tcx> {
         variant_index: usize,
         substs: &'tcx Substs<'tcx>,
         fields: Vec<FieldExprRef<'tcx>>,
-        base: Option<ExprRef<'tcx>>,
+        base: Option<FruInfo<'tcx>>
     },
     Closure {
         closure_id: DefId,
@@ -257,6 +257,12 @@ pub struct FieldExprRef<'tcx> {
 }
 
 #[derive(Clone, Debug)]
+pub struct FruInfo<'tcx> {
+    pub base: ExprRef<'tcx>,
+    pub field_types: Vec<Ty<'tcx>>
+}
+
+#[derive(Clone, Debug)]
 pub struct Arm<'tcx> {
     pub patterns: Vec<Pattern<'tcx>>,
     pub guard: Option<ExprRef<'tcx>>,
diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs
index 6f4128fc24d..5d915f37f6e 100644
--- a/src/librustc_mir/lib.rs
+++ b/src/librustc_mir/lib.rs
@@ -20,6 +20,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment!
 #![cfg_attr(not(stage0), deny(warnings))]
 #![unstable(feature = "rustc_private", issue = "27812")]
 
+#![feature(box_patterns)]
 #![feature(rustc_private)]
 #![feature(staged_api)]
 
diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs
index 5b887db7ec0..70c33540121 100644
--- a/src/librustc_mir/mir_map.rs
+++ b/src/librustc_mir/mir_map.rs
@@ -22,7 +22,8 @@ extern crate rustc_front;
 use build;
 use graphviz;
 use pretty;
-use transform::{simplify_cfg, no_landing_pads};
+use transform::{clear_dead_blocks, simplify_cfg, type_check};
+use transform::{no_landing_pads};
 use rustc::dep_graph::DepNode;
 use rustc::mir::repr::Mir;
 use hair::cx::Cx;
@@ -148,9 +149,12 @@ impl<'a, 'm, 'tcx> Visitor<'tcx> for InnerDump<'a,'m,'tcx> {
 
         match build_mir(Cx::new(&infcx), implicit_arg_tys, id, span, decl, body) {
             Ok(mut mir) => {
-                no_landing_pads::NoLandingPads.run_on_mir(&mut mir, self.tcx);
-                simplify_cfg::SimplifyCfg::new().run_on_mir(&mut mir, self.tcx);
-
+                clear_dead_blocks::ClearDeadBlocks::new().run_on_mir(&mut mir, &infcx);
+                type_check::TypeckMir::new().run_on_mir(&mut mir, &infcx);
+                no_landing_pads::NoLandingPads.run_on_mir(&mut mir, &infcx);
+                if self.tcx.sess.opts.mir_opt_level > 0 {
+                    simplify_cfg::SimplifyCfg::new().run_on_mir(&mut mir, &infcx);
+                }
                 let meta_item_list = self.attr
                                          .iter()
                                          .flat_map(|a| a.meta_item_list())
diff --git a/src/librustc_mir/transform/clear_dead_blocks.rs b/src/librustc_mir/transform/clear_dead_blocks.rs
new file mode 100644
index 00000000000..b35d8c08f5d
--- /dev/null
+++ b/src/librustc_mir/transform/clear_dead_blocks.rs
@@ -0,0 +1,81 @@
+// 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 erases the contents of dead blocks. This pass must
+//! run before any analysis passes because some of the dead blocks
+//! can be ill-typed.
+//!
+//! The main problem is that typeck lets most blocks whose end is not
+//! reachable have an arbitrary return type, rather than having the
+//! usual () return type (as a note, typeck's notion of reachability
+//! is in fact slightly weaker than MIR CFG reachability - see #31617).
+//!
+//! A standard example of the situation is:
+//! ```rust
+//!   fn example() {
+//!       let _a: char = { return; };
+//!   }
+//! ```
+//!
+//! Here the block (`{ return; }`) has the return type `char`,
+//! rather than `()`, but the MIR we naively generate still contains
+//! the `_a = ()` write in the unreachable block "after" the return.
+//!
+//! As we have to run this pass even when we want to debug the MIR,
+//! this pass just replaces the blocks with empty "return" blocks
+//! and does not renumber anything.
+
+use rustc::middle::infer;
+use rustc::mir::repr::*;
+use rustc::mir::transform::MirPass;
+
+pub struct ClearDeadBlocks;
+
+impl ClearDeadBlocks {
+    pub fn new() -> ClearDeadBlocks {
+        ClearDeadBlocks
+    }
+
+    fn clear_dead_blocks(&self, mir: &mut Mir) {
+        let mut seen = vec![false; mir.basic_blocks.len()];
+
+        // These blocks are always required.
+        seen[START_BLOCK.index()] = true;
+        seen[END_BLOCK.index()] = true;
+
+        let mut worklist = vec![START_BLOCK];
+        while let Some(bb) = worklist.pop() {
+            for succ in mir.basic_block_data(bb).terminator().successors().iter() {
+                if !seen[succ.index()] {
+                    seen[succ.index()] = true;
+                    worklist.push(*succ);
+                }
+            }
+        }
+
+        for (n, (block, seen)) in mir.basic_blocks.iter_mut().zip(seen).enumerate() {
+            if !seen {
+                info!("clearing block #{}: {:?}", n, block);
+                *block = BasicBlockData {
+                    statements: vec![],
+                    terminator: Some(Terminator::Return),
+                    is_cleanup: false
+                };
+            }
+        }
+    }
+}
+
+impl MirPass for ClearDeadBlocks {
+    fn run_on_mir<'a, 'tcx>(&mut self, mir: &mut Mir<'tcx>, _: &infer::InferCtxt<'a, 'tcx>)
+    {
+        self.clear_dead_blocks(mir);
+    }
+}
diff --git a/src/librustc_mir/transform/erase_regions.rs b/src/librustc_mir/transform/erase_regions.rs
index b927ab489f1..a82d1fc5399 100644
--- a/src/librustc_mir/transform/erase_regions.rs
+++ b/src/librustc_mir/transform/erase_regions.rs
@@ -16,18 +16,13 @@ use rustc::middle::ty;
 use rustc::mir::repr::*;
 use rustc::mir::visit::MutVisitor;
 use rustc::mir::mir_map::MirMap;
-use rustc::mir::transform::MirPass;
 
 pub fn erase_regions<'tcx>(tcx: &ty::ctxt<'tcx>, mir_map: &mut MirMap<'tcx>) {
-    let mut eraser = EraseRegions;
-
     for (_, mir) in &mut mir_map.map {
-        eraser.run_on_mir(mir, tcx);
+        EraseRegionsVisitor::new(tcx).visit_mir(mir);
     }
 }
 
-pub struct EraseRegions;
-
 struct EraseRegionsVisitor<'a, 'tcx: 'a> {
     tcx: &'a ty::ctxt<'tcx>,
 }
@@ -58,12 +53,6 @@ impl<'a, 'tcx> EraseRegionsVisitor<'a, 'tcx> {
     }
 }
 
-impl MirPass for EraseRegions {
-    fn run_on_mir<'tcx>(&mut self, mir: &mut Mir<'tcx>, tcx: &ty::ctxt<'tcx>) {
-        EraseRegionsVisitor::new(tcx).visit_mir(mir);
-    }
-}
-
 impl<'a, 'tcx> MutVisitor<'tcx> for EraseRegionsVisitor<'a, 'tcx> {
     fn visit_mir(&mut self, mir: &mut Mir<'tcx>) {
         self.erase_regions_return_ty(&mut mir.return_ty);
diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs
index 017cf360716..d27c208041f 100644
--- a/src/librustc_mir/transform/mod.rs
+++ b/src/librustc_mir/transform/mod.rs
@@ -8,7 +8,9 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+pub mod clear_dead_blocks;
 pub mod simplify_cfg;
 pub mod erase_regions;
 pub mod no_landing_pads;
+pub mod type_check;
 mod util;
diff --git a/src/librustc_mir/transform/no_landing_pads.rs b/src/librustc_mir/transform/no_landing_pads.rs
index d0ea9f10f2e..e2c93bd4e87 100644
--- a/src/librustc_mir/transform/no_landing_pads.rs
+++ b/src/librustc_mir/transform/no_landing_pads.rs
@@ -11,7 +11,7 @@
 //! This pass removes the unwind branch of all the terminators when the no-landing-pads option is
 //! specified.
 
-use rustc::middle::ty;
+use rustc::middle::infer;
 use rustc::mir::repr::*;
 use rustc::mir::visit::MutVisitor;
 use rustc::mir::transform::MirPass;
@@ -41,8 +41,9 @@ impl<'tcx> MutVisitor<'tcx> for NoLandingPads {
 }
 
 impl MirPass for NoLandingPads {
-    fn run_on_mir<'tcx>(&mut self, mir: &mut Mir<'tcx>, tcx: &ty::ctxt<'tcx>) {
-        if tcx.sess.no_landing_pads() {
+    fn run_on_mir<'a, 'tcx>(&mut self, mir: &mut Mir<'tcx>,
+                            infcx: &infer::InferCtxt<'a, 'tcx>) {
+        if infcx.tcx.sess.no_landing_pads() {
             self.visit_mir(mir);
         }
     }
diff --git a/src/librustc_mir/transform/simplify_cfg.rs b/src/librustc_mir/transform/simplify_cfg.rs
index 17c5b5f7c3c..16d12324202 100644
--- a/src/librustc_mir/transform/simplify_cfg.rs
+++ b/src/librustc_mir/transform/simplify_cfg.rs
@@ -9,6 +9,7 @@
 // except according to those terms.
 
 use rustc::middle::const_eval::ConstVal;
+use rustc::middle::infer;
 use rustc::mir::repr::*;
 use transform::util;
 use rustc::mir::transform::MirPass;
@@ -119,7 +120,7 @@ impl SimplifyCfg {
 }
 
 impl MirPass for SimplifyCfg {
-    fn run_on_mir<'tcx>(&mut self, mir: &mut Mir<'tcx>, _: &::rustc::middle::ty::ctxt<'tcx>) {
+    fn run_on_mir<'a, 'tcx>(&mut self, mir: &mut Mir<'tcx>, _: &infer::InferCtxt<'a, 'tcx>) {
         let mut changed = true;
         while changed {
             changed = self.simplify_branches(mir);
diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs
new file mode 100644
index 00000000000..0e97e362906
--- /dev/null
+++ b/src/librustc_mir/transform/type_check.rs
@@ -0,0 +1,598 @@
+// 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.
+
+//! This pass type-checks the MIR to ensure it is not broken.
+#![allow(unreachable_code)]
+
+use rustc::middle::infer::{self, InferCtxt};
+use rustc::middle::traits;
+use rustc::middle::ty::{self, Ty};
+use rustc::middle::ty::fold::TypeFoldable;
+use rustc::mir::repr::*;
+use rustc::mir::tcx::LvalueTy;
+use rustc::mir::transform::MirPass;
+use rustc::mir::visit::{self, Visitor};
+
+use syntax::codemap::{Span, DUMMY_SP};
+use std::fmt;
+
+macro_rules! span_mirbug {
+    ($context:expr, $elem:expr, $($message:tt)*) => ({
+        $context.tcx().sess.span_warn(
+            $context.last_span,
+            &format!("broken MIR ({:?}): {}", $elem, format!($($message)*))
+        )
+    })
+}
+
+macro_rules! span_mirbug_and_err {
+    ($context:expr, $elem:expr, $($message:tt)*) => ({
+        {
+            $context.tcx().sess.span_warn(
+                $context.last_span,
+                &format!("broken MIR ({:?}): {:?}", $elem, format!($($message)*))
+            );
+            $context.error()
+        }
+    })
+}
+
+enum FieldAccessError {
+    OutOfRange { field_count: usize }
+}
+
+/// Verifies that MIR types are sane to not crash further checks.
+///
+/// The sanitize_XYZ methods here take an MIR object and compute its
+/// type, calling `span_mirbug` and returning an error type if there
+/// is a problem.
+struct TypeVerifier<'a, 'b: 'a, 'tcx: 'b> {
+    cx: &'a mut TypeChecker<'b, 'tcx>,
+    mir: &'a Mir<'tcx>,
+    last_span: Span,
+    errors_reported: bool
+}
+
+impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
+    fn visit_span(&mut self, span: &Span) {
+        if *span != DUMMY_SP {
+            self.last_span = *span;
+        }
+    }
+
+    fn visit_lvalue(&mut self, lvalue: &Lvalue<'tcx>, _context: visit::LvalueContext) {
+        self.sanitize_lvalue(lvalue);
+    }
+
+    fn visit_constant(&mut self, constant: &Constant<'tcx>) {
+        self.super_constant(constant);
+        self.sanitize_type(constant, constant.ty);
+    }
+
+    fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>) {
+        self.super_rvalue(rvalue);
+        if let Some(ty) = self.mir.rvalue_ty(self.tcx(), rvalue) {
+            self.sanitize_type(rvalue, ty);
+        }
+    }
+
+    fn visit_mir(&mut self, mir: &Mir<'tcx>) {
+        if let ty::FnConverging(t) = mir.return_ty {
+            self.sanitize_type(&"return type", t);
+        }
+        for var_decl in &mir.var_decls {
+            self.sanitize_type(var_decl, var_decl.ty);
+        }
+        for (n, arg_decl) in mir.arg_decls.iter().enumerate() {
+            self.sanitize_type(&(n, arg_decl), arg_decl.ty);
+        }
+        for (n, tmp_decl) in mir.temp_decls.iter().enumerate() {
+            self.sanitize_type(&(n, tmp_decl), tmp_decl.ty);
+        }
+        if self.errors_reported {
+            return;
+        }
+        self.super_mir(mir);
+    }
+}
+
+impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
+    fn new(cx: &'a mut TypeChecker<'b, 'tcx>, mir: &'a Mir<'tcx>) -> Self {
+        TypeVerifier {
+            cx: cx,
+            mir: mir,
+            last_span: mir.span,
+            errors_reported: false
+        }
+    }
+
+    fn tcx(&self) -> &'a ty::ctxt<'tcx> {
+        self.cx.infcx.tcx
+    }
+
+    fn infcx(&self) -> &'a InferCtxt<'a, 'tcx> {
+        self.cx.infcx
+    }
+
+    fn sanitize_type(&mut self, parent: &fmt::Debug, ty: Ty<'tcx>) -> Ty<'tcx> {
+        if ty.needs_infer() || ty.has_escaping_regions() || ty.references_error() {
+            span_mirbug_and_err!(self, parent, "bad type {:?}", ty)
+        } else {
+            ty
+        }
+    }
+
+    fn sanitize_lvalue(&mut self, lvalue: &Lvalue<'tcx>) -> LvalueTy<'tcx> {
+        debug!("sanitize_lvalue: {:?}", lvalue);
+        match *lvalue {
+            Lvalue::Var(index) => LvalueTy::Ty { ty: self.mir.var_decls[index as usize].ty },
+            Lvalue::Temp(index) =>
+                LvalueTy::Ty { ty: self.mir.temp_decls[index as usize].ty },
+            Lvalue::Arg(index) =>
+                LvalueTy::Ty { ty: self.mir.arg_decls[index as usize].ty },
+            Lvalue::Static(def_id) =>
+                LvalueTy::Ty { ty: self.tcx().lookup_item_type(def_id).ty },
+            Lvalue::ReturnPointer => {
+                if let ty::FnConverging(return_ty) = self.mir.return_ty {
+                    LvalueTy::Ty { ty: return_ty }
+                } else {
+                    LvalueTy::Ty {
+                        ty: span_mirbug_and_err!(
+                            self, lvalue, "return in diverging function")
+                    }
+                }
+            }
+            Lvalue::Projection(ref proj) => {
+                let base_ty = self.sanitize_lvalue(&proj.base);
+                if let LvalueTy::Ty { ty } = base_ty {
+                    if ty.references_error() {
+                        assert!(self.errors_reported);
+                        return LvalueTy::Ty { ty: self.tcx().types.err };
+                    }
+                }
+                self.sanitize_projection(base_ty, &proj.elem, lvalue)
+            }
+        }
+    }
+
+    fn sanitize_projection(&mut self,
+                           base: LvalueTy<'tcx>,
+                           pi: &LvalueElem<'tcx>,
+                           lvalue: &Lvalue<'tcx>)
+                           -> LvalueTy<'tcx> {
+        debug!("sanitize_projection: {:?} {:?} {:?}", base, pi, lvalue);
+        let tcx = self.tcx();
+        let base_ty = base.to_ty(tcx);
+        let span = self.last_span;
+        match *pi {
+            ProjectionElem::Deref => {
+                let deref_ty = base_ty.builtin_deref(true, ty::LvaluePreference::NoPreference);
+                LvalueTy::Ty {
+                    ty: deref_ty.map(|t| t.ty).unwrap_or_else(|| {
+                        span_mirbug_and_err!(
+                            self, lvalue, "deref of non-pointer {:?}", base_ty)
+                    })
+                }
+            }
+            ProjectionElem::Index(ref i) => {
+                self.visit_operand(i);
+                let index_ty = self.mir.operand_ty(tcx, i);
+                if index_ty != tcx.types.usize {
+                    LvalueTy::Ty {
+                        ty: span_mirbug_and_err!(self, i, "index by non-usize {:?}", i)
+                    }
+                } else {
+                    LvalueTy::Ty {
+                        ty: base_ty.builtin_index().unwrap_or_else(|| {
+                            span_mirbug_and_err!(
+                                self, lvalue, "index of non-array {:?}", base_ty)
+                        })
+                    }
+                }
+            }
+            ProjectionElem::ConstantIndex { .. } => {
+                // consider verifying in-bounds
+                LvalueTy::Ty {
+                    ty: base_ty.builtin_index().unwrap_or_else(|| {
+                        span_mirbug_and_err!(
+                            self, lvalue, "index of non-array {:?}", base_ty)
+                    })
+                }
+            }
+            ProjectionElem::Downcast(adt_def1, index) =>
+                match base_ty.sty {
+                    ty::TyEnum(adt_def, substs) if adt_def == adt_def1 => {
+                        if index >= adt_def.variants.len() {
+                            LvalueTy::Ty {
+                                ty: span_mirbug_and_err!(
+                                    self,
+                                    lvalue,
+                                    "cast to variant #{:?} but enum only has {:?}",
+                                    index,
+                                    adt_def.variants.len())
+                            }
+                        } else {
+                            LvalueTy::Downcast {
+                                adt_def: adt_def,
+                                substs: substs,
+                                variant_index: index
+                            }
+                        }
+                    }
+                    _ => LvalueTy::Ty {
+                        ty: span_mirbug_and_err!(
+                            self, lvalue, "can't downcast {:?} as {:?}",
+                            base_ty, adt_def1)
+                    }
+                },
+            ProjectionElem::Field(field, fty) => {
+                let fty = self.sanitize_type(lvalue, fty);
+                match self.field_ty(lvalue, base, field) {
+                    Ok(ty) => {
+                        if let Err(terr) = self.cx.mk_eqty(span, ty, fty) {
+                            span_mirbug!(
+                                self, lvalue, "bad field access ({:?}: {:?}): {:?}",
+                                ty, fty, terr);
+                        }
+                    }
+                    Err(FieldAccessError::OutOfRange { field_count }) => {
+                        span_mirbug!(
+                            self, lvalue, "accessed field #{} but variant only has {}",
+                            field.index(), field_count)
+                    }
+                }
+                LvalueTy::Ty { ty: fty }
+            }
+        }
+    }
+
+    fn error(&mut self) -> Ty<'tcx> {
+        self.errors_reported = true;
+        self.tcx().types.err
+    }
+
+    fn field_ty(&mut self,
+                parent: &fmt::Debug,
+                base_ty: LvalueTy<'tcx>,
+                field: Field)
+                -> Result<Ty<'tcx>, FieldAccessError>
+    {
+        let tcx = self.tcx();
+
+        let (variant, substs) = match base_ty {
+            LvalueTy::Downcast { adt_def, substs, variant_index } => {
+                (&adt_def.variants[variant_index], substs)
+            }
+            LvalueTy::Ty { ty } => match ty.sty {
+                ty::TyStruct(adt_def, substs) | ty::TyEnum(adt_def, substs)
+                    if adt_def.is_univariant() => {
+                        (&adt_def.variants[0], substs)
+                    }
+                ty::TyTuple(ref tys) | ty::TyClosure(_, box ty::ClosureSubsts {
+                    upvar_tys: ref tys, ..
+                }) => {
+                    return match tys.get(field.index()) {
+                        Some(&ty) => Ok(ty),
+                        None => Err(FieldAccessError::OutOfRange {
+                            field_count: tys.len()
+                        })
+                    }
+                }
+                _ => return Ok(span_mirbug_and_err!(
+                    self, parent, "can't project out of {:?}", base_ty))
+            }
+        };
+
+        if let Some(field) = variant.fields.get(field.index()) {
+            Ok(self.normalize(field.ty(tcx, substs)))
+        } else {
+            Err(FieldAccessError::OutOfRange { field_count: variant.fields.len() })
+        }
+    }
+
+    fn normalize(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
+        let infcx = self.infcx();
+        let mut selcx = traits::SelectionContext::new(infcx);
+        let cause = traits::ObligationCause::misc(self.last_span, 0);
+        let traits::Normalized { value: ty, obligations } =
+            traits::normalize(&mut selcx, cause, &ty);
+
+        debug!("normalize: ty={:?} obligations={:?}",
+               ty,
+               obligations);
+
+        let mut fulfill_cx = &mut self.cx.fulfillment_cx;
+        for obligation in obligations {
+            fulfill_cx.register_predicate_obligation(infcx, obligation);
+        }
+
+        ty
+    }
+}
+
+pub struct TypeChecker<'a, 'tcx: 'a> {
+    infcx: &'a InferCtxt<'a, 'tcx>,
+    fulfillment_cx: traits::FulfillmentContext<'tcx>,
+    last_span: Span
+}
+
+impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
+    fn new(infcx: &'a InferCtxt<'a, 'tcx>) -> Self {
+        TypeChecker {
+            infcx: infcx,
+            fulfillment_cx: traits::FulfillmentContext::new(),
+            last_span: DUMMY_SP
+        }
+    }
+
+    fn mk_subty(&self, span: Span, sup: Ty<'tcx>, sub: Ty<'tcx>)
+                -> infer::UnitResult<'tcx>
+    {
+        infer::mk_subty(self.infcx, false, infer::TypeOrigin::Misc(span),
+                        sup, sub)
+    }
+
+    fn mk_eqty(&self, span: Span, a: Ty<'tcx>, b: Ty<'tcx>)
+                -> infer::UnitResult<'tcx>
+    {
+        infer::mk_eqty(self.infcx, false, infer::TypeOrigin::Misc(span),
+                       a, b)
+    }
+
+    fn tcx(&self) -> &'a ty::ctxt<'tcx> {
+        self.infcx.tcx
+    }
+
+    fn check_stmt(&mut self, mir: &Mir<'tcx>, stmt: &Statement<'tcx>) {
+        debug!("check_stmt: {:?}", stmt);
+        let tcx = self.tcx();
+        match stmt.kind {
+            StatementKind::Assign(ref lv, ref rv) => {
+                let lv_ty = mir.lvalue_ty(tcx, lv).to_ty(tcx);
+                let rv_ty = mir.rvalue_ty(tcx, rv);
+                if let Some(rv_ty) = rv_ty {
+                    if let Err(terr) = self.mk_subty(self.last_span, rv_ty, lv_ty) {
+                        span_mirbug!(self, stmt, "bad assignment ({:?} = {:?}): {:?}",
+                                     lv_ty, rv_ty, terr);
+                    }
+                }
+
+                // FIXME: rvalue with undeterminable type - e.g. inline
+                // asm.
+            }
+        }
+    }
+
+    fn check_terminator(&self,
+                        mir: &Mir<'tcx>,
+                        term: &Terminator<'tcx>) {
+        debug!("check_terminator: {:?}", term);
+        let tcx = self.tcx();
+        match *term {
+            Terminator::Goto { .. } |
+            Terminator::Resume |
+            Terminator::Return |
+            Terminator::Drop { .. } => {
+                // no checks needed for these
+            }
+
+            Terminator::If { ref cond, .. } => {
+                let cond_ty = mir.operand_ty(tcx, cond);
+                match cond_ty.sty {
+                    ty::TyBool => {}
+                    _ => {
+                        span_mirbug!(self, term, "bad If ({:?}, not bool", cond_ty);
+                    }
+                }
+            }
+            Terminator::SwitchInt { ref discr, switch_ty, .. } => {
+                let discr_ty = mir.lvalue_ty(tcx, discr).to_ty(tcx);
+                if let Err(terr) = self.mk_subty(self.last_span, discr_ty, switch_ty) {
+                    span_mirbug!(self, term, "bad SwitchInt ({:?} on {:?}): {:?}",
+                                 switch_ty, discr_ty, terr);
+                }
+                if !switch_ty.is_integral() && !switch_ty.is_char() &&
+                    !switch_ty.is_bool()
+                {
+                    span_mirbug!(self, term, "bad SwitchInt discr ty {:?}",switch_ty);
+                }
+                // FIXME: check the values
+            }
+            Terminator::Switch { ref discr, adt_def, ref targets } => {
+                let discr_ty = mir.lvalue_ty(tcx, discr).to_ty(tcx);
+                match discr_ty.sty {
+                    ty::TyEnum(def, _)
+                        if def == adt_def && adt_def.variants.len() == targets.len()
+                        => {},
+                    _ => {
+                        span_mirbug!(self, term, "bad Switch ({:?} on {:?})",
+                                     adt_def, discr_ty);
+                    }
+                }
+            }
+            Terminator::Call { ref func, ref args, ref destination, .. } => {
+                let func_ty = mir.operand_ty(tcx, func);
+                debug!("check_terminator: call, func_ty={:?}", func_ty);
+                let func_ty = match func_ty.sty {
+                    ty::TyBareFn(_, func_ty) => func_ty,
+                    _ => {
+                        span_mirbug!(self, term, "call to non-function {:?}", func_ty);
+                        return;
+                    }
+                };
+                let sig = tcx.erase_late_bound_regions(&func_ty.sig);
+                self.check_call_dest(mir, term, &sig, destination);
+
+                if self.is_box_free(func) {
+                    self.check_box_free_inputs(mir, term, &sig, args);
+                } else {
+                    self.check_call_inputs(mir, term, &sig, args);
+                }
+            }
+        }
+    }
+
+    fn check_call_dest(&self,
+                       mir: &Mir<'tcx>,
+                       term: &Terminator<'tcx>,
+                       sig: &ty::FnSig<'tcx>,
+                       destination: &Option<(Lvalue<'tcx>, BasicBlock)>) {
+        let tcx = self.tcx();
+        match (destination, sig.output) {
+            (&Some(..), ty::FnDiverging) => {
+                span_mirbug!(self, term, "call to diverging function {:?} with dest", sig);
+            }
+            (&Some((ref dest, _)), ty::FnConverging(ty)) => {
+                let dest_ty = mir.lvalue_ty(tcx, dest).to_ty(tcx);
+                if let Err(terr) = self.mk_subty(self.last_span, ty, dest_ty) {
+                    span_mirbug!(self, term,
+                                 "call dest mismatch ({:?} <- {:?}): {:?}",
+                                 dest_ty, ty, terr);
+                }
+            }
+            (&None, ty::FnDiverging) => {}
+            (&None, ty::FnConverging(..)) => {
+                span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig);
+             }
+        }
+    }
+
+    fn check_call_inputs(&self,
+                         mir: &Mir<'tcx>,
+                         term: &Terminator<'tcx>,
+                         sig: &ty::FnSig<'tcx>,
+                         args: &[Operand<'tcx>])
+    {
+        debug!("check_call_inputs({:?}, {:?})", sig, args);
+        if args.len() < sig.inputs.len() ||
+           (args.len() > sig.inputs.len() && !sig.variadic) {
+            span_mirbug!(self, term, "call to {:?} with wrong # of args", sig);
+        }
+        for (n, (fn_arg, op_arg)) in sig.inputs.iter().zip(args).enumerate() {
+            let op_arg_ty = mir.operand_ty(self.tcx(), op_arg);
+            if let Err(terr) = self.mk_subty(self.last_span, op_arg_ty, fn_arg) {
+                span_mirbug!(self, term, "bad arg #{:?} ({:?} <- {:?}): {:?}",
+                             n, fn_arg, op_arg_ty, terr);
+            }
+        }
+    }
+
+    fn is_box_free(&self, operand: &Operand<'tcx>) -> bool {
+        match operand {
+            &Operand::Constant(Constant {
+                literal: Literal::Item { def_id, .. }, ..
+            }) => {
+                Some(def_id) == self.tcx().lang_items.box_free_fn()
+            }
+            _ => false,
+        }
+    }
+
+    fn check_box_free_inputs(&self,
+                             mir: &Mir<'tcx>,
+                             term: &Terminator<'tcx>,
+                             sig: &ty::FnSig<'tcx>,
+                             args: &[Operand<'tcx>])
+    {
+        debug!("check_box_free_inputs");
+
+        // box_free takes a Box as a pointer. Allow for that.
+
+        if sig.inputs.len() != 1 {
+            span_mirbug!(self, term, "box_free should take 1 argument");
+            return;
+        }
+
+        let pointee_ty = match sig.inputs[0].sty {
+            ty::TyRawPtr(mt) => mt.ty,
+            _ => {
+                span_mirbug!(self, term, "box_free should take a raw ptr");
+                return;
+            }
+        };
+
+        if args.len() != 1 {
+            span_mirbug!(self, term, "box_free called with wrong # of args");
+            return;
+        }
+
+        let arg_ty = match mir.operand_ty(self.tcx(), &args[0]).sty {
+            ty::TyRawPtr(mt) => mt.ty,
+            ty::TyBox(ty) => ty,
+            _ => {
+                span_mirbug!(self, term, "box_free called with bad arg ty");
+                return;
+            }
+        };
+
+        if let Err(terr) = self.mk_subty(self.last_span, arg_ty, pointee_ty) {
+            span_mirbug!(self, term, "bad box_free arg ({:?} <- {:?}): {:?}",
+                         pointee_ty, arg_ty, terr);
+        }
+    }
+
+    fn typeck_mir(&mut self, mir: &Mir<'tcx>) {
+        self.last_span = mir.span;
+        debug!("run_on_mir: {:?}", mir.span);
+        for block in &mir.basic_blocks {
+            for stmt in &block.statements {
+                if stmt.span != DUMMY_SP {
+                    self.last_span = stmt.span;
+                }
+                self.check_stmt(mir, stmt);
+            }
+
+            if let Some(ref terminator) = block.terminator {
+                self.check_terminator(mir, terminator);
+            }
+        }
+    }
+
+    fn verify_obligations(&mut self, mir: &Mir<'tcx>) {
+        self.last_span = mir.span;
+        if let Err(e) = self.fulfillment_cx.select_all_or_error(self.infcx) {
+            span_mirbug!(self, "", "errors selecting obligation: {:?}",
+                         e);
+        }
+    }
+}
+
+pub struct TypeckMir;
+
+impl TypeckMir {
+    pub fn new() -> Self {
+        TypeckMir
+    }
+}
+
+impl MirPass for TypeckMir {
+    fn run_on_mir<'a, 'tcx>(&mut self, mir: &mut Mir<'tcx>, infcx: &InferCtxt<'a, 'tcx>)
+    {
+        if infcx.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 mut checker = TypeChecker::new(infcx);
+
+        {
+            let mut verifier = TypeVerifier::new(&mut checker, mir);
+            verifier.visit_mir(mir);
+            if verifier.errors_reported {
+                // don't do further checks to avoid ICEs
+                return;
+            }
+        }
+
+        checker.typeck_mir(mir);
+        checker.verify_obligations(mir);
+    }
+}
diff --git a/src/librustc_passes/consts.rs b/src/librustc_passes/consts.rs
index ee6003f713e..b0d459063ef 100644
--- a/src/librustc_passes/consts.rs
+++ b/src/librustc_passes/consts.rs
@@ -768,7 +768,8 @@ fn check_adjustments<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Exp
     match v.tcx.tables.borrow().adjustments.get(&e.id) {
         None |
         Some(&ty::adjustment::AdjustReifyFnPointer) |
-        Some(&ty::adjustment::AdjustUnsafeFnPointer) => {}
+        Some(&ty::adjustment::AdjustUnsafeFnPointer) |
+        Some(&ty::adjustment::AdjustMutToConstPointer) => {}
 
         Some(&ty::adjustment::AdjustDerefRef(
             ty::adjustment::AutoDerefRef { autoderefs, .. }
diff --git a/src/librustc_trans/trans/consts.rs b/src/librustc_trans/trans/consts.rs
index e8c90fa31ea..79c73a83ee7 100644
--- a/src/librustc_trans/trans/consts.rs
+++ b/src/librustc_trans/trans/consts.rs
@@ -40,7 +40,7 @@ use trans::type_of;
 use trans::Disr;
 use middle::subst::Substs;
 use middle::ty::adjustment::{AdjustDerefRef, AdjustReifyFnPointer};
-use middle::ty::adjustment::AdjustUnsafeFnPointer;
+use middle::ty::adjustment::{AdjustUnsafeFnPointer, AdjustMutToConstPointer};
 use middle::ty::{self, Ty};
 use middle::ty::cast::{CastTy,IntTy};
 use util::nodemap::NodeMap;
@@ -354,7 +354,7 @@ pub fn const_expr<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
             // FIXME(#19925) once fn item types are
             // zero-sized, we'll need to do something here
         }
-        Some(AdjustUnsafeFnPointer) => {
+        Some(AdjustUnsafeFnPointer) | Some(AdjustMutToConstPointer) => {
             // purely a type-level thing
         }
         Some(AdjustDerefRef(adj)) => {
diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs
index fac956c1ddd..782e38d3af2 100644
--- a/src/librustc_trans/trans/expr.rs
+++ b/src/librustc_trans/trans/expr.rs
@@ -71,7 +71,8 @@ use trans::tvec;
 use trans::type_of;
 use trans::Disr;
 use middle::ty::adjustment::{AdjustDerefRef, AdjustReifyFnPointer};
-use middle::ty::adjustment::{AdjustUnsafeFnPointer, CustomCoerceUnsized};
+use middle::ty::adjustment::{AdjustUnsafeFnPointer, AdjustMutToConstPointer};
+use middle::ty::adjustment::CustomCoerceUnsized;
 use middle::ty::{self, Ty};
 use middle::ty::MethodCall;
 use middle::ty::cast::{CastKind, CastTy};
@@ -354,7 +355,7 @@ fn adjustment_required<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
             // zero-sized, we'll need to return true here
             false
         }
-        AdjustUnsafeFnPointer => {
+        AdjustUnsafeFnPointer | AdjustMutToConstPointer => {
             // purely a type-level thing
             false
         }
@@ -391,7 +392,7 @@ fn apply_adjustments<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
             // FIXME(#19925) once fn item types are
             // zero-sized, we'll need to do something here
         }
-        AdjustUnsafeFnPointer => {
+        AdjustUnsafeFnPointer | AdjustMutToConstPointer => {
             // purely a type-level thing
         }
         AdjustDerefRef(ref adj) => {
diff --git a/src/librustc_trans/trans/mir/block.rs b/src/librustc_trans/trans/mir/block.rs
index 44ff974dc7f..609f1dee98a 100644
--- a/src/librustc_trans/trans/mir/block.rs
+++ b/src/librustc_trans/trans/mir/block.rs
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 use llvm::{BasicBlockRef, ValueRef, OperandBundleDef};
-use rustc::middle::ty;
+use rustc::middle::ty::{self, Ty};
 use rustc::mir::repr as mir;
 use syntax::abi::Abi;
 use trans::adt;
@@ -26,8 +26,55 @@ use trans::type_::Type;
 
 use super::MirContext;
 use super::operand::OperandValue::{FatPtr, Immediate, Ref};
+use super::operand::OperandRef;
+
+#[derive(PartialEq, Eq)]
+enum AbiStyle {
+    Foreign,
+    RustCall,
+    Rust
+}
 
 impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
+    fn abi_style(&self, fn_ty: Ty<'tcx>) -> AbiStyle {
+        if let ty::TyBareFn(_, ref f) = fn_ty.sty {
+            // We do not translate intrinsics here (they shouldn’t be functions)
+            assert!(f.abi != Abi::RustIntrinsic && f.abi != Abi::PlatformIntrinsic);
+
+            match f.abi {
+                Abi::Rust => AbiStyle::Rust,
+                Abi::RustCall => AbiStyle::RustCall,
+                _ => AbiStyle::Foreign
+            }
+        } else {
+            unreachable!()
+        }
+    }
+
+    fn arg_operands(&mut self,
+                    bcx: &BlockAndBuilder<'bcx, 'tcx>,
+                    abi_style: AbiStyle,
+                    args: &[mir::Operand<'tcx>])
+                    -> Vec<OperandRef<'tcx>>
+    {
+        match abi_style {
+            AbiStyle::Foreign | AbiStyle::Rust => {
+                args.iter().map(|arg| self.trans_operand(bcx, arg)).collect()
+            }
+            AbiStyle::RustCall => match args.split_last() {
+                None => vec![],
+                Some((tup, self_ty)) => {
+                    // we can reorder safely because of MIR
+                    let untupled_args = self.trans_operand_untupled(bcx, tup);
+                    self_ty
+                        .iter().map(|arg| self.trans_operand(bcx, arg))
+                        .chain(untupled_args.into_iter())
+                        .collect()
+                }
+            }
+        }
+    }
+
     pub fn trans_block(&mut self, bb: mir::BasicBlock) {
         debug!("trans_block({:?})", bb);
 
@@ -159,13 +206,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                 let mut arg_tys = Vec::new();
 
                 // Foreign-ABI functions are translated differently
-                let is_foreign = if let ty::TyBareFn(_, ref f) = callee.ty.sty {
-                    // We do not translate intrinsics here (they shouldn’t be functions)
-                    assert!(f.abi != Abi::RustIntrinsic && f.abi != Abi::PlatformIntrinsic);
-                    f.abi != Abi::Rust && f.abi != Abi::RustCall
-                } else {
-                    false
-                };
+                let abi_style = self.abi_style(callee.ty);
+                let is_foreign = abi_style == AbiStyle::Foreign;
 
                 // Prepare the return value destination
                 let (ret_dest_ty, must_copy_dest) = if let Some((ref d, _)) = *destination {
@@ -182,8 +224,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                 };
 
                 // Process the rest of the args.
-                for arg in args {
-                    let operand = self.trans_operand(&bcx, arg);
+                for operand in self.arg_operands(&bcx, abi_style, args) {
                     match operand.val {
                         Ref(llval) | Immediate(llval) => llargs.push(llval),
                         FatPtr(b, e) => {
diff --git a/src/librustc_trans/trans/mir/lvalue.rs b/src/librustc_trans/trans/mir/lvalue.rs
index 002584f51c6..826fb025bc1 100644
--- a/src/librustc_trans/trans/mir/lvalue.rs
+++ b/src/librustc_trans/trans/mir/lvalue.rs
@@ -126,7 +126,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                             }
                         })
                     }
-                    mir::ProjectionElem::Field(ref field) => {
+                    mir::ProjectionElem::Field(ref field, _) => {
                         let base_ty = tr_base.ty.to_ty(tcx);
                         let base_repr = adt::represent_type(ccx, base_ty);
                         let discr = match tr_base.ty {
diff --git a/src/librustc_trans/trans/mir/operand.rs b/src/librustc_trans/trans/mir/operand.rs
index d0eaaeef057..2183348c8b5 100644
--- a/src/librustc_trans/trans/mir/operand.rs
+++ b/src/librustc_trans/trans/mir/operand.rs
@@ -9,13 +9,16 @@
 // except according to those terms.
 
 use llvm::ValueRef;
-use rustc::middle::ty::{Ty, TypeFoldable};
+use rustc::middle::ty::{self, Ty};
 use rustc::mir::repr as mir;
+use trans::adt;
 use trans::base;
 use trans::common::{self, Block, BlockAndBuilder};
 use trans::datum;
+use trans::Disr;
 
 use super::{MirContext, TempRef};
+use super::lvalue::LvalueRef;
 
 /// The representation of a Rust value. The enum variant is in fact
 /// uniquely determined by the value's type, but is kept as a
@@ -90,6 +93,32 @@ impl<'tcx> OperandRef<'tcx> {
 }
 
 impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
+    pub fn trans_load(&mut self,
+                      bcx: &BlockAndBuilder<'bcx, 'tcx>,
+                      llval: ValueRef,
+                      ty: Ty<'tcx>)
+                      -> OperandRef<'tcx>
+    {
+        debug!("trans_load: {} @ {:?}", bcx.val_to_string(llval), ty);
+
+        let val = match datum::appropriate_rvalue_mode(bcx.ccx(), ty) {
+            datum::ByValue => {
+                bcx.with_block(|bcx| {
+                    OperandValue::Immediate(base::load_ty(bcx, llval, ty))
+                })
+            }
+            datum::ByRef if common::type_is_fat_ptr(bcx.tcx(), ty) => {
+                let (lldata, llextra) = bcx.with_block(|bcx| {
+                    base::load_fat_ptr(bcx, llval, ty)
+                });
+                OperandValue::FatPtr(lldata, llextra)
+            }
+            datum::ByRef => OperandValue::Ref(llval)
+        };
+
+        OperandRef { val: val, ty: ty }
+    }
+
     pub fn trans_operand(&mut self,
                          bcx: &BlockAndBuilder<'bcx, 'tcx>,
                          operand: &mir::Operand<'tcx>)
@@ -120,30 +149,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                 // out from their home
                 let tr_lvalue = self.trans_lvalue(bcx, lvalue);
                 let ty = tr_lvalue.ty.to_ty(bcx.tcx());
-                debug!("trans_operand: tr_lvalue={} @ {:?}",
-                       bcx.val_to_string(tr_lvalue.llval),
-                       ty);
-                let val = match datum::appropriate_rvalue_mode(bcx.ccx(), ty) {
-                    datum::ByValue => {
-                        bcx.with_block(|bcx| {
-                            OperandValue::Immediate(base::load_ty(bcx, tr_lvalue.llval, ty))
-                        })
-                    }
-                    datum::ByRef if common::type_is_fat_ptr(bcx.tcx(), ty) => {
-                        let (lldata, llextra) = bcx.with_block(|bcx| {
-                            base::load_fat_ptr(bcx, tr_lvalue.llval, ty)
-                        });
-                        OperandValue::FatPtr(lldata, llextra)
-                    }
-                    datum::ByRef => OperandValue::Ref(tr_lvalue.llval)
-                };
-
-                assert!(!ty.has_erasable_regions());
-
-                OperandRef {
-                    val: val,
-                    ty: ty
-                }
+                self.trans_load(bcx, tr_lvalue.llval, ty)
             }
 
             mir::Operand::Constant(ref constant) => {
@@ -197,4 +203,46 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
             }
         }
     }
+
+    pub fn trans_operand_untupled(&mut self,
+                                  bcx: &BlockAndBuilder<'bcx, 'tcx>,
+                                  operand: &mir::Operand<'tcx>)
+                                  -> Vec<OperandRef<'tcx>>
+    {
+        // FIXME: consider having some optimization to avoid tupling/untupling
+        // (and storing/loading in the case of immediates)
+
+        // avoid trans_operand for pointless copying
+        let lv = match *operand {
+            mir::Operand::Consume(ref lvalue) => self.trans_lvalue(bcx, lvalue),
+            mir::Operand::Constant(ref constant) => {
+                // FIXME: consider being less pessimized
+                if constant.ty.is_nil() {
+                    return vec![];
+                }
+
+                let ty = bcx.monomorphize(&constant.ty);
+                let lv = LvalueRef::alloca(bcx, ty, "__untuple_alloca");
+                let constant = self.trans_constant(bcx, constant);
+                self.store_operand(bcx, lv.llval, constant);
+                lv
+           }
+        };
+
+        let lv_ty = lv.ty.to_ty(bcx.tcx());
+        let result_types = match lv_ty.sty {
+            ty::TyTuple(ref tys) => tys,
+            _ => bcx.tcx().sess.span_bug(
+                self.mir.span,
+                &format!("bad final argument to \"rust-call\" fn {:?}", lv_ty))
+        };
+
+        let base_repr = adt::represent_type(bcx.ccx(), lv_ty);
+        let base = adt::MaybeSizedValue::sized(lv.llval);
+        result_types.iter().enumerate().map(|(n, &ty)| {
+            self.trans_load(bcx, bcx.with_block(|bcx| {
+                adt::trans_field_ptr(bcx, &base_repr, base, Disr(0), n)
+            }), ty)
+        }).collect()
+    }
 }
diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs
index 8f64e85de4b..f07464592fa 100644
--- a/src/librustc_typeck/check/coercion.rs
+++ b/src/librustc_typeck/check/coercion.rs
@@ -67,7 +67,7 @@ use middle::traits::{self, ObligationCause};
 use middle::traits::{predicate_for_trait_def, report_selection_error};
 use middle::ty::adjustment::{AutoAdjustment, AutoDerefRef, AdjustDerefRef};
 use middle::ty::adjustment::{AutoPtr, AutoUnsafe, AdjustReifyFnPointer};
-use middle::ty::adjustment::{AdjustUnsafeFnPointer};
+use middle::ty::adjustment::{AdjustUnsafeFnPointer, AdjustMutToConstPointer};
 use middle::ty::{self, LvaluePreference, TypeAndMut, Ty};
 use middle::ty::fold::TypeFoldable;
 use middle::ty::error::TypeError;
@@ -427,6 +427,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
                 autoref: Some(AutoUnsafe(mutbl_b)),
                 unsize: None
             })))
+        } else if mt_a.mutbl != mutbl_b {
+            Ok(Some(AdjustMutToConstPointer))
         } else {
             Ok(None)
         }
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 3768a2e3033..7ab4975c8b8 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -3179,8 +3179,8 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
             check_struct_fields_on_error(fcx, expr.id, fields, base_expr);
             return;
         }
-        let (adt, variant) = match fcx.def_struct_variant(def, path.span) {
-            Some((adt, variant)) => (adt, variant),
+        let variant = match fcx.def_struct_variant(def, path.span) {
+            Some((_, variant)) => variant,
             None => {
                 span_err!(fcx.tcx().sess, path.span, E0071,
                           "`{}` does not name a structure",
@@ -3195,12 +3195,23 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
 
         check_expr_struct_fields(fcx, expr_ty, expr.span, variant, fields,
                                  base_expr.is_none());
-
         if let &Some(ref base_expr) = base_expr {
             check_expr_has_type(fcx, base_expr, expr_ty);
-            if adt.adt_kind() == ty::AdtKind::Enum {
-                span_err!(tcx.sess, base_expr.span, E0436,
-                          "functional record update syntax requires a struct");
+            match expr_ty.sty {
+                ty::TyStruct(adt, substs) => {
+                    fcx.inh.tables.borrow_mut().fru_field_types.insert(
+                        expr.id,
+                        adt.struct_variant().fields.iter().map(|f| {
+                            fcx.normalize_associated_types_in(
+                                expr.span, &f.ty(tcx, substs)
+                            )
+                        }).collect()
+                    );
+                }
+                _ => {
+                    span_err!(tcx.sess, base_expr.span, E0436,
+                              "functional record update syntax requires a struct");
+                }
             }
         }
     }
diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs
index e663e449cfc..9b8b6dedb63 100644
--- a/src/librustc_typeck/check/writeback.rs
+++ b/src/librustc_typeck/check/writeback.rs
@@ -43,6 +43,7 @@ pub fn resolve_type_vars_in_expr(fcx: &FnCtxt, e: &hir::Expr) {
     wbcx.visit_upvar_borrow_map();
     wbcx.visit_closures();
     wbcx.visit_liberated_fn_sigs();
+    wbcx.visit_fru_field_types();
 }
 
 pub fn resolve_type_vars_in_fn(fcx: &FnCtxt,
@@ -64,6 +65,7 @@ pub fn resolve_type_vars_in_fn(fcx: &FnCtxt,
     wbcx.visit_upvar_borrow_map();
     wbcx.visit_closures();
     wbcx.visit_liberated_fn_sigs();
+    wbcx.visit_fru_field_types();
 }
 
 ///////////////////////////////////////////////////////////////////////////
@@ -305,6 +307,10 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
                         adjustment::AdjustReifyFnPointer
                     }
 
+                    adjustment::AdjustMutToConstPointer => {
+                        adjustment::AdjustMutToConstPointer
+                    }
+
                     adjustment::AdjustUnsafeFnPointer => {
                         adjustment::AdjustUnsafeFnPointer
                     }
@@ -367,6 +373,13 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
         }
     }
 
+    fn visit_fru_field_types(&self) {
+        for (&node_id, ftys) in self.fcx.inh.tables.borrow().fru_field_types.iter() {
+            let ftys = self.resolve(ftys, ResolvingFieldTypes(node_id));
+            self.tcx().tables.borrow_mut().fru_field_types.insert(node_id, ftys);
+        }
+    }
+
     fn resolve<T:TypeFoldable<'tcx>>(&self, t: &T, reason: ResolveReason) -> T {
         t.fold_with(&mut Resolver::new(self.fcx, reason))
     }
@@ -383,6 +396,7 @@ enum ResolveReason {
     ResolvingUpvar(ty::UpvarId),
     ResolvingClosure(DefId),
     ResolvingFnSig(ast::NodeId),
+    ResolvingFieldTypes(ast::NodeId)
 }
 
 impl ResolveReason {
@@ -397,6 +411,9 @@ impl ResolveReason {
             ResolvingFnSig(id) => {
                 tcx.map.span(id)
             }
+            ResolvingFieldTypes(id) => {
+                tcx.map.span(id)
+            }
             ResolvingClosure(did) => {
                 if let Some(node_id) = tcx.map.as_local_node_id(did) {
                     tcx.expr_span(node_id)
@@ -474,14 +491,14 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> {
                               "cannot determine a type for this closure")
                 }
 
-                ResolvingFnSig(id) => {
+                ResolvingFnSig(id) | ResolvingFieldTypes(id) => {
                     // any failures here should also fail when
                     // resolving the patterns, closure types, or
                     // something else.
                     let span = self.reason.span(self.tcx);
                     self.tcx.sess.delay_span_bug(
                         span,
-                        &format!("cannot resolve some aspect of fn sig for {:?}", id));
+                        &format!("cannot resolve some aspect of data for {:?}", id));
                 }
             }
         }
diff --git a/src/test/auxiliary/dummy_mir_pass.rs b/src/test/auxiliary/dummy_mir_pass.rs
index df9ea4ab012..16ef965e0db 100644
--- a/src/test/auxiliary/dummy_mir_pass.rs
+++ b/src/test/auxiliary/dummy_mir_pass.rs
@@ -21,17 +21,14 @@ extern crate syntax;
 use rustc::mir::transform::MirPass;
 use rustc::mir::repr::{Mir, Literal};
 use rustc::mir::visit::MutVisitor;
-use rustc::middle::ty;
+use rustc::middle::infer::InferCtxt;
 use rustc::middle::const_eval::ConstVal;
-use rustc::lint::{LateContext, LintContext, LintPass, LateLintPass, LateLintPassObject, LintArray};
 use rustc_plugin::Registry;
-use rustc_front::hir;
-use syntax::attr;
 
 struct Pass;
 
 impl MirPass for Pass {
-    fn run_on_mir<'tcx>(&mut self, mir: &mut Mir<'tcx>, tcx: &ty::ctxt<'tcx>) {
+    fn run_on_mir<'a, 'tcx>(&mut self, mir: &mut Mir<'tcx>, _: &InferCtxt<'a, 'tcx>) {
         Visitor.visit_mir(mir)
     }
 }
diff --git a/src/test/run-pass/mir_augmented_assignments.rs b/src/test/run-pass/mir_augmented_assignments.rs
new file mode 100644
index 00000000000..c85ac458edd
--- /dev/null
+++ b/src/test/run-pass/mir_augmented_assignments.rs
@@ -0,0 +1,187 @@
+// 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.
+
+#![feature(augmented_assignments)]
+#![feature(op_assign_traits)]
+#![feature(rustc_attrs)]
+
+use std::mem;
+use std::ops::{
+    AddAssign, BitAndAssign, BitOrAssign, BitXorAssign, DivAssign, MulAssign, RemAssign,
+    ShlAssign, ShrAssign, SubAssign,
+};
+
+#[derive(Debug, PartialEq)]
+struct Int(i32);
+
+struct Slice([i32]);
+
+impl Slice {
+    fn new(slice: &mut [i32]) -> &mut Slice {
+        unsafe {
+            mem::transmute(slice)
+        }
+    }
+}
+
+fn main() {
+    main_mir();
+}
+
+#[rustc_mir]
+fn main_mir() {
+    let mut x = Int(1);
+
+    x += Int(2);
+    assert_eq!(x, Int(0b11));
+
+    x &= Int(0b01);
+    assert_eq!(x, Int(0b01));
+
+    x |= Int(0b10);
+    assert_eq!(x, Int(0b11));
+
+    x ^= Int(0b01);
+    assert_eq!(x, Int(0b10));
+
+    x /= Int(2);
+    assert_eq!(x, Int(1));
+
+    x *= Int(3);
+    assert_eq!(x, Int(3));
+
+    x %= Int(2);
+    assert_eq!(x, Int(1));
+
+    // overloaded RHS
+    x <<= 1u8;
+    assert_eq!(x, Int(2));
+
+    x <<= 1u16;
+    assert_eq!(x, Int(4));
+
+    x >>= 1u8;
+    assert_eq!(x, Int(2));
+
+    x >>= 1u16;
+    assert_eq!(x, Int(1));
+
+    x -= Int(1);
+    assert_eq!(x, Int(0));
+
+    // indexed LHS
+    // FIXME(mir-drop): use the vec![..] macro
+    let mut v = Vec::new();
+    v.push(Int(1));
+    v.push(Int(2));
+    v[0] += Int(2);
+    assert_eq!(v[0], Int(3));
+
+    // unsized RHS
+    let mut array = [0, 1, 2];
+    *Slice::new(&mut array) += 1;
+    assert_eq!(array[0], 1);
+    assert_eq!(array[1], 2);
+    assert_eq!(array[2], 3);
+
+}
+
+impl AddAssign for Int {
+    #[rustc_mir]
+    fn add_assign(&mut self, rhs: Int) {
+        self.0 += rhs.0;
+    }
+}
+
+impl BitAndAssign for Int {
+    #[rustc_mir]
+    fn bitand_assign(&mut self, rhs: Int) {
+        self.0 &= rhs.0;
+    }
+}
+
+impl BitOrAssign for Int {
+    #[rustc_mir]
+    fn bitor_assign(&mut self, rhs: Int) {
+        self.0 |= rhs.0;
+    }
+}
+
+impl BitXorAssign for Int {
+    #[rustc_mir]
+    fn bitxor_assign(&mut self, rhs: Int) {
+        self.0 ^= rhs.0;
+    }
+}
+
+impl DivAssign for Int {
+    #[rustc_mir]
+    fn div_assign(&mut self, rhs: Int) {
+        self.0 /= rhs.0;
+    }
+}
+
+impl MulAssign for Int {
+    #[rustc_mir]
+    fn mul_assign(&mut self, rhs: Int) {
+        self.0 *= rhs.0;
+    }
+}
+
+impl RemAssign for Int {
+    #[rustc_mir]
+    fn rem_assign(&mut self, rhs: Int) {
+        self.0 %= rhs.0;
+    }
+}
+
+impl ShlAssign<u8> for Int {
+    #[rustc_mir]
+    fn shl_assign(&mut self, rhs: u8) {
+        self.0 <<= rhs;
+    }
+}
+
+impl ShlAssign<u16> for Int {
+    #[rustc_mir]
+    fn shl_assign(&mut self, rhs: u16) {
+        self.0 <<= rhs;
+    }
+}
+
+impl ShrAssign<u8> for Int {
+    #[rustc_mir]
+    fn shr_assign(&mut self, rhs: u8) {
+        self.0 >>= rhs;
+    }
+}
+
+impl ShrAssign<u16> for Int {
+    #[rustc_mir]
+    fn shr_assign(&mut self, rhs: u16) {
+        self.0 >>= rhs;
+    }
+}
+
+impl SubAssign for Int {
+    #[rustc_mir]
+    fn sub_assign(&mut self, rhs: Int) {
+        self.0 -= rhs.0;
+    }
+}
+
+impl AddAssign<i32> for Slice {
+    #[rustc_mir]
+    fn add_assign(&mut self, rhs: i32) {
+        for lhs in &mut self.0 {
+            *lhs += rhs;
+        }
+    }
+}
diff --git a/src/test/run-pass/mir_autoderef.rs b/src/test/run-pass/mir_autoderef.rs
new file mode 100644
index 00000000000..81712e4569f
--- /dev/null
+++ b/src/test/run-pass/mir_autoderef.rs
@@ -0,0 +1,41 @@
+// 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.
+
+#![feature(rustc_attrs)]
+
+use std::ops::{Deref, DerefMut};
+
+pub struct MyRef(u32);
+
+impl Deref for MyRef {
+    type Target = u32;
+    fn deref(&self) -> &u32 { &self.0 }
+}
+
+impl DerefMut for MyRef {
+    fn deref_mut(&mut self) -> &mut u32 { &mut self.0 }
+}
+
+
+#[rustc_mir]
+fn deref(x: &MyRef) -> &u32 {
+    x
+}
+
+#[rustc_mir]
+fn deref_mut(x: &mut MyRef) -> &mut u32 {
+    x
+}
+
+fn main() {
+    let mut r = MyRef(2);
+    assert_eq!(deref(&r) as *const _, &r.0 as *const _);
+    assert_eq!(deref_mut(&mut r) as *mut _, &mut r.0 as *mut _);
+}
diff --git a/src/test/run-pass/mir_struct_with_assoc_ty.rs b/src/test/run-pass/mir_struct_with_assoc_ty.rs
new file mode 100644
index 00000000000..1f75369b94a
--- /dev/null
+++ b/src/test/run-pass/mir_struct_with_assoc_ty.rs
@@ -0,0 +1,41 @@
+// 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.
+
+#![feature(rustc_attrs)]
+
+use std::marker::PhantomData;
+
+pub trait DataBind {
+    type Data;
+}
+
+impl<T> DataBind for Global<T> {
+    type Data = T;
+}
+
+pub struct Global<T>(PhantomData<T>);
+
+pub struct Data {
+    pub offsets: <Global<[u32; 2]> as DataBind>::Data,
+}
+
+#[rustc_mir]
+fn create_data() -> Data {
+    let mut d = Data { offsets: [1, 2] };
+    d.offsets[0] = 3;
+    d
+}
+
+
+fn main() {
+    let d = create_data();
+    assert_eq!(d.offsets[0], 3);
+    assert_eq!(d.offsets[1], 2);
+}
diff --git a/src/test/run-pass/mir_trans_calls.rs b/src/test/run-pass/mir_trans_calls.rs
index fc45fbf7278..b8b7ecbf03c 100644
--- a/src/test/run-pass/mir_trans_calls.rs
+++ b/src/test/run-pass/mir_trans_calls.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(rustc_attrs)]
+#![feature(rustc_attrs, unboxed_closures, fn_traits)]
 
 #[rustc_mir]
 fn test1(a: isize, b: (i32, i32), c: &[i32]) -> (isize, (i32, i32), &[i32]) {
@@ -117,6 +117,27 @@ fn test_fn_impl(f: &&Fn(i32, i32) -> i32, x: i32, y: i32) -> i32 {
     f(x, y)
 }
 
+#[rustc_mir]
+fn test_fn_direct_call<F>(f: &F, x: i32, y: i32) -> i32
+    where F: Fn(i32, i32) -> i32
+{
+    f.call((x, y))
+}
+
+#[rustc_mir]
+fn test_fn_const_call<F>(f: &F) -> i32
+    where F: Fn(i32, i32) -> i32
+{
+    f.call((100, -1))
+}
+
+#[rustc_mir]
+fn test_fn_nil_call<F>(f: &F) -> i32
+    where F: Fn() -> i32
+{
+    f()
+}
+
 fn main() {
     assert_eq!(test1(1, (2, 3), &[4, 5, 6]), (1, (2, 3), &[4, 5, 6][..]));
     assert_eq!(test2(98), 98);
@@ -128,9 +149,14 @@ fn main() {
     assert_eq!(test8(), 2);
     assert_eq!(test9(), 41 + 42 * 43);
 
-    let closure = |x: i32, y: i32| { x + y };
-    assert_eq!(test_closure(&closure, 100, 1), 101);
+    let r = 3;
+    let closure = |x: i32, y: i32| { r*(x + (y*2)) };
+    assert_eq!(test_fn_const_call(&closure), 294);
+    assert_eq!(test_closure(&closure, 100, 1), 306);
     let function_object = &closure as &Fn(i32, i32) -> i32;
-    assert_eq!(test_fn_object(function_object, 100, 2), 102);
-    assert_eq!(test_fn_impl(&function_object, 100, 3), 103);
+    assert_eq!(test_fn_object(function_object, 100, 2), 312);
+    assert_eq!(test_fn_impl(&function_object, 100, 3), 318);
+    assert_eq!(test_fn_direct_call(&closure, 100, 4), 324);
+
+    assert_eq!(test_fn_nil_call(&(|| 42)), 42);
 }