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>
9.4 KiB
9.4 KiB
Phase 200-C: digits.indexOf E2E 連携
Date: 2025-12-09 Status: Ready for Implementation Prerequisite: Phase 200-A/B complete
ゴール
- PatternPipelineContext / LoopPatternContext に fn_body(関数全体 AST)を通す
- Pattern 2 で FunctionScopeCaptureAnalyzer を実際に呼び出す
- 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.rssrc/mir/builder/control_flow/joinir/routing.rs
実装内容
1. LoopPatternContext 拡張
// 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 を渡す
// 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 解析を呼び出す:
// 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,
);
// 以降は既存のフロー...
}
注意点
-
ループ AST の構築:
analyze_captured_varsはloop_ast: &ASTNodeを必要とするctx.conditionとctx.bodyから Loop ノードを構築する必要がある- または
fn_body内でループ位置を見つける
-
既存フローとの互換性:
captured_envが空の場合は既存の動作と同じ
Task 200-C-3: ConditionEnvBuilder v2 の統合
対象
Pattern 2 lowerer 内の ConditionEnv 構築箇所
実装内容
// 既存コード (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 で作成済み)
検証手順
# 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"
トラブルシューティング
異常があれば:
# 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
追記内容:
### 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
追記内容:
- [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-D(ComplexAddendNormalizer 拡張 - 必要なら)
成功基準
- LoopPatternContext に fn_body が追加されている
- Pattern 2 で analyze_captured_vars() が呼ばれる
- digits が CapturedEnv に捕捉される
- ConditionEnv.captured に digits が存在する
- phase200_digits_atoi_min.hako → 123 出力
- phase200_digits_parse_number_min.hako → "42" 出力
- 既存テストに退行なし
関連ファイル
修正対象
src/mir/builder/control_flow/joinir/patterns/router.rssrc/mir/builder/control_flow/joinir/routing.rssrc/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs
ドキュメント
docs/development/current/main/joinir-architecture-overview.mdCURRENT_TASK.md
設計原則
- 後方互換: fn_body が取得できない場合も動作(空 CapturedEnv)
- 段階適用: Pattern 2 のみに統合、他パターンは影響なし
- Fail-Fast 維持: 安全でないパターンは無視(エラーにしない)
- 最小変更: 既存の routing/lowering フローを大幅に変えない