Files
hakorune/docs/development/current/main/phase84-3-remaining-4-analysis.md
nyash-codex c5abf62350 docs(phase84): Add Phase 84-3 analysis and Phase 84-4 recommendations
Task agent investigation results after Phase 84-3 completion.

Remaining 4 Case D analysis:
- test_lowering_await_expression: await construct
- mir_lowering_of_qmark_propagate: QMark (?) construct
- mir_stage1_cli_emit_program_min_*: Stage1Cli type inference (2 tests)

Root cause (unified): BoxCall/Await/QMark return types not registered in value_types

Phase 84-4 implementation recommendations:
- Phase 84-4-A: dev fallback (0.5 days) - immediate unblock
- Phase 84-4-B: BoxCall type registration (1-2 days) - solves 3 cases
- Phase 84-4-C: Await type special handling (0.5 days) - solves 1 case

Documents added:
- phase84-3-summary.md: Reduction results and Phase 84-4 recommendations
- phase84-3-remaining-4-analysis.md: Detailed analysis of each test
- phase84-4-implementation-recommendation.md: Implementation guide with code examples
- phase84-index.md: Phase 84 overall index and roadmap
- phase84-3-final-report.md: Complete report with executive summary

Cumulative results:
- Phase 82: 12 cases
- Phase 84-2: 9 cases (25% reduction)
- Phase 84-3: 4 cases (56% reduction)
- Total: 67% reduction achieved (12 → 4)

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-02 20:18:13 +09:00

17 KiB
Raw Blame History

Phase 84-3: 残り 4件 Case D の完全調査

概要

Phase 84-3 で PhiTypeResolver を実装した結果、Case D は 9件 → 4件に削減されました(56%削減達成!)。

本ドキュメントは残り 4件の詳細分析と、Phase 84-4 での完全削除ロードマップを提示します。

削減実績サマリー

Phase 実装内容 Case D 件数 削減率
Phase 84-1 (Initial) フォールバック検出実装 12件 -
Phase 84-2 CopyTypePropagator 実装 9件 25%
Phase 84-3 PhiTypeResolver 実装 4件 56%

残り 4件の一覧

# テスト名 ValueId 変更
1 test_lowering_await_expression ValueId(2) 継続
2 mir_lowering_of_qmark_propagate ValueId(7) 新規
3 mir_stage1_cli_emit_program_min_compiles_and_verifies ValueId(7) 継続
4 mir_stage1_cli_emit_program_min_exec_hits_type_error ValueId(7) 継続

Phase 84-3 で解決された 5件GroupA ループ系)

PhiTypeResolver により以下が解決されました:

  • loop_with_continue_and_break_edge_copy_merge - ValueId(56)
  • nested_loop_with_multi_continue_break_edge_copy_merge - ValueId(135)
  • loop_inner_if_multilevel_edge_copy - ValueId(74)
  • loop_break_and_early_return_edge_copy - ValueId(40)
  • vm_exec_break_inside_if - ValueId(27)

削減原因: Copy → PHI → Copy チェーンを DFS で遡り、base 型定義を発見する能力

残り 4件の詳細分析

1. test_lowering_await_expression (GroupC: await 特殊構文)

テストファイル: src/mir/mod.rs:363-384

コード:

let ast = ASTNode::AwaitExpression {
    expression: Box::new(ASTNode::Literal {
        value: LiteralValue::Integer(1),
        span: crate::ast::Span::unknown(),
    }),
    span: crate::ast::Span::unknown(),
};

Lowering 実装: src/mir/builder/stmts.rs:388-401

pub(super) fn build_await_expression(
    &mut self,
    expression: ASTNode,
) -> Result<ValueId, String> {
    let future_value = self.build_expression(expression)?;
    self.emit_instruction(MirInstruction::Safepoint)?;
    let result_id = self.next_value_id();
    self.emit_instruction(MirInstruction::Await {
        dst: result_id,
        future: future_value,
    })?;
    self.emit_instruction(MirInstruction::Safepoint)?;
    Ok(result_id)
}

問題の本質:

  • Await { dst: ValueId(2), future: ValueId(1) } 命令の戻り値型が未登録
  • ValueId(1)Integer(1) の型 (IntegerBox) が登録済み
  • しかし、Await 命令の戻り値型は Future の解決値の型 であり、 現在の MIR では型情報が失われている

