Files
hakorune/docs/development/current/main/phase84-2-case-d-investigation.md
nyash-codex 4ef5eec162 feat(mir): Phase 84-2 CopyTypePropagator for Copy chain type propagation
- Add CopyTypePropagator box (ChatGPT Pro design) for fixed-point
  Copy instruction type propagation
- Integrate into lifecycle.rs before return type inference
- Case D reduced from 12 to 9 (25% reduction)

Implementation:
- src/mir/phi_core/copy_type_propagator.rs: New box with fixed-point loop
- src/mir/phi_core/mod.rs: Add module export
- src/mir/builder/lifecycle.rs: Call propagator before return inference

Test results:
- Baseline: 494 passed, 33 failed (was 489/34)
- Case D: 9 remaining (from 12)
- Unit tests: 4/4 passed

Remaining 9 Case D breakdown:
- GroupA: Loop Edge Copy (7 cases) - PHI incoming needs Copy trace
- GroupB: Multi-level PHI (2 cases) - Recursive PHI resolution needed

Phase 84-3 will address GroupA with Edge Copy tracing in GenericTypeResolver.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-02 19:37:01 +09:00

13 KiB
Raw Blame History

Phase 84-2: Case D 残り 9件の詳細調査

概要

Phase 84-2 で CopyTypePropagator を実装した結果、Case D は 12件 → 9件に削減されました。 本ドキュメントは残り 9件の詳細分析結果をまとめます。

削減された 3件Phase 84-2 で解決)

CopyTypePropagator により以下のパターンが解決されました:

  • Copy チェーン伝播: r1 = Copy r0r2 = PHI [r1, r3] で r0 の型が r2 に伝播
  • 多段 Copy 追跡: Copy 命令を遡って元の ValueId の型を発見

残り 9件の一覧

# テスト名 ValueId パターン分類
1 test_lowering_await_expression ValueId(2) GroupC: await 特殊パターン
2 loop_with_continue_and_break_edge_copy_merge ValueId(56) GroupA: Loop + continue/break PHI
3 nested_loop_with_multi_continue_break_edge_copy_merge ValueId(135) GroupA: Nested loop 複雑 PHI
4 loop_inner_if_multilevel_edge_copy ValueId(74) GroupA: Loop + 多段 if
5 loop_break_and_early_return_edge_copy ValueId(40) GroupA: Loop + early return
6 vm_exec_break_inside_if ValueId(27) GroupA: Loop + if-break
7 loop_if_three_level_merge_edge_copy ValueId(75) GroupA: Loop + 3段 if
8 mir_stage1_cli_emit_program_min_exec_hits_type_error ValueId(7) GroupB: Stage1Cli 複雑型推論
9 mir_stage1_cli_emit_program_min_compiles_and_verifies ValueId(7) GroupB: Stage1Cli 複雑型推論

パターン分類の詳細

GroupA: Loop 制御フロー PHI7件

共通特徴:

  • NYASH_MIR_NO_PHI=1 でテストPHI-off モード)
  • Loop + continue/break による複雑な制御フロー
  • Edge Copy が複数の経路から合流する PHI

典型的な制御フロー:

loop(condition) {
  if (cond1) { break }    // → after_loop へ edge copy
  if (cond2) { continue } // → loop_header へ edge copy
  normal_path             // → loop_header へ edge copy
}
return merged_value       // ← PHI の型が未解決

問題の本質:

  • PHI の incoming 値が全て Edge Copy の dst ValueId
  • Edge Copy の src ValueId は value_types に登録されている
  • しかし、GenericTypeResolver は PHI の incoming 値の型 しか見ない
  • Copy の src を遡る処理が不足

なぜ CopyTypePropagator で解決できなかったか:

// CopyTypePropagator の現在のロジック
if let Some(ty) = types.get(&src) {
    types.insert(dst, ty.clone()); // ← dst に型を登録
}

問題点:

  • PHI の incoming 値 (dst) に型を登録
  • しかし、GenericTypeResolver::resolve_from_phi() は 既に登録された型 を参照
  • 参照のタイミングが遅い: lifecycle.rs が return 型を要求する時点で、 CopyTypePropagator はまだ実行されていない

具体例: loop_with_continue_and_break_edge_copy_merge

// 簡略化したコード
i = 0
sum = 0
loop(i < 5) {
  i = i + 1
  if (i == 3) { break }    // → edge copy: sum_final = sum
  if (i % 2 == 0) { continue } // → edge copy: sum_loop = sum
  sum = sum + i
}
return sum // ← ValueId(56) の型が未解決

MIR 構造(推測):

Block1 (loop_header):
  %sum_header = PHI [%sum_init, %sum_loop, %sum_updated]

Block2 (break):
  %sum_final = Copy %sum_header  ← value_types に型登録済み
  Jump after_loop

Block3 (continue):
  %sum_loop = Copy %sum_header   ← value_types に型登録済み
  Jump loop_header

Block4 (after_loop):
  %56 = PHI [%sum_final]         ← incoming の型が未登録!
  Return %56

