about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2016-03-09 20:16:20 -0800
committerbors <bors@rust-lang.org>2016-03-09 20:16:20 -0800
commitbcda58f49133921abd091d7f800732fe2c4e5a98 (patch)
treebf0595da87152141630ebfbc469b1de313f36f2d /src
parent0b9995b24ccc7489e0082199793c300015753dd1 (diff)
parent3855fa99cad8412be5c2f322fa40a0d415d6cbd6 (diff)
downloadrust-bcda58f49133921abd091d7f800732fe2c4e5a98.tar.gz
rust-bcda58f49133921abd091d7f800732fe2c4e5a98.zip
Auto merge of #31710 - eddyb:reify, r=nikomatsakis
Distinguish fn item types to allow reification from nothing to fn pointers.

The first commit is a rebase of #26284, except for files that have moved since.

This is a [breaking-change], due to:
* each FFI function has a distinct type, like all other functions currently do
* all generic parameters on functions are recorded in their item types, e.g.:
`size_of::<u8>` & `size_of::<i8>`'s types differ despite their identical signature.
* function items are zero-sized, which will stop transmutes from working on them

The first two cases are handled in most cases with the new coerce-unify logic,
which will combine incompatible function item types into function pointers,
at the outer-most level of if-else chains, match arms and array literals.

The last case is specially handled during type-checking such that transmutes
from a function item type to a pointer or integer type will continue to work for
another release cycle, but are being linted against. To get rid of warnings and
ensure your code will continue to compile, cast to a pointer before transmuting.
Diffstat (limited to 'src')
-rw-r--r--src/librustc/lint/builtin.rs9
-rw-r--r--src/librustc/lint/context.rs3
-rw-r--r--src/librustc/middle/effect.rs3
-rw-r--r--src/librustc/middle/expr_use_visitor.rs2
-rw-r--r--src/librustc/middle/infer/freshen.rs3
-rw-r--r--src/librustc/middle/infer/mod.rs14
-rw-r--r--src/librustc/middle/intrinsicck.rs6
-rw-r--r--src/librustc/middle/subst.rs4
-rw-r--r--src/librustc/middle/traits/coherence.rs3
-rw-r--r--src/librustc/middle/traits/mod.rs2
-rw-r--r--src/librustc/middle/traits/project.rs2
-rw-r--r--src/librustc/middle/traits/select.rs19
-rw-r--r--src/librustc/middle/traits/structural_impls.rs3
-rw-r--r--src/librustc/middle/ty/adjustment.rs6
-rw-r--r--src/librustc/middle/ty/cast.rs2
-rw-r--r--src/librustc/middle/ty/contents.rs2
-rw-r--r--src/librustc/middle/ty/context.rs38
-rw-r--r--src/librustc/middle/ty/error.rs4
-rw-r--r--src/librustc/middle/ty/fast_reject.rs2
-rw-r--r--src/librustc/middle/ty/flags.rs7
-rw-r--r--src/librustc/middle/ty/outlives.rs3
-rw-r--r--src/librustc/middle/ty/relate.rs24
-rw-r--r--src/librustc/middle/ty/structural_impls.rs16
-rw-r--r--src/librustc/middle/ty/sty.rs41
-rw-r--r--src/librustc/middle/ty/util.rs18
-rw-r--r--src/librustc/middle/ty/walk.rs6
-rw-r--r--src/librustc/middle/ty/wf.rs4
-rw-r--r--src/librustc/mir/repr.rs10
-rw-r--r--src/librustc/util/ppaux.rs31
-rw-r--r--src/librustc_driver/test.rs19
-rw-r--r--src/librustc_lint/builtin.rs4
-rw-r--r--src/librustc_lint/lib.rs4
-rw-r--r--src/librustc_lint/types.rs6
-rw-r--r--src/librustc_metadata/decoder.rs7
-rw-r--r--src/librustc_metadata/tydecode.rs5
-rw-r--r--src/librustc_metadata/tyencode.rs5
-rw-r--r--src/librustc_mir/build/expr/into.rs4
-rw-r--r--src/librustc_mir/build/misc.rs13
-rw-r--r--src/librustc_mir/build/scope.rs2
-rw-r--r--src/librustc_mir/hair/cx/expr.rs16
-rw-r--r--src/librustc_mir/hair/mod.rs10
-rw-r--r--src/librustc_mir/transform/type_check.rs2
-rw-r--r--src/librustc_passes/consts.rs2
-rw-r--r--src/librustc_privacy/lib.rs2
-rw-r--r--src/librustc_trans/trans/adt.rs4
-rw-r--r--src/librustc_trans/trans/asm.rs3
-rw-r--r--src/librustc_trans/trans/attributes.rs4
-rw-r--r--src/librustc_trans/trans/base.rs60
-rw-r--r--src/librustc_trans/trans/callee.rs692
-rw-r--r--src/librustc_trans/trans/closure.rs69
-rw-r--r--src/librustc_trans/trans/collector.rs67
-rw-r--r--src/librustc_trans/trans/common.rs10
-rw-r--r--src/librustc_trans/trans/consts.rs37
-rw-r--r--src/librustc_trans/trans/debuginfo/metadata.rs5
-rw-r--r--src/librustc_trans/trans/debuginfo/mod.rs2
-rw-r--r--src/librustc_trans/trans/debuginfo/type_names.rs3
-rw-r--r--src/librustc_trans/trans/declare.rs3
-rw-r--r--src/librustc_trans/trans/expr.rs310
-rw-r--r--src/librustc_trans/trans/foreign.rs14
-rw-r--r--src/librustc_trans/trans/glue.rs33
-rw-r--r--src/librustc_trans/trans/intrinsic.rs86
-rw-r--r--src/librustc_trans/trans/meth.rs366
-rw-r--r--src/librustc_trans/trans/mir/block.rs145
-rw-r--r--src/librustc_trans/trans/mir/constant.rs38
-rw-r--r--src/librustc_trans/trans/mir/did.rs171
-rw-r--r--src/librustc_trans/trans/mir/mod.rs1
-rw-r--r--src/librustc_trans/trans/mir/rvalue.rs16
-rw-r--r--src/librustc_trans/trans/monomorphize.rs11
-rw-r--r--src/librustc_trans/trans/type_of.rs34
-rw-r--r--src/librustc_typeck/astconv.rs3
-rw-r--r--src/librustc_typeck/check/_match.rs100
-rw-r--r--src/librustc_typeck/check/callee.rs7
-rw-r--r--src/librustc_typeck/check/cast.rs27
-rw-r--r--src/librustc_typeck/check/coercion.rs380
-rw-r--r--src/librustc_typeck/check/compare_method.rs12
-rw-r--r--src/librustc_typeck/check/demand.rs50
-rw-r--r--src/librustc_typeck/check/dropck.rs2
-rw-r--r--src/librustc_typeck/check/intrinsic.rs16
-rw-r--r--src/librustc_typeck/check/method/confirm.rs178
-rw-r--r--src/librustc_typeck/check/method/mod.rs7
-rw-r--r--src/librustc_typeck/check/method/probe.rs2
-rw-r--r--src/librustc_typeck/check/method/suggest.rs4
-rw-r--r--src/librustc_typeck/check/mod.rs357
-rw-r--r--src/librustc_typeck/check/regionck.rs2
-rw-r--r--src/librustc_typeck/check/wfcheck.rs4
-rw-r--r--src/librustc_typeck/coherence/mod.rs12
-rw-r--r--src/librustc_typeck/collect.rs69
-rw-r--r--src/librustc_typeck/constrained_type_params.rs3
-rw-r--r--src/librustc_typeck/lib.rs20
-rw-r--r--src/librustc_typeck/variance/constraints.rs3
-rw-r--r--src/librustdoc/clean/inline.rs2
-rw-r--r--src/librustdoc/clean/mod.rs3
-rw-r--r--src/test/auxiliary/issue13507.rs72
-rw-r--r--src/test/compile-fail/fn-item-type.rs38
-rw-r--r--src/test/compile-fail/invalid-intrinsic.rs16
-rw-r--r--src/test/compile-fail/issue-13482-2.rs2
-rw-r--r--src/test/compile-fail/issue-17728.rs4
-rw-r--r--src/test/compile-fail/issue-2149.rs2
-rw-r--r--src/test/compile-fail/transmute-from-fn-item-types-lint.rs51
-rw-r--r--src/test/compile-fail/variadic-ffi-3.rs2
-rw-r--r--src/test/pretty/issue-4264.pp6
-rw-r--r--src/test/run-pass/coerce-unify.rs77
-rw-r--r--src/test/run-pass/enum-clike-ffi-as-int.rs13
-rw-r--r--src/test/run-pass/fn-item-type-zero-sized.rs22
-rw-r--r--src/test/run-pass/issue-13507-2.rs34
-rw-r--r--src/test/run-pass/mir_refs_correct.rs81
-rw-r--r--src/test/run-pass/nullable-pointer-ffi-compat.rs5
-rw-r--r--src/test/run-pass/nullable-pointer-iotareduction.rs11
-rw-r--r--src/test/run-pass/transmute-from-fn-item-types.rs27
109 files changed, 2056 insertions, 2242 deletions
diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs
index 4bb69a2688a..97a550a4076 100644
--- a/src/librustc/lint/builtin.rs
+++ b/src/librustc/lint/builtin.rs
@@ -148,6 +148,12 @@ declare_lint! {
     "uses of #[derive] with raw pointers are rarely correct"
 }
 
+declare_lint! {
+    pub TRANSMUTE_FROM_FN_ITEM_TYPES,
+    Warn,
+    "transmute from function item type to pointer-sized type erroneously allowed"
+}
+
 /// Does nothing as a lint pass, but registers some `Lint`s
 /// which are used by other parts of the compiler.
 #[derive(Copy, Clone)]
@@ -177,7 +183,8 @@ impl LintPass for HardwiredLints {
             INVALID_TYPE_PARAM_DEFAULT,
             MATCH_OF_UNIT_VARIANT_VIA_PAREN_DOTDOT,
             CONST_ERR,
-            RAW_POINTER_DERIVE
+            RAW_POINTER_DERIVE,
+            TRANSMUTE_FROM_FN_ITEM_TYPES
         )
     }
 }
diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs
index 023b9722504..c11e9dc822e 100644
--- a/src/librustc/lint/context.rs
+++ b/src/librustc/lint/context.rs
@@ -1287,6 +1287,9 @@ pub fn check_crate(tcx: &TyCtxt, access_levels: &AccessLevels) {
     }
 
     *tcx.node_lint_levels.borrow_mut() = cx.node_levels.into_inner();
+
+    // Put the lint store back in the session.
+    mem::replace(&mut *tcx.sess.lint_store.borrow_mut(), cx.lints);
 }
 
 pub fn check_ast_crate(sess: &Session, krate: &ast::Crate) {
diff --git a/src/librustc/middle/effect.rs b/src/librustc/middle/effect.rs
index e3d05388f52..e6821cf639e 100644
--- a/src/librustc/middle/effect.rs
+++ b/src/librustc/middle/effect.rs
@@ -44,7 +44,8 @@ enum RootUnsafeContext {
 
 fn type_is_unsafe_function(ty: Ty) -> bool {
     match ty.sty {
-        ty::TyBareFn(_, ref f) => f.unsafety == hir::Unsafety::Unsafe,
+        ty::TyFnDef(_, _, ref f) |
+        ty::TyFnPtr(ref f) => f.unsafety == hir::Unsafety::Unsafe,
         _ => false,
     }
 }
diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs
index 5ddea691743..a87ce1206b4 100644
--- a/src/librustc/middle/expr_use_visitor.rs
+++ b/src/librustc/middle/expr_use_visitor.rs
@@ -556,7 +556,7 @@ impl<'d,'t,'a,'tcx> ExprUseVisitor<'d,'t,'a,'tcx> {
                callee, callee_ty);
         let call_scope = self.tcx().region_maps.node_extent(call.id);
         match callee_ty.sty {
-            ty::TyBareFn(..) => {
+            ty::TyFnDef(..) | ty::TyFnPtr(_) => {
                 self.consume_expr(callee);
             }
             ty::TyError => { }
diff --git a/src/librustc/middle/infer/freshen.rs b/src/librustc/middle/infer/freshen.rs
index b64fa688d51..6c0dd9b608d 100644
--- a/src/librustc/middle/infer/freshen.rs
+++ b/src/librustc/middle/infer/freshen.rs
@@ -161,7 +161,8 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> {
             ty::TySlice(..) |
             ty::TyRawPtr(..) |
             ty::TyRef(..) |
-            ty::TyBareFn(..) |
+            ty::TyFnDef(..) |
+            ty::TyFnPtr(_) |
             ty::TyTrait(..) |
             ty::TyStruct(..) |
             ty::TyClosure(..) |
diff --git a/src/librustc/middle/infer/mod.rs b/src/librustc/middle/infer/mod.rs
index b9a5b32b71d..1e3546269db 100644
--- a/src/librustc/middle/infer/mod.rs
+++ b/src/librustc/middle/infer/mod.rs
@@ -454,7 +454,7 @@ pub fn mk_eqty<'a, 'tcx>(cx: &InferCtxt<'a, 'tcx>,
                          -> UnitResult<'tcx>
 {
     debug!("mk_eqty({:?} <: {:?})", a, b);
-    cx.commit_if_ok(|_| cx.eq_types(a_is_expected, origin, a, b))
+    cx.eq_types(a_is_expected, origin, a, b)
 }
 
 pub fn mk_eq_trait_refs<'a, 'tcx>(cx: &InferCtxt<'a, 'tcx>,
@@ -466,7 +466,7 @@ pub fn mk_eq_trait_refs<'a, 'tcx>(cx: &InferCtxt<'a, 'tcx>,
 {
     debug!("mk_eq_trait_refs({:?} <: {:?})",
            a, b);
-    cx.commit_if_ok(|_| cx.eq_trait_refs(a_is_expected, origin, a.clone(), b.clone()))
+    cx.eq_trait_refs(a_is_expected, origin, a, b)
 }
 
 pub fn mk_sub_poly_trait_refs<'a, 'tcx>(cx: &InferCtxt<'a, 'tcx>,
@@ -478,7 +478,7 @@ pub fn mk_sub_poly_trait_refs<'a, 'tcx>(cx: &InferCtxt<'a, 'tcx>,
 {
     debug!("mk_sub_poly_trait_refs({:?} <: {:?})",
            a, b);
-    cx.commit_if_ok(|_| cx.sub_poly_trait_refs(a_is_expected, origin, a.clone(), b.clone()))
+    cx.sub_poly_trait_refs(a_is_expected, origin, a, b)
 }
 
 fn expected_found<T>(a_is_expected: bool,
@@ -1351,18 +1351,18 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     }
 
     pub fn report_mismatched_types(&self,
-                                   span: Span,
+                                   origin: TypeOrigin,
                                    expected: Ty<'tcx>,
                                    actual: Ty<'tcx>,
-                                   err: &TypeError<'tcx>) {
+                                   err: TypeError<'tcx>) {
         let trace = TypeTrace {
-            origin: TypeOrigin::Misc(span),
+            origin: origin,
             values: Types(ExpectedFound {
                 expected: expected,
                 found: actual
             })
         };
-        self.report_and_explain_type_error(trace, err);
+        self.report_and_explain_type_error(trace, &err);
     }
 
     pub fn report_conflicting_default_types(&self,
diff --git a/src/librustc/middle/intrinsicck.rs b/src/librustc/middle/intrinsicck.rs
index 7de8904e3f2..fd857513e5b 100644
--- a/src/librustc/middle/intrinsicck.rs
+++ b/src/librustc/middle/intrinsicck.rs
@@ -12,7 +12,7 @@ use dep_graph::DepNode;
 use middle::def::Def;
 use middle::def_id::DefId;
 use middle::subst::{Subst, Substs, EnumeratedItems};
-use middle::ty::{TransmuteRestriction, TyCtxt, TyBareFn};
+use middle::ty::{TransmuteRestriction, TyCtxt};
 use middle::ty::{self, Ty, TypeFoldable};
 
 use std::fmt;
@@ -53,7 +53,7 @@ struct IntrinsicCheckingVisitor<'a, 'tcx: 'a> {
 impl<'a, 'tcx> IntrinsicCheckingVisitor<'a, 'tcx> {
     fn def_id_is_transmute(&self, def_id: DefId) -> bool {
         let intrinsic = match self.tcx.lookup_item_type(def_id).ty.sty {
-            ty::TyBareFn(_, ref bfty) => bfty.abi == RustIntrinsic,
+            ty::TyFnDef(_, _, ref bfty) => bfty.abi == RustIntrinsic,
             _ => return false
         };
         intrinsic && self.tcx.item_name(def_id).as_str() == "transmute"
@@ -238,7 +238,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for IntrinsicCheckingVisitor<'a, 'tcx> {
                 Def::Fn(did) if self.def_id_is_transmute(did) => {
                     let typ = self.tcx.node_id_to_type(expr.id);
                     match typ.sty {
-                        TyBareFn(_, ref bare_fn_ty) if bare_fn_ty.abi == RustIntrinsic => {
+                        ty::TyFnDef(_, _, ref bare_fn_ty) if bare_fn_ty.abi == RustIntrinsic => {
                             if let ty::FnConverging(to) = bare_fn_ty.sig.0.output {
                                 let from = bare_fn_ty.sig.0.inputs[0];
                                 self.check_transmute(expr.span, from, to, expr.id);
diff --git a/src/librustc/middle/subst.rs b/src/librustc/middle/subst.rs
index 9cc94402b16..02dfeb80b92 100644
--- a/src/librustc/middle/subst.rs
+++ b/src/librustc/middle/subst.rs
@@ -148,11 +148,11 @@ impl<'tcx> Substs<'tcx> {
         Substs { types: types, regions: regions }
     }
 
-    pub fn with_method_from(self,
+    pub fn with_method_from(&self,
                             meth_substs: &Substs<'tcx>)
                             -> Substs<'tcx>
     {
-        let Substs { types, regions } = self;
+        let Substs { types, regions } = self.clone();
         let types = types.with_slice(FnSpace, meth_substs.types.get_slice(FnSpace));
         let regions = regions.map(|r| {
             r.with_slice(FnSpace, meth_substs.regions().get_slice(FnSpace))
diff --git a/src/librustc/middle/traits/coherence.rs b/src/librustc/middle/traits/coherence.rs
index b0970457892..b79849e87ff 100644
--- a/src/librustc/middle/traits/coherence.rs
+++ b/src/librustc/middle/traits/coherence.rs
@@ -301,7 +301,8 @@ fn ty_is_local_constructor<'tcx>(tcx: &TyCtxt<'tcx>,
         ty::TyUint(..) |
         ty::TyFloat(..) |
         ty::TyStr |
-        ty::TyBareFn(..) |
+        ty::TyFnDef(..) |
+        ty::TyFnPtr(_) |
         ty::TyArray(..) |
         ty::TySlice(..) |
         ty::TyRawPtr(..) |
diff --git a/src/librustc/middle/traits/mod.rs b/src/librustc/middle/traits/mod.rs
index f0ff0380aaa..8a2f0c0c093 100644
--- a/src/librustc/middle/traits/mod.rs
+++ b/src/librustc/middle/traits/mod.rs
@@ -278,7 +278,7 @@ pub enum Vtable<'tcx, N> {
 #[derive(Clone, PartialEq, Eq)]
 pub struct VtableImplData<'tcx, N> {
     pub impl_def_id: DefId,
-    pub substs: subst::Substs<'tcx>,
+    pub substs: &'tcx subst::Substs<'tcx>,
     pub nested: Vec<N>
 }
 
diff --git a/src/librustc/middle/traits/project.rs b/src/librustc/middle/traits/project.rs
index b19771420bd..e36307feddb 100644
--- a/src/librustc/middle/traits/project.rs
+++ b/src/librustc/middle/traits/project.rs
@@ -948,7 +948,7 @@ fn confirm_impl_candidate<'cx,'tcx>(
     for impl_item in &selcx.tcx().impl_items.borrow()[&impl_vtable.impl_def_id] {
         if let ty::TypeTraitItem(ref assoc_ty) = impl_or_trait_items_map[&impl_item.def_id()] {
             if assoc_ty.name == obligation.predicate.item_name {
-                return (assoc_ty.ty.unwrap().subst(selcx.tcx(), &impl_vtable.substs),
+                return (assoc_ty.ty.unwrap().subst(selcx.tcx(), impl_vtable.substs),
                         impl_vtable.nested);
             }
         }
diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs
index 373ec37663f..2ecfa119007 100644
--- a/src/librustc/middle/traits/select.rs
+++ b/src/librustc/middle/traits/select.rs
@@ -1286,7 +1286,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             }
 
             // provide an impl, but only for suitable `fn` pointers
-            ty::TyBareFn(_, &ty::BareFnTy {
+            ty::TyFnDef(_, _, &ty::BareFnTy {
+                unsafety: hir::Unsafety::Normal,
+                abi: Abi::Rust,
+                sig: ty::Binder(ty::FnSig {
+                    inputs: _,
+                    output: ty::FnConverging(_),
+                    variadic: false
+                })
+            }) |
+            ty::TyFnPtr(&ty::BareFnTy {
                 unsafety: hir::Unsafety::Normal,
                 abi: Abi::Rust,
                 sig: ty::Binder(ty::FnSig {
@@ -1646,7 +1655,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             ty::TyInt(_) |
             ty::TyBool |
             ty::TyFloat(_) |
-            ty::TyBareFn(..) |
+            ty::TyFnDef(..) |
+            ty::TyFnPtr(_) |
             ty::TyChar => {
                 // safe for everything
                 ok_if(Vec::new())
@@ -1850,7 +1860,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             ty::TyInt(_) |
             ty::TyBool |
             ty::TyFloat(_) |
-            ty::TyBareFn(..) |
+            ty::TyFnDef(..) |
+            ty::TyFnPtr(_) |
             ty::TyStr |
             ty::TyError |
             ty::TyInfer(ty::IntVar(_)) |
@@ -2294,7 +2305,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         impl_obligations.append(&mut substs.obligations);
 
         VtableImplData { impl_def_id: impl_def_id,
-                         substs: substs.value,
+                         substs: self.tcx().mk_substs(substs.value),
                          nested: impl_obligations }
     }
 
diff --git a/src/librustc/middle/traits/structural_impls.rs b/src/librustc/middle/traits/structural_impls.rs
index 453420e2a54..903b7c80baf 100644
--- a/src/librustc/middle/traits/structural_impls.rs
+++ b/src/librustc/middle/traits/structural_impls.rs
@@ -147,9 +147,10 @@ impl<'tcx, O: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::Obligation<'tcx
 
 impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::VtableImplData<'tcx, N> {
     fn super_fold_with<F:TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
+        let substs = self.substs.fold_with(folder);
         traits::VtableImplData {
             impl_def_id: self.impl_def_id,
-            substs: self.substs.fold_with(folder),
+            substs: folder.tcx().mk_substs(substs),
             nested: self.nested.fold_with(folder),
         }
     }
diff --git a/src/librustc/middle/ty/adjustment.rs b/src/librustc/middle/ty/adjustment.rs
index 40581cfa1c5..e4c293a74e8 100644
--- a/src/librustc/middle/ty/adjustment.rs
+++ b/src/librustc/middle/ty/adjustment.rs
@@ -155,8 +155,8 @@ impl<'tcx> ty::TyS<'tcx> {
                 match *adjustment {
                     AdjustReifyFnPointer => {
                         match self.sty {
-                            ty::TyBareFn(Some(_), b) => {
-                                cx.mk_fn(None, b)
+                            ty::TyFnDef(_, _, b) => {
+                                cx.mk_ty(ty::TyFnPtr(b))
                             }
                             _ => {
                                 cx.sess.bug(
@@ -168,7 +168,7 @@ impl<'tcx> ty::TyS<'tcx> {
 
                     AdjustUnsafeFnPointer => {
                         match self.sty {
-                            ty::TyBareFn(None, b) => cx.safe_to_unsafe_fn_ty(b),
+                            ty::TyFnPtr(b) => cx.safe_to_unsafe_fn_ty(b),
                             ref b => {
                                 cx.sess.bug(
                                     &format!("AdjustUnsafeFnPointer adjustment on non-fn-ptr: \
diff --git a/src/librustc/middle/ty/cast.rs b/src/librustc/middle/ty/cast.rs
index 8233b6b2b2b..b25d6e0476d 100644
--- a/src/librustc/middle/ty/cast.rs
+++ b/src/librustc/middle/ty/cast.rs
@@ -69,7 +69,7 @@ impl<'tcx> CastTy<'tcx> {
                 Some(CastTy::Int(IntTy::CEnum)),
             ty::TyRawPtr(ref mt) => Some(CastTy::Ptr(mt)),
             ty::TyRef(_, ref mt) => Some(CastTy::RPtr(mt)),
-            ty::TyBareFn(..) => Some(CastTy::FnPtr),
+            ty::TyFnPtr(..) => Some(CastTy::FnPtr),
             _ => None,
         }
     }
diff --git a/src/librustc/middle/ty/contents.rs b/src/librustc/middle/ty/contents.rs
index 8dfa0262f2b..47a15a14b41 100644
--- a/src/librustc/middle/ty/contents.rs
+++ b/src/librustc/middle/ty/contents.rs
@@ -187,7 +187,7 @@ impl<'tcx> ty::TyS<'tcx> {
                 // Scalar and unique types are sendable, and durable
                 ty::TyInfer(ty::FreshIntTy(_)) | ty::TyInfer(ty::FreshFloatTy(_)) |
                 ty::TyBool | ty::TyInt(_) | ty::TyUint(_) | ty::TyFloat(_) |
-                ty::TyBareFn(..) | ty::TyChar => {
+                ty::TyFnDef(..) | ty::TyFnPtr(_) | ty::TyChar => {
                     TC::None
                 }
 
diff --git a/src/librustc/middle/ty/context.rs b/src/librustc/middle/ty/context.rs
index ffc52af19bb..61a7f49f45d 100644
--- a/src/librustc/middle/ty/context.rs
+++ b/src/librustc/middle/ty/context.rs
@@ -41,7 +41,6 @@ use std::borrow::Borrow;
 use std::cell::{Cell, RefCell, Ref};
 use std::hash::{Hash, Hasher};
 use std::rc::Rc;
-use syntax::abi::Abi;
 use syntax::ast::{self, Name, NodeId};
 use syntax::attr;
 use syntax::parse::token::special_idents;
@@ -734,8 +733,8 @@ impl<'tcx> TyCtxt<'tcx> {
     pub fn print_debug_stats(&self) {
         sty_debug_print!(
             self,
-            TyEnum, TyBox, TyArray, TySlice, TyRawPtr, TyRef, TyBareFn, TyTrait,
-            TyStruct, TyClosure, TyTuple, TyParam, TyInfer, TyProjection);
+            TyEnum, TyBox, TyArray, TySlice, TyRawPtr, TyRef, TyFnDef, TyFnPtr,
+            TyTrait, TyStruct, TyClosure, TyTuple, TyParam, TyInfer, TyProjection);
 
         println!("Substs interner: #{}", self.substs_interner.borrow().len());
         println!("BareFnTy interner: #{}", self.bare_fn_interner.borrow().len());
@@ -792,12 +791,11 @@ impl<'tcx> TyCtxt<'tcx> {
     /// Create an unsafe fn ty based on a safe fn ty.
     pub fn safe_to_unsafe_fn_ty(&self, bare_fn: &BareFnTy<'tcx>) -> Ty<'tcx> {
         assert_eq!(bare_fn.unsafety, hir::Unsafety::Normal);
-        let unsafe_fn_ty_a = self.mk_bare_fn(ty::BareFnTy {
+        self.mk_fn_ptr(ty::BareFnTy {
             unsafety: hir::Unsafety::Unsafe,
             abi: bare_fn.abi,
             sig: bare_fn.sig.clone()
-        });
-        self.mk_fn(None, unsafe_fn_ty_a)
+        })
     }
 
     pub fn mk_bare_fn(&self, bare_fn: BareFnTy<'tcx>) -> &'tcx BareFnTy<'tcx> {
@@ -946,26 +944,14 @@ impl<'tcx> TyCtxt<'tcx> {
         self.mk_ty(TyBool)
     }
 
-    pub fn mk_fn(&self,
-                 opt_def_id: Option<DefId>,
-                 fty: &'tcx BareFnTy<'tcx>) -> Ty<'tcx> {
-        self.mk_ty(TyBareFn(opt_def_id, fty))
-    }
-
-    pub fn mk_ctor_fn(&self,
-                      def_id: DefId,
-                      input_tys: &[Ty<'tcx>],
-                      output: Ty<'tcx>) -> Ty<'tcx> {
-        let input_args = input_tys.iter().cloned().collect();
-        self.mk_fn(Some(def_id), self.mk_bare_fn(BareFnTy {
-            unsafety: hir::Unsafety::Normal,
-            abi: Abi::Rust,
-            sig: ty::Binder(ty::FnSig {
-                inputs: input_args,
-                output: ty::FnConverging(output),
-                variadic: false
-            })
-        }))
+    pub fn mk_fn_def(&self, def_id: DefId,
+                     substs: &'tcx Substs<'tcx>,
+                     fty: BareFnTy<'tcx>) -> Ty<'tcx> {
+        self.mk_ty(TyFnDef(def_id, substs, self.mk_bare_fn(fty)))
+    }
+
+    pub fn mk_fn_ptr(&self, fty: BareFnTy<'tcx>) -> Ty<'tcx> {
+        self.mk_ty(TyFnPtr(self.mk_bare_fn(fty)))
     }
 
     pub fn mk_trait(&self,
diff --git a/src/librustc/middle/ty/error.rs b/src/librustc/middle/ty/error.rs
index 39a5069e129..1033af5f331 100644
--- a/src/librustc/middle/ty/error.rs
+++ b/src/librustc/middle/ty/error.rs
@@ -223,8 +223,8 @@ impl<'tcx> ty::TyS<'tcx> {
             ty::TySlice(_) => "slice".to_string(),
             ty::TyRawPtr(_) => "*-ptr".to_string(),
             ty::TyRef(_, _) => "&-ptr".to_string(),
-            ty::TyBareFn(Some(_), _) => format!("fn item"),
-            ty::TyBareFn(None, _) => "fn pointer".to_string(),
+            ty::TyFnDef(..) => format!("fn item"),
+            ty::TyFnPtr(_) => "fn pointer".to_string(),
             ty::TyTrait(ref inner) => {
                 format!("trait {}", cx.item_path_str(inner.principal_def_id()))
             }
diff --git a/src/librustc/middle/ty/fast_reject.rs b/src/librustc/middle/ty/fast_reject.rs
index a42e5fc2e85..fc4db22a8a6 100644
--- a/src/librustc/middle/ty/fast_reject.rs
+++ b/src/librustc/middle/ty/fast_reject.rs
@@ -83,7 +83,7 @@ pub fn simplify_type(tcx: &TyCtxt,
         ty::TyTuple(ref tys) => {
             Some(TupleSimplifiedType(tys.len()))
         }
-        ty::TyBareFn(_, ref f) => {
+        ty::TyFnDef(_, _, ref f) | ty::TyFnPtr(ref f) => {
             Some(FunctionSimplifiedType(f.sig.0.inputs.len()))
         }
         ty::TyProjection(_) | ty::TyParam(_) => {
diff --git a/src/librustc/middle/ty/flags.rs b/src/librustc/middle/ty/flags.rs
index a0b03fe8126..c491bd6ca5e 100644
--- a/src/librustc/middle/ty/flags.rs
+++ b/src/librustc/middle/ty/flags.rs
@@ -134,7 +134,12 @@ impl FlagComputation {
                 self.add_tys(&ts[..]);
             }
 
-            &ty::TyBareFn(_, ref f) => {
+            &ty::TyFnDef(_, substs, ref f) => {
+                self.add_substs(substs);
+                self.add_fn_sig(&f.sig);
+            }
+
+            &ty::TyFnPtr(ref f) => {
                 self.add_fn_sig(&f.sig);
             }
         }
diff --git a/src/librustc/middle/ty/outlives.rs b/src/librustc/middle/ty/outlives.rs
index fc20c1bcb85..9439180a6cd 100644
--- a/src/librustc/middle/ty/outlives.rs
+++ b/src/librustc/middle/ty/outlives.rs
@@ -182,7 +182,8 @@ fn compute_components<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>,
         ty::TyRawPtr(..) |      // ...
         ty::TyRef(..) |         // OutlivesReference
         ty::TyTuple(..) |       // ...
-        ty::TyBareFn(..) |      // OutlivesFunction (*)
+        ty::TyFnDef(..) |       // OutlivesFunction (*)
+        ty::TyFnPtr(_) |        // OutlivesFunction (*)
         ty::TyTrait(..) |       // OutlivesObject, OutlivesFragment (*)
         ty::TyError => {
             // (*) Bare functions and traits are both binders. In the
diff --git a/src/librustc/middle/ty/relate.rs b/src/librustc/middle/ty/relate.rs
index 5d6106a6d77..6da65c85f91 100644
--- a/src/librustc/middle/ty/relate.rs
+++ b/src/librustc/middle/ty/relate.rs
@@ -139,11 +139,11 @@ fn relate_item_substs<'a,'tcx:'a,R>(relation: &mut R,
     relate_substs(relation, opt_variances, a_subst, b_subst)
 }
 
-fn relate_substs<'a,'tcx:'a,R>(relation: &mut R,
-                               variances: Option<&ty::ItemVariances>,
-                               a_subst: &Substs<'tcx>,
-                               b_subst: &Substs<'tcx>)
-                               -> RelateResult<'tcx, Substs<'tcx>>
+pub fn relate_substs<'a,'tcx:'a,R>(relation: &mut R,
+                                   variances: Option<&ty::ItemVariances>,
+                                   a_subst: &Substs<'tcx>,
+                                   b_subst: &Substs<'tcx>)
+                                   -> RelateResult<'tcx, Substs<'tcx>>
     where R: TypeRelation<'a,'tcx>
 {
     let mut substs = Substs::empty();
@@ -568,11 +568,19 @@ pub fn super_relate_tys<'a,'tcx:'a,R>(relation: &mut R,
             }
         }
 
-        (&ty::TyBareFn(a_opt_def_id, a_fty), &ty::TyBareFn(b_opt_def_id, b_fty))
-            if a_opt_def_id == b_opt_def_id =>
+        (&ty::TyFnDef(a_def_id, a_substs, a_fty),
+         &ty::TyFnDef(b_def_id, b_substs, b_fty))
+            if a_def_id == b_def_id =>
         {
+            let substs = try!(relate_substs(relation, None, a_substs, b_substs));
             let fty = try!(relation.relate(a_fty, b_fty));
-            Ok(tcx.mk_fn(a_opt_def_id, tcx.mk_bare_fn(fty)))
+            Ok(tcx.mk_fn_def(a_def_id, tcx.mk_substs(substs), fty))
+        }
+
+        (&ty::TyFnPtr(a_fty), &ty::TyFnPtr(b_fty)) =>
+        {
+            let fty = try!(relation.relate(a_fty, b_fty));
+            Ok(tcx.mk_fn_ptr(fty))
         }
 
         (&ty::TyProjection(ref a_data), &ty::TyProjection(ref b_data)) =>
diff --git a/src/librustc/middle/ty/structural_impls.rs b/src/librustc/middle/ty/structural_impls.rs
index 001ea02a27c..3fe9e02a90d 100644
--- a/src/librustc/middle/ty/structural_impls.rs
+++ b/src/librustc/middle/ty/structural_impls.rs
@@ -282,9 +282,16 @@ impl<'tcx> TypeFoldable<'tcx> for Ty<'tcx> {
             }
             ty::TyTrait(ref trait_ty) => ty::TyTrait(trait_ty.fold_with(folder)),
             ty::TyTuple(ref ts) => ty::TyTuple(ts.fold_with(folder)),
-            ty::TyBareFn(opt_def_id, ref f) => {
+            ty::TyFnDef(def_id, substs, ref f) => {
+                let substs = substs.fold_with(folder);
+                let bfn = f.fold_with(folder);
+                ty::TyFnDef(def_id,
+                            folder.tcx().mk_substs(substs),
+                            folder.tcx().mk_bare_fn(bfn))
+            }
+            ty::TyFnPtr(ref f) => {
                 let bfn = f.fold_with(folder);
-                ty::TyBareFn(opt_def_id, folder.tcx().mk_bare_fn(bfn))
+                ty::TyFnPtr(folder.tcx().mk_bare_fn(bfn))
             }
             ty::TyRef(r, ref tm) => {
                 let r = r.fold_with(folder);
@@ -318,7 +325,10 @@ impl<'tcx> TypeFoldable<'tcx> for Ty<'tcx> {
             ty::TyEnum(_tid, ref substs) => substs.visit_with(visitor),
             ty::TyTrait(ref trait_ty) => trait_ty.visit_with(visitor),
             ty::TyTuple(ref ts) => ts.visit_with(visitor),
-            ty::TyBareFn(_opt_def_id, ref f) => f.visit_with(visitor),
+            ty::TyFnDef(_, substs, ref f) => {
+                substs.visit_with(visitor) || f.visit_with(visitor)
+            }
+            ty::TyFnPtr(ref f) => f.visit_with(visitor),
             ty::TyRef(r, ref tm) => r.visit_with(visitor) || tm.visit_with(visitor),
             ty::TyStruct(_did, ref substs) => substs.visit_with(visitor),
             ty::TyClosure(_did, ref substs) => substs.visit_with(visitor),
diff --git a/src/librustc/middle/ty/sty.rs b/src/librustc/middle/ty/sty.rs
index c8f8e073832..2d7b7dc6e9b 100644
--- a/src/librustc/middle/ty/sty.rs
+++ b/src/librustc/middle/ty/sty.rs
@@ -127,14 +127,14 @@ pub enum TypeVariants<'tcx> {
     /// `&a mut T` or `&'a T`.
     TyRef(&'tcx Region, TypeAndMut<'tcx>),
 
-    /// If the def-id is Some(_), then this is the type of a specific
-    /// fn item. Otherwise, if None(_), it is a fn pointer type.
-    ///
-    /// FIXME: Conflating function pointers and the type of a
-    /// function is probably a terrible idea; a function pointer is a
-    /// value with a specific type, but a function can be polymorphic
-    /// or dynamically dispatched.
-    TyBareFn(Option<DefId>, &'tcx BareFnTy<'tcx>),
+    /// The anonymous type of a function declaration/definition. Each
+    /// function has a unique type.
+    TyFnDef(DefId, &'tcx Substs<'tcx>, &'tcx BareFnTy<'tcx>),
+
+    /// A pointer to a function.  Written as `fn() -> i32`.
+    /// FIXME: This is currently also used to represent the callee of a method;
+    /// see ty::MethodCallee etc.
+    TyFnPtr(&'tcx BareFnTy<'tcx>),
 
     /// A trait, defined with `trait`.
     TyTrait(Box<TraitTy<'tcx>>),
@@ -1029,7 +1029,7 @@ impl<'tcx> TyS<'tcx> {
         match self.sty {
             TyBool | TyChar | TyInt(_) | TyFloat(_) | TyUint(_) |
             TyInfer(IntVar(_)) | TyInfer(FloatVar(_)) |
-            TyBareFn(..) | TyRawPtr(_) => true,
+            TyFnDef(..) | TyFnPtr(_) | TyRawPtr(_) => true,
             _ => false
         }
     }
@@ -1080,20 +1080,6 @@ impl<'tcx> TyS<'tcx> {
         }
     }
 
-    pub fn is_bare_fn(&self) -> bool {
-        match self.sty {
-            TyBareFn(..) => true,
-            _ => false
-        }
-    }
-
-    pub fn is_bare_fn_item(&self) -> bool {
-        match self.sty {
-            TyBareFn(Some(_), _) => true,
-            _ => false
-        }
-    }
-
     pub fn is_fp(&self) -> bool {
         match self.sty {
             TyInfer(FloatVar(_)) | TyFloat(_) => true,
@@ -1154,7 +1140,7 @@ impl<'tcx> TyS<'tcx> {
 
     pub fn fn_sig(&self) -> &'tcx PolyFnSig<'tcx> {
         match self.sty {
-            TyBareFn(_, ref f) => &f.sig,
+            TyFnDef(_, _, ref f) | TyFnPtr(ref f) => &f.sig,
             _ => panic!("Ty::fn_sig() called on non-fn type: {:?}", self)
         }
     }
@@ -1162,7 +1148,7 @@ impl<'tcx> TyS<'tcx> {
     /// Returns the ABI of the given function.
     pub fn fn_abi(&self) -> abi::Abi {
         match self.sty {
-            TyBareFn(_, ref f) => f.abi,
+            TyFnDef(_, _, ref f) | TyFnPtr(ref f) => f.abi,
             _ => panic!("Ty::fn_abi() called on non-fn type"),
         }
     }
@@ -1178,7 +1164,7 @@ impl<'tcx> TyS<'tcx> {
 
     pub fn is_fn(&self) -> bool {
         match self.sty {
-            TyBareFn(..) => true,
+            TyFnDef(..) | TyFnPtr(_) => true,
             _ => false
         }
     }
@@ -1224,7 +1210,8 @@ impl<'tcx> TyS<'tcx> {
             TyProjection(ref data) => {
                 data.trait_ref.substs.regions().as_slice().to_vec()
             }
-            TyBareFn(..) |
+            TyFnDef(..) |
+            TyFnPtr(_) |
             TyBool |
             TyChar |
             TyInt(_) |
diff --git a/src/librustc/middle/ty/util.rs b/src/librustc/middle/ty/util.rs
index 7c32d931fff..2b83aaccdc4 100644
--- a/src/librustc/middle/ty/util.rs
+++ b/src/librustc/middle/ty/util.rs
@@ -514,9 +514,12 @@ impl<'tcx> TyCtxt<'tcx> {
                         region(state, *r);
                         mt(state, m);
                     }
-                    TyBareFn(opt_def_id, ref b) => {
+                    TyFnDef(def_id, _, _) => {
                         byte!(14);
-                        hash!(opt_def_id);
+                        hash!(def_id);
+                    }
+                    TyFnPtr(ref b) => {
+                        byte!(15);
                         hash!(b.unsafety);
                         hash!(b.abi);
                         fn_sig(state, &b.sig);
@@ -599,14 +602,14 @@ impl<'tcx> TyCtxt<'tcx> {
 #[derive(Debug)]
 pub struct ImplMethod<'tcx> {
     pub method: Rc<ty::Method<'tcx>>,
-    pub substs: Substs<'tcx>,
+    pub substs: &'tcx Substs<'tcx>,
     pub is_provided: bool
 }
 
 impl<'tcx> TyCtxt<'tcx> {
     pub fn get_impl_method(&self,
                            impl_def_id: DefId,
-                           substs: Substs<'tcx>,
+                           substs: &'tcx Substs<'tcx>,
                            name: Name)
                            -> ImplMethod<'tcx>
     {
@@ -633,9 +636,10 @@ impl<'tcx> TyCtxt<'tcx> {
                 if meth.name == name {
                     let impl_to_trait_substs = self
                         .make_substs_for_receiver_types(&trait_ref, meth);
+                    let substs = impl_to_trait_substs.subst(self, substs);
                     return ImplMethod {
                         method: meth.clone(),
-                        substs: impl_to_trait_substs.subst(self, &substs),
+                        substs: self.mk_substs(substs),
                         is_provided: true
                     }
                 }
@@ -677,7 +681,7 @@ impl<'tcx> ty::TyS<'tcx> {
         // Fast-path for primitive types
         let result = match self.sty {
             TyBool | TyChar | TyInt(..) | TyUint(..) | TyFloat(..) |
-            TyRawPtr(..) | TyBareFn(..) | TyRef(_, TypeAndMut {
+            TyRawPtr(..) | TyFnDef(..) | TyFnPtr(_) | TyRef(_, TypeAndMut {
                 mutbl: hir::MutImmutable, ..
             }) => Some(false),
 
@@ -719,7 +723,7 @@ impl<'tcx> ty::TyS<'tcx> {
         // Fast-path for primitive types
         let result = match self.sty {
             TyBool | TyChar | TyInt(..) | TyUint(..) | TyFloat(..) |
-            TyBox(..) | TyRawPtr(..) | TyRef(..) | TyBareFn(..) |
+            TyBox(..) | TyRawPtr(..) | TyRef(..) | TyFnDef(..) | TyFnPtr(_) |
             TyArray(..) | TyTuple(..) | TyClosure(..) => Some(true),
 
             TyStr | TyTrait(..) | TySlice(_) => Some(false),
diff --git a/src/librustc/middle/ty/walk.rs b/src/librustc/middle/ty/walk.rs
index 81cad448690..b6d93ecf78b 100644
--- a/src/librustc/middle/ty/walk.rs
+++ b/src/librustc/middle/ty/walk.rs
@@ -98,7 +98,11 @@ fn push_subtypes<'tcx>(stack: &mut Vec<Ty<'tcx>>, parent_ty: Ty<'tcx>) {
         ty::TyTuple(ref ts) => {
             push_reversed(stack, ts);
         }
-        ty::TyBareFn(_, ref ft) => {
+        ty::TyFnDef(_, substs, ref ft) => {
+            push_reversed(stack, substs.types.as_slice());
+            push_sig_subtypes(stack, &ft.sig);
+        }
+        ty::TyFnPtr(ref ft) => {
             push_sig_subtypes(stack, &ft.sig);
         }
     }
diff --git a/src/librustc/middle/ty/wf.rs b/src/librustc/middle/ty/wf.rs
index c6d1bc8d649..5f81d27a1f6 100644
--- a/src/librustc/middle/ty/wf.rs
+++ b/src/librustc/middle/ty/wf.rs
@@ -354,8 +354,8 @@ impl<'a,'tcx> WfPredicates<'a,'tcx> {
                     // WFedness.)
                 }
 
-                ty::TyBareFn(..) => {
-                    // let the loop iterator into the argument/return
+                ty::TyFnDef(..) | ty::TyFnPtr(_) => {
+                    // let the loop iterate into the argument/return
                     // types appearing in the fn signature
                 }
 
diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs
index ce7b1ceb355..4556611df59 100644
--- a/src/librustc/mir/repr.rs
+++ b/src/librustc/mir/repr.rs
@@ -861,20 +861,10 @@ impl<'tcx> Debug for TypedConstVal<'tcx> {
     }
 }
 
-#[derive(Clone, Copy, Debug, PartialEq, RustcEncodable, RustcDecodable)]
-pub enum ItemKind {
-    Constant,
-    /// This is any sort of callable (usually those that have a type of `fn(…) -> …`). This
-    /// includes functions, constructors, but not methods which have their own ItemKind.
-    Function,
-    Method,
-}
-
 #[derive(Clone, PartialEq, RustcEncodable, RustcDecodable)]
 pub enum Literal<'tcx> {
     Item {
         def_id: DefId,
-        kind: ItemKind,
         substs: &'tcx Substs<'tcx>,
     },
     Value {
diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs
index 9c92208191e..8fd784cbde7 100644
--- a/src/librustc/util/ppaux.rs
+++ b/src/librustc/util/ppaux.rs
@@ -13,7 +13,7 @@ use middle::def_id::DefId;
 use middle::subst::{self, Subst};
 use middle::ty::{BrAnon, BrEnv, BrFresh, BrNamed};
 use middle::ty::{TyBool, TyChar, TyStruct, TyEnum};
-use middle::ty::{TyError, TyStr, TyArray, TySlice, TyFloat, TyBareFn};
+use middle::ty::{TyError, TyStr, TyArray, TySlice, TyFloat, TyFnDef, TyFnPtr};
 use middle::ty::{TyParam, TyRawPtr, TyRef, TyTuple};
 use middle::ty::TyClosure;
 use middle::ty::{TyBox, TyTrait, TyInt, TyUint, TyInfer};
@@ -812,7 +812,7 @@ impl<'tcx> fmt::Display for ty::TypeVariants<'tcx> {
                 }
                 write!(f, ")")
             }
-            TyBareFn(opt_def_id, ref bare_fn) => {
+            TyFnDef(def_id, substs, ref bare_fn) => {
                 if bare_fn.unsafety == hir::Unsafety::Unsafe {
                     try!(write!(f, "unsafe "));
                 }
@@ -822,13 +822,30 @@ impl<'tcx> fmt::Display for ty::TypeVariants<'tcx> {
                 }
 
                 try!(write!(f, "{}", bare_fn.sig.0));
+                try!(ty::tls::with(|tcx| {
+                    write!(f, " {{{}", tcx.item_path_str(def_id))
+                }));
+
+                let tps = substs.types.get_slice(subst::FnSpace);
+                if tps.len() >= 1 {
+                    try!(write!(f, "::<{}", tps[0]));
+                    for &ty in &tps[1..] {
+                        try!(write!(f, ", {}", ty));
+                    }
+                    try!(write!(f, ">"));
+                }
+                write!(f, "}}")
+            }
+            TyFnPtr(ref bare_fn) => {
+                if bare_fn.unsafety == hir::Unsafety::Unsafe {
+                    try!(write!(f, "unsafe "));
+                }
 
-                if let Some(def_id) = opt_def_id {
-                    try!(write!(f, " {{{}}}", ty::tls::with(|tcx| {
-                        tcx.item_path_str(def_id)
-                    })));
+                if bare_fn.abi != Abi::Rust {
+                    try!(write!(f, "extern {} ", bare_fn.abi));
                 }
-                Ok(())
+
+                write!(f, "{}", bare_fn.sig.0)
             }
             TyInfer(infer_ty) => write!(f, "{}", infer_ty),
             TyError => write!(f, "[type error]"),
diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs
index 5c3c5beafad..3cab9cfb88c 100644
--- a/src/librustc_driver/test.rs
+++ b/src/librustc_driver/test.rs
@@ -261,16 +261,15 @@ impl<'a, 'tcx> Env<'a, 'tcx> {
 
     pub fn t_fn(&self, input_tys: &[Ty<'tcx>], output_ty: Ty<'tcx>) -> Ty<'tcx> {
         let input_args = input_tys.iter().cloned().collect();
-        self.infcx.tcx.mk_fn(None,
-                             self.infcx.tcx.mk_bare_fn(ty::BareFnTy {
-                                 unsafety: hir::Unsafety::Normal,
-                                 abi: Abi::Rust,
-                                 sig: ty::Binder(ty::FnSig {
-                                     inputs: input_args,
-                                     output: ty::FnConverging(output_ty),
-                                     variadic: false,
-                                 }),
-                             }))
+        self.infcx.tcx.mk_fn_ptr(ty::BareFnTy {
+            unsafety: hir::Unsafety::Normal,
+            abi: Abi::Rust,
+            sig: ty::Binder(ty::FnSig {
+                inputs: input_args,
+                output: ty::FnConverging(output_ty),
+                variadic: false,
+            }),
+        })
     }
 
     pub fn t_nil(&self) -> Ty<'tcx> {
diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs
index 86ab8d45e4e..0c906f8eb54 100644
--- a/src/librustc_lint/builtin.rs
+++ b/src/librustc_lint/builtin.rs
@@ -1065,7 +1065,7 @@ impl LateLintPass for MutableTransmutes {
                 }
                 let typ = cx.tcx.node_id_to_type(expr.id);
                 match typ.sty {
-                    ty::TyBareFn(_, ref bare_fn) if bare_fn.abi == RustIntrinsic => {
+                    ty::TyFnDef(_, _, ref bare_fn) if bare_fn.abi == RustIntrinsic => {
                         if let ty::FnConverging(to) = bare_fn.sig.0.output {
                             let from = bare_fn.sig.0.inputs[0];
                             return Some((&from.sty, &to.sty));
@@ -1079,7 +1079,7 @@ impl LateLintPass for MutableTransmutes {
 
         fn def_id_is_transmute(cx: &LateContext, def_id: DefId) -> bool {
             match cx.tcx.lookup_item_type(def_id).ty.sty {
-                ty::TyBareFn(_, ref bfty) if bfty.abi == RustIntrinsic => (),
+                ty::TyFnDef(_, _, ref bfty) if bfty.abi == RustIntrinsic => (),
                 _ => return false
             }
             cx.tcx.with_path(def_id, |path| match path.last() {
diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs
index 1cf0339c086..e47f67dad8f 100644
--- a/src/librustc_lint/lib.rs
+++ b/src/librustc_lint/lib.rs
@@ -171,6 +171,10 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
             reference: "RFC 218 <https://github.com/rust-lang/rfcs/blob/\
                         master/text/0218-empty-struct-with-braces.md>",
         },
+        FutureIncompatibleInfo {
+            id: LintId::of(TRANSMUTE_FROM_FN_ITEM_TYPES),
+            reference: "issue #19925 <https://github.com/rust-lang/rust/issues/19925>",
+        },
         ]);
 
     // We have one lint pass defined specially
diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs
index c7cb2d15a09..10535549ceb 100644
--- a/src/librustc_lint/types.rs
+++ b/src/librustc_lint/types.rs
@@ -391,7 +391,7 @@ fn is_repr_nullable_ptr<'tcx>(tcx: &TyCtxt<'tcx>,
 
         if def.variants[data_idx].fields.len() == 1 {
             match def.variants[data_idx].fields[0].ty(tcx, substs).sty {
-                ty::TyBareFn(None, _) => { return true; }
+                ty::TyFnPtr(_) => { return true; }
                 ty::TyRef(..) => { return true; }
                 _ => { }
             }
@@ -556,7 +556,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
                 self.check_type_for_ffi(cache, ty)
             }
 
-            ty::TyBareFn(None, bare_fn) => {
+            ty::TyFnPtr(bare_fn) => {
                 match bare_fn.abi {
                     Abi::Rust |
                     Abi::RustIntrinsic |
@@ -595,7 +595,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
 
             ty::TyParam(..) | ty::TyInfer(..) | ty::TyError |
             ty::TyClosure(..) | ty::TyProjection(..) |
-            ty::TyBareFn(Some(_), _) => {
+            ty::TyFnDef(..) => {
                 panic!("Unexpected type in foreign function")
             }
         }
diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs
index 96e4f8a72f1..06f81a17a06 100644
--- a/src/librustc_metadata/decoder.rs
+++ b/src/librustc_metadata/decoder.rs
@@ -472,7 +472,7 @@ pub fn get_adt_def<'tcx>(intr: &IdentInterner,
                    variant.name,
                    ctor_ty);
             let field_tys = match ctor_ty.sty {
-                ty::TyBareFn(_, &ty::BareFnTy { sig: ty::Binder(ty::FnSig {
+                ty::TyFnDef(_, _, &ty::BareFnTy { sig: ty::Binder(ty::FnSig {
                     ref inputs, ..
                 }), ..}) => {
                     // tuple-struct constructors don't have escaping regions
@@ -992,7 +992,7 @@ pub fn get_impl_or_trait_item<'tcx>(intr: Rc<IdentInterner>,
             let predicates = doc_predicates(item_doc, tcx, cdata, tag_method_ty_generics);
             let ity = tcx.lookup_item_type(def_id).ty;
             let fty = match ity.sty {
-                ty::TyBareFn(_, fty) => fty.clone(),
+                ty::TyFnDef(_, _, fty) => fty.clone(),
                 _ => tcx.sess.bug(&format!(
                     "the type {:?} of the method {:?} is not a function?",
                     ity, name))
@@ -1586,7 +1586,8 @@ pub fn is_extern_item(cdata: Cmd, id: DefIndex, tcx: &TyCtxt) -> bool {
             let ty::TypeScheme { generics, ty } = get_type(cdata, id, tcx);
             let no_generics = generics.types.is_empty();
             match ty.sty {
-                ty::TyBareFn(_, fn_ty) if fn_ty.abi != Abi::Rust => return no_generics,
+                ty::TyFnDef(_, _, fn_ty) | ty::TyFnPtr(fn_ty)
+                    if fn_ty.abi != Abi::Rust => return no_generics,
                 _ => no_generics,
             }
         },
diff --git a/src/librustc_metadata/tydecode.rs b/src/librustc_metadata/tydecode.rs
index bf5a97232fc..e9b23eb0458 100644
--- a/src/librustc_metadata/tydecode.rs
+++ b/src/librustc_metadata/tydecode.rs
@@ -380,10 +380,11 @@ impl<'a,'tcx> TyDecoder<'a,'tcx> {
             }
             'F' => {
                 let def_id = self.parse_def();
-                return tcx.mk_fn(Some(def_id), tcx.mk_bare_fn(self.parse_bare_fn_ty()));
+                let substs = self.tcx.mk_substs(self.parse_substs());
+                return tcx.mk_fn_def(def_id, substs, self.parse_bare_fn_ty());
             }
             'G' => {
-                return tcx.mk_fn(None, tcx.mk_bare_fn(self.parse_bare_fn_ty()));
+                return tcx.mk_fn_ptr(self.parse_bare_fn_ty());
             }
             '#' => {
                 // This is a hacky little caching scheme. The idea is that if we encode
diff --git a/src/librustc_metadata/tyencode.rs b/src/librustc_metadata/tyencode.rs
index 7289cd2b5b3..a6601e591ab 100644
--- a/src/librustc_metadata/tyencode.rs
+++ b/src/librustc_metadata/tyencode.rs
@@ -135,12 +135,13 @@ pub fn enc_ty<'a, 'tcx>(w: &mut Cursor<Vec<u8>>, cx: &ctxt<'a, 'tcx>, t: Ty<'tcx
         ty::TyStr => {
             write!(w, "v");
         }
-        ty::TyBareFn(Some(def_id), f) => {
+        ty::TyFnDef(def_id, substs, f) => {
             write!(w, "F");
             write!(w, "{}|", (cx.ds)(def_id));
+            enc_substs(w, cx, substs);
             enc_bare_fn_ty(w, cx, f);
         }
-        ty::TyBareFn(None, f) => {
+        ty::TyFnPtr(f) => {
             write!(w, "G");
             enc_bare_fn_ty(w, cx, f);
         }
diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs
index ca00b99b108..a7f4a53b022 100644
--- a/src/librustc_mir/build/expr/into.rs
+++ b/src/librustc_mir/build/expr/into.rs
@@ -239,7 +239,9 @@ impl<'a,'tcx> Builder<'a,'tcx> {
             }
             ExprKind::Call { ty, fun, args } => {
                 let diverges = match ty.sty {
-                    ty::TyBareFn(_, ref f) => f.sig.0.output.diverges(),
+                    ty::TyFnDef(_, _, ref f) | ty::TyFnPtr(ref f) => {
+                        f.sig.0.output.diverges()
+                    }
                     _ => false
                 };
                 let fun = unpack!(block = this.as_operand(block, fun));
diff --git a/src/librustc_mir/build/misc.rs b/src/librustc_mir/build/misc.rs
index 5d040bcb40a..8c435b45dae 100644
--- a/src/librustc_mir/build/misc.rs
+++ b/src/librustc_mir/build/misc.rs
@@ -12,7 +12,6 @@
 //! kind of thing.
 
 use build::Builder;
-use hair::*;
 use rustc::middle::ty::Ty;
 use rustc::mir::repr::*;
 use std::u32;
@@ -59,16 +58,4 @@ impl<'a,'tcx> Builder<'a,'tcx> {
             });
         temp
     }
-
-    pub fn item_ref_operand(&mut self,
-                            span: Span,
-                            item_ref: ItemRef<'tcx>)
-                            -> Operand<'tcx> {
-        let literal = Literal::Item {
-            def_id: item_ref.def_id,
-            kind: item_ref.kind,
-            substs: item_ref.substs,
-        };
-        self.literal_operand(span, item_ref.ty, literal)
-    }
 }
diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs
index 6b1b3a33d3d..3d14ad2374b 100644
--- a/src/librustc_mir/build/scope.rs
+++ b/src/librustc_mir/build/scope.rs
@@ -503,7 +503,6 @@ impl<'a,'tcx> Builder<'a,'tcx> {
             ty: self.hir.tcx().lookup_item_type(funcdid).ty,
             literal: Literal::Item {
                 def_id: funcdid,
-                kind: ItemKind::Function,
                 substs: self.hir.tcx().mk_substs(Substs::empty())
             }
         }
@@ -641,7 +640,6 @@ fn build_free<'tcx>(tcx: &TyCtxt<'tcx>,
             ty: tcx.lookup_item_type(free_func).ty.subst(tcx, substs),
             literal: Literal::Item {
                 def_id: free_func,
-                kind: ItemKind::Function,
                 substs: substs
             }
         }),
diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs
index 1a8558b063a..cbd6bed81a6 100644
--- a/src/librustc_mir/hair/cx/expr.rs
+++ b/src/librustc_mir/hair/cx/expr.rs
@@ -61,7 +61,7 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
                     let method = method_callee(cx, self, ty::MethodCall::expr(self.id));
 
                     let sig = match method.ty.sty {
-                        ty::TyBareFn(_, fn_ty) => &fn_ty.sig,
+                        ty::TyFnDef(_, _, fn_ty) => &fn_ty.sig,
                         _ => cx.tcx.sess.span_bug(self.span, "type of method is not an fn")
                     };
 
@@ -581,7 +581,6 @@ fn method_callee<'a, 'tcx: 'a>(cx: &mut Cx<'a, 'tcx>,
         kind: ExprKind::Literal {
             literal: Literal::Item {
                 def_id: callee.def_id,
-                kind: ItemKind::Method,
                 substs: callee.substs,
             },
         },
@@ -618,14 +617,13 @@ fn convert_path_expr<'a, 'tcx: 'a>(cx: &mut Cx<'a, 'tcx>, expr: &'tcx hir::Expr)
     let substs = cx.tcx.mk_substs(cx.tcx.node_id_item_substs(expr.id).substs);
     // Otherwise there may be def_map borrow conflicts
     let def = cx.tcx.def_map.borrow()[&expr.id].full_def();
-    let (def_id, kind) = match def {
+    let def_id = match def {
         // A regular function.
-        Def::Fn(def_id) => (def_id, ItemKind::Function),
-        Def::Method(def_id) => (def_id, ItemKind::Method),
+        Def::Fn(def_id) | Def::Method(def_id) => def_id,
         Def::Struct(def_id) => match cx.tcx.node_id_to_type(expr.id).sty {
             // A tuple-struct constructor. Should only be reached if not called in the same
             // expression.
-            ty::TyBareFn(..) => (def_id, ItemKind::Function),
+            ty::TyFnDef(..) => def_id,
             // A unit struct which is used as a value. We return a completely different ExprKind
             // here to account for this special case.
             ty::TyStruct(adt_def, substs) => return ExprKind::Adt {
@@ -640,7 +638,7 @@ fn convert_path_expr<'a, 'tcx: 'a>(cx: &mut Cx<'a, 'tcx>, expr: &'tcx hir::Expr)
         Def::Variant(enum_id, variant_id) => match cx.tcx.node_id_to_type(expr.id).sty {
             // A variant constructor. Should only be reached if not called in the same
             // expression.
-            ty::TyBareFn(..) => (variant_id, ItemKind::Function),
+            ty::TyFnDef(..) => variant_id,
             // A unit variant, similar special case to the struct case above.
             ty::TyEnum(adt_def, substs) => {
                 debug_assert!(adt_def.did == enum_id);
@@ -660,7 +658,7 @@ fn convert_path_expr<'a, 'tcx: 'a>(cx: &mut Cx<'a, 'tcx>, expr: &'tcx hir::Expr)
             if let Some(v) = cx.try_const_eval_literal(expr) {
                 return ExprKind::Literal { literal: v };
             } else {
-                (def_id, ItemKind::Constant)
+                def_id
             }
         }
 
@@ -677,7 +675,7 @@ fn convert_path_expr<'a, 'tcx: 'a>(cx: &mut Cx<'a, 'tcx>, expr: &'tcx hir::Expr)
                 &format!("def `{:?}` not yet implemented", def)),
     };
     ExprKind::Literal {
-        literal: Literal::Item { def_id: def_id, kind: kind, substs: substs }
+        literal: Literal::Item { def_id: def_id, substs: substs }
     }
 }
 
diff --git a/src/librustc_mir/hair/mod.rs b/src/librustc_mir/hair/mod.rs
index 707dd972003..6a22dce7af9 100644
--- a/src/librustc_mir/hair/mod.rs
+++ b/src/librustc_mir/hair/mod.rs
@@ -14,7 +14,7 @@
 //! unit-tested and separated from the Rust source and compiler data
 //! structures.
 
-use rustc::mir::repr::{BinOp, BorrowKind, Field, Literal, Mutability, UnOp, ItemKind,
+use rustc::mir::repr::{BinOp, BorrowKind, Field, Literal, Mutability, UnOp,
     TypedConstVal};
 use rustc::middle::const_eval::ConstVal;
 use rustc::middle::def_id::DefId;
@@ -29,14 +29,6 @@ use self::cx::Cx;
 pub mod cx;
 
 #[derive(Clone, Debug)]
-pub struct ItemRef<'tcx> {
-    pub ty: Ty<'tcx>,
-    pub kind: ItemKind,
-    pub def_id: DefId,
-    pub substs: &'tcx Substs<'tcx>,
-}
-
-#[derive(Clone, Debug)]
 pub struct Block<'tcx> {
     pub extent: CodeExtent,
     pub span: Span,
diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs
index bf22c7b0b8b..e021300f1b3 100644
--- a/src/librustc_mir/transform/type_check.rs
+++ b/src/librustc_mir/transform/type_check.rs
@@ -421,7 +421,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                 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,
+                    ty::TyFnDef(_, _, func_ty) | ty::TyFnPtr(func_ty) => func_ty,
                     _ => {
                         span_mirbug!(self, term, "call to non-function {:?}", func_ty);
                         return;
diff --git a/src/librustc_passes/consts.rs b/src/librustc_passes/consts.rs
index 810712bb0cf..3a39a3c6dd1 100644
--- a/src/librustc_passes/consts.rs
+++ b/src/librustc_passes/consts.rs
@@ -582,7 +582,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
                     v.add_qualif(ConstQualif::NON_ZERO_SIZED);
                 }
                 Some(Def::Struct(..)) => {
-                    if let ty::TyBareFn(..) = node_ty.sty {
+                    if let ty::TyFnDef(..) = node_ty.sty {
                         // Count the function pointer.
                         v.add_qualif(ConstQualif::NON_ZERO_SIZED);
                     }
diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs
index 82f4ad1d746..79ccc8fb2b2 100644
--- a/src/librustc_privacy/lib.rs
+++ b/src/librustc_privacy/lib.rs
@@ -856,7 +856,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for PrivacyVisitor<'a, 'tcx> {
                 if let Def::Struct(..) = self.tcx.resolve_expr(expr) {
                     let expr_ty = self.tcx.expr_ty(expr);
                     let def = match expr_ty.sty {
-                        ty::TyBareFn(_, &ty::BareFnTy { sig: ty::Binder(ty::FnSig {
+                        ty::TyFnDef(_, _, &ty::BareFnTy { sig: ty::Binder(ty::FnSig {
                             output: ty::FnConverging(ty), ..
                         }), ..}) => ty,
                         _ => expr_ty
diff --git a/src/librustc_trans/trans/adt.rs b/src/librustc_trans/trans/adt.rs
index e8368f1bd97..c5508a8268f 100644
--- a/src/librustc_trans/trans/adt.rs
+++ b/src/librustc_trans/trans/adt.rs
@@ -451,8 +451,8 @@ fn find_discr_field_candidate<'tcx>(tcx: &TyCtxt<'tcx>,
         // Regular thin pointer: &T/&mut T/Box<T>
         ty::TyRef(..) | ty::TyBox(..) => Some(path),
 
-        // Functions are just pointers
-        ty::TyBareFn(..) => Some(path),
+        // Function pointer: `fn() -> i32`
+        ty::TyFnPtr(_) => Some(path),
 
         // Is this the NonZero lang item wrapping a pointer or integer type?
         ty::TyStruct(def, substs) if Some(def.did) == tcx.lang_items.non_zero() => {
diff --git a/src/librustc_trans/trans/asm.rs b/src/librustc_trans/trans/asm.rs
index 33370abc3fc..98e9a1c98ad 100644
--- a/src/librustc_trans/trans/asm.rs
+++ b/src/librustc_trans/trans/asm.rs
@@ -50,7 +50,6 @@ pub fn trans_inline_asm<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, ia: &ast::InlineAsm)
                                           expr_ty(bcx, &out.expr),
                                           out_datum,
                                           cleanup::CustomScope(temp_scope),
-                                          callee::DontAutorefArg,
                                           &mut inputs);
             if out.is_rw {
                 ext_inputs.push(*inputs.last().unwrap());
@@ -64,7 +63,6 @@ pub fn trans_inline_asm<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, ia: &ast::InlineAsm)
                                               expr_ty(bcx, &out.expr),
                                               out_datum,
                                               cleanup::CustomScope(temp_scope),
-                                              callee::DontAutorefArg,
                                               &mut ext_inputs);
                 ext_constraints.push(i.to_string());
             }
@@ -80,7 +78,6 @@ pub fn trans_inline_asm<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, ia: &ast::InlineAsm)
                                     expr_ty(bcx, &input),
                                     in_datum,
                                     cleanup::CustomScope(temp_scope),
-                                    callee::DontAutorefArg,
                                     &mut inputs);
     }
     inputs.extend_from_slice(&ext_inputs[..]);
diff --git a/src/librustc_trans/trans/attributes.rs b/src/librustc_trans/trans/attributes.rs
index 8f9648b333b..009d43e813e 100644
--- a/src/librustc_trans/trans/attributes.rs
+++ b/src/librustc_trans/trans/attributes.rs
@@ -131,7 +131,7 @@ pub fn from_fn_type<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fn_type: ty::Ty<'tcx
 
     let function_type;
     let (fn_sig, abi, env_ty) = match fn_type.sty {
-        ty::TyBareFn(_, ref f) => (&f.sig, f.abi, None),
+        ty::TyFnDef(_, _, ref f) | ty::TyFnPtr(ref f) => (&f.sig, f.abi, None),
         ty::TyClosure(closure_did, ref substs) => {
             let infcx = infer::normalizing_infer_ctxt(ccx.tcx(), &ccx.tcx().tables);
             function_type = infcx.closure_type(closure_did, substs);
@@ -162,7 +162,7 @@ pub fn from_fn_type<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fn_type: ty::Ty<'tcx
                 _ => ccx.sess().bug("expected tuple'd inputs")
             }
         },
-        ty::TyBareFn(..) if abi == Abi::RustCall => {
+        ty::TyFnDef(..) | ty::TyFnPtr(_) if abi == Abi::RustCall => {
             let mut inputs = vec![fn_sig.inputs[0]];
 
             match fn_sig.inputs[1].sty {
diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs
index 8f9608926b9..5088dabfbe7 100644
--- a/src/librustc_trans/trans/base.rs
+++ b/src/librustc_trans/trans/base.rs
@@ -195,16 +195,14 @@ impl<'a, 'tcx> Drop for StatRecorder<'a, 'tcx> {
 fn get_extern_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                                 fn_ty: Ty<'tcx>,
                                 name: &str,
-                                did: DefId)
+                                attrs: &[ast::Attribute])
                                 -> ValueRef {
     if let Some(n) = ccx.externs().borrow().get(name) {
         return *n;
     }
 
     let f = declare::declare_rust_fn(ccx, name, fn_ty);
-
-    let attrs = ccx.sess().cstore.item_attrs(did);
-    attributes::from_fn_attrs(ccx, &attrs[..], f);
+    attributes::from_fn_attrs(ccx, &attrs, f);
 
     ccx.externs().borrow_mut().insert(name.to_string(), f);
     f
@@ -390,7 +388,7 @@ pub fn compare_scalar_types<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                 _ => bcx.sess().bug("compare_scalar_types: must be a comparison operator"),
             }
         }
-        ty::TyBareFn(..) | ty::TyBool | ty::TyUint(_) | ty::TyChar => {
+        ty::TyFnDef(..) | ty::TyFnPtr(_) | ty::TyBool | ty::TyUint(_) | ty::TyChar => {
             ICmp(bcx,
                  bin_op_to_icmp_predicate(bcx.ccx(), op, false),
                  lhs,
@@ -621,8 +619,7 @@ pub fn iter_structural_ty<'blk, 'tcx, F>(cx: Block<'blk, 'tcx>,
 pub fn unsized_info<'ccx, 'tcx>(ccx: &CrateContext<'ccx, 'tcx>,
                                 source: Ty<'tcx>,
                                 target: Ty<'tcx>,
-                                old_info: Option<ValueRef>,
-                                param_substs: &'tcx Substs<'tcx>)
+                                old_info: Option<ValueRef>)
                                 -> ValueRef {
     let (source, target) = ccx.tcx().struct_lockstep_tails(source, target);
     match (&source.sty, &target.sty) {
@@ -641,7 +638,7 @@ pub fn unsized_info<'ccx, 'tcx>(ccx: &CrateContext<'ccx, 'tcx>,
                 def_id: principal.def_id(),
                 substs: substs,
             });
-            consts::ptrcast(meth::get_vtable(ccx, trait_ref, param_substs),
+            consts::ptrcast(meth::get_vtable(ccx, trait_ref),
                             Type::vtable_ptr(ccx))
         }
         _ => ccx.sess().bug(&format!("unsized_info: invalid unsizing {:?} -> {:?}",
@@ -668,7 +665,7 @@ pub fn unsize_thin_ptr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
             assert!(common::type_is_sized(bcx.tcx(), a));
             let ptr_ty = type_of::in_memory_type_of(bcx.ccx(), b).ptr_to();
             (PointerCast(bcx, src, ptr_ty),
-             unsized_info(bcx.ccx(), a, b, None, bcx.fcx.param_substs))
+             unsized_info(bcx.ccx(), a, b, None))
         }
         _ => bcx.sess().bug("unsize_thin_ptr: called on bad types"),
     }
@@ -900,29 +897,31 @@ pub fn fail_if_zero_or_overflows<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
     }
 }
 
-pub fn trans_external_path<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                                     did: DefId,
-                                     t: Ty<'tcx>)
-                                     -> ValueRef {
-    let name = ccx.sess().cstore.item_symbol(did);
-    match t.sty {
-        ty::TyBareFn(_, ref fn_ty) => {
-            match ccx.sess().target.target.adjust_abi(fn_ty.abi) {
-                Abi::Rust | Abi::RustCall => {
-                    get_extern_rust_fn(ccx, t, &name[..], did)
-                }
+pub fn get_extern_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
+                               def_id: DefId)
+                               -> datum::Datum<'tcx, datum::Rvalue> {
+    let name = ccx.sess().cstore.item_symbol(def_id);
+    let attrs = ccx.sess().cstore.item_attrs(def_id);
+    let ty = ccx.tcx().lookup_item_type(def_id).ty;
+    match ty.sty {
+        ty::TyFnDef(_, _, fty) => {
+            let abi = fty.abi;
+            let fty = infer::normalize_associated_type(ccx.tcx(), fty);
+            let ty = ccx.tcx().mk_fn_ptr(fty);
+            let llfn = match ccx.sess().target.target.adjust_abi(abi) {
                 Abi::RustIntrinsic | Abi::PlatformIntrinsic => {
-                    ccx.sess().bug("unexpected intrinsic in trans_external_path")
+                    ccx.sess().bug("unexpected intrinsic in get_extern_fn")
+                }
+                Abi::Rust | Abi::RustCall => {
+                    get_extern_rust_fn(ccx, ty, &name, &attrs)
                 }
                 _ => {
-                    let attrs = ccx.sess().cstore.item_attrs(did);
-                    foreign::register_foreign_item_fn(ccx, fn_ty.abi, t, &name, &attrs)
+                    foreign::register_foreign_item_fn(ccx, abi, ty, &name, &attrs)
                 }
-            }
-        }
-        _ => {
-            get_extern_const(ccx, did, t)
+            };
+            datum::immediate_rvalue(llfn, ty)
         }
+        _ => unreachable!("get_extern_fn: expected fn item type, found {}", ty)
     }
 }
 
@@ -2610,7 +2609,7 @@ fn register_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                          node_id: ast::NodeId,
                          node_type: Ty<'tcx>)
                          -> ValueRef {
-    if let ty::TyBareFn(_, ref f) = node_type.sty {
+    if let ty::TyFnDef(_, _, ref f) = node_type.sty {
         if f.abi != Abi::Rust && f.abi != Abi::RustCall {
             ccx.sess().span_bug(sp,
                                 &format!("only the `{}` or `{}` calling conventions are valid \
@@ -2685,8 +2684,7 @@ pub fn create_entry_wrapper(ccx: &CrateContext, sp: Span, main_llfn: ValueRef) {
                                                                .as_local_node_id(start_def_id) {
                     get_item_val(ccx, start_node_id)
                 } else {
-                    let start_fn_type = ccx.tcx().lookup_item_type(start_def_id).ty;
-                    trans_external_path(ccx, start_def_id, start_fn_type)
+                    get_extern_fn(ccx, start_def_id).val
                 };
                 let args = {
                     let opaque_rust_main =
@@ -2915,7 +2913,7 @@ fn register_method(ccx: &CrateContext,
 
     let sym = exported_name(ccx, id, mty, &attrs);
 
-    if let ty::TyBareFn(_, ref f) = mty.sty {
+    if let ty::TyFnDef(_, _, ref f) = mty.sty {
         let llfn = if f.abi == Abi::Rust || f.abi == Abi::RustCall {
             register_fn(ccx, span, sym, id, mty)
         } else {
diff --git a/src/librustc_trans/trans/callee.rs b/src/librustc_trans/trans/callee.rs
index 85660f1641d..05e5ac808d0 100644
--- a/src/librustc_trans/trans/callee.rs
+++ b/src/librustc_trans/trans/callee.rs
@@ -14,7 +14,6 @@
 //! and methods are represented as just a fn ptr and not a full
 //! closure.
 
-pub use self::AutorefArg::*;
 pub use self::CalleeData::*;
 pub use self::CallArgs::*;
 
@@ -22,7 +21,6 @@ use arena::TypedArena;
 use back::link;
 use llvm::{self, ValueRef, get_params};
 use middle::cstore::LOCAL_CRATE;
-use middle::def::Def;
 use middle::def_id::DefId;
 use middle::infer;
 use middle::subst;
@@ -32,14 +30,13 @@ use trans::adt;
 use trans::base;
 use trans::base::*;
 use trans::build::*;
-use trans::callee;
 use trans::cleanup;
 use trans::cleanup::CleanupMethods;
 use trans::common::{self, Block, Result, NodeIdAndSpan, ExprId, CrateContext,
                     ExprOrMethodCall, FunctionContext, MethodCallKey};
 use trans::consts;
 use trans::datum::*;
-use trans::debuginfo::{DebugLoc, ToDebugLoc};
+use trans::debuginfo::DebugLoc;
 use trans::declare;
 use trans::expr;
 use trans::glue;
@@ -52,177 +49,148 @@ use trans::type_::Type;
 use trans::type_of;
 use trans::Disr;
 use middle::ty::{self, Ty, TyCtxt, TypeFoldable};
-use middle::ty::MethodCall;
 use rustc_front::hir;
 
 use syntax::abi::Abi;
 use syntax::ast;
+use syntax::codemap::DUMMY_SP;
 use syntax::errors;
 use syntax::ptr::P;
 
-#[derive(Copy, Clone)]
-pub struct MethodData {
-    pub llfn: ValueRef,
-    pub llself: ValueRef,
-}
-
 pub enum CalleeData<'tcx> {
-    // Constructor for enum variant/tuple-like-struct
-    // i.e. Some, Ok
+    /// Constructor for enum variant/tuple-like-struct.
     NamedTupleConstructor(Disr),
 
-    // Represents a (possibly monomorphized) top-level fn item or method
-    // item. Note that this is just the fn-ptr and is not a Rust closure
-    // value (which is a pair).
-    Fn(/* llfn */ ValueRef),
+    /// Function pointer.
+    Fn(ValueRef),
 
-    Intrinsic(ast::NodeId, subst::Substs<'tcx>),
+    Intrinsic(ast::NodeId, &'tcx subst::Substs<'tcx>),
 
-    TraitItem(MethodData)
+    /// Trait object found in the vtable at that index.
+    Virtual(usize)
 }
 
-pub struct Callee<'blk, 'tcx: 'blk> {
-    pub bcx: Block<'blk, 'tcx>,
+pub struct Callee<'tcx> {
     pub data: CalleeData<'tcx>,
     pub ty: Ty<'tcx>
 }
 
-fn trans<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, expr: &hir::Expr)
-                     -> Callee<'blk, 'tcx> {
-    let _icx = push_ctxt("trans_callee");
-    debug!("callee::trans(expr={:?})", expr);
-
-    // pick out special kinds of expressions that can be called:
-    match expr.node {
-        hir::ExprPath(..) => {
-            return trans_def(bcx, bcx.def(expr.id), expr);
+impl<'tcx> Callee<'tcx> {
+    /// Function pointer.
+    pub fn ptr(datum: Datum<'tcx, Rvalue>) -> Callee<'tcx> {
+        Callee {
+            data: Fn(datum.val),
+            ty: datum.ty
         }
-        _ => {}
     }
 
-    // any other expressions are closures:
-    return datum_callee(bcx, expr);
-
-    fn datum_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, expr: &hir::Expr)
-                                -> Callee<'blk, 'tcx> {
-        let DatumBlock { bcx, datum, .. } = expr::trans(bcx, expr);
-        match datum.ty.sty {
-            ty::TyBareFn(..) => {
-                Callee {
-                    bcx: bcx,
-                    ty: datum.ty,
-                    data: Fn(datum.to_llscalarish(bcx))
-                }
-            }
-            _ => {
-                bcx.tcx().sess.span_bug(
-                    expr.span,
-                    &format!("type of callee is neither bare-fn nor closure: {}",
-                             datum.ty));
-            }
-        }
+    /// Trait or impl method call.
+    pub fn method_call<'blk>(bcx: Block<'blk, 'tcx>,
+                             method_call: ty::MethodCall)
+                             -> Callee<'tcx> {
+        let method = bcx.tcx().tables.borrow().method_map[&method_call];
+        Callee::method(bcx, method)
     }
 
-    fn fn_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, datum: Datum<'tcx, Rvalue>)
-                             -> Callee<'blk, 'tcx> {
-        Callee {
-            bcx: bcx,
-            data: Fn(datum.val),
-            ty: datum.ty
-        }
+    /// Trait or impl method.
+    pub fn method<'blk>(bcx: Block<'blk, 'tcx>,
+                        method: ty::MethodCallee<'tcx>) -> Callee<'tcx> {
+        let substs = bcx.tcx().mk_substs(bcx.fcx.monomorphize(&method.substs));
+        let ty = bcx.fcx.monomorphize(&method.ty);
+        Callee::def(bcx.ccx(), method.def_id, substs, ty)
     }
 
-    fn trans_def<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
-                             def: Def,
-                             ref_expr: &hir::Expr)
-                             -> Callee<'blk, 'tcx> {
-        debug!("trans_def(def={:?}, ref_expr={:?})", def, ref_expr);
-        let expr_ty = common::node_id_type(bcx, ref_expr.id);
-        match def {
-            Def::Fn(did) if {
-                let maybe_def_id = inline::get_local_instance(bcx.ccx(), did);
-                let maybe_ast_node = maybe_def_id.and_then(|def_id| {
-                    let node_id = bcx.tcx().map.as_local_node_id(def_id).unwrap();
-                    bcx.tcx().map.find(node_id)
-                });
-                match maybe_ast_node {
-                    Some(hir_map::NodeStructCtor(_)) => true,
-                    _ => false
-                }
-            } => {
-                Callee {
-                    bcx: bcx,
+    /// Function or method definition.
+    pub fn def<'a>(ccx: &CrateContext<'a, 'tcx>,
+                   def_id: DefId,
+                   substs: &'tcx subst::Substs<'tcx>,
+                   ty: Ty<'tcx>)
+                   -> Callee<'tcx> {
+        let tcx = ccx.tcx();
+
+        if substs.self_ty().is_some() {
+            // Only trait methods can have a Self parameter.
+            let method_item = tcx.impl_or_trait_item(def_id);
+            let trait_id = method_item.container().id();
+            let trait_ref = ty::Binder(substs.to_trait_ref(tcx, trait_id));
+            let vtbl = common::fulfill_obligation(ccx, DUMMY_SP, trait_ref);
+            return meth::callee_for_trait_impl(ccx, def_id, substs,
+                                               trait_id, ty, vtbl);
+        }
+
+        let maybe_node_id = inline::get_local_instance(ccx, def_id)
+            .and_then(|def_id| tcx.map.as_local_node_id(def_id));
+        let maybe_ast_node = maybe_node_id.and_then(|node_id| {
+            tcx.map.find(node_id)
+        });
+        match maybe_ast_node {
+            Some(hir_map::NodeStructCtor(_)) => {
+                return Callee {
                     data: NamedTupleConstructor(Disr(0)),
-                    ty: expr_ty
-                }
-            }
-            Def::Fn(did) if match expr_ty.sty {
-                ty::TyBareFn(_, ref f) => f.abi == Abi::RustIntrinsic ||
-                                          f.abi == Abi::PlatformIntrinsic,
-                _ => false
-            } => {
-                let substs = common::node_id_substs(bcx.ccx(),
-                                                    ExprId(ref_expr.id),
-                                                    bcx.fcx.param_substs);
-                let def_id = inline::maybe_instantiate_inline(bcx.ccx(), did);
-                let node_id = bcx.tcx().map.as_local_node_id(def_id).unwrap();
-                Callee { bcx: bcx, data: Intrinsic(node_id, substs), ty: expr_ty }
-            }
-            Def::Fn(did) => {
-                fn_callee(bcx, trans_fn_ref(bcx.ccx(), did, ExprId(ref_expr.id),
-                                            bcx.fcx.param_substs))
-            }
-            Def::Method(meth_did) => {
-                let method_item = bcx.tcx().impl_or_trait_item(meth_did);
-                let fn_datum = match method_item.container() {
-                    ty::ImplContainer(_) => {
-                        trans_fn_ref(bcx.ccx(), meth_did,
-                                     ExprId(ref_expr.id),
-                                     bcx.fcx.param_substs)
-                    }
-                    ty::TraitContainer(trait_did) => {
-                        meth::trans_static_method_callee(bcx.ccx(),
-                                                         meth_did,
-                                                         trait_did,
-                                                         ref_expr.id,
-                                                         bcx.fcx.param_substs)
-                    }
+                    ty: ty
                 };
-                fn_callee(bcx, fn_datum)
             }
-            Def::Variant(tid, vid) => {
-                let vinfo = bcx.tcx().lookup_adt_def(tid).variant_with_id(vid);
+            Some(hir_map::NodeVariant(_)) => {
+                let vinfo = common::inlined_variant_def(ccx, maybe_node_id.unwrap());
                 assert_eq!(vinfo.kind(), ty::VariantKind::Tuple);
 
-                Callee {
-                    bcx: bcx,
+                return Callee {
                     data: NamedTupleConstructor(Disr::from(vinfo.disr_val)),
-                    ty: expr_ty
-                }
+                    ty: ty
+                };
             }
-            Def::Struct(..) => {
-                Callee {
-                    bcx: bcx,
-                    data: NamedTupleConstructor(Disr(0)),
-                    ty: expr_ty
+            Some(hir_map::NodeForeignItem(fi)) => {
+                let abi = tcx.map.get_foreign_abi(fi.id);
+                if abi == Abi::RustIntrinsic || abi == Abi::PlatformIntrinsic {
+                    return Callee {
+                        data: Intrinsic(fi.id, substs),
+                        ty: ty
+                    };
                 }
             }
-            Def::Static(..) |
-            Def::Const(..) |
-            Def::AssociatedConst(..) |
-            Def::Local(..) |
-            Def::Upvar(..) => {
-                datum_callee(bcx, ref_expr)
-            }
-            Def::Mod(..) | Def::ForeignMod(..) | Def::Trait(..) |
-            Def::Enum(..) | Def::TyAlias(..) | Def::PrimTy(..) |
-            Def::AssociatedTy(..) | Def::Label(..) | Def::TyParam(..) |
-            Def::SelfTy(..) | Def::Err => {
-                bcx.tcx().sess.span_bug(
-                    ref_expr.span,
-                    &format!("cannot translate def {:?} \
-                             to a callable thing!", def));
+            _ => {}
+        }
+        Callee::ptr(trans_fn_ref_with_substs(ccx, def_id, Some(ty), substs))
+    }
+
+    /// This behemoth of a function translates function calls. Unfortunately, in
+    /// order to generate more efficient LLVM output at -O0, it has quite a complex
+    /// signature (refactoring this into two functions seems like a good idea).
+    ///
+    /// In particular, for lang items, it is invoked with a dest of None, and in
+    /// that case the return value contains the result of the fn. The lang item must
+    /// not return a structural type or else all heck breaks loose.
+    ///
+    /// For non-lang items, `dest` is always Some, and hence the result is written
+    /// into memory somewhere. Nonetheless we return the actual return value of the
+    /// function.
+    pub fn call<'a, 'blk>(self, bcx: Block<'blk, 'tcx>,
+                          debug_loc: DebugLoc,
+                          args: CallArgs<'a, 'tcx>,
+                          dest: Option<expr::Dest>)
+                          -> Result<'blk, 'tcx> {
+        trans_call_inner(bcx, debug_loc, self, args, dest)
+    }
+
+    /// Turn the callee into a function pointer.
+    pub fn reify<'a>(self, ccx: &CrateContext<'a, 'tcx>)
+                     -> Datum<'tcx, Rvalue> {
+        match self.data {
+            Fn(llfn) => {
+                let fn_ptr_ty = match self.ty.sty {
+                    ty::TyFnDef(_, _, f) => ccx.tcx().mk_ty(ty::TyFnPtr(f)),
+                    _ => self.ty
+                };
+                immediate_rvalue(llfn, fn_ptr_ty)
             }
+            Virtual(idx) => meth::trans_object_shim(ccx, self.ty, idx),
+            NamedTupleConstructor(_) => match self.ty.sty {
+                ty::TyFnDef(def_id, substs, _) => {
+                    return trans_fn_ref_with_substs(ccx, def_id, Some(self.ty), substs);
+                }
+                _ => unreachable!("expected fn item type, found {}", self.ty)
+            },
+            Intrinsic(..) => unreachable!("intrinsic {} getting reified", self.ty)
         }
     }
 }
@@ -241,7 +209,17 @@ pub fn trans_fn_ref<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
            def_id,
            node,
            substs);
-    trans_fn_ref_with_substs(ccx, def_id, node, param_substs, substs)
+    let ref_ty = match node {
+        ExprId(0) => return trans_fn_ref_with_substs(ccx, def_id, None, substs),
+        ExprId(id) => ccx.tcx().node_id_to_type(id),
+        MethodCallKey(method_call) => {
+            ccx.tcx().tables.borrow().method_map[&method_call].ty
+        }
+    };
+    let ref_ty = monomorphize::apply_param_substs(ccx.tcx(),
+                                                  param_substs,
+                                                  &ref_ty);
+    trans_fn_ref_with_substs(ccx, def_id, Some(ref_ty), substs)
 }
 
 /// Translates an adapter that implements the `Fn` trait for a fn
@@ -290,33 +268,33 @@ pub fn trans_fn_pointer_shim<'a, 'tcx>(
 
     // Construct the "tuply" version of `bare_fn_ty`. It takes two arguments: `self`,
     // which is the fn pointer, and `args`, which is the arguments tuple.
-    let (opt_def_id, sig) =
-        match bare_fn_ty.sty {
-            ty::TyBareFn(opt_def_id,
-                           &ty::BareFnTy { unsafety: hir::Unsafety::Normal,
-                                           abi: Abi::Rust,
-                                           ref sig }) => {
-                (opt_def_id, sig)
-            }
+    let sig = match bare_fn_ty.sty {
+        ty::TyFnDef(_, _,
+                    &ty::BareFnTy { unsafety: hir::Unsafety::Normal,
+                                    abi: Abi::Rust,
+                                    ref sig }) |
+        ty::TyFnPtr(&ty::BareFnTy { unsafety: hir::Unsafety::Normal,
+                                    abi: Abi::Rust,
+                                    ref sig }) => sig,
 
-            _ => {
-                tcx.sess.bug(&format!("trans_fn_pointer_shim invoked on invalid type: {}",
-                                      bare_fn_ty));
-            }
-        };
+        _ => {
+            tcx.sess.bug(&format!("trans_fn_pointer_shim invoked on invalid type: {}",
+                                    bare_fn_ty));
+        }
+    };
     let sig = tcx.erase_late_bound_regions(sig);
     let sig = infer::normalize_associated_type(ccx.tcx(), &sig);
     let tuple_input_ty = tcx.mk_tup(sig.inputs.to_vec());
-    let tuple_fn_ty = tcx.mk_fn(opt_def_id,
-        tcx.mk_bare_fn(ty::BareFnTy {
-            unsafety: hir::Unsafety::Normal,
-            abi: Abi::RustCall,
-            sig: ty::Binder(ty::FnSig {
-                inputs: vec![bare_fn_ty_maybe_ref,
-                             tuple_input_ty],
-                output: sig.output,
-                variadic: false
-            })}));
+    let tuple_fn_ty = tcx.mk_fn_ptr(ty::BareFnTy {
+        unsafety: hir::Unsafety::Normal,
+        abi: Abi::RustCall,
+        sig: ty::Binder(ty::FnSig {
+            inputs: vec![bare_fn_ty_maybe_ref,
+                         tuple_input_ty],
+            output: sig.output,
+            variadic: false
+        })
+    });
     debug!("tuple_fn_ty: {:?}", tuple_fn_ty);
 
     //
@@ -341,11 +319,18 @@ pub fn trans_fn_pointer_shim<'a, 'tcx>(
     let llargs = get_params(fcx.llfn);
 
     let self_idx = fcx.arg_offset();
-    // the first argument (`self`) will be ptr to the fn pointer
-    let llfnpointer = if is_by_ref {
-        Load(bcx, llargs[self_idx])
-    } else {
-        llargs[self_idx]
+    let llfnpointer = match bare_fn_ty.sty {
+        ty::TyFnDef(def_id, substs, _) => {
+            // Function definitions have to be turned into a pointer.
+            Callee::def(ccx, def_id, substs, bare_fn_ty).reify(ccx).val
+        }
+
+        // the first argument (`self`) will be ptr to the fn pointer
+        _ => if is_by_ref {
+            Load(bcx, llargs[self_idx])
+        } else {
+            llargs[self_idx]
+        }
     };
 
     assert!(!fcx.needs_ret_allocas);
@@ -354,13 +339,11 @@ pub fn trans_fn_pointer_shim<'a, 'tcx>(
         expr::SaveIn(fcx.get_ret_slot(bcx, sig.output, "ret_slot"))
     );
 
-    bcx = trans_call_inner(bcx, DebugLoc::None, |bcx, _| {
-        Callee {
-            bcx: bcx,
-            data: Fn(llfnpointer),
-            ty: bare_fn_ty
-        }
-    }, ArgVals(&llargs[(self_idx + 1)..]), dest).bcx;
+    let callee = Callee {
+        data: Fn(llfnpointer),
+        ty: bare_fn_ty
+    };
+    bcx = callee.call(bcx, DebugLoc::None, ArgVals(&llargs[(self_idx + 1)..]), dest).bcx;
 
     finish_fn(&fcx, bcx, sig.output, DebugLoc::None);
 
@@ -379,30 +362,25 @@ pub fn trans_fn_pointer_shim<'a, 'tcx>(
 /// - `node`: node id of the reference to the fn/method, if applicable.
 ///   This parameter may be zero; but, if so, the resulting value may not
 ///   have the right type, so it must be cast before being used.
-/// - `param_substs`: if the `node` is in a polymorphic function, these
-///   are the substitutions required to monomorphize its type
+/// - `ref_ty`: monotype of the reference to the fn/method, if applicable.
+///   This parameter may be None; but, if so, the resulting value may not
+///   have the right type, so it must be cast before being used.
 /// - `substs`: values for each of the fn/method's parameters
 pub fn trans_fn_ref_with_substs<'a, 'tcx>(
     ccx: &CrateContext<'a, 'tcx>,
     def_id: DefId,
-    node: ExprOrMethodCall,
-    param_substs: &'tcx subst::Substs<'tcx>,
-    substs: subst::Substs<'tcx>)
+    ref_ty: Option<Ty<'tcx>>,
+    substs: &'tcx subst::Substs<'tcx>)
     -> Datum<'tcx, Rvalue>
 {
     let _icx = push_ctxt("trans_fn_ref_with_substs");
     let tcx = ccx.tcx();
 
-    debug!("trans_fn_ref_with_substs(def_id={:?}, node={:?}, \
-            param_substs={:?}, substs={:?})",
-           def_id,
-           node,
-           param_substs,
-           substs);
+    debug!("trans_fn_ref_with_substs(def_id={:?}, ref_ty={:?}, substs={:?})",
+           def_id, ref_ty, substs);
 
     assert!(!substs.types.needs_infer());
     assert!(!substs.types.has_escaping_regions());
-    let substs = substs.erase_regions();
 
     // Check whether this fn has an inlined copy and, if so, redirect
     // def_id to the local id of the inlined copy.
@@ -437,48 +415,45 @@ pub fn trans_fn_ref_with_substs<'a, 'tcx>(
         // Should be either intra-crate or inlined.
         assert_eq!(def_id.krate, LOCAL_CRATE);
 
-        let opt_ref_id = match node {
-            ExprId(id) => if id != 0 { Some(id) } else { None },
-            MethodCallKey(_) => None,
+        let substs = tcx.mk_substs(substs.clone().erase_regions());
+        let (mut val, fn_ty, must_cast) =
+            monomorphize::monomorphic_fn(ccx, def_id, substs);
+        let fn_ty = ref_ty.unwrap_or(fn_ty);
+        let fn_ptr_ty = match fn_ty.sty {
+            ty::TyFnDef(_, _, fty) => {
+                // Create a fn pointer with the substituted signature.
+                tcx.mk_ty(ty::TyFnPtr(fty))
+            }
+            _ => unreachable!("expected fn item type, found {}", fn_ty)
         };
-
-        let substs = tcx.mk_substs(substs);
-        let (val, fn_ty, must_cast) =
-            monomorphize::monomorphic_fn(ccx, def_id, substs, opt_ref_id);
-        if must_cast && node != ExprId(0) {
-            // Monotype of the REFERENCE to the function (type params
-            // are subst'd)
-            let ref_ty = match node {
-                ExprId(id) => tcx.node_id_to_type(id),
-                MethodCallKey(method_call) => {
-                    tcx.tables.borrow().method_map[&method_call].ty
-                }
-            };
-            let ref_ty = monomorphize::apply_param_substs(tcx,
-                                                          param_substs,
-                                                          &ref_ty);
-            let llptrty = type_of::type_of_fn_from_ty(ccx, ref_ty).ptr_to();
+        if must_cast && ref_ty.is_some() {
+            let llptrty = type_of::type_of(ccx, fn_ptr_ty);
             if llptrty != common::val_ty(val) {
-                let val = consts::ptrcast(val, llptrty);
-                return Datum::new(val, ref_ty, Rvalue::new(ByValue));
+                val = consts::ptrcast(val, llptrty);
             }
         }
-        return Datum::new(val, fn_ty, Rvalue::new(ByValue));
+        return immediate_rvalue(val, fn_ptr_ty);
     }
 
-    // Type scheme of the function item (may have type params)
-    let fn_type_scheme = tcx.lookup_item_type(def_id);
-    let fn_type = infer::normalize_associated_type(tcx, &fn_type_scheme.ty);
-
     // Find the actual function pointer.
-    let mut val = {
-        if let Some(node_id) = ccx.tcx().map.as_local_node_id(def_id) {
-            // Internal reference.
-            get_item_val(ccx, node_id)
-        } else {
-            // External reference.
-            trans_external_path(ccx, def_id, fn_type)
-        }
+    let local_node = ccx.tcx().map.as_local_node_id(def_id);
+    let mut datum = if let Some(node_id) = local_node {
+        // Type scheme of the function item (may have type params)
+        let fn_type_scheme = tcx.lookup_item_type(def_id);
+        let fn_type = match fn_type_scheme.ty.sty {
+            ty::TyFnDef(_, _, fty) => {
+                // Create a fn pointer with the normalized signature.
+                tcx.mk_fn_ptr(infer::normalize_associated_type(tcx, fty))
+            }
+            _ => unreachable!("expected fn item type, found {}",
+                              fn_type_scheme.ty)
+        };
+
+        // Internal reference.
+        immediate_rvalue(get_item_val(ccx, node_id), fn_type)
+    } else {
+        // External reference.
+        get_extern_fn(ccx, def_id)
     };
 
     // This is subtle and surprising, but sometimes we have to bitcast
@@ -504,93 +479,36 @@ pub fn trans_fn_ref_with_substs<'a, 'tcx>(
     // This can occur on either a crate-local or crate-external
     // reference. It also occurs when testing libcore and in some
     // other weird situations. Annoying.
-    let llty = type_of::type_of_fn_from_ty(ccx, fn_type);
-    let llptrty = llty.ptr_to();
-    if common::val_ty(val) != llptrty {
+    let llptrty = type_of::type_of(ccx, datum.ty);
+    if common::val_ty(datum.val) != llptrty {
         debug!("trans_fn_ref_with_substs(): casting pointer!");
-        val = consts::ptrcast(val, llptrty);
+        datum.val = consts::ptrcast(datum.val, llptrty);
     } else {
         debug!("trans_fn_ref_with_substs(): not casting pointer!");
     }
 
-    Datum::new(val, fn_type, Rvalue::new(ByValue))
+    datum
 }
 
 // ______________________________________________________________________
 // Translating calls
 
-pub fn trans_call<'a, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
-                                  call_expr: &hir::Expr,
-                                  f: &hir::Expr,
-                                  args: CallArgs<'a, 'tcx>,
-                                  dest: expr::Dest)
-                                  -> Block<'blk, 'tcx> {
-    let _icx = push_ctxt("trans_call");
-    trans_call_inner(bcx,
-                     call_expr.debug_loc(),
-                     |bcx, _| trans(bcx, f),
-                     args,
-                     Some(dest)).bcx
-}
-
-pub fn trans_method_call<'a, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
-                                         call_expr: &hir::Expr,
-                                         rcvr: &hir::Expr,
-                                         args: CallArgs<'a, 'tcx>,
-                                         dest: expr::Dest)
-                                         -> Block<'blk, 'tcx> {
-    let _icx = push_ctxt("trans_method_call");
-    debug!("trans_method_call(call_expr={:?})", call_expr);
-    let method_call = MethodCall::expr(call_expr.id);
-    trans_call_inner(
-        bcx,
-        call_expr.debug_loc(),
-        |cx, arg_cleanup_scope| {
-            meth::trans_method_callee(cx, method_call, Some(rcvr), arg_cleanup_scope)
-        },
-        args,
-        Some(dest)).bcx
-}
-
 pub fn trans_lang_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                    did: DefId,
                                    args: &[ValueRef],
                                    dest: Option<expr::Dest>,
                                    debug_loc: DebugLoc)
                                    -> Result<'blk, 'tcx> {
-    callee::trans_call_inner(bcx, debug_loc, |bcx, _| {
-        let datum = trans_fn_ref_with_substs(bcx.ccx(),
-                                             did,
-                                             ExprId(0),
-                                             bcx.fcx.param_substs,
-                                             subst::Substs::trans_empty());
-        Callee {
-            bcx: bcx,
-            data: Fn(datum.val),
-            ty: datum.ty
-        }
-    }, ArgVals(args), dest)
+    let datum = trans_fn_ref(bcx.ccx(), did, ExprId(0), bcx.fcx.param_substs);
+    Callee::ptr(datum).call(bcx, debug_loc, ArgVals(args), dest)
 }
 
-/// This behemoth of a function translates function calls. Unfortunately, in
-/// order to generate more efficient LLVM output at -O0, it has quite a complex
-/// signature (refactoring this into two functions seems like a good idea).
-///
-/// In particular, for lang items, it is invoked with a dest of None, and in
-/// that case the return value contains the result of the fn. The lang item must
-/// not return a structural type or else all heck breaks loose.
-///
-/// For non-lang items, `dest` is always Some, and hence the result is written
-/// into memory somewhere. Nonetheless we return the actual return value of the
-/// function.
-pub fn trans_call_inner<'a, 'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>,
-                                           debug_loc: DebugLoc,
-                                           get_callee: F,
-                                           args: CallArgs<'a, 'tcx>,
-                                           dest: Option<expr::Dest>)
-                                           -> Result<'blk, 'tcx> where
-    F: FnOnce(Block<'blk, 'tcx>, cleanup::ScopeId) -> Callee<'blk, 'tcx>,
-{
+fn trans_call_inner<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
+                                    debug_loc: DebugLoc,
+                                    callee: Callee<'tcx>,
+                                    args: CallArgs<'a, 'tcx>,
+                                    dest: Option<expr::Dest>)
+                                    -> Result<'blk, 'tcx> {
     // Introduce a temporary cleanup scope that will contain cleanups
     // for the arguments while they are being evaluated. The purpose
     // this cleanup is to ensure that, should a panic occur while
@@ -600,27 +518,17 @@ pub fn trans_call_inner<'a, 'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>,
     // scope will ever execute.
     let fcx = bcx.fcx;
     let ccx = fcx.ccx;
-    let arg_cleanup_scope = fcx.push_custom_cleanup_scope();
-
-    let callee = get_callee(bcx, cleanup::CustomScope(arg_cleanup_scope));
-    let mut bcx = callee.bcx;
 
     let (abi, ret_ty) = match callee.ty.sty {
-        ty::TyBareFn(_, ref f) => {
+        ty::TyFnDef(_, _, ref f) | ty::TyFnPtr(ref f) => {
             let sig = bcx.tcx().erase_late_bound_regions(&f.sig);
             let sig = infer::normalize_associated_type(bcx.tcx(), &sig);
             (f.abi, sig.output)
         }
-        _ => panic!("expected bare rust fn or closure in trans_call_inner")
+        _ => panic!("expected fn item or ptr in Callee::call")
     };
 
-    let (llfn, llself) = match callee.data {
-        Fn(llfn) => {
-            (llfn, None)
-        }
-        TraitItem(d) => {
-            (d.llfn, Some(d.llself))
-        }
+    match callee.data {
         Intrinsic(node, substs) => {
             assert!(abi == Abi::RustIntrinsic || abi == Abi::PlatformIntrinsic);
             assert!(dest.is_some());
@@ -632,14 +540,15 @@ pub fn trans_call_inner<'a, 'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>,
                 }
             };
 
+            let arg_cleanup_scope = fcx.push_custom_cleanup_scope();
             return intrinsic::trans_intrinsic_call(bcx, node, callee.ty,
                                                    arg_cleanup_scope, args,
-                                                   dest.unwrap(), substs,
+                                                   dest.unwrap(),
+                                                   substs,
                                                    call_info);
         }
         NamedTupleConstructor(disr) => {
             assert!(dest.is_some());
-            fcx.pop_custom_cleanup_scope(arg_cleanup_scope);
 
             return base::trans_named_tuple_constructor(bcx,
                                                        callee.ty,
@@ -648,7 +557,8 @@ pub fn trans_call_inner<'a, 'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>,
                                                        dest.unwrap(),
                                                        debug_loc);
         }
-    };
+        _ => {}
+    }
 
     // Intrinsics should not become actual functions.
     // We trans them in place in `trans_intrinsic_call`
@@ -688,6 +598,8 @@ pub fn trans_call_inner<'a, 'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>,
         llvm::LLVMGetUndef(Type::nil(ccx).ptr_to().to_ref())
     };
 
+    let arg_cleanup_scope = fcx.push_custom_cleanup_scope();
+
     // The code below invokes the function, using either the Rust
     // conventions (if it is a rust fn) or the native conventions
     // (otherwise).  The important part is that, when all is said
@@ -711,10 +623,7 @@ pub fn trans_call_inner<'a, 'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>,
             }
         }
 
-        // Push a trait object's self.
-        if let Some(llself) = llself {
-            llargs.push(llself);
-        }
+        let arg_start = llargs.len();
 
         // Push the arguments.
         bcx = trans_args(bcx,
@@ -722,16 +631,25 @@ pub fn trans_call_inner<'a, 'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>,
                          callee.ty,
                          &mut llargs,
                          cleanup::CustomScope(arg_cleanup_scope),
-                         llself.is_some(),
                          abi);
 
         fcx.scopes.borrow_mut().last_mut().unwrap().drop_non_lifetime_clean();
 
+        let datum = match callee.data {
+            Fn(f) => immediate_rvalue(f, callee.ty),
+            Virtual(idx) => {
+                // The data and vtable pointers were split by trans_arg_datum.
+                let vtable = llargs.remove(arg_start + 1);
+                meth::get_virtual_method(bcx, vtable, idx, callee.ty)
+            }
+            _ => unreachable!()
+        };
+
         // Invoke the actual rust fn and update bcx/llresult.
         let (llret, b) = base::invoke(bcx,
-                                      llfn,
+                                      datum.val,
                                       &llargs[..],
-                                      callee.ty,
+                                      datum.ty,
                                       debug_loc);
         bcx = b;
         llresult = llret;
@@ -754,16 +672,17 @@ pub fn trans_call_inner<'a, 'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>,
         assert!(dest.is_some());
 
         let mut llargs = Vec::new();
-        let arg_tys = match args {
-            ArgExprs(a) => a.iter().map(|x| common::expr_ty_adjusted(bcx, &x)).collect(),
-            _ => panic!("expected arg exprs.")
+        let (llfn, arg_tys) = match (callee.data, &args) {
+            (Fn(f), &ArgExprs(a)) => {
+                (f, a.iter().map(|x| common::expr_ty_adjusted(bcx, &x)).collect())
+            }
+            _ => panic!("expected fn ptr and arg exprs.")
         };
         bcx = trans_args(bcx,
                          args,
                          callee.ty,
                          &mut llargs,
                          cleanup::CustomScope(arg_cleanup_scope),
-                         false,
                          abi);
         fcx.scopes.borrow_mut().last_mut().unwrap().drop_non_lifetime_clean();
 
@@ -800,23 +719,22 @@ pub fn trans_call_inner<'a, 'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>,
 }
 
 pub enum CallArgs<'a, 'tcx> {
-    // Supply value of arguments as a list of expressions that must be
-    // translated. This is used in the common case of `foo(bar, qux)`.
+    /// Supply value of arguments as a list of expressions that must be
+    /// translated. This is used in the common case of `foo(bar, qux)`.
     ArgExprs(&'a [P<hir::Expr>]),
 
-    // Supply value of arguments as a list of LLVM value refs; frequently
-    // used with lang items and so forth, when the argument is an internal
-    // value.
+    /// Supply value of arguments as a list of LLVM value refs; frequently
+    /// used with lang items and so forth, when the argument is an internal
+    /// value.
     ArgVals(&'a [ValueRef]),
 
-    // For overloaded operators: `(lhs, Option(rhs, rhs_id), autoref)`. `lhs`
-    // is the left-hand-side and `rhs/rhs_id` is the datum/expr-id of
-    // the right-hand-side argument (if any). `autoref` indicates whether the `rhs`
-    // arguments should be auto-referenced
-    ArgOverloadedOp(Datum<'tcx, Expr>, Option<(Datum<'tcx, Expr>, ast::NodeId)>, bool),
+    /// For overloaded operators: `(lhs, Option(rhs))`.
+    /// `lhs` is the left-hand-side and `rhs` is the datum
+    /// of the right-hand-side argument (if any).
+    ArgOverloadedOp(Datum<'tcx, Expr>, Option<Datum<'tcx, Expr>>),
 
-    // Supply value of arguments as a list of expressions that must be
-    // translated, for overloaded call operators.
+    /// Supply value of arguments as a list of expressions that must be
+    /// translated, for overloaded call operators.
     ArgOverloadedCall(Vec<&'a hir::Expr>),
 }
 
@@ -825,8 +743,7 @@ fn trans_args_under_call_abi<'blk, 'tcx>(
                              arg_exprs: &[P<hir::Expr>],
                              fn_ty: Ty<'tcx>,
                              llargs: &mut Vec<ValueRef>,
-                             arg_cleanup_scope: cleanup::ScopeId,
-                             ignore_self: bool)
+                             arg_cleanup_scope: cleanup::ScopeId)
                              -> Block<'blk, 'tcx>
 {
     let sig = bcx.tcx().erase_late_bound_regions(&fn_ty.fn_sig());
@@ -834,15 +751,12 @@ fn trans_args_under_call_abi<'blk, 'tcx>(
     let args = sig.inputs;
 
     // Translate the `self` argument first.
-    if !ignore_self {
-        let arg_datum = unpack_datum!(bcx, expr::trans(bcx, &arg_exprs[0]));
-        bcx = trans_arg_datum(bcx,
-                              args[0],
-                              arg_datum,
-                              arg_cleanup_scope,
-                              DontAutorefArg,
-                              llargs);
-    }
+    let arg_datum = unpack_datum!(bcx, expr::trans(bcx, &arg_exprs[0]));
+    bcx = trans_arg_datum(bcx,
+                          args[0],
+                          arg_datum,
+                          arg_cleanup_scope,
+                          llargs);
 
     // Now untuple the rest of the arguments.
     let tuple_expr = &arg_exprs[1];
@@ -870,7 +784,6 @@ fn trans_args_under_call_abi<'blk, 'tcx>(
                                       field_type,
                                       arg_datum,
                                       arg_cleanup_scope,
-                                      DontAutorefArg,
                                       llargs);
             }
         }
@@ -888,23 +801,19 @@ fn trans_overloaded_call_args<'blk, 'tcx>(
                               arg_exprs: Vec<&hir::Expr>,
                               fn_ty: Ty<'tcx>,
                               llargs: &mut Vec<ValueRef>,
-                              arg_cleanup_scope: cleanup::ScopeId,
-                              ignore_self: bool)
+                              arg_cleanup_scope: cleanup::ScopeId)
                               -> Block<'blk, 'tcx> {
     // Translate the `self` argument first.
     let sig = bcx.tcx().erase_late_bound_regions(&fn_ty.fn_sig());
     let sig = infer::normalize_associated_type(bcx.tcx(), &sig);
     let arg_tys = sig.inputs;
 
-    if !ignore_self {
-        let arg_datum = unpack_datum!(bcx, expr::trans(bcx, arg_exprs[0]));
-        bcx = trans_arg_datum(bcx,
-                              arg_tys[0],
-                              arg_datum,
-                              arg_cleanup_scope,
-                              DontAutorefArg,
-                              llargs);
-    }
+    let arg_datum = unpack_datum!(bcx, expr::trans(bcx, arg_exprs[0]));
+    bcx = trans_arg_datum(bcx,
+                          arg_tys[0],
+                          arg_datum,
+                          arg_cleanup_scope,
+                          llargs);
 
     // Now untuple the rest of the arguments.
     let tuple_type = arg_tys[1];
@@ -917,7 +826,6 @@ fn trans_overloaded_call_args<'blk, 'tcx>(
                                       field_type,
                                       arg_datum,
                                       arg_cleanup_scope,
-                                      DontAutorefArg,
                                       llargs);
             }
         }
@@ -935,7 +843,6 @@ pub fn trans_args<'a, 'blk, 'tcx>(cx: Block<'blk, 'tcx>,
                                   fn_ty: Ty<'tcx>,
                                   llargs: &mut Vec<ValueRef>,
                                   arg_cleanup_scope: cleanup::ScopeId,
-                                  ignore_self: bool,
                                   abi: Abi)
                                   -> Block<'blk, 'tcx> {
     debug!("trans_args(abi={})", abi);
@@ -960,15 +867,11 @@ pub fn trans_args<'a, 'blk, 'tcx>(cx: Block<'blk, 'tcx>,
                                                  arg_exprs,
                                                  fn_ty,
                                                  llargs,
-                                                 arg_cleanup_scope,
-                                                 ignore_self)
+                                                 arg_cleanup_scope)
             }
 
             let num_formal_args = arg_tys.len();
             for (i, arg_expr) in arg_exprs.iter().enumerate() {
-                if i == 0 && ignore_self {
-                    continue;
-                }
                 let arg_ty = if i >= num_formal_args {
                     assert!(variadic);
                     common::expr_ty_adjusted(cx, &arg_expr)
@@ -979,7 +882,6 @@ pub fn trans_args<'a, 'blk, 'tcx>(cx: Block<'blk, 'tcx>,
                 let arg_datum = unpack_datum!(bcx, expr::trans(bcx, &arg_expr));
                 bcx = trans_arg_datum(bcx, arg_ty, arg_datum,
                                       arg_cleanup_scope,
-                                      DontAutorefArg,
                                       llargs);
             }
         }
@@ -988,22 +890,19 @@ pub fn trans_args<'a, 'blk, 'tcx>(cx: Block<'blk, 'tcx>,
                                               arg_exprs,
                                               fn_ty,
                                               llargs,
-                                              arg_cleanup_scope,
-                                              ignore_self)
+                                              arg_cleanup_scope)
         }
-        ArgOverloadedOp(lhs, rhs, autoref) => {
+        ArgOverloadedOp(lhs, rhs) => {
             assert!(!variadic);
 
             bcx = trans_arg_datum(bcx, arg_tys[0], lhs,
                                   arg_cleanup_scope,
-                                  DontAutorefArg,
                                   llargs);
 
-            if let Some((rhs, rhs_id)) = rhs {
+            if let Some(rhs) = rhs {
                 assert_eq!(arg_tys.len(), 2);
                 bcx = trans_arg_datum(bcx, arg_tys[1], rhs,
                                       arg_cleanup_scope,
-                                      if autoref { DoAutorefArg(rhs_id) } else { DontAutorefArg },
                                       llargs);
             } else {
                 assert_eq!(arg_tys.len(), 1);
@@ -1017,17 +916,10 @@ pub fn trans_args<'a, 'blk, 'tcx>(cx: Block<'blk, 'tcx>,
     bcx
 }
 
-#[derive(Copy, Clone)]
-pub enum AutorefArg {
-    DontAutorefArg,
-    DoAutorefArg(ast::NodeId)
-}
-
 pub fn trans_arg_datum<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                    formal_arg_ty: Ty<'tcx>,
                                    arg_datum: Datum<'tcx, Expr>,
                                    arg_cleanup_scope: cleanup::ScopeId,
-                                   autoref_arg: AutorefArg,
                                    llargs: &mut Vec<ValueRef>)
                                    -> Block<'blk, 'tcx> {
     let _icx = push_ctxt("trans_arg_datum");
@@ -1041,37 +933,25 @@ pub fn trans_arg_datum<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
 
     debug!("   arg datum: {}", arg_datum.to_string(bcx.ccx()));
 
-    let mut val;
-    // FIXME(#3548) use the adjustments table
-    match autoref_arg {
-        DoAutorefArg(arg_id) => {
-            // We will pass argument by reference
-            // We want an lvalue, so that we can pass by reference and
-            let arg_datum = unpack_datum!(
-                bcx, arg_datum.to_lvalue_datum(bcx, "arg", arg_id));
-            val = arg_datum.val;
-        }
-        DontAutorefArg if common::type_is_fat_ptr(bcx.tcx(), arg_datum_ty) &&
-                !bcx.fcx.type_needs_drop(arg_datum_ty) => {
-            val = arg_datum.val
-        }
-        DontAutorefArg => {
-            // Make this an rvalue, since we are going to be
-            // passing ownership.
-            let arg_datum = unpack_datum!(
-                bcx, arg_datum.to_rvalue_datum(bcx, "arg"));
-
-            // Now that arg_datum is owned, get it into the appropriate
-            // mode (ref vs value).
-            let arg_datum = unpack_datum!(
-                bcx, arg_datum.to_appropriate_datum(bcx));
-
-            // Technically, ownership of val passes to the callee.
-            // However, we must cleanup should we panic before the
-            // callee is actually invoked.
-            val = arg_datum.add_clean(bcx.fcx, arg_cleanup_scope);
-        }
-    }
+    let mut val = if common::type_is_fat_ptr(bcx.tcx(), arg_datum_ty) &&
+                     !bcx.fcx.type_needs_drop(arg_datum_ty) {
+        arg_datum.val
+    } else {
+        // Make this an rvalue, since we are going to be
+        // passing ownership.
+        let arg_datum = unpack_datum!(
+            bcx, arg_datum.to_rvalue_datum(bcx, "arg"));
+
+        // Now that arg_datum is owned, get it into the appropriate
+        // mode (ref vs value).
+        let arg_datum = unpack_datum!(
+            bcx, arg_datum.to_appropriate_datum(bcx));
+
+        // Technically, ownership of val passes to the callee.
+        // However, we must cleanup should we panic before the
+        // callee is actually invoked.
+        arg_datum.add_clean(bcx.fcx, arg_cleanup_scope)
+    };
 
     if type_of::arg_is_indirect(ccx, formal_arg_ty) && formal_arg_ty != arg_datum_ty {
         // this could happen due to e.g. subtyping
diff --git a/src/librustc_trans/trans/closure.rs b/src/librustc_trans/trans/closure.rs
index 0d9248a22b4..95ca250e844 100644
--- a/src/librustc_trans/trans/closure.rs
+++ b/src/librustc_trans/trans/closure.rs
@@ -17,7 +17,7 @@ use trans::adt;
 use trans::attributes;
 use trans::base::*;
 use trans::build::*;
-use trans::callee::{self, ArgVals, Callee, TraitItem, MethodData};
+use trans::callee::{self, ArgVals, Callee};
 use trans::cleanup::{CleanupMethods, CustomScope, ScopeId};
 use trans::common::*;
 use trans::datum::{self, Datum, rvalue_scratch_datum, Rvalue};
@@ -271,24 +271,8 @@ pub fn trans_closure_method<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>,
 
     // If the closure is a Fn closure, but a FnOnce is needed (etc),
     // then adapt the self type
-    let closure_kind = ccx.tcx().closure_kind(closure_def_id);
-    trans_closure_adapter_shim(ccx,
-                               closure_def_id,
-                               substs,
-                               closure_kind,
-                               trait_closure_kind,
-                               llfn)
-}
+    let llfn_closure_kind = ccx.tcx().closure_kind(closure_def_id);
 
-fn trans_closure_adapter_shim<'a, 'tcx>(
-    ccx: &'a CrateContext<'a, 'tcx>,
-    closure_def_id: DefId,
-    substs: ty::ClosureSubsts<'tcx>,
-    llfn_closure_kind: ty::ClosureKind,
-    trait_closure_kind: ty::ClosureKind,
-    llfn: ValueRef)
-    -> ValueRef
-{
     let _icx = push_ctxt("trans_closure_adapter_shim");
     let tcx = ccx.tcx();
 
@@ -355,28 +339,31 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>(
     // Make a version with the type of by-ref closure.
     let ty::ClosureTy { unsafety, abi, mut sig } = infcx.closure_type(closure_def_id, &substs);
     sig.0.inputs.insert(0, ref_closure_ty); // sig has no self type as of yet
-    let llref_bare_fn_ty = tcx.mk_bare_fn(ty::BareFnTy { unsafety: unsafety,
-                                                               abi: abi,
-                                                               sig: sig.clone() });
-    let llref_fn_ty = tcx.mk_fn(None, llref_bare_fn_ty);
+    let llref_fn_ty = tcx.mk_fn_ptr(ty::BareFnTy {
+        unsafety: unsafety,
+        abi: abi,
+        sig: sig.clone()
+    });
     debug!("trans_fn_once_adapter_shim: llref_fn_ty={:?}",
            llref_fn_ty);
 
+    let ret_ty = tcx.erase_late_bound_regions(&sig.output());
+    let ret_ty = infer::normalize_associated_type(ccx.tcx(), &ret_ty);
+
     // Make a version of the closure type with the same arguments, but
     // with argument #0 being by value.
     assert_eq!(abi, RustCall);
     sig.0.inputs[0] = closure_ty;
-    let llonce_bare_fn_ty = tcx.mk_bare_fn(ty::BareFnTy { unsafety: unsafety,
-                                                                abi: abi,
-                                                                sig: sig });
-    let llonce_fn_ty = tcx.mk_fn(None, llonce_bare_fn_ty);
+    let llonce_fn_ty = tcx.mk_fn_ptr(ty::BareFnTy {
+        unsafety: unsafety,
+        abi: abi,
+        sig: sig
+    });
 
     // Create the by-value helper.
     let function_name = link::mangle_internal_name_by_type_and_seq(ccx, llonce_fn_ty, "once_shim");
     let lloncefn = declare::define_internal_rust_fn(ccx, &function_name,
                                                     llonce_fn_ty);
-    let sig = tcx.erase_late_bound_regions(&llonce_bare_fn_ty.sig);
-    let sig = infer::normalize_associated_type(ccx.tcx(), &sig);
 
     let (block_arena, fcx): (TypedArena<_>, FunctionContext);
     block_arena = TypedArena::new();
@@ -384,13 +371,13 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>(
                       lloncefn,
                       ast::DUMMY_NODE_ID,
                       false,
-                      sig.output,
+                      ret_ty,
                       substs.func_substs,
                       None,
                       &block_arena);
-    let mut bcx = init_function(&fcx, false, sig.output);
+    let mut bcx = init_function(&fcx, false, ret_ty);
 
-    let llargs = get_params(fcx.llfn);
+    let mut llargs = get_params(fcx.llfn);
 
     // the first argument (`self`) will be the (by value) closure env.
     let self_scope = fcx.push_custom_cleanup_scope();
@@ -405,25 +392,21 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>(
 
     debug!("trans_fn_once_adapter_shim: env_datum={}",
            bcx.val_to_string(env_datum.val));
+    llargs[self_idx] = env_datum.val;
 
     let dest =
         fcx.llretslotptr.get().map(
-            |_| expr::SaveIn(fcx.get_ret_slot(bcx, sig.output, "ret_slot")));
-
-    let callee_data = TraitItem(MethodData { llfn: llreffn,
-                                             llself: env_datum.val });
+            |_| expr::SaveIn(fcx.get_ret_slot(bcx, ret_ty, "ret_slot")));
 
-    bcx = callee::trans_call_inner(bcx, DebugLoc::None, |bcx, _| {
-        Callee {
-            bcx: bcx,
-            data: callee_data,
-            ty: llref_fn_ty
-        }
-    }, ArgVals(&llargs[(self_idx + 1)..]), dest).bcx;
+    let callee = Callee {
+        data: callee::Fn(llreffn),
+        ty: llref_fn_ty
+    };
+    bcx = callee.call(bcx, DebugLoc::None, ArgVals(&llargs[self_idx..]), dest).bcx;
 
     fcx.pop_and_trans_custom_cleanup_scope(bcx, self_scope);
 
-    finish_fn(&fcx, bcx, sig.output, DebugLoc::None);
+    finish_fn(&fcx, bcx, ret_ty, DebugLoc::None);
 
     lloncefn
 }
diff --git a/src/librustc_trans/trans/collector.rs b/src/librustc_trans/trans/collector.rs
index b5b0f0a82d4..abfd127f388 100644
--- a/src/librustc_trans/trans/collector.rs
+++ b/src/librustc_trans/trans/collector.rs
@@ -542,14 +542,9 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
         debug!("visiting operand {:?}", *operand);
 
         let callee = match *operand {
-            mir::Operand::Constant(mir::Constant {
-                literal: mir::Literal::Item {
-                    def_id,
-                    kind,
-                    substs
-                },
-                ..
-            }) if is_function_or_method(kind) => Some((def_id, substs)),
+            mir::Operand::Constant(mir::Constant { ty: &ty::TyS {
+                sty: ty::TyFnDef(def_id, substs, _), ..
+            }, .. }) => Some((def_id, substs)),
             _ => None
         };
 
@@ -588,25 +583,18 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
 
         self.super_operand(operand);
 
-        fn is_function_or_method(item_kind: mir::ItemKind) -> bool {
-            match item_kind {
-                mir::ItemKind::Constant => false,
-                mir::ItemKind::Function |
-                mir::ItemKind::Method   => true
-            }
-        }
-
         fn can_result_in_trans_item<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                                               def_id: DefId)
                                               -> bool {
             if !match ccx.tcx().lookup_item_type(def_id).ty.sty {
-                ty::TyBareFn(Some(def_id), _) => {
-                    // Some constructors also have type TyBareFn but they are
+                ty::TyFnDef(def_id, _, _) => {
+                    // Some constructors also have type TyFnDef but they are
                     // always instantiated inline and don't result in
-                    // translation item.
+                    // translation item. Same for FFI functions.
                     match ccx.tcx().map.get_if_local(def_id) {
                         Some(hir_map::NodeVariant(_))    |
-                        Some(hir_map::NodeStructCtor(_)) => false,
+                        Some(hir_map::NodeStructCtor(_)) |
+                        Some(hir_map::NodeForeignItem(_)) => false,
                         Some(_) => true,
                         None => {
                             ccx.sess().cstore.variant_kind(def_id).is_none()
@@ -689,7 +677,7 @@ fn find_drop_glue_neighbors<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
         if can_have_local_instance(ccx, destructor_did) {
             let trans_item = create_fn_trans_item(ccx,
                                                   destructor_did,
-                                                  ccx.tcx().mk_substs(substs),
+                                                  substs,
                                                   &Substs::trans_empty());
             output.push(trans_item);
         }
@@ -697,17 +685,18 @@ fn find_drop_glue_neighbors<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
 
     // Finally add the types of nested values
     match ty.sty {
-        ty::TyBool       |
-        ty::TyChar       |
-        ty::TyInt(_)     |
-        ty::TyUint(_)    |
-        ty::TyStr        |
-        ty::TyFloat(_)   |
-        ty::TyRawPtr(_)  |
-        ty::TyRef(..)    |
-        ty::TyBareFn(..) |
-        ty::TySlice(_)   |
-        ty::TyTrait(_)   => {
+        ty::TyBool      |
+        ty::TyChar      |
+        ty::TyInt(_)    |
+        ty::TyUint(_)   |
+        ty::TyStr       |
+        ty::TyFloat(_)  |
+        ty::TyRawPtr(_) |
+        ty::TyRef(..)   |
+        ty::TyFnDef(..) |
+        ty::TyFnPtr(_)  |
+        ty::TySlice(_)  |
+        ty::TyTrait(_)  => {
             /* nothing to do */
         }
         ty::TyStruct(ref adt_def, substs) |
@@ -831,9 +820,9 @@ fn do_static_trait_method_dispatch<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
         {
             let callee_substs = impl_substs.with_method_from(&rcvr_substs);
             let impl_method = tcx.get_impl_method(impl_did,
-                                                  callee_substs,
+                                                  tcx.mk_substs(callee_substs),
                                                   trait_method.name);
-            Some((impl_method.method.def_id, tcx.mk_substs(impl_method.substs)))
+            Some((impl_method.method.def_id, impl_method.substs))
         }
         // If we have a closure or a function pointer, we will also encounter
         // the concrete closure/function somewhere else (during closure or fn
@@ -991,10 +980,9 @@ fn create_trans_items_for_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                         // create translation items
                         .filter_map(|impl_method| {
                             if can_have_local_instance(ccx, impl_method.method.def_id) {
-                                let substs = ccx.tcx().mk_substs(impl_method.substs);
                                 Some(create_fn_trans_item(ccx,
                                                           impl_method.method.def_id,
-                                                          substs,
+                                                          impl_method.substs,
                                                           &Substs::trans_empty()))
                             } else {
                                 None
@@ -1173,12 +1161,12 @@ fn create_trans_items_for_default_impls<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                     // The substitutions we have are on the impl, so we grab
                     // the method type from the impl to substitute into.
                     let mth = tcx.get_impl_method(impl_def_id,
-                                                  callee_substs.clone(),
+                                                  callee_substs,
                                                   default_impl.name);
 
                     assert!(mth.is_provided);
 
-                    let predicates = mth.method.predicates.predicates.subst(tcx, &mth.substs);
+                    let predicates = mth.method.predicates.predicates.subst(tcx, mth.substs);
                     if !normalize_and_test_predicates(ccx, predicates.into_vec()) {
                         continue;
                     }
@@ -1289,7 +1277,8 @@ pub fn push_unique_type_name<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                              &trait_data.bounds.projection_bounds,
                              output);
         },
-        ty::TyBareFn(_, &ty::BareFnTy{ unsafety, abi, ref sig } ) => {
+        ty::TyFnDef(_, _, &ty::BareFnTy{ unsafety, abi, ref sig } ) |
+        ty::TyFnPtr(&ty::BareFnTy{ unsafety, abi, ref sig } ) => {
             if unsafety == hir::Unsafety::Unsafe {
                 output.push_str("unsafe ");
             }
diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs
index 3ba27a4b787..34ef4f4acec 100644
--- a/src/librustc_trans/trans/common.rs
+++ b/src/librustc_trans/trans/common.rs
@@ -1228,7 +1228,7 @@ pub enum ExprOrMethodCall {
 pub fn node_id_substs<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                                 node: ExprOrMethodCall,
                                 param_substs: &subst::Substs<'tcx>)
-                                -> subst::Substs<'tcx> {
+                                -> &'tcx subst::Substs<'tcx> {
     let tcx = ccx.tcx();
 
     let substs = match node {
@@ -1245,9 +1245,9 @@ pub fn node_id_substs<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                               node, substs));
     }
 
-    monomorphize::apply_param_substs(tcx,
-                                     param_substs,
-                                     &substs.erase_regions())
+    ccx.tcx().mk_substs(monomorphize::apply_param_substs(tcx,
+                                                         param_substs,
+                                                         &substs.erase_regions()))
 }
 
 pub fn langcall(bcx: Block,
@@ -1277,7 +1277,7 @@ pub fn inlined_variant_def<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
     debug!("inlined_variant_def: ctor_ty={:?} inlined_vid={:?}", ctor_ty,
            inlined_vid);
     let adt_def = match ctor_ty.sty {
-        ty::TyBareFn(_, &ty::BareFnTy { sig: ty::Binder(ty::FnSig {
+        ty::TyFnDef(_, _, &ty::BareFnTy { sig: ty::Binder(ty::FnSig {
             output: ty::FnConverging(ty), ..
         }), ..}) => ty,
         _ => ctor_ty
diff --git a/src/librustc_trans/trans/consts.rs b/src/librustc_trans/trans/consts.rs
index 20da1583496..6c47cab64ef 100644
--- a/src/librustc_trans/trans/consts.rs
+++ b/src/librustc_trans/trans/consts.rs
@@ -28,6 +28,7 @@ use middle::def::Def;
 use middle::def_id::DefId;
 use trans::{adt, closure, debuginfo, expr, inline, machine};
 use trans::base::{self, push_ctxt};
+use trans::callee::Callee;
 use trans::collector::{self, TransItem};
 use trans::common::{self, type_is_sized, ExprOrMethodCall, node_id_substs, C_nil, const_get_elt};
 use trans::common::{CrateContext, C_integral, C_floating, C_bool, C_str_slice, C_bytes, val_ty};
@@ -211,7 +212,7 @@ fn const_fn_call<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
     let arg_ids = args.iter().map(|arg| arg.pat.id);
     let fn_args = arg_ids.zip(arg_vals.iter().cloned()).collect();
 
-    let substs = ccx.tcx().mk_substs(node_id_substs(ccx, node, param_substs));
+    let substs = node_id_substs(ccx, node, param_substs);
     match fn_like.body().expr {
         Some(ref expr) => {
             const_expr(ccx, &expr, substs, Some(&fn_args), trueconst).map(|(res, _)| res)
@@ -355,8 +356,16 @@ pub fn const_expr<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
     let opt_adj = cx.tcx().tables.borrow().adjustments.get(&e.id).cloned();
     match opt_adj {
         Some(AdjustReifyFnPointer) => {
-            // FIXME(#19925) once fn item types are
-            // zero-sized, we'll need to do something here
+            match ety.sty {
+                ty::TyFnDef(def_id, substs, _) => {
+                    let datum = Callee::def(cx, def_id, substs, ety).reify(cx);
+                    llconst = datum.val;
+                    ety_adjusted = datum.ty;
+                }
+                _ => {
+                    unreachable!("{} cannot be reified to a fn ptr", ety)
+                }
+            }
         }
         Some(AdjustUnsafeFnPointer) | Some(AdjustMutToConstPointer) => {
             // purely a type-level thing
@@ -413,8 +422,7 @@ pub fn const_expr<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                     .expect("consts: unsizing got non-pointer target type").ty;
                 let ptr_ty = type_of::in_memory_type_of(cx, unsized_ty).ptr_to();
                 let base = ptrcast(base, ptr_ty);
-                let info = base::unsized_info(cx, pointee_ty, unsized_ty,
-                                              old_info, param_substs);
+                let info = base::unsized_info(cx, pointee_ty, unsized_ty, old_info);
 
                 if old_info.is_none() {
                     let prev_const = cx.const_unsized().borrow_mut()
@@ -894,9 +902,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                         cx.sess().span_bug(e.span, "const fn argument not found")
                     }
                 }
-                Def::Fn(..) | Def::Method(..) => {
-                    expr::trans_def_fn_unadjusted(cx, e, def, param_substs).val
-                }
+                Def::Fn(..) | Def::Method(..) => C_nil(cx),
                 Def::Const(def_id) | Def::AssociatedConst(def_id) => {
                     load_const(cx, try!(get_const_val(cx, def_id, e, param_substs)),
                                ety)
@@ -908,23 +914,14 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                             let repr = adt::represent_type(cx, ety);
                             adt::trans_const(cx, &repr, Disr::from(vinfo.disr_val), &[])
                         }
-                        ty::VariantKind::Tuple => {
-                            expr::trans_def_fn_unadjusted(cx, e, def, param_substs).val
-                        }
+                        ty::VariantKind::Tuple => C_nil(cx),
                         ty::VariantKind::Struct => {
                             cx.sess().span_bug(e.span, "path-expr refers to a dict variant!")
                         }
                     }
                 }
-                Def::Struct(..) => {
-                    if let ty::TyBareFn(..) = ety.sty {
-                        // Tuple struct.
-                        expr::trans_def_fn_unadjusted(cx, e, def, param_substs).val
-                    } else {
-                        // Unit struct.
-                        C_null(type_of::type_of(cx, ety))
-                    }
-                }
+                // Unit struct or ctor.
+                Def::Struct(..) => C_null(type_of::type_of(cx, ety)),
                 _ => {
                     cx.sess().span_bug(e.span, "expected a const, fn, struct, \
                                                 or variant def")
diff --git a/src/librustc_trans/trans/debuginfo/metadata.rs b/src/librustc_trans/trans/debuginfo/metadata.rs
index 330d4077c41..0cd1f4e7fbf 100644
--- a/src/librustc_trans/trans/debuginfo/metadata.rs
+++ b/src/librustc_trans/trans/debuginfo/metadata.rs
@@ -253,7 +253,8 @@ impl<'tcx> TypeMap<'tcx> {
                                        principal.substs,
                                        &mut unique_type_id);
             },
-            ty::TyBareFn(_, &ty::BareFnTy{ unsafety, abi, ref sig } ) => {
+            ty::TyFnDef(_, _, &ty::BareFnTy{ unsafety, abi, ref sig } ) |
+            ty::TyFnPtr(&ty::BareFnTy{ unsafety, abi, ref sig } ) => {
                 if unsafety == hir::Unsafety::Unsafe {
                     unique_type_id.push_str("unsafe ");
                 }
@@ -765,7 +766,7 @@ pub fn type_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                 }
             }
         }
-        ty::TyBareFn(_, ref barefnty) => {
+        ty::TyFnDef(_, _, ref barefnty) | ty::TyFnPtr(ref barefnty) => {
             let fn_metadata = subroutine_type_metadata(cx,
                                                        unique_type_id,
                                                        &barefnty.sig,
diff --git a/src/librustc_trans/trans/debuginfo/mod.rs b/src/librustc_trans/trans/debuginfo/mod.rs
index 11dd631bee1..15275a46e9b 100644
--- a/src/librustc_trans/trans/debuginfo/mod.rs
+++ b/src/librustc_trans/trans/debuginfo/mod.rs
@@ -430,7 +430,7 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
         let fn_type = monomorphize::apply_param_substs(cx.tcx(), param_substs, &fn_type);
 
         let (sig, abi) = match fn_type.sty {
-            ty::TyBareFn(_, ref barefnty) => {
+            ty::TyFnDef(_, _, ref barefnty) | ty::TyFnPtr(ref barefnty) => {
                 let sig = cx.tcx().erase_late_bound_regions(&barefnty.sig);
                 let sig = infer::normalize_associated_type(cx.tcx(), &sig);
                 (sig, barefnty.abi)
diff --git a/src/librustc_trans/trans/debuginfo/type_names.rs b/src/librustc_trans/trans/debuginfo/type_names.rs
index f243b1e3bfa..cc9067677b2 100644
--- a/src/librustc_trans/trans/debuginfo/type_names.rs
+++ b/src/librustc_trans/trans/debuginfo/type_names.rs
@@ -101,7 +101,8 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
             push_item_name(cx, principal.def_id, false, output);
             push_type_params(cx, principal.substs, output);
         },
-        ty::TyBareFn(_, &ty::BareFnTy{ unsafety, abi, ref sig } ) => {
+        ty::TyFnDef(_, _, &ty::BareFnTy{ unsafety, abi, ref sig } ) |
+        ty::TyFnPtr(&ty::BareFnTy{ unsafety, abi, ref sig } ) => {
             if unsafety == hir::Unsafety::Unsafe {
                 output.push_str("unsafe ");
             }
diff --git a/src/librustc_trans/trans/declare.rs b/src/librustc_trans/trans/declare.rs
index 75b60be02f7..38e456c0688 100644
--- a/src/librustc_trans/trans/declare.rs
+++ b/src/librustc_trans/trans/declare.rs
@@ -106,7 +106,8 @@ pub fn declare_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, name: &str,
 
     let function_type; // placeholder so that the memory ownership works out ok
     let (sig, abi, env) = match fn_type.sty {
-        ty::TyBareFn(_, ref f) => {
+        ty::TyFnDef(_, _, f) |
+        ty::TyFnPtr(f) => {
             (&f.sig, f.abi, None)
         }
         ty::TyClosure(closure_did, ref substs) => {
diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs
index e3e53607829..ae03f58bce0 100644
--- a/src/librustc_trans/trans/expr.rs
+++ b/src/librustc_trans/trans/expr.rs
@@ -56,9 +56,10 @@ use llvm::{self, ValueRef, TypeKind};
 use middle::const_qualif::ConstQualif;
 use middle::def::Def;
 use middle::subst::Substs;
-use trans::{_match, adt, asm, base, callee, closure, consts, controlflow};
+use trans::{_match, adt, asm, base, closure, consts, controlflow};
 use trans::base::*;
 use trans::build::*;
+use trans::callee::{Callee, ArgExprs, ArgOverloadedCall, ArgOverloadedOp};
 use trans::cleanup::{self, CleanupMethods, DropHintMethods};
 use trans::common::*;
 use trans::datum::*;
@@ -66,7 +67,6 @@ use trans::debuginfo::{self, DebugLoc, ToDebugLoc};
 use trans::declare;
 use trans::glue;
 use trans::machine;
-use trans::meth;
 use trans::tvec;
 use trans::type_of;
 use trans::Disr;
@@ -85,7 +85,6 @@ use rustc_front::hir;
 
 use syntax::{ast, codemap};
 use syntax::parse::token::InternedString;
-use syntax::ptr::P;
 use std::mem;
 
 // Destinations
@@ -349,11 +348,7 @@ fn adjustment_required<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     }
 
     match adjustment {
-        AdjustReifyFnPointer => {
-            // FIXME(#19925) once fn item types are
-            // zero-sized, we'll need to return true here
-            false
-        }
+        AdjustReifyFnPointer => true,
         AdjustUnsafeFnPointer | AdjustMutToConstPointer => {
             // purely a type-level thing
             false
@@ -388,8 +383,15 @@ fn apply_adjustments<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
            adjustment);
     match adjustment {
         AdjustReifyFnPointer => {
-            // FIXME(#19925) once fn item types are
-            // zero-sized, we'll need to do something here
+            match datum.ty.sty {
+                ty::TyFnDef(def_id, substs, _) => {
+                    datum = Callee::def(bcx.ccx(), def_id, substs, datum.ty)
+                        .reify(bcx.ccx()).to_expr_datum();
+                }
+                _ => {
+                    unreachable!("{} cannot be reified to a fn ptr", datum.ty)
+                }
+            }
         }
         AdjustUnsafeFnPointer | AdjustMutToConstPointer => {
             // purely a type-level thing
@@ -492,8 +494,7 @@ fn coerce_unsized<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                 (val, None)
             };
 
-            let info = unsized_info(bcx.ccx(), inner_source, inner_target,
-                                    old_info, bcx.fcx.param_substs);
+            let info = unsized_info(bcx.ccx(), inner_source, inner_target, old_info);
 
             // Compute the base pointer. This doesn't change the pointer value,
             // but merely its type.
@@ -785,15 +786,10 @@ fn trans_index<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     let index_expr_debug_loc = index_expr.debug_loc();
 
     // Check for overloaded index.
-    let method_ty = ccx.tcx()
-                       .tables
-                       .borrow()
-                       .method_map
-                       .get(&method_call)
-                       .map(|method| method.ty);
-    let elt_datum = match method_ty {
-        Some(method_ty) => {
-            let method_ty = monomorphize_type(bcx, method_ty);
+    let method = ccx.tcx().tables.borrow().method_map.get(&method_call).cloned();
+    let elt_datum = match method {
+        Some(method) => {
+            let method_ty = monomorphize_type(bcx, method.ty);
 
             let base_datum = unpack_datum!(bcx, trans(bcx, base));
 
@@ -811,19 +807,16 @@ fn trans_index<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                 Some(elt_tm) => elt_tm.ty,
             };
 
-            // Overloaded. Evaluate `trans_overloaded_op`, which will
-            // invoke the user's index() method, which basically yields
-            // a `&T` pointer.  We can then proceed down the normal
-            // path (below) to dereference that `&T`.
+            // Overloaded. Invoke the index() method, which basically
+            // yields a `&T` pointer.  We can then proceed down the
+            // normal path (below) to dereference that `&T`.
             let scratch = rvalue_scratch_datum(bcx, ref_ty, "overloaded_index_elt");
-            unpack_result!(bcx,
-                           trans_overloaded_op(bcx,
-                                               index_expr,
-                                               method_call,
-                                               base_datum,
-                                               Some((ix_datum, idx.id)),
-                                               Some(SaveIn(scratch.val)),
-                                               false));
+
+            bcx = Callee::method(bcx, method)
+                .call(bcx, index_expr_debug_loc,
+                      ArgOverloadedOp(base_datum, Some(ix_datum)),
+                      Some(SaveIn(scratch.val))).bcx;
+
             let datum = scratch.to_expr_datum();
             let lval = Lvalue::new("expr::trans_index overload");
             if type_is_sized(bcx.tcx(), elt_ty) {
@@ -899,24 +892,18 @@ fn trans_def<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
 
     let _icx = push_ctxt("trans_def_lvalue");
     match def {
-        Def::Fn(..) | Def::Method(..) |
-        Def::Struct(..) | Def::Variant(..) => {
-            let datum = trans_def_fn_unadjusted(bcx.ccx(), ref_expr, def,
-                                                bcx.fcx.param_substs);
-            DatumBlock::new(bcx, datum.to_expr_datum())
-        }
         Def::Static(did, _) => {
             let const_ty = expr_ty(bcx, ref_expr);
             let val = get_static_val(bcx.ccx(), did, const_ty);
             let lval = Lvalue::new("expr::trans_def");
             DatumBlock::new(bcx, Datum::new(val, const_ty, LvalueExpr(lval)))
         }
-        Def::Const(_) | Def::AssociatedConst(_) => {
-            bcx.sess().span_bug(ref_expr.span,
-                "constant expression should not reach expr::trans_def")
+        Def::Local(..) | Def::Upvar(..) => {
+            DatumBlock::new(bcx, trans_local_var(bcx, def).to_expr_datum())
         }
         _ => {
-            DatumBlock::new(bcx, trans_local_var(bcx, def).to_expr_datum())
+            bcx.sess().span_bug(ref_expr.span,
+                &format!("{:?} should not reach expr::trans_def", def))
         }
     }
 }
@@ -1024,17 +1011,18 @@ fn trans_rvalue_stmt_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
             }
         }
         hir::ExprAssignOp(op, ref dst, ref src) => {
-            let has_method_map = bcx.tcx()
-                                    .tables
-                                    .borrow()
-                                    .method_map
-                                    .contains_key(&MethodCall::expr(expr.id));
+            let method = bcx.tcx().tables
+                                  .borrow()
+                                  .method_map
+                                  .get(&MethodCall::expr(expr.id)).cloned();
 
-            if has_method_map {
+            if let Some(method) = method {
                 let dst = unpack_datum!(bcx, trans(bcx, &dst));
                 let src_datum = unpack_datum!(bcx, trans(bcx, &src));
-                trans_overloaded_op(bcx, expr, MethodCall::expr(expr.id), dst,
-                                    Some((src_datum, src.id)), None, false).bcx
+
+                Callee::method(bcx, method)
+                    .call(bcx, expr.debug_loc(),
+                          ArgOverloadedOp(dst, Some(src_datum)), None).bcx
             } else {
                 trans_assign_op(bcx, expr, op, &dst, &src)
             }
@@ -1061,6 +1049,9 @@ fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
 
     debuginfo::set_source_location(bcx.fcx, expr.id, expr.span);
 
+    // Entry into the method table if this is an overloaded call/op.
+    let method_call = MethodCall::expr(expr.id);
+
     match expr.node {
         hir::ExprType(ref e, _) => {
             trans_into(bcx, &e, dest)
@@ -1144,47 +1135,54 @@ fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                         &expr.attrs).unwrap_or(bcx)
         }
         hir::ExprCall(ref f, ref args) => {
-            if bcx.tcx().is_method_call(expr.id) {
-                trans_overloaded_call(bcx,
-                                      expr,
-                                      &f,
-                                      &args[..],
-                                      Some(dest))
+            let method = bcx.tcx().tables.borrow().method_map.get(&method_call).cloned();
+            let (callee, args) = if let Some(method) = method {
+                let mut all_args = vec![&**f];
+                all_args.extend(args.iter().map(|e| &**e));
+
+                (Callee::method(bcx, method), ArgOverloadedCall(all_args))
             } else {
-                callee::trans_call(bcx,
-                                   expr,
-                                   &f,
-                                   callee::ArgExprs(&args[..]),
-                                   dest)
-            }
+                let f = unpack_datum!(bcx, trans(bcx, f));
+                (match f.ty.sty {
+                    ty::TyFnDef(def_id, substs, _) => {
+                        Callee::def(bcx.ccx(), def_id, substs, f.ty)
+                    }
+                    ty::TyFnPtr(_) => {
+                        let f = unpack_datum!(bcx,
+                            f.to_rvalue_datum(bcx, "callee"));
+                        Callee::ptr(f)
+                    }
+                    _ => {
+                        bcx.tcx().sess.span_bug(expr.span,
+                            &format!("type of callee is not a fn: {}", f.ty));
+                    }
+                }, ArgExprs(&args))
+            };
+            callee.call(bcx, expr.debug_loc(), args, Some(dest)).bcx
         }
         hir::ExprMethodCall(_, _, ref args) => {
-            callee::trans_method_call(bcx,
-                                      expr,
-                                      &args[0],
-                                      callee::ArgExprs(&args[..]),
-                                      dest)
+            Callee::method_call(bcx, method_call)
+                .call(bcx, expr.debug_loc(), ArgExprs(&args), Some(dest)).bcx
         }
-        hir::ExprBinary(op, ref lhs, ref rhs) => {
+        hir::ExprBinary(op, ref lhs, ref rhs_expr) => {
             // if not overloaded, would be RvalueDatumExpr
             let lhs = unpack_datum!(bcx, trans(bcx, &lhs));
-            let rhs_datum = unpack_datum!(bcx, trans(bcx, &rhs));
-            trans_overloaded_op(bcx, expr, MethodCall::expr(expr.id), lhs,
-                                Some((rhs_datum, rhs.id)), Some(dest),
-                                !rustc_front::util::is_by_value_binop(op.node)).bcx
+            let mut rhs = unpack_datum!(bcx, trans(bcx, &rhs_expr));
+            if !rustc_front::util::is_by_value_binop(op.node) {
+                rhs = unpack_datum!(bcx, auto_ref(bcx, rhs, rhs_expr));
+            }
+
+            Callee::method_call(bcx, method_call)
+                .call(bcx, expr.debug_loc(),
+                      ArgOverloadedOp(lhs, Some(rhs)), Some(dest)).bcx
         }
-        hir::ExprUnary(op, ref subexpr) => {
+        hir::ExprUnary(_, ref subexpr) => {
             // if not overloaded, would be RvalueDatumExpr
             let arg = unpack_datum!(bcx, trans(bcx, &subexpr));
-            trans_overloaded_op(bcx, expr, MethodCall::expr(expr.id),
-                                arg, None, Some(dest), !rustc_front::util::is_by_value_unop(op)).bcx
-        }
-        hir::ExprIndex(ref base, ref idx) => {
-            // if not overloaded, would be RvalueDatumExpr
-            let base = unpack_datum!(bcx, trans(bcx, &base));
-            let idx_datum = unpack_datum!(bcx, trans(bcx, &idx));
-            trans_overloaded_op(bcx, expr, MethodCall::expr(expr.id), base,
-                                Some((idx_datum, idx.id)), Some(dest), true).bcx
+
+            Callee::method_call(bcx, method_call)
+                .call(bcx, expr.debug_loc(),
+                      ArgOverloadedOp(arg, None), Some(dest)).bcx
         }
         hir::ExprCast(..) => {
             // Trait casts used to come this way, now they should be coercions.
@@ -1218,26 +1216,22 @@ fn trans_def_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
         Ignore => { return bcx; }
     };
 
+    let ty = expr_ty(bcx, ref_expr);
+    if let ty::TyFnDef(..) = ty.sty {
+        // Zero-sized function or ctor.
+        return bcx;
+    }
+
     match def {
         Def::Variant(tid, vid) => {
             let variant = bcx.tcx().lookup_adt_def(tid).variant_with_id(vid);
-            if let ty::VariantKind::Tuple = variant.kind() {
-                // N-ary variant.
-                let llfn = callee::trans_fn_ref(bcx.ccx(), vid,
-                                                ExprId(ref_expr.id),
-                                                bcx.fcx.param_substs).val;
-                Store(bcx, llfn, lldest);
-                return bcx;
-            } else {
-                // Nullary variant.
-                let ty = expr_ty(bcx, ref_expr);
-                let repr = adt::represent_type(bcx.ccx(), ty);
-                adt::trans_set_discr(bcx, &repr, lldest, Disr::from(variant.disr_val));
-                return bcx;
-            }
+            // Nullary variant.
+            let ty = expr_ty(bcx, ref_expr);
+            let repr = adt::represent_type(bcx.ccx(), ty);
+            adt::trans_set_discr(bcx, &repr, lldest, Disr::from(variant.disr_val));
+            bcx
         }
         Def::Struct(..) => {
-            let ty = expr_ty(bcx, ref_expr);
             match ty.sty {
                 ty::TyStruct(def, _) if def.has_dtor() => {
                     let repr = adt::represent_type(bcx.ccx(), ty);
@@ -1255,41 +1249,6 @@ fn trans_def_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     }
 }
 
-pub fn trans_def_fn_unadjusted<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                                         ref_expr: &hir::Expr,
-                                         def: Def,
-                                         param_substs: &'tcx Substs<'tcx>)
-                                         -> Datum<'tcx, Rvalue> {
-    let _icx = push_ctxt("trans_def_datum_unadjusted");
-
-    match def {
-        Def::Fn(did) |
-        Def::Struct(did) | Def::Variant(_, did) => {
-            callee::trans_fn_ref(ccx, did, ExprId(ref_expr.id), param_substs)
-        }
-        Def::Method(method_did) => {
-            match ccx.tcx().impl_or_trait_item(method_did).container() {
-                ty::ImplContainer(_) => {
-                    callee::trans_fn_ref(ccx, method_did,
-                                         ExprId(ref_expr.id),
-                                         param_substs)
-                }
-                ty::TraitContainer(trait_did) => {
-                    meth::trans_static_method_callee(ccx, method_did,
-                                                     trait_did, ref_expr.id,
-                                                     param_substs)
-                }
-            }
-        }
-        _ => {
-            ccx.tcx().sess.span_bug(ref_expr.span, &format!(
-                    "trans_def_fn_unadjusted invoked on: {:?} for {:?}",
-                    def,
-                    ref_expr));
-        }
-    }
-}
-
 /// Translates a reference to a local variable or argument. This always results in an lvalue datum.
 pub fn trans_local_var<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                    def: Def)
@@ -1898,51 +1857,6 @@ fn trans_binary<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     }
 }
 
-fn trans_overloaded_op<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
-                                   expr: &hir::Expr,
-                                   method_call: MethodCall,
-                                   lhs: Datum<'tcx, Expr>,
-                                   rhs: Option<(Datum<'tcx, Expr>, ast::NodeId)>,
-                                   dest: Option<Dest>,
-                                   autoref: bool)
-                                   -> Result<'blk, 'tcx> {
-    callee::trans_call_inner(bcx,
-                             expr.debug_loc(),
-                             |bcx, arg_cleanup_scope| {
-                                meth::trans_method_callee(bcx,
-                                                          method_call,
-                                                          None,
-                                                          arg_cleanup_scope)
-                             },
-                             callee::ArgOverloadedOp(lhs, rhs, autoref),
-                             dest)
-}
-
-fn trans_overloaded_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
-                                         expr: &hir::Expr,
-                                         callee: &'a hir::Expr,
-                                         args: &'a [P<hir::Expr>],
-                                         dest: Option<Dest>)
-                                         -> Block<'blk, 'tcx> {
-    debug!("trans_overloaded_call {}", expr.id);
-    let method_call = MethodCall::expr(expr.id);
-    let mut all_args = vec!(callee);
-    all_args.extend(args.iter().map(|e| &**e));
-    unpack_result!(bcx,
-                   callee::trans_call_inner(bcx,
-                                            expr.debug_loc(),
-                                            |bcx, arg_cleanup_scope| {
-                                                meth::trans_method_callee(
-                                                    bcx,
-                                                    method_call,
-                                                    None,
-                                                    arg_cleanup_scope)
-                                            },
-                                            callee::ArgOverloadedCall(all_args),
-                                            dest));
-    bcx
-}
-
 pub fn cast_is_noop<'tcx>(tcx: &TyCtxt<'tcx>,
                           expr: &hir::Expr,
                           t_in: Ty<'tcx>,
@@ -2179,18 +2093,12 @@ fn deref_once<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     let mut bcx = bcx;
 
     // Check for overloaded deref.
-    let method_ty = ccx.tcx()
-                       .tables
-                       .borrow()
-                       .method_map
-                       .get(&method_call).map(|method| method.ty);
-
-    let datum = match method_ty {
-        Some(method_ty) => {
-            let method_ty = monomorphize_type(bcx, method_ty);
-
-            // Overloaded. Evaluate `trans_overloaded_op`, which will
-            // invoke the user's deref() method, which basically
+    let method = ccx.tcx().tables.borrow().method_map.get(&method_call).cloned();
+    let datum = match method {
+        Some(method) => {
+            let method_ty = monomorphize_type(bcx, method.ty);
+
+            // Overloaded. Invoke the deref() method, which basically
             // converts from the `Smaht<T>` pointer that we have into
             // a `&T` pointer.  We can then proceed down the normal
             // path (below) to dereference that `&T`.
@@ -2205,9 +2113,10 @@ fn deref_once<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                 ccx.tcx().no_late_bound_regions(&method_ty.fn_ret()).unwrap().unwrap();
             let scratch = rvalue_scratch_datum(bcx, ref_ty, "overloaded_deref");
 
-            unpack_result!(bcx, trans_overloaded_op(bcx, expr, method_call,
-                                                    datum, None, Some(SaveIn(scratch.val)),
-                                                    false));
+            bcx = Callee::method(bcx, method)
+                .call(bcx, expr.debug_loc(),
+                      ArgOverloadedOp(datum, None),
+                      Some(SaveIn(scratch.val))).bcx;
             scratch.to_expr_datum()
         }
         None => {
@@ -2524,18 +2433,13 @@ fn expr_kind(tcx: &TyCtxt, expr: &hir::Expr) -> ExprKind {
     match expr.node {
         hir::ExprPath(..) => {
             match tcx.resolve_expr(expr) {
-                Def::Struct(..) | Def::Variant(..) => {
-                    if let ty::TyBareFn(..) = tcx.node_id_to_type(expr.id).sty {
-                        // ctor function
-                        ExprKind::RvalueDatum
-                    } else {
-                        ExprKind::RvalueDps
-                    }
+                // Put functions and ctors with the ADTs, as they
+                // are zero-sized, so DPS is the cheapest option.
+                Def::Struct(..) | Def::Variant(..) |
+                Def::Fn(..) | Def::Method(..) => {
+                    ExprKind::RvalueDps
                 }
 
-                // Fn pointers are just scalar values.
-                Def::Fn(..) | Def::Method(..) => ExprKind::RvalueDatum,
-
                 // Note: there is actually a good case to be made that
                 // DefArg's, particularly those of immediate type, ought to
                 // considered rvalues.
diff --git a/src/librustc_trans/trans/foreign.rs b/src/librustc_trans/trans/foreign.rs
index 2a2178dd63b..cace98a230f 100644
--- a/src/librustc_trans/trans/foreign.rs
+++ b/src/librustc_trans/trans/foreign.rs
@@ -262,7 +262,8 @@ pub fn trans_native_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
            ccx.tn().val_to_string(llretptr));
 
     let (fn_abi, fn_sig) = match callee_ty.sty {
-        ty::TyBareFn(_, ref fn_ty) => (fn_ty.abi, &fn_ty.sig),
+        ty::TyFnDef(_, _, ref fn_ty) |
+        ty::TyFnPtr(ref fn_ty) => (fn_ty.abi, &fn_ty.sig),
         _ => ccx.sess().bug("trans_native_call called on non-function type")
     };
     let fn_sig = ccx.tcx().erase_late_bound_regions(fn_sig);
@@ -501,7 +502,8 @@ pub fn trans_foreign_mod(ccx: &CrateContext, foreign_mod: &hir::ForeignMod) {
                 abi => {
                     let ty = ccx.tcx().node_id_to_type(foreign_item.id);
                     match ty.sty {
-                        ty::TyBareFn(_, bft) => gate_simd_ffi(ccx.tcx(), &decl, bft),
+                        ty::TyFnDef(_, _, bft) |
+                        ty::TyFnPtr(bft) => gate_simd_ffi(ccx.tcx(), &decl, bft),
                         _ => ccx.tcx().sess.span_bug(foreign_item.span,
                                                      "foreign fn's sty isn't a bare_fn_ty?")
                     }
@@ -552,7 +554,7 @@ pub fn decl_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
     let tys = foreign_types_for_fn_ty(ccx, t);
     let llfn_ty = lltype_for_fn_from_foreign_types(ccx, &tys);
     let cconv = match t.sty {
-        ty::TyBareFn(_, ref fn_ty) => {
+        ty::TyFnDef(_, _, ref fn_ty) | ty::TyFnPtr(ref fn_ty) => {
             llvm_calling_convention(ccx, fn_ty.abi)
         }
         _ => panic!("expected bare fn in decl_rust_fn_with_foreign_abi")
@@ -574,7 +576,7 @@ pub fn register_rust_fn_with_foreign_abi(ccx: &CrateContext,
 
     let t = ccx.tcx().node_id_to_type(node_id);
     let cconv = match t.sty {
-        ty::TyBareFn(_, ref fn_ty) => {
+        ty::TyFnDef(_, _, ref fn_ty) | ty::TyFnPtr(ref fn_ty) => {
             llvm_calling_convention(ccx, fn_ty.abi)
         }
         _ => panic!("expected bare fn in register_rust_fn_with_foreign_abi")
@@ -634,7 +636,7 @@ pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
         // Compute the type that the function would have if it were just a
         // normal Rust function. This will be the type of the wrappee fn.
         match t.sty {
-            ty::TyBareFn(_, ref f) => {
+            ty::TyFnDef(_, _, ref f) | ty::TyFnPtr(ref f)=> {
                 assert!(f.abi != Abi::Rust);
                 assert!(f.abi != Abi::RustIntrinsic);
                 assert!(f.abi != Abi::PlatformIntrinsic);
@@ -957,7 +959,7 @@ fn foreign_signature<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
 fn foreign_types_for_fn_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                                      ty: Ty<'tcx>) -> ForeignTypes<'tcx> {
     let fn_sig = match ty.sty {
-        ty::TyBareFn(_, ref fn_ty) => &fn_ty.sig,
+        ty::TyFnDef(_, _, ref fn_ty) | ty::TyFnPtr(ref fn_ty) => &fn_ty.sig,
         _ => ccx.sess().bug("foreign_types_for_fn_ty called on non-function type")
     };
     let fn_sig = ccx.tcx().erase_late_bound_regions(fn_sig);
diff --git a/src/librustc_trans/trans/glue.rs b/src/librustc_trans/trans/glue.rs
index 0b2ab58a835..d5f8cff4956 100644
--- a/src/librustc_trans/trans/glue.rs
+++ b/src/librustc_trans/trans/glue.rs
@@ -356,27 +356,18 @@ fn trans_struct_drop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
         &unsized_args
     };
 
-    bcx = callee::trans_call_inner(bcx, DebugLoc::None, |bcx, _| {
-        let trait_ref = ty::Binder(ty::TraitRef {
-            def_id: tcx.lang_items.drop_trait().unwrap(),
-            substs: tcx.mk_substs(Substs::trans_empty().with_self_ty(t))
-        });
-        let vtbl = match fulfill_obligation(bcx.ccx(), DUMMY_SP, trait_ref) {
-            traits::VtableImpl(data) => data,
-            _ => tcx.sess.bug(&format!("dtor for {:?} is not an impl???", t))
-        };
-        let dtor_did = def.destructor().unwrap();
-        let datum = callee::trans_fn_ref_with_substs(bcx.ccx(),
-                                                     dtor_did,
-                                                     ExprId(0),
-                                                     bcx.fcx.param_substs,
-                                                     vtbl.substs);
-        callee::Callee {
-            bcx: bcx,
-            data: callee::Fn(datum.val),
-            ty: datum.ty
-        }
-    }, callee::ArgVals(args), Some(expr::Ignore)).bcx;
+    let trait_ref = ty::Binder(ty::TraitRef {
+        def_id: tcx.lang_items.drop_trait().unwrap(),
+        substs: tcx.mk_substs(Substs::trans_empty().with_self_ty(t))
+    });
+    let vtbl = match fulfill_obligation(bcx.ccx(), DUMMY_SP, trait_ref) {
+        traits::VtableImpl(data) => data,
+        _ => tcx.sess.bug(&format!("dtor for {:?} is not an impl???", t))
+    };
+    let dtor_did = def.destructor().unwrap();
+    bcx = callee::Callee::ptr(callee::trans_fn_ref_with_substs(
+            bcx.ccx(), dtor_did, None, vtbl.substs))
+        .call(bcx, DebugLoc::None, callee::ArgVals(args), Some(expr::Ignore)).bcx;
 
     bcx.fcx.pop_and_trans_custom_cleanup_scope(bcx, contents_scope)
 }
diff --git a/src/librustc_trans/trans/intrinsic.rs b/src/librustc_trans/trans/intrinsic.rs
index b7b520f6c82..96c1d6a4d69 100644
--- a/src/librustc_trans/trans/intrinsic.rs
+++ b/src/librustc_trans/trans/intrinsic.rs
@@ -22,7 +22,7 @@ use trans::adt;
 use trans::attributes;
 use trans::base::*;
 use trans::build::*;
-use trans::callee;
+use trans::callee::{self, Callee};
 use trans::cleanup;
 use trans::cleanup::CleanupMethods;
 use trans::common::*;
@@ -45,6 +45,7 @@ use syntax::ast;
 use syntax::ptr::P;
 use syntax::parse::token;
 
+use rustc::lint;
 use rustc::session::Session;
 use syntax::codemap::Span;
 
@@ -125,29 +126,41 @@ pub fn check_intrinsics(ccx: &CrateContext) {
                                                transmute_restriction.substituted_to);
         let from_type_size = machine::llbitsize_of_real(ccx, llfromtype);
         let to_type_size = machine::llbitsize_of_real(ccx, lltotype);
+
+        if let ty::TyFnDef(..) = transmute_restriction.substituted_from.sty {
+            if to_type_size == machine::llbitsize_of_real(ccx, ccx.int_type()) {
+                // FIXME #19925 Remove this warning after a release cycle.
+                lint::raw_emit_lint(&ccx.tcx().sess,
+                                    &ccx.tcx().sess.lint_store.borrow(),
+                                    lint::builtin::TRANSMUTE_FROM_FN_ITEM_TYPES,
+                                    (lint::Warn, lint::LintSource::Default),
+                                    Some(transmute_restriction.span),
+                                    &format!("`{}` is now zero-sized and has to be cast \
+                                              to a pointer before transmuting to `{}`",
+                                             transmute_restriction.substituted_from,
+                                             transmute_restriction.substituted_to));
+                continue;
+            }
+        }
         if from_type_size != to_type_size {
             last_failing_id = Some(transmute_restriction.id);
 
             if transmute_restriction.original_from != transmute_restriction.substituted_from {
                 span_transmute_size_error(ccx.sess(), transmute_restriction.span,
                     &format!("transmute called with differently sized types: \
-                              {} (could be {} bit{}) to {} (could be {} bit{})",
+                              {} (could be {} bits) to {} (could be {} bits)",
                              transmute_restriction.original_from,
-                             from_type_size as usize,
-                             if from_type_size == 1 {""} else {"s"},
+                             from_type_size,
                              transmute_restriction.original_to,
-                             to_type_size as usize,
-                             if to_type_size == 1 {""} else {"s"}));
+                             to_type_size));
             } else {
                 span_transmute_size_error(ccx.sess(), transmute_restriction.span,
                     &format!("transmute called with differently sized types: \
-                              {} ({} bit{}) to {} ({} bit{})",
+                              {} ({} bits) to {} ({} bits)",
                              transmute_restriction.original_from,
-                             from_type_size as usize,
-                             if from_type_size == 1 {""} else {"s"},
+                             from_type_size,
                              transmute_restriction.original_to,
-                             to_type_size as usize,
-                             if to_type_size == 1 {""} else {"s"}));
+                             to_type_size));
             }
         }
     }
@@ -163,7 +176,7 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
                                             cleanup_scope: cleanup::CustomScopeIndex,
                                             args: callee::CallArgs<'a, 'tcx>,
                                             dest: expr::Dest,
-                                            substs: subst::Substs<'tcx>,
+                                            substs: &'tcx subst::Substs<'tcx>,
                                             call_info: NodeIdAndSpan)
                                             -> Result<'blk, 'tcx> {
     let fcx = bcx.fcx;
@@ -179,6 +192,8 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
     let foreign_item = tcx.map.expect_foreign_item(node);
     let name = foreign_item.name.as_str();
 
+    let call_debug_location = DebugLoc::At(call_info.id, call_info.span);
+
     // For `transmute` we can just trans the input expr directly into dest
     if name == "transmute" {
         let llret_ty = type_of::type_of(ccx, ret_ty.unwrap());
@@ -194,6 +209,27 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
                 let in_type_size = machine::llbitsize_of_real(ccx, llintype);
                 let out_type_size = machine::llbitsize_of_real(ccx, llouttype);
 
+                if let ty::TyFnDef(def_id, substs, _) = in_type.sty {
+                    if out_type_size != 0 {
+                        // FIXME #19925 Remove this hack after a release cycle.
+                        let _ = unpack_datum!(bcx, expr::trans(bcx, &arg_exprs[0]));
+                        let llfn = Callee::def(ccx, def_id, substs, in_type).reify(ccx).val;
+                        let llfnty = val_ty(llfn);
+                        let llresult = match dest {
+                            expr::SaveIn(d) => d,
+                            expr::Ignore => alloc_ty(bcx, out_type, "ret")
+                        };
+                        Store(bcx, llfn, PointerCast(bcx, llresult, llfnty.ptr_to()));
+                        if dest == expr::Ignore {
+                            bcx = glue::drop_ty(bcx, llresult, out_type,
+                                                call_debug_location);
+                        }
+                        fcx.scopes.borrow_mut().last_mut().unwrap().drop_non_lifetime_clean();
+                        fcx.pop_and_trans_custom_cleanup_scope(bcx, cleanup_scope);
+                        return Result::new(bcx, llresult);
+                    }
+                }
+
                 // This should be caught by the intrinsicck pass
                 assert_eq!(in_type_size, out_type_size);
 
@@ -311,8 +347,6 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
         }
     }
 
-    let call_debug_location = DebugLoc::At(call_info.id, call_info.span);
-
     // For `try` we need some custom control flow
     if &name[..] == "try" {
         if let callee::ArgExprs(ref exprs) = args {
@@ -364,7 +398,6 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
                              callee_ty,
                              &mut llargs,
                              cleanup::CustomScope(cleanup_scope),
-                             false,
                              Abi::RustIntrinsic);
 
     fcx.scopes.borrow_mut().last_mut().unwrap().drop_non_lifetime_clean();
@@ -1264,7 +1297,7 @@ fn get_rust_try_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>,
     // Define the type up front for the signature of the rust_try function.
     let tcx = ccx.tcx();
     let i8p = tcx.mk_mut_ptr(tcx.types.i8);
-    let fn_ty = tcx.mk_bare_fn(ty::BareFnTy {
+    let fn_ty = tcx.mk_fn_ptr(ty::BareFnTy {
         unsafety: hir::Unsafety::Unsafe,
         abi: Abi::Rust,
         sig: ty::Binder(ty::FnSig {
@@ -1273,9 +1306,8 @@ fn get_rust_try_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>,
             variadic: false,
         }),
     });
-    let fn_ty = tcx.mk_fn(None, fn_ty);
     let output = ty::FnOutput::FnConverging(tcx.types.i32);
-    let try_fn_ty  = tcx.mk_bare_fn(ty::BareFnTy {
+    let try_fn_ty  = ty::BareFnTy {
         unsafety: hir::Unsafety::Unsafe,
         abi: Abi::Rust,
         sig: ty::Binder(ty::FnSig {
@@ -1283,8 +1315,8 @@ fn get_rust_try_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>,
             output: output,
             variadic: false,
         }),
-    });
-    let rust_try = gen_fn(fcx, "__rust_try", tcx.mk_fn(None, try_fn_ty), output,
+    };
+    let rust_try = gen_fn(fcx, "__rust_try", tcx.mk_fn_ptr(try_fn_ty), output,
                           trans);
     *ccx.rust_try_fn().borrow_mut() = Some(rust_try);
     return rust_try
@@ -1353,7 +1385,7 @@ fn generate_filter_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>,
         // going on here, all I can say is that there's a few tests cases in
         // LLVM's test suite which follow this pattern of instructions, so we
         // just do the same.
-        let filter_fn_ty = tcx.mk_bare_fn(ty::BareFnTy {
+        let filter_fn_ty = tcx.mk_fn_ptr(ty::BareFnTy {
             unsafety: hir::Unsafety::Unsafe,
             abi: Abi::Rust,
             sig: ty::Binder(ty::FnSig {
@@ -1362,7 +1394,6 @@ fn generate_filter_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>,
                 variadic: false,
             }),
         });
-        let filter_fn_ty = tcx.mk_fn(None, filter_fn_ty);
         gen_fn(fcx, "__rustc_try_filter", filter_fn_ty, output, &mut |bcx| {
             let ebp = Call(bcx, frameaddress, &[C_i32(ccx, 1)], None, dloc);
             let exn = InBoundsGEP(bcx, ebp, &[C_i32(ccx, -20)]);
@@ -1373,7 +1404,7 @@ fn generate_filter_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>,
         // Conveniently on x86_64 the EXCEPTION_POINTERS handle and base pointer
         // are passed in as arguments to the filter function, so we just pass
         // those along.
-        let filter_fn_ty = tcx.mk_bare_fn(ty::BareFnTy {
+        let filter_fn_ty = tcx.mk_fn_ptr(ty::BareFnTy {
             unsafety: hir::Unsafety::Unsafe,
             abi: Abi::Rust,
             sig: ty::Binder(ty::FnSig {
@@ -1382,7 +1413,6 @@ fn generate_filter_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>,
                 variadic: false,
             }),
         });
-        let filter_fn_ty = tcx.mk_fn(None, filter_fn_ty);
         gen_fn(fcx, "__rustc_try_filter", filter_fn_ty, output, &mut |bcx| {
             let exn = llvm::get_param(bcx.fcx.llfn, 0);
             let rbp = llvm::get_param(bcx.fcx.llfn, 1);
@@ -1400,7 +1430,7 @@ fn span_invalid_monomorphization_error(a: &Session, b: Span, c: &str) {
 fn generic_simd_intrinsic<'blk, 'tcx, 'a>
     (bcx: Block<'blk, 'tcx>,
      name: &str,
-     substs: subst::Substs<'tcx>,
+     substs: &'tcx subst::Substs<'tcx>,
      callee_ty: Ty<'tcx>,
      args: Option<&[P<hir::Expr>]>,
      llargs: &[ValueRef],
@@ -1508,11 +1538,7 @@ fn generic_simd_intrinsic<'blk, 'tcx, 'a>
             None => bcx.sess().span_bug(call_info.span,
                                         "intrinsic call with unexpected argument shape"),
         };
-        let vector = match consts::const_expr(
-            bcx.ccx(),
-            vector,
-            tcx.mk_substs(substs),
-            None,
+        let vector = match consts::const_expr(bcx.ccx(), vector, substs, None,
             consts::TrueConst::Yes, // this should probably help simd error reporting
         ) {
             Ok((vector, _)) => vector,
diff --git a/src/librustc_trans/trans/meth.rs b/src/librustc_trans/trans/meth.rs
index 221d17e6641..78b86dafa18 100644
--- a/src/librustc_trans/trans/meth.rs
+++ b/src/librustc_trans/trans/meth.rs
@@ -18,9 +18,8 @@ use middle::subst;
 use middle::traits;
 use trans::base::*;
 use trans::build::*;
-use trans::callee::*;
-use trans::callee;
-use trans::cleanup;
+use trans::callee::{Callee, Virtual, ArgVals,
+                    trans_fn_pointer_shim, trans_fn_ref_with_substs};
 use trans::closure;
 use trans::common::*;
 use trans::consts;
@@ -30,11 +29,9 @@ use trans::declare;
 use trans::expr;
 use trans::glue;
 use trans::machine;
-use trans::monomorphize;
 use trans::type_::Type;
 use trans::type_of::*;
 use middle::ty::{self, Ty, TyCtxt};
-use middle::ty::MethodCall;
 
 use syntax::ast;
 use syntax::attr;
@@ -92,264 +89,99 @@ pub fn trans_impl(ccx: &CrateContext,
     }
 }
 
-pub fn trans_method_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
-                                       method_call: MethodCall,
-                                       self_expr: Option<&hir::Expr>,
-                                       arg_cleanup_scope: cleanup::ScopeId)
-                                       -> Callee<'blk, 'tcx> {
-    let _icx = push_ctxt("meth::trans_method_callee");
-
-    let method = bcx.tcx().tables.borrow().method_map[&method_call];
-
-    match bcx.tcx().impl_or_trait_item(method.def_id).container() {
-        ty::ImplContainer(_) => {
-            debug!("trans_method_callee: static, {:?}", method.def_id);
-            let datum = callee::trans_fn_ref(bcx.ccx(),
-                                             method.def_id,
-                                             MethodCallKey(method_call),
-                                             bcx.fcx.param_substs);
-            Callee {
-                bcx: bcx,
-                data: Fn(datum.val),
-                ty: datum.ty
-            }
-        }
-
-        ty::TraitContainer(trait_def_id) => {
-            let trait_ref = method.substs.to_trait_ref(bcx.tcx(), trait_def_id);
-            let trait_ref = ty::Binder(bcx.monomorphize(&trait_ref));
-            let span = bcx.tcx().map.span(method_call.expr_id);
-            debug!("method_call={:?} trait_ref={:?} trait_ref id={:?} substs={:?}",
-                   method_call,
-                   trait_ref,
-                   trait_ref.0.def_id,
-                   trait_ref.0.substs);
-            let origin = fulfill_obligation(bcx.ccx(), span, trait_ref);
-            debug!("origin = {:?}", origin);
-            trans_monomorphized_callee(bcx,
-                                       method_call,
-                                       self_expr,
-                                       trait_def_id,
-                                       method.def_id,
-                                       method.ty,
-                                       origin,
-                                       arg_cleanup_scope)
-        }
-    }
-}
-
-pub fn trans_static_method_callee<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                                            method_id: DefId,
-                                            trait_id: DefId,
-                                            expr_id: ast::NodeId,
-                                            param_substs: &'tcx subst::Substs<'tcx>)
-                                            -> Datum<'tcx, Rvalue>
-{
-    let _icx = push_ctxt("meth::trans_static_method_callee");
-    let tcx = ccx.tcx();
-
-    debug!("trans_static_method_callee(method_id={:?}, trait_id={}, \
-            expr_id={})",
-           method_id,
-           tcx.item_path_str(trait_id),
-           expr_id);
-
-    let mname = tcx.item_name(method_id);
-
-    debug!("trans_static_method_callee: method_id={:?}, expr_id={}, \
-            name={}", method_id, expr_id, mname);
-
-    // Find the substitutions for the fn itself. This includes
-    // type parameters that belong to the trait but also some that
-    // belong to the method:
-    let rcvr_substs = node_id_substs(ccx, ExprId(expr_id), param_substs);
-    debug!("rcvr_substs={:?}", rcvr_substs);
-    let trait_ref = ty::Binder(rcvr_substs.to_trait_ref(tcx, trait_id));
-    let vtbl = fulfill_obligation(ccx, DUMMY_SP, trait_ref);
-
-    // Now that we know which impl is being used, we can dispatch to
-    // the actual function:
-    match vtbl {
-        traits::VtableImpl(traits::VtableImplData {
-            impl_def_id: impl_did,
-            substs: impl_substs,
-            nested: _ }) =>
-        {
-            let callee_substs = impl_substs.with_method_from(&rcvr_substs);
-            let mth = tcx.get_impl_method(impl_did, callee_substs, mname);
-            trans_fn_ref_with_substs(ccx, mth.method.def_id, ExprId(expr_id),
-                                     param_substs,
-                                     mth.substs)
-        }
-        traits::VtableObject(ref data) => {
-            let idx = traits::get_vtable_index_of_object_method(tcx, data, method_id);
-            trans_object_shim(ccx,
-                              data.upcast_trait_ref.clone(),
-                              method_id,
-                              idx)
-        }
-        _ => {
-            // FIXME(#20847): handle at least VtableFnPointer
-            tcx.sess.bug(&format!("static call to invalid vtable: {:?}",
-                                 vtbl));
-        }
-    }
-}
-
-fn trans_monomorphized_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
-                                          method_call: MethodCall,
-                                          self_expr: Option<&hir::Expr>,
-                                          trait_id: DefId,
-                                          method_id: DefId,
-                                          method_ty: Ty<'tcx>,
-                                          vtable: traits::Vtable<'tcx, ()>,
-                                          arg_cleanup_scope: cleanup::ScopeId)
-                                          -> Callee<'blk, 'tcx> {
-    let _icx = push_ctxt("meth::trans_monomorphized_callee");
+/// Compute the appropriate callee, give na method's ID, trait ID,
+/// substitutions and a Vtable for that trait.
+pub fn callee_for_trait_impl<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
+                                       method_id: DefId,
+                                       substs: &'tcx subst::Substs<'tcx>,
+                                       trait_id: DefId,
+                                       method_ty: Ty<'tcx>,
+                                       vtable: traits::Vtable<'tcx, ()>)
+                                       -> Callee<'tcx> {
+    let _icx = push_ctxt("meth::callee_for_trait_impl");
     match vtable {
         traits::VtableImpl(vtable_impl) => {
-            let ccx = bcx.ccx();
             let impl_did = vtable_impl.impl_def_id;
-            let mname = match ccx.tcx().impl_or_trait_item(method_id) {
-                ty::MethodTraitItem(method) => method.name,
-                _ => {
-                    bcx.tcx().sess.bug("can't monomorphize a non-method trait \
-                                        item")
-                }
-            };
+            let mname = ccx.tcx().item_name(method_id);
             // create a concatenated set of substitutions which includes
             // those from the impl and those from the method:
-            let meth_substs = node_id_substs(ccx,
-                                             MethodCallKey(method_call),
-                                             bcx.fcx.param_substs);
-            let impl_substs = vtable_impl.substs.with_method_from(&meth_substs);
-            let mth = bcx.tcx().get_impl_method(impl_did, impl_substs, mname);
-            // translate the function
-            let datum = trans_fn_ref_with_substs(bcx.ccx(),
-                                                 mth.method.def_id,
-                                                 MethodCallKey(method_call),
-                                                 bcx.fcx.param_substs,
-                                                 mth.substs);
-
-            Callee { bcx: bcx, data: Fn(datum.val), ty: datum.ty }
+            let impl_substs = vtable_impl.substs.with_method_from(&substs);
+            let substs = ccx.tcx().mk_substs(impl_substs);
+            let mth = ccx.tcx().get_impl_method(impl_did, substs, mname);
+
+            // Translate the function, bypassing Callee::def.
+            // That is because default methods have the same ID as the
+            // trait method used to look up the impl method that ended
+            // up here, so calling Callee::def would infinitely recurse.
+            Callee::ptr(trans_fn_ref_with_substs(ccx, mth.method.def_id,
+                                                 Some(method_ty), mth.substs))
         }
         traits::VtableClosure(vtable_closure) => {
             // The substitutions should have no type parameters remaining
             // after passing through fulfill_obligation
-            let trait_closure_kind = bcx.tcx().lang_items.fn_trait_kind(trait_id).unwrap();
-            let llfn = closure::trans_closure_method(bcx.ccx(),
+            let trait_closure_kind = ccx.tcx().lang_items.fn_trait_kind(trait_id).unwrap();
+            let llfn = closure::trans_closure_method(ccx,
                                                      vtable_closure.closure_def_id,
                                                      vtable_closure.substs,
                                                      trait_closure_kind);
-            Callee {
-                bcx: bcx,
-                data: Fn(llfn),
-                ty: monomorphize_type(bcx, method_ty)
-            }
+            let fn_ptr_ty = match method_ty.sty {
+                ty::TyFnDef(_, _, fty) => ccx.tcx().mk_ty(ty::TyFnPtr(fty)),
+                _ => unreachable!("expected fn item type, found {}",
+                                  method_ty)
+            };
+            Callee::ptr(immediate_rvalue(llfn, fn_ptr_ty))
         }
         traits::VtableFnPointer(fn_ty) => {
-            let trait_closure_kind = bcx.tcx().lang_items.fn_trait_kind(trait_id).unwrap();
-            let llfn = trans_fn_pointer_shim(bcx.ccx(), trait_closure_kind, fn_ty);
-            Callee {
-                bcx: bcx,
-                data: Fn(llfn),
-                ty: monomorphize_type(bcx, method_ty)
-            }
+            let trait_closure_kind = ccx.tcx().lang_items.fn_trait_kind(trait_id).unwrap();
+            let llfn = trans_fn_pointer_shim(ccx, trait_closure_kind, fn_ty);
+            let fn_ptr_ty = match method_ty.sty {
+                ty::TyFnDef(_, _, fty) => ccx.tcx().mk_ty(ty::TyFnPtr(fty)),
+                _ => unreachable!("expected fn item type, found {}",
+                                  method_ty)
+            };
+            Callee::ptr(immediate_rvalue(llfn, fn_ptr_ty))
         }
         traits::VtableObject(ref data) => {
-            let idx = traits::get_vtable_index_of_object_method(bcx.tcx(), data, method_id);
-            if let Some(self_expr) = self_expr {
-                if let ty::TyBareFn(_, ref fty) = monomorphize_type(bcx, method_ty).sty {
-                    let ty = bcx.tcx().mk_fn(None, opaque_method_ty(bcx.tcx(), fty));
-                    return trans_trait_callee(bcx, ty, idx, self_expr, arg_cleanup_scope);
-                }
+            Callee {
+                data: Virtual(traits::get_vtable_index_of_object_method(
+                    ccx.tcx(), data, method_id)),
+                ty: method_ty
             }
-            let datum = trans_object_shim(bcx.ccx(),
-                                          data.upcast_trait_ref.clone(),
-                                          method_id,
-                                          idx);
-            Callee { bcx: bcx, data: Fn(datum.val), ty: datum.ty }
         }
         traits::VtableBuiltin(..) |
         traits::VtableDefaultImpl(..) |
         traits::VtableParam(..) => {
-            bcx.sess().bug(
+            ccx.sess().bug(
                 &format!("resolved vtable bad vtable {:?} in trans",
                         vtable));
         }
     }
 }
 
-/// Create a method callee where the method is coming from a trait object (e.g., Box<Trait> type).
-/// In this case, we must pull the fn pointer out of the vtable that is packaged up with the
-/// object. Objects are represented as a pair, so we first evaluate the self expression and then
-/// extract the self data and vtable out of the pair.
-fn trans_trait_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
-                                  opaque_fn_ty: Ty<'tcx>,
-                                  vtable_index: usize,
-                                  self_expr: &hir::Expr,
-                                  arg_cleanup_scope: cleanup::ScopeId)
-                                  -> Callee<'blk, 'tcx> {
-    let _icx = push_ctxt("meth::trans_trait_callee");
-    let mut bcx = bcx;
-
-    // Translate self_datum and take ownership of the value by
-    // converting to an rvalue.
-    let self_datum = unpack_datum!(
-        bcx, expr::trans(bcx, self_expr));
-
-    let llval = if bcx.fcx.type_needs_drop(self_datum.ty) {
-        let self_datum = unpack_datum!(
-            bcx, self_datum.to_rvalue_datum(bcx, "trait_callee"));
-
-        // Convert to by-ref since `trans_trait_callee_from_llval` wants it
-        // that way.
-        let self_datum = unpack_datum!(
-            bcx, self_datum.to_ref_datum(bcx));
-
-        // Arrange cleanup in case something should go wrong before the
-        // actual call occurs.
-        self_datum.add_clean(bcx.fcx, arg_cleanup_scope)
-    } else {
-        // We don't have to do anything about cleanups for &Trait and &mut Trait.
-        assert!(self_datum.kind.is_by_ref());
-        self_datum.val
-    };
-
-    let llself = Load(bcx, expr::get_dataptr(bcx, llval));
-    let llvtable = Load(bcx, expr::get_meta(bcx, llval));
-    trans_trait_callee_from_llval(bcx, opaque_fn_ty, vtable_index, llself, llvtable)
-}
-
-/// Same as `trans_trait_callee()` above, except that it is given a by-ref pointer to the object
-/// pair.
-fn trans_trait_callee_from_llval<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
-                                             opaque_fn_ty: Ty<'tcx>,
-                                             vtable_index: usize,
-                                             llself: ValueRef,
-                                             llvtable: ValueRef)
-                                             -> Callee<'blk, 'tcx> {
-    let _icx = push_ctxt("meth::trans_trait_callee");
+/// Extracts a method from a trait object's vtable, at the
+/// specified index, and casts it to the given type.
+pub fn get_virtual_method<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
+                                      llvtable: ValueRef,
+                                      vtable_index: usize,
+                                      method_ty: Ty<'tcx>)
+                                      -> Datum<'tcx, Rvalue> {
+    let _icx = push_ctxt("meth::get_virtual_method");
     let ccx = bcx.ccx();
 
     // Load the data pointer from the object.
-    debug!("trans_trait_callee_from_llval(callee_ty={}, vtable_index={}, llself={}, llvtable={})",
-           opaque_fn_ty,
+    debug!("get_virtual_method(callee_ty={}, vtable_index={}, llvtable={})",
+           method_ty,
            vtable_index,
-           bcx.val_to_string(llself),
            bcx.val_to_string(llvtable));
 
-    // Replace the self type (&Self or Box<Self>) with an opaque pointer.
     let mptr = Load(bcx, GEPi(bcx, llvtable, &[vtable_index + VTABLE_OFFSET]));
-    let llcallee_ty = type_of_fn_from_ty(ccx, opaque_fn_ty);
 
-    Callee {
-        bcx: bcx,
-        data: TraitItem(MethodData {
-            llfn: PointerCast(bcx, mptr, llcallee_ty.ptr_to()),
-            llself: PointerCast(bcx, llself, Type::i8p(ccx)),
-        }),
-        ty: opaque_fn_ty
+    // Replace the self type (&Self or Box<Self>) with an opaque pointer.
+    if let ty::TyFnDef(_, _, fty) = method_ty.sty {
+        let opaque_ty = opaque_method_ty(ccx.tcx(), fty);
+        immediate_rvalue(PointerCast(bcx, mptr, type_of(ccx, opaque_ty)), opaque_ty)
+    } else {
+        immediate_rvalue(mptr, method_ty)
     }
 }
 
@@ -374,46 +206,29 @@ fn trans_trait_callee_from_llval<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
 ///
 /// In fact, all virtual calls can be thought of as normal trait calls
 /// that go through this shim function.
-pub fn trans_object_shim<'a, 'tcx>(
-    ccx: &'a CrateContext<'a, 'tcx>,
-    upcast_trait_ref: ty::PolyTraitRef<'tcx>,
-    method_id: DefId,
-    vtable_index: usize)
-    -> Datum<'tcx, Rvalue>
-{
+pub fn trans_object_shim<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>,
+                                   method_ty: Ty<'tcx>,
+                                   vtable_index: usize)
+                                   -> Datum<'tcx, Rvalue> {
     let _icx = push_ctxt("trans_object_shim");
     let tcx = ccx.tcx();
 
-    debug!("trans_object_shim(upcast_trait_ref={:?}, method_id={:?})",
-           upcast_trait_ref,
-           method_id);
+    debug!("trans_object_shim(vtable_index={}, method_ty={:?})",
+           vtable_index,
+           method_ty);
 
-    // Upcast to the trait in question and extract out the substitutions.
-    let upcast_trait_ref = tcx.erase_late_bound_regions(&upcast_trait_ref);
-    let object_substs = upcast_trait_ref.substs.clone().erase_regions();
-    debug!("trans_object_shim: object_substs={:?}", object_substs);
+    let ret_ty = tcx.erase_late_bound_regions(&method_ty.fn_ret());
+    let ret_ty = infer::normalize_associated_type(tcx, &ret_ty);
 
-    // Lookup the type of this method as declared in the trait and apply substitutions.
-    let method_ty = match tcx.impl_or_trait_item(method_id) {
-        ty::MethodTraitItem(method) => method,
-        _ => {
-            tcx.sess.bug("can't create a method shim for a non-method item")
-        }
+    let shim_fn_ty = match method_ty.sty {
+        ty::TyFnDef(_, _, fty) => tcx.mk_ty(ty::TyFnPtr(fty)),
+        _ => unreachable!("expected fn item type, found {}", method_ty)
     };
-    let fty = monomorphize::apply_param_substs(tcx, &object_substs, &method_ty.fty);
-    let fty = tcx.mk_bare_fn(fty);
-    let method_ty = opaque_method_ty(tcx, fty);
-    debug!("trans_object_shim: fty={:?} method_ty={:?}", fty, method_ty);
 
     //
-    let shim_fn_ty = tcx.mk_fn(None, fty);
-    let method_bare_fn_ty = tcx.mk_fn(None, method_ty);
     let function_name = link::mangle_internal_name_by_type_and_seq(ccx, shim_fn_ty, "object_shim");
     let llfn = declare::define_internal_rust_fn(ccx, &function_name, shim_fn_ty);
 
-    let sig = ccx.tcx().erase_late_bound_regions(&fty.sig);
-    let sig = infer::normalize_associated_type(ccx.tcx(), &sig);
-
     let empty_substs = tcx.mk_substs(Substs::trans_empty());
     let (block_arena, fcx): (TypedArena<_>, FunctionContext);
     block_arena = TypedArena::new();
@@ -421,11 +236,11 @@ pub fn trans_object_shim<'a, 'tcx>(
                       llfn,
                       ast::DUMMY_NODE_ID,
                       false,
-                      sig.output,
+                      ret_ty,
                       empty_substs,
                       None,
                       &block_arena);
-    let mut bcx = init_function(&fcx, false, sig.output);
+    let mut bcx = init_function(&fcx, false, ret_ty);
 
     let llargs = get_params(fcx.llfn);
 
@@ -440,21 +255,18 @@ pub fn trans_object_shim<'a, 'tcx>(
 
     let dest =
         fcx.llretslotptr.get().map(
-            |_| expr::SaveIn(fcx.get_ret_slot(bcx, sig.output, "ret_slot")));
+            |_| expr::SaveIn(fcx.get_ret_slot(bcx, ret_ty, "ret_slot")));
 
     debug!("trans_object_shim: method_offset_in_vtable={}",
            vtable_index);
 
-    bcx = trans_call_inner(bcx,
-                           DebugLoc::None,
-                           |bcx, _| trans_trait_callee_from_llval(bcx,
-                                                                  method_bare_fn_ty,
-                                                                  vtable_index,
-                                                                  llself, llvtable),
-                           ArgVals(&llargs[(self_idx + 2)..]),
-                           dest).bcx;
+    let callee = Callee {
+        data: Virtual(vtable_index),
+        ty: method_ty
+    };
+    bcx = callee.call(bcx, DebugLoc::None, ArgVals(&llargs[self_idx..]), dest).bcx;
 
-    finish_fn(&fcx, bcx, sig.output, DebugLoc::None);
+    finish_fn(&fcx, bcx, ret_ty, DebugLoc::None);
 
     immediate_rvalue(llfn, shim_fn_ty)
 }
@@ -466,8 +278,7 @@ pub fn trans_object_shim<'a, 'tcx>(
 /// making an object `Foo<Trait>` from a value of type `Foo<T>`, then
 /// `trait_ref` would map `T:Trait`.
 pub fn get_vtable<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                            trait_ref: ty::PolyTraitRef<'tcx>,
-                            param_substs: &'tcx subst::Substs<'tcx>)
+                            trait_ref: ty::PolyTraitRef<'tcx>)
                             -> ValueRef
 {
     let tcx = ccx.tcx();
@@ -503,8 +314,7 @@ pub fn get_vtable<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                             Some(mth) => {
                                 trans_fn_ref_with_substs(ccx,
                                                          mth.method.def_id,
-                                                         ExprId(0),
-                                                         param_substs,
+                                                         None,
                                                          mth.substs).val
                             }
                             None => nullptr
@@ -567,7 +377,7 @@ pub fn get_vtable<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
 
 pub fn get_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                                     impl_id: DefId,
-                                    substs: subst::Substs<'tcx>)
+                                    substs: &'tcx subst::Substs<'tcx>)
                                     -> Vec<Option<ty::util::ImplMethod<'tcx>>>
 {
     let tcx = ccx.tcx();
@@ -618,7 +428,7 @@ pub fn get_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
 
             // The substitutions we have are on the impl, so we grab
             // the method type from the impl to substitute into.
-            let mth = tcx.get_impl_method(impl_id, substs.clone(), name);
+            let mth = tcx.get_impl_method(impl_id, substs, name);
 
             debug!("get_vtable_methods: mth={:?}", mth);
 
@@ -628,7 +438,7 @@ pub fn get_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
             // method could then never be called, so we do not want to
             // try and trans it, in that case. Issue #23435.
             if mth.is_provided {
-                let predicates = mth.method.predicates.predicates.subst(tcx, &mth.substs);
+                let predicates = mth.method.predicates.predicates.subst(tcx, mth.substs);
                 if !normalize_and_test_predicates(ccx, predicates.into_vec()) {
                     debug!("get_vtable_methods: predicates do not hold");
                     return None;
@@ -642,11 +452,11 @@ pub fn get_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
 
 /// Replace the self type (&Self or Box<Self>) with an opaque pointer.
 fn opaque_method_ty<'tcx>(tcx: &TyCtxt<'tcx>, method_ty: &ty::BareFnTy<'tcx>)
-                          -> &'tcx ty::BareFnTy<'tcx> {
+                          -> Ty<'tcx> {
     let mut inputs = method_ty.sig.0.inputs.clone();
     inputs[0] = tcx.mk_mut_ptr(tcx.mk_mach_int(ast::IntTy::I8));
 
-    tcx.mk_bare_fn(ty::BareFnTy {
+    tcx.mk_fn_ptr(ty::BareFnTy {
         unsafety: method_ty.unsafety,
         abi: method_ty.abi,
         sig: ty::Binder(ty::FnSig {
diff --git a/src/librustc_trans/trans/mir/block.rs b/src/librustc_trans/trans/mir/block.rs
index a9fee18ded8..50283c0959c 100644
--- a/src/librustc_trans/trans/mir/block.rs
+++ b/src/librustc_trans/trans/mir/block.rs
@@ -9,72 +9,27 @@
 // except according to those terms.
 
 use llvm::{BasicBlockRef, ValueRef, OperandBundleDef};
-use rustc::middle::ty::{self, Ty};
+use rustc::middle::ty;
 use rustc::mir::repr as mir;
 use syntax::abi::Abi;
 use trans::adt;
 use trans::attributes;
 use trans::base;
 use trans::build;
+use trans::callee::{Callee, Fn, Virtual};
 use trans::common::{self, Block, BlockAndBuilder};
 use trans::debuginfo::DebugLoc;
 use trans::Disr;
 use trans::foreign;
+use trans::meth;
 use trans::type_of;
 use trans::glue;
 use trans::type_::Type;
 
 use super::{MirContext, drop};
 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);
 
@@ -197,9 +152,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
             }
 
             mir::Terminator::Call { ref func, ref args, ref destination, ref cleanup } => {
-                // Create the callee. This will always be a fn ptr and hence a kind of scalar.
+                // Create the callee. This is a fn ptr or zero-sized and hence a kind of scalar.
                 let callee = self.trans_operand(&bcx, func);
-                let attrs = attributes::from_fn_type(bcx.ccx(), callee.ty);
                 let debugloc = DebugLoc::None;
                 // The arguments we'll be passing. Plus one to account for outptr, if used.
                 let mut llargs = Vec::with_capacity(args.len() + 1);
@@ -207,9 +161,23 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                 // filled when `is_foreign` is `true` and foreign calls are minority of the cases.
                 let mut arg_tys = Vec::new();
 
+                let (callee, fty) = match callee.ty.sty {
+                    ty::TyFnDef(def_id, substs, f) => {
+                        (Callee::def(bcx.ccx(), def_id, substs, callee.ty), f)
+                    }
+                    ty::TyFnPtr(f) => {
+                        (Callee {
+                            data: Fn(callee.immediate()),
+                            ty: callee.ty
+                        }, f)
+                    }
+                    _ => unreachable!("{} is not callable", callee.ty)
+                };
+
+                // We do not translate intrinsics here (they shouldn’t be functions)
+                assert!(fty.abi != Abi::RustIntrinsic && fty.abi != Abi::PlatformIntrinsic);
                 // Foreign-ABI functions are translated differently
-                let abi_style = self.abi_style(callee.ty);
-                let is_foreign = abi_style == AbiStyle::Foreign;
+                let is_foreign = fty.abi != Abi::Rust && fty.abi != Abi::RustCall;
 
                 // Prepare the return value destination
                 let (ret_dest_ty, must_copy_dest) = if let Some((ref d, _)) = *destination {
@@ -225,19 +193,58 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                     (None, false)
                 };
 
-                // Process the rest of the args.
-                for operand in self.arg_operands(&bcx, abi_style, args) {
-                    match operand.val {
-                        Ref(llval) | Immediate(llval) => llargs.push(llval),
-                        FatPtr(b, e) => {
-                            llargs.push(b);
-                            llargs.push(e);
+                // Split the rust-call tupled arguments off.
+                let (args, rest) = if fty.abi == Abi::RustCall && !args.is_empty() {
+                    let (tup, args) = args.split_last().unwrap();
+                    // we can reorder safely because of MIR
+                    (args, self.trans_operand_untupled(&bcx, tup))
+                } else {
+                    (&args[..], vec![])
+                };
+
+                let datum = {
+                    let mut arg_ops = args.iter().map(|arg| {
+                        self.trans_operand(&bcx, arg)
+                    }).chain(rest.into_iter());
+
+                    // Get the actual pointer we can call.
+                    // This can involve vtable accesses or reification.
+                    let datum = if let Virtual(idx) = callee.data {
+                        assert!(!is_foreign);
+
+                        // Grab the first argument which is a trait object.
+                        let vtable = match arg_ops.next().unwrap().val {
+                            FatPtr(data, vtable) => {
+                                llargs.push(data);
+                                vtable
+                            }
+                            _ => unreachable!("expected FatPtr for Virtual call")
+                        };
+
+                        bcx.with_block(|bcx| {
+                            meth::get_virtual_method(bcx, vtable, idx, callee.ty)
+                        })
+                    } else {
+                        callee.reify(bcx.ccx())
+                    };
+
+                    // Process the rest of the args.
+                    for operand in arg_ops {
+                        match operand.val {
+                            Ref(llval) | Immediate(llval) => llargs.push(llval),
+                            FatPtr(b, e) => {
+                                llargs.push(b);
+                                llargs.push(e);
+                            }
+                        }
+                        if is_foreign {
+                            arg_tys.push(operand.ty);
                         }
                     }
-                    if is_foreign {
-                        arg_tys.push(operand.ty);
-                    }
-                }
+
+                    datum
+                };
+                let attrs = attributes::from_fn_type(bcx.ccx(), datum.ty);
 
                 // Many different ways to call a function handled here
                 match (is_foreign, cleanup, destination) {
@@ -246,7 +253,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                         let cleanup = self.bcx(cleanup);
                         let landingpad = self.make_landing_pad(cleanup);
                         let unreachable_blk = self.unreachable_block();
-                        bcx.invoke(callee.immediate(),
+                        bcx.invoke(datum.val,
                                    &llargs[..],
                                    unreachable_blk.llbb,
                                    landingpad.llbb(),
@@ -259,7 +266,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                     (false, &Some(cleanup), &Some((_, success))) => {
                         let cleanup = self.bcx(cleanup);
                         let landingpad = self.make_landing_pad(cleanup);
-                        let invokeret = bcx.invoke(callee.immediate(),
+                        let invokeret = bcx.invoke(datum.val,
                                                    &llargs[..],
                                                    self.llblock(success),
                                                    landingpad.llbb(),
@@ -282,7 +289,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                         });
                     },
                     (false, _, &None) => {
-                        bcx.call(callee.immediate(),
+                        bcx.call(datum.val,
                                  &llargs[..],
                                  cleanup_bundle.as_ref(),
                                  Some(attrs));
@@ -290,7 +297,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                         bcx.unreachable();
                     }
                     (false, _, &Some((_, target))) => {
-                        let llret = bcx.call(callee.immediate(),
+                        let llret = bcx.call(datum.val,
                                              &llargs[..],
                                              cleanup_bundle.as_ref(),
                                              Some(attrs));
@@ -312,8 +319,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                             .expect("return destination is not set");
                         bcx = bcx.map_block(|bcx| {
                             foreign::trans_native_call(bcx,
-                                                       callee.ty,
-                                                       callee.immediate(),
+                                                       datum.ty,
+                                                       datum.val,
                                                        dest.llval,
                                                        &llargs[..],
                                                        arg_tys,
diff --git a/src/librustc_trans/trans/mir/constant.rs b/src/librustc_trans/trans/mir/constant.rs
index 7f03069385f..a0615a6cf5b 100644
--- a/src/librustc_trans/trans/mir/constant.rs
+++ b/src/librustc_trans/trans/mir/constant.rs
@@ -10,14 +10,14 @@
 
 use back::abi;
 use llvm::ValueRef;
-use middle::subst::Substs;
 use middle::ty::{Ty, TypeFoldable};
-use rustc::middle::const_eval::ConstVal;
+use rustc::middle::const_eval::{self, ConstVal};
 use rustc::mir::repr as mir;
 use trans::common::{self, BlockAndBuilder, C_bool, C_bytes, C_floating_f64, C_integral,
-                    C_str_slice};
+                    C_str_slice, C_nil, C_undef};
 use trans::consts;
 use trans::expr;
+use trans::inline;
 use trans::type_of;
 
 use super::operand::{OperandRef, OperandValue};
@@ -32,7 +32,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                           -> OperandRef<'tcx>
     {
         let ccx = bcx.ccx();
-        let val = self.trans_constval_inner(bcx, cv, ty, bcx.fcx().param_substs);
+        let val = self.trans_constval_inner(bcx, cv, ty);
         let val = if common::type_is_immediate(ccx, ty) {
             OperandValue::Immediate(val)
         } else if common::type_is_fat_ptr(bcx.tcx(), ty) {
@@ -55,8 +55,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
     fn trans_constval_inner(&mut self,
                             bcx: &BlockAndBuilder<'bcx, 'tcx>,
                             cv: &ConstVal,
-                            ty: Ty<'tcx>,
-                            param_substs: &'tcx Substs<'tcx>)
+                            ty: Ty<'tcx>)
                             -> ValueRef
     {
         let ccx = bcx.ccx();
@@ -75,8 +74,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                     expr::trans(bcx, expr).datum.val
                 })
             },
-            ConstVal::Function(did) =>
-                self.trans_fn_ref(bcx, ty, param_substs, did).immediate()
+            ConstVal::Function(_) => C_nil(ccx)
         }
     }
 
@@ -85,13 +83,31 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                           constant: &mir::Constant<'tcx>)
                           -> OperandRef<'tcx>
     {
+        let ty = bcx.monomorphize(&constant.ty);
         match constant.literal {
-            mir::Literal::Item { def_id, kind, substs } => {
+            mir::Literal::Item { def_id, substs } => {
+                // Shortcut for zero-sized types, including function item
+                // types, which would not work with lookup_const_by_id.
+                if common::type_is_zero_size(bcx.ccx(), ty) {
+                    let llty = type_of::type_of(bcx.ccx(), ty);
+                    return OperandRef {
+                        val: OperandValue::Immediate(C_undef(llty)),
+                        ty: ty
+                    };
+                }
+
                 let substs = bcx.tcx().mk_substs(bcx.monomorphize(&substs));
-                self.trans_item_ref(bcx, constant.ty, kind, substs, def_id)
+                let def_id = inline::maybe_instantiate_inline(bcx.ccx(), def_id);
+                let expr = const_eval::lookup_const_by_id(bcx.tcx(), def_id, None, Some(substs))
+                            .expect("def was const, but lookup_const_by_id failed");
+                // FIXME: this is falling back to translating from HIR. This is not easy to fix,
+                // because we would have somehow adapt const_eval to work on MIR rather than HIR.
+                let d = bcx.with_block(|bcx| {
+                    expr::trans(bcx, expr)
+                });
+                OperandRef::from_rvalue_datum(d.datum.to_rvalue_datum(d.bcx, "").datum)
             }
             mir::Literal::Value { ref value } => {
-                let ty = bcx.monomorphize(&constant.ty);
                 self.trans_constval(bcx, value, ty)
             }
         }
diff --git a/src/librustc_trans/trans/mir/did.rs b/src/librustc_trans/trans/mir/did.rs
deleted file mode 100644
index 3741b07d248..00000000000
--- a/src/librustc_trans/trans/mir/did.rs
+++ /dev/null
@@ -1,171 +0,0 @@
-// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-//! Code for translating references to other items (DefIds).
-
-use syntax::codemap::DUMMY_SP;
-use rustc::front::map;
-use rustc::middle::ty::{self, Ty, TyCtxt, TypeFoldable};
-use rustc::middle::subst::Substs;
-use rustc::middle::const_eval;
-use rustc::middle::def_id::DefId;
-use rustc::middle::traits;
-use rustc::mir::repr::ItemKind;
-use trans::common::{BlockAndBuilder, fulfill_obligation};
-use trans::base;
-use trans::closure;
-use trans::expr;
-use trans::monomorphize;
-use trans::meth;
-use trans::inline;
-
-use super::MirContext;
-use super::operand::{OperandRef, OperandValue};
-
-impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
-    /// Translate reference to item.
-    pub fn trans_item_ref(&mut self,
-                          bcx: &BlockAndBuilder<'bcx, 'tcx>,
-                          ty: Ty<'tcx>,
-                          kind: ItemKind,
-                          substs: &'tcx Substs<'tcx>,
-                          did: DefId)
-                          -> OperandRef<'tcx> {
-        debug!("trans_item_ref(ty={:?}, kind={:?}, substs={:?}, did={})",
-            ty, kind, substs, bcx.tcx().item_path_str(did));
-
-        match kind {
-            ItemKind::Function => self.trans_fn_ref(bcx, ty, substs, did),
-            ItemKind::Method => match bcx.tcx().impl_or_trait_item(did).container() {
-                ty::ImplContainer(_) => self.trans_fn_ref(bcx, ty, substs, did),
-                ty::TraitContainer(tdid) => self.trans_trait_method(bcx, ty, did, tdid, substs)
-            },
-            ItemKind::Constant => {
-                let did = inline::maybe_instantiate_inline(bcx.ccx(), did);
-                let expr = const_eval::lookup_const_by_id(bcx.tcx(), did, None, Some(substs))
-                            .expect("def was const, but lookup_const_by_id failed");
-                // FIXME: this is falling back to translating from HIR. This is not easy to fix,
-                // because we would have somehow adapt const_eval to work on MIR rather than HIR.
-                let d = bcx.with_block(|bcx| {
-                    expr::trans(bcx, expr)
-                });
-                OperandRef::from_rvalue_datum(d.datum.to_rvalue_datum(d.bcx, "").datum)
-            }
-        }
-    }
-
-    /// Translates references to a function-like items.
-    ///
-    /// That includes regular functions, non-static methods, struct and enum variant constructors,
-    /// closures and possibly more.
-    ///
-    /// This is an adaptation of callee::trans_fn_ref_with_substs.
-    pub fn trans_fn_ref(&mut self,
-                        bcx: &BlockAndBuilder<'bcx, 'tcx>,
-                        ty: Ty<'tcx>,
-                        substs: &'tcx Substs<'tcx>,
-                        did: DefId)
-                        -> OperandRef<'tcx> {
-        debug!("trans_fn_ref(ty={:?}, substs={:?}, did={})",
-            ty, substs, bcx.tcx().item_path_str(did));
-
-        let did = inline::maybe_instantiate_inline(bcx.ccx(), did);
-
-        if !substs.types.is_empty() || is_named_tuple_constructor(bcx.tcx(), did) {
-            let (val, fn_ty, _) = monomorphize::monomorphic_fn(bcx.ccx(), did, substs, None);
-            // FIXME: cast fnptr to proper type if necessary
-            OperandRef {
-                ty: fn_ty,
-                val: OperandValue::Immediate(val)
-            }
-        } else {
-            let val = if let Some(node_id) = bcx.tcx().map.as_local_node_id(did) {
-                base::get_item_val(bcx.ccx(), node_id)
-            } else {
-                base::trans_external_path(bcx.ccx(), did, ty)
-            };
-            // FIXME: cast fnptr to proper type if necessary
-            OperandRef {
-                ty: ty,
-                val: OperandValue::Immediate(val)
-            }
-        }
-    }
-
-    /// Translates references to trait methods.
-    ///
-    /// This is an adaptation of meth::trans_static_method_callee
-    pub fn trans_trait_method(&mut self,
-                              bcx: &BlockAndBuilder<'bcx, 'tcx>,
-                              ty: Ty<'tcx>,
-                              method_id: DefId,
-                              trait_id: DefId,
-                              substs: &'tcx Substs<'tcx>)
-                              -> OperandRef<'tcx> {
-        debug!("trans_static_method(ty={:?}, method={}, trait={}, substs={:?})",
-                ty,
-                bcx.tcx().item_path_str(method_id),
-                bcx.tcx().item_path_str(trait_id),
-                substs);
-
-        let ccx = bcx.ccx();
-        let tcx = bcx.tcx();
-        let trait_ref = ty::Binder(substs.to_trait_ref(tcx, trait_id));
-        let vtbl = fulfill_obligation(ccx, DUMMY_SP, trait_ref);
-        match vtbl {
-            traits::VtableImpl(traits::VtableImplData {
-                impl_def_id, substs: impl_substs, ..
-            }) => {
-                assert!(!impl_substs.types.needs_infer());
-
-                let mname = tcx.item_name(method_id);
-
-                let callee_substs = impl_substs.with_method_from(substs);
-                let mth = tcx.get_impl_method(impl_def_id, callee_substs, mname);
-                let mth_substs = tcx.mk_substs(mth.substs);
-                self.trans_fn_ref(bcx, ty, mth_substs, mth.method.def_id)
-            },
-            traits::VtableClosure(data) => {
-                let trait_closure_kind = bcx.tcx().lang_items.fn_trait_kind(trait_id).unwrap();
-                let llfn = closure::trans_closure_method(bcx.ccx(),
-                                                         data.closure_def_id,
-                                                         data.substs,
-                                                         trait_closure_kind);
-                OperandRef {
-                    ty: ty,
-                    val: OperandValue::Immediate(llfn)
-                }
-            },
-            traits::VtableObject(ref data) => {
-                let idx = traits::get_vtable_index_of_object_method(tcx, data, method_id);
-                OperandRef::from_rvalue_datum(
-                    meth::trans_object_shim(ccx, data.upcast_trait_ref.clone(), method_id, idx)
-                )
-            }
-            _ => {
-                tcx.sess.bug(&format!("static call to invalid vtable: {:?}", vtbl));
-            }
-        }
-   }
-}
-
-fn is_named_tuple_constructor(tcx: &TyCtxt, def_id: DefId) -> bool {
-    let node_id = match tcx.map.as_local_node_id(def_id) {
-        Some(n) => n,
-        None => { return false; }
-    };
-    match tcx.map.find(node_id).expect("local item should be in ast map") {
-        map::NodeVariant(v) => {
-            v.node.data.is_tuple()
-        }
-        map::NodeStructCtor(_) => true,
-        _ => false
-    }
-}
diff --git a/src/librustc_trans/trans/mir/mod.rs b/src/librustc_trans/trans/mir/mod.rs
index 40dc22e31aa..4ad2e035945 100644
--- a/src/librustc_trans/trans/mir/mod.rs
+++ b/src/librustc_trans/trans/mir/mod.rs
@@ -196,7 +196,6 @@ fn arg_value_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>,
 mod analyze;
 mod block;
 mod constant;
-mod did;
 mod drop;
 mod lvalue;
 mod operand;
diff --git a/src/librustc_trans/trans/mir/rvalue.rs b/src/librustc_trans/trans/mir/rvalue.rs
index 541df43b49b..ce10ed425f6 100644
--- a/src/librustc_trans/trans/mir/rvalue.rs
+++ b/src/librustc_trans/trans/mir/rvalue.rs
@@ -15,6 +15,7 @@ use rustc::mir::repr as mir;
 
 use trans::asm;
 use trans::base;
+use trans::callee::Callee;
 use trans::common::{self, BlockAndBuilder, Result};
 use trans::debuginfo::DebugLoc;
 use trans::declare;
@@ -193,9 +194,20 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                 let cast_ty = bcx.monomorphize(&cast_ty);
 
                 let val = match *kind {
-                    mir::CastKind::ReifyFnPointer |
+                    mir::CastKind::ReifyFnPointer => {
+                        match operand.ty.sty {
+                            ty::TyFnDef(def_id, substs, _) => {
+                                OperandValue::Immediate(
+                                    Callee::def(bcx.ccx(), def_id, substs, operand.ty)
+                                        .reify(bcx.ccx()).val)
+                            }
+                            _ => {
+                                unreachable!("{} cannot be reified to a fn ptr", operand.ty)
+                            }
+                        }
+                    }
                     mir::CastKind::UnsafeFnPointer => {
-                        // these are no-ops at the LLVM level
+                        // this is a no-op at the LLVM level
                         operand.val
                     }
                     mir::CastKind::Unsize => {
diff --git a/src/librustc_trans/trans/monomorphize.rs b/src/librustc_trans/trans/monomorphize.rs
index 9edda3d2b5c..c6119416e47 100644
--- a/src/librustc_trans/trans/monomorphize.rs
+++ b/src/librustc_trans/trans/monomorphize.rs
@@ -37,16 +37,9 @@ use std::hash::{Hasher, Hash, SipHasher};
 
 pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                                 fn_id: DefId,
-                                psubsts: &'tcx subst::Substs<'tcx>,
-                                ref_id: Option<ast::NodeId>)
+                                psubsts: &'tcx subst::Substs<'tcx>)
                                 -> (ValueRef, Ty<'tcx>, bool) {
-    debug!("monomorphic_fn(\
-            fn_id={:?}, \
-            real_substs={:?}, \
-            ref_id={:?})",
-           fn_id,
-           psubsts,
-           ref_id);
+    debug!("monomorphic_fn(fn_id={:?}, real_substs={:?})", fn_id, psubsts);
 
     assert!(!psubsts.types.needs_infer() && !psubsts.types.has_param_types());
 
diff --git a/src/librustc_trans/trans/type_of.rs b/src/librustc_trans/trans/type_of.rs
index 24a7fd372f6..b78bf9bfc3f 100644
--- a/src/librustc_trans/trans/type_of.rs
+++ b/src/librustc_trans/trans/type_of.rs
@@ -150,26 +150,6 @@ pub fn type_of_rust_fn<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
     Type::func(&atys[..], &lloutputtype)
 }
 
-// Given a function type and a count of ty params, construct an llvm type
-pub fn type_of_fn_from_ty<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, fty: Ty<'tcx>) -> Type {
-    match fty.sty {
-        ty::TyBareFn(_, ref f) => {
-            // FIXME(#19925) once fn item types are
-            // zero-sized, we'll need to do something here
-            if f.abi == Abi::Rust || f.abi == Abi::RustCall {
-                let sig = cx.tcx().erase_late_bound_regions(&f.sig);
-                let sig = infer::normalize_associated_type(cx.tcx(), &sig);
-                type_of_rust_fn(cx, None, &sig, f.abi)
-            } else {
-                foreign::lltype_for_foreign_fn(cx, fty)
-            }
-        }
-        _ => {
-            cx.sess().bug("type_of_fn_from_ty given non-closure, non-bare-fn")
-        }
-    }
-}
-
 // A "sizing type" is an LLVM type, the size and alignment of which are
 // guaranteed to be equivalent to what you would get out of `type_of()`. It's
 // useful because:
@@ -210,7 +190,8 @@ pub fn sizing_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Typ
             }
         }
 
-        ty::TyBareFn(..) => Type::i8p(cx),
+        ty::TyFnDef(..) => Type::nil(cx),
+        ty::TyFnPtr(_) => Type::i8p(cx),
 
         ty::TyArray(ty, size) => {
             let llty = sizing_type_of(cx, ty);
@@ -415,8 +396,15 @@ pub fn in_memory_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) ->
       ty::TySlice(ty) => in_memory_type_of(cx, ty),
       ty::TyStr | ty::TyTrait(..) => Type::i8(cx),
 
-      ty::TyBareFn(..) => {
-          type_of_fn_from_ty(cx, t).ptr_to()
+      ty::TyFnDef(..) => Type::nil(cx),
+      ty::TyFnPtr(f) => {
+        if f.abi == Abi::Rust || f.abi == Abi::RustCall {
+            let sig = cx.tcx().erase_late_bound_regions(&f.sig);
+            let sig = infer::normalize_associated_type(cx.tcx(), &sig);
+            type_of_rust_fn(cx, None, &sig, f.abi).ptr_to()
+        } else {
+            foreign::lltype_for_foreign_fn(cx, t).ptr_to()
+        }
       }
       ty::TyTuple(ref tys) if tys.is_empty() => Type::nil(cx),
       ty::TyTuple(..) => {
diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs
index 134da7a3bb0..1938fa75829 100644
--- a/src/librustc_typeck/astconv.rs
+++ b/src/librustc_typeck/astconv.rs
@@ -1636,8 +1636,7 @@ pub fn ast_ty_to_ty<'tcx>(this: &AstConv<'tcx>,
         }
         hir::TyBareFn(ref bf) => {
             require_c_abi_if_variadic(tcx, &bf.decl, bf.abi, ast_ty.span);
-            let bare_fn = ty_of_bare_fn(this, bf.unsafety, bf.abi, &bf.decl);
-            tcx.mk_fn(None, tcx.mk_bare_fn(bare_fn))
+            tcx.mk_fn_ptr(ty_of_bare_fn(this, bf.unsafety, bf.abi, &bf.decl))
         }
         hir::TyPolyTraitRef(ref bounds) => {
             conv_ty_poly_trait_ref(this, rscope, ast_ty.span, bounds)
diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs
index 548f5ee5e94..305970db9e7 100644
--- a/src/librustc_typeck/check/_match.rs
+++ b/src/librustc_typeck/check/_match.rs
@@ -15,9 +15,10 @@ use middle::pat_util::pat_is_resolved_const;
 use middle::subst::Substs;
 use middle::ty::{self, Ty, TypeFoldable, LvaluePreference};
 use check::{check_expr, check_expr_has_type, check_expr_with_expectation};
-use check::{check_expr_coercable_to_type, demand, FnCtxt, Expectation};
+use check::{demand, FnCtxt, Expectation};
 use check::{check_expr_with_lvalue_pref};
 use check::{instantiate_path, resolve_ty_and_def_ufcs, structurally_resolved_type};
+use check::coercion;
 use lint;
 use require_same_types;
 use util::nodemap::FnvHashMap;
@@ -492,54 +493,67 @@ pub fn check_match<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
     // of execution reach it, we will panic, so bottom is an appropriate
     // type in that case)
     let expected = expected.adjust_for_branches(fcx);
-    let result_ty = arms.iter().fold(fcx.infcx().next_diverging_ty_var(), |result_ty, arm| {
-        let bty = match expected {
-            // We don't coerce to `()` so that if the match expression is a
-            // statement it's branches can have any consistent type. That allows
-            // us to give better error messages (pointing to a usually better
-            // arm for inconsistent arms or to the whole match when a `()` type
-            // is required).
-            Expectation::ExpectHasType(ety) if ety != fcx.tcx().mk_nil() => {
-                check_expr_coercable_to_type(fcx, &arm.body, ety);
-                ety
-            }
-            _ => {
-                check_expr_with_expectation(fcx, &arm.body, expected);
-                fcx.node_ty(arm.body.id)
+    let mut result_ty = fcx.infcx().next_diverging_ty_var();
+    let coerce_first = match expected {
+        // We don't coerce to `()` so that if the match expression is a
+        // statement it's branches can have any consistent type. That allows
+        // us to give better error messages (pointing to a usually better
+        // arm for inconsistent arms or to the whole match when a `()` type
+        // is required).
+        Expectation::ExpectHasType(ety) if ety != fcx.tcx().mk_nil() => {
+            ety
+        }
+        _ => result_ty
+    };
+    for (i, arm) in arms.iter().enumerate() {
+        if let Some(ref e) = arm.guard {
+            check_expr_has_type(fcx, e, tcx.types.bool);
+        }
+        check_expr_with_expectation(fcx, &arm.body, expected);
+        let arm_ty = fcx.expr_ty(&arm.body);
+
+        if result_ty.references_error() || arm_ty.references_error() {
+            result_ty = tcx.types.err;
+            continue;
+        }
+
+        // Handle the fallback arm of a desugared if-let like a missing else.
+        let is_if_let_fallback = match match_src {
+            hir::MatchSource::IfLetDesugar { contains_else_clause: false } => {
+                i == arms.len() - 1 && arm_ty.is_nil()
             }
+            _ => false
         };
 
-        if let Some(ref e) = arm.guard {
-            check_expr_has_type(fcx, &e, tcx.types.bool);
-        }
+        let origin = if is_if_let_fallback {
+            TypeOrigin::IfExpressionWithNoElse(expr.span)
+        } else {
+            TypeOrigin::MatchExpressionArm(expr.span, arm.body.span, match_src)
+        };
 
-        if result_ty.references_error() || bty.references_error() {
-            tcx.types.err
+        let result = if is_if_let_fallback {
+            fcx.infcx().eq_types(true, origin, arm_ty, result_ty).map(|_| arm_ty)
+        } else if i == 0 {
+            // Special-case the first arm, as it has no "previous expressions".
+            coercion::try(fcx, &arm.body, coerce_first)
         } else {
-            let (origin, expected, found) = match match_src {
-                /* if-let construct without an else block */
-                hir::MatchSource::IfLetDesugar { contains_else_clause }
-                if !contains_else_clause => (
-                    TypeOrigin::IfExpressionWithNoElse(expr.span),
-                    bty,
-                    result_ty,
-                ),
-                _ => (
-                    TypeOrigin::MatchExpressionArm(expr.span, arm.body.span, match_src),
-                    result_ty,
-                    bty,
-                ),
-            };
+            let prev_arms = || arms[..i].iter().map(|arm| &*arm.body);
+            coercion::try_find_lub(fcx, origin, prev_arms, result_ty, &arm.body)
+        };
 
-            infer::common_supertype(
-                fcx.infcx(),
-                origin,
-                true,
-                expected,
-                found,
-            )
-        }
-    });
+        result_ty = match result {
+            Ok(ty) => ty,
+            Err(e) => {
+                let (expected, found) = if is_if_let_fallback {
+                    (arm_ty, result_ty)
+                } else {
+                    (result_ty, arm_ty)
+                };
+                fcx.infcx().report_mismatched_types(origin, expected, found, e);
+                fcx.tcx().types.err
+            }
+        };
+    }
 
     fcx.write_ty(expr.id, result_ty);
 }
diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs
index 42ea3cc2aaa..bf60f435a22 100644
--- a/src/librustc_typeck/check/callee.rs
+++ b/src/librustc_typeck/check/callee.rs
@@ -82,7 +82,7 @@ pub fn check_call<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
         autoderef(fcx,
                   callee_expr.span,
                   original_callee_ty,
-                  Some(callee_expr),
+                  || Some(callee_expr),
                   UnresolvedTypeAction::Error,
                   LvaluePreference::NoPreference,
                   |adj_ty, idx| {
@@ -130,7 +130,7 @@ fn try_overloaded_call_step<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
 
     // If the callee is a bare function or a closure, then we're all set.
     match structurally_resolved_type(fcx, callee_expr.span, adjusted_ty).sty {
-        ty::TyBareFn(..) => {
+        ty::TyFnDef(..) | ty::TyFnPtr(_) => {
             fcx.write_autoderef_adjustment(callee_expr.id, autoderefs);
             return Some(CallStep::Builtin);
         }
@@ -225,7 +225,8 @@ fn confirm_builtin_call<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
     let error_fn_sig;
 
     let fn_sig = match callee_ty.sty {
-        ty::TyBareFn(_, &ty::BareFnTy {ref sig, ..}) => {
+        ty::TyFnDef(_, _, &ty::BareFnTy {ref sig, ..}) |
+        ty::TyFnPtr(&ty::BareFnTy {ref sig, ..}) => {
             sig
         }
         _ => {
diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs
index 2ea0df280db..b5cd5d7f8e5 100644
--- a/src/librustc_typeck/check/cast.rs
+++ b/src/librustc_typeck/check/cast.rs
@@ -55,7 +55,7 @@ use syntax::ast;
 /// Reifies a cast check to be checked once we have full type information for
 /// a function context.
 pub struct CastCheck<'tcx> {
-    expr: hir::Expr,
+    expr: &'tcx hir::Expr,
     expr_ty: Ty<'tcx>,
     cast_ty: Ty<'tcx>,
     span: Span,
@@ -109,7 +109,7 @@ enum CastError {
 }
 
 impl<'tcx> CastCheck<'tcx> {
-    pub fn new(expr: hir::Expr, expr_ty: Ty<'tcx>, cast_ty: Ty<'tcx>, span: Span)
+    pub fn new(expr: &'tcx hir::Expr, expr_ty: Ty<'tcx>, cast_ty: Ty<'tcx>, span: Span)
                -> CastCheck<'tcx> {
         CastCheck {
             expr: expr,
@@ -235,6 +235,20 @@ impl<'tcx> CastCheck<'tcx> {
         let (t_from, t_cast) = match (CastTy::from_ty(self.expr_ty),
                                       CastTy::from_ty(self.cast_ty)) {
             (Some(t_from), Some(t_cast)) => (t_from, t_cast),
+            // Function item types may need to be reified before casts.
+            (None, Some(t_cast)) => {
+                if let ty::TyFnDef(_, _, f) = self.expr_ty.sty {
+                    // Attempt a coercion to a fn pointer type.
+                    let res = coercion::try(fcx, self.expr,
+                                            fcx.tcx().mk_ty(ty::TyFnPtr(f)));
+                    if !res.is_ok() {
+                        return Err(CastError::NonScalar);
+                    }
+                    (FnPtr, t_cast)
+                } else {
+                    return Err(CastError::NonScalar);
+                }
+            }
             _ => {
                 return Err(CastError::NonScalar)
             }
@@ -376,14 +390,7 @@ impl<'tcx> CastCheck<'tcx> {
     }
 
     fn try_coercion_cast<'a>(&self, fcx: &FnCtxt<'a, 'tcx>) -> bool {
-        if let Ok(()) = coercion::mk_assignty(fcx,
-                                              &self.expr,
-                                              self.expr_ty,
-                                              self.cast_ty) {
-            true
-        } else {
-            false
-        }
+        coercion::try(fcx, self.expr, self.cast_ty).is_ok()
     }
 
 }
diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs
index 5ab3c6f983f..aa359c95e2d 100644
--- a/src/librustc_typeck/check/coercion.rs
+++ b/src/librustc_typeck/check/coercion.rs
@@ -62,7 +62,7 @@
 
 use check::{autoderef, FnCtxt, UnresolvedTypeAction};
 
-use middle::infer::{self, Coercion, TypeOrigin};
+use middle::infer::{Coercion, TypeOrigin, TypeTrace};
 use middle::traits::{self, ObligationCause};
 use middle::traits::{predicate_for_trait_def, report_selection_error};
 use middle::ty::adjustment::{AutoAdjustment, AutoDerefRef, AdjustDerefRef};
@@ -71,7 +71,7 @@ use middle::ty::adjustment::{AdjustUnsafeFnPointer, AdjustMutToConstPointer};
 use middle::ty::{self, LvaluePreference, TypeAndMut, Ty, TyCtxt};
 use middle::ty::fold::TypeFoldable;
 use middle::ty::error::TypeError;
-use middle::ty::relate::RelateResult;
+use middle::ty::relate::{relate_substs, RelateResult, TypeRelation};
 use util::common::indent;
 
 use std::cell::RefCell;
@@ -80,42 +80,75 @@ use rustc_front::hir;
 
 struct Coerce<'a, 'tcx: 'a> {
     fcx: &'a FnCtxt<'a, 'tcx>,
-    origin: infer::TypeOrigin,
+    origin: TypeOrigin,
+    use_lub: bool,
     unsizing_obligations: RefCell<Vec<traits::PredicateObligation<'tcx>>>,
 }
 
-type CoerceResult<'tcx> = RelateResult<'tcx, Option<AutoAdjustment<'tcx>>>;
+type CoerceResult<'tcx> = RelateResult<'tcx, (Ty<'tcx>, AutoAdjustment<'tcx>)>;
+
+fn coerce_mutbls<'tcx>(from_mutbl: hir::Mutability,
+                       to_mutbl: hir::Mutability)
+                       -> RelateResult<'tcx, ()> {
+    match (from_mutbl, to_mutbl) {
+        (hir::MutMutable, hir::MutMutable) |
+        (hir::MutImmutable, hir::MutImmutable) |
+        (hir::MutMutable, hir::MutImmutable) => Ok(()),
+        (hir::MutImmutable, hir::MutMutable) => Err(TypeError::Mutability)
+    }
+}
 
 impl<'f, 'tcx> Coerce<'f, 'tcx> {
+    fn new(fcx: &'f FnCtxt<'f, 'tcx>, origin: TypeOrigin) -> Self {
+        Coerce {
+            fcx: fcx,
+            origin: origin,
+            use_lub: false,
+            unsizing_obligations: RefCell::new(vec![])
+        }
+    }
+
     fn tcx(&self) -> &TyCtxt<'tcx> {
         self.fcx.tcx()
     }
 
-    fn subtype(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
-        try!(self.fcx.infcx().sub_types(false, self.origin.clone(), a, b));
-        Ok(None) // No coercion required.
+    /// Unify two types (using sub or lub) and produce a noop coercion.
+    fn unify(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
+        let infcx = self.fcx.infcx();
+        infcx.commit_if_ok(|_| {
+            let trace = TypeTrace::types(self.origin, false, a, b);
+            if self.use_lub {
+                infcx.lub(false, trace).relate(&a, &b)
+            } else {
+                infcx.sub(false, trace).relate(&a, &b)
+            }
+        }).and_then(|ty| self.identity(ty))
     }
 
-    fn unpack_actual_value<T, F>(&self, a: Ty<'tcx>, f: F) -> T where
-        F: FnOnce(Ty<'tcx>) -> T,
-    {
-        f(self.fcx.infcx().shallow_resolve(a))
+    /// Synthesize an identity adjustment.
+    fn identity(&self, ty: Ty<'tcx>) -> CoerceResult<'tcx> {
+        Ok((ty, AdjustDerefRef(AutoDerefRef {
+            autoderefs: 0,
+            autoref: None,
+            unsize: None
+        })))
     }
 
-    fn coerce(&self,
-              expr_a: &hir::Expr,
-              a: Ty<'tcx>,
-              b: Ty<'tcx>)
-              -> CoerceResult<'tcx> {
-        debug!("Coerce.tys({:?} => {:?})",
-               a,
-               b);
+    fn coerce<'a, E, I>(&self,
+                        exprs: &E,
+                        a: Ty<'tcx>,
+                        b: Ty<'tcx>)
+                        -> CoerceResult<'tcx>
+        // FIXME(eddyb) use copyable iterators when that becomes ergonomic.
+        where E: Fn() -> I,
+              I: IntoIterator<Item=&'a hir::Expr> {
 
         let a = self.fcx.infcx().shallow_resolve(a);
+        debug!("Coerce.tys({:?} => {:?})", a, b);
 
         // Just ignore error types.
         if a.references_error() || b.references_error() {
-            return Ok(None);
+            return self.identity(b);
         }
 
         // Consider coercing the subtype to a DST
@@ -134,27 +167,27 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
             }
 
             ty::TyRef(_, mt_b) => {
-                return self.coerce_borrowed_pointer(expr_a, a, b, mt_b.mutbl);
+                return self.coerce_borrowed_pointer(exprs, a, b, mt_b.mutbl);
             }
 
             _ => {}
         }
 
         match a.sty {
-            ty::TyBareFn(Some(_), a_f) => {
+            ty::TyFnDef(_, _, a_f) => {
                 // Function items are coercible to any closure
                 // type; function pointers are not (that would
                 // require double indirection).
                 self.coerce_from_fn_item(a, a_f, b)
             }
-            ty::TyBareFn(None, a_f) => {
+            ty::TyFnPtr(a_f) => {
                 // We permit coercion of fn pointers to drop the
                 // unsafe qualifier.
                 self.coerce_from_fn_pointer(a, a_f, b)
             }
             _ => {
-                // Otherwise, just use subtyping rules.
-                self.subtype(a, b)
+                // Otherwise, just use unification rules.
+                self.unify(a, b)
             }
         }
     }
@@ -162,15 +195,17 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
     /// Reborrows `&mut A` to `&mut B` and `&(mut) A` to `&B`.
     /// To match `A` with `B`, autoderef will be performed,
     /// calling `deref`/`deref_mut` where necessary.
-    fn coerce_borrowed_pointer(&self,
-                               expr_a: &hir::Expr,
-                               a: Ty<'tcx>,
-                               b: Ty<'tcx>,
-                               mutbl_b: hir::Mutability)
-                               -> CoerceResult<'tcx> {
-        debug!("coerce_borrowed_pointer(a={:?}, b={:?})",
-               a,
-               b);
+    fn coerce_borrowed_pointer<'a, E, I>(&self,
+                                         exprs: &E,
+                                         a: Ty<'tcx>,
+                                         b: Ty<'tcx>,
+                                         mutbl_b: hir::Mutability)
+                                         -> CoerceResult<'tcx>
+        // FIXME(eddyb) use copyable iterators when that becomes ergonomic.
+        where E: Fn() -> I,
+              I: IntoIterator<Item=&'a hir::Expr> {
+
+        debug!("coerce_borrowed_pointer(a={:?}, b={:?})", a, b);
 
         // If we have a parameter of type `&M T_a` and the value
         // provided is `expr`, we will be adding an implicit borrow,
@@ -182,20 +217,18 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
             ty::TyRef(_, mt_a) => {
                 try!(coerce_mutbls(mt_a.mutbl, mutbl_b));
             }
-            _ => return self.subtype(a, b)
+            _ => return self.unify(a, b)
         }
 
-        let coercion = Coercion(self.origin.span());
+        let span = self.origin.span();
+        let coercion = Coercion(span);
         let r_borrow = self.fcx.infcx().next_region_var(coercion);
         let r_borrow = self.tcx().mk_region(r_borrow);
         let autoref = Some(AutoPtr(r_borrow, mutbl_b));
 
         let lvalue_pref = LvaluePreference::from_mutbl(mutbl_b);
         let mut first_error = None;
-        let (_, autoderefs, success) = autoderef(self.fcx,
-                                                 expr_a.span,
-                                                 a,
-                                                 Some(expr_a),
+        let (_, autoderefs, success) = autoderef(self.fcx, span, a, exprs,
                                                  UnresolvedTypeAction::Ignore,
                                                  lvalue_pref,
                                                  |inner_ty, autoderef| {
@@ -206,19 +239,20 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
             }
             let ty = self.tcx().mk_ref(r_borrow,
                                         TypeAndMut {ty: inner_ty, mutbl: mutbl_b});
-            if let Err(err) = self.subtype(ty, b) {
-                if first_error.is_none() {
-                    first_error = Some(err);
+            match self.unify(ty, b) {
+                Err(err) => {
+                    if first_error.is_none() {
+                        first_error = Some(err);
+                    }
+                    None
                 }
-                None
-            } else {
-                Some(())
+                Ok((ty, _)) => Some(ty)
             }
         });
 
         match success {
-            Some(_) => {
-                Ok(Some(AdjustDerefRef(AutoDerefRef {
+            Some(ty) => {
+                Ok((ty, AdjustDerefRef(AutoDerefRef {
                     autoderefs: autoderefs,
                     autoref: autoref,
                     unsize: None
@@ -329,9 +363,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
             }
         }
 
-        let mut obligations = self.unsizing_obligations.borrow_mut();
-        assert!(obligations.is_empty());
-        *obligations = leftover_predicates;
+        *self.unsizing_obligations.borrow_mut() = leftover_predicates;
 
         let adjustment = AutoDerefRef {
             autoderefs: if reborrow.is_some() { 1 } else { 0 },
@@ -339,7 +371,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
             unsize: Some(target)
         };
         debug!("Success, coerced with {:?}", adjustment);
-        Ok(Some(AdjustDerefRef(adjustment)))
+        Ok((target, AdjustDerefRef(adjustment)))
     }
 
     fn coerce_from_fn_pointer(&self,
@@ -353,22 +385,21 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
          * into a closure or a `proc`.
          */
 
-        self.unpack_actual_value(b, |b| {
-            debug!("coerce_from_fn_pointer(a={:?}, b={:?})",
-                   a, b);
+        let b = self.fcx.infcx().shallow_resolve(b);
+        debug!("coerce_from_fn_pointer(a={:?}, b={:?})", a, b);
 
-            if let ty::TyBareFn(None, fn_ty_b) = b.sty {
-                match (fn_ty_a.unsafety, fn_ty_b.unsafety) {
-                    (hir::Unsafety::Normal, hir::Unsafety::Unsafe) => {
-                        let unsafe_a = self.tcx().safe_to_unsafe_fn_ty(fn_ty_a);
-                        try!(self.subtype(unsafe_a, b));
-                        return Ok(Some(AdjustUnsafeFnPointer));
-                    }
-                    _ => {}
+        if let ty::TyFnPtr(fn_ty_b) = b.sty {
+            match (fn_ty_a.unsafety, fn_ty_b.unsafety) {
+                (hir::Unsafety::Normal, hir::Unsafety::Unsafe) => {
+                    let unsafe_a = self.tcx().safe_to_unsafe_fn_ty(fn_ty_a);
+                    return self.unify(unsafe_a, b).map(|(ty, _)| {
+                        (ty, AdjustUnsafeFnPointer)
+                    });
                 }
+                _ => {}
             }
-            self.subtype(a, b)
-        })
+        }
+        self.unify(a, b)
     }
 
     fn coerce_from_fn_item(&self,
@@ -381,19 +412,18 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
          * into a closure or a `proc`.
          */
 
-        self.unpack_actual_value(b, |b| {
-            debug!("coerce_from_fn_item(a={:?}, b={:?})",
-                   a, b);
+        let b = self.fcx.infcx().shallow_resolve(b);
+        debug!("coerce_from_fn_item(a={:?}, b={:?})", a, b);
 
-            match b.sty {
-                ty::TyBareFn(None, _) => {
-                    let a_fn_pointer = self.tcx().mk_fn(None, fn_ty_a);
-                    try!(self.subtype(a_fn_pointer, b));
-                    Ok(Some(AdjustReifyFnPointer))
-                }
-                _ => self.subtype(a, b)
+        match b.sty {
+            ty::TyFnPtr(_) => {
+                let a_fn_pointer = self.tcx().mk_ty(ty::TyFnPtr(fn_ty_a));
+                self.unify(a_fn_pointer, b).map(|(ty, _)| {
+                    (ty, AdjustReifyFnPointer)
+                })
             }
-        })
+            _ => self.unify(a, b)
+        }
     }
 
     fn coerce_unsafe_ptr(&self,
@@ -409,74 +439,192 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
             ty::TyRef(_, mt) => (true, mt),
             ty::TyRawPtr(mt) => (false, mt),
             _ => {
-                return self.subtype(a, b);
+                return self.unify(a, b);
             }
         };
 
         // Check that the types which they point at are compatible.
         let a_unsafe = self.tcx().mk_ptr(ty::TypeAndMut{ mutbl: mutbl_b, ty: mt_a.ty });
-        try!(self.subtype(a_unsafe, b));
+        let (ty, noop) = try!(self.unify(a_unsafe, b));
         try!(coerce_mutbls(mt_a.mutbl, mutbl_b));
 
         // Although references and unsafe ptrs have the same
         // representation, we still register an AutoDerefRef so that
         // regionck knows that the region for `a` must be valid here.
-        if is_ref {
-            Ok(Some(AdjustDerefRef(AutoDerefRef {
+        Ok((ty, if is_ref {
+            AdjustDerefRef(AutoDerefRef {
                 autoderefs: 1,
                 autoref: Some(AutoUnsafe(mutbl_b)),
                 unsize: None
-            })))
+            })
         } else if mt_a.mutbl != mutbl_b {
-            Ok(Some(AdjustMutToConstPointer))
+            AdjustMutToConstPointer
         } else {
-            Ok(None)
-        }
+            noop
+        }))
     }
 }
 
-pub fn mk_assignty<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                             expr: &hir::Expr,
+fn apply<'a, 'b, 'tcx, E, I>(coerce: &mut Coerce<'a, 'tcx>,
+                             exprs: &E,
                              a: Ty<'tcx>,
                              b: Ty<'tcx>)
-                             -> RelateResult<'tcx, ()> {
-    debug!("mk_assignty({:?} -> {:?})", a, b);
-    let mut unsizing_obligations = vec![];
-    let adjustment = try!(indent(|| {
-        fcx.infcx().commit_if_ok(|_| {
-            let coerce = Coerce {
-                fcx: fcx,
-                origin: TypeOrigin::ExprAssignable(expr.span),
-                unsizing_obligations: RefCell::new(vec![])
-            };
-            let adjustment = try!(coerce.coerce(expr, a, b));
-            unsizing_obligations = coerce.unsizing_obligations.into_inner();
-            Ok(adjustment)
-        })
-    }));
+                             -> CoerceResult<'tcx>
+    where E: Fn() -> I,
+          I: IntoIterator<Item=&'b hir::Expr> {
 
-    if let Some(AdjustDerefRef(auto)) = adjustment {
+    let (ty, adjustment) = try!(indent(|| coerce.coerce(exprs, a, b)));
+
+    let fcx = coerce.fcx;
+    if let AdjustDerefRef(auto) = adjustment {
         if auto.unsize.is_some() {
-            for obligation in unsizing_obligations {
+            let mut obligations = coerce.unsizing_obligations.borrow_mut();
+            for obligation in obligations.drain(..) {
                 fcx.register_predicate(obligation);
             }
         }
     }
 
-    if let Some(adjustment) = adjustment {
-        debug!("Success, coerced with {:?}", adjustment);
-        fcx.write_adjustment(expr.id, adjustment);
-    }
-    Ok(())
+    Ok((ty, adjustment))
 }
 
-fn coerce_mutbls<'tcx>(from_mutbl: hir::Mutability,
-                       to_mutbl: hir::Mutability)
-                       -> CoerceResult<'tcx> {
-    match (from_mutbl, to_mutbl) {
-        (hir::MutMutable, hir::MutMutable) |
-        (hir::MutImmutable, hir::MutImmutable) |
-        (hir::MutMutable, hir::MutImmutable) => Ok(None),
-        (hir::MutImmutable, hir::MutMutable) => Err(TypeError::Mutability)
+/// Attempt to coerce an expression to a type, and return the
+/// adjusted type of the expression, if successful.
+/// Adjustments are only recorded if the coercion succeeded.
+/// The expressions *must not* have any pre-existing adjustments.
+pub fn try<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
+                     expr: &hir::Expr,
+                     target: Ty<'tcx>)
+                     -> RelateResult<'tcx, Ty<'tcx>> {
+    let source = fcx.resolve_type_vars_if_possible(fcx.expr_ty(expr));
+    debug!("coercion::try({:?}: {:?} -> {:?})", expr, source, target);
+
+    let mut coerce = Coerce::new(fcx, TypeOrigin::ExprAssignable(expr.span));
+    fcx.infcx().commit_if_ok(|_| {
+        let (ty, adjustment) =
+            try!(apply(&mut coerce, &|| Some(expr), source, target));
+        if !adjustment.is_identity() {
+            debug!("Success, coerced with {:?}", adjustment);
+            assert!(!fcx.inh.tables.borrow().adjustments.contains_key(&expr.id));
+            fcx.write_adjustment(expr.id, adjustment);
+        }
+        Ok(ty)
+    })
+}
+
+/// Given some expressions, their known unified type and another expression,
+/// tries to unify the types, potentially inserting coercions on any of the
+/// provided expressions and returns their LUB (aka "common supertype").
+pub fn try_find_lub<'a, 'b, 'tcx, E, I>(fcx: &FnCtxt<'a, 'tcx>,
+                                        origin: TypeOrigin,
+                                        exprs: E,
+                                        prev_ty: Ty<'tcx>,
+                                        new: &'b hir::Expr)
+                                        -> RelateResult<'tcx, Ty<'tcx>>
+    // FIXME(eddyb) use copyable iterators when that becomes ergonomic.
+    where E: Fn() -> I,
+          I: IntoIterator<Item=&'b hir::Expr> {
+
+    let prev_ty = fcx.resolve_type_vars_if_possible(prev_ty);
+    let new_ty = fcx.resolve_type_vars_if_possible(fcx.expr_ty(new));
+    debug!("coercion::try_find_lub({:?}, {:?})", prev_ty, new_ty);
+
+    let trace = TypeTrace::types(origin, true, prev_ty, new_ty);
+    let mut lub = fcx.infcx().lub(true, trace);
+
+    // Special-case that coercion alone cannot handle:
+    // Two function item types of differing IDs or Substs.
+    match (&prev_ty.sty, &new_ty.sty) {
+        (&ty::TyFnDef(a_def_id, a_substs, a_fty),
+         &ty::TyFnDef(b_def_id, b_substs, b_fty)) => {
+            // The signature must always match.
+            let fty = try!(lub.relate(a_fty, b_fty));
+
+            if a_def_id == b_def_id {
+                // Same function, maybe the parameters match.
+                let substs = fcx.infcx().commit_if_ok(|_| {
+                    relate_substs(&mut lub, None, a_substs, b_substs)
+                }).map(|s| fcx.tcx().mk_substs(s));
+
+                if let Ok(substs) = substs {
+                    // We have a LUB of prev_ty and new_ty, just return it.
+                    return Ok(fcx.tcx().mk_fn_def(a_def_id, substs, fty));
+                }
+            }
+
+            // Reify both sides and return the reified fn pointer type.
+            for expr in exprs().into_iter().chain(Some(new)) {
+                // No adjustments can produce a fn item, so this should never trip.
+                assert!(!fcx.inh.tables.borrow().adjustments.contains_key(&expr.id));
+                fcx.write_adjustment(expr.id, AdjustReifyFnPointer);
+            }
+            return Ok(fcx.tcx().mk_fn_ptr(fty));
+        }
+        _ => {}
+    }
+
+    let mut coerce = Coerce::new(fcx, origin);
+    coerce.use_lub = true;
+
+    // First try to coerce the new expression to the type of the previous ones,
+    // but only if the new expression has no coercion already applied to it.
+    let mut first_error = None;
+    if !fcx.inh.tables.borrow().adjustments.contains_key(&new.id) {
+        let result = fcx.infcx().commit_if_ok(|_| {
+            apply(&mut coerce, &|| Some(new), new_ty, prev_ty)
+        });
+        match result {
+            Ok((ty, adjustment)) => {
+                if !adjustment.is_identity() {
+                    fcx.write_adjustment(new.id, adjustment);
+                }
+                return Ok(ty);
+            }
+            Err(e) => first_error = Some(e)
+        }
+    }
+
+    // Then try to coerce the previous expressions to the type of the new one.
+    // This requires ensuring there are no coercions applied to *any* of the
+    // previous expressions, other than noop reborrows (ignoring lifetimes).
+    for expr in exprs() {
+        let noop = match fcx.inh.tables.borrow().adjustments.get(&expr.id) {
+            Some(&AdjustDerefRef(AutoDerefRef {
+                autoderefs: 1,
+                autoref: Some(AutoPtr(_, mutbl_adj)),
+                unsize: None
+            })) => match fcx.expr_ty(expr).sty {
+                ty::TyRef(_, mt_orig) => {
+                    // Reborrow that we can safely ignore.
+                    mutbl_adj == mt_orig.mutbl
+                }
+                _ => false
+            },
+            Some(_) => false,
+            None => true
+        };
+
+        if !noop {
+            return fcx.infcx().commit_if_ok(|_| lub.relate(&prev_ty, &new_ty));
+        }
+    }
+
+    match fcx.infcx().commit_if_ok(|_| apply(&mut coerce, &exprs, prev_ty, new_ty)) {
+        Err(_) => {
+            // Avoid giving strange errors on failed attempts.
+            if let Some(e) = first_error {
+                Err(e)
+            } else {
+                fcx.infcx().commit_if_ok(|_| lub.relate(&prev_ty, &new_ty))
+            }
+        }
+        Ok((ty, adjustment)) => {
+            if !adjustment.is_identity() {
+                for expr in exprs() {
+                    fcx.write_adjustment(expr.id, adjustment);
+                }
+            }
+            Ok(ty)
+        }
     }
 }
diff --git a/src/librustc_typeck/check/compare_method.rs b/src/librustc_typeck/check/compare_method.rs
index 2bf7d65e331..ff7b809577f 100644
--- a/src/librustc_typeck/check/compare_method.rs
+++ b/src/librustc_typeck/check/compare_method.rs
@@ -276,9 +276,9 @@ pub fn compare_impl_method<'tcx>(tcx: &TyCtxt<'tcx>,
     // type.
 
     // Compute skolemized form of impl and trait method tys.
-    let impl_fty = tcx.mk_fn(None, tcx.mk_bare_fn(impl_m.fty.clone()));
+    let impl_fty = tcx.mk_fn_ptr(impl_m.fty.clone());
     let impl_fty = impl_fty.subst(tcx, impl_to_skol_substs);
-    let trait_fty = tcx.mk_fn(None, tcx.mk_bare_fn(trait_m.fty.clone()));
+    let trait_fty = tcx.mk_fn_ptr(trait_m.fty.clone());
     let trait_fty = trait_fty.subst(tcx, &trait_to_skol_substs);
 
     let err = infcx.commit_if_ok(|snapshot| {
@@ -296,11 +296,11 @@ pub fn compare_impl_method<'tcx>(tcx: &TyCtxt<'tcx>,
                                                  impl_m_span,
                                                  impl_m_body_id,
                                                  &impl_sig);
-        let impl_fty = tcx.mk_fn(None, tcx.mk_bare_fn(ty::BareFnTy {
+        let impl_fty = tcx.mk_fn_ptr(ty::BareFnTy {
             unsafety: impl_m.fty.unsafety,
             abi: impl_m.fty.abi,
             sig: ty::Binder(impl_sig)
-        }));
+        });
         debug!("compare_impl_method: impl_fty={:?}",
                impl_fty);
 
@@ -314,11 +314,11 @@ pub fn compare_impl_method<'tcx>(tcx: &TyCtxt<'tcx>,
                                                  impl_m_span,
                                                  impl_m_body_id,
                                                  &trait_sig);
-        let trait_fty = tcx.mk_fn(None, tcx.mk_bare_fn(ty::BareFnTy {
+        let trait_fty = tcx.mk_fn_ptr(ty::BareFnTy {
             unsafety: trait_m.fty.unsafety,
             abi: trait_m.fty.abi,
             sig: ty::Binder(trait_sig)
-        }));
+        });
 
         debug!("compare_impl_method: trait_fty={:?}",
                trait_fty);
diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs
index 63dac49b384..1f61198bef9 100644
--- a/src/librustc_typeck/check/demand.rs
+++ b/src/librustc_typeck/check/demand.rs
@@ -10,45 +10,27 @@
 
 
 use check::{coercion, FnCtxt};
-use middle::ty::{self, Ty};
-use middle::infer::{self, TypeOrigin};
+use middle::ty::Ty;
+use middle::infer::TypeOrigin;
 
-use std::result::Result::{Err, Ok};
 use syntax::codemap::Span;
 use rustc_front::hir;
 
 // Requires that the two types unify, and prints an error message if
 // they don't.
 pub fn suptype<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, sp: Span,
-                         ty_expected: Ty<'tcx>, ty_actual: Ty<'tcx>) {
-    suptype_with_fn(fcx, sp, false, ty_expected, ty_actual,
-        |sp, e, a, s| { fcx.report_mismatched_types(sp, e, a, s) })
-}
-
-/// As `suptype`, but call `handle_err` if unification for subtyping fails.
-pub fn suptype_with_fn<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
-                                    sp: Span,
-                                    b_is_expected: bool,
-                                    ty_a: Ty<'tcx>,
-                                    ty_b: Ty<'tcx>,
-                                    handle_err: F) where
-    F: FnOnce(Span, Ty<'tcx>, Ty<'tcx>, &ty::error::TypeError<'tcx>),
-{
-    // n.b.: order of actual, expected is reversed
-    match infer::mk_subty(fcx.infcx(), b_is_expected, TypeOrigin::Misc(sp),
-                          ty_b, ty_a) {
-      Ok(()) => { /* ok */ }
-      Err(ref err) => {
-          handle_err(sp, ty_a, ty_b, err);
-      }
+                         expected: Ty<'tcx>, actual: Ty<'tcx>) {
+    let origin = TypeOrigin::Misc(sp);
+    if let Err(e) = fcx.infcx().sub_types(false, origin, actual, expected) {
+        fcx.infcx().report_mismatched_types(origin, expected, actual, e);
     }
 }
 
 pub fn eqtype<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, sp: Span,
                         expected: Ty<'tcx>, actual: Ty<'tcx>) {
-    match infer::mk_eqty(fcx.infcx(), false, TypeOrigin::Misc(sp), actual, expected) {
-        Ok(()) => { /* ok */ }
-        Err(ref err) => { fcx.report_mismatched_types(sp, expected, actual, err); }
+    let origin = TypeOrigin::Misc(sp);
+    if let Err(e) = fcx.infcx().eq_types(false, origin, actual, expected) {
+        fcx.infcx().report_mismatched_types(origin, expected, actual, e);
     }
 }
 
@@ -57,16 +39,10 @@ pub fn coerce<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                         sp: Span,
                         expected: Ty<'tcx>,
                         expr: &hir::Expr) {
-    let expr_ty = fcx.expr_ty(expr);
-    debug!("demand::coerce(expected = {:?}, expr_ty = {:?})",
-           expected,
-           expr_ty);
-    let expr_ty = fcx.resolve_type_vars_if_possible(expr_ty);
     let expected = fcx.resolve_type_vars_if_possible(expected);
-    match coercion::mk_assignty(fcx, expr, expr_ty, expected) {
-      Ok(()) => { /* ok */ }
-      Err(ref err) => {
-        fcx.report_mismatched_types(sp, expected, expr_ty, err);
-      }
+    if let Err(e) = coercion::try(fcx, expr, expected) {
+        let origin = TypeOrigin::Misc(sp);
+        let expr_ty = fcx.resolve_type_vars_if_possible(fcx.expr_ty(expr));
+        fcx.infcx().report_mismatched_types(origin, expected, expr_ty, e);
     }
 }
diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs
index 78faef473dd..4ebe4c25dd1 100644
--- a/src/librustc_typeck/check/dropck.rs
+++ b/src/librustc_typeck/check/dropck.rs
@@ -479,7 +479,7 @@ fn iterate_over_potentially_unsafe_regions_in_type<'a, 'b, 'tcx>(
             Ok(())
         }
 
-        ty::TyBareFn(..) => {
+        ty::TyFnDef(..) | ty::TyFnPtr(_) => {
             // FIXME(#26656): this type is always destruction-safe, but
             // it implicitly witnesses Self: Fn, which can be false.
             Ok(())
diff --git a/src/librustc_typeck/check/intrinsic.rs b/src/librustc_typeck/check/intrinsic.rs
index 6d8fff3caca..a05329bc4a4 100644
--- a/src/librustc_typeck/check/intrinsic.rs
+++ b/src/librustc_typeck/check/intrinsic.rs
@@ -13,7 +13,7 @@
 
 use astconv::AstConv;
 use intrinsics;
-use middle::subst;
+use middle::subst::{self, Substs};
 use middle::ty::FnSig;
 use middle::ty::{self, Ty, TyCtxt};
 use middle::ty::fold::TypeFolder;
@@ -33,7 +33,13 @@ fn equate_intrinsic_type<'a, 'tcx>(tcx: &TyCtxt<'tcx>, it: &hir::ForeignItem,
                                    abi: Abi,
                                    inputs: Vec<ty::Ty<'tcx>>,
                                    output: ty::FnOutput<'tcx>) {
-    let fty = tcx.mk_fn(None, tcx.mk_bare_fn(ty::BareFnTy {
+    let def_id = tcx.map.local_def_id(it.id);
+    let i_ty = tcx.lookup_item_type(def_id);
+
+    let mut substs = Substs::empty();
+    substs.types = i_ty.generics.types.map(|def| tcx.mk_param_from_def(def));
+
+    let fty = tcx.mk_fn_def(def_id, tcx.mk_substs(substs), ty::BareFnTy {
         unsafety: hir::Unsafety::Unsafe,
         abi: abi,
         sig: ty::Binder(FnSig {
@@ -41,8 +47,7 @@ fn equate_intrinsic_type<'a, 'tcx>(tcx: &TyCtxt<'tcx>, it: &hir::ForeignItem,
             output: output,
             variadic: false,
         }),
-    }));
-    let i_ty = tcx.lookup_item_type(tcx.map.local_def_id(it.id));
+    });
     let i_n_tps = i_ty.generics.types.len(subst::FnSpace);
     if i_n_tps != n_tps {
         span_err!(tcx.sess, it.span, E0094,
@@ -296,8 +301,7 @@ pub fn check_intrinsic_type(ccx: &CrateCtxt, it: &hir::ForeignItem) {
                         variadic: false,
                     }),
                 };
-                let fn_ty = tcx.mk_bare_fn(fn_ty);
-                (0, vec![tcx.mk_fn(None, fn_ty), mut_u8, mut_u8], tcx.types.i32)
+                (0, vec![tcx.mk_fn_ptr(fn_ty), mut_u8, mut_u8], tcx.types.i32)
             }
 
             ref other => {
diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs
index f2f2eb66444..f4268deee37 100644
--- a/src/librustc_typeck/check/method/confirm.rs
+++ b/src/librustc_typeck/check/method/confirm.rs
@@ -98,27 +98,29 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
         let InstantiatedMethodSig {
             method_sig, all_substs, method_predicates
         } = self.instantiate_method_sig(&pick, all_substs);
+        let all_substs = self.tcx().mk_substs(all_substs);
         let method_self_ty = method_sig.inputs[0];
 
         // Unify the (adjusted) self type with what the method expects.
         self.unify_receivers(self_ty, method_self_ty);
 
         // Create the method type
+        let def_id = pick.item.def_id();
         let method_ty = pick.item.as_opt_method().unwrap();
-        let fty = self.tcx().mk_fn(None, self.tcx().mk_bare_fn(ty::BareFnTy {
+        let fty = self.tcx().mk_fn_def(def_id, all_substs, ty::BareFnTy {
             sig: ty::Binder(method_sig),
             unsafety: method_ty.fty.unsafety,
             abi: method_ty.fty.abi.clone(),
-        }));
+        });
 
         // Add any trait/regions obligations specified on the method's type parameters.
-        self.add_obligations(fty, &all_substs, &method_predicates);
+        self.add_obligations(fty, all_substs, &method_predicates);
 
         // Create the final `MethodCallee`.
         let callee = ty::MethodCallee {
-            def_id: pick.item.def_id(),
+            def_id: def_id,
             ty: fty,
-            substs: self.tcx().mk_substs(all_substs)
+            substs: all_substs
         };
         // If this is an `&mut self` method, bias the receiver
         // expression towards mutability (this will switch
@@ -156,7 +158,7 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
         let (autoderefd_ty, n, result) = check::autoderef(self.fcx,
                                                           self.span,
                                                           unadjusted_self_ty,
-                                                          Some(self.self_expr),
+                                                          || Some(self.self_expr),
                                                           UnresolvedTypeAction::Error,
                                                           NoPreference,
                                                           |_, n| {
@@ -285,7 +287,7 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
         let (_, _, result) = check::autoderef(self.fcx,
                                               self.span,
                                               self_ty,
-                                              None,
+                                              || None,
                                               UnresolvedTypeAction::Error,
                                               NoPreference,
                                               |ty, _| {
@@ -457,7 +459,7 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
     fn fixup_derefs_on_method_receiver_if_necessary(&self,
                                                     method_callee: &ty::MethodCallee) {
         let sig = match method_callee.ty.sty {
-            ty::TyBareFn(_, ref f) => f.sig.clone(),
+            ty::TyFnDef(_, _, ref f) => f.sig.clone(),
             _ => return,
         };
 
@@ -507,7 +509,7 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
                 check::autoderef(self.fcx,
                                  expr.span,
                                  self.fcx.expr_ty(expr),
-                                 Some(expr),
+                                 || Some(expr),
                                  UnresolvedTypeAction::Error,
                                  PreferMutLvalue,
                                  |_, autoderefs| {
@@ -520,92 +522,94 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
             }
 
             // Don't retry the first one or we might infinite loop!
-            if i != 0 {
-                match expr.node {
-                    hir::ExprIndex(ref base_expr, ref index_expr) => {
-                        // If this is an overloaded index, the
-                        // adjustment will include an extra layer of
-                        // autoref because the method is an &self/&mut
-                        // self method. We have to peel it off to get
-                        // the raw adjustment that `try_index_step`
-                        // expects. This is annoying and horrible. We
-                        // ought to recode this routine so it doesn't
-                        // (ab)use the normal type checking paths.
-                        let adj = self.fcx.inh.tables.borrow().adjustments.get(&base_expr.id)
-                                                                          .cloned();
-                        let (autoderefs, unsize) = match adj {
-                            Some(AdjustDerefRef(adr)) => match adr.autoref {
-                                None => {
-                                    assert!(adr.unsize.is_none());
-                                    (adr.autoderefs, None)
-                                }
-                                Some(AutoPtr(_, _)) => {
-                                    (adr.autoderefs, adr.unsize.map(|target| {
-                                        target.builtin_deref(false, NoPreference)
-                                              .expect("fixup: AutoPtr is not &T").ty
-                                    }))
-                                }
-                                Some(_) => {
-                                    self.tcx().sess.span_bug(
-                                        base_expr.span,
-                                        &format!("unexpected adjustment autoref {:?}",
-                                                adr));
-                                }
-                            },
-                            None => (0, None),
+            if i == 0 {
+                continue;
+            }
+            match expr.node {
+                hir::ExprIndex(ref base_expr, ref index_expr) => {
+                    // If this is an overloaded index, the
+                    // adjustment will include an extra layer of
+                    // autoref because the method is an &self/&mut
+                    // self method. We have to peel it off to get
+                    // the raw adjustment that `try_index_step`
+                    // expects. This is annoying and horrible. We
+                    // ought to recode this routine so it doesn't
+                    // (ab)use the normal type checking paths.
+                    let adj = self.fcx.inh.tables.borrow().adjustments.get(&base_expr.id)
+                                                                        .cloned();
+                    let (autoderefs, unsize) = match adj {
+                        Some(AdjustDerefRef(adr)) => match adr.autoref {
+                            None => {
+                                assert!(adr.unsize.is_none());
+                                (adr.autoderefs, None)
+                            }
+                            Some(AutoPtr(_, _)) => {
+                                (adr.autoderefs, adr.unsize.map(|target| {
+                                    target.builtin_deref(false, NoPreference)
+                                            .expect("fixup: AutoPtr is not &T").ty
+                                }))
+                            }
                             Some(_) => {
                                 self.tcx().sess.span_bug(
                                     base_expr.span,
-                                    "unexpected adjustment type");
+                                    &format!("unexpected adjustment autoref {:?}",
+                                            adr));
                             }
-                        };
-
-                        let (adjusted_base_ty, unsize) = if let Some(target) = unsize {
-                            (target, true)
-                        } else {
-                            (self.fcx.adjust_expr_ty(base_expr,
-                                Some(&AdjustDerefRef(AutoDerefRef {
-                                    autoderefs: autoderefs,
-                                    autoref: None,
-                                    unsize: None
-                                }))), false)
-                        };
-                        let index_expr_ty = self.fcx.expr_ty(&index_expr);
-
-                        let result = check::try_index_step(
-                            self.fcx,
-                            ty::MethodCall::expr(expr.id),
-                            expr,
-                            &base_expr,
-                            adjusted_base_ty,
-                            autoderefs,
-                            unsize,
-                            PreferMutLvalue,
-                            index_expr_ty);
-
-                        if let Some((input_ty, return_ty)) = result {
-                            demand::suptype(self.fcx, index_expr.span, input_ty, index_expr_ty);
-
-                            let expr_ty = self.fcx.expr_ty(&expr);
-                            demand::suptype(self.fcx, expr.span, expr_ty, return_ty);
+                        },
+                        None => (0, None),
+                        Some(_) => {
+                            self.tcx().sess.span_bug(
+                                base_expr.span,
+                                "unexpected adjustment type");
                         }
+                    };
+
+                    let (adjusted_base_ty, unsize) = if let Some(target) = unsize {
+                        (target, true)
+                    } else {
+                        (self.fcx.adjust_expr_ty(base_expr,
+                            Some(&AdjustDerefRef(AutoDerefRef {
+                                autoderefs: autoderefs,
+                                autoref: None,
+                                unsize: None
+                            }))), false)
+                    };
+                    let index_expr_ty = self.fcx.expr_ty(&index_expr);
+
+                    let result = check::try_index_step(
+                        self.fcx,
+                        ty::MethodCall::expr(expr.id),
+                        expr,
+                        &base_expr,
+                        adjusted_base_ty,
+                        autoderefs,
+                        unsize,
+                        PreferMutLvalue,
+                        index_expr_ty);
+
+                    if let Some((input_ty, return_ty)) = result {
+                        demand::suptype(self.fcx, index_expr.span, input_ty, index_expr_ty);
+
+                        let expr_ty = self.fcx.expr_ty(&expr);
+                        demand::suptype(self.fcx, expr.span, expr_ty, return_ty);
                     }
-                    hir::ExprUnary(hir::UnDeref, ref base_expr) => {
-                        // if this is an overloaded deref, then re-evaluate with
-                        // a preference for mut
-                        let method_call = ty::MethodCall::expr(expr.id);
-                        if self.fcx.inh.tables.borrow().method_map.contains_key(&method_call) {
-                            check::try_overloaded_deref(
-                                self.fcx,
-                                expr.span,
-                                Some(method_call),
-                                Some(&base_expr),
-                                self.fcx.expr_ty(&base_expr),
-                                PreferMutLvalue);
-                        }
+                }
+                hir::ExprUnary(hir::UnDeref, ref base_expr) => {
+                    // if this is an overloaded deref, then re-evaluate with
+                    // a preference for mut
+                    let method_call = ty::MethodCall::expr(expr.id);
+                    if self.fcx.inh.tables.borrow().method_map.contains_key(&method_call) {
+                        let method = check::try_overloaded_deref(
+                            self.fcx,
+                            expr.span,
+                            Some(&base_expr),
+                            self.fcx.expr_ty(&base_expr),
+                            PreferMutLvalue);
+                        let method = method.expect("re-trying deref failed");
+                        self.fcx.inh.tables.borrow_mut().method_map.insert(method_call, method);
                     }
-                    _ => {}
                 }
+                _ => {}
             }
         }
     }
diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs
index fce44683413..e74623eda6d 100644
--- a/src/librustc_typeck/check/method/mod.rs
+++ b/src/librustc_typeck/check/method/mod.rs
@@ -230,11 +230,12 @@ pub fn lookup_in_trait_adjusted<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                                                        &method_ty.fty.sig).0;
     let fn_sig = fcx.instantiate_type_scheme(span, trait_ref.substs, &fn_sig);
     let transformed_self_ty = fn_sig.inputs[0];
-    let fty = tcx.mk_fn(None, tcx.mk_bare_fn(ty::BareFnTy {
+    let def_id = method_item.def_id();
+    let fty = tcx.mk_fn_def(def_id, trait_ref.substs, ty::BareFnTy {
         sig: ty::Binder(fn_sig),
         unsafety: method_ty.fty.unsafety,
         abi: method_ty.fty.abi.clone(),
-    }));
+    });
 
     debug!("lookup_in_trait_adjusted: matched method fty={:?} obligation={:?}",
            fty,
@@ -318,7 +319,7 @@ pub fn lookup_in_trait_adjusted<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
     }
 
     let callee = ty::MethodCallee {
-        def_id: method_item.def_id(),
+        def_id: def_id,
         ty: fty,
         substs: trait_ref.substs
     };
diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs
index 4a8a893cc46..d11a07cb41f 100644
--- a/src/librustc_typeck/check/method/probe.rs
+++ b/src/librustc_typeck/check/method/probe.rs
@@ -200,7 +200,7 @@ fn create_steps<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
     let (final_ty, dereferences, _) = check::autoderef(fcx,
                                                        span,
                                                        self_ty,
-                                                       None,
+                                                       || None,
                                                        UnresolvedTypeAction::Error,
                                                        NoPreference,
                                                        |t, d| {
diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs
index e7d84efdaa2..7dc9d46c303 100644
--- a/src/librustc_typeck/check/method/suggest.rs
+++ b/src/librustc_typeck/check/method/suggest.rs
@@ -101,7 +101,7 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                     match field_ty.sty {
                         // Not all of these (e.g. unsafe fns) implement FnOnce
                         // so we look for these beforehand
-                        ty::TyClosure(..) | ty::TyBareFn(..) => {
+                        ty::TyClosure(..) | ty::TyFnDef(..) | ty::TyFnPtr(_) => {
                             span_stored_function!();
                         }
                         // If it's not a simple function, look for things which implement FnOnce
@@ -351,7 +351,7 @@ fn type_derefs_to_local<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
         return is_local(fcx.resolve_type_vars_if_possible(rcvr_ty));
     }
 
-    check::autoderef(fcx, span, rcvr_ty, None,
+    check::autoderef(fcx, span, rcvr_ty, || None,
                      check::UnresolvedTypeAction::Ignore, ty::NoPreference,
                      |ty, _| {
         if is_local(ty) {
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index eee3d232831..0743c0b9e18 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -89,7 +89,7 @@ use middle::cstore::LOCAL_CRATE;
 use middle::def::{self, Def};
 use middle::def_id::DefId;
 use middle::infer;
-use middle::infer::{TypeOrigin, type_variable};
+use middle::infer::{TypeOrigin, TypeTrace, type_variable};
 use middle::pat_util::{self, pat_id_map};
 use middle::subst::{self, Subst, Substs, VecPerParamSpace, ParamSpace};
 use middle::traits::{self, report_fulfillment_errors};
@@ -101,6 +101,7 @@ use middle::ty::{MethodCall, MethodCallee};
 use middle::ty::adjustment;
 use middle::ty::error::TypeError;
 use middle::ty::fold::{TypeFolder, TypeFoldable};
+use middle::ty::relate::TypeRelation;
 use middle::ty::util::Representability;
 use require_c_abi_if_variadic;
 use rscope::{ElisionFailureInfo, RegionScope};
@@ -434,7 +435,7 @@ fn check_bare_fn<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
                            param_env: ty::ParameterEnvironment<'a, 'tcx>)
 {
     match raw_fty.sty {
-        ty::TyBareFn(_, ref fn_ty) => {
+        ty::TyFnDef(_, _, ref fn_ty) => {
             let tables = RefCell::new(ty::Tables::empty());
             let inh = Inherited::new(ccx.tcx, &tables, param_env);
 
@@ -1622,14 +1623,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         self.infcx().type_error_struct(sp, mk_msg, actual_ty, err)
     }
 
-    pub fn report_mismatched_types(&self,
-                                   sp: Span,
-                                   e: Ty<'tcx>,
-                                   a: Ty<'tcx>,
-                                   err: &TypeError<'tcx>) {
-        self.infcx().report_mismatched_types(sp, e, a, err)
-    }
-
     /// Registers an obligation for checking later, during regionck, that the type `ty` must
     /// outlive the region `r`.
     pub fn register_region_obligation(&self,
@@ -1709,6 +1702,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     }
 
     // FIXME(arielb1): use this instead of field.ty everywhere
+    // Only for fields! Returns <none> for methods>
+    // Indifferent to privacy flags
     pub fn field_ty(&self,
                     span: Span,
                     field: ty::FieldDef<'tcx>,
@@ -1719,8 +1714,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                            &field.ty(self.tcx(), substs))
     }
 
-    // Only for fields! Returns <none> for methods>
-    // Indifferent to privacy flags
     fn check_casts(&self) {
         let mut deferred_cast_checks = self.inh.deferred_cast_checks.borrow_mut();
         for cast in deferred_cast_checks.drain(..) {
@@ -2061,20 +2054,21 @@ pub enum UnresolvedTypeAction {
 ///
 /// Note: this method does not modify the adjustments table. The caller is responsible for
 /// inserting an AutoAdjustment record into the `fcx` using one of the suitable methods.
-pub fn autoderef<'a, 'tcx, T, F>(fcx: &FnCtxt<'a, 'tcx>,
-                                 sp: Span,
-                                 base_ty: Ty<'tcx>,
-                                 opt_expr: Option<&hir::Expr>,
-                                 unresolved_type_action: UnresolvedTypeAction,
-                                 mut lvalue_pref: LvaluePreference,
-                                 mut should_stop: F)
-                                 -> (Ty<'tcx>, usize, Option<T>)
-    where F: FnMut(Ty<'tcx>, usize) -> Option<T>,
+pub fn autoderef<'a, 'b, 'tcx, E, I, T, F>(fcx: &FnCtxt<'a, 'tcx>,
+                                           sp: Span,
+                                           base_ty: Ty<'tcx>,
+                                           maybe_exprs: E,
+                                           unresolved_type_action: UnresolvedTypeAction,
+                                           mut lvalue_pref: LvaluePreference,
+                                           mut should_stop: F)
+                                           -> (Ty<'tcx>, usize, Option<T>)
+    // FIXME(eddyb) use copyable iterators when that becomes ergonomic.
+    where E: Fn() -> I,
+          I: IntoIterator<Item=&'b hir::Expr>,
+          F: FnMut(Ty<'tcx>, usize) -> Option<T>,
 {
-    debug!("autoderef(base_ty={:?}, opt_expr={:?}, lvalue_pref={:?})",
-           base_ty,
-           opt_expr,
-           lvalue_pref);
+    debug!("autoderef(base_ty={:?}, lvalue_pref={:?})",
+           base_ty, lvalue_pref);
 
     let mut t = base_ty;
     for autoderefs in 0..fcx.tcx().sess.recursion_limit.get() {
@@ -2087,7 +2081,7 @@ pub fn autoderef<'a, 'tcx, T, F>(fcx: &FnCtxt<'a, 'tcx>,
                 // (i.e. it is an inference variable) because `Ty::builtin_deref`
                 // and `try_overloaded_deref` both simply return `None`
                 // in such a case without producing spurious errors.
-                fcx.resolve_type_vars_if_possible(t)
+                fcx.infcx().resolve_type_vars_if_possible(&t)
             }
         };
         if resolved_t.references_error() {
@@ -2100,34 +2094,34 @@ pub fn autoderef<'a, 'tcx, T, F>(fcx: &FnCtxt<'a, 'tcx>,
         }
 
         // Otherwise, deref if type is derefable:
-        let mt = match resolved_t.builtin_deref(false, lvalue_pref) {
-            Some(mt) => Some(mt),
-            None => {
-                let method_call =
-                    opt_expr.map(|expr| MethodCall::autoderef(expr.id, autoderefs as u32));
-
-                // Super subtle: it might seem as though we should
-                // pass `opt_expr` to `try_overloaded_deref`, so that
-                // the (implicit) autoref of using an overloaded deref
-                // would get added to the adjustment table. However we
-                // do not do that, because it's kind of a
-                // "meta-adjustment" -- instead, we just leave it
-                // unrecorded and know that there "will be" an
-                // autoref. regionck and other bits of the code base,
-                // when they encounter an overloaded autoderef, have
-                // to do some reconstructive surgery. This is a pretty
-                // complex mess that is begging for a proper MIR.
-                try_overloaded_deref(fcx, sp, method_call, None, resolved_t, lvalue_pref)
+
+        // Super subtle: it might seem as though we should
+        // pass `opt_expr` to `try_overloaded_deref`, so that
+        // the (implicit) autoref of using an overloaded deref
+        // would get added to the adjustment table. However we
+        // do not do that, because it's kind of a
+        // "meta-adjustment" -- instead, we just leave it
+        // unrecorded and know that there "will be" an
+        // autoref. regionck and other bits of the code base,
+        // when they encounter an overloaded autoderef, have
+        // to do some reconstructive surgery. This is a pretty
+        // complex mess that is begging for a proper MIR.
+        let mt = if let Some(mt) = resolved_t.builtin_deref(false, lvalue_pref) {
+            mt
+        } else if let Some(method) = try_overloaded_deref(fcx, sp, None,
+                                                          resolved_t, lvalue_pref) {
+            for expr in maybe_exprs() {
+                let method_call = MethodCall::autoderef(expr.id, autoderefs as u32);
+                fcx.inh.tables.borrow_mut().method_map.insert(method_call, method);
             }
+            make_overloaded_lvalue_return_type(fcx.tcx(), method)
+        } else {
+            return (resolved_t, autoderefs, None);
         };
-        match mt {
-            Some(mt) => {
-                t = mt.ty;
-                if mt.mutbl == hir::MutImmutable {
-                    lvalue_pref = NoPreference;
-                }
-            }
-            None => return (resolved_t, autoderefs, None)
+
+        t = mt.ty;
+        if mt.mutbl == hir::MutImmutable {
+            lvalue_pref = NoPreference;
         }
     }
 
@@ -2140,11 +2134,10 @@ pub fn autoderef<'a, 'tcx, T, F>(fcx: &FnCtxt<'a, 'tcx>,
 
 fn try_overloaded_deref<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                   span: Span,
-                                  method_call: Option<MethodCall>,
                                   base_expr: Option<&hir::Expr>,
                                   base_ty: Ty<'tcx>,
                                   lvalue_pref: LvaluePreference)
-                                  -> Option<ty::TypeAndMut<'tcx>>
+                                  -> Option<MethodCallee<'tcx>>
 {
     // Try DerefMut first, if preferred.
     let method = match (lvalue_pref, fcx.tcx().lang_items.deref_mut_trait()) {
@@ -2166,33 +2159,23 @@ fn try_overloaded_deref<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
         (method, _) => method
     };
 
-    make_overloaded_lvalue_return_type(fcx, method_call, method)
+    method
 }
 
 /// For the overloaded lvalue expressions (`*x`, `x[3]`), the trait returns a type of `&T`, but the
 /// actual type we assign to the *expression* is `T`. So this function just peels off the return
-/// type by one layer to yield `T`. It also inserts the `method-callee` into the method map.
-fn make_overloaded_lvalue_return_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                                                method_call: Option<MethodCall>,
-                                                method: Option<MethodCallee<'tcx>>)
-                                                -> Option<ty::TypeAndMut<'tcx>>
+/// type by one layer to yield `T`.
+fn make_overloaded_lvalue_return_type<'tcx>(tcx: &TyCtxt<'tcx>,
+                                            method: MethodCallee<'tcx>)
+                                            -> ty::TypeAndMut<'tcx>
 {
-    match method {
-        Some(method) => {
-            // extract method return type, which will be &T;
-            // all LB regions should have been instantiated during method lookup
-            let ret_ty = method.ty.fn_ret();
-            let ret_ty = fcx.tcx().no_late_bound_regions(&ret_ty).unwrap().unwrap();
-
-            if let Some(method_call) = method_call {
-                fcx.inh.tables.borrow_mut().method_map.insert(method_call, method);
-            }
+    // extract method return type, which will be &T;
+    // all LB regions should have been instantiated during method lookup
+    let ret_ty = method.ty.fn_ret();
+    let ret_ty = tcx.no_late_bound_regions(&ret_ty).unwrap().unwrap();
 
-            // method returns &T, but the type as visible to user is T, so deref
-            ret_ty.builtin_deref(true, NoPreference)
-        }
-        None => None,
-    }
+    // method returns &T, but the type as visible to user is T, so deref
+    ret_ty.builtin_deref(true, NoPreference).unwrap()
 }
 
 fn lookup_indexing<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
@@ -2210,7 +2193,7 @@ fn lookup_indexing<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
     let (ty, autoderefs, final_mt) = autoderef(fcx,
                                                base_expr.span,
                                                base_ty,
-                                               Some(base_expr),
+                                               || Some(base_expr),
                                                UnresolvedTypeAction::Error,
                                                lvalue_pref,
                                                |adj_ty, idx| {
@@ -2307,10 +2290,10 @@ fn try_index_step<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
     // If some lookup succeeds, write callee into table and extract index/element
     // type from the method signature.
     // If some lookup succeeded, install method in table
-    method.and_then(|method| {
+    method.map(|method| {
         debug!("try_index_step: success, using overloaded indexing");
-        make_overloaded_lvalue_return_type(fcx, Some(method_call), Some(method)).
-            map(|ret| (input_ty, ret.ty))
+        fcx.inh.tables.borrow_mut().method_map.insert(method_call, method);
+        (input_ty, make_overloaded_lvalue_return_type(fcx.tcx(), method).ty)
     })
 }
 
@@ -2340,7 +2323,7 @@ fn check_method_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
         ty::FnConverging(fcx.tcx().types.err)
     } else {
         match method_fn_ty.sty {
-            ty::TyBareFn(_, ref fty) => {
+            ty::TyFnDef(_, _, ref fty) => {
                 // HACK(eddyb) ignore self in the definition (see above).
                 let expected_arg_tys = expected_types_for_fn_args(fcx,
                                                                   sp,
@@ -2509,20 +2492,17 @@ fn check_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                     Expectation::rvalue_hint(fcx.tcx(), ty)
                 });
 
-                check_expr_with_unifier(fcx,
-                                        &arg,
-                                        expected.unwrap_or(ExpectHasType(formal_ty)),
-                                        NoPreference, || {
-                    // 2. Coerce to the most detailed type that could be coerced
-                    //    to, which is `expected_ty` if `rvalue_hint` returns an
-                    //    `ExprHasType(expected_ty)`, or the `formal_ty` otherwise.
-                    let coerce_ty = expected.and_then(|e| e.only_has_type(fcx));
-                    demand::coerce(fcx, arg.span, coerce_ty.unwrap_or(formal_ty), &arg);
-
-                    // 3. Relate the expected type and the formal one,
-                    //    if the expected type was used for the coercion.
-                    coerce_ty.map(|ty| demand::suptype(fcx, arg.span, formal_ty, ty));
-                });
+                check_expr_with_expectation(fcx, &arg,
+                    expected.unwrap_or(ExpectHasType(formal_ty)));
+                // 2. Coerce to the most detailed type that could be coerced
+                //    to, which is `expected_ty` if `rvalue_hint` returns an
+                //    `ExpectHasType(expected_ty)`, or the `formal_ty` otherwise.
+                let coerce_ty = expected.and_then(|e| e.only_has_type(fcx));
+                demand::coerce(fcx, arg.span, coerce_ty.unwrap_or(formal_ty), &arg);
+
+                // 3. Relate the expected type and the formal one,
+                //    if the expected type was used for the coercion.
+                coerce_ty.map(|ty| demand::suptype(fcx, arg.span, formal_ty, ty));
             }
 
             if let Some(&arg_ty) = fcx.inh.tables.borrow().node_types.get(&arg.id) {
@@ -2619,7 +2599,7 @@ fn check_lit<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                     ty::TyInt(_) | ty::TyUint(_) => Some(ty),
                     ty::TyChar => Some(tcx.types.u8),
                     ty::TyRawPtr(..) => Some(tcx.types.usize),
-                    ty::TyBareFn(..) => Some(tcx.types.usize),
+                    ty::TyFnDef(..) | ty::TyFnPtr(_) => Some(tcx.types.usize),
                     _ => None
                 }
             });
@@ -2644,57 +2624,42 @@ fn check_lit<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
 fn check_expr_eq_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                 expr: &'tcx hir::Expr,
                                 expected: Ty<'tcx>) {
-    check_expr_with_unifier(
-        fcx, expr, ExpectHasType(expected), NoPreference,
-        || demand::eqtype(fcx, expr.span, expected, fcx.expr_ty(expr)));
+    check_expr_with_hint(fcx, expr, expected);
+    demand::eqtype(fcx, expr.span, expected, fcx.expr_ty(expr));
 }
 
 pub fn check_expr_has_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                      expr: &'tcx hir::Expr,
                                      expected: Ty<'tcx>) {
-    check_expr_with_unifier(
-        fcx, expr, ExpectHasType(expected), NoPreference,
-        || demand::suptype(fcx, expr.span, expected, fcx.expr_ty(expr)));
+    check_expr_with_hint(fcx, expr, expected);
+    demand::suptype(fcx, expr.span, expected, fcx.expr_ty(expr));
 }
 
 fn check_expr_coercable_to_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                           expr: &'tcx hir::Expr,
                                           expected: Ty<'tcx>) {
-    check_expr_with_unifier(
-        fcx, expr, ExpectHasType(expected), NoPreference,
-        || demand::coerce(fcx, expr.span, expected, expr));
+    check_expr_with_hint(fcx, expr, expected);
+    demand::coerce(fcx, expr.span, expected, expr);
 }
 
 fn check_expr_with_hint<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, expr: &'tcx hir::Expr,
                                   expected: Ty<'tcx>) {
-    check_expr_with_unifier(
-        fcx, expr, ExpectHasType(expected), NoPreference,
-        || ())
+    check_expr_with_expectation(fcx, expr, ExpectHasType(expected))
 }
 
 fn check_expr_with_expectation<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                          expr: &'tcx hir::Expr,
                                          expected: Expectation<'tcx>) {
-    check_expr_with_unifier(
-        fcx, expr, expected, NoPreference,
-        || ())
-}
-
-fn check_expr_with_expectation_and_lvalue_pref<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                                                         expr: &'tcx hir::Expr,
-                                                         expected: Expectation<'tcx>,
-                                                         lvalue_pref: LvaluePreference)
-{
-    check_expr_with_unifier(fcx, expr, expected, lvalue_pref, || ())
+    check_expr_with_expectation_and_lvalue_pref(fcx, expr, expected, NoPreference)
 }
 
 fn check_expr<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, expr: &'tcx hir::Expr)  {
-    check_expr_with_unifier(fcx, expr, NoExpectation, NoPreference, || ())
+    check_expr_with_expectation(fcx, expr, NoExpectation)
 }
 
 fn check_expr_with_lvalue_pref<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, expr: &'tcx hir::Expr,
                                         lvalue_pref: LvaluePreference)  {
-    check_expr_with_unifier(fcx, expr, NoExpectation, lvalue_pref, || ())
+    check_expr_with_expectation_and_lvalue_pref(fcx, expr, NoExpectation, lvalue_pref)
 }
 
 // determine the `self` type, using fresh variables for all variables
@@ -2796,13 +2761,10 @@ fn expected_types_for_fn_args<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
 /// Note that inspecting a type's structure *directly* may expose the fact
 /// that there are actually multiple representations for `TyError`, so avoid
 /// that when err needs to be handled differently.
-fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
-                                        expr: &'tcx hir::Expr,
-                                        expected: Expectation<'tcx>,
-                                        lvalue_pref: LvaluePreference,
-                                        unifier: F) where
-    F: FnOnce(),
-{
+fn check_expr_with_expectation_and_lvalue_pref<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
+                                                         expr: &'tcx hir::Expr,
+                                                         expected: Expectation<'tcx>,
+                                                         lvalue_pref: LvaluePreference) {
     debug!(">> typechecking: expr={:?} expected={:?}",
            expr, expected);
 
@@ -2873,30 +2835,52 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
         check_block_with_expected(fcx, then_blk, expected);
         let then_ty = fcx.node_ty(then_blk.id);
 
-        let branches_ty = match opt_else_expr {
-            Some(ref else_expr) => {
-                check_expr_with_expectation(fcx, &else_expr, expected);
-                let else_ty = fcx.expr_ty(&else_expr);
-                infer::common_supertype(fcx.infcx(),
-                                        TypeOrigin::IfExpression(sp),
-                                        true,
-                                        then_ty,
-                                        else_ty)
-            }
-            None => {
-                infer::common_supertype(fcx.infcx(),
-                                        TypeOrigin::IfExpressionWithNoElse(sp),
-                                        false,
-                                        then_ty,
-                                        fcx.tcx().mk_nil())
-            }
-        };
+        let unit = fcx.tcx().mk_nil();
+        let (origin, expected, found, result) =
+        if let Some(else_expr) = opt_else_expr {
+            check_expr_with_expectation(fcx, else_expr, expected);
+            let else_ty = fcx.expr_ty(else_expr);
+            let origin = TypeOrigin::IfExpression(sp);
+
+            // Only try to coerce-unify if we have a then expression
+            // to assign coercions to, otherwise it's () or diverging.
+            let result = if let Some(ref then) = then_blk.expr {
+                let res = coercion::try_find_lub(fcx, origin, || Some(&**then),
+                                                 then_ty, else_expr);
+
+                // In case we did perform an adjustment, we have to update
+                // the type of the block, because old trans still uses it.
+                let adj = fcx.inh.tables.borrow().adjustments.get(&then.id).cloned();
+                if res.is_ok() && adj.is_some() {
+                    fcx.write_ty(then_blk.id, fcx.adjust_expr_ty(then, adj.as_ref()));
+                }
 
-        let cond_ty = fcx.expr_ty(cond_expr);
-        let if_ty = if cond_ty.references_error() {
-            fcx.tcx().types.err
+                res
+            } else {
+                fcx.infcx().commit_if_ok(|_| {
+                    let trace = TypeTrace::types(origin, true, then_ty, else_ty);
+                    fcx.infcx().lub(true, trace).relate(&then_ty, &else_ty)
+                })
+            };
+            (origin, then_ty, else_ty, result)
         } else {
-            branches_ty
+            let origin = TypeOrigin::IfExpressionWithNoElse(sp);
+            (origin, unit, then_ty,
+             fcx.infcx().eq_types(true, origin, unit, then_ty).map(|_| unit))
+        };
+
+        let if_ty = match result {
+            Ok(ty) => {
+                if fcx.expr_ty(cond_expr).references_error() {
+                    fcx.tcx().types.err
+                } else {
+                    ty
+                }
+            }
+            Err(e) => {
+                fcx.infcx().report_mismatched_types(origin, expected, found, e);
+                fcx.tcx().types.err
+            }
         };
 
         fcx.write_ty(id, if_ty);
@@ -2915,7 +2899,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
         let (_, autoderefs, field_ty) = autoderef(fcx,
                                                   expr.span,
                                                   expr_t,
-                                                  Some(base),
+                                                  || Some(base),
                                                   UnresolvedTypeAction::Error,
                                                   lvalue_pref,
                                                   |base_t, _| {
@@ -3013,7 +2997,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
         let (_, autoderefs, field_ty) = autoderef(fcx,
                                                   expr.span,
                                                   expr_t,
-                                                  Some(base),
+                                                  || Some(base),
                                                   UnresolvedTypeAction::Error,
                                                   lvalue_pref,
                                                   |base_t, _| {
@@ -3261,21 +3245,21 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
             match unop {
                 hir::UnDeref => {
                     oprnd_t = structurally_resolved_type(fcx, expr.span, oprnd_t);
-                    oprnd_t = match oprnd_t.builtin_deref(true, NoPreference) {
-                        Some(mt) => mt.ty,
-                        None => match try_overloaded_deref(fcx, expr.span,
-                                                           Some(MethodCall::expr(expr.id)),
-                                                           Some(&oprnd), oprnd_t, lvalue_pref) {
-                            Some(mt) => mt.ty,
-                            None => {
-                                fcx.type_error_message(expr.span, |actual| {
-                                    format!("type `{}` cannot be \
-                                            dereferenced", actual)
-                                }, oprnd_t, None);
-                                tcx.types.err
-                            }
-                        }
-                    };
+
+                    if let Some(mt) = oprnd_t.builtin_deref(true, NoPreference) {
+                        oprnd_t = mt.ty;
+                    } else if let Some(method) = try_overloaded_deref(
+                            fcx, expr.span, Some(&oprnd), oprnd_t, lvalue_pref) {
+                        oprnd_t = make_overloaded_lvalue_return_type(tcx, method).ty;
+                        fcx.inh.tables.borrow_mut().method_map.insert(MethodCall::expr(expr.id),
+                                                                      method);
+                    } else {
+                        fcx.type_error_message(expr.span, |actual| {
+                            format!("type `{}` cannot be \
+                                    dereferenced", actual)
+                        }, oprnd_t, None);
+                        oprnd_t = tcx.types.err;
+                    }
                 }
                 hir::UnNot => {
                     oprnd_t = structurally_resolved_type(fcx, oprnd.span,
@@ -3519,7 +3503,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
 
             // Defer other checks until we're done type checking.
             let mut deferred_cast_checks = fcx.inh.deferred_cast_checks.borrow_mut();
-            let cast_check = cast::CastCheck::new((**e).clone(), t_expr, t_cast, expr.span);
+            let cast_check = cast::CastCheck::new(e, t_expr, t_cast, expr.span);
             deferred_cast_checks.push(cast_check);
         }
       }
@@ -3536,23 +3520,30 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
             }
         });
 
-        let typ = match uty {
-            Some(uty) => {
-                for e in args {
-                    check_expr_coercable_to_type(fcx, &e, uty);
-                }
-                uty
-            }
-            None => {
-                let t: Ty = fcx.infcx().next_ty_var();
-                for e in args {
-                    check_expr_has_type(fcx, &e, t);
+        let mut unified = fcx.infcx().next_ty_var();
+        let coerce_to = uty.unwrap_or(unified);
+
+        for (i, e) in args.iter().enumerate() {
+            check_expr_with_hint(fcx, e, coerce_to);
+            let e_ty = fcx.expr_ty(e);
+            let origin = TypeOrigin::Misc(e.span);
+
+            // Special-case the first element, as it has no "previous expressions".
+            let result = if i == 0 {
+                coercion::try(fcx, e, coerce_to)
+            } else {
+                let prev_elems = || args[..i].iter().map(|e| &**e);
+                coercion::try_find_lub(fcx, origin, prev_elems, unified, e)
+            };
+
+            match result {
+                Ok(ty) => unified = ty,
+                Err(e) => {
+                    fcx.infcx().report_mismatched_types(origin, unified, e_ty, e);
                 }
-                t
             }
-        };
-        let typ = tcx.mk_array(typ, args.len());
-        fcx.write_ty(id, typ);
+        }
+        fcx.write_ty(id, tcx.mk_array(unified, args.len()));
       }
       hir::ExprRepeat(ref element, ref count_expr) => {
         check_expr_has_type(fcx, &count_expr, tcx.types.usize);
@@ -3680,8 +3671,6 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
     debug!("... {:?}, expected is {:?}",
            fcx.expr_ty(expr),
            expected);
-
-    unifier();
 }
 
 pub fn resolve_ty_and_def_ufcs<'a, 'b, 'tcx>(fcx: &FnCtxt<'b, 'tcx>,
diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs
index f6225cf6ca7..e428fc927f0 100644
--- a/src/librustc_typeck/check/regionck.rs
+++ b/src/librustc_typeck/check/regionck.rs
@@ -881,7 +881,7 @@ fn constrain_callee(rcx: &mut Rcx,
                     _callee_expr: &hir::Expr) {
     let callee_ty = rcx.resolve_node_type(callee_id);
     match callee_ty.sty {
-        ty::TyBareFn(..) => { }
+        ty::TyFnDef(..) | ty::TyFnPtr(_) => { }
         _ => {
             // this should not happen, but it does if the program is
             // erroneous
diff --git a/src/librustc_typeck/check/wfcheck.rs b/src/librustc_typeck/check/wfcheck.rs
index 997f56bd449..a8ada806131 100644
--- a/src/librustc_typeck/check/wfcheck.rs
+++ b/src/librustc_typeck/check/wfcheck.rs
@@ -255,9 +255,9 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
             let type_scheme = fcx.tcx().lookup_item_type(fcx.tcx().map.local_def_id(item.id));
             let item_ty = fcx.instantiate_type_scheme(item.span, free_substs, &type_scheme.ty);
             let bare_fn_ty = match item_ty.sty {
-                ty::TyBareFn(_, ref bare_fn_ty) => bare_fn_ty,
+                ty::TyFnDef(_, _, ref bare_fn_ty) => bare_fn_ty,
                 _ => {
-                    this.tcx().sess.span_bug(item.span, "Fn item without bare fn type");
+                    this.tcx().sess.span_bug(item.span, "Fn item without fn type");
                 }
             };
 
diff --git a/src/librustc_typeck/coherence/mod.rs b/src/librustc_typeck/coherence/mod.rs
index 61060114330..cad321c0b23 100644
--- a/src/librustc_typeck/coherence/mod.rs
+++ b/src/librustc_typeck/coherence/mod.rs
@@ -27,7 +27,7 @@ use middle::ty::{Ty, TyBool, TyChar, TyEnum, TyError};
 use middle::ty::{TyParam, TyRawPtr};
 use middle::ty::{TyRef, TyStruct, TyTrait, TyTuple};
 use middle::ty::{TyStr, TyArray, TySlice, TyFloat, TyInfer, TyInt};
-use middle::ty::{TyUint, TyClosure, TyBox, TyBareFn};
+use middle::ty::{TyUint, TyClosure, TyBox, TyFnDef, TyFnPtr};
 use middle::ty::TyProjection;
 use middle::ty::util::CopyImplementationError;
 use middle::free_region::FreeRegionMap;
@@ -67,8 +67,8 @@ fn get_base_type_def_id<'a, 'tcx>(inference_context: &InferCtxt<'a, 'tcx>,
         }
 
         TyBool | TyChar | TyInt(..) | TyUint(..) | TyFloat(..) |
-        TyStr | TyArray(..) | TySlice(..) | TyBareFn(..) | TyTuple(..) |
-        TyParam(..) | TyError |
+        TyStr | TyArray(..) | TySlice(..) | TyFnDef(..) | TyFnPtr(_) |
+        TyTuple(..) | TyParam(..) | TyError |
         TyRawPtr(_) | TyRef(_, _) | TyProjection(..) => {
             None
         }
@@ -385,11 +385,12 @@ impl<'a, 'tcx> CoherenceChecker<'a, 'tcx> {
 
             let infcx = new_infer_ctxt(tcx, &tcx.tables, Some(param_env));
 
+            let origin = TypeOrigin::Misc(span);
             let check_mutbl = |mt_a: ty::TypeAndMut<'tcx>, mt_b: ty::TypeAndMut<'tcx>,
                                mk_ptr: &Fn(Ty<'tcx>) -> Ty<'tcx>| {
                 if (mt_a.mutbl, mt_b.mutbl) == (hir::MutImmutable, hir::MutMutable) {
-                    infcx.report_mismatched_types(span, mk_ptr(mt_b.ty),
-                                                  target, &ty::error::TypeError::Mutability);
+                    infcx.report_mismatched_types(origin, mk_ptr(mt_b.ty),
+                                                  target, ty::error::TypeError::Mutability);
                 }
                 (mt_a.ty, mt_b.ty, unsize_trait, None)
             };
@@ -418,7 +419,6 @@ impl<'a, 'tcx> CoherenceChecker<'a, 'tcx> {
                         return;
                     }
 
-                    let origin = TypeOrigin::Misc(span);
                     let fields = &def_a.struct_variant().fields;
                     let diff_fields = fields.iter().enumerate().filter_map(|(i, f)| {
                         let (a, b) = (f.ty(tcx, substs_a), f.ty(tcx, substs_b));
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index b3305fdb9a0..3ce03e24578 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -543,6 +543,8 @@ fn convert_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
                               sig, untransformed_rcvr_ty);
 
     let def_id = ccx.tcx.map.local_def_id(id);
+    let substs = ccx.tcx.mk_substs(mk_item_substs(ccx, &ty_generics));
+
     let ty_method = ty::Method::new(name,
                                     ty_generics,
                                     ty_generic_predicates,
@@ -552,8 +554,7 @@ fn convert_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
                                     def_id,
                                     container);
 
-    let fty = ccx.tcx.mk_fn(Some(def_id),
-                            ccx.tcx.mk_bare_fn(ty_method.fty.clone()));
+    let fty = ccx.tcx.mk_fn_def(def_id, substs, ty_method.fty.clone());
     debug!("method {} (id {}) has type {:?}",
             name, id, fty);
     ccx.tcx.register_item_type(def_id, TypeScheme {
@@ -715,17 +716,13 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) {
             tcx.register_item_type(def_id,
                                    TypeScheme { generics: ty_generics.clone(),
                                                 ty: selfty });
-            if let &Some(ref ast_trait_ref) = opt_trait_ref {
-                tcx.impl_trait_refs.borrow_mut().insert(
-                    def_id,
-                    Some(astconv::instantiate_mono_trait_ref(&ccx.icx(&ty_predicates),
-                                                             &ExplicitRscope,
-                                                             ast_trait_ref,
-                                                             Some(selfty)))
-                        );
-            } else {
-                tcx.impl_trait_refs.borrow_mut().insert(def_id, None);
-            }
+            let trait_ref = opt_trait_ref.as_ref().map(|ast_trait_ref| {
+                astconv::instantiate_mono_trait_ref(&ccx.icx(&ty_predicates),
+                                                    &ExplicitRscope,
+                                                    ast_trait_ref,
+                                                    Some(selfty))
+            });
+            tcx.impl_trait_refs.borrow_mut().insert(def_id, trait_ref);
 
             enforce_impl_params_are_constrained(tcx, generics, &mut ty_predicates, def_id);
             tcx.predicates.borrow_mut().insert(def_id, ty_predicates.clone());
@@ -902,7 +899,7 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) {
             }
 
             if !struct_def.is_struct() {
-                convert_variant_ctor(tcx, struct_def.id(), variant, scheme, predicates);
+                convert_variant_ctor(ccx, struct_def.id(), variant, scheme, predicates);
             }
         },
         hir::ItemTy(_, ref generics) => {
@@ -920,11 +917,12 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) {
     }
 }
 
-fn convert_variant_ctor<'a, 'tcx>(tcx: &TyCtxt<'tcx>,
+fn convert_variant_ctor<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
                                   ctor_id: ast::NodeId,
                                   variant: ty::VariantDef<'tcx>,
                                   scheme: ty::TypeScheme<'tcx>,
                                   predicates: ty::GenericPredicates<'tcx>) {
+    let tcx = ccx.tcx;
     let ctor_ty = match variant.kind() {
         VariantKind::Unit | VariantKind::Struct => scheme.ty,
         VariantKind::Tuple => {
@@ -933,9 +931,17 @@ fn convert_variant_ctor<'a, 'tcx>(tcx: &TyCtxt<'tcx>,
                 .iter()
                 .map(|field| field.unsubst_ty())
                 .collect();
-            tcx.mk_ctor_fn(tcx.map.local_def_id(ctor_id),
-                           &inputs[..],
-                           scheme.ty)
+            let def_id = tcx.map.local_def_id(ctor_id);
+            let substs = tcx.mk_substs(mk_item_substs(ccx, &scheme.generics));
+            tcx.mk_fn_def(def_id, substs, ty::BareFnTy {
+                unsafety: hir::Unsafety::Normal,
+                abi: abi::Abi::Rust,
+                sig: ty::Binder(ty::FnSig {
+                    inputs: inputs,
+                    output: ty::FnConverging(scheme.ty),
+                    variadic: false
+                })
+            })
         }
     };
     write_ty_to_tcx(tcx, ctor_id, ctor_ty);
@@ -961,7 +967,7 @@ fn convert_enum_variant_types<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
         // Convert the ctor, if any. This also registers the variant as
         // an item.
         convert_variant_ctor(
-            ccx.tcx,
+            ccx,
             variant.node.data.id(),
             ty_variant,
             scheme.clone(),
@@ -1436,7 +1442,9 @@ fn compute_type_scheme_of_item<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
         hir::ItemFn(ref decl, unsafety, _, abi, ref generics, _) => {
             let ty_generics = ty_generics_for_fn(ccx, generics, &ty::Generics::empty());
             let tofd = astconv::ty_of_bare_fn(&ccx.icx(generics), unsafety, abi, &decl);
-            let ty = tcx.mk_fn(Some(ccx.tcx.map.local_def_id(it.id)), tcx.mk_bare_fn(tofd));
+            let def_id = ccx.tcx.map.local_def_id(it.id);
+            let substs = tcx.mk_substs(mk_item_substs(ccx, &ty_generics));
+            let ty = tcx.mk_fn_def(def_id, substs, tofd);
             ty::TypeScheme { ty: ty, generics: ty_generics }
         }
         hir::ItemTy(ref t, ref generics) => {
@@ -1556,7 +1564,9 @@ fn compute_type_scheme_of_foreign_item<'a, 'tcx>(
 {
     match it.node {
         hir::ForeignItemFn(ref fn_decl, ref generics) => {
-            compute_type_scheme_of_foreign_fn_decl(ccx, fn_decl, generics, abi)
+            compute_type_scheme_of_foreign_fn_decl(
+                ccx, ccx.tcx.map.local_def_id(it.id),
+                fn_decl, generics, abi)
         }
         hir::ForeignItemStatic(ref t, _) => {
             ty::TypeScheme {
@@ -2107,6 +2117,7 @@ fn conv_param_bounds<'a,'tcx>(astconv: &AstConv<'tcx>,
 
 fn compute_type_scheme_of_foreign_fn_decl<'a, 'tcx>(
     ccx: &CrateCtxt<'a, 'tcx>,
+    id: DefId,
     decl: &hir::FnDecl,
     ast_generics: &hir::Generics,
     abi: abi::Abi)
@@ -2140,14 +2151,14 @@ fn compute_type_scheme_of_foreign_fn_decl<'a, 'tcx>(
             ty::FnDiverging
     };
 
-    let t_fn = ccx.tcx.mk_fn(None,
-        ccx.tcx.mk_bare_fn(ty::BareFnTy {
-            abi: abi,
-            unsafety: hir::Unsafety::Unsafe,
-            sig: ty::Binder(ty::FnSig {inputs: input_tys,
-                                       output: output,
-                                       variadic: decl.variadic}),
-        }));
+    let substs = ccx.tcx.mk_substs(mk_item_substs(ccx, &ty_generics));
+    let t_fn = ccx.tcx.mk_fn_def(id, substs, ty::BareFnTy {
+        abi: abi,
+        unsafety: hir::Unsafety::Unsafe,
+        sig: ty::Binder(ty::FnSig {inputs: input_tys,
+                                    output: output,
+                                    variadic: decl.variadic}),
+    });
 
     ty::TypeScheme {
         generics: ty_generics,
diff --git a/src/librustc_typeck/constrained_type_params.rs b/src/librustc_typeck/constrained_type_params.rs
index 336bff26e2c..907ee15c41b 100644
--- a/src/librustc_typeck/constrained_type_params.rs
+++ b/src/librustc_typeck/constrained_type_params.rs
@@ -72,7 +72,8 @@ fn parameters_for_type_shallow<'tcx>(ty: Ty<'tcx>) -> Vec<Parameter> {
             parameters_for_regions_in_substs(&pi.trait_ref.substs),
         ty::TyBool | ty::TyChar | ty::TyInt(..) | ty::TyUint(..) |
         ty::TyFloat(..) | ty::TyBox(..) | ty::TyStr |
-        ty::TyArray(..) | ty::TySlice(..) | ty::TyBareFn(..) |
+        ty::TyArray(..) | ty::TySlice(..) |
+        ty::TyFnDef(..) | ty::TyFnPtr(_) |
         ty::TyTuple(..) | ty::TyRawPtr(..) |
         ty::TyInfer(..) | ty::TyClosure(..) | ty::TyError =>
             vec![]
diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs
index a103cbc928b..035f8c60500 100644
--- a/src/librustc_typeck/lib.rs
+++ b/src/librustc_typeck/lib.rs
@@ -103,7 +103,7 @@ use dep_graph::DepNode;
 use front::map as hir_map;
 use middle::def::Def;
 use middle::infer::{self, TypeOrigin};
-use middle::subst;
+use middle::subst::Substs;
 use middle::ty::{self, Ty, TyCtxt, TypeFoldable};
 use session::{config, CompileResult};
 use util::common::time;
@@ -128,7 +128,7 @@ pub mod coherence;
 pub mod variance;
 
 pub struct TypeAndSubsts<'tcx> {
-    pub substs: subst::Substs<'tcx>,
+    pub substs: Substs<'tcx>,
     pub ty: Ty<'tcx>,
 }
 
@@ -220,7 +220,7 @@ fn check_main_fn_ty(ccx: &CrateCtxt,
     let tcx = ccx.tcx;
     let main_t = tcx.node_id_to_type(main_id);
     match main_t.sty {
-        ty::TyBareFn(..) => {
+        ty::TyFnDef(..) => {
             match tcx.map.find(main_id) {
                 Some(hir_map::NodeItem(it)) => {
                     match it.node {
@@ -236,7 +236,8 @@ fn check_main_fn_ty(ccx: &CrateCtxt,
                 _ => ()
             }
             let main_def_id = tcx.map.local_def_id(main_id);
-            let se_ty = tcx.mk_fn(Some(main_def_id), tcx.mk_bare_fn(ty::BareFnTy {
+            let substs = tcx.mk_substs(Substs::empty());
+            let se_ty = tcx.mk_fn_def(main_def_id, substs, ty::BareFnTy {
                 unsafety: hir::Unsafety::Normal,
                 abi: Abi::Rust,
                 sig: ty::Binder(ty::FnSig {
@@ -244,7 +245,7 @@ fn check_main_fn_ty(ccx: &CrateCtxt,
                     output: ty::FnConverging(tcx.mk_nil()),
                     variadic: false
                 })
-            }));
+            });
 
             require_same_types(tcx, None, false, main_span, main_t, se_ty,
                 || {
@@ -266,7 +267,7 @@ fn check_start_fn_ty(ccx: &CrateCtxt,
     let tcx = ccx.tcx;
     let start_t = tcx.node_id_to_type(start_id);
     match start_t.sty {
-        ty::TyBareFn(..) => {
+        ty::TyFnDef(..) => {
             match tcx.map.find(start_id) {
                 Some(hir_map::NodeItem(it)) => {
                     match it.node {
@@ -282,8 +283,9 @@ fn check_start_fn_ty(ccx: &CrateCtxt,
                 _ => ()
             }
 
-            let se_ty = tcx.mk_fn(Some(ccx.tcx.map.local_def_id(start_id)),
-                                  tcx.mk_bare_fn(ty::BareFnTy {
+            let start_def_id = ccx.tcx.map.local_def_id(start_id);
+            let substs = tcx.mk_substs(Substs::empty());
+            let se_ty = tcx.mk_fn_def(start_def_id, substs, ty::BareFnTy {
                 unsafety: hir::Unsafety::Normal,
                 abi: Abi::Rust,
                 sig: ty::Binder(ty::FnSig {
@@ -294,7 +296,7 @@ fn check_start_fn_ty(ccx: &CrateCtxt,
                     output: ty::FnConverging(tcx.types.isize),
                     variadic: false,
                 }),
-            }));
+            });
 
             require_same_types(tcx, None, false, start_span, start_t, se_ty,
                 || {
diff --git a/src/librustc_typeck/variance/constraints.rs b/src/librustc_typeck/variance/constraints.rs
index 0c9fa9fd0ab..aecc588c3e2 100644
--- a/src/librustc_typeck/variance/constraints.rs
+++ b/src/librustc_typeck/variance/constraints.rs
@@ -429,7 +429,8 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
                 }
             }
 
-            ty::TyBareFn(_, &ty::BareFnTy { ref sig, .. }) => {
+            ty::TyFnDef(_, _, &ty::BareFnTy { ref sig, .. }) |
+            ty::TyFnPtr(&ty::BareFnTy { ref sig, .. }) => {
                 self.add_constraints_from_sig(generics, sig, variance);
             }
 
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index 32f3706675a..c14e4af8103 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -164,7 +164,7 @@ pub fn build_external_trait(cx: &DocContext, tcx: &TyCtxt,
 fn build_external_function(cx: &DocContext, tcx: &TyCtxt, did: DefId) -> clean::Function {
     let t = tcx.lookup_item_type(did);
     let (decl, style, abi) = match t.ty.sty {
-        ty::TyBareFn(_, ref f) => ((did, &f.sig).clean(cx), f.unsafety, f.abi),
+        ty::TyFnDef(_, _, ref f) => ((did, &f.sig).clean(cx), f.unsafety, f.abi),
         _ => panic!("bad function"),
     };
 
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 6c9ee528782..5921093bcac 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -1663,7 +1663,8 @@ impl<'tcx> Clean<Type> for ty::Ty<'tcx> {
                 mutability: mt.mutbl.clean(cx),
                 type_: box mt.ty.clean(cx),
             },
-            ty::TyBareFn(_, ref fty) => BareFunction(box BareFunctionDecl {
+            ty::TyFnDef(_, _, ref fty) |
+            ty::TyFnPtr(ref fty) => BareFunction(box BareFunctionDecl {
                 unsafety: fty.unsafety,
                 generics: Generics {
                     lifetimes: Vec::new(),
diff --git a/src/test/auxiliary/issue13507.rs b/src/test/auxiliary/issue13507.rs
index 78d0394a6e5..4cb846b5186 100644
--- a/src/test/auxiliary/issue13507.rs
+++ b/src/test/auxiliary/issue13507.rs
@@ -14,26 +14,26 @@ pub mod testtypes {
     use std::any::TypeId;
 
     pub fn type_ids() -> Vec<TypeId> {
-        let mut ids = vec!();
-        ids.push(TypeId::of::<FooNil>());
-        ids.push(TypeId::of::<FooBool>());
-        ids.push(TypeId::of::<FooInt>());
-        ids.push(TypeId::of::<FooUint>());
-        ids.push(TypeId::of::<FooFloat>());
-        ids.push(TypeId::of::<FooEnum>());
-        ids.push(TypeId::of::<FooUniq>());
-        ids.push(TypeId::of::<FooPtr>());
-        ids.push(TypeId::of::<&'static FooTrait>());
-        ids.push(TypeId::of::<FooStruct>());
-        ids.push(TypeId::of::<FooTuple>());
-        ids
+        vec![
+            TypeId::of::<FooBool>(),
+            TypeId::of::<FooInt>(),
+            TypeId::of::<FooUint>(),
+            TypeId::of::<FooFloat>(),
+            TypeId::of::<FooStr>(),
+            TypeId::of::<FooArray>(),
+            TypeId::of::<FooSlice>(),
+            TypeId::of::<FooBox>(),
+            TypeId::of::<FooPtr>(),
+            TypeId::of::<FooRef>(),
+            TypeId::of::<FooFnPtr>(),
+            TypeId::of::<FooNil>(),
+            TypeId::of::<FooTuple>(),
+            TypeId::of::<FooTrait>(),
+            TypeId::of::<FooStruct>(),
+            TypeId::of::<FooEnum>()
+        ]
     }
 
-    // Tests ty_nil
-    pub type FooNil = ();
-
-    // Skipping ty_bot
-
     // Tests TyBool
     pub type FooBool = bool;
 
@@ -49,25 +49,26 @@ pub mod testtypes {
     // Tests TyFloat (does not test all variants of FloatTy)
     pub type FooFloat = f64;
 
-    // For TyStr, what kind of string should I use? &'static str? String? Raw str?
+    // Tests TyStr
+    pub type FooStr = str;
 
-    // Tests TyEnum
-    pub enum FooEnum {
-        VarA(usize),
-        VarB(usize, usize)
-    }
+    // Tests TyArray
+    pub type FooArray = [u8; 1];
 
-    // Tests TyBox (of u8)
-    pub type FooUniq = Box<u8>;
+    // Tests TySlice
+    pub type FooSlice = [u8];
 
-    // As with TyStr, what type should be used for TyArray?
+    // Tests TyBox (of u8)
+    pub type FooBox = Box<u8>;
 
     // Tests TyRawPtr
     pub type FooPtr = *const u8;
 
-    // Skipping TyRef
+    // Tests TyRef
+    pub type FooRef = &'static u8;
 
-    // Skipping TyBareFn (how do you get a bare function type, rather than proc or closure?)
+    // Tests TyFnPtr
+    pub type FooFnPtr = fn(u8) -> bool;
 
     // Tests TyTrait
     pub trait FooTrait {
@@ -80,14 +81,17 @@ pub mod testtypes {
         foo_field: usize
     }
 
+    // Tests TyEnum
+    pub enum FooEnum {
+        VarA(usize),
+        VarB(usize, usize)
+    }
+
     // Tests TyTuple
+    pub type FooNil = ();
     pub type FooTuple = (u8, i8, bool);
 
-    // Skipping ty_param
-
-    // Skipping ty_self
-
-    // Skipping ty_self
+    // Skipping TyParam
 
     // Skipping TyInfer
 
diff --git a/src/test/compile-fail/fn-item-type.rs b/src/test/compile-fail/fn-item-type.rs
index 5015810ff47..2fbd1ddb1e6 100644
--- a/src/test/compile-fail/fn-item-type.rs
+++ b/src/test/compile-fail/fn-item-type.rs
@@ -11,23 +11,37 @@
 // Test that the types of distinct fn items are not compatible by
 // default. See also `run-pass/fn-item-type-*.rs`.
 
-fn foo(x: isize) -> isize { x * 2 }
-fn bar(x: isize) -> isize { x * 4 }
+fn foo<T>(x: isize) -> isize { x * 2 }
+fn bar<T>(x: isize) -> isize { x * 4 }
 
 fn eq<T>(x: T, y: T) { }
 
+trait Foo { fn foo() { /* this is a default fn */ } }
+impl<T> Foo for T { /* `foo` is still default here */ }
+
 fn main() {
-    let f = if true { foo } else { bar };
-    //~^ ERROR if and else have incompatible types
-    //~| expected `fn(isize) -> isize {foo}`
-    //~| found `fn(isize) -> isize {bar}`
-    //~| expected fn item,
-    //~| found a different fn item
-
-    eq(foo, bar);
+    eq(foo::<u8>, bar::<u8>);
     //~^ ERROR mismatched types
-    //~|  expected `fn(isize) -> isize {foo}`
-    //~|  found `fn(isize) -> isize {bar}`
+    //~|  expected `fn(isize) -> isize {foo::<u8>}`
+    //~|  found `fn(isize) -> isize {bar::<u8>}`
     //~|  expected fn item
     //~|  found a different fn item
+
+    eq(foo::<u8>, foo::<i8>);
+    //~^ ERROR mismatched types
+    //~|  expected `fn(isize) -> isize {foo::<u8>}`
+    //~|  found `fn(isize) -> isize {foo::<i8>}`
+
+    eq(bar::<String>, bar::<Vec<u8>>);
+    //~^ ERROR mismatched types
+    //~|  expected `fn(isize) -> isize {bar::<collections::string::String>}`
+    //~|  found `fn(isize) -> isize {bar::<collections::vec::Vec<u8>>}`
+    //~|  expected struct `collections::string::String`
+    //~|  found struct `collections::vec::Vec`
+
+    // Make sure we distinguish between trait methods correctly.
+    eq(<u8 as Foo>::foo, <u16 as Foo>::foo);
+    //~^ ERROR mismatched types
+    //~|  expected `fn() {Foo::foo}`
+    //~|  found `fn() {Foo::foo}`
 }
diff --git a/src/test/compile-fail/invalid-intrinsic.rs b/src/test/compile-fail/invalid-intrinsic.rs
new file mode 100644
index 00000000000..2aa2546cb9f
--- /dev/null
+++ b/src/test/compile-fail/invalid-intrinsic.rs
@@ -0,0 +1,16 @@
+// 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(intrinsics)]
+extern "rust-intrinsic" {
+    pub static breakpoint : unsafe extern "rust-intrinsic" fn();
+    //~^ ERROR intrinsic has wrong type
+}
+fn main() { unsafe { breakpoint(); } }
\ No newline at end of file
diff --git a/src/test/compile-fail/issue-13482-2.rs b/src/test/compile-fail/issue-13482-2.rs
index f907be161fa..e1fe2d06993 100644
--- a/src/test/compile-fail/issue-13482-2.rs
+++ b/src/test/compile-fail/issue-13482-2.rs
@@ -17,7 +17,7 @@ fn main() {
     let y = match x {
         [] => None,
 //~^ ERROR mismatched types
-//~| expected `[_#0i; 2]`
+//~| expected `[_#1i; 2]`
 //~| found `[_#7t; 0]`
 //~| expected an array with a fixed size of 2 elements
 //~| found one with 0 elements
diff --git a/src/test/compile-fail/issue-17728.rs b/src/test/compile-fail/issue-17728.rs
index 83e52216be2..787eb7a3b88 100644
--- a/src/test/compile-fail/issue-17728.rs
+++ b/src/test/compile-fail/issue-17728.rs
@@ -107,7 +107,7 @@ impl Debug for Player {
 }
 
 fn str_to_direction(to_parse: &str) -> RoomDirection {
-    match to_parse {
+    match to_parse { //~ ERROR match arms have incompatible types
         "w" | "west" => RoomDirection::West,
         "e" | "east" => RoomDirection::East,
         "n" | "north" => RoomDirection::North,
@@ -116,7 +116,7 @@ fn str_to_direction(to_parse: &str) -> RoomDirection {
         "out" => RoomDirection::Out,
         "up" => RoomDirection::Up,
         "down" => RoomDirection::Down,
-        _ => None //~ ERROR mismatched types
+        _ => None //~ NOTE match arm with an incompatible type
     }
 }
 
diff --git a/src/test/compile-fail/issue-2149.rs b/src/test/compile-fail/issue-2149.rs
index 256c5d8e6f7..9143a226a24 100644
--- a/src/test/compile-fail/issue-2149.rs
+++ b/src/test/compile-fail/issue-2149.rs
@@ -21,5 +21,5 @@ impl<A> vec_monad<A> for Vec<A> {
 }
 fn main() {
     ["hi"].bind(|x| [x] );
-    //~^ ERROR no method named `bind` found for type `[&str; 1]` in the current scope
+    //~^ ERROR no method named `bind` found for type `[&'static str; 1]` in the current scope
 }
diff --git a/src/test/compile-fail/transmute-from-fn-item-types-lint.rs b/src/test/compile-fail/transmute-from-fn-item-types-lint.rs
new file mode 100644
index 00000000000..3eae76f9492
--- /dev/null
+++ b/src/test/compile-fail/transmute-from-fn-item-types-lint.rs
@@ -0,0 +1,51 @@
+// 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.
+
+use std::mem;
+
+unsafe fn foo() -> (isize, *const (), Option<fn()>) {
+    let i = mem::transmute(bar);
+    //~^ WARN is now zero-sized and has to be cast to a pointer before transmuting
+    //~^^ WARN was previously accepted
+
+    let p = mem::transmute(foo);
+    //~^ WARN is now zero-sized and has to be cast to a pointer before transmuting
+    //~^^ WARN was previously accepted
+
+    let of = mem::transmute(main);
+    //~^ WARN is now zero-sized and has to be cast to a pointer before transmuting
+    //~^^ WARN was previously accepted
+
+    (i, p, of)
+}
+
+unsafe fn bar() {
+    mem::transmute::<_, *mut ()>(foo);
+    //~^ WARN is now zero-sized and has to be cast to a pointer before transmuting
+    //~^^ WARN was previously accepted
+
+    mem::transmute::<_, fn()>(bar);
+    //~^ WARN is now zero-sized and has to be cast to a pointer before transmuting
+    //~^^ WARN was previously accepted
+
+    // No error if a coercion would otherwise occur.
+    mem::transmute::<fn(), usize>(main);
+
+    // Error, still, if the resulting type is not pointer-sized.
+    mem::transmute::<_, u8>(main);
+    //~^ ERROR transmute called with differently sized types
+}
+
+fn main() {
+    unsafe {
+        foo();
+        bar();
+    }
+}
diff --git a/src/test/compile-fail/variadic-ffi-3.rs b/src/test/compile-fail/variadic-ffi-3.rs
index 94055450bc6..1d5ebdbae3e 100644
--- a/src/test/compile-fail/variadic-ffi-3.rs
+++ b/src/test/compile-fail/variadic-ffi-3.rs
@@ -22,7 +22,7 @@ fn main() {
         let x: unsafe extern "C" fn(f: isize, x: u8) = foo;
         //~^ ERROR: mismatched types
         //~| expected `unsafe extern "C" fn(isize, u8)`
-        //~| found `unsafe extern "C" fn(isize, u8, ...)`
+        //~| found `unsafe extern "C" fn(isize, u8, ...) {foo}`
         //~| expected non-variadic fn
         //~| found variadic function
 
diff --git a/src/test/pretty/issue-4264.pp b/src/test/pretty/issue-4264.pp
index 835f7fc96c6..0347631aeb3 100644
--- a/src/test/pretty/issue-4264.pp
+++ b/src/test/pretty/issue-4264.pp
@@ -86,8 +86,10 @@ pub fn id<T>(x: T) -> T { (x as T) }
 pub fn use_id() {
     let _ =
         ((id::<[i32; (3 as usize)]> as
-             fn([i32; 3]) -> [i32; 3] {id})(([(1 as i32), (2 as i32),
-                                              (3 as i32)] as [i32; 3])) as
+             fn([i32; 3]) -> [i32; 3] {id::<[i32; 3]>})(([(1 as i32),
+                                                          (2 as i32),
+                                                          (3 as i32)] as
+                                                            [i32; 3])) as
             [i32; 3]);
 }
 fn main() { }
diff --git a/src/test/run-pass/coerce-unify.rs b/src/test/run-pass/coerce-unify.rs
new file mode 100644
index 00000000000..3d690146931
--- /dev/null
+++ b/src/test/run-pass/coerce-unify.rs
@@ -0,0 +1,77 @@
+// 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.
+
+// Check that coercions can unify if-else, match arms and array elements.
+
+// Try to construct if-else chains, matches and arrays out of given expressions.
+macro_rules! check {
+    ($last:expr $(, $rest:expr)+) => {
+        // Last expression comes first because of whacky ifs and matches.
+        let _ = $(if false { $rest })else+ else { $last };
+
+        let _ = match 0 { $(_ if false => $rest,)+ _ => $last };
+
+        let _ = [$($rest,)+ $last];
+    }
+}
+
+// Check all non-uniform cases of 2 and 3 expressions of 2 types.
+macro_rules! check2 {
+    ($a:expr, $b:expr) => {
+        check!($a, $b);
+        check!($b, $a);
+
+        check!($a, $a, $b);
+        check!($a, $b, $a);
+        check!($a, $b, $b);
+
+        check!($b, $a, $a);
+        check!($b, $a, $b);
+        check!($b, $b, $a);
+    }
+}
+
+// Check all non-uniform cases of 2 and 3 expressions of 3 types.
+macro_rules! check3 {
+    ($a:expr, $b:expr, $c:expr) => {
+        // Delegate to check2 for cases where a type repeats.
+        check2!($a, $b);
+        check2!($b, $c);
+        check2!($a, $c);
+
+        // Check the remaining cases, i.e. permutations of ($a, $b, $c).
+        check!($a, $b, $c);
+        check!($a, $c, $b);
+        check!($b, $a, $c);
+        check!($b, $c, $a);
+        check!($c, $a, $b);
+        check!($c, $b, $a);
+    }
+}
+
+use std::mem::size_of;
+
+fn foo() {}
+fn bar() {}
+
+pub fn main() {
+    check3!(foo, bar, foo as fn());
+    check3!(size_of::<u8>, size_of::<u16>, size_of::<usize> as fn() -> usize);
+
+    let s = String::from("bar");
+    check2!("foo", &s);
+
+    let a = [1, 2, 3];
+    let v = vec![1, 2, 3];
+    check2!(&a[..], &v);
+
+    // Make sure in-array coercion still works.
+    let _ = [("a", Default::default()), (Default::default(), "b"), (&s, &s)];
+}
diff --git a/src/test/run-pass/enum-clike-ffi-as-int.rs b/src/test/run-pass/enum-clike-ffi-as-int.rs
index 8be3634c88a..fdaad9e1fab 100644
--- a/src/test/run-pass/enum-clike-ffi-as-int.rs
+++ b/src/test/run-pass/enum-clike-ffi-as-int.rs
@@ -25,16 +25,17 @@
 
 #[repr(u32)]
 enum Foo {
-  A = 0,
-  B = 23
+    A = 0,
+    B = 23
 }
 
 #[inline(never)]
 extern "C" fn foo(_x: usize) -> Foo { Foo::B }
 
 pub fn main() {
-  unsafe {
-    let f: extern "C" fn(usize) -> u32 = ::std::mem::transmute(foo);
-    assert_eq!(f(0xDEADBEEF), Foo::B as u32);
-  }
+    unsafe {
+        let f: extern "C" fn(usize) -> u32 =
+            ::std::mem::transmute(foo as extern "C" fn(usize) -> Foo);
+        assert_eq!(f(0xDEADBEEF), Foo::B as u32);
+    }
 }
diff --git a/src/test/run-pass/fn-item-type-zero-sized.rs b/src/test/run-pass/fn-item-type-zero-sized.rs
new file mode 100644
index 00000000000..5fdaf083d07
--- /dev/null
+++ b/src/test/run-pass/fn-item-type-zero-sized.rs
@@ -0,0 +1,22 @@
+// 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.
+
+// Test that fn item types are zero-sized.
+
+use std::mem::{size_of, size_of_val};
+
+fn main() {
+    assert_eq!(size_of_val(&main), 0);
+
+    let (a, b) = (size_of::<u8>, size_of::<u16>);
+    assert_eq!(size_of_val(&a), 0);
+    assert_eq!(size_of_val(&b), 0);
+    assert_eq!((a(), b()), (1, 2));
+}
diff --git a/src/test/run-pass/issue-13507-2.rs b/src/test/run-pass/issue-13507-2.rs
index 91ec3e85404..084b7a166cd 100644
--- a/src/test/run-pass/issue-13507-2.rs
+++ b/src/test/run-pass/issue-13507-2.rs
@@ -19,23 +19,29 @@ use issue13507::testtypes;
 use std::any::TypeId;
 
 pub fn type_ids() -> Vec<TypeId> {
-    let mut ids = vec!();
-    ids.push(TypeId::of::<testtypes::FooNil>());
-    ids.push(TypeId::of::<testtypes::FooBool>());
-    ids.push(TypeId::of::<testtypes::FooInt>());
-    ids.push(TypeId::of::<testtypes::FooUint>());
-    ids.push(TypeId::of::<testtypes::FooFloat>());
-    ids.push(TypeId::of::<testtypes::FooEnum>());
-    ids.push(TypeId::of::<testtypes::FooUniq>());
-    ids.push(TypeId::of::<testtypes::FooPtr>());
-    ids.push(TypeId::of::<&'static testtypes::FooTrait>());
-    ids.push(TypeId::of::<testtypes::FooStruct>());
-    ids.push(TypeId::of::<testtypes::FooTuple>());
-    ids
+    use issue13507::testtypes::*;
+    vec![
+        TypeId::of::<FooBool>(),
+        TypeId::of::<FooInt>(),
+        TypeId::of::<FooUint>(),
+        TypeId::of::<FooFloat>(),
+        TypeId::of::<FooStr>(),
+        TypeId::of::<FooArray>(),
+        TypeId::of::<FooSlice>(),
+        TypeId::of::<FooBox>(),
+        TypeId::of::<FooPtr>(),
+        TypeId::of::<FooRef>(),
+        TypeId::of::<FooFnPtr>(),
+        TypeId::of::<FooNil>(),
+        TypeId::of::<FooTuple>(),
+        TypeId::of::<FooTrait>(),
+        TypeId::of::<FooStruct>(),
+        TypeId::of::<FooEnum>()
+    ]
 }
 
 pub fn main() {
-    let othercrate = testtypes::type_ids();
+    let othercrate = issue13507::testtypes::type_ids();
     let thiscrate = type_ids();
     assert_eq!(thiscrate, othercrate);
 }
diff --git a/src/test/run-pass/mir_refs_correct.rs b/src/test/run-pass/mir_refs_correct.rs
index 93953e3f58a..67baf2f9c49 100644
--- a/src/test/run-pass/mir_refs_correct.rs
+++ b/src/test/run-pass/mir_refs_correct.rs
@@ -204,48 +204,41 @@ fn t24() -> fn(u8) -> S {
     C4
 }
 
-fn main(){
-    unsafe {
-        assert_eq!(t1()(), regular());
-
-        assert!(::std::mem::transmute::<_, *mut ()>(t2()) ==
-                ::std::mem::transmute::<_, *mut ()>(E::U));
-        assert!(::std::mem::transmute::<_, *mut ()>(t3()) ==
-                ::std::mem::transmute::<_, *mut ()>(S));
-
-        assert_eq!(t4()(), S::hey());
-        let s = S(42);
-        assert_eq!(t5()(&s), <S as X>::hoy(&s));
-
-
-        assert_eq!(t6()(), ext::regular_fn());
-        assert!(::std::mem::transmute::<_, *mut ()>(t7()) ==
-                ::std::mem::transmute::<_, *mut ()>(ext::E::U));
-        assert!(::std::mem::transmute::<_, *mut ()>(t8()) ==
-                ::std::mem::transmute::<_, *mut ()>(ext::S));
-
-        assert_eq!(t9()(), ext::S::hey());
-        let sext = ext::S(6);
-        assert_eq!(t10()(&sext), <ext::S as ext::X>::hoy(&sext));
-
-        let p = parametric::<u8>;
-        assert!(::std::mem::transmute::<_, *mut ()>(t11()) ==
-                ::std::mem::transmute::<_, *mut ()>(p));
-
-        assert_eq!(t12(), C);
-        assert_eq!(t13(), C2);
-        assert_eq!(t13_2(), C3);
-
-        assert_eq!(t14()(), <S as X>::hoy2());
-        assert_eq!(t15()(&s), S::hey2(&s));
-        assert_eq!(t16()(10u32, 20u32), F::f(10u32, 20u32));
-        assert_eq!(t17()(30u32, 10u64), F::f(30u32, 10u64));
-        assert_eq!(t18()(50u64, 5u64), F::f(50u64, 5u64));
-        assert_eq!(t19()(322u64, 2u32), F::f(322u64, 2u32));
-        assert_eq!(t20()(123u64, 38u32), <u32 as T<_, _>>::staticmeth(123, 38));
-        assert_eq!(t21(), Unit);
-        assert_eq!(t22(), None);
-        assert_eq!(t23(), (CEnum::A, CEnum::B));
-        assert_eq!(t24(), C4);
-    }
+fn main() {
+    assert_eq!(t1()(), regular());
+
+    assert_eq!(t2() as *mut (), E::U as *mut ());
+    assert_eq!(t3() as *mut (), S as *mut ());
+
+    assert_eq!(t4()(), S::hey());
+    let s = S(42);
+    assert_eq!(t5()(&s), <S as X>::hoy(&s));
+
+
+    assert_eq!(t6()(), ext::regular_fn());
+    assert_eq!(t7() as *mut (), ext::E::U as *mut ());
+    assert_eq!(t8() as *mut (), ext::S as *mut ());
+
+    assert_eq!(t9()(), ext::S::hey());
+    let sext = ext::S(6);
+    assert_eq!(t10()(&sext), <ext::S as ext::X>::hoy(&sext));
+
+    let p = parametric::<u8>;
+    assert_eq!(t11() as *mut (), p as *mut ());
+
+    assert_eq!(t12(), C);
+    assert_eq!(t13(), C2);
+    assert_eq!(t13_2(), C3);
+
+    assert_eq!(t14()(), <S as X>::hoy2());
+    assert_eq!(t15()(&s), S::hey2(&s));
+    assert_eq!(t16()(10u32, 20u32), F::f(10u32, 20u32));
+    assert_eq!(t17()(30u32, 10u64), F::f(30u32, 10u64));
+    assert_eq!(t18()(50u64, 5u64), F::f(50u64, 5u64));
+    assert_eq!(t19()(322u64, 2u32), F::f(322u64, 2u32));
+    assert_eq!(t20()(123u64, 38u32), <u32 as T<_, _>>::staticmeth(123, 38));
+    assert_eq!(t21(), Unit);
+    assert_eq!(t22(), None);
+    assert_eq!(t23(), (CEnum::A, CEnum::B));
+    assert_eq!(t24(), C4);
 }
diff --git a/src/test/run-pass/nullable-pointer-ffi-compat.rs b/src/test/run-pass/nullable-pointer-ffi-compat.rs
index 224111900d6..2b7cf6c6682 100644
--- a/src/test/run-pass/nullable-pointer-ffi-compat.rs
+++ b/src/test/run-pass/nullable-pointer-ffi-compat.rs
@@ -24,13 +24,14 @@
 use std::mem;
 
 #[inline(never)]
-extern "C" fn foo<'a>(x: &'a isize) -> Option<&'a isize> { Some(x) }
+extern "C" fn foo(x: &isize) -> Option<&isize> { Some(x) }
 
 static FOO: isize = 0xDEADBEE;
 
 pub fn main() {
     unsafe {
-        let f: for<'a> extern "C" fn(&'a isize) -> &'a isize = mem::transmute(foo);
+        let f: extern "C" fn(&isize) -> &isize =
+            mem::transmute(foo as extern "C" fn(&isize) -> Option<&isize>);
         assert_eq!(*f(&FOO), FOO);
     }
 }
diff --git a/src/test/run-pass/nullable-pointer-iotareduction.rs b/src/test/run-pass/nullable-pointer-iotareduction.rs
index cfd3bb49f34..dffdcfe0af5 100644
--- a/src/test/run-pass/nullable-pointer-iotareduction.rs
+++ b/src/test/run-pass/nullable-pointer-iotareduction.rs
@@ -12,8 +12,6 @@
 #![allow(unknown_features)]
 #![feature(box_syntax)]
 
-use std::{option, mem};
-
 // Iota-reduction is a rule in the Calculus of (Co-)Inductive Constructions,
 // which "says that a destructor applied to an object built from a constructor
 // behaves as expected".  -- http://coq.inria.fr/doc/Reference-Manual006.html
@@ -43,9 +41,9 @@ macro_rules! check_option {
         check_option!($e, $T, |ptr| assert_eq!(*ptr, $e));
     }};
     ($e:expr, $T:ty, |$v:ident| $chk:expr) => {{
-        assert!(option::Option::None::<$T>.is_none());
+        assert!(None::<$T>.is_none());
         let e = $e;
-        let s_ = option::Option::Some::<$T>(e);
+        let s_ = Some::<$T>(e);
         let $v = s_.as_ref().unwrap();
         $chk
     }}
@@ -78,9 +76,8 @@ pub fn main() {
     check_type!(&17, &isize);
     check_type!(box 18, Box<isize>);
     check_type!("foo".to_string(), String);
-    check_type!(vec!(20, 22), Vec<isize> );
-    let mint: usize = unsafe { mem::transmute(main) };
+    check_type!(vec!(20, 22), Vec<isize>);
     check_type!(main, fn(), |pthing| {
-        assert_eq!(mint, unsafe { mem::transmute(*pthing) })
+        assert_eq!(main as fn(), *pthing as fn())
     });
 }
diff --git a/src/test/run-pass/transmute-from-fn-item-types.rs b/src/test/run-pass/transmute-from-fn-item-types.rs
new file mode 100644
index 00000000000..574a90e2ad6
--- /dev/null
+++ b/src/test/run-pass/transmute-from-fn-item-types.rs
@@ -0,0 +1,27 @@
+// 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.
+
+#![allow(transmute_from_fn_item_types)]
+
+use std::mem;
+
+fn main() {
+    unsafe {
+        let u = mem::transmute(main);
+        let p = mem::transmute(main);
+        let f = mem::transmute(main);
+        let tuple: (usize, *mut (), fn()) = (u, p, f);
+        assert_eq!(mem::transmute::<_, [usize; 3]>(tuple), [main as usize; 3]);
+
+        mem::transmute::<_, usize>(main);
+        mem::transmute::<_, *mut ()>(main);
+        mem::transmute::<_, fn()>(main);
+    }
+}