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>
13 KiB
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) chis defined inside loop body ass.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) chis defined inside loop body ass.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:
- Clear structure: Simple substring + equality checks
- Representative: Same pattern as many real-world parsers
- Self-contained: Doesn't depend on complex outer state
- 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?
chis 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_whitespacecarrier - Use
is_whitespacein 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:
- Add
is_whitespacecarrier initialization - Update loop condition to include
is_whitespace - Compute
is_whitespacein loop body - Original if-else becomes simpler (could be optimized away)
Next Steps (Phase 171-C)
Phase 171-C-1: Skeleton Implementation ✅
- Create
LoopBodyCarrierPromoterbox - Define
PromotionRequest/PromotionResulttypes - 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_whitespacecarrier - 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
chchecks →is_whitespacecarrier - 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 definitionsis_substring_method_call(): Detectssubstring()method callsextract_equality_literals(): Extracts string literals from OR chainsTrimPatternInfo: 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 sortingTrimPatternInfo::to_carrier_info(): Conversion to CarrierInfo with TrimLoopHelper- Pattern 2/4 lowerers: Promoted carrier merging in
Promotedbranch - 7 unit tests: Merge success/failure/duplication/determinism validation
- Phase 171-C-5: ✅ TrimLoopHelper design complete (2025-12-07)
TrimLoopHelperstruct: Encapsulates Trim pattern lowering logicCarrierInfo::trim_helper(): Accessor for pattern-specific helper- Module export:
mod.rsupdated withpub 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:
- router.rs: Pattern table +
can_lower()/lower()call abstraction (no Scope/condition logic) - Pattern 2/4 lowerer: Holds LoopScope / ConditionScope / CarrierInfo / Promoter
- LoopBodyCarrierPromoter: LoopBodyLocal handling specialist box
- 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: Addedmerge_from(),trim_helper(),trim_helperfieldsrc/mir/loop_pattern_detection/loop_body_carrier_promoter.rs: Updatedto_carrier_info()to attach TrimLoopHelpersrc/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs: Promoted branch now merges carrierssrc/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 testssrc/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 パターンだけを箱経由でホワイトリストに載せる
安全機構
TrimLoopHelper::is_safe_trim()- 構造的に安全か判定TrimLoopHelper::is_trim_like()- Trim パターンに合致するか判定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()implementedPattern2/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.hakovalidated- 出力:
[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.mdupdatedCURRENT_TASK.mdstatus tracking
実装詳細
ファイル変更:
-
src/mir/loop_pattern_detection/trim_loop_helper.rsis_safe_trim(),is_trim_like(),has_valid_structure()methods- 4 new unit tests for safety validation
-
src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs- Trim exception route with safety check
body_localsextraction from loop body AST- Validation message for successful detection
-
src/mir/builder/control_flow/joinir/patterns/pattern4_with_continue.rs- Same Trim exception logic as Pattern2
body_localsextraction from normalized loop body
-
src/mir/loop_pattern_detection/loop_body_carrier_promoter.rsASTNode::Local { variables, initial_values, .. }handler- Support for
local ch = exprpattern recognition
テスト結果
ユニットテスト: ✅ 9/9 passing
test_is_safe_trimtest_is_safe_trim_empty_carriertest_is_safe_trim_no_whitespacetest_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: Successcargo test --lib trim_loop_helper: 9/9 passing
重要な発見
- AST構造の理解:
local ch = exprはASTNode::Local { variables, initial_values, .. }として表現される - body_locals 抽出: Pattern2/4 で
LoopScopeShape.body_localsを AST から抽出する必要があった - 段階的実装: 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