Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 38544f5

Browse files
committed
Catch overflow in shift binop evaluation
1 parent 266ceb7 commit 38544f5

File tree

2 files changed

+27
-20
lines changed

2 files changed

+27
-20
lines changed

crates/hir-ty/src/consteval/tests.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,10 @@ fn bit_op() {
9797
check_number(r#"const GOAL: u8 = !0 & !(!0 >> 1)"#, 128);
9898
check_number(r#"const GOAL: i8 = !0 & !(!0 >> 1)"#, 0);
9999
check_number(r#"const GOAL: i8 = 1 << 7"#, (1i8 << 7) as i128);
100-
// FIXME: report panic here
101-
check_number(r#"const GOAL: i8 = 1 << 8"#, 0);
100+
check_number(r#"const GOAL: i8 = -1 << 2"#, (-1i8 << 2) as i128);
101+
check_fail(r#"const GOAL: i8 = 1 << 8"#, |e| {
102+
e == ConstEvalError::MirEvalError(MirEvalError::Panic("Overflow in Shl".to_string()))
103+
});
102104
}
103105

104106
#[test]

crates/hir-ty/src/mir/eval.rs

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -860,6 +860,16 @@ impl Evaluator<'_> {
860860
let is_signed = matches!(ty.as_builtin(), Some(BuiltinType::Int(_)));
861861
let l128 = i128::from_le_bytes(pad16(lc, is_signed));
862862
let r128 = i128::from_le_bytes(pad16(rc, is_signed));
863+
let check_overflow = |r: i128| {
864+
// FIXME: this is not very correct, and only catches the basic cases.
865+
let r = r.to_le_bytes();
866+
for &k in &r[lc.len()..] {
867+
if k != 0 && (k != 255 || !is_signed) {
868+
return Err(MirEvalError::Panic(format!("Overflow in {op:?}")));
869+
}
870+
}
871+
Ok(Owned(r[0..lc.len()].into()))
872+
};
863873
match op {
864874
BinOp::Ge | BinOp::Gt | BinOp::Le | BinOp::Lt | BinOp::Eq | BinOp::Ne => {
865875
let r = op.run_compare(l128, r128) as u8;
@@ -888,28 +898,23 @@ impl Evaluator<'_> {
888898
BinOp::BitXor => l128 ^ r128,
889899
_ => unreachable!(),
890900
};
891-
let r = r.to_le_bytes();
892-
for &k in &r[lc.len()..] {
893-
if k != 0 && (k != 255 || !is_signed) {
894-
return Err(MirEvalError::Panic(format!("Overflow in {op:?}")));
895-
}
896-
}
897-
Owned(r[0..lc.len()].into())
901+
check_overflow(r)?
898902
}
899903
BinOp::Shl | BinOp::Shr => {
900-
let shift_amount = if r128 < 0 {
901-
return Err(MirEvalError::Panic(format!("Overflow in {op:?}")));
902-
} else if r128 > 128 {
904+
let r = 'b: {
905+
if let Ok(shift_amount) = u32::try_from(r128) {
906+
let r = match op {
907+
BinOp::Shl => l128.checked_shl(shift_amount),
908+
BinOp::Shr => l128.checked_shr(shift_amount),
909+
_ => unreachable!(),
910+
};
911+
if let Some(r) = r {
912+
break 'b r;
913+
}
914+
};
903915
return Err(MirEvalError::Panic(format!("Overflow in {op:?}")));
904-
} else {
905-
r128 as u8
906-
};
907-
let r = match op {
908-
BinOp::Shl => l128 << shift_amount,
909-
BinOp::Shr => l128 >> shift_amount,
910-
_ => unreachable!(),
911916
};
912-
Owned(r.to_le_bytes()[0..lc.len()].into())
917+
check_overflow(r)?
913918
}
914919
BinOp::Offset => not_supported!("offset binop"),
915920
}

0 commit comments

Comments
 (0)