8457: Implement more precise binary op return type heuristic r=flodiebold a=Veykril

Should fix #7150

Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
This commit is contained in:
bors[bot] 2021-04-10 15:53:06 +00:00 committed by GitHub
commit 4bf32eea21
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 139 additions and 32 deletions

View file

@ -9,21 +9,55 @@ pub(super) fn binary_op_return_ty(op: BinaryOp, lhs_ty: Ty, rhs_ty: Ty) -> Ty {
BinaryOp::LogicOp(_) | BinaryOp::CmpOp(_) => TyKind::Scalar(Scalar::Bool).intern(&Interner),
BinaryOp::Assignment { .. } => TyBuilder::unit(),
BinaryOp::ArithOp(ArithOp::Shl) | BinaryOp::ArithOp(ArithOp::Shr) => {
match lhs_ty.kind(&Interner) {
// all integer combinations are valid here
if matches!(
lhs_ty.kind(&Interner),
TyKind::Scalar(Scalar::Int(_))
| TyKind::Scalar(Scalar::Uint(_))
| TyKind::Scalar(Scalar::Float(_)) => lhs_ty,
TyKind::InferenceVar(_, TyVariableKind::Integer)
| TyKind::InferenceVar(_, TyVariableKind::Float) => lhs_ty,
_ => TyKind::Error.intern(&Interner),
| TyKind::Scalar(Scalar::Uint(_))
| TyKind::InferenceVar(_, TyVariableKind::Integer)
) && matches!(
rhs_ty.kind(&Interner),
TyKind::Scalar(Scalar::Int(_))
| TyKind::Scalar(Scalar::Uint(_))
| TyKind::InferenceVar(_, TyVariableKind::Integer)
) {
lhs_ty
} else {
TyKind::Error.intern(&Interner)
}
}
BinaryOp::ArithOp(_) => match rhs_ty.kind(&Interner) {
TyKind::Scalar(Scalar::Int(_))
| TyKind::Scalar(Scalar::Uint(_))
| TyKind::Scalar(Scalar::Float(_)) => rhs_ty,
TyKind::InferenceVar(_, TyVariableKind::Integer)
| TyKind::InferenceVar(_, TyVariableKind::Float) => rhs_ty,
BinaryOp::ArithOp(_) => match (lhs_ty.kind(&Interner), rhs_ty.kind(&Interner)) {
// (int, int) | (uint, uint) | (float, float)
(TyKind::Scalar(Scalar::Int(_)), TyKind::Scalar(Scalar::Int(_)))
| (TyKind::Scalar(Scalar::Uint(_)), TyKind::Scalar(Scalar::Uint(_)))
| (TyKind::Scalar(Scalar::Float(_)), TyKind::Scalar(Scalar::Float(_))) => rhs_ty,
// ({int}, int) | ({int}, uint)
(TyKind::InferenceVar(_, TyVariableKind::Integer), TyKind::Scalar(Scalar::Int(_)))
| (TyKind::InferenceVar(_, TyVariableKind::Integer), TyKind::Scalar(Scalar::Uint(_))) => {
rhs_ty
}
// (int, {int}) | (uint, {int})
(TyKind::Scalar(Scalar::Int(_)), TyKind::InferenceVar(_, TyVariableKind::Integer))
| (TyKind::Scalar(Scalar::Uint(_)), TyKind::InferenceVar(_, TyVariableKind::Integer)) => {
lhs_ty
}
// ({float} | float)
(TyKind::InferenceVar(_, TyVariableKind::Float), TyKind::Scalar(Scalar::Float(_))) => {
rhs_ty
}
// (float, {float})
(TyKind::Scalar(Scalar::Float(_)), TyKind::InferenceVar(_, TyVariableKind::Float)) => {
lhs_ty
}
// ({int}, {int}) | ({float}, {float})
(
TyKind::InferenceVar(_, TyVariableKind::Integer),
TyKind::InferenceVar(_, TyVariableKind::Integer),
)
| (
TyKind::InferenceVar(_, TyVariableKind::Float),
TyKind::InferenceVar(_, TyVariableKind::Float),
) => rhs_ty,
_ => TyKind::Error.intern(&Interner),
},
}

View file

@ -1919,10 +1919,26 @@ fn closure_1() {
fn closure_2() {
check_infer_with_mismatches(
r#"
#[lang = "add"]
pub trait Add<Rhs = Self> {
type Output;
fn add(self, rhs: Rhs) -> Self::Output;
}
trait FnOnce<Args> {
type Output;
}
impl Add for u64 {
type Output = Self;
fn add(self, rhs: u64) -> Self::Output {0}
}
impl Add for u128 {
type Output = Self;
fn add(self, rhs: u128) -> Self::Output {0}
}
fn test<F: FnOnce(u32) -> u64>(f: F) {
f(1);
let g = |v| v + 1;
@ -1931,26 +1947,36 @@ fn closure_2() {
}
"#,
expect![[r#"
72..73 'f': F
78..154 '{ ...+ v; }': ()
84..85 'f': F
84..88 'f(1)': {unknown}
86..87 '1': i32
98..99 'g': |u64| -> i32
102..111 '|v| v + 1': |u64| -> i32
103..104 'v': u64
106..107 'v': u64
106..111 'v + 1': i32
110..111 '1': i32
117..118 'g': |u64| -> i32
117..124 'g(1u64)': i32
119..123 '1u64': u64
134..135 'h': |u128| -> u128
138..151 '|v| 1u128 + v': |u128| -> u128
139..140 'v': u128
142..147 '1u128': u128
142..151 '1u128 + v': u128
150..151 'v': u128
72..76 'self': Self
78..81 'rhs': Rhs
203..207 'self': u64
209..212 'rhs': u64
235..238 '{0}': u64
236..237 '0': u64
297..301 'self': u128
303..306 'rhs': u128
330..333 '{0}': u128
331..332 '0': u128
368..369 'f': F
374..450 '{ ...+ v; }': ()
380..381 'f': F
380..384 'f(1)': {unknown}
382..383 '1': i32
394..395 'g': |u64| -> u64
398..407 '|v| v + 1': |u64| -> u64
399..400 'v': u64
402..403 'v': u64
402..407 'v + 1': u64
406..407 '1': u64
413..414 'g': |u64| -> u64
413..420 'g(1u64)': u64
415..419 '1u64': u64
430..431 'h': |u128| -> u128
434..447 '|v| 1u128 + v': |u128| -> u128
435..436 'v': u128
438..443 '1u128': u128
438..447 '1u128 + v': u128
446..447 'v': u128
"#]],
);
}
@ -3443,3 +3469,50 @@ pub trait Deserialize {
"#,
);
}
#[test]
fn bin_op_adt_with_rhs_primitive() {
check_infer_with_mismatches(
r#"
#[lang = "add"]
pub trait Add<Rhs = Self> {
type Output;
fn add(self, rhs: Rhs) -> Self::Output;
}
struct Wrapper(u32);
impl Add<u32> for Wrapper {
type Output = Self;
fn add(self, rhs: u32) -> Wrapper {
Wrapper(rhs)
}
}
fn main(){
let wrapped = Wrapper(10);
let num: u32 = 2;
let res = wrapped + num;
}"#,
expect![[r#"
72..76 'self': Self
78..81 'rhs': Rhs
192..196 'self': Wrapper
198..201 'rhs': u32
219..247 '{ ... }': Wrapper
229..236 'Wrapper': Wrapper(u32) -> Wrapper
229..241 'Wrapper(rhs)': Wrapper
237..240 'rhs': u32
259..345 '{ ...um; }': ()
269..276 'wrapped': Wrapper
279..286 'Wrapper': Wrapper(u32) -> Wrapper
279..290 'Wrapper(10)': Wrapper
287..289 '10': u32
300..303 'num': u32
311..312 '2': u32
322..325 'res': Wrapper
328..335 'wrapped': Wrapper
328..341 'wrapped + num': Wrapper
338..341 'num': u32
"#]],
)
}