feat(joinir): Phase 240-EX - Pattern2 header condition ExprLowerer integration
Implementation: - Add make_pattern2_scope_manager() helper for DRY - Header conditions use ExprLowerer for supported patterns - Legacy fallback for unsupported patterns - Fail-Fast on supported patterns that fail Tests: - 4 new tests (all pass) - test_expr_lowerer_supports_simple_header_condition_i_less_literal - test_expr_lowerer_supports_header_condition_var_less_var - test_expr_lowerer_header_condition_generates_expected_instructions - test_pattern2_header_condition_via_exprlowerer Also: Archive old phase documentation (34k lines removed) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -0,0 +1,425 @@
|
||||
# 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
|
||||
Status: Historical
|
||||
Reference in New Issue
Block a user