Skip to content

Make use of a binary operator's RHS type for LHS inference #21817

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 3, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 41 additions & 16 deletions src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
14 changes: 14 additions & 0 deletions src/libsyntax/ast_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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`
}
2 changes: 1 addition & 1 deletion src/test/compile-fail/issue-2149.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
22 changes: 22 additions & 0 deletions src/test/run-pass/issue-21634.rs
Original file line number Diff line number Diff line change
@@ -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 });
}
}