rollup merge of #21817: edwardw/symmetric-binop

For "symmetric" binary operators, meaning the types of two sides 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:
Alex Crichton 2015-02-02 10:58:10 -08:00
commit c64b73e520
5 changed files with 80 additions and 19 deletions

View File

@ -2858,11 +2858,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
@ -5114,6 +5122,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>,
@ -5121,19 +5156,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

@ -29,6 +29,6 @@ trait Add<RHS=Self> {
fn ice<A>(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`
}

View File

@ -16,7 +16,7 @@ impl<A> vec_monad<A> for Vec<A> {
fn bind<B, F>(&self, mut f: F) where F: FnMut(A) -> Vec<B> {
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<B>`
}
}
fn main() {

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 });
}
}