]> git.lizzy.rs Git - rust.git/commitdiff
Make use of a binary operator's RHS type for LHS inference
authorEdward Wang <edward.yu.wang@gmail.com>
Sat, 31 Jan 2015 18:49:12 +0000 (02:49 +0800)
committerEdward Wang <edward.yu.wang@gmail.com>
Sat, 31 Jan 2015 20:06:06 +0000 (04:06 +0800)
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

src/librustc_typeck/check/mod.rs
src/libsyntax/ast_util.rs
src/test/run-pass/issue-21634.rs [new file with mode: 0644]

index 9dcde1c2a0a50cc9cb4ed5438d2bec8526b6b6e5..a41f27cfea88092921ddc44cef1a3352d32d09b7 100644 (file)
@@ -2815,11 +2815,19 @@ fn check_binop(fcx: &FnCtxt,
             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,28 +5079,45 @@ fn adjust_region_parameters(
     }
 }
 
-// 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
index 7f1264ac9a1d304811e723f3a110a787b5945cc8..5049b87d694524f5a9389ddfed92a993f5050d9d 100644 (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 {
diff --git a/src/test/run-pass/issue-21634.rs b/src/test/run-pass/issue-21634.rs
new file mode 100644 (file)
index 0000000..e5a2790
--- /dev/null
@@ -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 });
+    }
+}