なぜ PhiTypeResolver で解決できないか:

  • ValueId(2)Await 命令の dstbase 定義)
  • value_types に型が登録されていない
  • PHI/Copy チェーンではないため、探索しても型が見つからない

解決策:

  1. 短期: Await 命令の戻り値を Unknown として許容
  2. 中期: Await 命令 lowering 時に future の型から戻り値型を推論
  3. 長期: Phase 67+ async/await 型システム実装

2. mir_lowering_of_qmark_propagate (GroupD: QMark 特殊構文) 新規失敗

テストファイル: src/tests/mir_qmark_lower.rs:5-33

コード:

let ast = ASTNode::QMarkPropagate {
    expression: Box::new(ASTNode::New {
        class: "StringBox".to_string(),
        arguments: vec![ASTNode::Literal {
            value: crate::ast::LiteralValue::String("ok".to_string()),
            span: Span::unknown(),
        }],
        type_arguments: vec![],
        span: Span::unknown(),
    }),
    span: Span::unknown(),
};

Lowering 実装: src/mir/builder/exprs_qmark.rs:6-42

pub(super) fn build_qmark_propagate_expression(
    &mut self,
    expression: ASTNode,
) -> Result<ValueId, String> {
    let res_val = self.build_expression_impl(expression)?;
    let res_local = self.local_ssa_ensure(res_val, 0);
    let ok_id = self.next_value_id();
    self.emit_instruction(super::MirInstruction::BoxCall {
        dst: Some(ok_id),
        box_val: res_local,
        method: "isOk".to_string(),
        args: vec![],
        method_id: None,
        effects: super::EffectMask::PURE,
    })?;
    let then_block = self.block_gen.next();
    let else_block = self.block_gen.next();
    let ok_local = self.local_ssa_ensure(ok_id, 4);
    crate::mir::builder::emission::branch::emit_conditional(
        self, ok_local, then_block, else_block,
    )?;
    self.start_new_block(then_block)?;
    self.emit_instruction(super::MirInstruction::Return {
        value: Some(res_local),
    })?;
    self.start_new_block(else_block)?;
    let val_id = self.next_value_id();
    self.emit_instruction(super::MirInstruction::BoxCall {
        dst: Some(val_id),
        box_val: res_local,
        method: "getValue".to_string(),
        args: vec![],
        method_id: None,
        effects: super::EffectMask::PURE,
    })?;
    Ok(val_id)
}

制御フロー構造:

Block1:
  %res = new StringBox("ok")
  %ok = BoxCall(%res, "isOk")
  br %ok, then_block, else_block

Block2 (then_block):
  ret %res

Block3 (else_block):
  %val = BoxCall(%res, "getValue")  ← ValueId(7) の型が未登録
  // 暗黙の return %val

問題の本質:

  • BoxCall { dst: ValueId(7), method: "getValue" } の戻り値型が未登録
  • getValue() の戻り値型は Result の T 型 だが、型情報が失われている
  • main 関数の return 型を推論する際に ValueId(7) の型が必要

なぜ PhiTypeResolver で解決できないか:

  • ValueId(7)BoxCall 命令の dstbase 定義)
  • value_types に型が登録されていない
  • PHI/Copy チェーンではないため、探索しても型が見つからない

Phase 84-2 で失敗していなかった理由:

  • 以前の調査文書には記載なし → PhiTypeResolver 実装の副作用で新たに顕在化
  • 可能性1: 以前は別の型推論経路で偶然解決していた
  • 可能性2: テスト自体が無効化されていた(要調査)

解決策:

  1. 短期: BoxCall の戻り値型を Unknown として許容dev 環境のみ)
  2. 中期: BoxCall 命令 lowering 時にメソッド型情報から戻り値型を登録
  3. 長期: Phase 26-A ValueKind 型安全化で BoxCall 戻り値型を完全追跡

3-4. mir_stage1_cli_emit_program_min_* (GroupB: Stage1Cli 複雑型推論)

テストファイル: src/tests/mir_stage1_cli_emit_program_min.rs:71-138

コード: 138行の複雑な static box 定義

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 が複数の経路から合流するが、BoxCall の戻り値型が未登録

なぜ PhiTypeResolver で解決できないか:

  • ValueId(7)BoxCall { method: "stage1_main" } の dstbase 定義)
  • value_types に型が登録されていない
  • PHI/Copy チェーンではないため、探索しても型が見つからない

デバッグ情報:

