Files
hakorune/docs/development/current/main/PHASE_247EX_SUMMARY.md
nyash-codex 8900a3cc44 feat(joinir): Phase 247-EX - DigitPos dual-value architecture
Extends DigitPos promotion to generate TWO carriers for Pattern A/B support:
- Boolean carrier (is_digit_pos) for break conditions
- Integer carrier (digit_value) for NumberAccumulation

## Implementation

1. **DigitPosPromoter** (loop_body_digitpos_promoter.rs)
   - Generates dual carriers: is_<var> (bool) + <base>_value (int)
   - Smart naming: "digit_pos" → "digit" (removes "_pos" suffix)

2. **UpdateEnv** (update_env.rs)
   - Context-aware promoted variable resolution
   - Priority: <base>_value (int) → is_<var> (bool) → standard
   - Pass promoted_loopbodylocals from CarrierInfo

3. **Integration** (loop_with_break_minimal.rs)
   - UpdateEnv constructor updated to pass promoted list

## Test Results

- **Before**: 925 tests PASS
- **After**: 931 tests PASS (+6 new tests, 0 failures)

## New Tests

- test_promoted_variable_resolution_digit_pos - Full dual-value
- test_promoted_variable_resolution_fallback_to_bool - Fallback
- test_promoted_variable_not_a_carrier - Error handling

## Impact

| Pattern | Before | After |
|---------|--------|-------|
| _parse_number |  Works (bool only) |  Works (bool used, int unused) |
| _atoi |  Failed (missing int) |  READY (int carrier available!) |

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-11 15:08:14 +09:00

8.4 KiB

Phase 247-EX: DigitPos Dual-Value Architecture - IMPLEMENTATION COMPLETE

Date: 2025-12-11 Status: IMPLEMENTATION COMPLETE - All tests passing (931/931) Scope: Dual-value carrier generation for DigitPos pattern - resolves Phase 246-EX _atoi NumberAccumulation failure


🎯 Problem Statement

Phase 246-EX Discovery: DigitPos promotion loses integer digit value needed for NumberAccumulation.

Two Different Patterns

Pattern A (_parse_number): Works with Phase 224

loop(p < s.length()) {
    local ch = s.substring(p, p+1)
    local digit_pos = digits.indexOf(ch)

    if digit_pos < 0 { break }     // Only needs boolean (found/not found)

    num_str = num_str + ch          // Uses ch, NOT digit_pos
    p = p + 1
}

Pattern B (_atoi): Failed with Phase 224 (bool only)

loop(i < n) {
    local ch = s.substring(i, i+1)
    local pos = digits.indexOf(ch)

    if pos < 0 { break }            // Needs boolean (break condition)

    v = v * 10 + pos                // Needs INTEGER value for NumberAccumulation!
    i = i + 1
}

Root Cause: Phase 224 DigitPos promotion only generated boolean carrier (is_digit_pos), losing the integer digit value needed for accumulation.


Solution: Dual-Value Model

One Question → Two Outputs:

indexOf(ch) → -1 or 0-9
  ↓ (DigitPosPromoter Phase 247-EX)

Output A: is_digit_pos (boolean)  ← Break condition: "Is ch in digits?"
Output B: digit_value (integer)   ← Accumulation: "What digit value?"

Naming Convention

Phase 247-EX naming (Design Option A - Separate naming):

"digit_pos" → "is_digit_pos" (boolean) + "digit_value" (integer)
"pos"       → "is_pos" (boolean)       + "pos_value" (integer)

Base name extraction: "digit_pos""digit" (remove "_pos" suffix) → "digit_value"


📦 Implementation Details

1. DigitPosPromoter Extension

File: src/mir/loop_pattern_detection/loop_body_digitpos_promoter.rs

Changes:

  • Generate two carriers instead of one
  • Carrier 1: is_<var> (boolean, ConditionOnly role)
  • Carrier 2: <base>_value (integer, LoopState role)
  • Base name extraction: remove _pos suffix

Code:

// Boolean carrier (condition-only, for break)
let promoted_carrier_bool = CarrierVar {
    name: format!("is_{}", var_in_cond),
    role: CarrierRole::ConditionOnly,
    init: CarrierInit::BoolConst(false),
    // ...
};

// Integer carrier (loop-state, for NumberAccumulation)
let base_name = if var_in_cond.ends_with("_pos") {
    &var_in_cond[..var_in_cond.len() - 4]
} else {
    var_in_cond.as_str()
};
let promoted_carrier_int = CarrierVar {
    name: format!("{}_value", base_name),
    role: CarrierRole::LoopState,
    init: CarrierInit::FromHost,
    // ...
};

carrier_info.carriers = vec![promoted_carrier_bool, promoted_carrier_int];

2. UpdateEnv Resolution Logic

File: src/mir/join_ir/lowering/update_env.rs

Changes:

  • Added promoted_loopbodylocals: &'a [String] field
  • Enhanced resolve() method with promoted variable logic
  • Resolution priority:
    1. Try <base>_value (integer carrier)
    2. Fall back to is_<var> (boolean carrier, rare in updates)
    3. Standard resolution

