feat(joinir): Phase 220-B ConditionEnv integration for if-sum lowerer

Integrates ConditionEnv infrastructure into if-sum lowerer to support
variable conditions like `loop(i < len)`.

## Implementation (Following Pattern 2)

### ConditionEnv Construction
- Added early construction via ConditionEnvBuilder::build_for_break_condition_v2()
- Extracts condition variables from loop condition AST
- Creates ConditionBindings for HOST↔JoinIR ValueId mapping

### Variable Support
- Created ValueOrLiteral enum (Literal(i64) | Variable(String, ValueId))
- Added extract_value_or_variable() with ConditionEnv lookup
- Updated extract_loop_condition() and extract_if_condition()

### JoinIR Generation Updates
- Condition-only variables as loop_step function parameters
- Proper parameter passing in recursive calls
- Condition ValueIds in JoinIR param region (100+)

### Boundary Builder Wiring
- Pass condition_bindings to JoinInlineBoundaryBuilder
- Updated call site in pattern3_with_if_phi.rs
- Added variable_map, loop_var_name, loop_var_id parameters

## Build Status

 Compilation successful (0 errors, 3 warnings)

## Test Status

 Tests not yet passing - remap issue identified:
- HOST: len = ValueId(6)
- JoinIR: len = ValueId(101)
- After Merge: len = ValueId(10) 

Root cause: JoinIRConversionPipeline remap needs investigation

## Files Modified

- src/mir/join_ir/lowering/loop_with_if_phi_if_sum.rs (+80 lines)
  - ConditionEnv construction
  - Variable support in conditions
  - Updated function signatures

- src/mir/builder/control_flow/joinir/patterns/pattern3_with_if_phi.rs (+10 lines)
  - Pass builder/loop_var to lowerer
  - Wire condition_bindings

## Design Principles

1. Reuse Existing Boxes (ConditionEnv/ConditionBinding)
2. Follow Pattern 2 Structure (proven blueprint)
3. Fail-Fast (variable not in ConditionEnv → error)
4. ParamRole::Condition Routing (separate from carriers)

## Next Steps: Phase 220-C

Fix remap issue in JoinIRConversionPipeline to properly use
condition_bindings.host_value during merge.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-12-10 02:54:56 +09:00
parent e6e306c020
commit 2fc2cf74d1
3 changed files with 366 additions and 47 deletions

View File

