Files
hakorune/docs/development/current/main/phase200-C-digits-e2e.md
nyash-codex d4f90976da refactor(joinir): Phase 244 - ConditionLoweringBox trait unification
Unify condition lowering logic across Pattern 2/4 with trait-based API.

New infrastructure:
- condition_lowering_box.rs: ConditionLoweringBox trait + ConditionContext (293 lines)
- ExprLowerer implements ConditionLoweringBox trait (+51 lines)

Pattern migrations:
- Pattern 2 (loop_with_break_minimal.rs): Use trait API
- Pattern 4 (loop_with_continue_minimal.rs): Use trait API

Benefits:
- Unified condition lowering interface
- Extensible for future lowering strategies
- Clean API boundary between patterns and lowering logic
- Zero code duplication

Test results: 911/911 PASS (+2 new tests)

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-11 02:35:31 +09:00

9.5 KiB
Raw Blame History

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 拡張

// 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,
    );

    // 以降は既存のフロー...
}

注意点

  1. ループ AST の構築: analyze_captured_varsloop_ast: &ASTNode を必要とする

    • ctx.conditionctx.body から Loop ノードを構築する必要がある
    • または fn_body 内でループ位置を見つける
  2. 既存フローとの互換性: 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-DComplexAddendNormalizer 拡張 - 必要なら)

成功基準

  • 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.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 フローを大幅に変えない Status: Active
    Scope: digits ケースの end-to-end 収束メモConditionEnv ライン)