]> git.lizzy.rs Git - rust.git/commitdiff
Implement new type-checking strategy for binary operators. Basically,
authorNiko Matsakis <niko@alum.mit.edu>
Tue, 24 Mar 2015 19:34:59 +0000 (15:34 -0400)
committerNiko Matsakis <niko@alum.mit.edu>
Mon, 30 Mar 2015 08:59:56 +0000 (04:59 -0400)
the plan is to treat all binary operators as if they were overloaded,
relying on the fact that we have impls for all the builtin scalar
operations (and no more). But then during writeback we clear the
overload if the types correspond to a builtin op.

This strategy allows us to avoid having to know the types of the
operands ahead of time. It also avoids us overspecializing as we did in
the past.

src/librustc/middle/ty.rs
src/librustc_trans/trans/base.rs
src/librustc_trans/trans/consts.rs
src/librustc_trans/trans/expr.rs
src/librustc_typeck/check/callee.rs
src/librustc_typeck/check/mod.rs
src/librustc_typeck/check/op.rs [new file with mode: 0644]
src/librustc_typeck/check/writeback.rs
src/librustc_typeck/diagnostics.rs
src/libsyntax/ast_util.rs

index 89af3e8f3a97b0580e04d2020249de22bef37564..a788386d2424fec79dd3215cb3acc09553883b9d 100644 (file)
@@ -3039,6 +3039,10 @@ pub fn mk_nil<'tcx>(cx: &ctxt<'tcx>) -> Ty<'tcx> {
     mk_tup(cx, Vec::new())
 }
 
+pub fn mk_bool<'tcx>(cx: &ctxt<'tcx>) -> Ty<'tcx> {
+    mk_t(cx, ty_bool)
+}
+
 pub fn mk_bare_fn<'tcx>(cx: &ctxt<'tcx>,
                         opt_def_id: Option<ast::DefId>,
                         fty: &'tcx BareFnTy<'tcx>) -> Ty<'tcx> {
@@ -3406,8 +3410,12 @@ pub fn type_is_scalar(ty: Ty) -> bool {
 /// Returns true if this type is a floating point type and false otherwise.
 pub fn type_is_floating_point(ty: Ty) -> bool {
     match ty.sty {
-        ty_float(_) => true,
-        _ => false,
+        ty_float(_) |
+        ty_infer(FloatVar(_)) =>
+            true,
+
+        _ =>
+            false,
     }
 }
 
@@ -5805,78 +5813,6 @@ pub fn closure_upvars<'tcx>(typer: &mc::Typer<'tcx>,
     }
 }
 
-pub fn is_binopable<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>, op: ast::BinOp) -> bool {
-    #![allow(non_upper_case_globals)]
-    const tycat_other: isize = 0;
-    const tycat_bool: isize = 1;
-    const tycat_char: isize = 2;
-    const tycat_int: isize = 3;
-    const tycat_float: isize = 4;
-    const tycat_raw_ptr: isize = 6;
-
-    const opcat_add: isize = 0;
-    const opcat_sub: isize = 1;
-    const opcat_mult: isize = 2;
-    const opcat_shift: isize = 3;
-    const opcat_rel: isize = 4;
-    const opcat_eq: isize = 5;
-    const opcat_bit: isize = 6;
-    const opcat_logic: isize = 7;
-    const opcat_mod: isize = 8;
-
-    fn opcat(op: ast::BinOp) -> isize {
-        match op.node {
-          ast::BiAdd => opcat_add,
-          ast::BiSub => opcat_sub,
-          ast::BiMul => opcat_mult,
-          ast::BiDiv => opcat_mult,
-          ast::BiRem => opcat_mod,
-          ast::BiAnd => opcat_logic,
-          ast::BiOr => opcat_logic,
-          ast::BiBitXor => opcat_bit,
-          ast::BiBitAnd => opcat_bit,
-          ast::BiBitOr => opcat_bit,
-          ast::BiShl => opcat_shift,
-          ast::BiShr => opcat_shift,
-          ast::BiEq => opcat_eq,
-          ast::BiNe => opcat_eq,
-          ast::BiLt => opcat_rel,
-          ast::BiLe => opcat_rel,
-          ast::BiGe => opcat_rel,
-          ast::BiGt => opcat_rel
-        }
-    }
-
-    fn tycat<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>) -> isize {
-        if type_is_simd(cx, ty) {
-            return tycat(cx, simd_type(cx, ty))
-        }
-        match ty.sty {
-          ty_char => tycat_char,
-          ty_bool => tycat_bool,
-          ty_int(_) | ty_uint(_) | ty_infer(IntVar(_)) => tycat_int,
-          ty_float(_) | ty_infer(FloatVar(_)) => tycat_float,
-          ty_ptr(_) => tycat_raw_ptr,
-          _ => tycat_other
-        }
-    }
-
-    const t: bool = true;
-    const f: bool = false;
-
-    let tbl = [
-    //           +, -, *, shift, rel, ==, bit, logic, mod
-    /*other*/   [f, f, f, f,     f,   f,  f,   f,     f],
-    /*bool*/    [f, f, f, f,     t,   t,  t,   t,     f],
-    /*char*/    [f, f, f, f,     t,   t,  f,   f,     f],
-    /*isize*/     [t, t, t, t,     t,   t,  t,   f,     t],
-    /*float*/   [t, t, t, f,     t,   t,  f,   f,     f],
-    /*bot*/     [t, t, t, t,     t,   t,  t,   t,     t],
-    /*raw ptr*/ [f, f, f, f,     t,   t,  f,   f,     f]];
-
-    return tbl[tycat(cx, ty) as usize ][opcat(op) as usize];
-}
-
 // Returns the repeat count for a repeating vector expression.
 pub fn eval_repeat_count(tcx: &ctxt, count_expr: &ast::Expr) -> usize {
     match const_eval::eval_const_expr_partial(tcx, count_expr, Some(tcx.types.usize)) {
index bc83df419c3582dfbeef15947f43b3f8a2194651..61e332f87ad1a2683aeb65b8676b7e4cfc570509 100644 (file)
@@ -560,7 +560,7 @@ pub fn compare_scalar_types<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                 _ => bcx.sess().bug("compare_scalar_types: must be a comparison operator")
             }
         }
