about summary refs log tree commit diff
path: root/src/librustc_mir
diff options
context:
space:
mode:
Diffstat (limited to 'src/librustc_mir')
-rw-r--r--src/librustc_mir/build/expr/into.rs9
-rw-r--r--src/librustc_mir/hair/cx/expr.rs450
2 files changed, 153 insertions, 306 deletions
diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs
index 5982d3bdc81..d456bc3ded3 100644
--- a/src/librustc_mir/build/expr/into.rs
+++ b/src/librustc_mir/build/expr/into.rs
@@ -201,13 +201,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 exit_block.unit()
             }
             ExprKind::Call { ty, fun, args } => {
-                let diverges = match ty.sty {
-                    ty::TyFnDef(_, _, ref f) | ty::TyFnPtr(ref f) => {
-                        // FIXME(canndrew): This is_never should probably be an is_uninhabited
-                        f.output().skip_binder().is_never()
-                    }
-                    _ => false
-                };
+                // FIXME(canndrew): This is_never should probably be an is_uninhabited
+                let diverges = expr.ty.is_never();
                 let intrinsic = match ty.sty {
                     ty::TyFnDef(def_id, _, ref f) if
                         f.abi() == Abi::RustIntrinsic ||
diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs
index b180d982e86..2c3f5196926 100644
--- a/src/librustc_mir/hair/cx/expr.rs
+++ b/src/librustc_mir/hair/cx/expr.rs
@@ -17,9 +17,10 @@ use hair::cx::to_ref::ToRef;
 use rustc::hir::def::{Def, CtorKind};
 use rustc::middle::const_val::ConstVal;
 use rustc::ty::{self, AdtKind, VariantDef, Ty};
+use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow};
 use rustc::ty::cast::CastKind as TyCastKind;
+use rustc::ty::subst::Subst;
 use rustc::hir;
-use syntax::ptr::P;
 
 impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
     type Output = Expr<'tcx>;
@@ -31,175 +32,13 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
         debug!("Expr::make_mirror(): id={}, span={:?}", self.id, self.span);
 
         let mut expr = make_mirror_unadjusted(cx, self);
-        let adj = cx.tables().adjustments.get(&self.id).cloned();
-
-        debug!("make_mirror: unadjusted-expr={:?} applying adjustments={:?}",
-               expr,
-               adj);
 
         // Now apply adjustments, if any.
-        match adj.map(|adj| (adj.kind, adj.target)) {
-            None => {}
-            Some((ty::adjustment::Adjust::ReifyFnPointer, adjusted_ty)) => {
-                expr = Expr {
-                    temp_lifetime: temp_lifetime,
-                    temp_lifetime_was_shrunk: was_shrunk,
-                    ty: adjusted_ty,
-                    span: self.span,
-                    kind: ExprKind::ReifyFnPointer { source: expr.to_ref() },
-                };
-            }
-            Some((ty::adjustment::Adjust::UnsafeFnPointer, adjusted_ty)) => {
-                expr = Expr {
-                    temp_lifetime: temp_lifetime,
-                    temp_lifetime_was_shrunk: was_shrunk,
-                    ty: adjusted_ty,
-                    span: self.span,
-                    kind: ExprKind::UnsafeFnPointer { source: expr.to_ref() },
-                };
-            }
-            Some((ty::adjustment::Adjust::ClosureFnPointer, adjusted_ty)) => {
-                expr = Expr {
-                    temp_lifetime: temp_lifetime,
-                    temp_lifetime_was_shrunk: was_shrunk,
-                    ty: adjusted_ty,
-                    span: self.span,
-                    kind: ExprKind::ClosureFnPointer { source: expr.to_ref() },
-                };
-            }
-            Some((ty::adjustment::Adjust::NeverToAny, adjusted_ty)) => {
-                expr = Expr {
-                    temp_lifetime: temp_lifetime,
-                    temp_lifetime_was_shrunk: was_shrunk,
-                    ty: adjusted_ty,
-                    span: self.span,
-                    kind: ExprKind::NeverToAny { source: expr.to_ref() },
-                };
-            }
-            Some((ty::adjustment::Adjust::MutToConstPointer, adjusted_ty)) => {
-                expr = Expr {
-                    temp_lifetime: temp_lifetime,
-                    temp_lifetime_was_shrunk: was_shrunk,
-                    ty: adjusted_ty,
-                    span: self.span,
-                    kind: ExprKind::Cast { source: expr.to_ref() },
-                };
-            }
-            Some((ty::adjustment::Adjust::DerefRef { autoderefs, autoref, unsize },
-                  adjusted_ty)) => {
-                for i in 0..autoderefs {
-                    let i = i as u32;
-                    let adjusted_ty =
-                        expr.ty.adjust_for_autoderef(cx.tcx, self.id, self.span, i, |mc| {
-                            cx.tables().method_map.get(&mc).map(|m| m.ty)
-                        });
-                    debug!("make_mirror: autoderef #{}, adjusted_ty={:?}",
-                           i,
-                           adjusted_ty);
-                    let method_key = ty::MethodCall::autoderef(self.id, i);
-                    let meth_ty = cx.tables().method_map.get(&method_key).map(|m| m.ty);
-                    let kind = if let Some(meth_ty) = meth_ty {
-                        debug!("make_mirror: overloaded autoderef (meth_ty={:?})", meth_ty);
-
-                        let ref_ty = cx.tcx.no_late_bound_regions(&meth_ty.fn_ret());
-                        let (region, mutbl) = match ref_ty {
-                            Some(&ty::TyS { sty: ty::TyRef(region, mt), .. }) => (region, mt.mutbl),
-                            _ => span_bug!(expr.span, "autoderef returned bad type"),
-                        };
-
-                        expr = Expr {
-                            temp_lifetime: temp_lifetime,
-                            temp_lifetime_was_shrunk: was_shrunk,
-                            ty: cx.tcx.mk_ref(region,
-                                              ty::TypeAndMut {
-                                                  ty: expr.ty,
-                                                  mutbl: mutbl,
-                                              }),
-                            span: expr.span,
-                            kind: ExprKind::Borrow {
-                                region: region,
-                                borrow_kind: to_borrow_kind(mutbl),
-                                arg: expr.to_ref(),
-                            },
-                        };
-
-                        overloaded_lvalue(cx,
-                                          self,
-                                          method_key,
-                                          PassArgs::ByRef,
-                                          expr.to_ref(),
-                                          vec![])
-                    } else {
-                        debug!("make_mirror: built-in autoderef");
-                        ExprKind::Deref { arg: expr.to_ref() }
-                    };
-                    expr = Expr {
-                        temp_lifetime: temp_lifetime,
-                        temp_lifetime_was_shrunk: was_shrunk,
-                        ty: adjusted_ty,
-                        span: self.span,
-                        kind: kind,
-                    };
-                }
-
-                if let Some(autoref) = autoref {
-                    let adjusted_ty = expr.ty.adjust_for_autoref(cx.tcx, Some(autoref));
-                    match autoref {
-                        ty::adjustment::AutoBorrow::Ref(r, m) => {
-                            expr = Expr {
-                                temp_lifetime: temp_lifetime,
-                                temp_lifetime_was_shrunk: was_shrunk,
-                                ty: adjusted_ty,
-                                span: self.span,
-                                kind: ExprKind::Borrow {
-                                    region: r,
-                                    borrow_kind: to_borrow_kind(m),
-                                    arg: expr.to_ref(),
-                                },
-                            };
-                        }
-                        ty::adjustment::AutoBorrow::RawPtr(m) => {
-                            // Convert this to a suitable `&foo` and
-                            // then an unsafe coercion. Limit the region to be just this
-                            // expression.
-                            let region = ty::ReScope(expr_extent);
-                            let region = cx.tcx.mk_region(region);
-                            expr = Expr {
-                                temp_lifetime: temp_lifetime,
-                                temp_lifetime_was_shrunk: was_shrunk,
-                                ty: cx.tcx.mk_ref(region,
-                                                  ty::TypeAndMut {
-                                                      ty: expr.ty,
-                                                      mutbl: m,
-                                                  }),
-                                span: self.span,
-                                kind: ExprKind::Borrow {
-                                    region: region,
-                                    borrow_kind: to_borrow_kind(m),
-                                    arg: expr.to_ref(),
-                                },
-                            };
-                            expr = Expr {
-                                temp_lifetime: temp_lifetime,
-                                temp_lifetime_was_shrunk: was_shrunk,
-                                ty: adjusted_ty,
-                                span: self.span,
-                                kind: ExprKind::Cast { source: expr.to_ref() },
-                            };
-                        }
-                    }
-                }
-
-                if unsize {
-                    expr = Expr {
-                        temp_lifetime: temp_lifetime,
-                        temp_lifetime_was_shrunk: was_shrunk,
-                        ty: adjusted_ty,
-                        span: self.span,
-                        kind: ExprKind::Unsize { source: expr.to_ref() },
-                    };
-                }
-            }
+        for adjustment in cx.tables().expr_adjustments(self) {
+            debug!("make_mirror: expr={:?} applying adjustment={:?}",
+                   expr,
+                   adjustment);
+            expr = apply_adjustment(cx, self, expr, adjustment);
         }
 
         // Next, wrap this up in the expr's scope.
@@ -233,6 +72,96 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
     }
 }
 
