in checker/src/abstract_value.rs [3727:3800]
fn mul_overflows(
&self,
other: Rc<AbstractValue>,
target_type: ExpressionType,
) -> Rc<AbstractValue> {
// [x * c] -> c * x
if !self.is_compile_time_constant() && other.is_compile_time_constant() {
// Normalize multiply expressions so that if only one of the operands is a constant, it is
// always the left operand.
return other.mul_overflows(self.clone(), target_type);
}
if self.is_bottom() || self.is_top() || other.is_bottom() || other.is_top() {
return AbstractValue::make_typed_unknown(
ExpressionType::Bool,
Path::new_computed(TOP.into()),
);
}
if let Expression::CompileTimeConstant(c1) = &self.expression {
match c1 {
// [0 * y] -> 0
ConstantDomain::I128(0) | ConstantDomain::U128(0) => {
return Rc::new(FALSE);
}
// [1 * y] -> y
ConstantDomain::I128(1) | ConstantDomain::U128(1) => {
return Rc::new(FALSE);
}
ConstantDomain::I128(..) | ConstantDomain::U128(..) => match &other.expression {
// [c * (x + y)] -> (c * x) + (c * y)
// Do not apply distribution rule because it may introduce spurious overflows
// [c1 * (c2 * y)] -> (c1 * c2) * y
Expression::Mul { left: x, right: y } if x.is_compile_time_constant() => {
return self
.multiply(x.clone())
.mul_overflows(y.clone(), target_type);
}
_ => {}
},
_ => {
// [overflows(c1 * (x / c2))] -> false if c2 > c1 && c2 % c1 == 0
if let Expression::Div { right, .. } = &other.expression {
if let Expression::CompileTimeConstant(c2) = &right.expression {
if let (ConstantDomain::U128(c1), ConstantDomain::U128(c2)) = (c1, c2) {
if c2 > c1 && c2 % c1 == 0 {
return Rc::new(FALSE);
}
}
}
}
}
}
}
let interval = self.get_cached_interval().mul(&other.get_cached_interval());
if interval.is_contained_in(target_type) {
return Rc::new(FALSE);
}
self.try_to_constant_fold_and_distribute_typed_binary_op(
other,
target_type,
ConstantDomain::mul_overflows,
Self::mul_overflows,
|l, r, t| {
AbstractValue::make_typed_binary(l, r, t, |left, right, result_type| {
Expression::MulOverflows {
left,
right,
result_type,
}
})
},
)
}