feat(joinir): Phase 213-2 Step 2-2 & 2-3 Data structure extensions

Extended PatternPipelineContext and CarrierUpdateInfo for Pattern 3 AST-based generalization.

Changes:
1. PatternPipelineContext:
   - Added loop_condition: Option<ASTNode>
   - Added loop_body: Option<Vec<ASTNode>>
   - Added loop_update_summary: Option<LoopUpdateSummary>
   - Updated build_pattern_context() for Pattern 3

2. CarrierUpdateInfo:
   - Added then_expr: Option<ASTNode>
   - Added else_expr: Option<ASTNode>
   - Updated analyze_loop_updates() with None defaults

Status: Phase 213-2 Steps 2-2 & 2-3 complete
Next: Create Pattern3IfAnalyzer to extract if statement and populate update summary
This commit is contained in:
nyash-codex
2025-12-10 00:01:53 +09:00
parent 577b5b01d5
commit d7805e5974
138 changed files with 3529 additions and 378 deletions

View File

@ -0,0 +1,411 @@
# Phase 213: Pattern3 Lowerer 汎用化if-sum minimal
**Phase**: 213
**Date**: 2025-12-09
**Status**: 🚧 In Progress
**Prerequisite**: Phase 212.5 完了(構造ベース if 検出 + Pattern 3 routing
---
## 🎯 Phase 213 の目的
Phase 212.5 で正しく Pattern 3 にルーティングされるようになった `phase212_if_sum_min.hako` を、JoinIR Pattern 3If-PHIで正しく実行できるようにする。
**問題**: 現在の Pattern 3 lowerer は **test-only PoC 実装**
- Loop condition: `i <= 5` (hardcoded)
- If condition: `i % 2 == 1` (hardcoded)
- Update logic: `sum + i` (hardcoded)
**目標**: AST-based 汎用 Pattern 3 lowerer の実装
- LoopUpdateSummary / CarrierInfo / BoolExprLowerer ベースの汎用実装
- `phase212_if_sum_min.hako` で RC=2 達成
- 既存パターン(`loop_if_phi.hako` 等)の後方互換維持
---
## 📋 現状の Pattern 3 実装の問題点
### 1. ハードコードされた条件・更新式
**Loop condition** (`loop_with_if_phi_minimal.rs`):
```rust
// Hardcoded: i <= 5
let loop_cond_value = /* ... */;
```
**If condition**:
```rust
// Hardcoded: i % 2 == 1
let if_cond = /* modulo operation */;
```
**Update expressions**:
```rust
// Hardcoded: sum = sum + i, count = count + 1
let sum_update = /* sum + i */;
let count_update = /* count + 1 */;
```
### 2. テスト専用の ValueId マッピング
```rust
const PATTERN3_K_EXIT_SUM_FINAL_ID: ValueId = ValueId(24);
const PATTERN3_K_EXIT_COUNT_FINAL_ID: ValueId = ValueId(25);
```
これらは特定のテストケース用に固定されており、異なる carrier 構成には対応できない。
### 3. 汎用性の欠如
- `phase212_if_sum_min.hako` のような実際の if-sum パターンが動かない
- Carrier 構成が変わると動作しない
- If 条件が変わると対応できない
---
## 🏗️ 新しい入力情報アーキテクチャ
### 入力: PatternPipelineContext
Phase 213 では、以下の情報を利用して汎用的な lowering を実現:
**1. LoopFeatures** (from pattern_pipeline.rs)
- `has_if`: Loop body に if 文が存在するか
- `has_if_else_phi`: PHI merge が必要な if-else か
- `carrier_count`: Carrier 変数の数
**2. CarrierInfo**
- Carrier 変数のリスト名前、host_id、join_id
- 各 carrier の UpdateKindCounterLike, AccumulationLike, etc.
**3. LoopUpdateSummary**
```rust
pub struct LoopUpdateSummary {
pub updates: Vec<CarrierUpdateInfo>, // 各 carrier の更新情報
}
pub struct CarrierUpdateInfo {
pub carrier_name: String,
pub update_kind: UpdateKind,
pub then_expr: Option<ASTNode>, // then branch update
pub else_expr: Option<ASTNode>, // else branch update
}
```
**4. BoolExprLowerer / condition_to_joinir**
- 任意の bool 条件を JoinIR に変換
- 既存の `condition_to_joinir()` 関数を活用
**5. ConditionEnv / JoinValueSpace**
- Variable → ValueId マッピング
- ValueId allocation 管理
---
## 🔄 目標となる変換フロー
### Phase 213 汎用 Lowering Pipeline
```
Input: PatternPipelineContext
├─ loop_condition: ASTNode (e.g., "i < 3")
├─ loop_body: Vec<ASTNode> (contains if statement)
├─ CarrierInfo (e.g., [i, sum])
└─ LoopUpdateSummary (e.g., sum: then=sum+1, else=sum+0)
Step 1: Loop Condition Lowering
loop_condition AST → BoolExprLowerer
→ JoinIR loop_cond: ValueId
Step 2: Extract If Statement from Loop Body
Find ASTNode::If in loop_body
→ if_condition: ASTNode (e.g., "i > 0")
→ then_body: Vec<ASTNode>
→ else_body: Option<Vec<ASTNode>>
Step 3: If Condition Lowering
if_condition AST → BoolExprLowerer
→ JoinIR if_cond: ValueId
Step 4: Carrier Update Lowering (from LoopUpdateSummary)
For each carrier in CarrierInfo:
- Get then_expr from LoopUpdateSummary
- Get else_expr from LoopUpdateSummary
- Lower then_expr → JoinIR then_value: ValueId
- Lower else_expr → JoinIR else_value: ValueId
- Generate PHI: carrier_new = phi [then_value, else_value]
Step 5: JoinIR Function Generation
- entry(): Initialize carriers
- loop_step(i, carrier1, carrier2, ...):
if if_cond:
then_branch → update carriers (then values)
else:
else_branch → update carriers (else values)
PHI merge → carrier_new values
next iteration or exit
- k_exit(carrier1_final, carrier2_final, ...): Return final values
Step 6: ExitMeta Construction
ExitMeta {
carriers: [
{ name: "sum", join_id: ValueId(X), host_slot: ValueId(Y) },
...
]
}
Output: (JoinModule, ExitMeta)
```
---
## 🚨 Fail-Fast ポリシー
### 対応外パターンの明示的エラー
Pattern 3 lowerer は以下の場合に **明示的にエラー**を返す:
**1. LoopUpdateSummary 不整合**
```rust
if carrier.then_expr.is_none() || carrier.else_expr.is_none() {
return Err(JoinIrError::UnsupportedPattern {
reason: format!("Carrier '{}' missing then/else update", carrier.name)
});
}
```
**2. UpdateKind 未対応**
```rust
match carrier.update_kind {
UpdateKind::Complex | UpdateKind::Unknown => {
return Err(JoinIrError::UnsupportedPattern {
reason: format!("Carrier '{}' has unsupported UpdateKind: {:?}",
carrier.name, carrier.update_kind)
});
}
_ => { /* OK */ }
}
```
**3. If 構造不整合**
```rust
if loop_body.iter().filter(|n| matches!(n, ASTNode::If { .. })).count() != 1 {
return Err(JoinIrError::UnsupportedPattern {
reason: "Pattern 3 requires exactly one if statement in loop body".to_string()
});
}
```
**禁止事項**:
- ❌ Silent fallback to Pattern 1
- ❌ Default values for missing updates
- ❌ Ignoring unsupported UpdateKind
**原則**: **すべての制約は明示的エラーで通知**Fail-Fast
---
## 📐 設計の核心アイデア
### 1. 入力を「箱」として分離
**現状**: ハードコードされた値が scattered
**Phase 213**: 入力情報を構造化された箱から取得
```rust
// Before (Phase 195)
const LOOP_BOUND: i64 = 5; // Hardcoded
const IF_MODULO: i64 = 2; // Hardcoded
// After (Phase 213)
let loop_cond = ctx.loop_condition; // From PatternPipelineContext
let if_cond = extract_if_condition(&ctx.loop_body)?; // From AST
let updates = ctx.loop_update_summary; // From LoopUpdateSummary
```
### 2. Lowering を既存箱に委譲
**BoolExprLowerer**: Bool condition → JoinIR
```rust
let loop_cond_value = condition_to_joinir(
loop_cond,
&condition_env,
&mut join_value_space
)?;
```
**CarrierUpdateEmitter**: Update expression → JoinIR
```rust
let then_value = emit_carrier_update_with_env(
carrier.then_expr,
&update_env,
&mut join_value_space
)?;
```
### 3. ExitMeta で複数 Carrier を統一的に扱う
**現状**: 固定 ValueId の const 定義
**Phase 213**: ExitMeta に動的登録
```rust
// Before
exit_bindings.push(LoopExitBinding {
carrier_name: "sum".to_string(),
join_exit_value: PATTERN3_K_EXIT_SUM_FINAL_ID, // Hardcoded!
host_slot: sum_var_id,
});
// After
for carrier in carrier_info.carriers.iter() {
exit_bindings.push(LoopExitBinding {
carrier_name: carrier.name.clone(),
join_exit_value: carrier.join_final_id, // From JoinIR generation
host_slot: carrier.host_id,
});
}
```
---
## 🔧 実装の構造
### Target Files
**1. JoinIR Lowerer**
- `src/mir/join_ir/lowering/loop_with_if_phi_minimal.rs`
- **変更内容**:
- ハードコード削除
- PatternPipelineContext からの入力受け取り
- BoolExprLowerer / CarrierUpdateEmitter への委譲
**2. Pattern 3 Entry Point**
- `src/mir/builder/control_flow/joinir/patterns/pattern3_with_if_phi.rs`
- **変更内容**:
- PatternPipelineContext の構築
- ExitMeta の動的構築
- Fail-Fast エラーハンドリング
### Signature Changes
**Before (Phase 195)**:
```rust
pub fn lower_loop_with_if_phi_pattern(
scope: LoopScopeShape,
join_value_space: &mut JoinValueSpace,
) -> Option<JoinModule>
```
**After (Phase 213)**:
```rust
pub fn lower_loop_with_if_phi_pattern(
ctx: &PatternPipelineContext,
join_value_space: &mut JoinValueSpace,
) -> Result<(JoinModule, ExitMeta), JoinIrError>
```
**変更点**:
1. 入力: `LoopScopeShape``PatternPipelineContext`
2. 戻り値: `Option<JoinModule>``Result<(JoinModule, ExitMeta), JoinIrError>`
3. ExitMeta を返して動的 exit binding を可能に
---
## ✅ 検証計画
### Test Case 1: phase212_if_sum_min.hako主目標
**Input**:
```nyash
loop(i < 3) {
if i > 0 {
sum = sum + 1
}
i = i + 1
}
```
**Expected**:
- RC: **2**
- Pattern: Pattern 3 (If-Else PHI)
- Carriers: `i` (CounterLike), `sum` (AccumulationLike)
- Trace: `[joinir/pattern3] Generated JoinIR for Loop with If-Else PHI`
### Test Case 2: loop_if_phi.hako後方互換
既存の Phase 195 テストケース:
```nyash
loop(i < 5) {
if i % 2 == 1 {
sum = sum + i
} else {
sum = sum + 0
}
i = i + 1
}
```
**Expected**:
- 既存と同じ出力・RC
- Regression なし
### Test Case 3: Multi-carrier Phase 195 tests
Phase 195 で追加された multi-carrier tests:
- sum + count の 2-carrier
- sum + count + index の 3-carrier (if exists)
**Expected**:
- 既存と同じ挙動
- ExitMeta が複数 carrier を正しく処理
---
## 📊 Phase 213 タスクチェックリスト
- [ ] Task 213-1: 設計ドキュメント作成 ✅ (this file)
- [ ] Task 213-2: Pattern3 Lowerer 本体リファクタリング
- [ ] Step 2-1: ハードコード削除
- [ ] Step 2-2: 入力を Context ベースに変更
- [ ] Step 2-3: 条件 lowering を BoolExprLowerer に委譲
- [ ] Step 2-4: キャリア更新の一般化
- [ ] Step 2-5: PHI 生成
- [ ] Step 2-6: 戻り値と boundary 連携
- [ ] Task 213-3: Fail-Fast 条件の明確化
- [ ] Task 213-4: テスト & 検証
- [ ] phase212_if_sum_min.hako → RC=2
- [ ] loop_if_phi.hako → Regression check
- [ ] Multi-carrier tests → Regression check
- [ ] Task 213-5: ドキュメント更新
- [ ] phase212-if-sum-impl.md
- [ ] joinir-architecture-overview.md
- [ ] CURRENT_TASK.md
---
## 🎯 Success Criteria
**Phase 213 is complete when**:
1.`phase212_if_sum_min.hako` produces RC=2
2. ✅ All existing Pattern 3 tests pass (no regression)
3. ✅ No hardcoded conditions/updates in Pattern 3 lowerer
4. ✅ Fail-Fast errors for unsupported patterns
5. ✅ Documentation updated (3 files)
**Commit message format**:
```
feat(joinir): Phase 213 Pattern3 AST-based generalization
Phase 213 で Pattern3 lowerer を AST-based 汎用実装に書き換え。
phase212_if_sum_min.hako が RC=2 で正常動作。
- Removed hardcoded conditions/updates
- Integrated BoolExprLowerer for dynamic condition lowering
- Generalized carrier update via LoopUpdateSummary
- Dynamic ExitMeta construction for multi-carrier support
- Fail-Fast for unsupported patterns
```
---
**Phase 213: READY TO START** 🚀