Skip to content

Commit c64b73e

Browse files
committed
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
2 parents 22fdf97 + 60fa1ff commit c64b73e

File tree

5 files changed

+80
-19
lines changed

5 files changed

+80
-19
lines changed

src/librustc_typeck/check/mod.rs

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2858,11 +2858,19 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
28582858
BinopAssignment => PreferMutLvalue,
28592859
SimpleBinop => NoPreference
28602860
};
2861-
check_expr_with_lvalue_pref(fcx, &*lhs, lvalue_pref);
2861+
check_expr_with_lvalue_pref(fcx, lhs, lvalue_pref);
28622862

28632863
// Callee does bot / err checking
2864-
let lhs_t = structurally_resolved_type(fcx, lhs.span,
2865-
fcx.expr_ty(&*lhs));
2864+
let lhs_t =
2865+
structurally_resolve_type_or_else(fcx, lhs.span, fcx.expr_ty(lhs), || {
2866+
if ast_util::is_symmetric_binop(op.node) {
2867+
// Try RHS first
2868+
check_expr(fcx, &**rhs);
2869+
fcx.expr_ty(&**rhs)
2870+
} else {
2871+
fcx.tcx().types.err
2872+
}
2873+
});
28662874

28672875
if ty::type_is_integral(lhs_t) && ast_util::is_shift_binop(op.node) {
28682876
// Shift is a special case: rhs must be uint, no matter what lhs is
@@ -5114,28 +5122,45 @@ pub fn instantiate_path<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
51145122
}
51155123
}
51165124

5117-
// Resolves `typ` by a single level if `typ` is a type variable. If no
5118-
// resolution is possible, then an error is reported.
5119-
pub fn structurally_resolved_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
5120-
sp: Span,
5121-
ty: Ty<'tcx>)
5122-
-> Ty<'tcx>
5125+
fn structurally_resolve_type_or_else<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
5126+
sp: Span,
5127+
ty: Ty<'tcx>,
5128+
f: F) -> Ty<'tcx>
5129+
where F: Fn() -> Ty<'tcx>
51235130
{
51245131
let mut ty = fcx.resolve_type_vars_if_possible(ty);
51255132

5126-
// If not, error.
51275133
if ty::type_is_ty_var(ty) {
5128-
fcx.type_error_message(sp, |_actual| {
5129-
"the type of this value must be known in this \
5130-
context".to_string()
5131-
}, ty, None);
5132-
demand::suptype(fcx, sp, fcx.tcx().types.err, ty);
5133-
ty = fcx.tcx().types.err;
5134+
let alternative = f();
5135+
5136+
// If not, error.
5137+
if ty::type_is_ty_var(alternative) || ty::type_is_error(alternative) {
5138+
fcx.type_error_message(sp, |_actual| {
5139+
"the type of this value must be known in this context".to_string()
5140+
}, ty, None);
5141+
demand::suptype(fcx, sp, fcx.tcx().types.err, ty);
5142+
ty = fcx.tcx().types.err;
5143+
} else {
5144+
demand::suptype(fcx, sp, alternative, ty);
5145+
ty = alternative;
5146+
}
51345147
}
51355148

51365149
ty
51375150
}
51385151

5152+
// Resolves `typ` by a single level if `typ` is a type variable. If no
5153+
// resolution is possible, then an error is reported.
5154+
pub fn structurally_resolved_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
5155+
sp: Span,
5156+
ty: Ty<'tcx>)
5157+
-> Ty<'tcx>
5158+
{
5159+
structurally_resolve_type_or_else(fcx, sp, ty, || {
5160+
fcx.tcx().types.err
5161+
})
5162+
}
5163+
51395164
// Returns true if b contains a break that can exit from b
51405165
pub fn may_break(cx: &ty::ctxt, id: ast::NodeId, b: &ast::Block) -> bool {
51415166
// First: is there an unlabeled break immediately

src/libsyntax/ast_util.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,20 @@ pub fn is_by_value_binop(b: BinOp_) -> bool {
102102
}
103103
}
104104

105+
/// Returns `true` if the binary operator is symmetric in the sense that LHS
106+
/// and RHS must have the same type. So the type of LHS can serve as an hint
107+
/// for the type of RHS and vice versa.
108+
pub fn is_symmetric_binop(b: BinOp_) -> bool {
109+
match b {
110+
BiAdd | BiSub | BiMul | BiDiv | BiRem |
111+
BiBitXor | BiBitAnd | BiBitOr |
112+
BiEq | BiLt | BiLe | BiNe | BiGt | BiGe => {
113+
true
114+
}
115+
_ => false
116+
}
117+
}
118+
105119
/// Returns `true` if the unary operator takes its argument by value
106120
pub fn is_by_value_unop(u: UnOp) -> bool {
107121
match u {

src/test/compile-fail/associated-types-ICE-when-projecting-out-of-err.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,6 @@ trait Add<RHS=Self> {
2929

3030
fn ice<A>(a: A) {
3131
let r = loop {};
32-
r = r + a; // here the type `r` is not yet inferred, hence `r+a` generates an error.
33-
//~^ ERROR type of this value must be known
32+
r = r + a;
33+
//~^ ERROR binary operation `+` cannot be applied to type `A`
3434
}

src/test/compile-fail/issue-2149.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ impl<A> vec_monad<A> for Vec<A> {
1616
fn bind<B, F>(&self, mut f: F) where F: FnMut(A) -> Vec<B> {
1717
let mut r = panic!();
1818
for elt in self.iter() { r = r + f(*elt); }
19-
//~^ ERROR the type of this value must be known
19+
//~^ ERROR binary operation `+` cannot be applied to type `collections::vec::Vec<B>`
2020
}
2121
}
2222
fn main() {

src/test/run-pass/issue-21634.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
12+
fn main() {
13+
if let Ok(x) = "3.1415".parse() {
14+
assert_eq!(false, x <= 0.0);
15+
}
16+
if let Ok(x) = "3.1415".parse() {
17+
assert_eq!(3.1415, x + 0.0);
18+
}
19+
if let Ok(mut x) = "3.1415".parse() {
20+
assert_eq!(8.1415, { x += 5.0; x });
21+
}
22+
}

0 commit comments

Comments
 (0)