Files
hakorune/docs/development/current/main/phase195-impl-pattern3-multicarrier.md

628 lines
18 KiB
Markdown
Raw Normal View History

docs: Phase 195-impl Pattern3 multi-carrier implementation plan Phase 195-impl instruction document for P3 multi-carrier implementation: - Goal: Extend Pattern3 (If-Else PHI) to handle 2-3 carriers simultaneously - Approach: ExitLine extension (no PhiGroupBox - reuse existing infrastructure) Task breakdown: - 195-impl-1: Pattern3 lowerer multi-carrier support - extract_update_or_unchanged function (~40 lines) - Loop over all carriers in CarrierInfo - Handle unchanged carriers (use previous value) - Fail-Fast for partial updates / nested if - 195-impl-2: ExitLine extension verification - Check meta_collector.rs (likely already multi-carrier ready) - Check reconnector.rs (loop over all carrier_bindings) - Reuse CarrierVar.join_id infrastructure - 195-impl-3: if-sum test (sum + count) - PRIORITY - phase195_sum_count.hako (new file) - Expected: 72 (sum=7, count=2) - Verify no [joinir/freeze] - 195-impl-4: _parse_string simple (escaped only) - OPTIONAL - phase195_flag_buffer.hako (new file) - BoolFlag carrier test - Buffer concat deferred to Phase 19x+ - 195-impl-5: Documentation updates - phase195 design: Implementation Status section - CURRENT_TASK.md: Phase 195-impl completion - overview: Phase 195 completion mark Implementation focus: ```rust // Core change: single → multi carrier for (carrier_name, (then_val, else_val)) in carrier_updates { let phi_result = emit_phi(merge, then_val, else_val); exit_line.connect(carrier_name, phi_result); } ``` Success criteria: - Pattern3 lowerer handles multiple carriers - if-sum pattern works (sum+count) - No regressions in existing tests - Clear documentation of multi-carrier support 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-09 13:28:15 +09:00
# Phase 195-impl: Pattern3 複数キャリア対応(実装フェーズ)
**Status**: Ready for Implementation
**Date**: 2025-12-09
**Prerequisite**: Phase 195 design complete (phase195-pattern3-extension-design.md)
---
## 目的
**Pattern 3If-Else PHIで複数キャリア + 条件付き更新**を実装する。
**スコープ**:
- ✅ 複数キャリア2-3個の P3 処理
- ✅ ExitLine 拡張で対応PhiGroupBox は作らない)
- ✅ if-sum パターンsum + countを通す
- ⏸️ _parse_string 簡易版escaped のみ)は余力で
---
## Task 195-impl-1: Pattern3 lowerer の multi-carrier 対応
### 目標
Pattern3 lowerer を単一キャリア前提から**複数キャリア対応**に拡張する。
### 対象ファイル
- `src/mir/builder/control_flow/joinir/patterns/pattern3_with_if_phi.rs`
### 現状の実装Phase 170-189
**単一キャリア処理**:
```rust
// 既存: 単一キャリアのみ
fn lower_if_else_phi(
if_node: &ASTNode,
carrier_info: &CarrierInfo,
// ...
) -> Result<ExitMeta, String> {
// 1. Then/Else で carrier 更新式を取得
let carrier_name = &carrier_info.carriers[0]; // ← 単一キャリア前提
let then_update = extract_update(&if_node.then_branch, carrier_name)?;
let else_update = extract_update(&if_node.else_branch, carrier_name)?;
// 2. Then/Else ブロックで値を emit
let then_value = emit_update(&then_update, ...)?;
let else_value = emit_update(&else_update, ...)?;
// 3. Merge 点で PHI 生成
let phi_result = emit_phi(merge_block, then_value, else_value)?;
// 4. ExitMeta に接続情報を載せる
let exit_meta = ExitMeta {
carrier_bindings: vec![(carrier_name.clone(), phi_result)],
// ...
};
Ok(exit_meta)
}
```
### Phase 195-impl での拡張
**複数キャリア処理**:
```rust
// Phase 195: 複数キャリア対応
fn lower_if_else_phi(
if_node: &ASTNode,
carrier_info: &CarrierInfo,
// ...
) -> Result<ExitMeta, String> {
let mut carrier_bindings = Vec::new();
// 1. 全キャリアについてループ処理
for carrier_name in &carrier_info.carriers {
// 2. Then/Else で carrier 更新式を取得
let then_update = extract_update_or_unchanged(
&if_node.then_branch,
carrier_name,
&previous_values, // ← 更新なしの場合は前の値
)?;
let else_update = extract_update_or_unchanged(
&if_node.else_branch,
carrier_name,
&previous_values,
)?;
// 3. Then/Else ブロックで値を emit
let then_value = emit_update(&then_update, ...)?;
let else_value = emit_update(&else_update, ...)?;
// 4. Merge 点で PHI 生成carrier ごとに1つ
let phi_result = emit_phi(merge_block, then_value, else_value)?;
// 5. ExitMeta 用に保存
carrier_bindings.push((carrier_name.clone(), phi_result));
}
// 6. ExitMeta に全キャリアの接続情報を載せる
let exit_meta = ExitMeta {
carrier_bindings,
// ...
};
Ok(exit_meta)
}
```
### `extract_update_or_unchanged` 関数の追加
**目的**: 片方のブランチで更新がない場合、「前の値」を使用する。
```rust
fn extract_update_or_unchanged(
branch: &[ASTNode],
carrier_name: &str,
previous_values: &HashMap<String, ValueId>,
) -> Result<UpdateExpr, String> {
// Then/Else ブランチで carrier の Assign を探す
if let Some(assign) = find_assign(branch, carrier_name) {
// 更新あり: Assign の RHS を返す
Ok(UpdateExpr::FromAST(assign.rhs.clone()))
} else {
// 更新なし: 前の値(ループ header の PHI paramを使用
let prev_value = previous_values.get(carrier_name)
.ok_or_else(|| format!("Carrier '{}' not in previous values", carrier_name))?;
Ok(UpdateExpr::Unchanged(*prev_value))
}
}
enum UpdateExpr {
FromAST(Box<ASTNode>), // 更新式あり
Unchanged(ValueId), // 更新なし(前の値使用)
}
```
### Fail-Fast 条件
**Phase 195-impl で弾くケース**:
1. **片方のブランチのみで carrier 定義**(明示的な更新なしも不可):
```nyash
if(cond) {
sum = sum + i
// count は更新なしNG: 明示的に count = count が必要)
} else {
// sum も count も更新なしNG
}
```
→ エラー: "Carrier 'count' not updated in both branches"
2. **複雑なネスト**:
```nyash
if(cond1) {
if(cond2) { sum = sum + i }
}
```
→ エラー: "Nested if not supported in Phase 195"
### 実装手順
1. **`extract_update_or_unchanged` 関数追加** (~40 lines)
2. **`lower_if_else_phi` をループ処理に変更** (~30 lines 修正)
3. **`emit_update``UpdateExpr::Unchanged` 対応** (~10 lines)
4. **Fail-Fast エラーメッセージ追加** (~5 lines)
### ユニットテスト追加
```rust
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_multi_carrier_if_else() {
// CarrierInfo with 2 carriers: sum, count
let carrier_info = CarrierInfo {
carriers: vec!["sum".to_string(), "count".to_string()],
updates: hashmap! {
"sum" => ASTNode::BinOp { ... },
"count" => ASTNode::BinOp { ... },
},
};
// If-Else AST with updates for both carriers
let if_node = create_if_else_ast(/* ... */);
let result = lower_if_else_phi(&if_node, &carrier_info, ...);
assert!(result.is_ok());
let exit_meta = result.unwrap();
assert_eq!(exit_meta.carrier_bindings.len(), 2);
}
#[test]
fn test_unchanged_carrier_in_branch() {
// If with update only in then branch
let if_node = create_if_with_partial_update();
// Should handle unchanged carrier (use previous value)
let result = lower_if_else_phi(&if_node, &carrier_info, ...);
assert!(result.is_ok());
// Verify else branch uses previous value for unchanged carrier
}
}
```
---
## Task 195-impl-2: ExitLine 拡張で複数キャリア PHI を扱う
### 目標
ExitLine 側で**複数 PHI を処理**する拡張を入れる。
### 対象ファイル
- `src/mir/builder/control_flow/joinir/exit_line/meta_collector.rs`
- `src/mir/builder/control_flow/joinir/exit_line/reconnector.rs`
### 現状の ExitLinePhase 170-189
**単一キャリア処理**:
```rust
// ExitMetaCollector
pub fn collect_exit_meta(
pattern_result: &PatternResult,
) -> ExitMeta {
ExitMeta {
carrier_bindings: vec![
(carrier_name, phi_dst) // ← 単一キャリア
],
// ...
}
}
// ExitLineReconnector
pub fn reconnect(
exit_meta: &ExitMeta,
variable_map: &mut HashMap<String, ValueId>,
) {
for (carrier_name, phi_dst) in &exit_meta.carrier_bindings {
variable_map.insert(carrier_name.clone(), *phi_dst);
}
}
```
### Phase 195-impl での拡張
**既に multi-carrier インフラあり**CarrierVar.join_id, carrier_order:
```rust
pub struct CarrierVar {
pub name: String,
pub join_id: ValueId, // ← JoinIR 空間での ValueId
// ...
}
```
**実装内容**:
1. **ExitMetaCollector**: 既に複数 carrier 対応(変更不要の可能性高い)
2. **ExitLineReconnector**: ループで全 carrier を処理(既存コードを確認)
### 実装確認手順
1. **ExitMetaCollector を確認**:
```rust
// 既に複数 carrier_bindings を扱えているか確認
pub fn collect_exit_meta(
pattern_result: &PatternResult,
) -> ExitMeta {
let mut carrier_bindings = Vec::new();
// Pattern3 から渡ってくる全 carrier について
for carrier_var in &pattern_result.carriers {
carrier_bindings.push((
carrier_var.name.clone(),
carrier_var.join_id,
));
}
ExitMeta { carrier_bindings, ... }
}
```
2. **ExitLineReconnector を確認**:
```rust
// 既にループで全 carrier を処理しているか確認
pub fn reconnect(
exit_meta: &ExitMeta,
variable_map: &mut HashMap<String, ValueId>,
) {
for (carrier_name, phi_dst) in &exit_meta.carrier_bindings {
// variable_map で carrier.name -> phi_dst を更新
variable_map.insert(carrier_name.clone(), *phi_dst);
}
}
```
**想定**: 既存コードが既に複数対応している可能性が高いPhase 170-189 の設計が良好)
**修正が必要な場合**: ループ処理を追加するだけ(~5 lines
### ユニットテスト追加
```rust
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_exit_line_multi_carrier() {
let exit_meta = ExitMeta {
carrier_bindings: vec![
("sum".to_string(), ValueId(10)),
("count".to_string(), ValueId(11)),
],
// ...
};
let mut variable_map = HashMap::new();
reconnect(&exit_meta, &mut variable_map);
assert_eq!(variable_map.get("sum"), Some(&ValueId(10)));
assert_eq!(variable_map.get("count"), Some(&ValueId(11)));
}
}
```
---
## Task 195-impl-3: if-sum テストsum + countで確認
### 目標
複数キャリア P3 が**実際に動作する**ことを E2E テストで確認。
### テストファイル
**ファイル**: `apps/tests/phase195_sum_count.hako`
```nyash
static box Main {
main() {
local sum = 0
local count = 0
local i = 0
local len = 5
loop(i < len) {
if(i > 2) {
sum = sum + i // i=3,4 で加算
count = count + 1
}
i = i + 1
}
// Expected: sum=7 (3+4), count=2
local result = sum * 10 + count // 72
print(result)
return 0
}
}
```
### 実行手順
#### 1. ビルド
```bash
cargo build --release
```
#### 2. E2E 実行
```bash
# JoinIR 経路で実行
NYASH_JOINIR_CORE=1 ./target/release/hakorune apps/tests/phase195_sum_count.hako
# Expected output: 72
```
#### 3. Trace 確認
```bash
# JoinIR debug trace 有効化
NYASH_JOINIR_CORE=1 NYASH_JOINIR_DEBUG=1 ./target/release/hakorune apps/tests/phase195_sum_count.hako 2>&1 | grep "\[trace:joinir\]"
# Expected output (example):
# [trace:joinir] Pattern 3 applied: if-else with 2 carriers
# [trace:joinir] Carrier 'sum': PHI(%10, %11) -> %12
# [trace:joinir] Carrier 'count': PHI(%20, %21) -> %22
# CRITICAL: [joinir/freeze] が出ないこと
```
#### 4. 退行確認
```bash
# 既存の単一キャリア P3 テスト
./target/release/hakorune apps/tests/loop_if_phi.hako
# Expected: 既存の期待値
# Phase 190-194 テスト
./target/release/hakorune apps/tests/phase190_atoi_impl.hako
# Expected: 12
./target/release/hakorune apps/tests/phase191_body_local_atoi.hako
# Expected: 123
./target/release/hakorune apps/tests/phase193_init_method_call.hako
# Expected: RC:0
```
### 成功基準
- [ ] phase195_sum_count.hako が 72 を出力
- [ ] [joinir/freeze] が出ないJoinIR 経路で動作)
- [ ] 既存テストが退行しない
---
## Task 195-impl-4: _parse_string 簡易版escaped のみ)余力で
### 目標(オプション)
_parse_string の escaped フラグを P3 で扱うbuffer 連結は後回し)。
### テストファイル
**ファイル**: `apps/tests/phase195_flag_buffer.hako`
```nyash
static box Main {
main() {
local escaped = false
local i = 0
local len = 3
loop(i < len) {
if(i == 1) {
escaped = true
} else {
escaped = false
}
i = i + 1
}
// Expected: escaped=false (最後に i=2 で false 代入)
local result = 0
if(escaped) {
result = 1
}
print(result) // Expected: 0
return 0
}
}
```
### 実行手順
```bash
# JoinIR 経路で実行
NYASH_JOINIR_CORE=1 ./target/release/hakorune apps/tests/phase195_flag_buffer.hako
# Expected output: 0
```
### 成功基準(余力で)
- [ ] phase195_flag_buffer.hako が 0 を出力
- [ ] P3 で BoolFlagescapedが動作
- [ ] buffer 連結は Phase 19x 後半で対応(今回は対象外)
---
## Task 195-impl-5: ドキュメント更新
### 1. phase195-pattern3-extension-design.md 更新
末尾に "Implementation Status" セクション追加:
```markdown
## Implementation Status
**完了日**: 2025-12-XX
### 実装サマリ
**対応パターン**:
- [x] 複数キャリア P3 処理2-3個
- [x] if-sum パターンsum + count - phase195_sum_count.hako
- [ ] _parse_string 簡易版escaped のみ)- phase195_flag_buffer.hakoオプション
### 実装内容
**ファイル変更**:
1. `pattern3_with_if_phi.rs` (+60 lines)
- `extract_update_or_unchanged` 関数追加
- `lower_if_else_phi` を複数キャリア対応に拡張
- Fail-Fast 条件追加
2. `exit_line/meta_collector.rs`, `exit_line/reconnector.rs` (確認のみ)
- 既に複数 carrier_bindings 対応済み(変更不要)
### E2E テスト結果
**phase195_sum_count.hako**:
```bash
NYASH_JOINIR_CORE=1 ./target/release/hakorune apps/tests/phase195_sum_count.hako
# Output: 72 ✅
```
**退行テスト**:
- loop_if_phi.hako: PASS ✅
- phase190-194 tests: ALL PASS ✅
### 技術的発見
1. **ExitLine は既に multi-carrier 対応**:
- CarrierVar.join_id インフラが Phase 170-189 で整備済み
- 新規実装不要、Pattern3 側の拡張のみで動作
2. **Unchanged carrier の扱い**:
- 片方のブランチで更新なし → 前の値(ループ header PHI paramを使用
- `UpdateExpr::Unchanged(ValueId)` で表現
3. **Fail-Fast 効果**:
- 両ブランチで carrier 定義必須(明示的エラー)
- ネストした if は Phase 196+ に延期
### 制限事項Phase 195-impl
- ❌ ネストした if: `if(c1) { if(c2) { ... } }`
- ❌ 3個以上の carrier設計は対応済みだが、テスト未実施
- ❌ LoopBodyLocal + MethodCall 混在は Phase 195+ 延期
### 次のステップ
**Phase 196+**: 候補
- Pattern 3 のネスト対応
- 3個以上の carrier での動作検証
- _parse_string 完全版buffer 連結 + escaped flag 統合)
**Phase 200+**: ConditionEnv 拡張
- function-scoped variables サポート
- _parse_number, _atoi が動作可能に
```
### 2. CURRENT_TASK.md 更新
```markdown
## Phase 195-impl: Pattern3 複数キャリア対応(完了: 2025-12-XX
**目的**: P3If-Else PHIで複数キャリア + 条件付き更新を実装
**実装内容**:
- ✅ Pattern3 lowerer 拡張(複数キャリアループ処理)
- ✅ ExitLine 確認(既に multi-carrier 対応済み)
- ✅ if-sum テスト成功sum + count - phase195_sum_count.hako → 72 ✅
- ⏸️ _parse_string 簡易版escaped のみ)- 余力により実施
**成果**:
- P3 が「単一キャリア専用」から「複数キャリア対応」に昇格
- JsonParser/selfhost で if-in-loop パターンを拡張可能に
- 既存テスト退行なし
**技術的発見**:
- ExitLine は既に multi-carrier 対応CarrierVar.join_id インフラ活用)
- Unchanged carrier は前の値(ループ header PHI param使用
- Fail-Fast で複雑な分岐を明示的に弾く
**次のステップ**: Phase 196+Pattern 3 ネスト対応or Phase 200+ConditionEnv 拡張)
```
### 3. joinir-architecture-overview.md 更新
Section 7.2 の Phase 195 を完了マークに更新:
```markdown
- [x] **Phase 195**: Pattern 3 拡張(複数キャリア対応)
- P3 で 2-3 個の Carrier を同時処理可能に
- ExitLine 拡張で複数 PHI 生成(既存インフラ活用)
- if-sum パターンsum+count動作確認完了
- JsonParser カバレッジ向上への基盤完成
```
---
## 成功基準
- [x] Pattern3 lowerer が複数キャリア対応extract_update_or_unchanged 実装)
- [x] ExitLine が複数 PHI を処理(既存確認 or 拡張)
- [x] phase195_sum_count.hako が期待値72を出力
- [x] [joinir/freeze] が出ないJoinIR 経路で動作)
- [x] 既存テストphase190-194, loop_if_phiが退行しない
- [x] ドキュメント更新Implementation Status, CURRENT_TASK, overview
---
## 関連ファイル
### 実装対象
- `src/mir/builder/control_flow/joinir/patterns/pattern3_with_if_phi.rs`(主要実装)
- `src/mir/builder/control_flow/joinir/exit_line/meta_collector.rs`(確認のみ)
- `src/mir/builder/control_flow/joinir/exit_line/reconnector.rs`(確認のみ)
### テストファイル
- `apps/tests/phase195_sum_count.hako`(新規作成・必須)
- `apps/tests/phase195_flag_buffer.hako`(新規作成・オプション)
- `apps/tests/loop_if_phi.hako`(退行確認)
### ドキュメント
- `docs/development/current/main/phase195-pattern3-extension-design.md`Implementation Status 追加)
- `docs/development/current/main/joinir-architecture-overview.md`Phase 195 完了マーク)
- `CURRENT_TASK.md`Phase 195-impl 完了記録)
---
## 設計原則Phase 195-impl
1. **既存インフラ活用**:
- ExitLine の CarrierVar.join_id を再利用
- 新規箱なしPhiGroupBox は作らない)
2. **段階的実装**:
- まず if-sum で基盤確認
- 次に _parse_string 簡易版(余力で)
3. **Fail-Fast 継承**:
- 両ブランチで carrier 定義必須
- 複雑な分岐は明示的に弾く
4. **箱理論の実践**:
- 設計書に基づく実装
- 単一責任の原則維持
- ドキュメント駆動開発