@ -0,0 +1,135 @@
# Phase 220-B: ConditionEnv Integration for If-Sum
## Overview
Integrated ConditionEnv infrastructure into if-sum lowerer to support
variable conditions like `loop(i < len)`.
## Implementation Status
### Completed
- ✅ ConditionEnv construction in `lower_if_sum_pattern()`
-`extract_value_or_variable()` function (supports both literals and variables)
- ✅ Updated `extract_loop_condition()` and `extract_if_condition()` to use ConditionEnv
- ✅ JoinIR generation includes condition-only variables as parameters
- ✅ Condition bindings wired to boundary builder
- ✅ Updated call sites in `pattern3_with_if_phi.rs`
### In Progress
- 🔧 **Issue**: phase212_if_sum_min.hako returns RC=0 instead of RC=2
- 🔧 **Root Cause**: Condition variable (`len`) remapping issue during merge
- HOST ValueId(6) → JoinIR ValueId(101) → After merge ValueId(10) ❌
- Expected: HOST ValueId(6) → JoinIR ValueId(101) → After merge ValueId(6) ✅
- 🔧 **Next Step**: Investigate condition_bindings handling in merge pipeline
## Implementation
### Pattern 2 Reference
Followed proven Pattern 2 (break condition) integration pattern:
1. Build ConditionEnv early with extract_condition_variables
2. Pass to condition lowering functions
3. Lookup variables via cond_env.get()
4. Wire condition_bindings to JoinInlineBoundary
### Key Changes
**File**: `src/mir/join_ir/lowering/loop_with_if_phi_if_sum.rs`
- Added ConditionEnv construction (lines 70-127)
- Updated signature to accept `variable_map`, `loop_var_name`, `loop_var_id`
- Return type changed to include `Vec<ConditionBinding>`
- JoinIR generation updated to include condition variables as parameters
**File**: `src/mir/builder/control_flow/joinir/patterns/pattern3_with_if_phi.rs`
- Updated call site to pass variable_map and loop context
- Wired condition_bindings to boundary builder (line 189)
### ValueOrLiteral Enum
```rust
enum ValueOrLiteral {
Literal(i64),
Variable(String, ValueId),
}
```
### Function Signatures Updated
```rust
fn extract_value_or_variable(
node: &ASTNode,
cond_env: &ConditionEnv,
) -> Result<ValueOrLiteral, String>
fn extract_loop_condition(
cond: &ASTNode,
cond_env: &ConditionEnv,
) -> Result<(String, CompareOp, ValueOrLiteral), String>
```
## Test Results (Preliminary)
### phase212_if_sum_min.hako
- Expected: RC=2
- Actual: RC=0 ❌
- Issue: Loop not executing (no print output)
- Debug: Condition variable `len` extracted correctly but remapping issue
### Debug Trace
```
[joinir/pattern3/if-sum] Extracted 1 condition variables: ["len"]
[joinir/pattern3/if-sum] Condition variable 'len': host=ValueId(6), join=ValueId(101)
[if-sum/joinir] loop_step params: 4 total (3 carriers + 1 condition vars)
[DEBUG-177] 'len': JoinIR ValueId(101) → Some(ValueId(10)) ← Should be ValueId(6)
```
## Design Principles
1. **Reuse Existing Boxes** (ConditionEnv/ConditionBinding)
2. **Follow Pattern 2 Structure** (proven blueprint)
3. **Fail-Fast** (variable not in ConditionEnv → error)
4. **ParamRole::Condition Routing** (separate from carriers)
## Next Steps
1. **Investigate Merge Pipeline**:
- Check how condition_bindings are processed in JoinIRConversionPipeline
- Verify that condition variables are loaded at loop entry
- Ensure remapping uses host_value from ConditionBinding
2. **Add Load Instructions**:
- May need to generate Load instructions for condition variables at loop entry
- Similar to how Pattern 2 handles break condition variables
3. **Test Additional Cases**:
- phase218_json_if_sum_min.hako (expect RC=10)
- Regression: loop_if_phi.hako (expect RC=2)
- Regression: phase217_if_sum_multi_min.hako (expect RC=2)
## Files Modified
**Primary**:
- `src/mir/join_ir/lowering/loop_with_if_phi_if_sum.rs` (ConditionEnv integration)
- `src/mir/builder/control_flow/joinir/patterns/pattern3_with_if_phi.rs` (call site update)
**Documentation**:
- `docs/development/current/main/phase220-condition-env-integration.md` (this file)
## Important Notes
- **Fail-Fast Maintained**: Variables not in ConditionEnv cause explicit errors
- **Method Call Limitation**: `defs.len()` style not supported yet (Phase 221+)
- **Pattern 2 Parity**: Uses identical ConditionEnvBuilder API
- **ParamRole Separation**: Condition variables kept separate from carriers
## Known Issues
1. **Condition Variable Remapping** (Critical):
- Condition variables get remapped to wrong ValueIds during merge
- Need to verify condition_bindings handling in merge pipeline
2. **No Test Passing Yet**:
- phase212: RC=0 (expected 2) ❌
- Other tests not yet verified
## References
- [Phase 171-fix: ConditionEnv](../../../reference/joinir/condition-env.md)
- [Phase 200-A/B: ConditionBinding](../../../reference/joinir/condition-binding.md)
- [Phase 201: JoinValueSpace](../../../reference/joinir/join-value-space.md)
- [Pattern 2: Break Condition](./pattern2-break-condition.md)

View File