[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 合流が複雑であることを示唆。

解決策:

  1. 短期: BoxCall の戻り値型を Unknown として許容dev 環境のみ)
  2. 中期: BoxCall 命令 lowering 時にメソッド型情報から戻り値型を登録
  3. 長期: Phase 26-A ValueKind 型安全化で BoxCall 戻り値型を完全追跡

パターン分類の再整理

Phase 84-3 の結果を踏まえ、パターン分類を更新:

GroupA: Loop 制御フロー PHI5件 → 0件 完全解決

PhiTypeResolver で解決: Copy → PHI → Copy チェーンを DFS で遡る能力

GroupB: Stage1Cli 複雑型推論2件 → 2件 ⚠️ 継続

根本原因: BoxCall 戻り値型の未登録base 定義の型情報欠落)

GroupC: await 特殊構文1件 → 1件 ⚠️ 継続

根本原因: Await 戻り値型の未登録base 定義の型情報欠落)

GroupD: QMark 特殊構文0件 → 1件 ⚠️ 新規出現

根本原因: BoxCall 戻り値型の未登録getValue メソッドの型情報欠落)

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

推奨1: BoxCall 戻り値型の実行時登録(優先度: 最高)

対象: GroupB2件、GroupD1件

問題の統一化:

  • 全て「BoxCall の dst が value_types に未登録」という同一問題
  • PhiTypeResolver では base 定義の型を発見できない

解決策2段階:

Phase 84-4-A: 暫定フォールバックdev 環境専用)

実装箇所: src/mir/builder/lifecycle.rs

// lifecycle.rs の infer_type_from_phi() 内
if should_enable_dev_fallback() {
    // dev 環境専用: BoxCall/Await の戻り値型を Unknown として許容
    if is_boxcall_or_await_result(function, ret_val) {
        eprintln!(
            "[phase84/dev_fallback] BoxCall/Await result {} → Unknown (dev only)",
            ret_val
        );
        return Ok(MirType::Unknown);
    }
}

環境変数制御:

fn should_enable_dev_fallback() -> bool {
    std::env::var("NYASH_PHI_DEV_FALLBACK").ok().as_deref() == Some("1")
}

期待効果:

  • 3件 → 1件await のみ残存)
  • dev 環境でのビルド通過
  • production 環境では依然として厳格なエラー

Phase 84-4-B: BoxCall 型情報の実行時登録(根本解決)

実装箇所: src/mir/builder/builder_calls.rs

// emit_box_call() 内に追加
pub fn emit_box_call(
    &mut self,
    box_val: ValueId,
    method: &str,
    args: Vec<ValueId>,
) -> Result<ValueId, String> {
    let dst = self.next_value_id();

    // 既存の BoxCall 命令生成
    self.emit_instruction(MirInstruction::BoxCall {
        dst: Some(dst),
        box_val,
        method: method.to_string(),
        args,
        method_id: None,
        effects: EffectMask::UNKNOWN,
    })?;

    // **新機能**: メソッド型情報から戻り値型を推論して登録
    if let Some(method_ty) = self.infer_boxcall_return_type(box_val, method, &args) {
        self.value_types.insert(dst, method_ty);
    }

    Ok(dst)
}

fn infer_boxcall_return_type(
    &self,
    box_val: ValueId,
    method: &str,
    args: &[ValueId],
) -> Option<MirType> {
    // 1. box_val の型を取得
    let box_ty = self.value_types.get(&box_val)?;

    // 2. method の型情報を slot_registry から取得
    if let Some(slot_id) = self.current_slot_registry
        .as_ref()
        .and_then(|reg| reg.resolve_method(box_ty, method))
    {
        // 3. slot_id からメソッドシグネチャを取得
        // Phase 26-A で実装予定)
        return Some(MirType::Unknown); // 暫定
    }

    None
}

期待効果:

  • 3件 → 1件await のみ残存)
  • production 環境でも安全に動作
  • 型情報の追跡可能性向上

推奨2: Await 型情報の特殊処理(優先度: 中)

対象: GroupC1件

短期対応: Phase 84-4-A の dev フォールバックで対応Unknown として許容)

長期対応: Phase 67+ async/await 型システム実装