+fn apply_adjustment<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
+                                    hir_expr: &'tcx hir::Expr,
+                                    mut expr: Expr<'tcx>,
+                                    adjustment: &Adjustment<'tcx>)
+                                    -> Expr<'tcx> {
+    let Expr { temp_lifetime, temp_lifetime_was_shrunk, span, .. } = expr;
+    let kind = match adjustment.kind {
+        Adjust::ReifyFnPointer => {
+            ExprKind::ReifyFnPointer { source: expr.to_ref() }
+        }
+        Adjust::UnsafeFnPointer => {
+            ExprKind::UnsafeFnPointer { source: expr.to_ref() }
+        }
+        Adjust::ClosureFnPointer => {
+            ExprKind::ClosureFnPointer { source: expr.to_ref() }
+        }
+        Adjust::NeverToAny => {
+            ExprKind::NeverToAny { source: expr.to_ref() }
+        }
+        Adjust::MutToConstPointer => {
+            ExprKind::Cast { source: expr.to_ref() }
+        }
+        Adjust::Deref(None) => {
+            ExprKind::Deref { arg: expr.to_ref() }
+        }
+        Adjust::Deref(Some(deref)) => {
+            let call = deref.method_call(cx.tcx, expr.ty);
+
+            expr = Expr {
+                temp_lifetime,
+                temp_lifetime_was_shrunk,
+                ty: cx.tcx.mk_ref(deref.region,
+                                  ty::TypeAndMut {
+                                    ty: expr.ty,
+                                    mutbl: deref.mutbl,
+                                  }),
+                span,
+                kind: ExprKind::Borrow {
+                    region: deref.region,
+                    borrow_kind: to_borrow_kind(deref.mutbl),
+                    arg: expr.to_ref(),
+                },
+            };
+
+            overloaded_lvalue(cx, hir_expr, adjustment.target, Some(call), vec![expr.to_ref()])
+        }
+        Adjust::Borrow(AutoBorrow::Ref(r, m)) => {
+            ExprKind::Borrow {
+                region: r,
+                borrow_kind: to_borrow_kind(m),
+                arg: expr.to_ref(),
+            }
+        }
+        Adjust::Borrow(AutoBorrow::RawPtr(m)) => {
+            // Convert this to a suitable `&foo` and
+            // then an unsafe coercion. Limit the region to be just this
+            // expression.
+            let region = ty::ReScope(CodeExtent::Misc(hir_expr.id));
+            let region = cx.tcx.mk_region(region);
+            expr = Expr {
+                temp_lifetime,
+                temp_lifetime_was_shrunk,
+                ty: cx.tcx.mk_ref(region,
+                                  ty::TypeAndMut {
+                                    ty: expr.ty,
+                                    mutbl: m,
+                                  }),
+                span,
+                kind: ExprKind::Borrow {
+                    region: region,
+                    borrow_kind: to_borrow_kind(m),
+                    arg: expr.to_ref(),
+                },
+            };
+            ExprKind::Cast { source: expr.to_ref() }
+        }
+        Adjust::Unsize => {
+            ExprKind::Unsize { source: expr.to_ref() }
+        }
+    };
+
+    Expr {
+        temp_lifetime,
+        temp_lifetime_was_shrunk,
+        ty: adjustment.target,
+        span,
+        kind,
+    }
+}
+
 fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
                                           expr: &'tcx hir::Expr)
                                           -> Expr<'tcx> {