@ -112,12 +112,15 @@ impl MirBuilder {
"[cf_loop/pattern3] if-sum pattern detected but no if statement found".to_string()
})?;
// Call AST-based if-sum lowerer
let (join_module, fragment_meta) = lower_if_sum_pattern(
// Phase 220-B: Call AST-based if-sum lowerer with ConditionEnv support
let (join_module, fragment_meta, cond_bindings) = lower_if_sum_pattern(
condition,
if_stmt,
body,
&mut join_value_space,
&self.variable_map, // Phase 220-B: Pass variable_map for ConditionEnv
&ctx.loop_var_name, // Phase 220-B: Pass loop variable name
ctx.loop_var_id, // Phase 220-B: Pass loop variable ValueId
)?;
let exit_meta = &fragment_meta.exit_meta;
@ -173,11 +176,17 @@ impl MirBuilder {
)
);
// Phase 215-2: Pass expr_result to boundary
// Phase 220-B: Wire condition_bindings to boundary builder
trace::trace().debug(
"pattern3/if-sum",
&format!("Wiring {} condition bindings to boundary", cond_bindings.len())
);
let mut boundary_builder = JoinInlineBoundaryBuilder::new()
.with_inputs(join_inputs, host_inputs)
.with_exit_bindings(exit_bindings)
.with_loop_var_name(Some(ctx.loop_var_name.clone()));
.with_loop_var_name(Some(ctx.loop_var_name.clone()))
.with_condition_bindings(cond_bindings); // Phase 220-B: Add condition bindings
// Add expr_result if present
if let Some(expr_id) = fragment_meta.expr_result {

View File

@ -30,13 +30,17 @@
use crate::ast::ASTNode;
use crate::mir::join_ir::lowering::carrier_info::{ExitMeta, JoinFragmentMeta};
use crate::mir::join_ir::lowering::condition_env::{ConditionBinding, ConditionEnv};
use crate::mir::join_ir::lowering::condition_to_joinir::extract_condition_variables;
use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace;
use crate::mir::join_ir::{
BinOpKind, CompareOp, ConstValue, JoinFuncId, JoinFunction, JoinInst, JoinModule,
MirLikeInst, UnaryOp,
};
use crate::mir::ValueId;
use std::collections::BTreeMap;
/// Phase 213: Lower if-sum pattern to JoinIR using AST
/// Phase 220-B: Lower if-sum pattern to JoinIR using AST (with ConditionEnv support)
///
/// # Arguments
///
@ -44,26 +48,95 @@ use crate::mir::join_ir::{
/// * `if_stmt` - If statement AST from loop body
/// * `body` - Full loop body AST (for finding counter update)
/// * `join_value_space` - Unified ValueId allocator
/// * `variable_map` - HOST function's variable_map for ConditionEnv construction
/// * `loop_var_name` - Loop variable name (e.g., "i")
/// * `loop_var_id` - HOST ValueId for loop variable
///
/// # Returns
///
/// * `Ok((JoinModule, JoinFragmentMeta))` - JoinIR module with exit metadata
/// * `Ok((JoinModule, JoinFragmentMeta, Vec<ConditionBinding>))` - JoinIR module, exit metadata, and condition bindings
/// * `Err(String)` - Pattern not supported or extraction failed
pub fn lower_if_sum_pattern(
loop_condition: &ASTNode,
if_stmt: &ASTNode,
body: &[ASTNode],
join_value_space: &mut JoinValueSpace,
) -> Result<(JoinModule, JoinFragmentMeta), String> {
variable_map: &BTreeMap<String, ValueId>,
loop_var_name: &str,
loop_var_id: ValueId,
) -> Result<(JoinModule, JoinFragmentMeta, Vec<ConditionBinding>), String> {
eprintln!("[joinir/pattern3/if-sum] Starting AST-based if-sum lowering");
// Step 1: Extract loop condition info (e.g., i < len → var="i", op=Lt, limit=len)
let (loop_var, loop_op, loop_limit) = extract_loop_condition(loop_condition)?;
eprintln!("[joinir/pattern3/if-sum] Loop condition: {} {:?} {}", loop_var, loop_op, loop_limit);
// Phase 220-B Step 1: Build ConditionEnv (following Pattern 2 style)
let cond_vars = extract_condition_variables(
loop_condition,
&[loop_var_name.to_string()], // Exclude loop variable itself
);
// Step 2: Extract if condition info (e.g., i > 0 → var="i", op=Gt, value=0)
let (if_var, if_op, if_value) = extract_if_condition(if_stmt)?;
eprintln!("[joinir/pattern3/if-sum] If condition: {} {:?} {}", if_var, if_op, if_value);
eprintln!(
"[joinir/pattern3/if-sum] Extracted {} condition variables: {:?}",
cond_vars.len(),
cond_vars
);
// Phase 220-B Step 2: Build ConditionEnv using ConditionEnvBuilder pattern
let mut cond_env = ConditionEnv::new();
let mut cond_bindings = Vec::new();
// Add loop variable to environment
let loop_var_join_id = join_value_space.alloc_param();
cond_env.insert(loop_var_name.to_string(), loop_var_join_id);
eprintln!(
"[joinir/pattern3/if-sum] Loop variable '{}': host={:?}, join={:?}",
loop_var_name, loop_var_id, loop_var_join_id
);
// Add condition-only variables
for var_name in &cond_vars {
let host_id = variable_map
.get(var_name)
.copied()
.ok_or_else(|| {
format!(
"[if-sum] Condition variable '{}' not found in variable_map. \
Available: {:?}",
var_name,
variable_map.keys().collect::<Vec<_>>()
)
})?;
let join_id = join_value_space.alloc_param();
cond_env.insert(var_name.clone(), join_id);
cond_bindings.push(ConditionBinding {
name: var_name.clone(),
host_value: host_id,
join_value: join_id,
});
eprintln!(
"[joinir/pattern3/if-sum] Condition variable '{}': host={:?}, join={:?}",
var_name, host_id, join_id
);
}
eprintln!(
"[joinir/pattern3/if-sum] ConditionEnv built: {} variables, {} bindings",
cond_env.len(),
cond_bindings.len()
);
// Step 3: Extract loop condition info (e.g., i < len → var="i", op=Lt, limit=len/ValueId)
let (loop_var, loop_op, loop_limit) = extract_loop_condition(loop_condition, &cond_env)?;
eprintln!("[joinir/pattern3/if-sum] Loop condition: {} {:?} {:?}", loop_var, loop_op, loop_limit);
// Step 4: Extract if condition info (e.g., i > 0 → var="i", op=Gt, value=0)
let (if_var, if_op, if_value) = extract_if_condition(if_stmt, &cond_env)?;
let if_value_desc = match &if_value {
ValueOrLiteral::Literal(n) => format!("literal {}", n),
ValueOrLiteral::Variable(name, id) => format!("variable '{}' ({:?})", name, id),
};
eprintln!("[joinir/pattern3/if-sum] If condition: {} {:?} {}", if_var, if_op, if_value_desc);
// Step 3: Extract then-branch update (e.g., sum = sum + 1 → var="sum", addend=1)
let (update_var, update_addend) = extract_then_update(if_stmt)?;
@ -137,10 +210,16 @@ pub fn lower_if_sum_pattern(
value: ConstValue::Integer(0),
}));
// result = loop_step(i_init, sum_init, count_init)
// Phase 220-B: Build call args including condition-only variables
// result = loop_step(i_init, sum_init, count_init, ...condition_vars)
let mut loop_call_args = vec![i_init_val, sum_init_val, count_init_val];
for binding in &cond_bindings {
loop_call_args.push(binding.join_value); // Include condition vars
}
main_func.body.push(JoinInst::Call {
func: loop_step_id,
args: vec![i_init_val, sum_init_val, count_init_val],
args: loop_call_args,
k_next: None,
dst: Some(loop_result),
});
@ -151,26 +230,53 @@ pub fn lower_if_sum_pattern(
join_module.add_function(main_func);
// === loop_step(i, sum, count) function ===
// === loop_step function ===
// Phase 220-B: Include condition-only variables as parameters
let mut loop_step_params = vec![loop_var_join_id, sum_param, count_param];
// Add condition-only variables as parameters
for binding in &cond_bindings {
loop_step_params.push(binding.join_value);
}
eprintln!(
"[if-sum/joinir] loop_step params: {} total ({} carriers + {} condition vars)",
loop_step_params.len(),
3, // i, sum, count
cond_bindings.len()
);
let mut loop_step_func = JoinFunction::new(
loop_step_id,
"loop_step".to_string(),
vec![i_param, sum_param, count_param],
loop_step_params,
);
// --- Exit Condition Check ---
// Load loop limit from AST
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const {
dst: loop_limit_val,
value: ConstValue::Integer(loop_limit),
}));
// Phase 220-B: Handle both literal and variable loop limits
let loop_limit_value_id = match loop_limit {
ValueOrLiteral::Literal(n) => {
// Literal: Generate Const instruction
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const {
dst: loop_limit_val,
value: ConstValue::Integer(n),
}));
loop_limit_val
}
ValueOrLiteral::Variable(ref var_name, value_id) => {
// Variable: Use ValueId from ConditionEnv (already a parameter)
eprintln!("[if-sum/joinir] Loop limit is variable '{}' = {:?}", var_name, value_id);
value_id
}
};
// Compare: i < limit (or other op from AST)
// Phase 220-B: Use loop_var_join_id (from ConditionEnv) instead of i_param
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Compare {
dst: cmp_loop,
op: loop_op,
lhs: i_param,
rhs: loop_limit_val,
lhs: loop_var_join_id, // Phase 220-B: Use ConditionEnv ValueId
rhs: loop_limit_value_id,
}));
// exit_cond = !cmp_loop
@ -188,18 +294,30 @@ pub fn lower_if_sum_pattern(
});
// --- If Condition (AST-based) ---
// Load if constant
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const {
dst: if_const,
value: ConstValue::Integer(if_value),
}));
// Phase 220-B: Handle both literal and variable if conditions
let if_value_id = match if_value {
ValueOrLiteral::Literal(n) => {
// Literal: Generate Const instruction
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const {
dst: if_const,
value: ConstValue::Integer(n),
}));
if_const
}
ValueOrLiteral::Variable(ref var_name, value_id) => {
// Variable: Use ValueId from ConditionEnv (already a parameter)
eprintln!("[if-sum/joinir] If condition value is variable '{}' = {:?}", var_name, value_id);
value_id
}
};
// Compare: if_var <op> if_value
// Phase 220-B: Use loop_var_join_id (from ConditionEnv)
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Compare {
dst: if_cmp,
op: if_op,
lhs: i_param, // Assuming if_var == loop_var (common case)
rhs: if_const,
lhs: loop_var_join_id, // Phase 220-B: Use ConditionEnv ValueId
rhs: if_value_id,
}));
// --- Then Branch ---
@ -269,17 +387,24 @@ pub fn lower_if_sum_pattern(
dst: step_const2,
value: ConstValue::Integer(counter_step),
}));
// Phase 220-B: Use loop_var_join_id for counter increment
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
dst: i_next,
op: BinOpKind::Add,
lhs: i_param,
lhs: loop_var_join_id, // Phase 220-B: Use ConditionEnv ValueId
rhs: step_const2,
}));
// --- Tail Recursion ---
// Phase 220-B: Include condition-only variables in recursive call
let mut recursive_args = vec![i_next, sum_new, count_new];
for binding in &cond_bindings {
recursive_args.push(binding.join_value); // Pass condition vars through
}
loop_step_func.body.push(JoinInst::Call {
func: loop_step_id,
args: vec![i_next, sum_new, count_new],
args: recursive_args,
k_next: None,
dst: None,
});
@ -312,21 +437,29 @@ pub fn lower_if_sum_pattern(
let fragment_meta = JoinFragmentMeta::with_expr_result(sum_final, exit_meta);
eprintln!("[joinir/pattern3/if-sum] Generated AST-based JoinIR");
eprintln!("[joinir/pattern3/if-sum] Loop: {} {:?} {}", loop_var, loop_op, loop_limit);
eprintln!("[joinir/pattern3/if-sum] If: {} {:?} {}", if_var, if_op, if_value);
let loop_limit_desc = match &loop_limit {
ValueOrLiteral::Literal(n) => format!("literal {}", n),
ValueOrLiteral::Variable(name, id) => format!("variable '{}' ({:?})", name, id),
};
eprintln!("[joinir/pattern3/if-sum] Loop: {} {:?} {}", loop_var, loop_op, loop_limit_desc);
eprintln!("[joinir/pattern3/if-sum] If: {} {:?} {}", if_var, if_op, if_value_desc);
eprintln!("[joinir/pattern3/if-sum] Phase 215-2: expr_result={:?}", sum_final);
eprintln!("[joinir/pattern3/if-sum] Phase 220-B: Returning {} condition bindings", cond_bindings.len());
Ok((join_module, fragment_meta))
Ok((join_module, fragment_meta, cond_bindings))
}
/// Extract loop condition: variable, operator, and limit
/// Phase 220-B: Extract loop condition with variable support
///
/// Supports: `var < lit`, `var <= lit`, `var > lit`, `var >= lit`
fn extract_loop_condition(cond: &ASTNode) -> Result<(String, CompareOp, i64), String> {
/// Supports: `var < lit/var`, `var <= lit/var`, `var > lit/var`, `var >= lit/var`
fn extract_loop_condition(
cond: &ASTNode,
cond_env: &ConditionEnv,
) -> Result<(String, CompareOp, ValueOrLiteral), String> {
match cond {
ASTNode::BinaryOp { operator, left, right, .. } => {
let var_name = extract_variable_name(left)?;
let limit = extract_integer_literal(right)?;
let limit = extract_value_or_variable(right, cond_env)?;
let op = match operator {
crate::ast::BinaryOperator::Less => CompareOp::Lt,
crate::ast::BinaryOperator::LessEqual => CompareOp::Le,
@ -340,11 +473,14 @@ fn extract_loop_condition(cond: &ASTNode) -> Result<(String, CompareOp, i64), St
}
}
/// Extract if condition: variable, operator, and value
fn extract_if_condition(if_stmt: &ASTNode) -> Result<(String, CompareOp, i64), String> {
/// Phase 220-B: Extract if condition with variable support
fn extract_if_condition(
if_stmt: &ASTNode,
cond_env: &ConditionEnv,
) -> Result<(String, CompareOp, ValueOrLiteral), String> {
match if_stmt {
ASTNode::If { condition, .. } => {
extract_loop_condition(condition) // Same format
extract_loop_condition(condition, cond_env) // Same format
}
_ => Err("[if-sum] Expected If statement".to_string()),
}
@ -406,15 +542,54 @@ fn extract_variable_name(node: &ASTNode) -> Result<String, String> {
}
}
/// Extract integer literal from AST node
/// Phase 220-B: Value or literal enum for condition expressions
#[derive(Debug)]
enum ValueOrLiteral {
Literal(i64),
Variable(String, ValueId),
}
/// Helper: Extract integer literal (for non-condition contexts like arithmetic)
fn extract_integer_literal(node: &ASTNode) -> Result<i64, String> {
match node {
ASTNode::Literal { value: crate::ast::LiteralValue::Integer(n), .. } => Ok(*n),
ASTNode::Variable { name, .. } => {
// Handle variable reference (e.g., `len`)
// For Phase 213, we only support literals. Variables need Phase 214+
Err(format!("[if-sum] Variable '{}' in condition not supported yet (Phase 214+)", name))
Err(format!("[if-sum] Variable '{}' in arithmetic expression not supported yet", name))
}
_ => Err(format!("[if-sum] Expected integer literal, got {:?}", node)),
}
}
/// Phase 220-B: Extract value or variable from AST node (with ConditionEnv support)
fn extract_value_or_variable(
node: &ASTNode,
cond_env: &ConditionEnv,
) -> Result<ValueOrLiteral, String> {
match node {
// Literal: Return as immediate value
ASTNode::Literal { value: crate::ast::LiteralValue::Integer(n), .. } => {
Ok(ValueOrLiteral::Literal(*n))
}
// Variable: Lookup in ConditionEnv
ASTNode::Variable { name, .. } => {
if let Some(value_id) = cond_env.get(name) {
eprintln!("[if-sum/extract] Variable '{}' resolved to {:?}", name, value_id);
Ok(ValueOrLiteral::Variable(name.clone(), value_id))
} else {
// Fail-Fast: Variable not in ConditionEnv
Err(format!(
"[if-sum] Variable '{}' not found in ConditionEnv (available: {:?})",
name, cond_env.names()
))
}
}
// Method call: Not supported yet (defer to Phase 221+)
ASTNode::MethodCall { .. } => {
Err("[if-sum] Method calls in conditions not supported yet (Phase 221+)".to_string())
}
_ => Err(format!("[if-sum] Expected integer literal or variable, got {:?}", node)),
}
}