@@ -1071,11 +1071,9 @@ impl<'a> InferenceContext<'a> {
10711071
10721072 let ret_ty = self . normalize_associated_types_in ( ret_ty) ;
10731073
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) ;
10791077 self . unify ( & builtin_ret, & ret_ty) ;
10801078 }
10811079
@@ -1545,7 +1543,10 @@ impl<'a> InferenceContext<'a> {
15451543 fn builtin_binary_op_rhs_expectation ( & mut self , op : BinaryOp , lhs_ty : Ty ) -> Option < Ty > {
15461544 Some ( match op {
15471545 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+ }
15491550 BinaryOp :: CmpOp ( CmpOp :: Eq { .. } ) => match self
15501551 . resolve_ty_shallow ( & lhs_ty)
15511552 . kind ( Interner )
@@ -1565,6 +1566,126 @@ impl<'a> InferenceContext<'a> {
15651566 } )
15661567 }
15671568
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+
15681689 fn with_breakable_ctx < T > (
15691690 & mut self ,
15701691 kind : BreakableKind ,
0 commit comments