Skip to content

Commit 1935bbd

Browse files
committed
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
1 parent 474b324 commit 1935bbd

File tree

3 files changed

+77
-16
lines changed

3 files changed

+77
-16
lines changed

src/librustc_typeck/check/mod.rs

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2815,11 +2815,19 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
28152815
BinopAssignment => PreferMutLvalue,
28162816
SimpleBinop => NoPreference
28172817
};
2818-
check_expr_with_lvalue_pref(fcx, &*lhs, lvalue_pref);
2818+
check_expr_with_lvalue_pref(fcx, lhs, lvalue_pref);
28192819

28202820
// Callee does bot / err checking
2821-
let lhs_t = structurally_resolved_type(fcx, lhs.span,
2822-
fcx.expr_ty(&*lhs));
2821+
let lhs_t =
2822+
structurally_resolve_type_or_else(fcx, lhs.span, fcx.expr_ty(lhs), || {
2823+
if ast_util::is_symmetric_binop(op.node) {
2824+
// Try RHS first
2825+
check_expr(fcx, &**rhs);
2826+
fcx.expr_ty(&**rhs)
2827+
} else {
2828+
fcx.tcx().types.err
2829+
}
2830+
});
28232831

28242832
if ty::type_is_integral(lhs_t) && ast_util::is_shift_binop(op.node) {
28252833
// Shift is a special case: rhs must be uint, no matter what lhs is
@@ -5071,28 +5079,45 @@ pub fn instantiate_path<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
50715079
}
50725080
}
50735081

5074-
// Resolves `typ` by a single level if `typ` is a type variable. If no
5075-
// resolution is possible, then an error is reported.
5076-
pub fn structurally_resolved_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
5077-
sp: Span,
5078-
ty: Ty<'tcx>)
5079-
-> Ty<'tcx>
5082+
fn structurally_resolve_type_or_else<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
5083+
sp: Span,
5084+
ty: Ty<'tcx>,
5085+
f: F) -> Ty<'tcx>
5086+
where F: Fn() -> Ty<'tcx>
50805087
{
50815088
let mut ty = fcx.resolve_type_vars_if_possible(ty);
50825089

5083-
// If not, error.
50845090
if ty::type_is_ty_var(ty) {
5085-
fcx.type_error_message(sp, |_actual| {
5086-
"the type of this value must be known in this \
5087-
context".to_string()
5088-
}, ty, None);
5089-
demand::suptype(fcx, sp, fcx.tcx().types.err, ty);
5090-
ty = fcx.tcx().types.err;
5091+
let alternative = f();
5092+
5093+
// If not, error.
5094+
if ty::type_is_ty_var(alternative) || ty::type_is_error(alternative) {
5095+
fcx.type_error_message(sp, |_actual| {
5096+
"the type of this value must be known in this context".to_string()
5097+
}, ty, None);
5098+
demand::suptype(fcx, sp, fcx.tcx().types.err, ty);
5099+
ty = fcx.tcx().types.err;
5100+
} else {
5101+
demand::suptype(fcx, sp, alternative, ty);
5102+
ty = alternative;
5103+
}
50915104
}
50925105

50935106
ty
50945107
}
50955108

5109+
// Resolves `typ` by a single level if `typ` is a type variable. If no
5110+
// resolution is possible, then an error is reported.
5111+
pub fn structurally_resolved_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
5112+
sp: Span,
5113+
ty: Ty<'tcx>)
5114+
-> Ty<'tcx>
5115+
{
5116+
structurally_resolve_type_or_else(fcx, sp, ty, || {
5117+
fcx.tcx().types.err
5118+
})
5119+
}
5120+
50965121
// Returns true if b contains a break that can exit from b
50975122
pub fn may_break(cx: &ty::ctxt, id: ast::NodeId, b: &ast::Block) -> bool {
50985123
// 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/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)