テストファイルの所在:

  • src/tests/loop_continue_break_no_phi_tests.rs
  • src/tests/loop_nested_no_phi_tests.rs
  • src/tests/loop_return_no_phi_tests.rs
  • src/tests/mir_ctrlflow_break_continue.rs

GroupB: Stage1Cli 複雑型推論2件

共通特徴:

  • mir_stage1_cli_emit_program_min.rs の 2テスト
  • static box + env.get/set による複雑な型フロー
  • ValueId(7) が Main.main の戻り値

コードの特徴:

static box Stage1Cli {
  emit_program_json(source) {
    if source == null || source == "" { return null }
    return "{prog:" + source + "}"
  }

  stage1_main(args) {
    local src = env.get("STAGE1_SOURCE")
    if src == null || src == "" { return 96 }
    local prog = me.emit_program_json(src)
    if prog == null { return 96 }
    print(prog)
    return 0
  }
}

static box Main {
  main(args) {
    env.set("STAGE1_SOURCE", "apps/tests/stage1_using_minimal.hako")
    return Stage1Cli.stage1_main(args) // ← ValueId(7) の型
  }
}

問題の本質:

  • 多段メソッド呼び出し: Main.mainStage1Cli.stage1_mainemit_program_json
  • 複数の return 経路: null / 96 / 0
  • PHI が複数の経路から合流

デバッグ情報:

[DEBUG/build_block] Completed, returning value ValueId(14)
[DEBUG/build_block] Completed, returning value ValueId(83)
[DEBUG/build_block] Completed, returning value ValueId(95)
[DEBUG/build_block] Completed, returning value ValueId(47)
[DEBUG/build_block] Completed, returning value ValueId(63)
[DEBUG/build_block] Completed, returning value ValueId(7)

多数の ValueId が生成されており、PHI 合流が複雑であることを示唆。

テストファイル:

  • src/tests/mir_stage1_cli_emit_program_min.rs

GroupC: await 特殊パターン1件

テスト: test_lowering_await_expression ValueId: ValueId(2)

コードの特徴:

// src/mir/mod.rs:363
let ast = ASTNode::AwaitExpression {
    expression: Box::new(ASTNode::Literal {
        value: LiteralValue::Integer(1),
        span: crate::ast::Span::unknown(),
    }),
    span: crate::ast::Span::unknown(),
};

問題の本質:

  • await 式の MIR lowering が特殊な制御フローを生成
  • Core-13 pure mode では skip される(非同期システム未実装)
  • ValueId(2) は await の戻り値

なぜ特殊か:

  • await は将来的に Safepoint/Checkpoint 命令に変換される予定
  • 現在は簡易実装のため、型推論が不完全

テストファイル:

  • src/mir/mod.rs:363-384

Phase 84-3 で必要な機能の推奨

推奨1: Edge Copy 追跡 PHI 型推論(優先度: 高)

対象: GroupA7件

アルゴリズム:

// GenericTypeResolver に追加
pub fn resolve_from_phi_with_copy_trace(
    function: &MirFunction,
    ret_val: ValueId,
    types: &BTreeMap<ValueId, MirType>,
) -> Option<MirType> {
    // 1. PHI 命令を探索
    for inst in find_phi_instructions(function, ret_val) {
        if let MirInstruction::Phi { inputs, .. } = inst {
            // 2. incoming 値ごとに Copy を遡る
            let mut inferred_types = Vec::new();
            for (_, incoming_val) in inputs {
                // 2-1. incoming_val の型を直接取得
                if let Some(ty) = types.get(incoming_val) {
                    inferred_types.push(ty.clone());
                    continue;
                }

                // 2-2. incoming_val を定義する Copy 命令を探索
                if let Some(src_val) = find_copy_src(function, *incoming_val) {
                    if let Some(ty) = types.get(&src_val) {
                        inferred_types.push(ty.clone());
                        continue;
                    }
                }

                // 2-3. 多段 Copy を再帰的に遡る
                if let Some(ty) = trace_copy_chain(function, *incoming_val, types, 10) {
                    inferred_types.push(ty);
                }
            }

            // 3. 全ての型が一致すれば返す
            if let Some(first) = inferred_types.first() {
                if inferred_types.iter().all(|t| t == first) {
                    return Some(first.clone());
                }
            }
        }
    }
    None
}

fn trace_copy_chain(
    function: &MirFunction,
    start: ValueId,
    types: &BTreeMap<ValueId, MirType>,
    max_depth: usize,
) -> Option<MirType> {
    let mut current = start;
    for _ in 0..max_depth {
        if let Some(ty) = types.get(&current) {
            return Some(ty.clone());
        }
        if let Some(src) = find_copy_src(function, current) {
            current = src;
        } else {
            break;
        }
    }
    None
}

実装箇所:

  • src/mir/join_ir/lowering/generic_type_resolver.rs

期待効果:

  • GroupA の 7件を一気に解決
  • Loop + continue/break パターンの完全対応

