use leo_ast::{AccessExpression, BinaryOperation, Expression, Node as _, Type, UnaryOperation};
use leo_span::{Symbol, sym};
use indexmap::IndexSet;
use crate::TypeTable;
#[derive(Debug)]
pub struct DeadCodeEliminator<'a> {
pub(crate) used_variables: IndexSet<Symbol>,
pub(crate) program_name: Symbol,
pub(crate) type_table: &'a TypeTable,
pub(crate) statements_before: u32,
pub(crate) statements_after: u32,
}
impl<'a> DeadCodeEliminator<'a> {
pub(crate) fn new(type_table: &'a TypeTable) -> Self {
Self {
used_variables: Default::default(),
program_name: Symbol::intern(""),
type_table,
statements_before: 0,
statements_after: 0,
}
}
pub(crate) fn side_effect_free(&self, expr: &Expression) -> bool {
use Expression::*;
let sef = |expr| self.side_effect_free(expr);
match expr {
Access(AccessExpression::Array(array)) => sef(&array.array) && sef(&array.index),
Access(AccessExpression::Member(mem)) => sef(&mem.inner),
Access(AccessExpression::Tuple(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.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.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,
}
}
}