Skip to content

Commit

Permalink
Properly distinguish checked and unchecked binary operations
Browse files Browse the repository at this point in the history
This is important because they have different types and runtime
semantics.
  • Loading branch information
Nadrieril committed Apr 22, 2024
1 parent c0cae44 commit 2335415
Show file tree
Hide file tree
Showing 5 changed files with 32 additions and 7 deletions.
8 changes: 8 additions & 0 deletions charon/src/expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,14 @@ pub enum BinOp {
Sub,
/// Can overflow
Mul,
/// Returns `(result, did_overflow)`, where `result` is the result of the operation with
/// wrapping semantics, and `did_overflow` is a boolean that indicates whether the operation
/// overflowed.
CheckedAdd,
/// Like `CheckedAdd`.
CheckedSub,
/// Like `CheckedAdd`.
CheckedMul,
/// Can fail if the shift is too big
Shl,
/// Can fail if the shift is too big
Expand Down
3 changes: 3 additions & 0 deletions charon/src/expressions_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ impl std::fmt::Display for BinOp {
BinOp::Add => write!(f, "+"),
BinOp::Sub => write!(f, "-"),
BinOp::Mul => write!(f, "*"),
BinOp::CheckedAdd => write!(f, "checked.+"),
BinOp::CheckedSub => write!(f, "checked.-"),
BinOp::CheckedMul => write!(f, "checked.*"),
BinOp::Shl => write!(f, "<<"),
BinOp::Shr => write!(f, ">>"),
}
Expand Down
4 changes: 2 additions & 2 deletions charon/src/transform/remove_dynamic_checks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,13 @@ fn remove_dynamic_checks(ctx: &mut TransCtx, block: &mut BlockData) {
}] if lt_op2 == x && cond == has_overflow && *expected == true => rest,

// Overflow checks for addition/subtraction/multiplication. They look like:
// r := x + y;
// r := x checked.+ y;
// assert(move r.1 == false);
// They only happen in constants because we compile with `-C opt-level=3`. They're tricky
// to remove so we leave them.
[.., Statement {
content:
RawStatement::Assign(result, Rvalue::BinaryOp(BinOp::Add | BinOp::Sub | BinOp::Mul, ..)),
RawStatement::Assign(result, Rvalue::BinaryOp(BinOp::CheckedAdd | BinOp::CheckedSub | BinOp::CheckedMul, ..)),
..
}] if cond.var_id == result.var_id
&& result.projection.is_empty()
Expand Down
20 changes: 17 additions & 3 deletions charon/src/translate_functions_to_ullbc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -682,16 +682,30 @@ impl<'tcx, 'ctx, 'ctx1> BodyTransCtx<'tcx, 'ctx, 'ctx1> {
}
}
}
hax::Rvalue::BinaryOp(binop, operands)
| hax::Rvalue::CheckedBinaryOp(binop, operands) => {
// We merge checked and unchecked binary operations
hax::Rvalue::BinaryOp(binop, operands) => {
let (left, right) = operands.deref();
Ok(Rvalue::BinaryOp(
self.t_ctx.translate_binaryop_kind(span, *binop)?,
self.translate_operand(span, left)?,
self.translate_operand(span, right)?,
))
}
hax::Rvalue::CheckedBinaryOp(binop, operands) => {
let binop = match binop {
hax::BinOp::Add => BinOp::CheckedAdd,
hax::BinOp::Sub => BinOp::CheckedSub,
hax::BinOp::Mul => BinOp::CheckedMul,
_ => {
error_or_panic!(self, span, "Only Add, Sub and Mul are supported as checked binary operations, found {binop:?}");
}
};
let (left, right) = operands.deref();
Ok(Rvalue::BinaryOp(
binop,
self.translate_operand(span, left)?,
self.translate_operand(span, right)?,
))
}
hax::Rvalue::NullaryOp(nullop, _ty) => {
trace!("NullOp: {:?}", nullop);
// Nullary operations are very low-level and shouldn't be necessary
Expand Down
4 changes: 2 additions & 2 deletions charon/tests/ui/remove-dynamic-checks.out
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ global test_crate::CONST0 {
let @0: usize; // return
let @1: (usize, bool); // anonymous local

@1 := const (1 : usize) + const (1 : usize)
@1 := const (1 : usize) checked.+ const (1 : usize)
assert(move ((@1).1) == false)
@0 := move ((@1).0)
return
Expand All @@ -385,7 +385,7 @@ global test_crate::CONST1 {
let @0: usize; // return
let @1: (usize, bool); // anonymous local

@1 := const (2 : usize) * const (2 : usize)
@1 := const (2 : usize) checked.* const (2 : usize)
assert(move ((@1).1) == false)
@0 := move ((@1).0)
return
Expand Down

0 comments on commit 2335415

Please sign in to comment.