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>
425 lines
13 KiB
Markdown
425 lines
13 KiB
Markdown
# 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
|