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

425 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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
```hako
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
```hako
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`
```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**:
```hako
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)**:
```hako
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)**:
```hako
// 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)
```
### 実装状況
- [x] Phase 171-impl-Trim-1: 受け入れ条件を 1 箇所に ✅
- `TrimLoopHelper::is_safe_trim()` implemented
- `Pattern2/4` で Trim 特例ルート実装
- Fail-Fast on unsafe patterns
- [x] Phase 171-impl-Trim-2: TrimLoopHelper 判定メソッド ✅
- `is_trim_like()`, `has_valid_structure()` implemented
- 4+ ユニットテスト追加 (9 tests total, all passing)
- [x] 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)
- [x] 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 = expr``ASTNode::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