Files
hakorune/docs/development/current/main/phase171-pattern5-loop-inventory.md
nyash-codex 14c84fc583 feat(joinir): Phase 171 complete - Trim pattern LoopBodyLocal promotion
Phase 171-C-4/5 + impl-Trim: Full Trim pattern validation infrastructure

## CarrierInfo 統合 (C-4)
- CarrierInfo::merge_from(): Deduplicated carrier merging
- TrimPatternInfo::to_carrier_info(): Conversion helper
- Pattern2/4: Promoted carrier merge integration
- 7 unit tests for merge logic

## TrimLoopHelper 設計 (C-5)
- TrimLoopHelper struct: Trim-specific validation box
- carrier_type(), initial_value(), whitespace helpers
- CarrierInfo::trim_helper() accessor
- 5 unit tests

## Validation-Only Integration (impl-Trim)
- TrimLoopHelper::is_safe_trim(), is_trim_like(), has_valid_structure()
- Pattern2/4: Trim exception route with safety validation
- body_locals extraction from loop body AST
- LoopBodyCarrierPromoter: ASTNode::Local handler extension
- 4 unit tests for safety validation

## Architecture
- Box Theory: TrimLoopHelper is "validation only" (no JoinIR generation)
- Fail-Fast: Non-Trim LoopBodyLocal immediately rejected
- Whitelist approach: Only Trim pattern bypasses LoopBodyLocal restriction

Tests: 16 new unit tests, all passing
E2E: test_trim_main_pattern.hako validation successful

Next: Phase 172 - Actual JoinIR lowering for Trim pattern

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-08 02:41:53 +09:00

13 KiB
Raw Blame History

Phase 171-A: Blocked Loop Inventory

Date: 2025-12-07 Status: Initial inventory complete Purpose: Identify loops blocked by LoopBodyLocal variables in break conditions


Overview

This document catalogs loops that cannot be lowered by Pattern 2/4 because they use LoopBodyLocal variables in their break conditions. These are candidates for Pattern 5 carrier promotion.


Blocked Loops Found

1. TrimTest.trim/1 - Leading Whitespace Trim

File: local_tests/test_trim_main_pattern.hako Lines: 20-27

loop(start < end) {
    local ch = s.substring(start, start+1)
    if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" {
        start = start + 1
    } else {
        break
    }
}

Blocking Variable: ch (LoopBodyLocal)

Analysis:

  • Loop parameter: start (LoopParam)
  • Outer variable: end (OuterLocal)
  • Break condition uses: ch (LoopBodyLocal)
  • ch is defined inside loop body as s.substring(start, start+1)

Error Message:

[trace:debug] pattern2: Pattern 2 lowerer failed: Variable 'ch' not bound in ConditionEnv

Why Blocked: Pattern 2 expects break conditions to only use:

  • LoopParam (start)
  • OuterLocal (end, s)

But the break condition ch == " " || ... uses ch, which is defined inside the loop body.


2. TrimTest.trim/1 - Trailing Whitespace Trim

File: local_tests/test_trim_main_pattern.hako Lines: 30-37

loop(end > start) {
    local ch = s.substring(end-1, end)
    if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" {
        end = end - 1
    } else {
        break
    }
}

Blocking Variable: ch (LoopBodyLocal)

Analysis:

  • Loop parameter: end (LoopParam)
  • Outer variable: start (OuterLocal)
  • Break condition uses: ch (LoopBodyLocal)
  • ch is defined inside loop body as s.substring(end-1, end)

Same blocking reason as Loop 1.


3. JsonParserBox - MethodCall in Condition

File: tools/hako_shared/json_parser.hako

loop(i < s.length()) {
    // ...
}

Blocking Issue: s.length() is a MethodCall in the condition expression.

Error Message:

[ERROR] ❌ MIR compilation error: [cf_loop/pattern4] Lowering failed:
Unsupported expression in value context: MethodCall {
    object: Variable { name: "s", ... },
    method: "length",
    arguments: [],
    ...
}

Why Blocked: Pattern 4's value context lowering doesn't support MethodCall expressions yet.

Note: This is not a LoopBodyLocal issue, but a MethodCall limitation. May be addressed in Phase 171-D (Optional).


Pattern5-A Target Decision

Selected Target: TrimTest Loop 1 (Leading Whitespace)

We select the first loop from TrimTest as Pattern5-A target for the following reasons:

  1. Clear structure: Simple substring + equality checks
  2. Representative: Same pattern as many real-world parsers
  3. Self-contained: Doesn't depend on complex outer state
  4. Testable: Easy to write unit tests

Pattern5-A Specification

Loop Structure:

loop(start < end) {
    local ch = s.substring(start, start+1)
    if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" {
        start = start + 1
    } else {
        break
    }
}

Variables:

  • start: LoopParam (carrier, mutated)
  • end: OuterLocal (condition-only)
  • s: OuterLocal (used in body)
  • ch: LoopBodyLocal (blocking variable)

Break Condition:

!(ch == " " || ch == "\t" || ch == "\n" || ch == "\r")