-        ty::ty_bool | ty::ty_uint(_) | ty::ty_char => {
+        ty::ty_bare_fn(..) | ty::ty_bool | ty::ty_uint(_) | ty::ty_char => {
             ICmp(bcx, bin_op_to_icmp_predicate(bcx.ccx(), op, false), lhs, rhs, debug_loc)
         }
         ty::ty_ptr(mt) if common::type_is_sized(bcx.tcx(), mt.ty) => {
index 348335139da649e4c4a8a4919cfd376c5c9d36ec..0a9df2b5dc180dba91802d120abf6ea96d901e8c 100644 (file)
@@ -351,7 +351,14 @@ pub fn const_expr<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
 fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                                    e: &ast::Expr,
                                    ety: Ty<'tcx>,
-                                   param_substs: &'tcx Substs<'tcx>) -> ValueRef {
+                                   param_substs: &'tcx Substs<'tcx>)
+                                   -> ValueRef
+{
+    debug!("const_expr_unadjusted(e={}, ety={}, param_substs={})",
+           e.repr(cx.tcx()),
+           ety.repr(cx.tcx()),
+           param_substs.repr(cx.tcx()));
+
     let map_list = |exprs: &[P<ast::Expr>]| {
         exprs.iter().map(|e| const_expr(cx, &**e, param_substs).0)
              .fold(Vec::new(), |mut l, val| { l.push(val); l })
@@ -366,6 +373,9 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
             /* Neither type is bottom, and we expect them to be unified
              * already, so the following is safe. */
             let (te1, ty) = const_expr(cx, &**e1, param_substs);
+            debug!("const_expr_unadjusted: te1={}, ty={}",
+                   cx.tn().val_to_string(te1),
+                   ty.repr(cx.tcx()));
             let is_simd = ty::type_is_simd(cx.tcx(), ty);
             let intype = if is_simd {
                 ty::simd_type(cx.tcx(), ty)
index c0ad279d744a8c0ea054adbd1f54866d0a638d18..46cbd1936002aef12621e03f91d45205ea0eb74b 100644 (file)
@@ -2384,6 +2384,7 @@ fn deref_owned_pointer<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     }
 }
 
+#[derive(Debug)]
 enum OverflowOp {
     Add,
     Sub,
@@ -2413,6 +2414,7 @@ enum OverflowCodegen {
 
 enum OverflowOpViaInputCheck { Shl, Shr, }
 
+#[derive(Debug)]
 enum OverflowOpViaIntrinsic { Add, Sub, Mul, }
 
 impl OverflowOpViaIntrinsic {
@@ -2437,7 +2439,8 @@ fn to_intrinsic_name(&self, tcx: &ty::ctxt, ty: Ty) -> &'static str {
                 _ => panic!("unsupported target word size")
             },
             ref t @ ty_uint(_) | ref t @ ty_int(_) => t.clone(),
-            _ => panic!("tried to get overflow intrinsic for non-int type")
+            _ => panic!("tried to get overflow intrinsic for {:?} applied to non-int type",
+                        *self)
         };
 
         match *self {
index 6ba21e25e1fe5c62d48f4ca1c1f65465893005a5..31ac0a57ba0e14361d7c990855a02f01c1048e42 100644 (file)
@@ -9,7 +9,6 @@
 // except according to those terms.
 
 use super::autoderef;
-use super::AutorefArgs;
 use super::check_argument_types;
 use super::check_expr;
 use super::check_method_argument_types;
@@ -258,7 +257,6 @@ fn confirm_builtin_call<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
                          &fn_sig.inputs,
                          &expected_arg_tys[..],
                          arg_exprs,
-                         AutorefArgs::No,
                          fn_sig.variadic,
                          TupleArgumentsFlag::DontTupleArguments);
 
@@ -288,7 +286,6 @@ fn confirm_deferred_closure_call<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
                          &*fn_sig.inputs,
                          &*expected_arg_tys,
                          arg_exprs,
-                         AutorefArgs::No,
                          fn_sig.variadic,
                          TupleArgumentsFlag::TupleArguments);
 
@@ -308,7 +305,6 @@ fn confirm_overloaded_call<'a,'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                     method_callee.ty,
                                     callee_expr,
                                     arg_exprs,
-                                    AutorefArgs::No,
                                     TupleArgumentsFlag::TupleArguments,
                                     expected);
     write_call(fcx, call_expr, output_type);
index def877d92b52380a57fa9d9ad27b504472b9b78e..1f992b9c3ae6c97dfb0480820f064fbf52eca745 100644 (file)
@@ -79,7 +79,6 @@
 pub use self::LvaluePreference::*;
 pub use self::Expectation::*;
 pub use self::compare_method::compare_impl_method;
-use self::IsBinopAssignment::*;
 use self::TupleArgumentsFlag::*;
 
 use astconv::{self, ast_region_to_region, ast_ty_to_ty, AstConv, PathParamMode};
 mod closure;
 mod callee;
 mod compare_method;
+mod op;
 
 /// closures defined within the function.  For example:
 ///
@@ -288,15 +288,6 @@ pub fn recurse(&mut self, blk: &ast::Block) -> UnsafetyState {
     }
 }
 
-/// Whether `check_binop` is part of an assignment or not.
-/// Used to know whether we allow user overloads and to print
-/// better messages on error.
-#[derive(PartialEq)]
-enum IsBinopAssignment{
-    SimpleBinop,
-    BinopAssignment,
-}
-
 #[derive(Clone)]
 pub struct FnCtxt<'a, 'tcx: 'a> {
     body_id: ast::NodeId,
