ADR-0003: Constant Expression Evaluation
Status
Implemented
Summary
Implement a constant expression evaluator at the RIR level during semantic analysis, enabling compile-time bounds checking for expressions like arr[1 + 1].
Context
The current try_get_const_index function in gruel-air/src/sema.rs only recognizes direct integer literals and negated literals as compile-time constants:
This means expressions like arr[1 + 1] or arr[2 * 3] don't get compile-time bounds checking, even though their values are statically known. Users expect the compiler to catch obvious out-of-bounds errors like arr[1 + 100] at compile time rather than waiting for a runtime panic.
Additionally, Gruel may want to add a comptime feature in the future (similar to Zig), which would require a more robust constant evaluation infrastructure. The design of this feature should not foreclose on that possibility.
Decision
Implement a constant expression evaluator at the RIR level during semantic analysis. This evaluator will:
- Recursively evaluate RIR expressions that are compile-time determinable
- Return
Nonefor expressions that require runtime values - Be used by bounds checking and potentially other compile-time checks
Core Design
/// A value that can be computed at compile time.
Supported Expressions
| Expression | Example | Notes |
|---|---|---|
| Integer literals | 42 | Direct constant |
| Boolean literals | true, false | Direct constant |
| Negation | -42 | Unary minus on constant |
| Logical NOT | !true | Returns false |
| Addition | 1 + 2 | Checked arithmetic |
| Subtraction | 5 - 3 | Checked arithmetic |
| Multiplication | 2 * 3 | Checked arithmetic |
| Division | 6 / 2 | Returns None if divisor is 0 |
| Modulo | 7 % 3 | Returns None if divisor is 0 |
| Comparisons | 1 < 2 | Returns ConstValue::Bool |
| Logical AND/OR | true && false | Short-circuit not needed for constants |
| Parentheses | (1 + 2) * 3 | Handled by recursion |
Expressions That Return None
- Variable references (
x,arr[i]) - Function calls
- Operations that would overflow (e.g.,
i64::MAX + 1) - Operations that would panic (e.g.,
1 / 0) - Struct/array literals (for now)
Implementation Phases
- Phase 1: Core evaluator - Add ConstValue, try_evaluate_const, refactor try_get_const_index
Consequences
Positive
- Better compile-time error detection: Catches more bounds errors at compile time
- Foundation for
comptime: The evaluator can be extended for full compile-time execution - Clean separation: Evaluation logic is isolated and testable
- Predictable behavior: Overflow/division-by-zero during evaluation returns
None(runtime check)
Negative
- Incomplete evaluation: Not all mathematically constant expressions are recognized (e.g.,
xwherexis assigned a constant but never mutated) - Recursion depth: Deeply nested expressions could cause stack overflow (unlikely in practice)
Open Questions
None remaining.
Future Work
When implementing comptime, the following extensions would be needed:
constitems:const FOO: i32 = 1 + 2;comptimeblocks:comptime { complex_computation() }const fn: Functions that can be evaluated at compile time- Compile-time function evaluation: Call
const fnduring compilation - Type-level constants: For const generics like
[T; N]
This ADR specifically avoids these to keep the scope minimal while establishing the foundation.