Files
hakorune/docs/development/current/main/phase217-if-sum-multi.md
nyash-codex d4f90976da refactor(joinir): Phase 244 - ConditionLoweringBox trait unification
Unify condition lowering logic across Pattern 2/4 with trait-based API.

New infrastructure:
- condition_lowering_box.rs: ConditionLoweringBox trait + ConditionContext (293 lines)
- ExprLowerer implements ConditionLoweringBox trait (+51 lines)

Pattern migrations:
- Pattern 2 (loop_with_break_minimal.rs): Use trait API
- Pattern 4 (loop_with_continue_minimal.rs): Use trait API

Benefits:
- Unified condition lowering interface
- Extensible for future lowering strategies
- Clean API boundary between patterns and lowering logic
- Zero code duplication

Test results: 911/911 PASS (+2 new tests)

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-11 02:35:31 +09:00

8.8 KiB
Raw Blame History

Phase 217: Multi-Carrier If-Sum Complete

Overview

Phase 217 validates that Pattern 3 if-sum implementation supports multiple accumulators (sum + count) with zero additional code.

Status: COMPLETE - Multi-carrier if-sum works out-of-the-box

The Surprise: Zero Implementation Needed

Expected Work (Task 217-3)

  • Modify loop_with_if_phi_if_sum.rs for multi-carrier
  • Update AST extraction to handle 2+ updates
  • Wire multiple carriers through JoinIR generation

Actual Work

ZERO LINES OF CODE CHANGED 🎉

Why It Just Worked

Phase 217 is the culmination of three previous phases working in perfect harmony:

  1. Phase 195 (Multi-Carrier PHI Foundation)

    • CarrierInfo tracks arbitrary number of carriers
    • Exit PHI generation handles N carriers
    • ExitLineReconnector updates variable_map for all carriers
  2. Phase 214 (Dynamic Join Inputs)

    • Removed hardcoded 3-input assumption
    • join_inputs = (0..total_inputs).map(|i| ValueId(i))
    • Automatically scales: 1 loop_var + N carriers = N+1 inputs
  3. Phase 215 (ExprResult Exit Contract)

    • Marks first carrier as expr_result
    • Propagates through Boundary → ExitLine → Return
    • Works regardless of carrier count

Result: The "boxes" compose perfectly!

Test Case

Multi-Carrier If-Sum Pattern

File: apps/tests/phase217_if_sum_multi_min.hako

static box IfSumMultiTest {
    sum_and_count(defs) {
        local sum = 0
        local count = 0
        local i = 0
        local len = 3

        loop(i < len) {
            if i > 0 {
                sum = sum + 1      // Carrier 1 (conditional)
                count = count + 1  // Carrier 2 (conditional)
                print(sum)
                print(count)
            } else {
                print(0)
            }
            i = i + 1
        }

        return sum  // Returns first carrier
    }

    main() {
        local result = IfSumMultiTest.sum_and_count(0)
        return result
    }
}

Pattern:

  • Counter: i (0 to 2)
  • Accumulator 1: sum (incremented at i=1, i=2)
  • Accumulator 2: count (incremented at i=1, i=2)

Expected: RC=2 (sum value) Actual: RC=2

Test Results

Primary Target

Test File Carriers Loop Var Expected Actual Status
phase217_if_sum_multi_min.hako sum+count (2) i RC=2 RC=2 PASS

Verification:

./target/release/nyash apps/tests/phase217_if_sum_multi_min.hako
# Output: (prints 1, 1, 2, 2)
# RC=2

Regression Tests (All Passing)

Test File Pattern Carriers Expected Actual Status
loop_if_phi.hako P3 sum (1) RC=2 RC=2 PASS
phase212_if_sum_min.hako P3 sum (1) RC=2 RC=2 PASS
loop_min_while.hako P1 i (1) RC=2 RC=2 PASS

Architecture Verification

Carrier Detection (Automatic)

Phase 195's CarrierInfo automatically detected:

  • Carrier 1: sum (UpdateKind::AccumulationLike)
  • Carrier 2: count (UpdateKind::AccumulationLike)
  • Loop var: i (UpdateKind::CounterLike)

Dynamic Input Generation (Phase 214)

// Automatic scaling
let total_inputs = 1 + exit_bindings.len();  // 1 + 2 = 3
let join_inputs: Vec<ValueId> = (0..total_inputs)
    .map(|i| ValueId(i as u32))
    .collect();  // [ValueId(0), ValueId(1), ValueId(2)]

let host_inputs = vec![
    ctx.loop_var_id,     // i
    exit_binding_0,      // sum
    exit_binding_1,      // count
];