推奨2: 多段 PHI 型推論(優先度: 中)

対象: GroupB2件

問題:

Block1:
  %a = PHI [const_96, const_0]

Block2:
  %b = PHI [%a, const_0]

Block3:
  %7 = PHI [%b]  ← %b の型が未解決

アルゴリズム:

pub fn resolve_from_phi_recursive(
    function: &MirFunction,
    ret_val: ValueId,
    types: &BTreeMap<ValueId, MirType>,
    visited: &mut HashSet<ValueId>,
) -> Option<MirType> {
    if visited.contains(&ret_val) {
        return None; // 循環検出
    }
    visited.insert(ret_val);

    // 1. 直接型推論を試みる
    if let Some(ty) = resolve_from_phi(function, ret_val, types) {
        return Some(ty);
    }

    // 2. PHI の incoming 値を再帰的に解決
    for inst in find_phi_instructions(function, ret_val) {
        if let MirInstruction::Phi { inputs, .. } = inst {
            let mut inferred_types = Vec::new();
            for (_, incoming_val) in inputs {
                // 再帰的に型を解決
                if let Some(ty) = resolve_from_phi_recursive(
                    function, *incoming_val, types, visited
                ) {
                    inferred_types.push(ty);
                }
            }

            if let Some(first) = inferred_types.first() {
                if inferred_types.iter().all(|t| t == first) {
                    return Some(first.clone());
                }
            }
        }
    }
    None
}

実装箇所:

  • src/mir/join_ir/lowering/generic_type_resolver.rs

期待効果:

  • GroupB の 2件を解決
  • 多段メソッド呼び出しの型推論強化

推奨3: await 型推論特殊処理(優先度: 低)

対象: GroupC1件

短期対応:

// lifecycle.rs に特殊ケース追加
if function_name == "main" && is_await_expression {
    // await の戻り値型は Unknown で許容
    return MirType::Unknown;
}

長期対応:

  • Phase 67+ で async/await システム完全実装
  • Safepoint/Checkpoint 命令の型推論統合

実装箇所:

  • src/mir/builder/lifecycle.rs

期待効果:

  • GroupC の 1件を解決暫定

推奨実装順序

Phase 84-3: Edge Copy 追跡 PHI 型推論1-2日

目標: GroupA の 7件を解決

ステップ:

  1. GenericTypeResolver::resolve_from_phi_with_copy_trace() 実装
  2. trace_copy_chain() ヘルパー関数実装
  3. find_copy_src() ヘルパー関数実装
  4. lifecycle.rs から新関数を呼び出す
  5. テスト実行: 7件 → 0件 を確認

Phase 84-4: 多段 PHI 型推論1-2日

目標: GroupB の 2件を解決

ステップ:

  1. GenericTypeResolver::resolve_from_phi_recursive() 実装
  2. 循環検出ロジック実装
  3. lifecycle.rs から新関数を呼び出す
  4. テスト実行: 2件 → 0件 を確認

Phase 84-5: await 暫定対応30分

目標: GroupC の 1件を解決暫定

ステップ:

  1. lifecycle.rs に await 特殊ケース追加
  2. テスト実行: 1件 → 0件 を確認

完了条件

# Phase 84-3 完了
NYASH_PHI_FALLBACK_DISABLED=1 cargo test --release --lib 2>&1 | grep "Case D" | wc -l
# 期待: 2 (GroupB のみ残存)

# Phase 84-4 完了
NYASH_PHI_FALLBACK_DISABLED=1 cargo test --release --lib 2>&1 | grep "Case D" | wc -l
# 期待: 1 (GroupC のみ残存)

# Phase 84-5 完了
NYASH_PHI_FALLBACK_DISABLED=1 cargo test --release --lib 2>&1 | grep "Case D" | wc -l
# 期待: 0 (全件解決)

ChatGPT Pro との設計相談ポイント

相談1: Edge Copy 追跡の最適化

質問:

  • Copy チェーンの追跡深度は 10 で十分か?
  • 循環 Copy 検出は必要か?(理論上は発生しないが)
  • パフォーマンス最適化(キャッシュ戦略)

相談2: 多段 PHI の循環検出

質問:

  • 循環 PHI は実際に発生するか?
  • 発生する場合、どう処理すべきか?(エラー or Unknown
  • visited セットの最適なデータ構造

相談3: await 型推論の長期戦略

質問:

  • Phase 67+ async/await システムの型推論設計
  • Safepoint/Checkpoint 命令の型情報統合方法
  • 現在の暫定対応が将来の実装を妨げないか

まとめ

Phase 84-2 の CopyTypePropagator により 12件 → 9件に削減成功。 残り 9件は以下の 3パターンに分類:

  • GroupA: Loop 制御フロー PHI7件→ Edge Copy 追跡で解決可能
  • GroupB: 多段 PHI2件→ 再帰的型推論で解決可能
  • GroupC: await 特殊1件→ 暫定対応で解決可能

Phase 84-3/4/5 の実装により、Case D を完全解決 できる見込み。