feat(joinir): Phase 40-4.1 delete collect_assigned_vars (35 lines)

Breaking: collect_assigned_vars function removed from if_phi.rs

Changes:
- Delete collect_assigned_vars() function (35 lines)
- Make JoinIR route the default in loop_builder.rs
- Rewrite collect_assigned_vars_via_joinir() with ast_to_json support
  - Now detects both Local declarations and Assignment nodes
  - Add extract_vars_from_json_stmts/stmt helpers
- Update tests to use new implementation
  - phase40_joinir_detects_local_declarations
  - phase40_joinir_nested_if_local

Test results: 407 passed, 11 failed (same as before, no regression)

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-11-28 11:07:01 +09:00
parent c7975d4bd9
commit 59f7f03efb
4 changed files with 143 additions and 185 deletions

View File

@ -16,13 +16,13 @@
*
* **if_phi.rs**: 315行 → 225行28.6%削減達成)
*
* ## 🔜 Phase 40で削除予定Level 2、115行
* ## Phase 40-4.1で削除済みLevel 2-A、35行
*
* - `collect_assigned_vars` (32行)
* - **削除条件**: array_ext.filter実装完了if-in-loop AST lowering
* - **置換**: JoinIR Frontend `extract_if_in_loop_modified_vars()`
* - **callsites**: loop_builder.rs:1069, 1075
* - **Phase**: Phase 40-1
* - `collect_assigned_vars` (35行)**削除完了 2025-11-28**
* - **置換**: `collect_assigned_vars_via_joinir()` (JoinIR Frontend経由)
* - **理由**: A/Bテストで退行なし確認、むしろ2テスト改善
*
* ## 🔜 Phase 40で削除予定Level 2-B、80行
*
* - `compute_modified_names` (26行)
* - **削除条件**: JoinIR Verifier conservative migration完了
@ -90,81 +90,22 @@ pub fn infer_type_from_phi(
// ========================================
// Phase 40削除予定Level 2
// 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文の代入変数収集
/// ループ内if文の代入変数収集JoinIR Frontend経由
///
/// Collect all variable names that are assigned within the given AST subtree.
/// Useful for computing PHI merge candidates across branches/blocks.
///
/// # Phase 40削減計画
///
/// - **削除予定**: Phase 40-1array_ext.filter実装後
/// - **置換先**: JoinIR Frontend `extract_if_in_loop_modified_vars()`
/// - **削除条件**:
/// - ✅ array_ext.filter via JoinIR Frontend実装完了
/// - ✅ A/Bテスト旧PHIパス vs JoinIRパスPASS
/// - ✅ この関数の全callsiteでJoinIR置換確認
/// - **callsites**: 2箇所
/// - loop_builder.rs:1069 (if-in-loop pattern)
/// - loop_builder.rs:1075 (if-in-loop pattern)
/// - **削減効果**: 32行
///
/// # Migration Path
///
/// ```rust,ignore
/// // Before (Phase 38以前)
/// let vars = if_phi::collect_assigned_vars(loop_body, var_map);
///
/// // After (Phase 40)
/// // JoinIR Frontend AST loweringで自動収集
/// // ast_lowerer.extract_if_in_loop_modified_vars()が代替
/// ```
///
/// # See Also
/// - Design: `docs/.../phase-39-if-phi-level2/joinir_extension_design.md`
/// - Replacement: `src/mir/join_ir/frontend/ast_lowerer.rs::extract_if_in_loop_modified_vars()`
pub fn collect_assigned_vars(ast: &ASTNode, out: &mut std::collections::BTreeSet<String>) {
// TODO(Phase 40-1): この関数は削除予定
// JoinIR Frontend if-in-loop AST loweringに置き換え
match ast {
ASTNode::Assignment { target, .. } => {
if let ASTNode::Variable { name, .. } = target.as_ref() {
out.insert(name.clone());
}
}
ASTNode::Program { statements, .. } => {
for s in statements {
collect_assigned_vars(s, out);
}
}
ASTNode::If {
then_body,
else_body,
..
} => {
let tp = ASTNode::Program {
statements: then_body.clone(),
span: crate::ast::Span::unknown(),
};
collect_assigned_vars(&tp, out);
if let Some(eb) = else_body {
let ep = ASTNode::Program {
statements: eb.clone(),
span: crate::ast::Span::unknown(),
};
collect_assigned_vars(&ep, out);
}
}
_ => {}
}
}
/// Phase 40-3.5: JoinIR経由の代入変数収集
///
/// `collect_assigned_vars`のJoinIR代替版。
/// `HAKO_JOINIR_ARRAY_FILTER=1`で有効化。
/// Uses JoinIR infrastructure for analysis.
///
/// # Arguments
/// - `then_body`: thenブランチのAST
@ -178,29 +119,23 @@ pub fn collect_assigned_vars(ast: &ASTNode, out: &mut std::collections::BTreeSet
/// 2. JoinIR Frontend extract_assigned_vars_from_body()呼び出し
/// 3. BTreeSet形式で返却
///
/// # Note
/// この関数はPhase 40-1インフラを使用し、将来的にcollect_assigned_varsを置換する。
#[allow(dead_code)]
/// # 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: &[crate::ast::ASTNode],
else_body: Option<&Vec<crate::ast::ASTNode>>,
) -> std::collections::BTreeSet<String> {
use crate::mir::join_ir::frontend::AstToJoinIrLowerer;
let mut result = std::collections::BTreeSet::new();
// Convert then_body to JSON
// Convert then_body to JSON and extract
let then_prog = crate::ast::ASTNode::Program {
statements: then_body.to_vec(),
span: crate::ast::Span::unknown(),
};
let then_json = crate::r#macro::ast_json::ast_to_json(&then_prog);
// Extract assigned vars via JoinIR
let mut lowerer = AstToJoinIrLowerer::new();
if let Some(body) = then_json.get("statements") {
let assigned = lowerer.extract_assigned_vars_from_body(body);
result.extend(assigned);
if let Some(stmts) = then_json.get("statements") {
extract_vars_from_json_stmts(stmts, &mut result);
}
// Process else_body if present
@ -210,15 +145,14 @@ pub fn collect_assigned_vars_via_joinir(
span: crate::ast::Span::unknown(),
};
let else_json = crate::r#macro::ast_json::ast_to_json(&else_prog);
if let Some(body) = else_json.get("statements") {
let assigned = lowerer.extract_assigned_vars_from_body(body);
result.extend(assigned);
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-3.5] collect_assigned_vars_via_joinir: {:?}",
"[Phase 40-4.1] collect_assigned_vars_via_joinir: {:?}",
result
);
}
@ -226,6 +160,75 @@ pub fn collect_assigned_vars_via_joinir(
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);
}
}
_ => {
// その他は無視
}
}
}
/// 保守的修正名前計算
///
/// Compute the set of variable names whose values changed in either branch