@@ -10,8 +10,7 @@ use chalk_ir::{
10
10
} ;
11
11
use hir_def:: {
12
12
expr:: {
13
- ArithOp , Array , BinaryOp , ClosureKind , CmpOp , Expr , ExprId , LabelId , Literal , Statement ,
14
- UnaryOp ,
13
+ ArithOp , Array , BinaryOp , ClosureKind , Expr , ExprId , LabelId , Literal , Statement , UnaryOp ,
15
14
} ,
16
15
generics:: TypeOrConstParamData ,
17
16
path:: { GenericArg , GenericArgs } ,
@@ -1017,11 +1016,21 @@ impl<'a> InferenceContext<'a> {
1017
1016
let ( trait_, func) = match trait_func {
1018
1017
Some ( it) => it,
1019
1018
None => {
1020
- let rhs_ty = self . builtin_binary_op_rhs_expectation ( op, lhs_ty. clone ( ) ) ;
1021
- let rhs_ty = self . infer_expr_coerce ( rhs, & Expectation :: from_option ( rhs_ty) ) ;
1022
- return self
1023
- . builtin_binary_op_return_ty ( op, lhs_ty, rhs_ty)
1024
- . unwrap_or_else ( || self . err_ty ( ) ) ;
1019
+ // HACK: `rhs_ty` is a general inference variable with no clue at all at this
1020
+ // point. Passing `lhs_ty` as both operands just to check if `lhs_ty` is a builtin
1021
+ // type applicable to `op`.
1022
+ let ret_ty = if self . is_builtin_binop ( & lhs_ty, & lhs_ty, op) {
1023
+ // Assume both operands are builtin so we can continue inference. No guarantee
1024
+ // on the correctness, rustc would complain as necessary lang items don't seem
1025
+ // to exist anyway.
1026
+ self . enforce_builtin_binop_types ( & lhs_ty, & rhs_ty, op)
1027
+ } else {
1028
+ self . err_ty ( )
1029
+ } ;
1030
+
1031
+ self . infer_expr_coerce ( rhs, & Expectation :: has_type ( rhs_ty) ) ;
1032
+
1033
+ return ret_ty;
1025
1034
}
1026
1035
} ;
1027
1036
@@ -1071,11 +1080,9 @@ impl<'a> InferenceContext<'a> {
1071
1080
1072
1081
let ret_ty = self . normalize_associated_types_in ( ret_ty) ;
1073
1082
1074
- // use knowledge of built-in binary ops, which can sometimes help inference
1075
- if let Some ( builtin_rhs) = self . builtin_binary_op_rhs_expectation ( op, lhs_ty. clone ( ) ) {
1076
- self . unify ( & builtin_rhs, & rhs_ty) ;
1077
- }
1078
- if let Some ( builtin_ret) = self . builtin_binary_op_return_ty ( op, lhs_ty, rhs_ty) {
1083
+ if self . is_builtin_binop ( & lhs_ty, & rhs_ty, op) {
1084
+ // use knowledge of built-in binary ops, which can sometimes help inference
1085
+ let builtin_ret = self . enforce_builtin_binop_types ( & lhs_ty, & rhs_ty, op) ;
1079
1086
self . unify ( & builtin_ret, & ret_ty) ;
1080
1087
}
1081
1088
@@ -1477,92 +1484,124 @@ impl<'a> InferenceContext<'a> {
1477
1484
indices
1478
1485
}
1479
1486
1480
- fn builtin_binary_op_return_ty ( & mut self , op : BinaryOp , lhs_ty : Ty , rhs_ty : Ty ) -> Option < Ty > {
1481
- let lhs_ty = self . resolve_ty_shallow ( & lhs_ty) ;
1482
- let rhs_ty = self . resolve_ty_shallow ( & rhs_ty) ;
1483
- match op {
1484
- BinaryOp :: LogicOp ( _) | BinaryOp :: CmpOp ( _) => {
1485
- Some ( TyKind :: Scalar ( Scalar :: Bool ) . intern ( Interner ) )
1487
+ /// Dereferences a single level of immutable referencing.
1488
+ fn deref_ty_if_possible ( & mut self , ty : & Ty ) -> Ty {
1489
+ let ty = self . resolve_ty_shallow ( ty) ;
1490
+ match ty. kind ( Interner ) {
1491
+ TyKind :: Ref ( Mutability :: Not , _, inner) => self . resolve_ty_shallow ( inner) ,
1492
+ _ => ty,
1493
+ }
1494
+ }
1495
+
1496
+ /// Enforces expectations on lhs type and rhs type depending on the operator and returns the
1497
+ /// output type of the binary op.
1498
+ fn enforce_builtin_binop_types ( & mut self , lhs : & Ty , rhs : & Ty , op : BinaryOp ) -> Ty {
1499
+ // Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work (See rust-lang/rust#57447).
1500
+ let lhs = self . deref_ty_if_possible ( lhs) ;
1501
+ let rhs = self . deref_ty_if_possible ( rhs) ;
1502
+
1503
+ let ( op, is_assign) = match op {
1504
+ BinaryOp :: Assignment { op : Some ( inner) } => ( BinaryOp :: ArithOp ( inner) , true ) ,
1505
+ _ => ( op, false ) ,
1506
+ } ;
1507
+
1508
+ let output_ty = match op {
1509
+ BinaryOp :: LogicOp ( _) => {
1510
+ let bool_ = self . result . standard_types . bool_ . clone ( ) ;
1511
+ self . unify ( & lhs, & bool_) ;
1512
+ self . unify ( & rhs, & bool_) ;
1513
+ bool_
1486
1514
}
1487
- BinaryOp :: Assignment { .. } => Some ( TyBuilder :: unit ( ) ) ,
1515
+
1488
1516
BinaryOp :: ArithOp ( ArithOp :: Shl | ArithOp :: Shr ) => {
1489
- // all integer combinations are valid here
1490
- if matches ! (
1491
- lhs_ty. kind( Interner ) ,
1492
- TyKind :: Scalar ( Scalar :: Int ( _) | Scalar :: Uint ( _) )
1493
- | TyKind :: InferenceVar ( _, TyVariableKind :: Integer )
1494
- ) && matches ! (
1495
- rhs_ty. kind( Interner ) ,
1496
- TyKind :: Scalar ( Scalar :: Int ( _) | Scalar :: Uint ( _) )
1497
- | TyKind :: InferenceVar ( _, TyVariableKind :: Integer )
1498
- ) {
1499
- Some ( lhs_ty)
1500
- } else {
1501
- None
1502
- }
1517
+ // result type is same as LHS always
1518
+ lhs
1503
1519
}
1504
- BinaryOp :: ArithOp ( _) => match ( lhs_ty. kind ( Interner ) , rhs_ty. kind ( Interner ) ) {
1505
- // (int, int) | (uint, uint) | (float, float)
1506
- ( TyKind :: Scalar ( Scalar :: Int ( _) ) , TyKind :: Scalar ( Scalar :: Int ( _) ) )
1507
- | ( TyKind :: Scalar ( Scalar :: Uint ( _) ) , TyKind :: Scalar ( Scalar :: Uint ( _) ) )
1508
- | ( TyKind :: Scalar ( Scalar :: Float ( _) ) , TyKind :: Scalar ( Scalar :: Float ( _) ) ) => {
1509
- Some ( rhs_ty)
1510
- }
1511
- // ({int}, int) | ({int}, uint)
1512
- (
1513
- TyKind :: InferenceVar ( _, TyVariableKind :: Integer ) ,
1514
- TyKind :: Scalar ( Scalar :: Int ( _) | Scalar :: Uint ( _) ) ,
1515
- ) => Some ( rhs_ty) ,
1516
- // (int, {int}) | (uint, {int})
1517
- (
1518
- TyKind :: Scalar ( Scalar :: Int ( _) | Scalar :: Uint ( _) ) ,
1519
- TyKind :: InferenceVar ( _, TyVariableKind :: Integer ) ,
1520
- ) => Some ( lhs_ty) ,
1521
- // ({float} | float)
1522
- (
1523
- TyKind :: InferenceVar ( _, TyVariableKind :: Float ) ,
1524
- TyKind :: Scalar ( Scalar :: Float ( _) ) ,
1525
- ) => Some ( rhs_ty) ,
1526
- // (float, {float})
1527
- (
1528
- TyKind :: Scalar ( Scalar :: Float ( _) ) ,
1529
- TyKind :: InferenceVar ( _, TyVariableKind :: Float ) ,
1530
- ) => Some ( lhs_ty) ,
1531
- // ({int}, {int}) | ({float}, {float})
1532
- (
1533
- TyKind :: InferenceVar ( _, TyVariableKind :: Integer ) ,
1534
- TyKind :: InferenceVar ( _, TyVariableKind :: Integer ) ,
1535
- )
1536
- | (
1537
- TyKind :: InferenceVar ( _, TyVariableKind :: Float ) ,
1538
- TyKind :: InferenceVar ( _, TyVariableKind :: Float ) ,
1539
- ) => Some ( rhs_ty) ,
1540
- _ => None ,
1541
- } ,
1520
+
1521
+ BinaryOp :: ArithOp ( _) => {
1522
+ // LHS, RHS, and result will have the same type
1523
+ self . unify ( & lhs, & rhs) ;
1524
+ lhs
1525
+ }
1526
+
1527
+ BinaryOp :: CmpOp ( _) => {
1528
+ // LHS and RHS will have the same type
1529
+ self . unify ( & lhs, & rhs) ;
1530
+ self . result . standard_types . bool_ . clone ( )
1531
+ }
1532
+
1533
+ BinaryOp :: Assignment { op : None } => {
1534
+ stdx:: never!( "Simple assignment operator is not binary op." ) ;
1535
+ lhs
1536
+ }
1537
+
1538
+ BinaryOp :: Assignment { .. } => unreachable ! ( "handled above" ) ,
1539
+ } ;
1540
+
1541
+ if is_assign {
1542
+ self . result . standard_types . unit . clone ( )
1543
+ } else {
1544
+ output_ty
1542
1545
}
1543
1546
}
1544
1547
1545
- fn builtin_binary_op_rhs_expectation ( & mut self , op : BinaryOp , lhs_ty : Ty ) -> Option < Ty > {
1546
- Some ( match op {
1547
- BinaryOp :: LogicOp ( ..) => TyKind :: Scalar ( Scalar :: Bool ) . intern ( Interner ) ,
1548
- BinaryOp :: Assignment { op : None } => lhs_ty,
1549
- BinaryOp :: CmpOp ( CmpOp :: Eq { .. } ) => match self
1550
- . resolve_ty_shallow ( & lhs_ty)
1551
- . kind ( Interner )
1552
- {
1553
- TyKind :: Scalar ( _) | TyKind :: Str => lhs_ty,
1554
- TyKind :: InferenceVar ( _, TyVariableKind :: Integer | TyVariableKind :: Float ) => lhs_ty,
1555
- _ => return None ,
1556
- } ,
1557
- BinaryOp :: ArithOp ( ArithOp :: Shl | ArithOp :: Shr ) => return None ,
1558
- BinaryOp :: CmpOp ( CmpOp :: Ord { .. } )
1559
- | BinaryOp :: Assignment { op : Some ( _) }
1560
- | BinaryOp :: ArithOp ( _) => match self . resolve_ty_shallow ( & lhs_ty) . kind ( Interner ) {
1561
- TyKind :: Scalar ( Scalar :: Int ( _) | Scalar :: Uint ( _) | Scalar :: Float ( _) ) => lhs_ty,
1562
- TyKind :: InferenceVar ( _, TyVariableKind :: Integer | TyVariableKind :: Float ) => lhs_ty,
1563
- _ => return None ,
1564
- } ,
1565
- } )
1548
+ fn is_builtin_binop ( & mut self , lhs : & Ty , rhs : & Ty , op : BinaryOp ) -> bool {
1549
+ // Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work (See rust-lang/rust#57447).
1550
+ let lhs = self . deref_ty_if_possible ( lhs) ;
1551
+ let rhs = self . deref_ty_if_possible ( rhs) ;
1552
+
1553
+ let op = match op {
1554
+ BinaryOp :: Assignment { op : Some ( inner) } => BinaryOp :: ArithOp ( inner) ,
1555
+ _ => op,
1556
+ } ;
1557
+
1558
+ match op {
1559
+ BinaryOp :: LogicOp ( _) => true ,
1560
+
1561
+ BinaryOp :: ArithOp ( ArithOp :: Shl | ArithOp :: Shr ) => {
1562
+ lhs. is_integral ( ) && rhs. is_integral ( )
1563
+ }
1564
+
1565
+ BinaryOp :: ArithOp (
1566
+ ArithOp :: Add | ArithOp :: Sub | ArithOp :: Mul | ArithOp :: Div | ArithOp :: Rem ,
1567
+ ) => {
1568
+ lhs. is_integral ( ) && rhs. is_integral ( )
1569
+ || lhs. is_floating_point ( ) && rhs. is_floating_point ( )
1570
+ }
1571
+
1572
+ BinaryOp :: ArithOp ( ArithOp :: BitAnd | ArithOp :: BitOr | ArithOp :: BitXor ) => {
1573
+ lhs. is_integral ( ) && rhs. is_integral ( )
1574
+ || lhs. is_floating_point ( ) && rhs. is_floating_point ( )
1575
+ || matches ! (
1576
+ ( lhs. kind( Interner ) , rhs. kind( Interner ) ) ,
1577
+ ( TyKind :: Scalar ( Scalar :: Bool ) , TyKind :: Scalar ( Scalar :: Bool ) )
1578
+ )
1579
+ }
1580
+
1581
+ BinaryOp :: CmpOp ( _) => {
1582
+ let is_scalar = |kind| {
1583
+ matches ! (
1584
+ kind,
1585
+ & TyKind :: Scalar ( _)
1586
+ | TyKind :: FnDef ( ..)
1587
+ | TyKind :: Function ( _)
1588
+ | TyKind :: Raw ( ..)
1589
+ | TyKind :: InferenceVar (
1590
+ _,
1591
+ TyVariableKind :: Integer | TyVariableKind :: Float
1592
+ )
1593
+ )
1594
+ } ;
1595
+ is_scalar ( lhs. kind ( Interner ) ) && is_scalar ( rhs. kind ( Interner ) )
1596
+ }
1597
+
1598
+ BinaryOp :: Assignment { op : None } => {
1599
+ stdx:: never!( "Simple assignment operator is not binary op." ) ;
1600
+ false
1601
+ }
1602
+
1603
+ BinaryOp :: Assignment { .. } => unreachable ! ( "handled above" ) ,
1604
+ }
1566
1605
}
1567
1606
1568
1607
fn with_breakable_ctx < T > (
0 commit comments