@@ -243,7 +172,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
         // Here comes the interesting stuff:
         hir::ExprMethodCall(.., ref args) => {
             // Rewrite a.b(c) into UFCS form like Trait::b(a, c)
-            let expr = method_callee(cx, expr, ty::MethodCall::expr(expr.id));
+            let expr = method_callee(cx, expr, None);
             let args = args.iter()
                 .map(|e| e.to_ref())
                 .collect();
@@ -255,7 +184,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
         }
 
         hir::ExprCall(ref fun, ref args) => {
-            if cx.tables().is_method_call(expr.id) {
+            if cx.tables().is_method_call(expr) {
                 // The callee is something implementing Fn, FnMut, or FnOnce.
                 // Find the actual method implementation being called and
                 // build the appropriate UFCS call expression with the
@@ -263,18 +192,11 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
 
                 // rewrite f(u, v) into FnOnce::call_once(f, (u, v))
 
-                let method = method_callee(cx, expr, ty::MethodCall::expr(expr.id));
-
-                let sig = method.ty.fn_sig();
-
-                let sig = cx.tcx
-                    .no_late_bound_regions(&sig)
-                    .unwrap_or_else(|| span_bug!(expr.span, "method call has late-bound regions"));
-
-                assert_eq!(sig.inputs().len(), 2);
+                let method = method_callee(cx, expr, None);
 
+                let arg_tys = args.iter().map(|e| cx.tables().expr_ty_adjusted(e));
                 let tupled_args = Expr {
-                    ty: sig.inputs()[1],
+                    ty: cx.tcx.mk_tup(arg_tys, false),
                     temp_lifetime: temp_lifetime,
                     temp_lifetime_was_shrunk: was_shrunk,
                     span: expr.span,
@@ -302,8 +224,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
                     None
                 };
                 if let Some((adt_def, index)) = adt_data {
-                    let substs = cx.tables().node_id_item_substs(fun.id)
-                        .unwrap_or_else(|| cx.tcx.intern_substs(&[]));
+                    let substs = cx.tables().node_substs(fun.id);
                     let field_refs = args.iter()
                         .enumerate()
                         .map(|(idx, e)| {
@@ -352,18 +273,8 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
         }
 
         hir::ExprAssignOp(op, ref lhs, ref rhs) => {
-            if cx.tables().is_method_call(expr.id) {
-                let pass_args = if op.node.is_by_value() {
-                    PassArgs::ByValue
-                } else {
-                    PassArgs::ByRef
-                };
-                overloaded_operator(cx,
-                                    expr,
-                                    ty::MethodCall::expr(expr.id),
-                                    pass_args,
-                                    lhs.to_ref(),
-                                    vec![rhs])
+            if cx.tables().is_method_call(expr) {
+                overloaded_operator(cx, expr, vec![lhs.to_ref(), rhs.to_ref()])
             } else {
                 ExprKind::AssignOp {
                     op: bin_op(op.node),
@@ -376,18 +287,8 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
         hir::ExprLit(..) => ExprKind::Literal { literal: cx.const_eval_literal(expr) },
 
         hir::ExprBinary(op, ref lhs, ref rhs) => {
-            if cx.tables().is_method_call(expr.id) {
-                let pass_args = if op.node.is_by_value() {
-                    PassArgs::ByValue
-                } else {
-                    PassArgs::ByRef
-                };
-                overloaded_operator(cx,
-                                    expr,
-                                    ty::MethodCall::expr(expr.id),
-                                    pass_args,
-                                    lhs.to_ref(),
-                                    vec![rhs])
+            if cx.tables().is_method_call(expr) {
+                overloaded_operator(cx, expr, vec![lhs.to_ref(), rhs.to_ref()])
             } else {
                 // FIXME overflow
                 match (op.node, cx.constness) {
@@ -436,13 +337,8 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
         }
 
         hir::ExprIndex(ref lhs, ref index) => {
-            if cx.tables().is_method_call(expr.id) {
-                overloaded_lvalue(cx,
-                                  expr,
-                                  ty::MethodCall::expr(expr.id),
-                                  PassArgs::ByValue,
-                                  lhs.to_ref(),
-                                  vec![index])
+            if cx.tables().is_method_call(expr) {
+                overloaded_lvalue(cx, expr, expr_ty, None, vec![lhs.to_ref(), index.to_ref()])
             } else {
                 ExprKind::Index {
                     lhs: lhs.to_ref(),
@@ -452,26 +348,16 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
         }
 
         hir::ExprUnary(hir::UnOp::UnDeref, ref arg) => {
-            if cx.tables().is_method_call(expr.id) {
-                overloaded_lvalue(cx,
-                                  expr,
-                                  ty::MethodCall::expr(expr.id),
-                                  PassArgs::ByValue,
-                                  arg.to_ref(),
-                                  vec![])
+            if cx.tables().is_method_call(expr) {
+                overloaded_lvalue(cx, expr, expr_ty, None, vec![arg.to_ref()])
             } else {
                 ExprKind::Deref { arg: arg.to_ref() }
             }
         }
 
         hir::ExprUnary(hir::UnOp::UnNot, ref arg) => {
-            if cx.tables().is_method_call(expr.id) {
-                overloaded_operator(cx,
-                                    expr,
-                                    ty::MethodCall::expr(expr.id),
-                                    PassArgs::ByValue,
-                                    arg.to_ref(),
-                                    vec![])
+            if cx.tables().is_method_call(expr) {
+                overloaded_operator(cx, expr, vec![arg.to_ref()])
             } else {
                 ExprKind::Unary {
                     op: UnOp::Not,
@@ -481,13 +367,8 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
         }
 
         hir::ExprUnary(hir::UnOp::UnNeg, ref arg) => {
-            if cx.tables().is_method_call(expr.id) {
-                overloaded_operator(cx,
-                                    expr,
-                                    ty::MethodCall::expr(expr.id),
-                                    PassArgs::ByValue,
-                                    arg.to_ref(),
-                                    vec![])
+            if cx.tables().is_method_call(expr) {
+                overloaded_operator(cx, expr, vec![arg.to_ref()])
             } else {
                 // FIXME runtime-overflow
                 if let hir::ExprLit(_) = arg.node {
@@ -703,18 +584,21 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
 
 fn method_callee<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
                                  expr: &hir::Expr,
-                                 method_call: ty::MethodCall)
+                                 custom_callee: Option<(DefId, &'tcx Substs<'tcx>)>)
                                  -> Expr<'tcx> {
-    let callee = cx.tables().method_map[&method_call];
     let (temp_lifetime, was_shrunk) = cx.region_maps.temporary_scope2(expr.id);
+    let (def_id, substs) = custom_callee.unwrap_or_else(|| {
+        (cx.tables().type_dependent_defs[&expr.id].def_id(),
+         cx.tables().node_substs(expr.id))
+    });
     Expr {
         temp_lifetime: temp_lifetime,
         temp_lifetime_was_shrunk: was_shrunk,
-        ty: callee.ty,
+        ty: cx.tcx.type_of(def_id).subst(cx.tcx, substs),
         span: expr.span,
         kind: ExprKind::Literal {
             literal: Literal::Value {
-                value: ConstVal::Function(callee.def_id, callee.substs),
+                value: ConstVal::Function(def_id, substs),
             },
         },
     }
@@ -739,8 +623,7 @@ fn convert_path_expr<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
                                      expr: &'tcx hir::Expr,
                                      def: Def)
                                      -> ExprKind<'tcx> {
-    let substs = cx.tables().node_id_item_substs(expr.id)
-        .unwrap_or_else(|| cx.tcx.intern_substs(&[]));
+    let substs = cx.tables().node_substs(expr.id);
     match def {
         // A regular function, constructor function or a constant.
         Def::Fn(def_id) |
@@ -941,90 +824,59 @@ fn bin_op(op: hir::BinOp_) -> BinOp {
     }
 }
 
-enum PassArgs {
-    ByValue,
-    ByRef,
-}
-
 fn overloaded_operator<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
                                        expr: &'tcx hir::Expr,
-                                       method_call: ty::MethodCall,
-                                       pass_args: PassArgs,
-                                       receiver: ExprRef<'tcx>,
-                                       args: Vec<&'tcx P<hir::Expr>>)
+                                       args: Vec<ExprRef<'tcx>>)
                                        -> ExprKind<'tcx> {
-    // the receiver has all the adjustments that are needed, so we can
-    // just push a reference to it
-    let mut argrefs = vec![receiver];
-
-    // the arguments, unfortunately, do not, so if this is a ByRef
-    // operator, we have to gin up the autorefs (but by value is easy)
-    match pass_args {
-        PassArgs::ByValue => argrefs.extend(args.iter().map(|arg| arg.to_ref())),
-
-        PassArgs::ByRef => {
-            let region = cx.tcx.node_scope_region(expr.id);
-            let (temp_lifetime, was_shrunk) =
-                cx.region_maps.temporary_scope2(expr.id);
-            argrefs.extend(args.iter()
-                .map(|arg| {
-                    let arg_ty = cx.tables().expr_ty_adjusted(arg);
-                    let adjusted_ty = cx.tcx.mk_ref(region,
-                                                    ty::TypeAndMut {
-                                                        ty: arg_ty,
-                                                        mutbl: hir::MutImmutable,
-                                                    });
-                    Expr {
-                        temp_lifetime: temp_lifetime,
-                        temp_lifetime_was_shrunk: was_shrunk,
-                        ty: adjusted_ty,
-                        span: expr.span,
-                        kind: ExprKind::Borrow {
-                            region: region,
-                            borrow_kind: BorrowKind::Shared,
-                            arg: arg.to_ref(),
-                        },
-                    }
-                    .to_ref()
-                }))
-        }
-    }
-
-    // now create the call itself
-    let fun = method_callee(cx, expr, method_call);
+    let fun = method_callee(cx, expr, None);
     ExprKind::Call {
         ty: fun.ty,
         fun: fun.to_ref(),
-        args: argrefs,
+        args,
     }
 }
 
 fn overloaded_lvalue<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
                                      expr: &'tcx hir::Expr,
-                                     method_call: ty::MethodCall,
-                                     pass_args: PassArgs,
-                                     receiver: ExprRef<'tcx>,
-                                     args: Vec<&'tcx P<hir::Expr>>)
+                                     lvalue_ty: Ty<'tcx>,
+                                     custom_callee: Option<(DefId, &'tcx Substs<'tcx>)>,
+                                     args: Vec<ExprRef<'tcx>>)
                                      -> ExprKind<'tcx> {
     // For an overloaded *x or x[y] expression of type T, the method
     // call returns an &T and we must add the deref so that the types
     // line up (this is because `*x` and `x[y]` represent lvalues):
 
-    // to find the type &T of the content returned by the method;
-    let ref_ty = cx.tables().method_map[&method_call].ty.fn_ret();
-    let ref_ty = cx.tcx.no_late_bound_regions(&ref_ty).unwrap();
-    // callees always have all late-bound regions fully instantiated,
+    let recv_ty = match args[0] {
+        ExprRef::Hair(e) => cx.tables().expr_ty_adjusted(e),
+        ExprRef::Mirror(ref e) => e.ty
+    };
+
+    // Reconstruct the output assuming it's a reference with the
+    // same region and mutability as the receiver. This holds for
+    // `Deref(Mut)::Deref(_mut)` and `Index(Mut)::index(_mut)`.
+    let (region, mt) = match recv_ty.sty {
+        ty::TyRef(region, mt) => (region, mt),
+        _ => span_bug!(expr.span, "overloaded_lvalue: receiver is not a reference"),
+    };
+    let ref_ty = cx.tcx.mk_ref(region, ty::TypeAndMut {
+        ty: lvalue_ty,
+        mutbl: mt.mutbl,
+    });
 
     // construct the complete expression `foo()` for the overloaded call,
     // which will yield the &T type
     let (temp_lifetime, was_shrunk) = cx.region_maps.temporary_scope2(expr.id);
-    let ref_kind = overloaded_operator(cx, expr, method_call, pass_args, receiver, args);
+    let fun = method_callee(cx, expr, custom_callee);
     let ref_expr = Expr {
         temp_lifetime: temp_lifetime,
         temp_lifetime_was_shrunk: was_shrunk,
         ty: ref_ty,
         span: expr.span,
-        kind: ref_kind,
+        kind: ExprKind::Call {
+            ty: fun.ty,
+            fun: fun.to_ref(),
+            args,
+        },
     };
 
     // construct and return a deref wrapper `*foo()`