From d649292e601a47b3b602415a2e9d9b6ebc298d45 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 24 Mar 2015 15:34:59 -0400 Subject: [PATCH 1/5] Implement new type-checking strategy for binary operators. Basically, 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 | 84 +---- src/librustc_trans/trans/base.rs | 2 +- src/librustc_trans/trans/consts.rs | 12 +- src/librustc_trans/trans/expr.rs | 5 +- src/librustc_typeck/check/callee.rs | 4 - src/librustc_typeck/check/mod.rs | 399 ++------------------ src/librustc_typeck/check/op.rs | 481 +++++++++++++++++++++++++ src/librustc_typeck/check/writeback.rs | 26 ++ src/librustc_typeck/diagnostics.rs | 4 +- src/libsyntax/ast_util.rs | 28 +- 10 files changed, 573 insertions(+), 472 deletions(-) create mode 100644 src/librustc_typeck/check/op.rs 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 From d6466ff13aef6af45f24f28e23f2f3dd36c96cf0 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 24 Mar 2015 15:36:59 -0400 Subject: [PATCH 2/5] Driveby cleanup of the impl for negation, which had some kind of surprising casts. This version more obviously corresponds to the builtin semantics. --- src/libcore/ops.rs | 24 ++----------------- .../{run-pass => compile-fail}/issue-13352.rs | 2 ++ 2 files changed, 4 insertions(+), 22 deletions(-) rename src/test/{run-pass => compile-fail}/issue-13352.rs (94%) 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/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 } From c92bdcb232da3973a8a548e6b2044b610e286210 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 24 Mar 2015 15:45:11 -0400 Subject: [PATCH 3/5] Fallout where types must be specified. This is due to a [breaking-change] to operators. The primary affected code is uses of the `Rng` trait where we used to (incorrectly) infer the right-hand-side type from the left-hand-side, in the case that the LHS type was a scalar like `i32`. The fix is to add a type annotation like `x + rng.gen::()`. --- src/libcollectionstest/bench.rs | 6 +++--- src/librand/distributions/mod.rs | 2 +- src/librand/distributions/range.rs | 2 +- src/test/auxiliary/lang-item-public.rs | 18 +++++++++++++++++ src/test/bench/noise.rs | 2 +- src/test/run-pass/dst-raw.rs | 4 ++-- src/test/run-pass/early-ret-binop-add.rs | 5 ++++- src/test/run-pass/issue-1460.rs | 2 +- src/test/run-pass/issue-16560.rs | 2 +- src/test/run-pass/issue-21634.rs | 6 +++--- src/test/run-pass/issue-8460.rs | 20 +++++++++---------- .../reexported-static-methods-cross-crate.rs | 4 ++-- 12 files changed, 47 insertions(+), 26 deletions(-) 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/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/test/auxiliary/lang-item-public.rs b/src/test/auxiliary/lang-item-public.rs index 3b4547e31f5d5..3c416dc2ef8d1 100644 --- a/src/test/auxiliary/lang-item-public.rs +++ b/src/test/auxiliary/lang-item-public.rs @@ -32,3 +32,21 @@ extern fn eh_personality() {} pub trait Copy : PhantomFn { // Empty. } + +#[lang="rem"] +pub trait Rem { + /// The resulting type after applying the `%` operator + #[stable(feature = "rust1", since = "1.0.0")] + type Output = Self; + + /// The method for the `%` operator + #[stable(feature = "rust1", since = "1.0.0")] + fn rem(self, rhs: RHS) -> Self::Output; +} + +impl Rem for i32 { + type Output = i32; + + #[inline] + fn rem(self, other: i32) -> i32 { self % other } +} 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/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/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()); } From e2b2a53d704899e5e59c943ab39fa692125233be Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 30 Mar 2015 04:56:24 -0400 Subject: [PATCH 4/5] Fallout in tests: largely changes to error messages. --- src/test/auxiliary/lang-item-public.rs | 17 +++++++++-------- .../assignment-operator-unimplemented.rs | 2 +- ...ated-types-ICE-when-projecting-out-of-err.rs | 3 ++- src/test/compile-fail/binop-logic-float.rs | 5 +++-- src/test/compile-fail/binop-logic-int.rs | 4 ++-- src/test/compile-fail/fn-compare-mismatch.rs | 1 + src/test/compile-fail/issue-11771.rs | 14 ++++---------- src/test/compile-fail/issue-2149.rs | 3 ++- src/test/compile-fail/issue-5239-1.rs | 2 +- .../compile-fail/shift-various-bad-types.rs | 14 +++++++------- src/test/compile-fail/simd-binop.rs | 1 + src/test/pretty/issue-929.rs | 13 ------------- .../{compile-fail => run-fail}/binop-fail-3.rs | 5 ++--- src/test/run-fail/bounds-check-no-overflow.rs | 2 +- src/test/run-pass/lang-item-public.rs | 2 +- 15 files changed, 37 insertions(+), 51 deletions(-) delete mode 100644 src/test/pretty/issue-929.rs rename src/test/{compile-fail => run-fail}/binop-fail-3.rs (84%) diff --git a/src/test/auxiliary/lang-item-public.rs b/src/test/auxiliary/lang-item-public.rs index 3c416dc2ef8d1..72dfc75f41b96 100644 --- a/src/test/auxiliary/lang-item-public.rs +++ b/src/test/auxiliary/lang-item-public.rs @@ -35,18 +35,19 @@ pub trait Copy : PhantomFn { #[lang="rem"] pub trait Rem { - /// The resulting type after applying the `%` operator - #[stable(feature = "rust1", since = "1.0.0")] type Output = Self; - - /// The method for the `%` operator - #[stable(feature = "rust1", since = "1.0.0")] fn rem(self, rhs: RHS) -> Self::Output; } -impl Rem for i32 { - type Output = i32; +impl Rem for isize { + type Output = isize; #[inline] - fn rem(self, other: i32) -> i32 { self % other } + 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/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/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/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 } From 7595c25ef93bb18b5f96805daee4d1890f5b6a6b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 24 Mar 2015 17:28:28 -0400 Subject: [PATCH 5/5] Add test case for #22743. Fixes #22743. Fixes #19035. Fixes #22099. (Those all seem to be exactly the same scenario.) --- src/test/run-pass/binops-issue-22743.rs | 32 +++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 src/test/run-pass/binops-issue-22743.rs 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); +}