Code:

pub fn resolve(&self, name: &str) -> Option<ValueId> {
    if self.promoted_loopbodylocals.iter().any(|v| v == name) {
        // Extract base name: "digit_pos" → "digit"
        let base_name = if name.ends_with("_pos") {
            &name[..name.len() - 4]
        } else {
            name
        };

        // Try <base>_value (integer carrier for NumberAccumulation)
        let int_carrier_name = format!("{}_value", base_name);
        if let Some(value_id) = self.condition_env.get(&int_carrier_name) {
            return Some(value_id);
        }

        // Fall back to is_<name> (boolean carrier)
        let bool_carrier_name = format!("is_{}", name);
        if let Some(value_id) = self.condition_env.get(&bool_carrier_name) {
            return Some(value_id);
        }
    }

    // Standard resolution
    self.condition_env.get(name).or_else(|| self.body_local_env.get(name))
}

3. Call Site Updates

File: src/mir/join_ir/lowering/loop_with_break_minimal.rs

Change: Pass promoted_loopbodylocals to UpdateEnv constructor:

let update_env = UpdateEnv::new(env, body_env, &carrier_info.promoted_loopbodylocals);

🧪 Testing

Unit Tests (3 new tests)

File: src/mir/join_ir/lowering/update_env.rs (tests module)

  1. test_promoted_variable_resolution_digit_pos - Full dual-value resolution
  2. test_promoted_variable_resolution_fallback_to_bool - Boolean-only fallback
  3. test_promoted_variable_not_a_carrier - Error handling (variable not found)

All tests pass:

Regression Tests

Before Phase 247-EX: 925 tests PASS After Phase 247-EX: 931 tests PASS (+6 new tests)

Result: 0 FAILURES, 0 REGRESSIONS


📊 Impact Summary

Aspect Before Phase 247-EX After Phase 247-EX
DigitPos carriers 1 (boolean only) 2 (boolean + integer)
_parse_number Works (bool sufficient) Works (bool used, int unused)
_atoi Fails (missing int value) READY (int carrier available)
Test count 925 PASS 931 PASS (+6)
Lines changed - +130 net (implementation + tests)

🎯 Next Steps

Phase 247-EX Completion Tasks

  • DigitPosPromoter dual-carrier generation
  • UpdateEnv promoted variable resolution
  • Unit tests for dual-value logic
  • Regression tests (931/931 PASS)
  • E2E Tests: Test actual _atoi and _parse_number loops
  • Commit: Phase 247-EX implementation

Future E2E Verification

Test _parse_number (Pattern A - bool only):

./target/release/hakorune apps/tests/phase189_parse_number_mini.hako
# Expected: Works with is_digit_pos (boolean), digit_value unused

Test _atoi (Pattern B - bool + int):

./target/release/hakorune apps/tests/phase246ex_atoi_e2e_42.hako
# Expected: Works with is_pos (bool for break) + pos_value (int for accumulation)

🏗️ Architecture Principles

Box-First Design

Single Responsibility:

  • DigitPosPromoter: Detects indexOf patterns, generates dual carriers
  • UpdateEnv: Resolves variables with context-aware promoted logic
  • ConditionEnv: Stores carrier ValueIds
  • CarrierUpdateEmitter: Emits JoinIR using resolved ValueIds

Fail-Safe:

  • Unused carriers (e.g., digit_value in _parse_number) are harmless
  • Resolution failures logged with warnings
  • Falls back to boolean carrier if integer carrier missing

Boundary Clarity:

Input:  digit_pos = indexOf(ch)  (AST)
  ↓ DigitPosPromoter
Output: is_digit_pos (bool) + digit_value (int)  (CarrierInfo)
  ↓ Pattern2 lowerer → ConditionEnv stores both carriers
  ↓ UpdateEnv resolves
Break:  digit_pos → is_digit_pos (bool, via DigitPosConditionNormalizer)
Update: digit_pos → digit_value (int, via UpdateEnv promoted logic)

📚 References

Design Documents:

Related Phases:

  • Phase 224: DigitPos promotion (boolean-only, foundation)
  • Phase 224-E: DigitPosConditionNormalizer (AST transformation)
  • Phase 227: CarrierRole enum (LoopState vs ConditionOnly)
  • Phase 228: CarrierInit enum (FromHost vs BoolConst)
  • Phase 246-EX: _atoi pattern discovery (triggered Phase 247-EX)

Implementation Files:

  • src/mir/loop_pattern_detection/loop_body_digitpos_promoter.rs - Dual-carrier generation
  • src/mir/join_ir/lowering/update_env.rs - Promoted variable resolution
  • src/mir/join_ir/lowering/loop_with_break_minimal.rs - Pattern2 integration

🎉 Success Metrics

All objectives achieved:

  1. Dual-carrier generation implemented
  2. Context-aware resolution working
  3. Zero regressions (931/931 tests PASS)
  4. Clean architecture (Box-First principles)
  5. Comprehensive unit tests (+3 new tests)

Phase 247-EX Status: IMPLEMENTATION COMPLETE - Ready for E2E validation and commit