feat(joinir): Phase 84-5 if_phi.rs レガシーフォールバック完全削除
Phase 84-4-B で Case D を 0件に削減完了したことにより、 if_phi.rs のレガシーフォールバックが完全に不要になったため削除。 主な変更: - if_phi.rs 削除(339行) - test_utils.rs 新規作成(テスト専用ユーティリティ分離、127行) - lifecycle.rs: if_phi 呼び出し削除、Phase 84-5 安全ガード追加 - env.rs: phi_fallback_disabled() を常に true に変更 - テスト: A/B テスト → GenericTypeResolver 単独テストに変更 検証結果: - Case D: 0件(完全解消継続) - Tests: 498 passed(Phase 84-4: 497 から +1) Phase 84 プロジェクト完全達成: 15件 → 0件(100%削減) 純削減: 220行 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -1,339 +0,0 @@
|
||||
/*!
|
||||
* phi_core::if_phi – if/else PHI helpers (Phase 2)
|
||||
*
|
||||
* Public thin wrappers that mirror the semantics of existing builder::phi
|
||||
* helpers. Implemented locally to avoid depending on private submodules.
|
||||
* Behavior is identical to the current in-tree logic.
|
||||
*
|
||||
* # Phase 35-39 PHI削減計画
|
||||
*
|
||||
* このファイルは段階的に縮退・削除される。各関数のPhase別削除計画を以下に示す。
|
||||
*
|
||||
* ## ✅ Phase 38で削除済み(Level 1、90行)
|
||||
*
|
||||
* - `merge_modified_with_control` (51行, dead code, 呼び出し0)
|
||||
* - `extract_assigned_var` (39行, JoinIR AST lowering replacement)
|
||||
*
|
||||
* **if_phi.rs**: 315行 → 225行(28.6%削減達成)
|
||||
*
|
||||
* ## ✅ Phase 40-4.1で削除済み(Level 2-A、35行)
|
||||
*
|
||||
* - `collect_assigned_vars` (35行) → **削除完了 2025-11-28**
|
||||
* - **置換**: `collect_assigned_vars_via_joinir()` (JoinIR Frontend経由)
|
||||
* - **理由**: A/Bテストで退行なし確認、むしろ2テスト改善
|
||||
*
|
||||
* ## ✅ Phase 41-1で削除済み(デッドコード、99行)
|
||||
*
|
||||
* - `merge_modified_at_merge_with` (70行) → **削除完了 2025-11-29**
|
||||
* - **理由**: 外部callsiteゼロ、PhiBuilderBox::generate_if_phis()に置き換え済み
|
||||
*
|
||||
* - `merge_with_reset_at_merge_with` (29行) → **削除完了 2025-11-29**
|
||||
* - **理由**: 上記のwrapper、同様にデッドコード
|
||||
*
|
||||
* ## ✅ Phase 47で削除済み(Level 2-B、33行)
|
||||
*
|
||||
* - `compute_modified_names` (33行) → **削除完了 2025-11-29**
|
||||
* - **置換**: conservative.rs::ConservativeMerge::analyze 内にインライン化
|
||||
* - **理由**: callsite 1箇所のみ、if_phi.rs責務縮小
|
||||
*
|
||||
* ## ✅ Phase 57で削除済み(デッドコード、17行)
|
||||
*
|
||||
* - `PhiMergeOps` trait (17行) → **削除完了 2025-11-29**
|
||||
* - **理由**: 完全にデッドコード(trait定義あるがメソッド呼び出しゼロ)
|
||||
* - **置換**: PhiBuilderOps に統一済み
|
||||
*
|
||||
* ## 現在の状態
|
||||
*
|
||||
* - **if_phi.rs**: 288行 → 271行(Phase 57後、17行削減)
|
||||
* - **累計削減**: 365行(Phase 38: 90行, Phase 40-4.1: 35行, Phase 41-1: 99行, Phase 47: 33行, Phase 57: 17行)+ conservative 48行
|
||||
*
|
||||
* ## 参照ドキュメント
|
||||
*
|
||||
* - 全体計画: `docs/private/roadmap2/phases/phi-reduction-series/INDEX.md`
|
||||
* - Phase 37設計: `docs/private/roadmap2/phases/phase-37-if-phi-reduction/`
|
||||
* - Phase 39詳細: `docs/private/roadmap2/phases/phase-39-if-phi-level2/`
|
||||
* - Phase 40ロードマップ: `docs/.../phase-39-if-phi-level2/deletion_sequence_detailed.md`
|
||||
*/
|
||||
|
||||
use crate::ast::{ASTNode, Span};
|
||||
use crate::mir::{MirFunction, MirInstruction, MirType, ValueId};
|
||||
use std::collections::BTreeMap; // Phase 25.1: 決定性確保
|
||||
|
||||
/// Infer return type with optional JoinIR type hint (Phase 63-5)
|
||||
///
|
||||
/// # Phase 63-5: JoinIR 型ヒント優先への縮退
|
||||
///
|
||||
/// ## 責務
|
||||
///
|
||||
/// 1. **優先**: JoinIR から渡された type_hint があればそれを返す(SSOT)
|
||||
/// 2. **フォールバック**: type_hint がない場合は従来の PHI 走査ロジックで推論
|
||||
///
|
||||
/// ## 使用箇所
|
||||
///
|
||||
/// - `lifecycle.rs:281, 296` - 関数の return 型推論(P1 ケースのみ Phase 63-5 で移行開始)
|
||||
///
|
||||
/// ## 削除条件(Phase 63-4 設計)
|
||||
///
|
||||
/// 全関数で type_hint が揃った段階(Phase 64+ 完了時)で削除予定。
|
||||
/// 現時点の達成率: 2/5 (40%) → Phase 63-5 完了後: 4/5 (80%)
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `type_hint`: JoinIR から渡される型ヒント(Select/IfMerge の type_hint フィールド)
|
||||
/// - `function`: MIR 関数
|
||||
/// - `ret_val`: 推論対象の ValueId
|
||||
/// - `types`: 既知の型情報マップ
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// - `Some(MirType)`: 推論成功(type_hint または PHI 走査から)
|
||||
/// - `None`: 推論失敗(両ルートとも型が決まらない)
|
||||
///
|
||||
pub fn infer_type_from_phi_with_hint(
|
||||
type_hint: Option<MirType>,
|
||||
function: &MirFunction,
|
||||
ret_val: ValueId,
|
||||
types: &BTreeMap<ValueId, MirType>,
|
||||
) -> Option<MirType> {
|
||||
// Route B: JoinIR 型ヒント優先(SSOT)
|
||||
if let Some(hint) = type_hint {
|
||||
return Some(hint);
|
||||
}
|
||||
|
||||
// Route A: 従来ロジックへフォールバック
|
||||
infer_type_from_phi(function, ret_val, types)
|
||||
}
|
||||
|
||||
/// Infer return type by scanning for a Phi that defines `ret_val` and
|
||||
/// verifying that all incoming values have the same type in `types`.
|
||||
///
|
||||
/// # Phase 65-5: P3-C フォールバック専用(Phase 66+ まで保持)
|
||||
///
|
||||
/// ## Phase 65 完了後の位置づけ
|
||||
///
|
||||
/// **P1/P2/P3-A/P3-B は JoinIR 型ヒント経路で型決定完了!**
|
||||
///
|
||||
/// - **P1**: If Select パターン(Phase 63-6)
|
||||
/// - **P2**: If Merge パターン(Phase 64-3)
|
||||
/// - **P3-A**: StringBox メソッド(Phase 65-2-A)
|
||||
/// - **P3-B**: Box コンストラクタ(Phase 65-2-B)
|
||||
///
|
||||
/// これらのケースでは lifecycle.rs の `is_type_hint_target()` 判定により、
|
||||
/// `get_phi_type_hint()` → `infer_type_from_phi_with_hint()` 経由で
|
||||
/// type_hint を優先使用する。
|
||||
///
|
||||
/// ## 現在の責務(P3-C フォールバック専用)
|
||||
///
|
||||
/// **P3-C(ジェネリック型推論)専用のフォールバック**として保持。
|
||||
///
|
||||
/// - ArrayBox.get() の要素型推論
|
||||
/// - MapBox.get() の値型推論
|
||||
/// - その他のジェネリック型パラメータ推論
|
||||
///
|
||||
/// これらは Phase 66+ で型変数システムを導入するまで、
|
||||
/// 従来の PHI 解析フォールバックが必要。
|
||||
///
|
||||
/// ## Phase 66+ での削除計画
|
||||
///
|
||||
/// P3-C のジェネリック型推論システムが完成し、全ケースで
|
||||
/// JoinIR 型ヒント経由の型決定が可能になった時点で削除。
|
||||
///
|
||||
pub fn infer_type_from_phi(
|
||||
function: &MirFunction,
|
||||
ret_val: ValueId,
|
||||
types: &BTreeMap<ValueId, MirType>,
|
||||
) -> Option<MirType> {
|
||||
for (_bid, bb) in function.blocks.iter() {
|
||||
for inst in bb.instructions.iter() {
|
||||
if let MirInstruction::Phi { dst, inputs, .. } = inst {
|
||||
// Phase 63-6: type_hint is ignored here, will be used in lifecycle.rs
|
||||
if *dst == ret_val {
|
||||
let mut it = inputs.iter().filter_map(|(_, v)| types.get(v));
|
||||
if let Some(first) = it.next() {
|
||||
if it.all(|mt| mt == first) {
|
||||
return Some(first.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// Phase 40-4.1: collect_assigned_vars削除完了
|
||||
// ========================================
|
||||
//
|
||||
// 削除日: 2025-11-28
|
||||
// 削除行数: 35行(関数本体 + コメント)
|
||||
// 置換先: collect_assigned_vars_via_joinir (JoinIR Frontend経由)
|
||||
//
|
||||
// 旧関数は以下の理由で削除:
|
||||
// - JoinIR経路がRoute Bとして実装完了
|
||||
// - A/Bテストで退行なし確認(むしろ2テスト改善)
|
||||
// - callsite 2箇所(loop_builder.rs)を JoinIR経路に統一
|
||||
|
||||
/// ループ内if文の代入変数収集(JoinIR Frontend経由)
|
||||
///
|
||||
/// Collect all variable names that are assigned within the given AST subtree.
|
||||
/// Uses JoinIR infrastructure for analysis.
|
||||
///
|
||||
/// # Arguments
|
||||
/// - `then_body`: thenブランチのAST
|
||||
/// - `else_body`: elseブランチのAST(オプション)
|
||||
///
|
||||
/// # Returns
|
||||
/// - 代入された変数名のセット
|
||||
///
|
||||
/// # Implementation
|
||||
/// 1. ASTNode → JSON変換
|
||||
/// 2. JoinIR Frontend extract_assigned_vars_from_body()呼び出し
|
||||
/// 3. BTreeSet形式で返却
|
||||
///
|
||||
/// # History
|
||||
/// - Phase 40-3.5: 作成(collect_assigned_varsのJoinIR代替版)
|
||||
/// - Phase 40-4.1: メイン実装に昇格(collect_assigned_vars削除)
|
||||
pub fn collect_assigned_vars_via_joinir(
|
||||
then_body: &[ASTNode],
|
||||
else_body: Option<&Vec<ASTNode>>,
|
||||
) -> std::collections::BTreeSet<String> {
|
||||
let mut result = std::collections::BTreeSet::new();
|
||||
|
||||
// Convert then_body to JSON and extract
|
||||
let then_prog = ASTNode::Program {
|
||||
statements: then_body.to_vec(),
|
||||
span: Span::unknown(),
|
||||
};
|
||||
let then_json = crate::r#macro::ast_json::ast_to_json(&then_prog);
|
||||
if let Some(stmts) = then_json.get("statements") {
|
||||
extract_vars_from_json_stmts(stmts, &mut result);
|
||||
}
|
||||
|
||||
// Process else_body if present
|
||||
if let Some(else_statements) = else_body {
|
||||
let else_prog = ASTNode::Program {
|
||||
statements: else_statements.clone(),
|
||||
span: Span::unknown(),
|
||||
};
|
||||
let else_json = crate::r#macro::ast_json::ast_to_json(&else_prog);
|
||||
if let Some(stmts) = else_json.get("statements") {
|
||||
extract_vars_from_json_stmts(stmts, &mut result);
|
||||
}
|
||||
}
|
||||
|
||||
if crate::config::env::joinir_vm_bridge_debug() {
|
||||
eprintln!(
|
||||
"[Phase 40-4.1] collect_assigned_vars_via_joinir: {:?}",
|
||||
result
|
||||
);
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Phase 40-4.1: JSON AST から代入変数を抽出(ast_to_json形式対応)
|
||||
fn extract_vars_from_json_stmts(
|
||||
stmts: &serde_json::Value,
|
||||
out: &mut std::collections::BTreeSet<String>,
|
||||
) {
|
||||
if let Some(arr) = stmts.as_array() {
|
||||
for stmt in arr {
|
||||
extract_vars_from_json_stmt(stmt, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Phase 40-4.1: 単一JSON文から変数抽出
|
||||
fn extract_vars_from_json_stmt(
|
||||
stmt: &serde_json::Value,
|
||||
out: &mut std::collections::BTreeSet<String>,
|
||||
) {
|
||||
// ast_to_json uses "kind", not "type"
|
||||
match stmt.get("kind").and_then(|k| k.as_str()) {
|
||||
Some("Local") => {
|
||||
// ast_to_json: { "kind": "Local", "variables": ["x", "y"], ... }
|
||||
if let Some(vars) = stmt.get("variables").and_then(|v| v.as_array()) {
|
||||
for var in vars {
|
||||
if let Some(name) = var.as_str() {
|
||||
out.insert(name.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some("Assignment") => {
|
||||
// ast_to_json: { "kind": "Assignment", "target": { "kind": "Variable", "name": "x" }, ... }
|
||||
if let Some(target) = stmt.get("target") {
|
||||
if target.get("kind").and_then(|k| k.as_str()) == Some("Variable") {
|
||||
if let Some(name) = target.get("name").and_then(|n| n.as_str()) {
|
||||
out.insert(name.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some("If") => {
|
||||
// ast_to_json: { "kind": "If", "then": [...], "else": [...] }
|
||||
if let Some(then_stmts) = stmt.get("then") {
|
||||
extract_vars_from_json_stmts(then_stmts, out);
|
||||
}
|
||||
if let Some(else_stmts) = stmt.get("else") {
|
||||
extract_vars_from_json_stmts(else_stmts, out);
|
||||
}
|
||||
}
|
||||
Some("Loop") => {
|
||||
// ast_to_json: { "kind": "Loop", "body": [...] }
|
||||
if let Some(body) = stmt.get("body") {
|
||||
extract_vars_from_json_stmts(body, out);
|
||||
}
|
||||
}
|
||||
Some("Block") => {
|
||||
// ast_to_json: { "kind": "Block", "body": [...] } - 通常は "statements" かも
|
||||
if let Some(body) = stmt.get("body") {
|
||||
extract_vars_from_json_stmts(body, out);
|
||||
}
|
||||
if let Some(stmts) = stmt.get("statements") {
|
||||
extract_vars_from_json_stmts(stmts, out);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// その他は無視
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// Phase 47: compute_modified_names削除完了(2025-11-29)
|
||||
// ========================================
|
||||
//
|
||||
// 削除日: 2025-11-29
|
||||
// 削除行数: 33行(関数本体 + docコメント)
|
||||
// 置換先: conservative.rs::ConservativeMerge::analyze 内にインライン化
|
||||
//
|
||||
// 旧関数は以下の理由で削除:
|
||||
// - callsite 1箇所(conservative.rs:78)のみ
|
||||
// - ロジックをconservative.rs内にインライン化完了
|
||||
// - if_phi.rsの責務縮小(JoinIR移行準備)
|
||||
|
||||
// ========================================
|
||||
// Phase 57: PhiMergeOps trait 削除(2025-11-29)
|
||||
// ========================================
|
||||
// 削除日: 2025-11-29
|
||||
// 削除行数: 17行
|
||||
// 理由: 完全にデッドコード
|
||||
// - trait定義はあったが、どのメソッドも呼ばれていない
|
||||
// - 唯一のimpl(loop_builder.rs)もメソッドが使われていなかった
|
||||
// - PhiBuilderOps に統一され、PhiMergeOps は不要に
|
||||
//
|
||||
// 旧定義:
|
||||
// pub trait PhiMergeOps {
|
||||
// fn new_value(&mut self) -> ValueId;
|
||||
// fn emit_phi_at_block_start(...);
|
||||
// fn update_var(...);
|
||||
// fn debug_verify_phi_inputs(...);
|
||||
// }
|
||||
|
||||
// ========================================
|
||||
// Phase 41-1削除済み(2025-11-29)
|
||||
// ========================================
|
||||
// - merge_modified_at_merge_with (70行) - PhiBuilderBox::generate_if_phis()に置き換え済み
|
||||
// - merge_with_reset_at_merge_with (29行) - 上記のwrapper、同様にデッドコード
|
||||
@ -9,7 +9,7 @@
|
||||
|
||||
pub mod common;
|
||||
pub mod conservative;
|
||||
pub mod if_phi;
|
||||
// Phase 84-5: if_phi 削除(レガシーフォールバック完全削除)
|
||||
// Phase 30 F-2.1: loop_phi 削除(LoopFormBuilder が SSOT)
|
||||
pub mod loop_snapshot_merge;
|
||||
pub mod loopform_builder;
|
||||
@ -35,6 +35,9 @@ pub mod copy_type_propagator;
|
||||
// Phase 84-3: PHI + Copy グラフ型推論箱(ChatGPT Pro設計)
|
||||
pub mod phi_type_resolver;
|
||||
|
||||
// Phase 84-5: テスト専用ユーティリティ(if_phi.rs から移動)
|
||||
pub mod test_utils;
|
||||
|
||||
// Phase 35-5: if_body_local_merge 削除(PhiBuilderBoxに吸収済み)
|
||||
// Phase 35-5: phi_invariants 削除(JoinIR Verifierに移譲済み)
|
||||
|
||||
|
||||
120
src/mir/phi_core/test_utils.rs
Normal file
120
src/mir/phi_core/test_utils.rs
Normal file
@ -0,0 +1,120 @@
|
||||
//! Phase 84-5: Test utilities for PHI-related tests
|
||||
//!
|
||||
//! This module contains utility functions that are only used by test code.
|
||||
//! Moved from if_phi.rs during Phase 84-5 cleanup.
|
||||
|
||||
use crate::ast::{ASTNode, Span};
|
||||
|
||||
/// Phase 40-4.1: JoinIR経由で代入変数を収集
|
||||
///
|
||||
/// AST→JSON変換を経由して、then/else ブロック内で代入される変数名を抽出します。
|
||||
///
|
||||
/// **使用箇所**:
|
||||
/// - `src/tests/phase40_array_ext_filter_test.rs` のテストコード
|
||||
/// - `src/mir/loop_builder/if_lowering.rs` (unused, dead code)
|
||||
pub fn collect_assigned_vars_via_joinir(
|
||||
then_body: &[ASTNode],
|
||||
else_body: Option<&Vec<ASTNode>>,
|
||||
) -> std::collections::BTreeSet<String> {
|
||||
let mut result = std::collections::BTreeSet::new();
|
||||
|
||||
// Convert then_body to JSON and extract
|
||||
let then_prog = ASTNode::Program {
|
||||
statements: then_body.to_vec(),
|
||||
span: Span::unknown(),
|
||||
};
|
||||
let then_json = crate::r#macro::ast_json::ast_to_json(&then_prog);
|
||||
if let Some(stmts) = then_json.get("statements") {
|
||||
extract_vars_from_json_stmts(stmts, &mut result);
|
||||
}
|
||||
|
||||
// Process else_body if present
|
||||
if let Some(else_statements) = else_body {
|
||||
let else_prog = ASTNode::Program {
|
||||
statements: else_statements.clone(),
|
||||
span: Span::unknown(),
|
||||
};
|
||||
let else_json = crate::r#macro::ast_json::ast_to_json(&else_prog);
|
||||
if let Some(stmts) = else_json.get("statements") {
|
||||
extract_vars_from_json_stmts(stmts, &mut result);
|
||||
}
|
||||
}
|
||||
|
||||
if crate::config::env::joinir_vm_bridge_debug() {
|
||||
eprintln!(
|
||||
"[Phase 40-4.1] collect_assigned_vars_via_joinir: {:?}",
|
||||
result
|
||||
);
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Phase 40-4.1: JSON AST から代入変数を抽出(ast_to_json形式対応)
|
||||
fn extract_vars_from_json_stmts(
|
||||
stmts: &serde_json::Value,
|
||||
out: &mut std::collections::BTreeSet<String>,
|
||||
) {
|
||||
if let Some(arr) = stmts.as_array() {
|
||||
for stmt in arr {
|
||||
extract_vars_from_json_stmt(stmt, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Phase 40-4.1: 単一JSON文から変数抽出
|
||||
fn extract_vars_from_json_stmt(
|
||||
stmt: &serde_json::Value,
|
||||
out: &mut std::collections::BTreeSet<String>,
|
||||
) {
|
||||
// ast_to_json uses "kind", not "type"
|
||||
match stmt.get("kind").and_then(|k| k.as_str()) {
|
||||
Some("Local") => {
|
||||
// ast_to_json: { "kind": "Local", "variables": ["x", "y"], ... }
|
||||
if let Some(vars) = stmt.get("variables").and_then(|v| v.as_array()) {
|
||||
for var in vars {
|
||||
if let Some(name) = var.as_str() {
|
||||
out.insert(name.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some("Assignment") => {
|
||||
// ast_to_json: { "kind": "Assignment", "target": { "kind": "Variable", "name": "x" }, ... }
|
||||
if let Some(target) = stmt.get("target") {
|
||||
if target.get("kind").and_then(|k| k.as_str()) == Some("Variable") {
|
||||
if let Some(name) = target.get("name").and_then(|n| n.as_str()) {
|
||||
out.insert(name.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some("If") => {
|
||||
// ast_to_json: { "kind": "If", "then": [...], "else": [...] }
|
||||
if let Some(then_stmts) = stmt.get("then") {
|
||||
extract_vars_from_json_stmts(then_stmts, out);
|
||||
}
|
||||
if let Some(else_stmts) = stmt.get("else") {
|
||||
extract_vars_from_json_stmts(else_stmts, out);
|
||||
}
|
||||
}
|
||||
Some("Loop") => {
|
||||
// ast_to_json: { "kind": "Loop", "body": [...] }
|
||||
if let Some(body) = stmt.get("body") {
|
||||
extract_vars_from_json_stmts(body, out);
|
||||
}
|
||||
}
|
||||
Some("Block") => {
|
||||
// ast_to_json: { "kind": "Block", "body": [...] } - 通常は "statements" かも
|
||||
if let Some(body) = stmt.get("body") {
|
||||
extract_vars_from_json_stmts(body, out);
|
||||
}
|
||||
if let Some(stmts) = stmt.get("statements") {
|
||||
extract_vars_from_json_stmts(stmts, out);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// その他は無視
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user