mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-02 22:54:58 +00:00
Catch overflow in shift binop evaluation
This commit is contained in:
parent
266ceb7b4d
commit
38544f56ab
2 changed files with 27 additions and 20 deletions
|
@ -97,8 +97,10 @@ fn bit_op() {
|
||||||
check_number(r#"const GOAL: u8 = !0 & !(!0 >> 1)"#, 128);
|
check_number(r#"const GOAL: u8 = !0 & !(!0 >> 1)"#, 128);
|
||||||
check_number(r#"const GOAL: i8 = !0 & !(!0 >> 1)"#, 0);
|
check_number(r#"const GOAL: i8 = !0 & !(!0 >> 1)"#, 0);
|
||||||
check_number(r#"const GOAL: i8 = 1 << 7"#, (1i8 << 7) as i128);
|
check_number(r#"const GOAL: i8 = 1 << 7"#, (1i8 << 7) as i128);
|
||||||
// FIXME: report panic here
|
check_number(r#"const GOAL: i8 = -1 << 2"#, (-1i8 << 2) as i128);
|
||||||
check_number(r#"const GOAL: i8 = 1 << 8"#, 0);
|
check_fail(r#"const GOAL: i8 = 1 << 8"#, |e| {
|
||||||
|
e == ConstEvalError::MirEvalError(MirEvalError::Panic("Overflow in Shl".to_string()))
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -860,6 +860,16 @@ impl Evaluator<'_> {
|
||||||
let is_signed = matches!(ty.as_builtin(), Some(BuiltinType::Int(_)));
|
let is_signed = matches!(ty.as_builtin(), Some(BuiltinType::Int(_)));
|
||||||
let l128 = i128::from_le_bytes(pad16(lc, is_signed));
|
let l128 = i128::from_le_bytes(pad16(lc, is_signed));
|
||||||
let r128 = i128::from_le_bytes(pad16(rc, is_signed));
|
let r128 = i128::from_le_bytes(pad16(rc, is_signed));
|
||||||
|
let check_overflow = |r: i128| {
|
||||||
|
// FIXME: this is not very correct, and only catches the basic cases.
|
||||||
|
let r = r.to_le_bytes();
|
||||||
|
for &k in &r[lc.len()..] {
|
||||||
|
if k != 0 && (k != 255 || !is_signed) {
|
||||||
|
return Err(MirEvalError::Panic(format!("Overflow in {op:?}")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Owned(r[0..lc.len()].into()))
|
||||||
|
};
|
||||||
match op {
|
match op {
|
||||||
BinOp::Ge | BinOp::Gt | BinOp::Le | BinOp::Lt | BinOp::Eq | BinOp::Ne => {
|
BinOp::Ge | BinOp::Gt | BinOp::Le | BinOp::Lt | BinOp::Eq | BinOp::Ne => {
|
||||||
let r = op.run_compare(l128, r128) as u8;
|
let r = op.run_compare(l128, r128) as u8;
|
||||||
|
@ -888,28 +898,23 @@ impl Evaluator<'_> {
|
||||||
BinOp::BitXor => l128 ^ r128,
|
BinOp::BitXor => l128 ^ r128,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
let r = r.to_le_bytes();
|
check_overflow(r)?
|
||||||
for &k in &r[lc.len()..] {
|
|
||||||
if k != 0 && (k != 255 || !is_signed) {
|
|
||||||
return Err(MirEvalError::Panic(format!("Overflow in {op:?}")));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Owned(r[0..lc.len()].into())
|
|
||||||
}
|
}
|
||||||
BinOp::Shl | BinOp::Shr => {
|
BinOp::Shl | BinOp::Shr => {
|
||||||
let shift_amount = if r128 < 0 {
|
let r = 'b: {
|
||||||
|
if let Ok(shift_amount) = u32::try_from(r128) {
|
||||||
|
let r = match op {
|
||||||
|
BinOp::Shl => l128.checked_shl(shift_amount),
|
||||||
|
BinOp::Shr => l128.checked_shr(shift_amount),
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
if let Some(r) = r {
|
||||||
|
break 'b r;
|
||||||
|
}
|
||||||
|
};
|
||||||
return Err(MirEvalError::Panic(format!("Overflow in {op:?}")));
|
return Err(MirEvalError::Panic(format!("Overflow in {op:?}")));
|
||||||
} else if r128 > 128 {
|
|
||||||
return Err(MirEvalError::Panic(format!("Overflow in {op:?}")));
|
|
||||||
} else {
|
|
||||||
r128 as u8
|
|
||||||
};
|
};
|
||||||
let r = match op {
|
check_overflow(r)?
|
||||||
BinOp::Shl => l128 << shift_amount,
|
|
||||||
BinOp::Shr => l128 >> shift_amount,
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
Owned(r.to_le_bytes()[0..lc.len()].into())
|
|
||||||
}
|
}
|
||||||
BinOp::Offset => not_supported!("offset binop"),
|
BinOp::Offset => not_supported!("offset binop"),
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue