diff --git a/src/libcollectionstest/bench.rs b/src/libcollectionstest/bench.rs index e883b07dc5a48..8f2e71b666c6b 100644 --- a/src/libcollectionstest/bench.rs +++ b/src/libcollectionstest/bench.rs @@ -22,13 +22,13 @@ macro_rules! map_insert_rand_bench { let mut rng = rand::weak_rng(); for _ in 0..n { - let i = rng.gen() % n; + let i = rng.gen::() % n; map.insert(i, i); } // measure b.iter(|| { - let k = rng.gen() % n; + let k = rng.gen::() % n; map.insert(k, k); map.remove(&k); }); @@ -77,7 +77,7 @@ macro_rules! map_find_rand_bench { // setup let mut rng = rand::weak_rng(); - let mut keys: Vec<_> = (0..n).map(|_| rng.gen() % n).collect(); + let mut keys: Vec<_> = (0..n).map(|_| rng.gen::() % n).collect(); for &k in &keys { map.insert(k, k); diff --git a/src/libcore/ops.rs b/src/libcore/ops.rs index 26deb80d8c51f..862eb16d0bfb3 100644 --- a/src/libcore/ops.rs +++ b/src/libcore/ops.rs @@ -485,6 +485,7 @@ pub trait Neg { macro_rules! neg_impl { ($($t:ty)*) => ($( #[stable(feature = "rust1", since = "1.0.0")] + #[allow(unsigned_negation)] impl Neg for $t { #[stable(feature = "rust1", since = "1.0.0")] type Output = $t; @@ -498,28 +499,7 @@ macro_rules! neg_impl { )*) } -macro_rules! neg_uint_impl { - ($t:ty, $t_signed:ty) => { - #[stable(feature = "rust1", since = "1.0.0")] - impl Neg for $t { - type Output = $t; - - #[inline] - fn neg(self) -> $t { -(self as $t_signed) as $t } - } - - forward_ref_unop! { impl Neg, neg for $t } - } -} - -neg_impl! { isize i8 i16 i32 i64 f32 f64 } - -neg_uint_impl! { usize, isize } -neg_uint_impl! { u8, i8 } -neg_uint_impl! { u16, i16 } -neg_uint_impl! { u32, i32 } -neg_uint_impl! { u64, i64 } - +neg_impl! { usize u8 u16 u32 u64 isize i8 i16 i32 i64 f32 f64 } /// The `Not` trait is used to specify the functionality of unary `!`. /// diff --git a/src/librand/distributions/mod.rs b/src/librand/distributions/mod.rs index cb0829f52457e..62189e721e59d 100644 --- a/src/librand/distributions/mod.rs +++ b/src/librand/distributions/mod.rs @@ -256,7 +256,7 @@ fn ziggurat( return zero_case(rng, u); } // algebraically equivalent to f1 + DRanU()*(f0 - f1) < 1 - if f_tab[i + 1] + (f_tab[i] - f_tab[i + 1]) * rng.gen() < pdf(x) { + if f_tab[i + 1] + (f_tab[i] - f_tab[i + 1]) * rng.gen::() < pdf(x) { return x; } } diff --git a/src/librand/distributions/range.rs b/src/librand/distributions/range.rs index 0f74e67f5a724..347d494259d08 100644 --- a/src/librand/distributions/range.rs +++ b/src/librand/distributions/range.rs @@ -154,7 +154,7 @@ macro_rules! float_impl { } } fn sample_range(r: &Range<$ty>, rng: &mut R) -> $ty { - r.low + r.range * rng.gen() + r.low + r.range * rng.gen::<$ty>() } } } diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 89af3e8f3a97b..a788386d2424f 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -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, 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)) { diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index bc83df419c358..61e332f87ad1a 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -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) => { diff --git a/src/librustc_trans/trans/consts.rs b/src/librustc_trans/trans/consts.rs index 348335139da64..0a9df2b5dc180 100644 --- a/src/librustc_trans/trans/consts.rs +++ b/src/librustc_trans/trans/consts.rs @@ -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]| { 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) diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index c0ad279d744a8..46cbd1936002a 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -2384,6 +2384,7 @@ fn deref_once<'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 @@ impl OverflowOpViaIntrinsic { _ => 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 { diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs index 6ba21e25e1fe5..31ac0a57ba0e1 100644 --- a/src/librustc_typeck/check/callee.rs +++ b/src/librustc_typeck/check/callee.rs @@ -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); diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index def877d92b523..1f992b9c3ae6c 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -79,7 +79,6 @@ type parameter). 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}; @@ -142,6 +141,7 @@ pub mod wf; mod closure; mod callee; mod compare_method; +mod op; /// closures defined within the function. For example: /// @@ -288,15 +288,6 @@ impl 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 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// 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 @@ impl<'a, 'tcx> FnCtxt<'a, '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 @@ impl<'a, 'tcx> FnCtxt<'a, '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], - 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], - 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_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, fn_ty, expr, &args[1..], - AutorefArgs::No, DontTupleArguments, expected); @@ -2806,277 +2766,6 @@ fn check_expr_with_unifier<'a, 'tcx, F>(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, - lhs: &'a ast::Expr, - rhs: Option<&'tcx P>, - 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, - 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) -> 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, - 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_expr_with_unifier<'a, 'tcx, F>(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_expr_with_unifier<'a, 'tcx, F>(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_expr_with_unifier<'a, 'tcx, F>(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_expr_with_unifier<'a, 'tcx, F>(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 index 0000000000000..49e88dc1483eb --- /dev/null +++ b/src/librustc_typeck/check/op.rs @@ -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 or the MIT license +// , 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 = 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, + 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) { + 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>, + opname: ast::Name, + trait_did: Option, + lhs_expr: &'a ast::Expr) + -> Result,()> +{ + 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) + } + } +} + diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs index e555d3085a4c7..4d7a046fc607b 100644 --- a/src/librustc_typeck/check/writeback.rs +++ b/src/librustc_typeck/check/writeback.rs @@ -26,6 +26,7 @@ use util::ppaux::Repr; 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 @@ impl<'cx, 'tcx, 'v> Visitor<'v> for WritebackCx<'cx, 'tcx> { 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)); diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 95e06879fb223..7d01bece01cb8 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -179,7 +179,9 @@ register_diagnostics! { 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 `=` cannot be applied to types + E0369 // binary operation `` cannot be applied to types } __build_diagnostic_array! { DIAGNOSTICS } diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs index b7aa2aebbfac1..b83b42c73e708 100644 --- a/src/libsyntax/ast_util.rs +++ b/src/libsyntax/ast_util.rs @@ -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 diff --git a/src/test/auxiliary/lang-item-public.rs b/src/test/auxiliary/lang-item-public.rs index 3b4547e31f5d5..72dfc75f41b96 100644 --- a/src/test/auxiliary/lang-item-public.rs +++ b/src/test/auxiliary/lang-item-public.rs @@ -32,3 +32,22 @@ extern fn eh_personality() {} pub trait Copy : PhantomFn { // Empty. } + +#[lang="rem"] +pub trait Rem { + type Output = Self; + fn rem(self, rhs: RHS) -> Self::Output; +} + +impl Rem for isize { + type Output = isize; + + #[inline] + fn rem(self, other: isize) -> isize { + // if you use `self % other` here, as one would expect, you + // get back an error because of potential failure/overflow, + // which tries to invoke error fns that don't have the + // appropriate signatures anymore. So...just return 0. + 0 + } +} diff --git a/src/test/bench/noise.rs b/src/test/bench/noise.rs index ff2428286d262..d6577036b8ebe 100644 --- a/src/test/bench/noise.rs +++ b/src/test/bench/noise.rs @@ -29,7 +29,7 @@ fn lerp(a: f32, b: f32, v: f32) -> f32 { a * (1.0 - v) + b * v } fn smooth(v: f32) -> f32 { v * v * (3.0 - 2.0 * v) } fn random_gradient(r: &mut R) -> Vec2 { - let v = PI * 2.0 * r.gen(); + let v = PI * 2.0 * r.gen::(); Vec2 { x: v.cos(), y: v.sin() } } diff --git a/src/test/compile-fail/assignment-operator-unimplemented.rs b/src/test/compile-fail/assignment-operator-unimplemented.rs index 5b24c6bd79f96..fef27af59571b 100644 --- a/src/test/compile-fail/assignment-operator-unimplemented.rs +++ b/src/test/compile-fail/assignment-operator-unimplemented.rs @@ -13,5 +13,5 @@ struct Foo; fn main() { let mut a = Foo; let ref b = Foo; - a += *b; //~ Error: binary assignment operation `+=` cannot be applied to type `Foo` + a += *b; //~ Error: binary assignment operation `+=` cannot be applied to types `Foo` and `Foo` } diff --git a/src/test/compile-fail/associated-types-ICE-when-projecting-out-of-err.rs b/src/test/compile-fail/associated-types-ICE-when-projecting-out-of-err.rs index edd1b8255ccdc..04170779ed2f6 100644 --- a/src/test/compile-fail/associated-types-ICE-when-projecting-out-of-err.rs +++ b/src/test/compile-fail/associated-types-ICE-when-projecting-out-of-err.rs @@ -35,5 +35,6 @@ trait Add { fn ice(a: A) { let r = loop {}; r = r + a; - //~^ ERROR binary operation `+` cannot be applied to type `A` + //~^ ERROR not implemented + //~| ERROR not implemented } diff --git a/src/test/compile-fail/binop-logic-float.rs b/src/test/compile-fail/binop-logic-float.rs index 923d611cebeea..f3fb5a08c8541 100644 --- a/src/test/compile-fail/binop-logic-float.rs +++ b/src/test/compile-fail/binop-logic-float.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern:`||` cannot be applied to type `f32` - fn main() { let x = 1.0_f32 || 2.0_f32; } +//~^ ERROR mismatched types +//~| ERROR mismatched types + diff --git a/src/test/compile-fail/binop-logic-int.rs b/src/test/compile-fail/binop-logic-int.rs index d5dd9e00902f9..f5e53f84c16e6 100644 --- a/src/test/compile-fail/binop-logic-int.rs +++ b/src/test/compile-fail/binop-logic-int.rs @@ -8,6 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern:`&&` cannot be applied to type `_` - fn main() { let x = 1 && 2; } +//~^ ERROR mismatched types +//~| ERROR mismatched types diff --git a/src/test/compile-fail/fn-compare-mismatch.rs b/src/test/compile-fail/fn-compare-mismatch.rs index 6178d37a5bd6c..27be1ada44550 100644 --- a/src/test/compile-fail/fn-compare-mismatch.rs +++ b/src/test/compile-fail/fn-compare-mismatch.rs @@ -13,4 +13,5 @@ fn main() { fn g() { } let x = f == g; //~^ ERROR binary operation `==` cannot be applied + //~| ERROR mismatched types } diff --git a/src/test/compile-fail/issue-11771.rs b/src/test/compile-fail/issue-11771.rs index 2de86e527ef59..40fc6b1ed6aaa 100644 --- a/src/test/compile-fail/issue-11771.rs +++ b/src/test/compile-fail/issue-11771.rs @@ -11,19 +11,13 @@ fn main() { let x = (); 1 + - x //~ ERROR mismatched types - //~| expected `_` - //~| found `()` - //~| expected integral variable - //~| found () + x //~^ ERROR E0277 + //~| ERROR E0277 ; let x: () = (); 1 + - x //~ ERROR mismatched types - //~| expected `_` - //~| found `()` - //~| expected integral variable - //~| found () + x //~^ ERROR E0277 + //~| ERROR E0277 ; } diff --git a/src/test/run-pass/issue-13352.rs b/src/test/compile-fail/issue-13352.rs similarity index 94% rename from src/test/run-pass/issue-13352.rs rename to src/test/compile-fail/issue-13352.rs index af31fee048c6a..a8c8c8b40c1b6 100644 --- a/src/test/run-pass/issue-13352.rs +++ b/src/test/compile-fail/issue-13352.rs @@ -23,4 +23,6 @@ fn main() { unsafe { libc::exit(0 as libc::c_int); } }); 2_usize + (loop {}); + //~^ ERROR E0277 + //~| ERROR E0277 } diff --git a/src/test/compile-fail/issue-2149.rs b/src/test/compile-fail/issue-2149.rs index 37dbcaf39bd10..ea305c96af4a1 100644 --- a/src/test/compile-fail/issue-2149.rs +++ b/src/test/compile-fail/issue-2149.rs @@ -16,7 +16,8 @@ impl vec_monad for Vec { fn bind(&self, mut f: F) where F: FnMut(A) -> Vec { let mut r = panic!(); for elt in self { r = r + f(*elt); } - //~^ ERROR binary operation `+` cannot be applied to type `collections::vec::Vec` + //~^ ERROR E0277 + //~| ERROR E0277 } } fn main() { diff --git a/src/test/compile-fail/issue-5239-1.rs b/src/test/compile-fail/issue-5239-1.rs index 49a43ee37adca..1ebef06008ffa 100644 --- a/src/test/compile-fail/issue-5239-1.rs +++ b/src/test/compile-fail/issue-5239-1.rs @@ -12,5 +12,5 @@ fn main() { let x = |ref x: isize| -> isize { x += 1; }; - //~^ ERROR binary assignment operation `+=` cannot be applied to type `&isize` + //~^ ERROR E0368 } diff --git a/src/test/compile-fail/shift-various-bad-types.rs b/src/test/compile-fail/shift-various-bad-types.rs index 901ae1d5e2ab1..24b66213b39bd 100644 --- a/src/test/compile-fail/shift-various-bad-types.rs +++ b/src/test/compile-fail/shift-various-bad-types.rs @@ -17,19 +17,19 @@ struct Panolpy { fn foo(p: &Panolpy) { 22 >> p.char; - //~^ ERROR right-hand-side of a shift operation must have integral type + //~^ ERROR E0277 + //~| ERROR E0277 22 >> p.str; - //~^ ERROR right-hand-side of a shift operation must have integral type + //~^ ERROR E0277 + //~| ERROR E0277 22 >> p; - //~^ ERROR right-hand-side of a shift operation must have integral type + //~^ ERROR E0277 + //~| ERROR E0277 - // We could be more accepting in the case of a type not yet inferred, but not - // known to be an integer, but meh. let x; - 22 >> x; - //~^ ERROR the type of this value must be known in this context + 22 >> x; // ambiguity error winds up being suppressed 22 >> 1; // Integer literal types are OK diff --git a/src/test/compile-fail/simd-binop.rs b/src/test/compile-fail/simd-binop.rs index f028c9af46235..feffe5c0b06c8 100644 --- a/src/test/compile-fail/simd-binop.rs +++ b/src/test/compile-fail/simd-binop.rs @@ -10,6 +10,7 @@ // ignore-tidy-linelength +#![feature(core)] use std::simd::f32x4; diff --git a/src/test/pretty/issue-929.rs b/src/test/pretty/issue-929.rs deleted file mode 100644 index 75a6b919342bf..0000000000000 --- a/src/test/pretty/issue-929.rs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2012 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -fn f() { if (1 == panic!()) { } else { } } - -fn main() { } diff --git a/src/test/compile-fail/binop-fail-3.rs b/src/test/run-fail/binop-fail-3.rs similarity index 84% rename from src/test/compile-fail/binop-fail-3.rs rename to src/test/run-fail/binop-fail-3.rs index 097a52b894179..8cabd3b326296 100644 --- a/src/test/compile-fail/binop-fail-3.rs +++ b/src/test/run-fail/binop-fail-3.rs @@ -8,9 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// error-pattern:quux fn foo() -> ! { panic!("quux"); } fn main() { - foo() //~ ERROR the type of this value must be known in this context - == - foo(); + foo() == foo(); // these types wind up being defaulted to () } diff --git a/src/test/run-fail/bounds-check-no-overflow.rs b/src/test/run-fail/bounds-check-no-overflow.rs index c15c4b83828a3..4d502cb2106b1 100644 --- a/src/test/run-fail/bounds-check-no-overflow.rs +++ b/src/test/run-fail/bounds-check-no-overflow.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern:index out of bounds: the len is 3 but the index is +// error-pattern:assertion failed: index < self.len() use std::usize; use std::mem::size_of; diff --git a/src/test/run-pass/binops-issue-22743.rs b/src/test/run-pass/binops-issue-22743.rs new file mode 100644 index 0000000000000..01c85023eda39 --- /dev/null +++ b/src/test/run-pass/binops-issue-22743.rs @@ -0,0 +1,32 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::ops::Mul; + +#[derive(Copy)] +pub struct Foo { + x: f64, +} + +impl Mul for f64 { + type Output = Foo; + + fn mul(self, rhs: Foo) -> Foo { + // intentionally do something that is not * + Foo { x: self + rhs.x } + } +} + +pub fn main() { + let f: Foo = Foo { x: 5.0 }; + let val: f64 = 3.0; + let f2: Foo = val * f; + assert_eq!(f2.x, 8.0); +} diff --git a/src/test/run-pass/dst-raw.rs b/src/test/run-pass/dst-raw.rs index c8f8218cc28dc..5e0e5bd03fe6f 100644 --- a/src/test/run-pass/dst-raw.rs +++ b/src/test/run-pass/dst-raw.rs @@ -56,7 +56,7 @@ pub fn main() { } // raw slice with explicit cast - let a = &[1, 2, 3] as *const [_]; + let a = &[1, 2, 3] as *const [i32]; unsafe { let b = (*a)[2]; assert!(b == 3); @@ -96,7 +96,7 @@ pub fn main() { assert!(len == 3); } - let a = &mut [1, 2, 3] as *mut [_]; + let a = &mut [1, 2, 3] as *mut [i32]; unsafe { let b = (*a)[2]; assert!(b == 3); diff --git a/src/test/run-pass/early-ret-binop-add.rs b/src/test/run-pass/early-ret-binop-add.rs index b01d6523bf01e..7bd292e66f258 100644 --- a/src/test/run-pass/early-ret-binop-add.rs +++ b/src/test/run-pass/early-ret-binop-add.rs @@ -10,5 +10,8 @@ // pretty-expanded FIXME #23616 -fn wsucc(n: isize) -> isize { 0 + { return n + 1 } } +use std::num::Int; + +fn wsucc(n: T) -> T { n + { return n } } + pub fn main() { } diff --git a/src/test/run-pass/issue-1460.rs b/src/test/run-pass/issue-1460.rs index 6d2d02d2b8b63..6e1cfc7186299 100644 --- a/src/test/run-pass/issue-1460.rs +++ b/src/test/run-pass/issue-1460.rs @@ -12,5 +12,5 @@ // pretty-expanded FIXME #23616 pub fn main() { - {|i| if 1 == i { }}; + {|i: u32| if 1 == i { }}; } diff --git a/src/test/run-pass/issue-16560.rs b/src/test/run-pass/issue-16560.rs index 15a5080f5a242..33842fab6989c 100644 --- a/src/test/run-pass/issue-16560.rs +++ b/src/test/run-pass/issue-16560.rs @@ -17,7 +17,7 @@ use std::mem; fn main() { let y = 0u8; - let closure = move |x| y + x; + let closure = move |x: u8| y + x; // Check that both closures are capturing by value assert_eq!(1, mem::size_of_val(&closure)); diff --git a/src/test/run-pass/issue-21634.rs b/src/test/run-pass/issue-21634.rs index 53297d0a8f3b9..fe540e1aabef8 100644 --- a/src/test/run-pass/issue-21634.rs +++ b/src/test/run-pass/issue-21634.rs @@ -12,13 +12,13 @@ // pretty-expanded FIXME #23616 fn main() { - if let Ok(x) = "3.1415".parse() { + if let Ok(x) = "3.1415".parse::() { assert_eq!(false, x <= 0.0); } - if let Ok(x) = "3.1415".parse() { + if let Ok(x) = "3.1415".parse::() { assert_eq!(3.1415, x + 0.0); } - if let Ok(mut x) = "3.1415".parse() { + if let Ok(mut x) = "3.1415".parse::() { assert_eq!(8.1415, { x += 5.0; x }); } } diff --git a/src/test/run-pass/issue-8460.rs b/src/test/run-pass/issue-8460.rs index 0ef668794ec0d..7d8c4ab210d00 100644 --- a/src/test/run-pass/issue-8460.rs +++ b/src/test/run-pass/issue-8460.rs @@ -25,19 +25,19 @@ fn main() { assert!(thread::spawn(move|| { min_val::() / -1; }).join().is_err()); assert!(thread::spawn(move|| { min_val::() / -1; }).join().is_err()); assert!(thread::spawn(move|| { min_val::() / -1; }).join().is_err()); - assert!(thread::spawn(move|| { 1isize / zero(); }).join().is_err()); - assert!(thread::spawn(move|| { 1i8 / zero(); }).join().is_err()); - assert!(thread::spawn(move|| { 1i16 / zero(); }).join().is_err()); - assert!(thread::spawn(move|| { 1i32 / zero(); }).join().is_err()); - assert!(thread::spawn(move|| { 1i64 / zero(); }).join().is_err()); + assert!(thread::spawn(move|| { 1isize / zero::(); }).join().is_err()); + assert!(thread::spawn(move|| { 1i8 / zero::(); }).join().is_err()); + assert!(thread::spawn(move|| { 1i16 / zero::(); }).join().is_err()); + assert!(thread::spawn(move|| { 1i32 / zero::(); }).join().is_err()); + assert!(thread::spawn(move|| { 1i64 / zero::(); }).join().is_err()); assert!(thread::spawn(move|| { min_val::() % -1; }).join().is_err()); assert!(thread::spawn(move|| { min_val::() % -1; }).join().is_err()); assert!(thread::spawn(move|| { min_val::() % -1; }).join().is_err()); assert!(thread::spawn(move|| { min_val::() % -1; }).join().is_err()); assert!(thread::spawn(move|| { min_val::() % -1; }).join().is_err()); - assert!(thread::spawn(move|| { 1isize % zero(); }).join().is_err()); - assert!(thread::spawn(move|| { 1i8 % zero(); }).join().is_err()); - assert!(thread::spawn(move|| { 1i16 % zero(); }).join().is_err()); - assert!(thread::spawn(move|| { 1i32 % zero(); }).join().is_err()); - assert!(thread::spawn(move|| { 1i64 % zero(); }).join().is_err()); + assert!(thread::spawn(move|| { 1isize % zero::(); }).join().is_err()); + assert!(thread::spawn(move|| { 1i8 % zero::(); }).join().is_err()); + assert!(thread::spawn(move|| { 1i16 % zero::(); }).join().is_err()); + assert!(thread::spawn(move|| { 1i32 % zero::(); }).join().is_err()); + assert!(thread::spawn(move|| { 1i64 % zero::(); }).join().is_err()); } diff --git a/src/test/run-pass/lang-item-public.rs b/src/test/run-pass/lang-item-public.rs index 780bb52b3e8b7..f5b9bd4fbaa69 100644 --- a/src/test/run-pass/lang-item-public.rs +++ b/src/test/run-pass/lang-item-public.rs @@ -46,5 +46,5 @@ extern {} #[start] fn main(_: isize, _: *const *const u8) -> isize { - 1 % 1 + 1_isize % 1_isize } diff --git a/src/test/run-pass/reexported-static-methods-cross-crate.rs b/src/test/run-pass/reexported-static-methods-cross-crate.rs index 374d0d8d9b961..3efd913cf543c 100644 --- a/src/test/run-pass/reexported-static-methods-cross-crate.rs +++ b/src/test/run-pass/reexported-static-methods-cross-crate.rs @@ -19,8 +19,8 @@ use reexported_static_methods::Boz; use reexported_static_methods::Bort; pub fn main() { - assert_eq!(42, Foo::foo()); - assert_eq!(84, Baz::bar()); + assert_eq!(42_isize, Foo::foo()); + assert_eq!(84_isize, Baz::bar()); assert!(Boz::boz(1)); assert_eq!("bort()".to_string(), Bort::bort()); }