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
This commit is contained in:
Edward Wang 2015-02-01 02:49:12 +08:00
parent 474b324eda
commit 1935bbd913
3 changed files with 77 additions and 16 deletions

View File

@ -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

View File

@ -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 {

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
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 });
}
}