@@ -1325,14 +1316,18 @@ pub fn err_count_since_creation(&self) -> usize {
     /// version, this version will also select obligations if it seems
     /// useful, in an effort to get more type information.
     fn resolve_type_vars_if_possible(&self, mut ty: Ty<'tcx>) -> Ty<'tcx> {
+        debug!("resolve_type_vars_if_possible(ty={})", ty.repr(self.tcx()));
+
         // No ty::infer()? Nothing needs doing.
         if !ty::type_has_ty_infer(ty) {
+            debug!("resolve_type_vars_if_possible: ty={}", ty.repr(self.tcx()));
             return ty;
         }
 
         // If `ty` is a type variable, see whether we already know what it is.
         ty = self.infcx().resolve_type_vars_if_possible(&ty);
         if !ty::type_has_ty_infer(ty) {
+            debug!("resolve_type_vars_if_possible: ty={}", ty.repr(self.tcx()));
             return ty;
         }
 
@@ -1340,6 +1335,7 @@ fn resolve_type_vars_if_possible(&self, mut ty: Ty<'tcx>) -> Ty<'tcx> {
         vtable::select_new_fcx_obligations(self);
         ty = self.infcx().resolve_type_vars_if_possible(&ty);
         if !ty::type_has_ty_infer(ty) {
+            debug!("resolve_type_vars_if_possible: ty={}", ty.repr(self.tcx()));
             return ty;
         }
 
@@ -1348,7 +1344,10 @@ fn resolve_type_vars_if_possible(&self, mut ty: Ty<'tcx>) -> Ty<'tcx> {
         // indirect dependencies that don't seem worth tracking
         // precisely.
         vtable::select_fcx_obligations_where_possible(self);
-        self.infcx().resolve_type_vars_if_possible(&ty)
+        ty = self.infcx().resolve_type_vars_if_possible(&ty);
+
+        debug!("resolve_type_vars_if_possible: ty={}", ty.repr(self.tcx()));
+        ty
     }
 
     /// Resolves all type variables in `t` and then, if any were left
@@ -2092,24 +2091,17 @@ fn make_overloaded_lvalue_return_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
 {
     match method {
         Some(method) => {
-            let ref_ty = // invoked methods have all LB regions instantiated
-                ty::no_late_bound_regions(
-                    fcx.tcx(), &ty::ty_fn_ret(method.ty)).unwrap();
-            match method_call {
-                Some(method_call) => {
-                    fcx.inh.method_map.borrow_mut().insert(method_call,
-                                                           method);
-                }
-                None => {}
-            }
-            match ref_ty {
-                ty::FnConverging(ref_ty) => {
-                    ty::deref(ref_ty, true)
-                }
-                ty::FnDiverging => {
-                    fcx.tcx().sess.bug("index/deref traits do not define a `!` return")
-                }
+            // extract method method return type, which will be &T;
+            // all LB regions should have been instantiated during method lookup
+            let ret_ty = ty::ty_fn_ret(method.ty);
+            let ret_ty = ty::no_late_bound_regions(fcx.tcx(), &ret_ty).unwrap().unwrap();
+
+            if let Some(method_call) = method_call {
+                fcx.inh.method_map.borrow_mut().insert(method_call, method);
             }
+
+            // method returns &T, but the type as visible to user is T, so deref
+            ty::deref(ret_ty, true)
         }
         None => None,
     }
@@ -2238,7 +2230,6 @@ fn check_method_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                          method_fn_ty: Ty<'tcx>,
                                          callee_expr: &'tcx ast::Expr,
                                          args_no_rcvr: &'tcx [P<ast::Expr>],
-                                         autoref_args: AutorefArgs,
                                          tuple_arguments: TupleArgumentsFlag,
                                          expected: Expectation<'tcx>)
                                          -> ty::FnOutput<'tcx> {
@@ -2255,7 +2246,6 @@ fn check_method_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                              &err_inputs[..],
                              &[],
                              args_no_rcvr,
-                             autoref_args,
                              false,
                              tuple_arguments);
         ty::FnConverging(fcx.tcx().types.err)
@@ -2273,7 +2263,6 @@ fn check_method_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                      &fty.sig.0.inputs[1..],
                                      &expected_arg_tys[..],
                                      args_no_rcvr,
-                                     autoref_args,
                                      fty.sig.0.variadic,
                                      tuple_arguments);
                 fty.sig.0.output
@@ -2293,7 +2282,6 @@ fn check_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                   fn_inputs: &[Ty<'tcx>],
                                   expected_arg_tys: &[Ty<'tcx>],
                                   args: &'tcx [P<ast::Expr>],
-                                  autoref_args: AutorefArgs,
                                   variadic: bool,
                                   tuple_arguments: TupleArgumentsFlag) {
     let tcx = fcx.ccx.tcx;
@@ -2406,26 +2394,7 @@ fn check_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
 
             if is_block == check_blocks {
                 debug!("checking the argument");
-                let mut formal_ty = formal_tys[i];
-
-                match autoref_args {
-                    AutorefArgs::Yes => {
-                        match formal_ty.sty {
-                            ty::ty_rptr(_, mt) => formal_ty = mt.ty,
-                            ty::ty_err => (),
-                            _ => {
-                                // So we hit this case when one implements the
-                                // operator traits but leaves an argument as
-                                // just T instead of &T. We'll catch it in the
-                                // mismatch impl/trait method phase no need to
-                                // ICE here.
-                                // See: #11450
-                                formal_ty = tcx.types.err;
-                            }
-                        }
-                    }
-                    AutorefArgs::No => {}
-                }
+                let formal_ty = formal_tys[i];
 
                 // The special-cased logic below has three functions:
                 // 1. Provide as good of an expected type as possible.
@@ -2622,14 +2591,6 @@ pub fn impl_self_ty<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
     TypeAndSubsts { substs: substs, ty: substd_ty }
 }
 
-// Controls whether the arguments are automatically referenced. This is useful
-// for overloaded binary and unary operators.
-#[derive(Copy, PartialEq)]
-pub enum AutorefArgs {
-    Yes,
-    No,
-}
-
 /// Controls whether the arguments are tupled. This is used for the call
 /// operator.
 ///
@@ -2755,7 +2716,6 @@ fn check_method_call<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                                  fn_ty,
                                                  expr,
                                                  &args[1..],
-                                                 AutorefArgs::No,
                                                  DontTupleArguments,
                                                  expected);
 
@@ -2806,277 +2766,6 @@ fn check_then_else<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
         fcx.write_ty(id, if_ty);
     }
 
-    fn lookup_op_method<'a, 'tcx, F>(fcx: &'a FnCtxt<'a, 'tcx>,
-                                     op_ex: &'tcx ast::Expr,
-                                     lhs_ty: Ty<'tcx>,
-                                     opname: ast::Name,
-                                     trait_did: Option<ast::DefId>,
-                                     lhs: &'a ast::Expr,
-                                     rhs: Option<&'tcx P<ast::Expr>>,
-                                     unbound_method: F,
-                                     autoref_args: AutorefArgs) -> Ty<'tcx> where
-        F: FnOnce(),
-    {
-        let method = match trait_did {
-            Some(trait_did) => {
-                // We do eager coercions to make using operators
-                // more ergonomic:
-                //
-                // - If the input is of type &'a T (resp. &'a mut T),
-                //   then reborrow it to &'b T (resp. &'b mut T) where
-                //   'b <= 'a.  This makes things like `x == y`, where
-                //   `x` and `y` are both region pointers, work.  We
-                //   could also solve this with variance or different
-                //   traits that don't force left and right to have same
-                //   type.
-                let (adj_ty, adjustment) = match lhs_ty.sty {
-                    ty::ty_rptr(r_in, mt) => {
-                        let r_adj = fcx.infcx().next_region_var(infer::Autoref(lhs.span));
-                        fcx.mk_subr(infer::Reborrow(lhs.span), r_adj, *r_in);
-                        let adjusted_ty = ty::mk_rptr(fcx.tcx(), fcx.tcx().mk_region(r_adj), mt);
-                        let autoptr = ty::AutoPtr(r_adj, mt.mutbl, None);
-                        let adjustment = ty::AutoDerefRef { autoderefs: 1, autoref: Some(autoptr) };
-                        (adjusted_ty, adjustment)
-                    }
-                    _ => {
-                        (lhs_ty, ty::AutoDerefRef { autoderefs: 0, autoref: None })
-                    }
-                };
-
-                debug!("adjusted_ty={} adjustment={:?}",
-                       adj_ty.repr(fcx.tcx()),
-                       adjustment);
-
-                method::lookup_in_trait_adjusted(fcx, op_ex.span, Some(lhs), opname,
-                                                 trait_did, adjustment, adj_ty, None)
-            }
-            None => None
-        };
-        let args = match rhs {
-            Some(rhs) => slice::ref_slice(rhs),
-            None => &[][..]
-        };
-        match method {
-            Some(method) => {
-                let method_ty = method.ty;
-                // HACK(eddyb) Fully qualified path to work around a resolve bug.
-                let method_call = ::middle::ty::MethodCall::expr(op_ex.id);
-                fcx.inh.method_map.borrow_mut().insert(method_call, method);
-                match check_method_argument_types(fcx,
-                                                  op_ex.span,
-                                                  method_ty,
-                                                  op_ex,
-                                                  args,
-                                                  autoref_args,
-                                                  DontTupleArguments,
-                                                  NoExpectation) {
-                    ty::FnConverging(result_type) => result_type,
-                    ty::FnDiverging => fcx.tcx().types.err
-                }
-            }
-            None => {
-                unbound_method();
-                // Check the args anyway
-                // so we get all the error messages
-                let expected_ty = fcx.tcx().types.err;
-                check_method_argument_types(fcx,
-                                            op_ex.span,
-                                            expected_ty,
-                                            op_ex,
-                                            args,
-                                            autoref_args,
-                                            DontTupleArguments,
-                                            NoExpectation);
-                fcx.tcx().types.err
-            }
-        }
-    }
-
-    // could be either an expr_binop or an expr_assign_binop
-    fn check_binop<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
-                            expr: &'tcx ast::Expr,
-                            op: ast::BinOp,
-                            lhs: &'tcx ast::Expr,
-                            rhs: &'tcx P<ast::Expr>,
-                            is_binop_assignment: IsBinopAssignment) {
-        let tcx = fcx.ccx.tcx;
-
-        let lvalue_pref = match is_binop_assignment {
-            BinopAssignment => PreferMutLvalue,
-            SimpleBinop => NoPreference
-        };
-        check_expr_with_lvalue_pref(fcx, lhs, lvalue_pref);
-
-        // Callee does bot / err checking
-        let lhs_t =
-            structurally_resolve_type_or_else(fcx, lhs.span, fcx.expr_ty(lhs), || {
-                if ast_util::is_symmetric_binop(op.node) {
-                    // Try RHS first
-                    check_expr(fcx, &**rhs);
-                    fcx.expr_ty(&**rhs)
-                } else {
-                    fcx.tcx().types.err
-                }
-            });
-
-        if ty::type_is_integral(lhs_t) && ast_util::is_shift_binop(op.node) {
-            // Shift is a special case: rhs must be usize, no matter what lhs is
-            check_expr(fcx, &**rhs);
-            let rhs_ty = fcx.expr_ty(&**rhs);
-            let rhs_ty = structurally_resolved_type(fcx, rhs.span, rhs_ty);
-            if ty::type_is_integral(rhs_ty) {
-                fcx.write_ty(expr.id, lhs_t);
-            } else {
-                fcx.type_error_message(
-                    expr.span,
-                    |actual| {
-                        format!(
-                            "right-hand-side of a shift operation must have integral type, \
-                             not `{}`",
-                            actual)
-                    },
-                    rhs_ty,
-                    None);
-                fcx.write_ty(expr.id, fcx.tcx().types.err);
-            }
-            return;
-        }
-
-        if ty::is_binopable(tcx, lhs_t, op) {
-            let tvar = fcx.infcx().next_ty_var();
-            demand::suptype(fcx, expr.span, tvar, lhs_t);
-            check_expr_has_type(fcx, &**rhs, tvar);
-
-            let result_t = match op.node {
-                ast::BiEq | ast::BiNe | ast::BiLt | ast::BiLe | ast::BiGe |
-                ast::BiGt => {
-                    if ty::type_is_simd(tcx, lhs_t) {
-                        if ty::type_is_fp(ty::simd_type(tcx, lhs_t)) {
-                            fcx.type_error_message(expr.span,
-                                |actual| {
-                                    format!("binary comparison \
-                                             operation `{}` not \
-                                             supported for floating \
-                                             point SIMD vector `{}`",
-                                            ast_util::binop_to_string(op.node),
-                                            actual)
-                                },
-                                lhs_t,
-                                None
-                            );
-                            fcx.tcx().types.err
-                        } else {
-                            lhs_t
-                        }
-                    } else {
-                        fcx.tcx().types.bool
-                    }
-                },
-                _ => lhs_t,
-            };
-
-            fcx.write_ty(expr.id, result_t);
-            return;
-        }
-
-        if op.node == ast::BiOr || op.node == ast::BiAnd {
-            // This is an error; one of the operands must have the wrong
-            // type
-            fcx.write_error(expr.id);
-            fcx.write_error(rhs.id);
-            fcx.type_error_message(expr.span,
-                                   |actual| {
-                    format!("binary operation `{}` cannot be applied \
-                             to type `{}`",
-                            ast_util::binop_to_string(op.node),
-                            actual)
-                },
-                lhs_t,
-                None)
-        }
-
-        // Check for overloaded operators if not an assignment.
-        let result_t = if is_binop_assignment == SimpleBinop {
-            check_user_binop(fcx, expr, lhs, lhs_t, op, rhs)
-        } else {
-            fcx.type_error_message(expr.span,
-                                   |actual| {
-                                        format!("binary assignment \
-                                                 operation `{}=` \
-                                                 cannot be applied to \
-                                                 type `{}`",
-                                                ast_util::binop_to_string(op.node),
-                                                actual)
-                                   },
-                                   lhs_t,
-                                   None);
-            check_expr(fcx, &**rhs);
-            fcx.tcx().types.err
-        };
-
-        fcx.write_ty(expr.id, result_t);
-        if ty::type_is_error(result_t) {
-            fcx.write_ty(rhs.id, result_t);
-        }
-    }
-
-    fn check_user_binop<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                                  ex: &'tcx ast::Expr,
-                                  lhs_expr: &'tcx ast::Expr,
-                                  lhs_resolved_t: Ty<'tcx>,
-                                  op: ast::BinOp,
-                                  rhs: &'tcx P<ast::Expr>) -> Ty<'tcx> {
-        let tcx = fcx.ccx.tcx;
-        let lang = &tcx.lang_items;
-        let (name, trait_did) = match op.node {
-            ast::BiAdd => ("add", lang.add_trait()),
-            ast::BiSub => ("sub", lang.sub_trait()),
-            ast::BiMul => ("mul", lang.mul_trait()),
-            ast::BiDiv => ("div", lang.div_trait()),
-            ast::BiRem => ("rem", lang.rem_trait()),
-            ast::BiBitXor => ("bitxor", lang.bitxor_trait()),
-            ast::BiBitAnd => ("bitand", lang.bitand_trait()),
-            ast::BiBitOr => ("bitor", lang.bitor_trait()),
-            ast::BiShl => ("shl", lang.shl_trait()),
-            ast::BiShr => ("shr", lang.shr_trait()),
-            ast::BiLt => ("lt", lang.ord_trait()),
-            ast::BiLe => ("le", lang.ord_trait()),
-            ast::BiGe => ("ge", lang.ord_trait()),
-            ast::BiGt => ("gt", lang.ord_trait()),
-            ast::BiEq => ("eq", lang.eq_trait()),
-            ast::BiNe => ("ne", lang.eq_trait()),
-            ast::BiAnd | ast::BiOr => {
-                check_expr(fcx, &**rhs);
-                return tcx.types.err;
-            }
-        };
-        lookup_op_method(fcx, ex, lhs_resolved_t, token::intern(name),
-                         trait_did, lhs_expr, Some(rhs), || {
-            fcx.type_error_message(ex.span, |actual| {
-                format!("binary operation `{}` cannot be applied to type `{}`",
-                        ast_util::binop_to_string(op.node),
-                        actual)
-            }, lhs_resolved_t, None)
-        }, if ast_util::is_by_value_binop(op.node) { AutorefArgs::No } else { AutorefArgs::Yes })
-    }
-
-    fn check_user_unop<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                                 op_str: &str,
-                                 mname: &str,
-                                 trait_did: Option<ast::DefId>,
-                                 ex: &'tcx ast::Expr,
-                                 rhs_expr: &'tcx ast::Expr,
-                                 rhs_t: Ty<'tcx>,
-                                 op: ast::UnOp) -> Ty<'tcx> {
-       lookup_op_method(fcx, ex, rhs_t, token::intern(mname),
-                        trait_did, rhs_expr, None, || {
-            fcx.type_error_message(ex.span, |actual| {
-                format!("cannot apply unary operator `{}` to type `{}`",
-                        op_str, actual)
-            }, rhs_t, None);
-        }, if ast_util::is_by_value_unop(op) { AutorefArgs::No } else { AutorefArgs::Yes })
-    }
-
     // Check field access expressions
     fn check_field<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
                             expr: &'tcx ast::Expr,
@@ -3479,35 +3168,10 @@ fn check_struct_fields_on_error<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
         fcx.write_ty(id, typ);
       }
       ast::ExprBinary(op, ref lhs, ref rhs) => {
-        check_binop(fcx, expr, op, &**lhs, rhs, SimpleBinop);
-
-        let lhs_ty = fcx.expr_ty(&**lhs);
-        let rhs_ty = fcx.expr_ty(&**rhs);
-        if ty::type_is_error(lhs_ty) ||
-            ty::type_is_error(rhs_ty) {
-            fcx.write_error(id);
-        }
+        op::check_binop(fcx, expr, op, lhs, rhs);
       }
       ast::ExprAssignOp(op, ref lhs, ref rhs) => {
-        check_binop(fcx, expr, op, &**lhs, rhs, BinopAssignment);
-
-        let lhs_t = fcx.expr_ty(&**lhs);
-        let result_t = fcx.expr_ty(expr);
-        demand::suptype(fcx, expr.span, result_t, lhs_t);
-
-        let tcx = fcx.tcx();
-        if !ty::expr_is_lval(tcx, &**lhs) {
-            span_err!(tcx.sess, lhs.span, E0067, "illegal left-hand side expression");
-        }
-
-        fcx.require_expr_have_sized_type(&**lhs, traits::AssignmentLhsSized);
-
-        // Overwrite result of check_binop...this preserves existing behavior
-        // but seems quite dubious with regard to user-defined methods
-        // and so forth. - Niko
-        if !ty::type_is_error(result_t) {
-            fcx.write_nil(expr.id);
-        }
+        op::check_binop_assign(fcx, expr, op, lhs, rhs);
       }
       ast::ExprUnary(unop, ref oprnd) => {
         let expected_inner = expected.to_option(fcx).map_or(NoExpectation, |ty| {
@@ -3580,9 +3244,9 @@ fn check_struct_fields_on_error<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
                                                          oprnd_t);
                     if !(ty::type_is_integral(oprnd_t) ||
                          oprnd_t.sty == ty::ty_bool) {
-                        oprnd_t = check_user_unop(fcx, "!", "not",
-                                                  tcx.lang_items.not_trait(),
-                                                  expr, &**oprnd, oprnd_t, unop);
+                        oprnd_t = op::check_user_unop(fcx, "!", "not",
+                                                      tcx.lang_items.not_trait(),
+                                                      expr, &**oprnd, oprnd_t, unop);
                     }
                 }
                 ast::UnNeg => {
@@ -3590,9 +3254,9 @@ fn check_struct_fields_on_error<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
                                                          oprnd_t);
                     if !(ty::type_is_integral(oprnd_t) ||
                          ty::type_is_fp(oprnd_t)) {
-                        oprnd_t = check_user_unop(fcx, "-", "neg",
-                                                  tcx.lang_items.neg_trait(),
-                                                  expr, &**oprnd, oprnd_t, unop);
+                        oprnd_t = op::check_user_unop(fcx, "-", "neg",
+                                                      tcx.lang_items.neg_trait(),
+                                                      expr, &**oprnd, oprnd_t, unop);
                     }
                 }
             }
@@ -4073,9 +3737,8 @@ fn check_struct_fields_on_error<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
 
                   match result {
                       Some((index_ty, element_ty)) => {
-                          // FIXME: we've already checked idx above, we should
-                          // probably just demand subtype or something here.
-                          check_expr_has_type(fcx, &**idx, index_ty);
+                          let idx_expr_ty = fcx.expr_ty(idx);
+                          demand::eqtype(fcx, expr.span, index_ty, idx_expr_ty);
                           fcx.write_ty(id, element_ty);
                       }
                       _ => {
diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs
new file mode 100644 (file)
index 0000000..49e88dc
--- /dev/null
@@ -0,0 +1,481 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Code related to processing overloaded binary and unary operators.
+
+use super::{
+    check_expr,
+    check_expr_coercable_to_type,
+    check_expr_with_lvalue_pref,
+    demand,
+    method,
+    FnCtxt,
+    PreferMutLvalue,
+    structurally_resolved_type,
+};
+use middle::infer;
+use middle::traits;
+use middle::ty::{self, Ty};
+use syntax::ast;
+use syntax::ast_util;
+use syntax::parse::token;
+use util::ppaux::{Repr, UserString};
+
+/// Check a `a <op>= b`
+pub fn check_binop_assign<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
+                                   expr: &'tcx ast::Expr,
+                                   op: ast::BinOp,
+                                   lhs_expr: &'tcx ast::Expr,
+                                   rhs_expr: &'tcx ast::Expr)
+{
+    let tcx = fcx.ccx.tcx;
+
+    check_expr_with_lvalue_pref(fcx, lhs_expr, PreferMutLvalue);
+    check_expr(fcx, rhs_expr);
+
+    let lhs_ty = structurally_resolved_type(fcx, lhs_expr.span, fcx.expr_ty(lhs_expr));
+    let rhs_ty = structurally_resolved_type(fcx, rhs_expr.span, fcx.expr_ty(rhs_expr));
+
+    if is_builtin_binop(fcx.tcx(), lhs_ty, rhs_ty, op) {
+        enforce_builtin_binop_types(fcx, lhs_expr, lhs_ty, rhs_expr, rhs_ty, op);
+        fcx.write_nil(expr.id);
+    } else {
+        // error types are considered "builtin"
+        assert!(!ty::type_is_error(lhs_ty) || !ty::type_is_error(rhs_ty));
+        span_err!(tcx.sess, lhs_expr.span, E0368,
+                  "binary assignment operation `{}=` cannot be applied to types `{}` and `{}`",
+                  ast_util::binop_to_string(op.node),
+                  lhs_ty.user_string(fcx.tcx()),
+                  rhs_ty.user_string(fcx.tcx()));
+        fcx.write_error(expr.id);
+    }
+
+    let tcx = fcx.tcx();
+    if !ty::expr_is_lval(tcx, lhs_expr) {
+        span_err!(tcx.sess, lhs_expr.span, E0067, "illegal left-hand side expression");
+    }
+
+    fcx.require_expr_have_sized_type(lhs_expr, traits::AssignmentLhsSized);
+}
+
+/// Check a potentially overloaded binary operator.
+pub fn check_binop<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
+                             expr: &'tcx ast::Expr,
+                             op: ast::BinOp,
+                             lhs_expr: &'tcx ast::Expr,
+                             rhs_expr: &'tcx ast::Expr)
+{
+    let tcx = fcx.ccx.tcx;
+
+    debug!("check_binop(expr.id={}, expr={}, op={:?}, lhs_expr={}, rhs_expr={})",
+           expr.id,
+           expr.repr(tcx),
+           op,
+           lhs_expr.repr(tcx),
+           rhs_expr.repr(tcx));
+
+    check_expr(fcx, lhs_expr);
+    let lhs_ty = fcx.resolve_type_vars_if_possible(fcx.expr_ty(lhs_expr));
+
+    // Annoyingly, SIMD ops don't fit into the PartialEq/PartialOrd
+    // traits, because their return type is not bool. Perhaps this
+    // should change, but for now if LHS is SIMD we go down a
+    // different path that bypassess all traits.
+    if ty::type_is_simd(fcx.tcx(), lhs_ty) {
+        check_expr_coercable_to_type(fcx, rhs_expr, lhs_ty);
+        let rhs_ty = fcx.resolve_type_vars_if_possible(fcx.expr_ty(lhs_expr));
+        let return_ty = enforce_builtin_binop_types(fcx, lhs_expr, lhs_ty, rhs_expr, rhs_ty, op);
+        fcx.write_ty(expr.id, return_ty);
+        return;
+    }
+
+    match BinOpCategory::from(op) {
+        BinOpCategory::Shortcircuit => {
+            // && and || are a simple case.
+            demand::suptype(fcx, lhs_expr.span, ty::mk_bool(tcx), lhs_ty);
+            check_expr_coercable_to_type(fcx, rhs_expr, ty::mk_bool(tcx));
+            fcx.write_ty(expr.id, ty::mk_bool(tcx));
+        }
+        _ => {
+            // Otherwise, we always treat operators as if they are
+            // overloaded. This is the way to be most flexible w/r/t
+            // types that get inferred.
+            let (rhs_ty, return_ty) =
+                check_overloaded_binop(fcx, expr, lhs_expr, lhs_ty, rhs_expr, op);
+
+            // Supply type inference hints if relevant. Probably these
+            // hints should be enforced during select as part of the
+            // `consider_unification_despite_ambiguity` routine, but this
+            // more convenient for now.
+            //
+            // The basic idea is to help type inference by taking
+            // advantage of things we know about how the impls for
+            // scalar types are arranged. This is important in a
+            // scenario like `1_u32 << 2`, because it lets us quickly
+            // deduce that the result type should be `u32`, even
+            // though we don't know yet what type 2 has and hence
+            // can't pin this down to a specific impl.
+            let rhs_ty = fcx.resolve_type_vars_if_possible(rhs_ty);
+            if
+                !ty::type_is_ty_var(lhs_ty) &&
+                !ty::type_is_ty_var(rhs_ty) &&
+                is_builtin_binop(fcx.tcx(), lhs_ty, rhs_ty, op)
+            {
+                let builtin_return_ty =
+                    enforce_builtin_binop_types(fcx, lhs_expr, lhs_ty, rhs_expr, rhs_ty, op);
+                demand::suptype(fcx, expr.span, builtin_return_ty, return_ty);
+            }
+
+            fcx.write_ty(expr.id, return_ty);
+        }
+    }
+}
+
+fn enforce_builtin_binop_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
+                                         lhs_expr: &'tcx ast::Expr,
+                                         lhs_ty: Ty<'tcx>,
+                                         rhs_expr: &'tcx ast::Expr,
+                                         rhs_ty: Ty<'tcx>,
+                                         op: ast::BinOp)
+                                         -> Ty<'tcx>
+{
+    debug_assert!(is_builtin_binop(fcx.tcx(), lhs_ty, rhs_ty, op));
+
+    let tcx = fcx.tcx();
+    match BinOpCategory::from(op) {
+        BinOpCategory::Shortcircuit => {
+            demand::suptype(fcx, lhs_expr.span, ty::mk_bool(tcx), lhs_ty);
+            demand::suptype(fcx, rhs_expr.span, ty::mk_bool(tcx), rhs_ty);
+            ty::mk_bool(tcx)
+        }
+
+        BinOpCategory::Shift => {
+            // For integers, the shift amount can be of any integral
+            // type. For simd, the type must match exactly.
+            if ty::type_is_simd(tcx, lhs_ty) {
+                demand::suptype(fcx, rhs_expr.span, lhs_ty, rhs_ty);
+            }
+
+            // result type is same as LHS always
+            lhs_ty
+        }
+
+        BinOpCategory::Math |
+        BinOpCategory::Bitwise => {
+            // both LHS and RHS and result will have the same type
+            demand::suptype(fcx, rhs_expr.span, lhs_ty, rhs_ty);
+            lhs_ty
+        }
+
+        BinOpCategory::Comparison => {
+            // both LHS and RHS and result will have the same type
+            demand::suptype(fcx, rhs_expr.span, lhs_ty, rhs_ty);
+
+            // if this is simd, result is same as lhs, else bool
+            if ty::type_is_simd(tcx, lhs_ty) {
+                let unit_ty = ty::simd_type(tcx, lhs_ty);
+                debug!("enforce_builtin_binop_types: lhs_ty={} unit_ty={}",
+                       lhs_ty.repr(tcx),
+                       unit_ty.repr(tcx));
+                if !ty::type_is_integral(unit_ty) {
+                    tcx.sess.span_err(
+                        lhs_expr.span,
+                        &format!("binary comparison operation `{}` not supported \
+                                  for floating point SIMD vector `{}`",
+                                 ast_util::binop_to_string(op.node),
+                                 lhs_ty.user_string(tcx)));
+                    tcx.types.err
+                } else {
+                    lhs_ty
+                }
+            } else {
+                ty::mk_bool(tcx)
+            }
+        }
+    }
+}
+
+fn check_overloaded_binop<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
+                                    expr: &'tcx ast::Expr,
+                                    lhs_expr: &'tcx ast::Expr,
+                                    lhs_ty: Ty<'tcx>,
+                                    rhs_expr: &'tcx ast::Expr,
+                                    op: ast::BinOp)
+                                    -> (Ty<'tcx>, Ty<'tcx>)
+{
+    debug!("check_overloaded_binop(expr.id={}, lhs_ty={})",
+           expr.id,
+           lhs_ty.repr(fcx.tcx()));
+
+    let (name, trait_def_id) = name_and_trait_def_id(fcx, op);
+
+    // NB: As we have not yet type-checked the RHS, we don't have the
+    // type at hand. Make a variable to represent it. The whole reason
+    // for this indirection is so that, below, we can check the expr
+    // using this variable as the expected type, which sometimes lets
+    // us do better coercions than we would be able to do otherwise,
+    // particularly for things like `String + &String`.
+    let rhs_ty_var = fcx.infcx().next_ty_var();
+
+    let return_ty = match lookup_op_method(fcx, expr, lhs_ty, vec![rhs_ty_var],
+                                           token::intern(name), trait_def_id,
+                                           lhs_expr) {
+        Ok(return_ty) => return_ty,
+        Err(()) => {
+            // error types are considered "builtin"
+            if !ty::type_is_error(lhs_ty) {
+                span_err!(fcx.tcx().sess, lhs_expr.span, E0369,
+                          "binary operation `{}` cannot be applied to type `{}`",
+                          ast_util::binop_to_string(op.node),
+                          lhs_ty.user_string(fcx.tcx()));
+            }
+            fcx.tcx().types.err
+        }
+    };
+
+    // see `NB` above
+    check_expr_coercable_to_type(fcx, rhs_expr, rhs_ty_var);
+
+    (rhs_ty_var, return_ty)
+}
+
+pub fn check_user_unop<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
+                                 op_str: &str,
+                                 mname: &str,
+                                 trait_did: Option<ast::DefId>,
+                                 ex: &'tcx ast::Expr,
+                                 operand_expr: &'tcx ast::Expr,
+                                 operand_ty: Ty<'tcx>,
+                                 op: ast::UnOp)
+                                 -> Ty<'tcx>
+{
+    assert!(ast_util::is_by_value_unop(op));
+    match lookup_op_method(fcx, ex, operand_ty, vec![],
+                           token::intern(mname), trait_did,
+                           operand_expr) {
+        Ok(t) => t,
+        Err(()) => {
+            fcx.type_error_message(ex.span, |actual| {
+                format!("cannot apply unary operator `{}` to type `{}`",
+                        op_str, actual)
+            }, operand_ty, None);
+            fcx.tcx().types.err
+        }
+    }
+}
+
+fn name_and_trait_def_id(fcx: &FnCtxt, op: ast::BinOp) -> (&'static str, Option<ast::DefId>) {
+    let lang = &fcx.tcx().lang_items;
+    match op.node {
+        ast::BiAdd => ("add", lang.add_trait()),
+        ast::BiSub => ("sub", lang.sub_trait()),
+        ast::BiMul => ("mul", lang.mul_trait()),
+        ast::BiDiv => ("div", lang.div_trait()),
+        ast::BiRem => ("rem", lang.rem_trait()),
+        ast::BiBitXor => ("bitxor", lang.bitxor_trait()),
+        ast::BiBitAnd => ("bitand", lang.bitand_trait()),
+        ast::BiBitOr => ("bitor", lang.bitor_trait()),
+        ast::BiShl => ("shl", lang.shl_trait()),
+        ast::BiShr => ("shr", lang.shr_trait()),
+        ast::BiLt => ("lt", lang.ord_trait()),
+        ast::BiLe => ("le", lang.ord_trait()),
+        ast::BiGe => ("ge", lang.ord_trait()),
+        ast::BiGt => ("gt", lang.ord_trait()),
+        ast::BiEq => ("eq", lang.eq_trait()),
+        ast::BiNe => ("ne", lang.eq_trait()),
+        ast::BiAnd | ast::BiOr => {
+            fcx.tcx().sess.span_bug(op.span, "&& and || are not overloadable")
+        }
+    }
+}
+
+fn lookup_op_method<'a, 'tcx>(fcx: &'a FnCtxt<'a, 'tcx>,
+                              expr: &'tcx ast::Expr,
+                              lhs_ty: Ty<'tcx>,
+                              other_tys: Vec<Ty<'tcx>>,
+                              opname: ast::Name,
+                              trait_did: Option<ast::DefId>,
+                              lhs_expr: &'a ast::Expr)
+                              -> Result<Ty<'tcx>,()>
+{
+    debug!("lookup_op_method(expr={}, lhs_ty={}, opname={:?}, trait_did={}, lhs_expr={})",
+           expr.repr(fcx.tcx()),
+           lhs_ty.repr(fcx.tcx()),
+           opname,
+           trait_did.repr(fcx.tcx()),
+           lhs_expr.repr(fcx.tcx()));
+
+    let method = match trait_did {
+        Some(trait_did) => {
+            // We do eager coercions to make using operators
+            // more ergonomic:
+            //
+            // - If the input is of type &'a T (resp. &'a mut T),
+            //   then reborrow it to &'b T (resp. &'b mut T) where
+            //   'b <= 'a.  This makes things like `x == y`, where
+            //   `x` and `y` are both region pointers, work.  We
+            //   could also solve this with variance or different
+            //   traits that don't force left and right to have same
+            //   type.
+            let (adj_ty, adjustment) = match lhs_ty.sty {
+                ty::ty_rptr(r_in, mt) => {
+                    let r_adj = fcx.infcx().next_region_var(infer::Autoref(lhs_expr.span));
+                    fcx.mk_subr(infer::Reborrow(lhs_expr.span), r_adj, *r_in);
+                    let adjusted_ty = ty::mk_rptr(fcx.tcx(), fcx.tcx().mk_region(r_adj), mt);
+                    let autoptr = ty::AutoPtr(r_adj, mt.mutbl, None);
+                    let adjustment = ty::AutoDerefRef { autoderefs: 1, autoref: Some(autoptr) };
+                    (adjusted_ty, adjustment)
+                }
+                _ => {
+                    (lhs_ty, ty::AutoDerefRef { autoderefs: 0, autoref: None })
+                }
+            };
+
+            debug!("adjusted_ty={} adjustment={:?}",
+                   adj_ty.repr(fcx.tcx()),
+                   adjustment);
+
+            method::lookup_in_trait_adjusted(fcx, expr.span, Some(lhs_expr), opname,
+                                             trait_did, adjustment, adj_ty, Some(other_tys))
+        }
+        None => None
+    };
+
+    match method {
+        Some(method) => {
+            let method_ty = method.ty;
+
+            // HACK(eddyb) Fully qualified path to work around a resolve bug.
+            let method_call = ::middle::ty::MethodCall::expr(expr.id);
+            fcx.inh.method_map.borrow_mut().insert(method_call, method);
+
+            // extract return type for method; all late bound regions
+            // should have been instantiated by now
+            let ret_ty = ty::ty_fn_ret(method_ty);
+            Ok(ty::no_late_bound_regions(fcx.tcx(), &ret_ty).unwrap().unwrap())
+        }
+        None => {
+            Err(())
+        }
+    }
+}
+
+// Binary operator categories. These categories summarize the behavior
+// with respect to the builtin operationrs supported.
+enum BinOpCategory {
+    /// &&, || -- cannot be overridden
+    Shortcircuit,
+
+    /// <<, >> -- when shifting a single integer, rhs can be any
+    /// integer type. For simd, types must match.
+    Shift,
+
+    /// +, -, etc -- takes equal types, produces same type as input,
+    /// applicable to ints/floats/simd
+    Math,
+
+    /// &, |, ^ -- takes equal types, produces same type as input,
+    /// applicable to ints/floats/simd/bool
+    Bitwise,
+
+    /// ==, !=, etc -- takes equal types, produces bools, except for simd,
+    /// which produce the input type
+    Comparison,
+}
+
+impl BinOpCategory {
+    fn from(op: ast::BinOp) -> BinOpCategory {
+        match op.node {
+            ast::BiShl | ast::BiShr =>
+                BinOpCategory::Shift,
+
+            ast::BiAdd |
+            ast::BiSub |
+            ast::BiMul |
+            ast::BiDiv |
+            ast::BiRem =>
+                BinOpCategory::Math,
+
+            ast::BiBitXor |
+            ast::BiBitAnd |
+            ast::BiBitOr =>
+                BinOpCategory::Bitwise,
+
+            ast::BiEq |
+            ast::BiNe |
+            ast::BiLt |
+            ast::BiLe |
+            ast::BiGe |
+            ast::BiGt =>
+                BinOpCategory::Comparison,
+
+            ast::BiAnd |
+            ast::BiOr =>
+                BinOpCategory::Shortcircuit,
+        }
+    }
+}
+
+/// Returns true if this is a built-in arithmetic operation (e.g. u32
+/// + u32, i16x4 == i16x4) and false if these types would have to be
+/// overloaded to be legal. There are two reasons that we distinguish
+/// builtin operations from overloaded ones (vs trying to drive
+/// everything uniformly through the trait system and intrinsics or
+/// something like that):
+///
+/// 1. Builtin operations can trivially be evaluated in constants.
+/// 2. For comparison operators applied to SIMD types the result is
+///    not of type `bool`. For example, `i16x4==i16x4` yields a
+///    type like `i16x4`. This means that the overloaded trait
+///    `PartialEq` is not applicable.
+///
+/// Reason #2 is the killer. I tried for a while to always use
+/// overloaded logic and just check the types in constants/trans after
+/// the fact, and it worked fine, except for SIMD types. -nmatsakis
+fn is_builtin_binop<'tcx>(cx: &ty::ctxt<'tcx>,
+                          lhs: Ty<'tcx>,
+                          rhs: Ty<'tcx>,
+                          op: ast::BinOp)
+                          -> bool
+{
+    match BinOpCategory::from(op) {
+        BinOpCategory::Shortcircuit => {
+            true
+        }
+
+        BinOpCategory::Shift => {
+            ty::type_is_error(lhs) || ty::type_is_error(rhs) ||
+                ty::type_is_integral(lhs) && ty::type_is_integral(rhs) ||
+                ty::type_is_simd(cx, lhs) && ty::type_is_simd(cx, rhs)
+        }
+
+        BinOpCategory::Math => {
+            ty::type_is_error(lhs) || ty::type_is_error(rhs) ||
+                ty::type_is_integral(lhs) && ty::type_is_integral(rhs) ||
+                ty::type_is_floating_point(lhs) && ty::type_is_floating_point(rhs) ||
+                ty::type_is_simd(cx, lhs) && ty::type_is_simd(cx, rhs)
+        }
+
+        BinOpCategory::Bitwise => {
+            ty::type_is_error(lhs) || ty::type_is_error(rhs) ||
+                ty::type_is_integral(lhs) && ty::type_is_integral(rhs) ||
+                ty::type_is_floating_point(lhs) && ty::type_is_floating_point(rhs) ||
+                ty::type_is_simd(cx, lhs) && ty::type_is_simd(cx, rhs) ||
+                ty::type_is_bool(lhs) && ty::type_is_bool(rhs)
+        }
+
+        BinOpCategory::Comparison => {
+            ty::type_is_error(lhs) || ty::type_is_error(rhs) ||
+                ty::type_is_scalar(lhs) && ty::type_is_scalar(rhs) ||
+                ty::type_is_simd(cx, lhs) && ty::type_is_simd(cx, rhs)
+        }
+    }
+}
+
index e555d3085a4c7348fa4d3b2c176e8641e06c0a57..4d7a046fc607bd125df812c1d6fd86d5343b76af 100644 (file)
@@ -26,6 +26,7 @@
 use std::cell::Cell;
 
 use syntax::ast;
+use syntax::ast_util;
 use syntax::codemap::{DUMMY_SP, Span};
 use syntax::print::pprust::pat_to_string;
 use syntax::visit;
@@ -113,6 +114,31 @@ fn visit_expr(&mut self, e: &ast::Expr) {
             return;
         }
 
+        // Hacky hack: During type-checking, we treat *all* operators
+        // as potentially overloaded. But then, during writeback, if
+        // we observe that something like `a+b` is (known to be)
+        // operating on scalars, we clear the overload.
+        match e.node {
+            ast::ExprBinary(ref op, ref lhs, ref rhs) => {
+                let lhs_ty = self.fcx.expr_ty(lhs);
+                let lhs_ty = self.fcx.infcx().resolve_type_vars_if_possible(&lhs_ty);
+                let rhs_ty = self.fcx.expr_ty(rhs);
+                let rhs_ty = self.fcx.infcx().resolve_type_vars_if_possible(&rhs_ty);
+                if ty::type_is_scalar(lhs_ty) && ty::type_is_scalar(rhs_ty) {
+                    self.fcx.inh.method_map.borrow_mut().remove(&MethodCall::expr(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.
+                    if !ast_util::is_by_value_binop(op.node) {
+                        self.fcx.inh.adjustments.borrow_mut().remove(&lhs.id);
+                    }
+                }
+            }
+            _ => { }
+        }
+
         self.visit_node_id(ResolvingExpr(e.span), e.id);
         self.visit_method_map_entry(ResolvingExpr(e.span),
                                     MethodCall::expr(e.id));
index 95e06879fb2235a066c2aa83104fe7ec9ca643b0..7d01bece01cb808188d027b30b37b22736fbbb49 100644 (file)
     E0321, // extended coherence rules for defaulted traits violated
     E0322, // cannot implement Sized explicitly
     E0366, // dropck forbid specialization to concrete type or region
-    E0367  // dropck forbid specialization to predicate not in struct/enum
+    E0367, // dropck forbid specialization to predicate not in struct/enum
+    E0368, // binary operation `<op>=` cannot be applied to types
+    E0369  // binary operation `<op>` cannot be applied to types
 }
 
 __build_diagnostic_array! { DIAGNOSTICS }
index b7aa2aebbfac16fee39f6edbf829ac3d3cfa12d6..b83b42c73e70899895dcd2e8f7ed367ef1859176 100644 (file)
@@ -86,33 +86,17 @@ pub fn is_shift_binop(b: BinOp_) -> bool {
 
 pub fn is_comparison_binop(b: BinOp_) -> bool {
     match b {
-        BiEq | BiLt | BiLe | BiNe | BiGt | BiGe => true,
-        _ => false
+        BiEq | BiLt | BiLe | BiNe | BiGt | BiGe =>
+            true,
+        BiAnd | BiOr | BiAdd | BiSub | BiMul | BiDiv | BiRem |
+        BiBitXor | BiBitAnd | BiBitOr | BiShl | BiShr =>
+            false,
     }
 }
 
 /// Returns `true` if the binary operator takes its arguments by value
 pub fn is_by_value_binop(b: BinOp_) -> bool {
-    match b {
-        BiAdd | BiSub | BiMul | BiDiv | BiRem | BiBitXor | BiBitAnd | BiBitOr | BiShl | BiShr => {
-            true
-        }
-        _ => false
-    }
-}
-
-/// Returns `true` if the binary operator is symmetric in the sense that LHS
-/// and RHS must have the same type. So the type of LHS can serve as an hint
-/// for the type of RHS and vice versa.
-pub fn is_symmetric_binop(b: BinOp_) -> bool {
-    match b {
-        BiAdd | BiSub | BiMul | BiDiv | BiRem |
-        BiBitXor | BiBitAnd | BiBitOr |
-        BiEq | BiLt | BiLe | BiNe | BiGt | BiGe => {
-            true
-        }
-        _ => false
-    }
+    !is_comparison_binop(b)
 }
 
 /// Returns `true` if the unary operator takes its argument by value