Catch overflow in shift binop evaluation

This commit is contained in:
hkalbasi 2023-05-02 12:57:34 +03:30
parent 266ceb7b4d
commit 38544f56ab
2 changed files with 27 additions and 20 deletions

View file

@ -97,8 +97,10 @@ fn bit_op() {
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 = 1 << 7"#, (1i8 << 7) as i128);
// FIXME: report panic here
check_number(r#"const GOAL: i8 = 1 << 8"#, 0);
check_number(r#"const GOAL: i8 = -1 << 2"#, (-1i8 << 2) as i128);
check_fail(r#"const GOAL: i8 = 1 << 8"#, |e| {
e == ConstEvalError::MirEvalError(MirEvalError::Panic("Overflow in Shl".to_string()))
});
}
#[test]

View file

@ -860,6 +860,16 @@ impl Evaluator<'_> {
let is_signed = matches!(ty.as_builtin(), Some(BuiltinType::Int(_)));
let l128 = i128::from_le_bytes(pad16(lc, 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 {
BinOp::Ge | BinOp::Gt | BinOp::Le | BinOp::Lt | BinOp::Eq | BinOp::Ne => {
let r = op.run_compare(l128, r128) as u8;
@ -888,28 +898,23 @@ impl Evaluator<'_> {
BinOp::BitXor => l128 ^ r128,
_ => unreachable!(),
};
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:?}")));
}
}
Owned(r[0..lc.len()].into())
check_overflow(r)?
}
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:?}")));
} else if r128 > 128 {
return Err(MirEvalError::Panic(format!("Overflow in {op:?}")));
} else {
r128 as u8
};
let r = match op {
BinOp::Shl => l128 << shift_amount,
BinOp::Shr => l128 >> shift_amount,
_ => unreachable!(),
};
Owned(r.to_le_bytes()[0..lc.len()].into())
check_overflow(r)?
}
BinOp::Offset => not_supported!("offset binop"),
}