@@ -1071,11 +1071,9 @@ impl<'a> InferenceContext<'a> {
1071
1071
1072
1072
let ret_ty = self . normalize_associated_types_in ( ret_ty) ;
1073
1073
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) {
1074
+ if self . is_builtin_binop ( & lhs_ty, & rhs_ty, op) {
1075
+ // use knowledge of built-in binary ops, which can sometimes help inference
1076
+ let builtin_ret = self . enforce_builtin_binop_types ( & lhs_ty, & rhs_ty, op) ;
1079
1077
self . unify ( & builtin_ret, & ret_ty) ;
1080
1078
}
1081
1079
@@ -1545,7 +1543,10 @@ impl<'a> InferenceContext<'a> {
1545
1543
fn builtin_binary_op_rhs_expectation ( & mut self , op : BinaryOp , lhs_ty : Ty ) -> Option < Ty > {
1546
1544
Some ( match op {
1547
1545
BinaryOp :: LogicOp ( ..) => TyKind :: Scalar ( Scalar :: Bool ) . intern ( Interner ) ,
1548
- BinaryOp :: Assignment { op : None } => lhs_ty,
1546
+ BinaryOp :: Assignment { op : None } => {
1547
+ stdx:: never!( "Simple assignment operator is not binary op." ) ;
1548
+ return None ;
1549
+ }
1549
1550
BinaryOp :: CmpOp ( CmpOp :: Eq { .. } ) => match self
1550
1551
. resolve_ty_shallow ( & lhs_ty)
1551
1552
. kind ( Interner )
@@ -1565,6 +1566,126 @@ impl<'a> InferenceContext<'a> {
1565
1566
} )
1566
1567
}
1567
1568
1569
+ /// Dereferences a single level of immutable referencing.
1570
+ fn deref_ty_if_possible ( & mut self , ty : & Ty ) -> Ty {
1571
+ let ty = self . resolve_ty_shallow ( ty) ;
1572
+ match ty. kind ( Interner ) {
1573
+ TyKind :: Ref ( Mutability :: Not , _, inner) => self . resolve_ty_shallow ( inner) ,
1574
+ _ => ty,
1575
+ }
1576
+ }
1577
+
1578
+ /// Enforces expectations on lhs type and rhs type depending on the operator and returns the
1579
+ /// output type of the binary op.
1580
+ fn enforce_builtin_binop_types ( & mut self , lhs : & Ty , rhs : & Ty , op : BinaryOp ) -> Ty {
1581
+ // Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work (See rust-lang/rust#57447).
1582
+ let lhs = self . deref_ty_if_possible ( lhs) ;
1583
+ let rhs = self . deref_ty_if_possible ( rhs) ;
1584
+
1585
+ let ( op, is_assign) = match op {
1586
+ BinaryOp :: Assignment { op : Some ( inner) } => ( BinaryOp :: ArithOp ( inner) , true ) ,
1587
+ _ => ( op, false ) ,
1588
+ } ;
1589
+
1590
+ let output_ty = match op {
1591
+ BinaryOp :: LogicOp ( _) => {
1592
+ let bool_ = self . result . standard_types . bool_ . clone ( ) ;
1593
+ self . unify ( & lhs, & bool_) ;
1594
+ self . unify ( & rhs, & bool_) ;
1595
+ bool_
1596
+ }
1597
+
1598
+ BinaryOp :: ArithOp ( ArithOp :: Shl | ArithOp :: Shr ) => {
1599
+ // result type is same as LHS always
1600
+ lhs
1601
+ }
1602
+
1603
+ BinaryOp :: ArithOp ( _) => {
1604
+ // LHS, RHS, and result will have the same type
1605
+ self . unify ( & lhs, & rhs) ;
1606
+ lhs
1607
+ }
1608
+
1609
+ BinaryOp :: CmpOp ( _) => {
1610
+ // LHS and RHS will have the same type
1611
+ self . unify ( & lhs, & rhs) ;
1612
+ self . result . standard_types . bool_ . clone ( )
1613
+ }
1614
+
1615
+ BinaryOp :: Assignment { op : None } => {
1616
+ stdx:: never!( "Simple assignment operator is not binary op." ) ;
1617
+ lhs
1618
+ }
1619
+
1620
+ BinaryOp :: Assignment { .. } => unreachable ! ( "handled above" ) ,
1621
+ } ;
1622
+
1623
+ if is_assign {
1624
+ self . result . standard_types . unit . clone ( )
1625
+ } else {
1626
+ output_ty
1627
+ }
1628
+ }
1629
+
1630
+ fn is_builtin_binop ( & mut self , lhs : & Ty , rhs : & Ty , op : BinaryOp ) -> bool {
1631
+ // Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work (See rust-lang/rust#57447).
1632
+ let lhs = self . deref_ty_if_possible ( lhs) ;
1633
+ let rhs = self . deref_ty_if_possible ( rhs) ;
1634
+
1635
+ let op = match op {
1636
+ BinaryOp :: Assignment { op : Some ( inner) } => BinaryOp :: ArithOp ( inner) ,
1637
+ _ => op,
1638
+ } ;
1639
+
1640
+ match op {
1641
+ BinaryOp :: LogicOp ( _) => true ,
1642
+
1643
+ BinaryOp :: ArithOp ( ArithOp :: Shl | ArithOp :: Shr ) => {
1644
+ lhs. is_integral ( ) && rhs. is_integral ( )
1645
+ }
1646
+
1647
+ BinaryOp :: ArithOp (
1648
+ ArithOp :: Add | ArithOp :: Sub | ArithOp :: Mul | ArithOp :: Div | ArithOp :: Rem ,
1649
+ ) => {
1650
+ lhs. is_integral ( ) && rhs. is_integral ( )
1651
+ || lhs. is_floating_point ( ) && rhs. is_floating_point ( )
1652
+ }
1653
+
1654
+ BinaryOp :: ArithOp ( ArithOp :: BitAnd | ArithOp :: BitOr | ArithOp :: BitXor ) => {
1655
+ lhs. is_integral ( ) && rhs. is_integral ( )
1656
+ || lhs. is_floating_point ( ) && rhs. is_floating_point ( )
1657
+ || matches ! (
1658
+ ( lhs. kind( Interner ) , rhs. kind( Interner ) ) ,
1659
+ ( TyKind :: Scalar ( Scalar :: Bool ) , TyKind :: Scalar ( Scalar :: Bool ) )
1660
+ )
1661
+ }
1662
+
1663
+ BinaryOp :: CmpOp ( _) => {
1664
+ let is_scalar = |kind| {
1665
+ matches ! (
1666
+ kind,
1667
+ & TyKind :: Scalar ( _)
1668
+ | TyKind :: FnDef ( ..)
1669
+ | TyKind :: Function ( _)
1670
+ | TyKind :: Raw ( ..)
1671
+ | TyKind :: InferenceVar (
1672
+ _,
1673
+ TyVariableKind :: Integer | TyVariableKind :: Float
1674
+ )
1675
+ )
1676
+ } ;
1677
+ is_scalar ( lhs. kind ( Interner ) ) && is_scalar ( rhs. kind ( Interner ) )
1678
+ }
1679
+
1680
+ BinaryOp :: Assignment { op : None } => {
1681
+ stdx:: never!( "Simple assignment operator is not binary op." ) ;
1682
+ false
1683
+ }
1684
+
1685
+ BinaryOp :: Assignment { .. } => unreachable ! ( "handled above" ) ,
1686
+ }
1687
+ }
1688
+
1568
1689
fn with_breakable_ctx < T > (
1569
1690
& mut self ,
1570
1691
kind : BreakableKind ,
0 commit comments