// build_await_expression() 内に追加
pub(super) fn build_await_expression(
    &mut self,
    expression: ASTNode,
) -> Result<ValueId, String> {
    let future_value = self.build_expression(expression)?;
    self.emit_instruction(MirInstruction::Safepoint)?;

    let result_id = self.next_value_id();

    // **新機能**: Future の型から戻り値型を推論
    if let Some(future_ty) = self.value_types.get(&future_value) {
        if let MirType::Box { name } = future_ty {
            if name.contains("Future") {
                // Future<T> の T を抽出Phase 67+ で実装)
                let resolved_ty = extract_future_inner_type(name);
                self.value_types.insert(result_id, resolved_ty);
            }
        }
    }

    self.emit_instruction(MirInstruction::Await {
        dst: result_id,
        future: future_value,
    })?;
    self.emit_instruction(MirInstruction::Safepoint)?;
    Ok(result_id)
}

Phase 84-4 実装ロードマップ

Phase 84-4-A: dev フォールバック実装0.5日)

目標: 開発環境でのビルド通過

ステップ:

  1. lifecycle.rsis_boxcall_or_await_result() 実装
  2. should_enable_dev_fallback() 環境変数チェック実装
  3. dev フォールバック警告ログ追加
  4. テスト実行: NYASH_PHI_DEV_FALLBACK=1 で 4件 → 0件 確認

成果:

  • dev 環境での即座のアンブロック
  • production 環境は依然として厳格

Phase 84-4-B: BoxCall 型情報登録1-2日

目標: BoxCall 戻り値型の根本解決

ステップ:

  1. builder_calls.rsinfer_boxcall_return_type() 実装
  2. emit_box_call() 内で戻り値型を value_types に登録
  3. slot_registry とのインテグレーション
  4. テスト実行: 3件 → 1件await のみ残存)確認

成果:

  • GroupB2件完全解決
  • GroupD1件完全解決
  • production 環境でも安全

Phase 84-4-C: Await 型情報特殊処理0.5日)

目標: Await 戻り値型の暫定対応

ステップ:

  1. build_await_expression() に Future 型チェック追加
  2. Unknown 型での暫定登録
  3. テスト実行: 1件 → 0件 確認

成果:

  • GroupC1件暫定解決
  • Phase 67+ 実装までの橋渡し

完了条件

# Phase 84-4-A 完了
NYASH_PHI_DEV_FALLBACK=1 NYASH_PHI_FALLBACK_DISABLED=1 cargo test --release --lib 2>&1 | grep "Case D" | wc -l
# 期待: 0dev フォールバックで全件通過)

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

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

if_phi.rs レガシー削除ロードマップ

Phase 84-5: レガシーフォールバック完全削除1日

前提条件: Phase 84-4-C 完了Case D = 0件

ステップ:

  1. src/mir/join_ir/lowering/if_phi.rs 完全削除
  2. GenericTypeResolver の if_phi 呼び出し削除
  3. lifecycle.rs の Case D 処理を全て削除
  4. 全テスト実行: Case D panic が 0件であることを確認
  5. ドキュメント更新: Phase 82-84 完了宣言

期待成果:

  • if_phi.rs約 300行完全削除
  • 型推論システムの完全箱化達成
  • レガシーフォールバック根絶

削除による技術的利点

  1. 箱理論の完全実現:

    • PhiTypeResolver: PHI + Copy グラフ専用箱
    • CopyTypePropagator: Copy 型伝播専用箱
    • GenericTypeResolver: 統合調整箱
    • if_phi.rs: 削除(レガシー汚染源の根絶)
  2. 保守性向上:

    • 型推論ロジックが 3箱に明確分離
    • 各箱の責務が単一明確
    • 新規型推論パターン追加が容易
  3. パフォーマンス改善:

    • if_phi.rs の非効率な全探索削除
    • PhiTypeResolver の DFS による効率的探索
    • value_types キャッシュの最適化

まとめ

Phase 84-3 の成果:

  • PhiTypeResolver 実装により 9件 → 4件56%削減)
  • GroupALoop 制御フロー5件を完全解決

残り 4件の本質:

  • 全て「base 定義BoxCall/Awaitの型情報欠落」という同一問題
  • PhiTypeResolver では解決不可能(設計上正しい制約)

Phase 84-4 の戦略:

  1. Phase 84-4-A: dev フォールバック実装0.5日)
  2. Phase 84-4-B: BoxCall 型情報登録1-2日
  3. Phase 84-4-C: Await 型情報特殊処理0.5日)

Phase 84-5 の目標:

  • if_phi.rs レガシーフォールバック完全削除
  • 型推論システムの完全箱化達成
  • Phase 82-84 完全達成宣言

総削減見込み:

  • 12件初期→ 0件Phase 84-5 完了時)
  • 100%削減達成!