Proper HOST↔JoinIR ValueId separation for condition variables: - Add ConditionEnv struct (name → JoinIR-local ValueId mapping) - Add ConditionBinding struct (HOST/JoinIR ValueId pairs) - Modify condition_to_joinir to use ConditionEnv instead of builder.variable_map - Update Pattern2 lowerer to build ConditionEnv and ConditionBindings - Extend JoinInlineBoundary with condition_bindings field - Update BoundaryInjector to inject Copy instructions for condition variables This fixes the undefined ValueId errors where HOST ValueIds were being used directly in JoinIR instructions. Programs now execute (RC: 0), though loop variable exit values still need Phase 172 work. Key invariants established: 1. JoinIR uses ONLY JoinIR-local ValueIds 2. HOST↔JoinIR bridging is ONLY through JoinInlineBoundary 3. condition_to_joinir NEVER accesses builder.variable_map 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
8.1 KiB
Phase 171-3: Register Condition Inputs in JoinIR Lowerers
Date: 2025-12-07 Status: ✅ Complete
Implementation Summary
Successfully implemented the infrastructure for registering condition-only input variables in JoinIR lowerers.
Changes Made
1. Extended JoinInlineBoundary Structure
File: src/mir/join_ir/lowering/inline_boundary.rs
Added field:
pub struct JoinInlineBoundary {
pub join_inputs: Vec<ValueId>,
pub host_inputs: Vec<ValueId>,
pub join_outputs: Vec<ValueId>,
pub host_outputs: Vec<ValueId>,
pub exit_bindings: Vec<LoopExitBinding>,
/// NEW: Condition-only input variables (Phase 171+)
pub condition_inputs: Vec<(String, ValueId)>, // [(var_name, host_value_id)]
}
Rationale: Condition variables like start, end, len are read-only inputs that don't participate in loop iteration but must be available in JoinIR scope for condition evaluation.
2. Updated All Constructors
Modified constructors:
new_inputs_only()- Addedcondition_inputs: vec![]new_with_outputs()- Addedcondition_inputs: vec![]new_with_input_and_host_outputs()- Addedcondition_inputs: vec![]new_with_exit_bindings()- Addedcondition_inputs: vec![]
New constructors:
new_with_condition_inputs()- For loops with condition variablesnew_with_exit_and_condition_inputs()- For loops with carriers AND condition variables
Example usage:
let boundary = JoinInlineBoundary::new_with_condition_inputs(
vec![ValueId(0)], // join_inputs (i)
vec![ValueId(5)], // host_inputs (i)
vec![
("start".to_string(), ValueId(33)),
("end".to_string(), ValueId(34)),
],
);
3. Implemented Condition Variable Extraction
File: src/mir/join_ir/lowering/condition_to_joinir.rs
New functions:
extract_condition_variables()
pub fn extract_condition_variables(
cond_ast: &ASTNode,
exclude_vars: &[String],
) -> Vec<String>
Recursively traverses condition AST and collects all unique variable names, excluding loop parameters that are already registered as join_inputs.
Features:
- ✅ Sorted output (BTreeSet) for determinism
- ✅ Filters excluded variables (loop parameters)
- ✅ Handles complex conditions (AND, OR, NOT chains)
collect_variables_recursive()
fn collect_variables_recursive(
ast: &ASTNode,
vars: &mut BTreeSet<String>
)
Helper function that recursively visits all AST nodes and extracts variable names.
Supported AST nodes:
Variable- Adds variable name to setBinaryOp- Recursively processes left and right operandsUnaryOp- Recursively processes operandLiteral- No variables (skipped)
4. Added Comprehensive Tests
File: src/mir/join_ir/lowering/condition_to_joinir.rs
Test cases:
test_extract_condition_variables_simple()
// AST: start < end
let vars = extract_condition_variables(&ast, &[]);
assert_eq!(vars, vec!["end", "start"]); // Sorted
test_extract_condition_variables_with_exclude()
// AST: i < end
let vars = extract_condition_variables(&ast, &["i".to_string()]);
assert_eq!(vars, vec!["end"]); // 'i' excluded
test_extract_condition_variables_complex()
// AST: start < end && i < len
let vars = extract_condition_variables(&ast, &["i".to_string()]);
assert_eq!(vars, vec!["end", "len", "start"]); // Sorted, 'i' excluded
5. Updated Test Code
File: src/mir/builder/control_flow/joinir/patterns/exit_binding.rs
Fixed test:
let mut boundary = JoinInlineBoundary {
host_inputs: vec![],
join_inputs: vec![],
host_outputs: vec![],
join_outputs: vec![],
exit_bindings: vec![], // Phase 171: Added
condition_inputs: vec![], // Phase 171: Added
};
Design Decisions
Why Separate condition_inputs from join_inputs?
| Aspect | Loop Parameters (join_inputs) |
Condition Inputs (condition_inputs) |
|---|---|---|
| Mutability | Mutable (e.g., i = i + 1) |
Read-only (never modified) |
| Lifetime | Entire loop duration | Only during condition evaluation |
| JoinIR representation | Function parameters | Captured values |
| Example | i in loop(i < 3) |
end in loop(i < end) |
Semantic clarity: Separating them makes the intent explicit and prevents accidental mutation of condition-only variables.
Why Use Vec<(String, ValueId)> Instead of HashMap?
Chosen: Vec<(String, ValueId)>
Alternative: HashMap<String, ValueId>
Rationale:
- Order preservation: Vec maintains insertion order for deterministic behavior
- Small size: Typically 0-3 variables, Vec is more efficient than HashMap
- Iteration simplicity: Direct iteration without collecting keys
- Consistency: Matches pattern of
exit_bindings
Build Status
✅ Library build: Successful (0 errors, 57 warnings) ✅ Struct extension: All constructors updated ✅ Tests: 3 new tests added for extraction function ✅ Backward compatibility: All existing code still compiles
Build command:
cargo build --release
Result:
Finished `release` profile [optimized] target(s) in 0.07s
What's Still Missing
Not Yet Implemented (Phase 171-4 scope):
-
Condition variable registration in loop lowerers
loop_with_break_minimal.rsneeds to callextract_condition_variables()loop_with_continue_minimal.rsneeds to callextract_condition_variables()- Pass extracted variables to boundary constructor
-
ValueId mapping in merge logic
merge_joinir_mir_blocks()needs to inject Copy instructions for condition inputs- Instruction rewriter needs to remap condition variable ValueIds
-
Test with actual failing case
test_trim_main_pattern.hakostill fails (ValueId(33) undefined)- Need to apply the infrastructure to actual loop lowerers
Next Steps
Phase 171-4: Implement condition input mapping in merge/reconnect logic
Priority tasks:
- Modify
loop_with_break_minimal.rsto extract and register condition variables - Update
merge_joinir_mir_blocks()to handlecondition_inputs - Inject Copy instructions for condition variables
- Remap ValueIds in condition evaluation instructions
- Test with
test_trim_main_pattern.hako
Technical Notes
Extraction Algorithm Complexity
Time complexity: O(n) where n = number of AST nodes in condition Space complexity: O(v) where v = number of unique variables
Determinism guarantee: BTreeSet ensures sorted output regardless of traversal order
Edge Cases Handled
- No condition variables: Returns empty vector
- Loop variable in condition: Properly excluded (e.g.,
loop(i < 10)) - Duplicate variables: BTreeSet automatically deduplicates
- Nested expressions: Recursion handles arbitrary depth
Boundary Contract
Invariant: condition_inputs contains ONLY variables that:
- Appear in the loop condition AST
- Are NOT loop parameters (not in
join_inputs) - Exist in HOST
variable_mapat lowering time
Violation detection: Will be caught in Phase 171-4 when looking up HOST ValueIds
Files Modified
-
src/mir/join_ir/lowering/inline_boundary.rs(+93 lines)- Added
condition_inputsfield - Added 2 new constructors
- Updated 4 existing constructors
- Updated test assertions
- Added
-
src/mir/join_ir/lowering/condition_to_joinir.rs(+180 lines)- Added
extract_condition_variables()function - Added
collect_variables_recursive()helper - Added 3 comprehensive tests
- Added
-
src/mir/builder/control_flow/joinir/patterns/exit_binding.rs(+2 lines)- Fixed test boundary initialization
Total: +275 lines (infrastructure only, no behavior change yet)
References
- Phase 171-1 Analysis:
phase171-1-boundary-analysis.md - Phase 171-2 Design:
phase171-2-condition-inputs-design.md - JoinIR Design:
docs/development/current/main/phase33-10-if-joinir-design.md