Promotion Strategy: Design D (Evaluated Bool Carrier)

Rationale

We choose Design D (Evaluated Bool Carrier) over other options:

Why not carry ch directly?

  • ch is a StringBox, not a primitive value
  • Would require complex carrier type system
  • Would break existing Pattern 2/4 assumptions

Design D approach:

  • Introduce a new carrier: is_whitespace (bool)
  • Evaluate ch == " " || ... in loop body
  • Store result in is_whitespace carrier
  • Use is_whitespace in break condition

Transformed Structure

Before (Pattern5-A):

loop(start < end) {
    local ch = s.substring(start, start+1)
    if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" {
        start = start + 1
    } else {
        break
    }
}

After (Pattern2 compatible):

// Initialization (before loop)
local is_whitespace = true  // Initial assumption

loop(start < end && is_whitespace) {
    local ch = s.substring(start, start+1)
    is_whitespace = (ch == " " || ch == "\t" || ch == "\n" || ch == "\r")

    if is_whitespace {
        start = start + 1
    } else {
        break  // Now redundant, but kept for clarity
    }
}

Key transformations:

  1. Add is_whitespace carrier initialization
  2. Update loop condition to include is_whitespace
  3. Compute is_whitespace in loop body
  4. Original if-else becomes simpler (could be optimized away)

Next Steps (Phase 171-C)

Phase 171-C-1: Skeleton Implementation

  • Create LoopBodyCarrierPromoter box
  • Define PromotionRequest / PromotionResult types
  • Implement skeleton try_promote() method
  • Add find_definition_in_body() helper

Phase 171-C-2: Trim Pattern Promotion Logic

  • Detect substring + equality pattern
  • Generate is_whitespace carrier
  • Generate initialization statement
  • Generate update statement

Phase 171-C-3: Integration with Pattern 2/4

  • Call LoopBodyCarrierPromoter::try_promote() in routing
  • If promotion succeeds, route to Pattern 2
  • If promotion fails, return UnsupportedPattern

Phase 171-D: MethodCall Support (Optional)

  • Handle s.length() in loop conditions
  • May require carrier promotion for method results
  • Lower priority than Trim pattern

Summary

Blocked Loops:

  • 2 loops in TrimTest (LoopBodyLocal ch)
  • 1+ loops in JsonParser (MethodCall in condition)

Pattern5-A Target:

  • TrimTest leading whitespace trim loop
  • Clear, representative, testable

Promotion Strategy:

  • Design D: Evaluated Bool Carrier
  • Transform ch checks → is_whitespace carrier
  • Make compatible with Pattern 2

Implementation Status:

  • Phase 171-A: Inventory complete
  • Phase 171-B: Target selected
  • Phase 171-C-1: Skeleton implementation complete
  • Phase 171-C-2: Trim pattern detection implemented
    • find_definition_in_body(): AST traversal for variable definitions
    • is_substring_method_call(): Detects substring() method calls
    • extract_equality_literals(): Extracts string literals from OR chains
    • TrimPatternInfo: Captures pattern details for carrier promotion
  • Phase 171-C-3: Integration with Pattern 2/4 routing complete
  • Phase 171-C-4: CarrierInfo integration complete (2025-12-07)
    • CarrierInfo::merge_from(): Deduplicated carrier merging with deterministic sorting
    • TrimPatternInfo::to_carrier_info(): Conversion to CarrierInfo with TrimLoopHelper
    • Pattern 2/4 lowerers: Promoted carrier merging in Promoted branch
    • 7 unit tests: Merge success/failure/duplication/determinism validation
  • Phase 171-C-5: TrimLoopHelper design complete (2025-12-07)
    • TrimLoopHelper struct: Encapsulates Trim pattern lowering logic
    • CarrierInfo::trim_helper(): Accessor for pattern-specific helper
    • Module export: mod.rs updated with pub use TrimLoopHelper
    • 4 unit tests: Helper creation and accessor validation

Phase 171-C-3/4/5: Responsibility Positions and Data Flow

Responsibility Separation Principle

The promotion system follows Box Theory's single responsibility principle:

  1. router.rs: Pattern table + can_lower()/lower() call abstraction (no Scope/condition logic)
  2. Pattern 2/4 lowerer: Holds LoopScope / ConditionScope / CarrierInfo / Promoter
  3. LoopBodyCarrierPromoter: LoopBodyLocal handling specialist box
  4. TrimLoopHelper: Trim pattern-specific helper (future extensibility)

Data Flow Diagram

LoopConditionScopeBox::analyze()
        ↓
    has_loop_body_local()?
        ↓ true
LoopBodyCarrierPromoter::try_promote()
        ↓ Promoted { trim_info }
    TrimPatternInfo::to_carrier_info()
        ↓
    CarrierInfo::merge_from()
        ↓
    TrimLoopHelper (attached to CarrierInfo)
        ↓
    Pattern 2/4 lowerer (JoinIR generation)

Implementation Locations

