1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
//! Constant Folding Optimization
//!
//! Performs compile-time evaluation of constant expressions to optimize
//! generated code. This is a classic compiler optimization that reduces
//! runtime computation by folding constant operations at compile time.
//!
//! # Examples
//! - `2 + 3` → `5`
//! - `10 * 2 + 5` → `25`
//! - `true && false` → `false`
//! - `-42` → `-42`
//!
//! # Safety
//! - Does not fold division/modulo by zero
//! - Returns `None` for non-foldable expressions
//! - Preserves type safety (no mixed-type folding)
use crate::parser::{BinaryOp, Expression, Literal, UnaryOp};
/// Try to fold a constant expression at compile time
///
/// Recursively evaluates constant expressions and returns a folded literal
/// if the expression can be evaluated at compile time. Returns `None` if
/// the expression cannot be folded (non-constant, division by zero, etc.).
///
/// # Arguments
/// * `expr` - The expression to attempt to fold
///
/// # Returns
/// * `Some(Expression::Literal)` - The folded constant value
/// * `None` - Expression cannot be folded
///
/// # Examples
/// ```ignore
/// let expr = Binary { left: Int(2), op: Add, right: Int(3) };
/// let folded = try_fold_constant(&expr); // Some(Int(5))
/// ```
pub fn try_fold_constant<'ast>(expr: &Expression<'ast>) -> Option<Expression<'ast>> {
match expr {
Expression::Binary {
left,
op,
right,
location,
..
} => {
// Try to fold both sides first (recursive)
let left_folded = try_fold_constant(left).unwrap_or_else(|| (**left).clone());
let right_folded = try_fold_constant(right).unwrap_or_else(|| (**right).clone());
// If both sides are literals, try to evaluate
if let (Expression::Literal { value: l, .. }, Expression::Literal { value: r, .. }) =
(&left_folded, &right_folded)
{
use BinaryOp::*;
use Literal::*;
let result = match (l, op, r) {
// Integer arithmetic
(Int(a), Add, Int(b)) => Some(Literal::Int(a + b)),
(Int(a), Sub, Int(b)) => Some(Literal::Int(a - b)),
(Int(a), Mul, Int(b)) => Some(Literal::Int(a * b)),
(Int(a), Div, Int(b)) if *b != 0 => Some(Literal::Int(a / b)),
(Int(a), Mod, Int(b)) if *b != 0 => Some(Literal::Int(a % b)),
// Float arithmetic
(Float(a), Add, Float(b)) => Some(Literal::Float(a + b)),
(Float(a), Sub, Float(b)) => Some(Literal::Float(a - b)),
(Float(a), Mul, Float(b)) => Some(Literal::Float(a * b)),
(Float(a), Div, Float(b)) if *b != 0.0 => Some(Literal::Float(a / b)),
// Integer comparisons
(Int(a), Eq, Int(b)) => Some(Literal::Bool(a == b)),
(Int(a), Ne, Int(b)) => Some(Literal::Bool(a != b)),
(Int(a), Lt, Int(b)) => Some(Literal::Bool(a < b)),
(Int(a), Le, Int(b)) => Some(Literal::Bool(a <= b)),
(Int(a), Gt, Int(b)) => Some(Literal::Bool(a > b)),
(Int(a), Ge, Int(b)) => Some(Literal::Bool(a >= b)),
// Boolean operations
(Bool(a), And, Bool(b)) => Some(Literal::Bool(*a && *b)),
(Bool(a), Or, Bool(b)) => Some(Literal::Bool(*a || *b)),
_ => None,
};
// TDD FIX: Preserve location from original Binary expression
// This ensures folded literals maintain the same ExprId during type inference,
// allowing constraints to propagate correctly through regeneration passes
return result.map(|value| Expression::Literal {
value,
location: location.clone(),
});
}
None
}
Expression::Unary {
op,
operand,
location,
..
} => {
// Try to fold operand first (recursive)
let operand_folded = try_fold_constant(operand).unwrap_or_else(|| (**operand).clone());
if let Expression::Literal { value: lit, .. } = &operand_folded {
use Literal::*;
use UnaryOp::*;
let result = match (op, lit) {
(Neg, Int(n)) => Some(Literal::Int(-n)),
(Neg, Float(f)) => Some(Literal::Float(-f)),
(Not, Bool(b)) => Some(Literal::Bool(!b)),
_ => None,
};
// TDD FIX: Preserve location from original Unary expression
return result.map(|value| Expression::Literal {
value,
location: location.clone(),
});
}
None
}
// Already a literal - can't fold further
Expression::Literal { .. } => None,
// Can't fold non-constant expressions
_ => None,
}
}