Exit PHI Generation (Phase 195)

Loop Header (bb4):
  %i_phi = phi [0, bb3], [%i_next, bb14]      // Loop variable
  %sum_phi = phi [0, bb3], [%sum_exit, bb14]  // Carrier 1
  %count_phi = phi [0, bb3], [%count_exit, bb14]  // Carrier 2

Loop Body (bb5-bb13):
  (if-else branches update sum/count conditionally)

Loop Exit (bb14):
  %sum_exit = phi [%sum_then, bb_then], [%sum_phi, bb_else]
  %count_exit = phi [%count_then, bb_then], [%count_phi, bb_else]
  %i_next = add %i_phi, 1
  branch bb4  // Loop back with all 3 values

ExprResult Selection (Phase 215)

// Phase 215: First carrier becomes expr_result
let fragment_meta = JoinFragmentMeta::with_expr_result(
    sum_exit,  // First carrier (sum)
    exit_meta  // Contains both sum and count
);

// Result: sum reaches return statement (RC=2)
//         count updates variable_map only

Box Theory Validation

Phase 217 proves the "Everything is Box" philosophy:

Box Composition

┌─────────────────────────────────────────────┐
│ Phase 195: Multi-Carrier PHI (Box A)       │
│ - Handles N carriers automatically         │
│ - CarrierInfo + ExitMeta                   │
└─────────────────────────────────────────────┘
                    ↓
┌─────────────────────────────────────────────┐
│ Phase 214: Dynamic Inputs (Box B)          │
│ - Scales to N+1 inputs automatically       │
│ - join_inputs generation                    │
└─────────────────────────────────────────────┘
                    ↓
┌─────────────────────────────────────────────┐
│ Phase 215: ExprResult Contract (Box C)     │
│ - Selects first carrier for return         │
│ - Works regardless of carrier count        │
└─────────────────────────────────────────────┘
                    ↓
┌─────────────────────────────────────────────┐
│ Phase 217: Multi-Carrier If-Sum            │
│ - Boxes A + B + C compose perfectly         │
│ - ZERO additional code needed              │
└─────────────────────────────────────────────┘

Key Insight

Well-designed boxes compose without modification.

Each phase created a reusable box with clear contracts:

  • Box A: "I handle N carriers"
  • Box B: "I generate N+1 inputs"
  • Box C: "I select expr_result from carriers"

When combined, these boxes just work for multi-carrier patterns.

Lessons Learned

1. Fail-Fast Design Pays Off

Phase 195's strict assertions caught problems early:

debug_assert_eq!(
    join_inputs.len(),
    host_inputs.len(),
    "join_inputs != host_inputs"
);

This forced Phase 214 to fix the root cause (hardcoded inputs), which then made Phase 217 trivial.

2. Single Responsibility Principle

Each phase had one clear job:

  • Phase 195: Multi-carrier detection and PHI generation
  • Phase 214: Multi-carrier input scaling
  • Phase 215: Multi-carrier expr_result selection

No phase tried to solve everything at once.

3. Box-First Development

By treating each capability as a "box" (module with clear interface), we:

  • Avoided tight coupling
  • Enabled composition
  • Made testing independent
  • Reduced implementation risk

Future Work

Phase 218: Nested If-Else Patterns

  • Target: esc_json() with nested conditions
  • Validates complex if-else inside loops
  • Likely needs: ConditionEnv or BoolExprLowerer enhancements

Phase 219: JsonParser Integration

  • Apply multi-carrier if-sum to actual JsonParser loops
  • First target: numeric processing with flags
  • Validates selfhost compiler real use case

Phase 220: Variable Limit Conditions

  • Support: loop(i < len) where len is variable (not literal)
  • Currently only integer literals supported in if-sum lowerer
  • Needs: AST extraction enhancement for variable references

Files Modified

ZERO source code files modified in Phase 217.

Files Added:

  • apps/tests/phase217_if_sum_multi_min.hako (test case)
  • docs/development/current/main/phase217-if-sum-multi.md (this doc)

Summary

Phase 217 is a validation phase that proves the correctness of the Phase 195/214/215 architecture:

  • Multi-carrier if-sum works with zero additional code
  • All regression tests passing
  • Box composition working perfectly
  • Ready for more complex patterns (Phase 218+)

The fact that Phase 217 required no implementation is not a bug—it's a feature of good box-first design. When boxes compose correctly, new capabilities emerge naturally.

「箱理論」の勝利! 🎉 Status: Active
Scope: If-sum multi ケースJoinIR v2