about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEduard-Mihai Burtescu <edy.burt@gmail.com>2017-05-28 15:04:13 +0300
committerEduard-Mihai Burtescu <edy.burt@gmail.com>2017-06-01 08:59:47 +0300
commit3ce443828be823cac2791d2b8392c33db9320677 (patch)
tree2d77afc66e961aacbebd03d429d1456a39469840
parent194fe695e3af6f03953cbb4ca66f159993f6214d (diff)
downloadrust-3ce443828be823cac2791d2b8392c33db9320677.tar.gz
rust-3ce443828be823cac2791d2b8392c33db9320677.zip
rustc: adjust the RHS of comparison operators instead of assuming autorefs.
-rw-r--r--src/librustc/middle/expr_use_visitor.rs100
-rw-r--r--src/librustc_mir/hair/cx/expr.rs129
-rw-r--r--src/librustc_typeck/check/mod.rs11
-rw-r--r--src/librustc_typeck/check/op.rs102
-rw-r--r--src/librustc_typeck/check/regionck.rs83
-rw-r--r--src/librustc_typeck/check/writeback.rs9
6 files changed, 114 insertions, 320 deletions
diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs
index 55c3049155f..bb56439a157 100644
--- a/src/librustc/middle/expr_use_visitor.rs
+++ b/src/librustc/middle/expr_use_visitor.rs
@@ -263,12 +263,6 @@ macro_rules! return_if_err {
     )
 }
 
-/// Whether the elements of an overloaded operation are passed by value or by reference
-enum PassArgs {
-    ByValue,
-    ByRef,
-}
-
 impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
     pub fn new(delegate: &'a mut (Delegate<'tcx>+'a),
                region_maps: &'a RegionMaps,
@@ -382,9 +376,7 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
             }
 
             hir::ExprUnary(hir::UnDeref, ref base) => {      // *base
-                if !self.walk_overloaded_operator(expr, &base, Vec::new(), PassArgs::ByRef) {
-                    self.select_from_expr(&base);
-                }
+                self.select_from_expr(&base);
             }
 
             hir::ExprField(ref base, _) => {         // base.f
@@ -396,13 +388,8 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
             }
 
             hir::ExprIndex(ref lhs, ref rhs) => {       // lhs[rhs]
-                if !self.walk_overloaded_operator(expr,
-                                                  &lhs,
-                                                  vec![&rhs],
-                                                  PassArgs::ByValue) {
-                    self.select_from_expr(&lhs);
-                    self.consume_expr(&rhs);
-                }
+                self.select_from_expr(&lhs);
+                self.consume_expr(&rhs);
             }
 
             hir::ExprCall(ref callee, ref args) => {    // callee(args)
@@ -485,29 +472,13 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
                 self.walk_block(&blk);
             }
 
