Files
hakorune/docs/development/current/main/phase200-C-digits-e2e.md
nyash-codex 32a91e31ac 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>
2025-12-09 18:32:03 +09:00

334 lines
9.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 フローを大幅に変えない