diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 9dcde1c2a0a50..a41f27cfea880 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -2815,11 +2815,19 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, BinopAssignment => PreferMutLvalue, SimpleBinop => NoPreference }; - check_expr_with_lvalue_pref(fcx, &*lhs, lvalue_pref); + check_expr_with_lvalue_pref(fcx, lhs, lvalue_pref); // Callee does bot / err checking - let lhs_t = structurally_resolved_type(fcx, lhs.span, - fcx.expr_ty(&*lhs)); + 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 uint, no matter what lhs is @@ -5071,28 +5079,45 @@ pub fn instantiate_path<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, } } -// Resolves `typ` by a single level if `typ` is a type variable. If no -// resolution is possible, then an error is reported. -pub fn structurally_resolved_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, - sp: Span, - ty: Ty<'tcx>) - -> Ty<'tcx> +fn structurally_resolve_type_or_else<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, + sp: Span, + ty: Ty<'tcx>, + f: F) -> Ty<'tcx> + where F: Fn() -> Ty<'tcx> { let mut ty = fcx.resolve_type_vars_if_possible(ty); - // If not, error. if ty::type_is_ty_var(ty) { - fcx.type_error_message(sp, |_actual| { - "the type of this value must be known in this \ - context".to_string() - }, ty, None); - demand::suptype(fcx, sp, fcx.tcx().types.err, ty); - ty = fcx.tcx().types.err; + let alternative = f(); + + // If not, error. + if ty::type_is_ty_var(alternative) || ty::type_is_error(alternative) { + fcx.type_error_message(sp, |_actual| { + "the type of this value must be known in this context".to_string() + }, ty, None); + demand::suptype(fcx, sp, fcx.tcx().types.err, ty); + ty = fcx.tcx().types.err; + } else { + demand::suptype(fcx, sp, alternative, ty); + ty = alternative; + } } ty } +// Resolves `typ` by a single level if `typ` is a type variable. If no +// resolution is possible, then an error is reported. +pub fn structurally_resolved_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, + sp: Span, + ty: Ty<'tcx>) + -> Ty<'tcx> +{ + structurally_resolve_type_or_else(fcx, sp, ty, || { + fcx.tcx().types.err + }) +} + // Returns true if b contains a break that can exit from b pub fn may_break(cx: &ty::ctxt, id: ast::NodeId, b: &ast::Block) -> bool { // First: is there an unlabeled break immediately diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs index 7f1264ac9a1d3..5049b87d69452 100644 --- a/src/libsyntax/ast_util.rs +++ b/src/libsyntax/ast_util.rs @@ -102,6 +102,20 @@ pub fn is_by_value_binop(b: BinOp_) -> bool { } } +/// 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 + } +} + /// Returns `true` if the unary operator takes its argument by value pub fn is_by_value_unop(u: UnOp) -> bool { match u { 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 183781e9e2417..b35d5131c781a 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 @@ -29,6 +29,6 @@ trait Add { fn ice(a: A) { let r = loop {}; - r = r + a; // here the type `r` is not yet inferred, hence `r+a` generates an error. - //~^ ERROR type of this value must be known + r = r + a; + //~^ ERROR binary operation `+` cannot be applied to type `A` } diff --git a/src/test/compile-fail/issue-2149.rs b/src/test/compile-fail/issue-2149.rs index 691660f897157..4852dfc9a02bc 100644 --- a/src/test/compile-fail/issue-2149.rs +++ b/src/test/compile-fail/issue-2149.rs @@ -16,7 +16,7 @@ impl vec_monad for Vec { fn bind(&self, mut f: F) where F: FnMut(A) -> Vec { let mut r = panic!(); for elt in self.iter() { r = r + f(*elt); } - //~^ ERROR the type of this value must be known + //~^ ERROR binary operation `+` cannot be applied to type `collections::vec::Vec` } } fn main() { diff --git a/src/test/run-pass/issue-21634.rs b/src/test/run-pass/issue-21634.rs new file mode 100644 index 0000000000000..e5a2790917ff0 --- /dev/null +++ b/src/test/run-pass/issue-21634.rs @@ -0,0 +1,22 @@ +// 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. + + +fn main() { + if let Ok(x) = "3.1415".parse() { + assert_eq!(false, x <= 0.0); + } + if let Ok(x) = "3.1415".parse() { + assert_eq!(3.1415, x + 0.0); + } + if let Ok(mut x) = "3.1415".parse() { + assert_eq!(8.1415, { x += 5.0; x }); + } +}