Completes Phase 218 investigation: Identifies root cause preventing AST-based if-sum lowerer from activating. ## Investigation Summary ### Test Case Created - File: apps/tests/phase218_json_if_sum_min.hako - Pattern: JsonParser-style conditional accumulation (sum = sum + i) - Expected: RC=10, Actual: RC=0 (blocked by phantom carrier) ### Root Cause Identified: Phantom Carrier Bug **Problem**: AST-based if-sum lowerer (Phase 213) never activates **Cause Chain**: 1. loop_update_summary.rs uses name-based heuristics 2. Detects phantom "count" variable (doesn't exist in code) 3. counter_count() returns 2 instead of 1 (i + phantom "count") 4. is_simple_if_sum_pattern() returns false 5. Dual-mode dispatch uses legacy fallback instead of AST lowerer **Evidence**: ``` Debug output shows hardcoded template "i % 2 == 1" (legacy lowerer) Not from AST extraction (AST-based lowerer never called) ``` ### Critical Discovery **Phase 216 claim "RC=2 ✅" for phase212_if_sum_min.hako is false** - Current execution: RC=0 (wrong) - Expected: RC=2 - Indicates: Phase 213-217 "success" was actually legacy lowerer ### Documentation Gap Phase 216/217 tests may not have been executed correctly, or regression occurred. All "if-sum" patterns are currently broken due to phantom carrier detection blocking AST lowerer activation. ## Files Created 1. apps/tests/phase218_json_if_sum_min.hako - Test case 2. docs/development/current/main/phase218-jsonparser-if-sum-min.md - Investigation report ## Files Updated 1. CURRENT_TASK.md - Phase 218 investigation results 2. joinir-architecture-overview.md - Phase 218 entry ## Next Steps: Phase 219 **Target**: Fix phantom carrier detection in loop_update_summary.rs **Fix**: Remove name-based heuristics, use assignment-based detection only **Expected Result**: Both phase212 and phase218 tests pass with correct RC ## Lessons Learned 1. Documentation can become stale - verify test results 2. Silent fallbacks hide bugs - dual-mode needs clear activation logging 3. Name heuristics are fragile - prefer structural analysis 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
7.7 KiB
Phase 218: JsonParser If-Sum Mini Test - Pattern Recognition Gap Found
Overview
Phase 218 attempts to apply Pattern 3 if-sum infrastructure (Phase 213-217) to JsonParser-style conditional accumulation pattern.
Status: 🔍 INVESTIGATION COMPLETE - Pattern recognition gap identified
Test Case Created
File: apps/tests/phase218_json_if_sum_min.hako
Pattern: JsonParser-style conditional accumulation
static box JsonIfSumTest {
sum_digits(data) {
local sum = 0
local i = 0
local len = 5
loop(i < len) {
// JsonParser pattern: if digit > 0 { sum = sum + digit }
if i > 0 {
sum = sum + i // ← Variable-based accumulation (not literal!)
print(sum)
} else {
print(0)
}
i = i + 1
}
return sum
}
main() {
local result = JsonIfSumTest.sum_digits(0)
return result
}
}
Expected: RC=10 (sum = 1 + 2 + 3 + 4) Actual: RC=0 (legacy lowerer used, wrong logic)
Investigation Results
Observation 1: Legacy Lowerer Triggered
Debug output shows:
[joinir/pattern3] Generated JoinIR for Loop with If-Else PHI (Phase 195: multi-carrier)
[joinir/pattern3] Carriers: i (counter), sum (accumulator), count (counter) [Phase 195]
[joinir/pattern3] If-Else PHI in loop body:
[joinir/pattern3] sum_new = (i % 2 == 1) ? sum+i : sum+0
Finding: Legacy template lowerer is used (hardcoded i % 2 == 1 condition)
Observation 2: AST-Based Lowerer NOT Called
Expected debug messages from loop_with_if_phi_if_sum.rs:
[joinir/pattern3/if-sum] Starting AST-based if-sum lowering ← NOT SEEN
[joinir/pattern3/if-sum] Loop condition: ... ← NOT SEEN
Finding: ctx.is_if_sum_pattern() returns false, so AST-based lowerer is not triggered
Observation 3: Pattern vs Literal Difference
| Test | Update Pattern | Lowerer Used | Result |
|---|---|---|---|
| phase212_if_sum_min.hako | sum = sum + 1 |
Legacy ❌ | RC=0 |
| phase218_json_if_sum_min.hako | sum = sum + i |
Legacy ❌ | RC=0 |
Finding: Both use legacy lowerer, suggesting either:
- AST-based lowerer was never fully integrated, OR
- There's a regression since Phase 216 documentation
Observation 4: Phase 216 Claims Success
Phase 216 documentation states:
**Expected**: RC=2 (sum=1 at i=1, sum=2 at i=2)
**Actual**: **RC=2** ✅
But current execution of phase212_if_sum_min.hako returns RC=0, not RC=2.
Finding: Either a regression occurred, OR Phase 216 tests were never actually run successfully
Root Cause Analysis
Pattern Detection Logic
File: src/mir/builder/control_flow/joinir/patterns/pattern_pipeline.rs
pub fn is_if_sum_pattern(&self) -> bool {
// Check if loop_body has if statement
let has_if = self.loop_body.as_ref().map_or(false, |body| {
body.iter().any(|stmt| matches!(stmt, ASTNode::If { .. }))
});
if !has_if {
return false;
}
// Check carrier pattern using name heuristics
let summary = analyze_loop_updates(&all_names);
summary.is_simple_if_sum_pattern() // ← Returns false
}
Why Detection Fails
Carrier detection reports:
Carriers: i (counter), sum (accumulator), count (counter)
Issue: A phantom count carrier is detected as CounterLike instead of not existing.
This causes is_simple_if_sum_pattern() to fail:
pub fn is_simple_if_sum_pattern(&self) -> bool {
if self.counter_count() != 1 { return false; } // ← Fails here (2 counters: i, count)
if self.accumulation_count() < 1 { return false; }
if self.accumulation_count() > 2 { return false; }
true
}
Root cause: Name heuristic incorrectly classifies count as a counter based on its name alone.
Implications for JsonParser Pattern
Variable-Based Accumulation
JsonParser uses patterns like:
local digit = extract_digit(json, i)
if digit != "" {
sum = sum + _str_to_int(digit) // ← Variable, not literal!
}
This requires:
- Pattern recognition: Detect
sum = sum + variableas accumulation (not justsum = sum + 1) - AST extraction: Extract variable references (not just integer literals)
Current Limitations
File: src/mir/join_ir/lowering/loop_with_if_phi_if_sum.rs
extract_then_update() only supports literal addends:
fn extract_then_update(if_stmt: &ASTNode) -> Result<(String, i64), String> {
// ...
match addend_expr {
ASTNode::IntegerLiteral { value } => {
Ok((var_name, *value)) // ← Only literals!
}
_ => Err("Then update addend must be integer literal".to_string())
}
}
Needed: Support for variable references:
ASTNode::Variable { name } => {
// Return variable name, lowerer generates Load instruction
Ok((var_name, VariableRef(name.clone())))
}
Task 218-3: Minimal Fix Strategy (80/20 Rule)
Option A: Fix Pattern Detection (Recommended)
File: src/mir/join_ir/lowering/loop_update_summary.rs
Issue: Phantom count carrier detected
Fix: Improve name heuristic to not detect non-existent variables
Option B: Support Variable Addends
File: src/mir/join_ir/lowering/loop_with_if_phi_if_sum.rs
Issue: extract_then_update() only supports literals
Fix: Support ASTNode::Variable in addend extraction
Recommended Approach
Start with Option A (simpler):
- Fix phantom
countdetection - Verify phase212 test passes with AST-based lowerer
- If successful, phase218 test should also work (same pattern, different addend value)
Only if needed, do Option B:
- Extend AST extraction for variable references
- Update JoinIR generation to handle variable loads
Files Created
apps/tests/phase218_json_if_sum_min.hako- JsonParser-style test casedocs/development/current/main/phase218-jsonparser-if-sum-min.md- This document
Next Steps
Immediate (Phase 219)
-
Fix phantom carrier detection (Option A)
- Investigate why
countis detected when it doesn't exist - Fix name heuristic or carrier enumeration logic
- Verify phase212 test passes (RC=2)
- Investigate why
-
Regression check
- Run all Phase 212/217 tests
- Verify RC values match Phase 216 documentation
Future (Phase 220+)
-
Variable addend support (Option B)
- Extend AST extraction for variable references
- Update JoinIR lowerer to generate Load instructions
- Target: JsonParser real-world pattern (
sum = sum + digit)
-
Method call results
- Support:
sum = sum + _str_to_int(digit) - Requires: Expression lowering in JoinIR context
- Support:
Lessons Learned
1. Documentation vs Reality Gap
Phase 216 claims "RC=2 ✅" but current execution shows "RC=0 ❌".
Learning: Always verify documented success with actual execution. Documentation can become stale.
2. Legacy Code Path Persistence
Even with AST-based lowerer implemented, legacy path is still used.
Learning: Dual-mode architectures need clear activation conditions. If detection fails, system silently falls back.
3. Name Heuristics Are Fragile
Phantom count carrier detected based on name alone.
Learning: Name-based detection is a temporary solution. Phase 220+ should use AST-based analysis exclusively.
Summary
Phase 218 Goal: Apply Pattern 3 if-sum to JsonParser-style loop Outcome: Pattern recognition gap found (phantom carrier detection) Value: Identified root cause blocking AST-based lowerer activation Next: Fix carrier detection (Phase 219), then retry JsonParser pattern
The investigation was successful - we found why AST-based lowerer doesn't activate! 🔍