feat(joinir): Phase 200-B/C/D capture analysis + Phase 201-A reserved_value_ids infra

Phase 200-B: FunctionScopeCaptureAnalyzer implementation
- analyze_captured_vars_v2() with structural loop matching
- CapturedEnv for immutable function-scope variables
- ParamRole::Condition for condition-only variables

Phase 200-C: ConditionEnvBuilder extension
- build_with_captures() integrates CapturedEnv into ConditionEnv
- fn_body propagation through LoopPatternContext to Pattern 2

Phase 200-D: E2E verification
- capture detection working for base, limit, n etc.
- Test files: phase200d_capture_minimal.hako, phase200d_capture_in_condition.hako

Phase 201-A: MirBuilder reserved_value_ids infrastructure
- reserved_value_ids: HashSet<ValueId> field in MirBuilder
- next_value_id() skips reserved IDs
- merge/mod.rs sets/clears reserved IDs around JoinIR merge

Phase 201: JoinValueSpace design document
- Param/Local/PHI disjoint regions design
- API: alloc_param(), alloc_local(), reserve_phi()
- Migration plan for Pattern 1-4 lowerers

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-12-09 18:32:03 +09:00
parent 3a9b44c4e2
commit 32a91e31ac
24 changed files with 2815 additions and 193 deletions

View File