-            hir::ExprUnary(op, ref lhs) => {
-                let pass_args = if op.is_by_value() {
-                    PassArgs::ByValue
-                } else {
-                    PassArgs::ByRef
-                };
-
-                if !self.walk_overloaded_operator(expr, &lhs, Vec::new(), pass_args) {
-                    self.consume_expr(&lhs);
-                }
+            hir::ExprUnary(_, ref lhs) => {
+                self.consume_expr(&lhs);
             }
 
-            hir::ExprBinary(op, ref lhs, ref rhs) => {
-                let pass_args = if op.node.is_by_value() {
-                    PassArgs::ByValue
-                } else {
-                    PassArgs::ByRef
-                };
-
-                if !self.walk_overloaded_operator(expr, &lhs, vec![&rhs], pass_args) {
-                    self.consume_expr(&lhs);
-                    self.consume_expr(&rhs);
-                }
+            hir::ExprBinary(_, ref lhs, ref rhs) => {
+                self.consume_expr(&lhs);
+                self.consume_expr(&rhs);
             }
 
             hir::ExprBlock(ref blk) => {
@@ -529,14 +500,13 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
                 self.consume_expr(&base);
             }
 
-            hir::ExprAssignOp(op, ref lhs, ref rhs) => {
-                // NB All our assignment operations take the RHS by value
-                assert!(op.node.is_by_value());
-
-                if !self.walk_overloaded_operator(expr, lhs, vec![rhs], PassArgs::ByValue) {
+            hir::ExprAssignOp(_, ref lhs, ref rhs) => {
+                if self.mc.infcx.tables.borrow().is_method_call(expr) {
+                    self.consume_expr(lhs);
+                } else {
                     self.mutate_expr(expr, &lhs, MutateMode::WriteAndRead);
-                    self.consume_expr(&rhs);
                 }
+                self.consume_expr(&rhs);
             }
 
             hir::ExprRepeat(ref base, _) => {
@@ -784,50 +754,6 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
         }
     }
 
-
-    // When this returns true, it means that the expression *is* a
-    // method-call (i.e. via the operator-overload).  This true result
-    // also implies that walk_overloaded_operator already took care of
-    // recursively processing the input arguments, and thus the caller
-    // should not do so.
-    fn walk_overloaded_operator(&mut self,
-                                expr: &hir::Expr,
-                                receiver: &hir::Expr,
-                                rhs: Vec<&hir::Expr>,
-                                pass_args: PassArgs)
-                                -> bool
-    {
-        if !self.mc.infcx.tables.borrow().is_method_call(expr) {
-            return false;
-        }
-
-        match pass_args {
-            PassArgs::ByValue => {
-                self.consume_expr(receiver);
-                for &arg in &rhs {
-                    self.consume_expr(arg);
-                }
-
-                return true;
-            },
-            PassArgs::ByRef => {},
-        }
-
-        self.walk_expr(receiver);
-
-        // Arguments (but not receivers) to overloaded operator
-        // methods are implicitly autoref'd which sadly does not use
-        // adjustments, so we must hardcode the borrow here.
-
-        let r = self.tcx().node_scope_region(expr.id);
-        let bk = ty::ImmBorrow;
-
-        for &arg in &rhs {
-            self.borrow_expr(arg, r, bk, OverloadedOperator);
-        }
-        return true;
-    }
-
     fn arm_move_mode(&mut self, discr_cmt: mc::cmt<'tcx>, arm: &hir::Arm) -> TrackMatchMode {
         let mut mode = Unknown;
         for pat in &arm.pats {
diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs
index 6d1509e7e24..2c3f5196926 100644
--- a/src/librustc_mir/hair/cx/expr.rs
+++ b/src/librustc_mir/hair/cx/expr.rs
@@ -21,7 +21,6 @@ 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>;
@@ -117,13 +116,7 @@ fn apply_adjustment<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
                 },
             };
 
-            overloaded_lvalue(cx,
-                              hir_expr,
-                              adjustment.target,
-                              Some(call),
-                              PassArgs::ByValue,
-                              expr.to_ref(),
-                              vec![])
+            overloaded_lvalue(cx, hir_expr, adjustment.target, Some(call), vec![expr.to_ref()])
         }
         Adjust::Borrow(AutoBorrow::Ref(r, m)) => {
             ExprKind::Borrow {
@@ -281,17 +274,7 @@ 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) {
-                let pass_args = if op.node.is_by_value() {
-                    PassArgs::ByValue
-                } else {
-                    PassArgs::ByRef
-                };
-                overloaded_operator(cx,
-                                    expr,
-                                    None,
-                                    pass_args,
-                                    lhs.to_ref(),
-                                    vec![rhs])
+                overloaded_operator(cx, expr, vec![lhs.to_ref(), rhs.to_ref()])
             } else {
                 ExprKind::AssignOp {
                     op: bin_op(op.node),
@@ -305,17 +288,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
 
         hir::ExprBinary(op, ref lhs, ref rhs) => {
             if cx.tables().is_method_call(expr) {
-                let pass_args = if op.node.is_by_value() {
-                    PassArgs::ByValue
-                } else {
-                    PassArgs::ByRef
-                };
-                overloaded_operator(cx,
-                                    expr,
-                                    None,
-                                    pass_args,
-                                    lhs.to_ref(),
-                                    vec![rhs])
+                overloaded_operator(cx, expr, vec![lhs.to_ref(), rhs.to_ref()])
             } else {
                 // FIXME overflow
                 match (op.node, cx.constness) {
@@ -365,13 +338,7 @@ 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) {
-                overloaded_lvalue(cx,
-                                  expr,
-                                  expr_ty,
-                                  None,
-                                  PassArgs::ByValue,
-                                  lhs.to_ref(),
-                                  vec![index])
+                overloaded_lvalue(cx, expr, expr_ty, None, vec![lhs.to_ref(), index.to_ref()])
             } else {
                 ExprKind::Index {
                     lhs: lhs.to_ref(),
@@ -382,13 +349,7 @@ 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) {
-                overloaded_lvalue(cx,
-                                  expr,
-                                  expr_ty,
-                                  None,
-                                  PassArgs::ByValue,
-                                  arg.to_ref(),
-                                  vec![])
+                overloaded_lvalue(cx, expr, expr_ty, None, vec![arg.to_ref()])
             } else {
                 ExprKind::Deref { arg: arg.to_ref() }
             }
@@ -396,12 +357,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
 
         hir::ExprUnary(hir::UnOp::UnNot, ref arg) => {
             if cx.tables().is_method_call(expr) {
-                overloaded_operator(cx,
-                                    expr,
-                                    None,
-                                    PassArgs::ByValue,
-                                    arg.to_ref(),
-                                    vec![])
+                overloaded_operator(cx, expr, vec![arg.to_ref()])
             } else {
                 ExprKind::Unary {
                     op: UnOp::Not,
@@ -412,12 +368,7 @@ 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) {
-                overloaded_operator(cx,
-                                    expr,
-                                    None,
-                                    PassArgs::ByValue,
-                                    arg.to_ref(),
-                                    vec![])
+                overloaded_operator(cx, expr, vec![arg.to_ref()])
             } else {
                 // FIXME runtime-overflow
                 if let hir::ExprLit(_) = arg.node {
@@ -873,61 +824,15 @@ 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,
-                                       custom_callee: Option<(DefId, &'tcx Substs<'tcx>)>,
-                                       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, custom_callee);
+    let fun = method_callee(cx, expr, None);
     ExprKind::Call {
         ty: fun.ty,
         fun: fun.to_ref(),
-        args: argrefs,
+        args,
     }
 }
 
@@ -935,15 +840,13 @@ fn overloaded_lvalue<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
                                      expr: &'tcx hir::Expr,
                                      lvalue_ty: Ty<'tcx>,
                                      custom_callee: Option<(DefId, &'tcx Substs<'tcx>)>,
-                                     pass_args: PassArgs,
-                                     receiver: ExprRef<'tcx>,
-                                     args: Vec<&'tcx P<hir::Expr>>)
+                                     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):
 
-    let recv_ty = match receiver {
+    let recv_ty = match args[0] {
         ExprRef::Hair(e) => cx.tables().expr_ty_adjusted(e),
         ExprRef::Mirror(ref e) => e.ty
     };
@@ -963,13 +866,17 @@ fn overloaded_lvalue<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
     // 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, custom_callee, 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()`
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 813e199f85a..32c3f5c8a5e 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -3424,10 +3424,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                                                                                lvalue_pref);
 
             if !oprnd_t.references_error() {
+                oprnd_t = self.structurally_resolved_type(expr.span, oprnd_t);
                 match unop {
                     hir::UnDeref => {
-                        oprnd_t = self.structurally_resolved_type(expr.span, oprnd_t);
-
                         if let Some(mt) = oprnd_t.builtin_deref(true, NoPreference) {
                             oprnd_t = mt.ty;
                         } else if let Some(ok) = self.try_overloaded_deref(
@@ -3450,18 +3449,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                         }
                     }
                     hir::UnNot => {
-                        oprnd_t = self.structurally_resolved_type(oprnd.span,
-                                                                  oprnd_t);
-                        let result = self.check_user_unop(expr, &oprnd, oprnd_t, unop);
+                        let result = self.check_user_unop(expr, oprnd_t, unop);
                         // If it's builtin, we can reuse the type, this helps inference.
                         if !(oprnd_t.is_integral() || oprnd_t.sty == ty::TyBool) {
                             oprnd_t = result;
                         }
                     }
                     hir::UnNeg => {
-                        oprnd_t = self.structurally_resolved_type(oprnd.span,
-                                                                  oprnd_t);
-                        let result = self.check_user_unop(expr, &oprnd, oprnd_t, unop);
+                        let result = self.check_user_unop(expr, oprnd_t, unop);
                         // If it's builtin, we can reuse the type, this helps inference.
                         if !(oprnd_t.is_integral() || oprnd_t.is_fp()) {
                             oprnd_t = result;
diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs
index cbb89355bf9..8e5b7a65469 100644
--- a/src/librustc_typeck/check/op.rs
+++ b/src/librustc_typeck/check/op.rs
@@ -11,11 +11,13 @@
 //! Code related to processing overloaded binary and unary operators.
 
 use super::FnCtxt;
+use super::method::MethodCallee;
 use rustc::ty::{self, Ty, TypeFoldable, PreferMutLvalue, TypeVariants};
 use rustc::ty::TypeVariants::{TyStr, TyRef};
 use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow};
 use rustc::infer::type_variable::TypeVariableOrigin;
 use errors;
+use syntax_pos::Span;
 use syntax::symbol::Symbol;
 use rustc::hir;
 
@@ -181,14 +183,41 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         // particularly for things like `String + &String`.
         let rhs_ty_var = self.next_ty_var(TypeVariableOrigin::MiscVariable(rhs_expr.span));
 
-        let return_ty = self.lookup_op_method(expr, lhs_ty, &[rhs_ty_var],
-                                              Op::Binary(op, is_assign), lhs_expr);
+        let result = self.lookup_op_method(lhs_ty, &[rhs_ty_var], Op::Binary(op, is_assign));
 
         // see `NB` above
         let rhs_ty = self.check_expr_coercable_to_type(rhs_expr, rhs_ty_var);
 
-        let return_ty = match return_ty {
-            Ok(return_ty) => return_ty,
+        let return_ty = match result {
+            Ok(method) => {
+                let by_ref_binop = !op.node.is_by_value();
+                if is_assign == IsAssign::Yes || by_ref_binop {
+                    if let ty::TyRef(region, mt) = method.sig.inputs()[0].sty {
+                        let autoref = Adjustment {
+                            kind: Adjust::Borrow(AutoBorrow::Ref(region, mt.mutbl)),
+                            target: method.sig.inputs()[0]
+                        };
+                        self.apply_adjustments(lhs_expr, vec![autoref]);
+                    }
+                }
+                if by_ref_binop {
+                    if let ty::TyRef(region, mt) = method.sig.inputs()[1].sty {
+                        let autoref = Adjustment {
+                            kind: Adjust::Borrow(AutoBorrow::Ref(region, mt.mutbl)),
+                            target: method.sig.inputs()[1]
+                        };
+                        // HACK(eddyb) Bypass checks due to reborrows being in
+                        // some cases applied on the RHS, on top of which we need
+                        // to autoref, which is not allowed by apply_adjustments.
+                        // self.apply_adjustments(rhs_expr, vec![autoref]);
+                        self.tables.borrow_mut().adjustments.entry(rhs_expr.id)
+                            .or_insert(vec![]).push(autoref);
+                    }
+                }
+                self.write_method_call(expr.id, method);
+
+                method.sig.output()
+            }
             Err(()) => {
                 // error types are considered "builtin"
                 if !lhs_ty.references_error() {
@@ -210,8 +239,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
 
                         if let TypeVariants::TyRef(_, ref ty_mut) = lhs_ty.sty {
                             if !self.infcx.type_moves_by_default(ty_mut.ty, lhs_expr.span) &&
-                                self.lookup_op_method(expr, ty_mut.ty, &[rhs_ty],
-                                                      Op::Binary(op, is_assign), lhs_expr).is_ok() {
+                                self.lookup_op_method(ty_mut.ty, &[rhs_ty],
+                                                      Op::Binary(op, is_assign)).is_ok() {
                                 err.note(
                                     &format!(
                                         "this is a reference to a type that `{}` can be applied \
@@ -298,14 +327,16 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
 
     pub fn check_user_unop(&self,
                            ex: &'gcx hir::Expr,
-                           operand_expr: &'gcx hir::Expr,
                            operand_ty: Ty<'tcx>,
                            op: hir::UnOp)
                            -> Ty<'tcx>
     {
         assert!(op.is_by_value());
-        match self.lookup_op_method(ex, operand_ty, &[], Op::Unary(op), operand_expr) {
-            Ok(t) => t,
+        match self.lookup_op_method(operand_ty, &[], Op::Unary(op, ex.span)) {
+            Ok(method) => {
+                self.write_method_call(ex.id, method);
+                method.sig.output()
+            }
             Err(()) => {
                 let actual = self.resolve_type_vars_if_possible(&operand_ty);
                 if !actual.references_error() {
@@ -318,16 +349,15 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         }
     }
 
-    fn lookup_op_method(&self,
-                        expr: &'gcx hir::Expr,
-                        lhs_ty: Ty<'tcx>,
-                        other_tys: &[Ty<'tcx>],
-                        op: Op,
-                        lhs_expr: &'a hir::Expr)
-                        -> Result<Ty<'tcx>,()>
+    fn lookup_op_method(&self, lhs_ty: Ty<'tcx>, other_tys: &[Ty<'tcx>], op: Op)
+                        -> Result<MethodCallee<'tcx>, ()>
     {
         let lang = &self.tcx.lang_items;
 
+        let span = match op {
+            Op::Binary(op, _) => op.span,
+            Op::Unary(_, span) => span
+        };
         let (opname, trait_did) = if let Op::Binary(op, IsAssign::Yes) = op {
             match op.node {
                 hir::BiAdd => ("add_assign", lang.add_assign_trait()),
@@ -344,7 +374,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                 hir::BiGe | hir::BiGt |
                 hir::BiEq | hir::BiNe |
                 hir::BiAnd | hir::BiOr => {
-                    span_bug!(op.span,
+                    span_bug!(span,
                               "impossible assignment operation: {}=",
                               op.node.as_str())
                 }
@@ -368,28 +398,26 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                 hir::BiEq => ("eq", lang.eq_trait()),
                 hir::BiNe => ("ne", lang.eq_trait()),
                 hir::BiAnd | hir::BiOr => {
-                    span_bug!(op.span, "&& and || are not overloadable")
+                    span_bug!(span, "&& and || are not overloadable")
                 }
             }
-        } else if let Op::Unary(hir::UnNot) = op {
+        } else if let Op::Unary(hir::UnNot, _) = op {
             ("not", lang.not_trait())
-        } else if let Op::Unary(hir::UnNeg) = op {
+        } else if let Op::Unary(hir::UnNeg, _) = op {
             ("neg", lang.neg_trait())
         } else {
             bug!("lookup_op_method: op not supported: {:?}", op)
         };
 
-        debug!("lookup_op_method(expr={:?}, lhs_ty={:?}, opname={:?}, \
-                                 trait_did={:?}, lhs_expr={:?})",
-               expr,
+        debug!("lookup_op_method(lhs_ty={:?}, op={:?}, opname={:?}, trait_did={:?})",
                lhs_ty,
+               op,
                opname,
-               trait_did,
-               lhs_expr);
+               trait_did);
 
         let method = trait_did.and_then(|trait_did| {
             let opname = Symbol::intern(opname);
-            self.lookup_method_in_trait(expr.span, opname, trait_did, lhs_ty, Some(other_tys))
+            self.lookup_method_in_trait(span, opname, trait_did, lhs_ty, Some(other_tys))
         });
 
         match method {
@@ -397,23 +425,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                 let method = self.register_infer_ok_obligations(ok);
                 self.select_obligations_where_possible();
 
-                let (lhs_by_ref, _rhs_by_ref) = match op {
-                    Op::Binary(_, IsAssign::Yes) => (true, false),
-                    Op::Binary(op, _) if !op.node.is_by_value() => (true, true),
-                    Op::Binary(..) | Op::Unary(_) => (false, false),
-                };
-                if lhs_by_ref {
-                    if let ty::TyRef(region, mt) = method.sig.inputs()[0].sty {
-                        let autoref = Adjustment {
-                            kind: Adjust::Borrow(AutoBorrow::Ref(region, mt.mutbl)),
-                            target: method.sig.inputs()[0]
-                        };
-                        self.apply_adjustments(lhs_expr, vec![autoref]);
-                    }
-                }
-                self.write_method_call(expr.id, method);
-
-                Ok(method.sig.output())
+                Ok(method)
             }
             None => {
                 Err(())
@@ -479,7 +491,7 @@ impl BinOpCategory {
 }
 
 /// Whether the binary operation is an assignment (`a += b`), or not (`a + b`)
-#[derive(Clone, Copy, Debug)]
+#[derive(Clone, Copy, Debug, PartialEq)]
 enum IsAssign {
     No,
     Yes,
@@ -488,7 +500,7 @@ enum IsAssign {
 #[derive(Clone, Copy, Debug)]
 enum Op {
     Binary(hir::BinOp, IsAssign),
-    Unary(hir::UnOp),
+    Unary(hir::UnOp, Span),
 }
 
 /// Returns true if this is a built-in arithmetic operation (e.g. u32
diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs
index e1905c93106..33fbc006e4e 100644
--- a/src/librustc_typeck/check/regionck.rs
+++ b/src/librustc_typeck/check/regionck.rs
@@ -566,49 +566,38 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for RegionCtxt<'a, 'gcx, 'tcx> {
 
             hir::ExprCall(ref callee, ref args) => {
                 if is_method_call {
-                    self.constrain_call(expr, Some(&callee),
-                                        args.iter().map(|e| &*e), false);
+                    self.constrain_call(expr, Some(&callee), args.iter().map(|e| &*e));
                 } else {
                     self.constrain_callee(callee.id, expr, &callee);
-                    self.constrain_call(expr, None,
-                                        args.iter().map(|e| &*e), false);
+                    self.constrain_call(expr, None, args.iter().map(|e| &*e));
                 }
 
                 intravisit::walk_expr(self, expr);
             }
 
             hir::ExprMethodCall(.., ref args) => {
-                self.constrain_call(expr, Some(&args[0]),
-                                    args[1..].iter().map(|e| &*e), false);
+                self.constrain_call(expr, Some(&args[0]), args[1..].iter().map(|e| &*e));
 
                 intravisit::walk_expr(self, expr);
             }
 
             hir::ExprAssignOp(_, ref lhs, ref rhs) => {
                 if is_method_call {
-                    self.constrain_call(expr, Some(&lhs),
-                                        Some(&**rhs).into_iter(), false);
+                    self.constrain_call(expr, Some(&lhs), Some(&**rhs).into_iter());
                 }
 
                 intravisit::walk_expr(self, expr);
             }
 
             hir::ExprIndex(ref lhs, ref rhs) if is_method_call => {
-                self.constrain_call(expr, Some(&lhs),
-                                    Some(&**rhs).into_iter(), true);
+                self.constrain_call(expr, Some(&lhs), Some(&**rhs).into_iter());
 
                 intravisit::walk_expr(self, expr);
             },
 
-            hir::ExprBinary(op, ref lhs, ref rhs) if is_method_call => {
-                let implicitly_ref_args = !op.node.is_by_value();
-
-                // As `expr_method_call`, but the call is via an
-                // overloaded op.  Note that we (sadly) currently use an
-                // implicit "by ref" sort of passing style here.  This
-                // should be converted to an adjustment!
-                self.constrain_call(expr, Some(&lhs),
-                                    Some(&**rhs).into_iter(), implicitly_ref_args);
+            hir::ExprBinary(_, ref lhs, ref rhs) if is_method_call => {
+                // As `ExprMethodCall`, but the call is via an overloaded op.
+                self.constrain_call(expr, Some(&lhs), Some(&**rhs).into_iter());
 
                 intravisit::walk_expr(self, expr);
             }
@@ -625,21 +614,10 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for RegionCtxt<'a, 'gcx, 'tcx> {
                 intravisit::walk_expr(self, expr);
             }
 
-            hir::ExprUnary(op, ref lhs) if is_method_call => {
-                let implicitly_ref_args = !op.is_by_value();
-
-                // As above.
-                self.constrain_call(expr, Some(&lhs),
-                                    None::<hir::Expr>.iter(), implicitly_ref_args);
-
-                intravisit::walk_expr(self, expr);
-            }
-
             hir::ExprUnary(hir::UnDeref, ref base) => {
                 // For *a, the lifetime of a must enclose the deref
-                if self.tables.borrow().is_method_call(expr) {
-                    self.constrain_call(expr, Some(base),
-                                        None::<hir::Expr>.iter(), true);
+                if is_method_call {
+                    self.constrain_call(expr, Some(base), None::<hir::Expr>.iter());
                 }
                 // For overloaded derefs, base_ty is the input to `Deref::deref`,
                 // but it's a reference type uing the same region as the output.
@@ -651,6 +629,13 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for RegionCtxt<'a, 'gcx, 'tcx> {
                 intravisit::walk_expr(self, expr);
             }
 
+            hir::ExprUnary(_, ref lhs) if is_method_call => {
+                // As above.
+                self.constrain_call(expr, Some(&lhs), None::<hir::Expr>.iter());
+
+                intravisit::walk_expr(self, expr);
+            }
+
             hir::ExprIndex(ref vec_expr, _) => {
                 // For a[b], the lifetime of a must enclose the deref
                 let vec_type = self.resolve_expr_type_adjusted(&vec_expr);
@@ -802,19 +787,15 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
     fn constrain_call<'b, I: Iterator<Item=&'b hir::Expr>>(&mut self,
                                                            call_expr: &hir::Expr,
                                                            receiver: Option<&hir::Expr>,
-                                                           arg_exprs: I,
-                                                           implicitly_ref_args: bool) {
+                                                           arg_exprs: I) {
         //! Invoked on every call site (i.e., normal calls, method calls,
         //! and overloaded operators). Constrains the regions which appear
         //! in the type of the function. Also constrains the regions that
         //! appear in the arguments appropriately.
 
-        debug!("constrain_call(call_expr={:?}, \
-                receiver={:?}, \
-                implicitly_ref_args={})",
+        debug!("constrain_call(call_expr={:?}, receiver={:?})",
                 call_expr,
-                receiver,
-                implicitly_ref_args);
+                receiver);
 
         // `callee_region` is the scope representing the time in which the
         // call occurs.
@@ -832,14 +813,6 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
             // valid for at least the lifetime of the function:
             self.type_of_node_must_outlive(infer::CallArg(arg_expr.span),
                                            arg_expr.id, callee_region);
-
-            // unfortunately, there are two means of taking implicit
-            // references, and we need to propagate constraints as a
-            // result. modes are going away and the "DerefArgs" code
-            // should be ported to use adjustments
-            if implicitly_ref_args {
-                self.link_by_ref(arg_expr, callee_scope);
-            }
         }
 
         // as loop above, but for receiver
@@ -847,9 +820,6 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
             debug!("receiver: {:?}", r);
             self.type_of_node_must_outlive(infer::CallRcvr(r.span),
                                            r.id, callee_region);
-            if implicitly_ref_args {
-                self.link_by_ref(&r, callee_scope);
-            }
         }
     }
 
@@ -1111,19 +1081,6 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
         }
     }
 
-    /// Computes the guarantor for cases where the `expr` is being passed by implicit reference and
-    /// must outlive `callee_scope`.
-    fn link_by_ref(&self,
-                   expr: &hir::Expr,
-                   callee_scope: CodeExtent) {
-        debug!("link_by_ref(expr={:?}, callee_scope={:?})",
-               expr, callee_scope);
-        let mc = mc::MemCategorizationContext::new(self, &self.region_maps);
-        let expr_cmt = ignore_err!(mc.cat_expr(expr));
-        let borrow_region = self.tcx.mk_region(ty::ReScope(callee_scope));
-        self.link_region(expr.span, borrow_region, ty::ImmBorrow, expr_cmt);
-    }
-
     /// Like `link_region()`, except that the region is extracted from the type of `id`,
     /// which must be some reference (`&T`, `&str`, etc).
     fn link_region_from_node_type(&self,
diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs
index 0b93db54980..012fde16d87 100644
--- a/src/librustc_typeck/check/writeback.rs
+++ b/src/librustc_typeck/check/writeback.rs
@@ -123,18 +123,15 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> {
                     tables.type_dependent_defs.remove(&e.id);
                     tables.node_substs.remove(&e.id);
 
-                    // weird but true: the by-ref binops put an
-                    // adjustment on the lhs but not the rhs; the
-                    // adjustment for rhs is kind of baked into the
-                    // system.
                     match e.node {
                         hir::ExprBinary(..) => {
                             if !op.node.is_by_value() {
-                                tables.adjustments.remove(&lhs.id);
+                                tables.adjustments.get_mut(&lhs.id).map(|a| a.pop());
+                                tables.adjustments.get_mut(&rhs.id).map(|a| a.pop());
                             }
                         },
                         hir::ExprAssignOp(..) => {
-                            tables.adjustments.remove(&lhs.id);
+                            tables.adjustments.get_mut(&lhs.id).map(|a| a.pop());
                         },
                         _ => {},
                     }