Correct inference of primitive operand type behind binary operation
This commit is contained in:
parent
07a34df18b
commit
38060567e8
@ -25,7 +25,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
|
|
||||||
let ty =
|
let ty =
|
||||||
if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, op) {
|
if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, op) {
|
||||||
self.enforce_builtin_binop_types(lhs, lhs_ty, rhs, rhs_ty, op);
|
self.enforce_builtin_binop_types(&lhs.span, lhs_ty, &rhs.span, rhs_ty, op);
|
||||||
self.tcx.mk_unit()
|
self.tcx.mk_unit()
|
||||||
} else {
|
} else {
|
||||||
return_ty
|
return_ty
|
||||||
@ -86,8 +86,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
&& !rhs_ty.is_ty_var()
|
&& !rhs_ty.is_ty_var()
|
||||||
&& is_builtin_binop(lhs_ty, rhs_ty, op)
|
&& is_builtin_binop(lhs_ty, rhs_ty, op)
|
||||||
{
|
{
|
||||||
let builtin_return_ty =
|
let builtin_return_ty = self.enforce_builtin_binop_types(
|
||||||
self.enforce_builtin_binop_types(lhs_expr, lhs_ty, rhs_expr, rhs_ty, op);
|
&lhs_expr.span,
|
||||||
|
lhs_ty,
|
||||||
|
&rhs_expr.span,
|
||||||
|
rhs_ty,
|
||||||
|
op,
|
||||||
|
);
|
||||||
self.demand_suptype(expr.span, builtin_return_ty, return_ty);
|
self.demand_suptype(expr.span, builtin_return_ty, return_ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,19 +103,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
|
|
||||||
fn enforce_builtin_binop_types(
|
fn enforce_builtin_binop_types(
|
||||||
&self,
|
&self,
|
||||||
lhs_expr: &'tcx hir::Expr<'tcx>,
|
lhs_span: &Span,
|
||||||
lhs_ty: Ty<'tcx>,
|
lhs_ty: Ty<'tcx>,
|
||||||
rhs_expr: &'tcx hir::Expr<'tcx>,
|
rhs_span: &Span,
|
||||||
rhs_ty: Ty<'tcx>,
|
rhs_ty: Ty<'tcx>,
|
||||||
op: hir::BinOp,
|
op: hir::BinOp,
|
||||||
) -> Ty<'tcx> {
|
) -> Ty<'tcx> {
|
||||||
debug_assert!(is_builtin_binop(lhs_ty, rhs_ty, op));
|
debug_assert!(is_builtin_binop(lhs_ty, rhs_ty, op));
|
||||||
|
|
||||||
|
// Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work.
|
||||||
|
// (See https://github.com/rust-lang/rust/issues/57447.)
|
||||||
|
let (lhs_ty, rhs_ty) = (deref_ty_if_possible(lhs_ty), deref_ty_if_possible(rhs_ty));
|
||||||
|
|
||||||
let tcx = self.tcx;
|
let tcx = self.tcx;
|
||||||
match BinOpCategory::from(op) {
|
match BinOpCategory::from(op) {
|
||||||
BinOpCategory::Shortcircuit => {
|
BinOpCategory::Shortcircuit => {
|
||||||
self.demand_suptype(lhs_expr.span, tcx.mk_bool(), lhs_ty);
|
self.demand_suptype(*lhs_span, tcx.mk_bool(), lhs_ty);
|
||||||
self.demand_suptype(rhs_expr.span, tcx.mk_bool(), rhs_ty);
|
self.demand_suptype(*rhs_span, tcx.mk_bool(), rhs_ty);
|
||||||
tcx.mk_bool()
|
tcx.mk_bool()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,13 +130,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
|
|
||||||
BinOpCategory::Math | BinOpCategory::Bitwise => {
|
BinOpCategory::Math | BinOpCategory::Bitwise => {
|
||||||
// both LHS and RHS and result will have the same type
|
// both LHS and RHS and result will have the same type
|
||||||
self.demand_suptype(rhs_expr.span, lhs_ty, rhs_ty);
|
self.demand_suptype(*rhs_span, lhs_ty, rhs_ty);
|
||||||
lhs_ty
|
lhs_ty
|
||||||
}
|
}
|
||||||
|
|
||||||
BinOpCategory::Comparison => {
|
BinOpCategory::Comparison => {
|
||||||
// both LHS and RHS and result will have the same type
|
// both LHS and RHS and result will have the same type
|
||||||
self.demand_suptype(rhs_expr.span, lhs_ty, rhs_ty);
|
self.demand_suptype(*rhs_span, lhs_ty, rhs_ty);
|
||||||
tcx.mk_bool()
|
tcx.mk_bool()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -862,6 +871,14 @@ enum Op {
|
|||||||
Unary(hir::UnOp, Span),
|
Unary(hir::UnOp, Span),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Dereferences a single level of immutable referencing.
|
||||||
|
fn deref_ty_if_possible<'tcx>(ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||||
|
match ty.kind {
|
||||||
|
ty::Ref(_, ty, hir::Mutability::Not) => ty,
|
||||||
|
_ => ty,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns `true` if this is a built-in arithmetic operation (e.g., u32
|
/// Returns `true` if this is a built-in arithmetic operation (e.g., u32
|
||||||
/// + u32, i16x4 == i16x4) and false if these types would have to be
|
/// + u32, i16x4 == i16x4) and false if these types would have to be
|
||||||
/// overloaded to be legal. There are two reasons that we distinguish
|
/// overloaded to be legal. There are two reasons that we distinguish
|
||||||
@ -878,7 +895,11 @@ enum Op {
|
|||||||
/// Reason #2 is the killer. I tried for a while to always use
|
/// Reason #2 is the killer. I tried for a while to always use
|
||||||
/// overloaded logic and just check the types in constants/codegen after
|
/// overloaded logic and just check the types in constants/codegen after
|
||||||
/// the fact, and it worked fine, except for SIMD types. -nmatsakis
|
/// the fact, and it worked fine, except for SIMD types. -nmatsakis
|
||||||
fn is_builtin_binop(lhs: Ty<'_>, rhs: Ty<'_>, op: hir::BinOp) -> bool {
|
fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, op: hir::BinOp) -> bool {
|
||||||
|
// Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work.
|
||||||
|
// (See https://github.com/rust-lang/rust/issues/57447.)
|
||||||
|
let (lhs, rhs) = (deref_ty_if_possible(lhs), deref_ty_if_possible(rhs));
|
||||||
|
|
||||||
match BinOpCategory::from(op) {
|
match BinOpCategory::from(op) {
|
||||||
BinOpCategory::Shortcircuit => true,
|
BinOpCategory::Shortcircuit => true,
|
||||||
|
|
||||||
|
@ -0,0 +1,13 @@
|
|||||||
|
// check-pass
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let _: u8 = 0 + 0;
|
||||||
|
let _: u8 = 0 + &0;
|
||||||
|
let _: u8 = &0 + 0;
|
||||||
|
let _: u8 = &0 + &0;
|
||||||
|
|
||||||
|
let _: f32 = 0.0 + 0.0;
|
||||||
|
let _: f32 = 0.0 + &0.0;
|
||||||
|
let _: f32 = &0.0 + 0.0;
|
||||||
|
let _: f32 = &0.0 + &0.0;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user