@ -0,0 +1,333 @@
# Phase 200-C: digits.indexOf E2E 連携
**Date**: 2025-12-09
**Status**: Ready for Implementation
**Prerequisite**: Phase 200-A/B complete
---
## ゴール
1. **PatternPipelineContext / LoopPatternContext に fn_body関数全体 ASTを通す**
2. **Pattern 2 で FunctionScopeCaptureAnalyzer を実際に呼び出す**
3. **digits.indexOf(ch) を含む最小ループを JoinIR 経由で最後まで動かす**
**成功基準**:
- `phase200_digits_atoi_min.hako` が正しい結果123を出力
- `phase200_digits_parse_number_min.hako` が正しい結果("42")を出力
---
## Task 200-C-1: LoopPatternContext に fn_body を追加
### 対象ファイル
- `src/mir/builder/control_flow/joinir/patterns/router.rs`
- `src/mir/builder/control_flow/joinir/routing.rs`
### 実装内容
#### 1. LoopPatternContext 拡張
```rust
// router.rs
pub struct LoopPatternContext<'a> {
// 既存フィールド
pub condition: &'a ASTNode,
pub body: &'a [ASTNode],
pub func_name: &'a str,
pub debug: bool,
pub has_continue: bool,
pub has_break: bool,
pub features: LoopFeatures,
pub pattern_kind: LoopPatternKind,
// Phase 200-C: NEW - 関数全体の AST
pub fn_body: Option<&'a [ASTNode]>,
}
impl<'a> LoopPatternContext<'a> {
pub fn new(
condition: &'a ASTNode,
body: &'a [ASTNode],
func_name: &'a str,
debug: bool,
) -> Self {
// 既存コード...
Self {
// ...
fn_body: None, // Phase 200-C: Default to None
}
}
/// Phase 200-C: Create context with fn_body for capture analysis
pub fn with_fn_body(
condition: &'a ASTNode,
body: &'a [ASTNode],
func_name: &'a str,
debug: bool,
fn_body: &'a [ASTNode],
) -> Self {
let mut ctx = Self::new(condition, body, func_name, debug);
ctx.fn_body = Some(fn_body);
ctx
}
}
```
#### 2. routing.rs から fn_body を渡す
```rust
// routing.rs - cf_loop_joinir_impl()
pub(in crate::mir::builder) fn cf_loop_joinir_impl(
&mut self,
condition: &ASTNode,
body: &[ASTNode],
func_name: &str,
debug: bool,
) -> Result<Option<ValueId>, String> {
use super::patterns::{route_loop_pattern, LoopPatternContext};
// Phase 200-C: Get fn_body from current_function if available
let fn_body_opt = self.current_function.as_ref()
.map(|f| f.body.as_slice());
let ctx = if let Some(fn_body) = fn_body_opt {
LoopPatternContext::with_fn_body(condition, body, func_name, debug, fn_body)
} else {
LoopPatternContext::new(condition, body, func_name, debug)
};
if let Some(result) = route_loop_pattern(self, &ctx)? {
// ...
}
// ...
}
```
### 制約
- P1/P3/P4 は `fn_body` を使わなくても動く(`None` を無視)
- `fn_body` が取得できない場合も動作する(空の CapturedEnv になる)
---
## Task 200-C-2: Pattern 2 で FunctionScopeCaptureAnalyzer を呼ぶ
### 対象ファイル
- `src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs`
### 実装内容
Pattern 2 lowerer の `lower()` 関数内で capture 解析を呼び出す:
```rust
// pattern2_with_break.rs
use crate::mir::loop_pattern_detection::function_scope_capture::{
analyze_captured_vars, CapturedEnv
};
pub fn lower(
builder: &mut MirBuilder,
ctx: &LoopPatternContext,
) -> Result<Option<ValueId>, String> {
// 既存のループスコープ分析...
let scope = /* ... */;
// Phase 200-C: Capture analysis
let captured_env = if let Some(fn_body) = ctx.fn_body {
// fn_body が利用可能 - capture 解析を実行
let loop_ast = /* ループ AST を構築 or ctx から取得 */;
analyze_captured_vars(fn_body, &loop_ast, &scope)
} else {
// fn_body なし - 空の CapturedEnv
CapturedEnv::new()
};
// 既存の ConditionEnv 構築を v2 に置き換え
let cond_env = build_with_captures(
&loop_var_name,
&captured_env,
&builder.variable_map,
loop_var_id,
);
// 以降は既存のフロー...
}
```
### 注意点
1. **ループ AST の構築**: `analyze_captured_vars``loop_ast: &ASTNode` を必要とする
- `ctx.condition``ctx.body` から Loop ノードを構築する必要がある
- または `fn_body` 内でループ位置を見つける
2. **既存フローとの互換性**: `captured_env` が空の場合は既存の動作と同じ
---
## Task 200-C-3: ConditionEnvBuilder v2 の統合
### 対象
Pattern 2 lowerer 内の ConditionEnv 構築箇所
### 実装内容
```rust
// 既存コード (Phase 200-B まで)
let cond_env = build_loop_param_only(&loop_var_name, &boundary)?;
// Phase 200-C: v2 に置き換え
let cond_env = build_with_captures(
&loop_var_name,
&captured_env, // 200-C-2 で取得
&builder.variable_map,
loop_var_id,
);
```
### 不変条件
- `captured_env` が空の場合、既存の `build_loop_param_only` と同じ結果
- `captured_env` に変数がある場合:
- `ConditionEnv.captured` に追加される
- `ParamRole::Condition` として boundary に登録される
- Header PHI や ExitLine の対象にはならない
---
## Task 200-C-4: digits ループの E2E 検証
### テストファイル
- `apps/tests/phase200_digits_atoi_min.hako` (Phase 200-B で作成済み)
- `apps/tests/phase200_digits_parse_number_min.hako` (Phase 200-B で作成済み)
### 検証手順
```bash
# Step 1: 構造トレース - Pattern 2 がマッチすること確認
NYASH_JOINIR_STRUCTURE_ONLY=1 ./target/release/hakorune \
apps/tests/phase200_digits_atoi_min.hako 2>&1 | head -30
# 確認:
# - Pattern2_WithBreak がマッチ
# - [joinir/freeze] や UnsupportedPattern が出ていない
# Step 2: Capture debug - digits が捕捉されていること確認
NYASH_CAPTURE_DEBUG=1 ./target/release/hakorune \
apps/tests/phase200_digits_atoi_min.hako 2>&1 | grep -i "capture"
# 期待出力:
# [capture] Found: digits (host_id=XX, is_immutable=true)
# Step 3: E2E 実行
./target/release/hakorune apps/tests/phase200_digits_atoi_min.hako
# 期待: 123
./target/release/hakorune apps/tests/phase200_digits_parse_number_min.hako
# 期待: "42"
```
### トラブルシューティング
異常があれば:
```bash
# PHI トレース
NYASH_TRACE_PHI=1 NYASH_TRACE_VARMAP=1 ./target/release/hakorune \
apps/tests/phase200_digits_atoi_min.hako 2>&1 | tail -50
# 確認ポイント:
# - digits が ConditionEnv.captured に入っているか
# - digits の ValueId が未定義になっていないか
```
### 期待される結果
| テスト | 期待値 | 確認内容 |
|--------|--------|----------|
| `phase200_digits_atoi_min.hako` | 123 | print(v) の出力 |
| `phase200_digits_parse_number_min.hako` | "42" | print(num_str) の出力 |
---
## Task 200-C-5: ドキュメント更新
### 1. joinir-architecture-overview.md
**追記内容**:
```markdown
### Phase 200-C: digits.indexOf E2E 連携 (完了)
- **LoopPatternContext 拡張**
- `fn_body: Option<&[ASTNode]>` フィールド追加
- `with_fn_body()` コンストラクタ追加
- 関数全体の AST を Pattern 2 lowerer に渡す
- **Pattern 2 キャプチャ統合**
- `analyze_captured_vars()` を Pattern 2 で呼び出し
- `build_with_captures()` で ConditionEnv 構築
- digits のような関数ローカルが JoinIR 経由で参照可能に
- **JsonParser 対応状況** (更新)
| メソッド | Pattern | ConditionEnv | Status |
|----------|---------|--------------|--------|
| _parse_number | P2 | digits capture | ✅ JoinIR |
| _atoi | P2 | digits capture | ✅ JoinIR |
```
### 2. CURRENT_TASK.md
**追記内容**:
```markdown
- [x] **Phase 200-C: digits.indexOf E2E 連携** ✅ (完了: 2025-12-09)
- **目的**: 200-A/B インフラを実際に Pattern 2 経路に統合
- **実装内容**:
- 200-C-1: LoopPatternContext に fn_body 追加 ✅
- 200-C-2: Pattern 2 で capture 解析呼び出し ✅
- 200-C-3: ConditionEnvBuilder v2 統合 ✅
- 200-C-4: digits E2E 検証 ✅
- 200-C-5: ドキュメント更新 ✅
- **成果**:
- phase200_digits_atoi_min.hako → 123 ✅
- phase200_digits_parse_number_min.hako → "42" ✅
- **次フェーズ**: Phase 200-DComplexAddendNormalizer 拡張 - 必要なら)
```
---
## 成功基準
- [x] LoopPatternContext に fn_body が追加されている
- [x] Pattern 2 で analyze_captured_vars() が呼ばれる
- [x] digits が CapturedEnv に捕捉される
- [x] ConditionEnv.captured に digits が存在する
- [x] phase200_digits_atoi_min.hako → 123 出力
- [x] phase200_digits_parse_number_min.hako → "42" 出力
- [x] 既存テストに退行なし
---
## 関連ファイル
### 修正対象
- `src/mir/builder/control_flow/joinir/patterns/router.rs`
- `src/mir/builder/control_flow/joinir/routing.rs`
- `src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs`
### ドキュメント
- `docs/development/current/main/joinir-architecture-overview.md`
- `CURRENT_TASK.md`
---
## 設計原則
1. **後方互換**: fn_body が取得できない場合も動作(空 CapturedEnv
2. **段階適用**: Pattern 2 のみに統合、他パターンは影響なし
3. **Fail-Fast 維持**: 安全でないパターンは無視(エラーにしない)
4. **最小変更**: 既存の routing/lowering フローを大幅に変えない