in checker/src/abstract_value.rs [3919:3988]
fn logical_not(&self) -> Rc<AbstractValue> {
match &self.expression {
Expression::Bottom => self.clone(),
Expression::Equals { left: x, right: y }
if !x.expression.infer_type().is_floating_point_number() =>
{
// [!(x == y)] -> x != y
x.not_equals(y.clone())
}
Expression::GreaterThan { left: x, right: y }
if x.expression.infer_type().is_integer() =>
{
// [!(x > y)] -> x <= y
x.less_or_equal(y.clone())
}
Expression::GreaterOrEqual { left: x, right: y }
if x.expression.infer_type().is_integer() =>
{
// [!(x >= y)] -> x < y
x.less_than(y.clone())
}
Expression::LessThan { left: x, right: y }
if x.expression.infer_type().is_integer() =>
{
// [!(x < y)] -> x >= y
x.greater_or_equal(y.clone())
}
Expression::LessOrEqual { left: x, right: y }
if x.expression.infer_type().is_integer() =>
{
// [!(x <= y)] -> x > y
x.greater_than(y.clone())
}
Expression::LogicalNot { operand } => {
// [!!x] -> x
operand.clone()
}
Expression::Ne { left: x, right: y }
if !x.expression.infer_type().is_floating_point_number() =>
{
// [!(x != y)] -> x == y
x.equals(y.clone())
}
Expression::Or { left: x, right }
if matches!(right.expression, Expression::LogicalNot { .. }) =>
{
// [!(x || !y)] -> !x && y
if let Expression::LogicalNot { operand: y } = &right.expression {
x.logical_not().and(y.clone())
} else {
unreachable!()
}
}
Expression::Or { left, right: y }
if matches!(left.expression, Expression::LogicalNot { .. }) =>
{
// [!(!x || y)] -> x && !y
if let Expression::LogicalNot { operand: x } = &left.expression {
x.and(y.logical_not())
} else {
unreachable!()
}
}
_ => self.try_to_constant_fold_and_distribute_unary_op(
ConstantDomain::logical_not,
Self::logical_not,
|o| AbstractValue::make_unary(o, |operand| Expression::LogicalNot { operand }),
),
}
}