use crate::ir::{BinOp, Expr, Literal, Statement};
pub fn fold_constant_guards(stmts: Vec<Statement>) -> Vec<Statement> {
let mut result = Vec::new();
for stmt in stmts {
match stmt {
Statement::If { condition, then_body, else_body } => {
let then_folded = fold_constant_guards(then_body);
let else_folded = fold_constant_guards(else_body);
if is_truthy_literal(&condition) {
result.extend(then_folded);
} else if is_falsy_literal(&condition) {
result.extend(else_folded);
} else if is_type_tag_check(&condition) {
result.extend(then_folded);
} else if is_artifact_constant_comparison(&condition) {
result.extend(then_folded);
} else if is_non_boolean_condition(&condition) && else_folded.is_empty() {
result.extend(then_folded);
} else {
result.push(Statement::If {
condition,
then_body: then_folded,
else_body: else_folded,
});
}
}
Statement::While { condition, body } => {
result.push(Statement::While {
condition,
body: fold_constant_guards(body),
});
}
Statement::Loop { body } => {
result.push(Statement::Loop {
body: fold_constant_guards(body),
});
}
Statement::ForEach { var_name, collection, body } => {
result.push(Statement::ForEach {
var_name,
collection,
body: fold_constant_guards(body),
});
}
Statement::ForRange { var_name, bound, body } => {
result.push(Statement::ForRange {
var_name,
bound,
body: fold_constant_guards(body),
});
}
other => result.push(other),
}
}
result
}
fn is_truthy_literal(expr: &Expr) -> bool {
match expr {
Expr::Literal(crate::ir::Literal::I32(n)) => *n != 0,
Expr::Literal(crate::ir::Literal::I64(n)) => *n != 0,
Expr::Literal(crate::ir::Literal::Bool(b)) => *b,
Expr::Literal(crate::ir::Literal::Unit) => true,
_ => false,
}
}
fn is_falsy_literal(expr: &Expr) -> bool {
match expr {
Expr::Literal(crate::ir::Literal::I32(0)) => true,
Expr::Literal(crate::ir::Literal::I64(0)) => true,
Expr::Literal(crate::ir::Literal::Bool(false)) => true,
_ => false,
}
}
fn is_type_tag_check(expr: &Expr) -> bool {
if let Expr::BinOp { left, op, right } = expr {
if matches!(op, crate::ir::BinOp::Eq | crate::ir::BinOp::Ne) {
return is_val_tag_mask(left) || is_val_tag_mask(right);
}
}
if let Expr::UnOp { op: crate::ir::UnOp::Not, operand } = expr {
return is_type_tag_check(operand);
}
false
}
fn is_val_tag_mask(expr: &Expr) -> bool {
if let Expr::BinOp { op: crate::ir::BinOp::BitAnd, right, .. } = expr {
matches!(right.as_ref(),
Expr::Literal(crate::ir::Literal::I32(255))
| Expr::Literal(crate::ir::Literal::I64(255))
)
} else {
false
}
}
fn is_artifact_constant_comparison(expr: &Expr) -> bool {
if let Expr::BinOp { left, op, right } = expr {
if matches!(op,
crate::ir::BinOp::Eq | crate::ir::BinOp::Ne
| crate::ir::BinOp::Lt | crate::ir::BinOp::Le
| crate::ir::BinOp::Gt | crate::ir::BinOp::Ge
) {
return is_raw_constant(left) && is_raw_constant(right);
}
}
false
}
fn is_raw_constant(expr: &Expr) -> bool {
matches!(expr,
Expr::Literal(crate::ir::Literal::I32(_))
| Expr::Literal(crate::ir::Literal::I64(_))
)
}
pub fn normalize_comparisons(stmts: Vec<Statement>) -> Vec<Statement> {
stmts.into_iter().map(|stmt| normalize_cmp_in_stmt(stmt)).collect()
}
fn normalize_cmp_in_stmt(stmt: Statement) -> Statement {
match stmt {
Statement::If { condition, then_body, else_body } => Statement::If {
condition: normalize_cmp_expr(condition),
then_body: normalize_comparisons(then_body),
else_body: normalize_comparisons(else_body),
},
Statement::Let { name, mutable, value } => Statement::Let {
name, mutable,
value: normalize_cmp_expr(value),
},
Statement::Expr(e) => Statement::Expr(normalize_cmp_expr(e)),
Statement::Return(Some(e)) => Statement::Return(Some(normalize_cmp_expr(e))),
other => other,
}
}
fn normalize_cmp_expr(expr: Expr) -> Expr {
match expr {
Expr::BinOp { op: BinOp::Lt, left, right } => {
if let Some(n) = as_positive_i64(&right) {
return Expr::BinOp {
op: BinOp::Le,
left: Box::new(normalize_cmp_expr(*left)),
right: Box::new(Expr::Literal(Literal::I64(n - 1))),
};
}
Expr::BinOp { op: BinOp::Lt, left, right }
}
Expr::BinOp { op: BinOp::Gt, left, right } => Expr::BinOp {
op: BinOp::Gt,
left: Box::new(normalize_cmp_expr(*left)),
right: Box::new(normalize_cmp_expr(*right)),
},
Expr::BinOp { op, left, right } => Expr::BinOp {
op,
left: Box::new(normalize_cmp_expr(*left)),
right: Box::new(normalize_cmp_expr(*right)),
},
Expr::UnOp { op, operand } => Expr::UnOp {
op,
operand: Box::new(normalize_cmp_expr(*operand)),
},
other => other,
}
}
fn as_positive_i64(expr: &Expr) -> Option<i64> {
match expr {
Expr::Literal(Literal::I64(n)) if *n >= 2 => Some(*n),
Expr::Literal(Literal::I32(n)) if *n >= 2 => Some(*n as i64),
_ => None,
}
}
fn is_non_boolean_condition(expr: &Expr) -> bool {
match expr {
Expr::BinOp { op, .. } => matches!(op,
crate::ir::BinOp::Add | crate::ir::BinOp::Sub
| crate::ir::BinOp::Mul | crate::ir::BinOp::Div
| crate::ir::BinOp::Rem
| crate::ir::BinOp::BitAnd | crate::ir::BinOp::BitOr
| crate::ir::BinOp::BitXor
| crate::ir::BinOp::Shl | crate::ir::BinOp::Shr
),
Expr::Var(_) => true,
Expr::MethodChain { .. } => true,
_ => false,
}
}