From 1935bbd91321b4ebb61b21a300f43234a0be6433 Mon Sep 17 00:00:00 2001 From: Edward Wang Date: Sun, 1 Feb 2015 02:49:12 +0800 Subject: [PATCH 1/2] Make use of a binary operator's RHS type for LHS inference For "symmetric" binary operators, meaning the types of two side must be equal, if the type of LHS doesn't know yet but RHS does, use that as an hint to infer LHS' type. Closes #21634 --- src/librustc_typeck/check/mod.rs | 57 +++++++++++++++++++++++--------- src/libsyntax/ast_util.rs | 14 ++++++++ src/test/run-pass/issue-21634.rs | 22 ++++++++++++ 3 files changed, 77 insertions(+), 16 deletions(-) create mode 100644 src/test/run-pass/issue-21634.rs diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 9dcde1c2a0a..a41f27cfea8 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,6 +5079,33 @@ pub fn instantiate_path<'a, 'tcx>(fcx: &FnCtxt<'a, '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 ty::type_is_ty_var(ty) { + 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>, @@ -5078,19 +5113,9 @@ pub fn structurally_resolved_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, ty: Ty<'tcx>) -> 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; - } - - ty + structurally_resolve_type_or_else(fcx, sp, ty, || { + fcx.tcx().types.err + }) } // Returns true if b contains a break that can exit from b diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs index 7f1264ac9a1..5049b87d694 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/run-pass/issue-21634.rs b/src/test/run-pass/issue-21634.rs new file mode 100644 index 00000000000..e5a2790917f --- /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 }); + } +} From 60fa1ff347588027c4a1786b978f386447c35016 Mon Sep 17 00:00:00 2001 From: Edward Wang Date: Sun, 1 Feb 2015 02:57:26 +0800 Subject: [PATCH 2/2] Fix fallout --- .../associated-types-ICE-when-projecting-out-of-err.rs | 4 ++-- src/test/compile-fail/issue-2149.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) 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 183781e9e24..b35d5131c78 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 691660f8971..4852dfc9a02 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() {