use crate::CompilerState;
use leo_ast::{BinaryOperation, Expression, Node as _, Type, UnaryOperation};
use leo_span::{Symbol, sym};
use indexmap::IndexSet;
pub struct DeadCodeEliminatingVisitor<'a> {
pub state: &'a mut CompilerState,
pub used_variables: IndexSet<Symbol>,
pub program_name: Symbol,
pub statements_before: u32,
pub statements_after: u32,
}
impl DeadCodeEliminatingVisitor<'_> {
pub fn side_effect_free(&self, expr: &Expression) -> bool {
use Expression::*;
let sef = |expr| self.side_effect_free(expr);
match expr {
ArrayAccess(array) => sef(&array.array) && sef(&array.index),
MemberAccess(mem) => sef(&mem.inner),
TupleAccess(tuple) => sef(&tuple.tuple),
Array(array) => array.elements.iter().all(sef),
AssociatedConstant(_) => true,
AssociatedFunction(func) => {
func.arguments.iter().all(sef)
&& !matches!(
func.variant.name,
sym::CheatCode | sym::Mapping | sym::Future | sym::Pedersen64 | sym::Pedersen128
)
}
Binary(bin) => {
use BinaryOperation::*;
let halting_op = match bin.op {
Div | Mod | Rem | Shl | Shr => true,
Add | Mul | Pow => {
matches!(
self.state.type_table.get(&expr.id()).expect("Types should be assigned."),
Type::Integer(..)
)
}
_ => false,
};
!halting_op && sef(&bin.left) && sef(&bin.right)
}
Call(..) => {
false
}
Cast(..) => {
false
}
Struct(struct_) => struct_.members.iter().all(|mem| mem.expression.as_ref().is_none_or(sef)),
Ternary(tern) => [&tern.condition, &tern.if_true, &tern.if_false].into_iter().all(sef),
Tuple(tuple) => tuple.elements.iter().all(sef),
Unary(un) => {
use UnaryOperation::*;
let halting_op = match un.op {
Abs | Inverse | SquareRoot => true,
Negate => {
matches!(
self.state.type_table.get(&expr.id()).expect("Type should be assigned."),
Type::Integer(..)
)
}
_ => false,
};
!halting_op && sef(&un.receiver)
}
Err(_) => false,
Identifier(_) | Literal(_) | Locator(_) | Unit(_) => true,
}
}
}