Phase 171-C-4 Changes:

  • src/mir/join_ir/lowering/carrier_info.rs: Added merge_from(), trim_helper(), trim_helper field
  • src/mir/loop_pattern_detection/loop_body_carrier_promoter.rs: Updated to_carrier_info() to attach TrimLoopHelper
  • src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs: Promoted branch now merges carriers
  • src/mir/builder/control_flow/joinir/patterns/pattern4_with_continue.rs: Promoted branch now merges carriers

Phase 171-C-5 Changes:

  • src/mir/loop_pattern_detection/trim_loop_helper.rs: NEW - TrimLoopHelper struct with 4 unit tests
  • src/mir/loop_pattern_detection/mod.rs: Export TrimLoopHelper module

Phase 171-impl-Trim: Trim 特例の実戦投入

Date: 2025-12-08 Status: Validation complete Purpose: Safely integrate Trim pattern detection into JoinIR pipeline with validation-only implementation

設計原則

「LoopBodyLocal を全面解禁」ではなく、Trim パターンだけを箱経由でホワイトリストに載せる

安全機構

  1. TrimLoopHelper::is_safe_trim() - 構造的に安全か判定
  2. TrimLoopHelper::is_trim_like() - Trim パターンに合致するか判定
  3. TrimLoopHelper::has_valid_structure() - 構造チェック

データフローTrim 特例)

LoopConditionScopeBox::analyze()
        ↓
    has_loop_body_local() == true
        ↓
LoopBodyCarrierPromoter::try_promote()
        ↓ Promoted { trim_info }
    TrimPatternInfo::to_carrier_info()
        ↓
    CarrierInfo::merge_from()
        ↓
    carrier_info.trim_helper()?.is_safe_trim()
        ↓ true
    ✅ Validation Success (TODO: JoinIR lowering in Phase 172)

実装状況

  • Phase 171-impl-Trim-1: 受け入れ条件を 1 箇所に
    • TrimLoopHelper::is_safe_trim() implemented
    • Pattern2/4 で Trim 特例ルート実装
    • Fail-Fast on unsafe patterns
  • Phase 171-impl-Trim-2: TrimLoopHelper 判定メソッド
    • is_trim_like(), has_valid_structure() implemented
    • 4+ ユニットテスト追加 (9 tests total, all passing)
  • Phase 171-impl-Trim-3: E2E テスト
    • local_tests/test_trim_main_pattern.hako validated
    • 出力: [pattern2/trim] Safe Trim pattern detected
    • 出力: ✅ Trim pattern validation successful!
    • Status: Validation-only (JoinIR lowering deferred to Phase 172)
  • Phase 171-impl-Trim-4: ドキュメント更新
    • phase171-pattern5-loop-inventory.md updated
    • CURRENT_TASK.md status tracking

実装詳細

ファイル変更:

  1. src/mir/loop_pattern_detection/trim_loop_helper.rs

    • is_safe_trim(), is_trim_like(), has_valid_structure() methods
    • 4 new unit tests for safety validation
  2. src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs

    • Trim exception route with safety check
    • body_locals extraction from loop body AST
    • Validation message for successful detection
  3. src/mir/builder/control_flow/joinir/patterns/pattern4_with_continue.rs

    • Same Trim exception logic as Pattern2
    • body_locals extraction from normalized loop body
  4. src/mir/loop_pattern_detection/loop_body_carrier_promoter.rs

    • ASTNode::Local { variables, initial_values, .. } handler
    • Support for local ch = expr pattern recognition

テスト結果

ユニットテスト: 9/9 passing

  • test_is_safe_trim
  • test_is_safe_trim_empty_carrier
  • test_is_safe_trim_no_whitespace
  • test_has_valid_structure
  • (+ 5 existing tests)

E2E テスト: Validation successful

[pattern2/check] Analyzing condition scope: 3 variables
[pattern2/check]   'ch': LoopBodyLocal
[pattern2/promoter] LoopBodyLocal 'ch' promoted to carrier 'is_ch_match'
[pattern2/trim] Safe Trim pattern detected, bypassing LoopBodyLocal restriction
[pattern2/trim] Carrier: 'is_ch_match', original var: 'ch', whitespace chars: ["\r", "\n", "\t", " "]
✅ Trim pattern validation successful! Carrier 'is_ch_match' ready for Phase 172 implementation.
(Pattern detection: PASS, Safety check: PASS, JoinIR lowering: TODO)

ビルド結果: Success

  • cargo build --release: Success
  • cargo test --lib trim_loop_helper: 9/9 passing

重要な発見

  1. AST構造の理解: local ch = exprASTNode::Local { variables, initial_values, .. } として表現される
  2. body_locals 抽出: Pattern2/4 で LoopScopeShape.body_locals を AST から抽出する必要があった
  3. 段階的実装: Validation-only approach で安全性を先に確立し、JoinIR lowering は Phase 172 に分離

次のステップ (Phase 172)

  • Phase 172-1: Trim pattern JoinIR generation
    • Carrier initialization code
    • Carrier update logic (substring + OR chain → bool)
    • Exit PHI mapping
  • Phase 172-2: JsonParser loops への展開
    • Similar pattern recognition
    • Generalized carrier promotion