## 実装内容 ### 新規ファイル - `if_phi_context.rs`: If-in-loop用PHIコンテキスト構造体 (135行) - `IfPhiContext::for_loop_body()`: ループ内if用コンストラクタ - `is_carrier()`: ループキャリア変数判定 - 単体テスト2個完全動作 ### 既存ファイル拡張 - `if_select.rs`, `if_merge.rs`: context パラメータ追加 (+68行) - `with_context()` コンストラクタ実装 - Pure If との完全互換性維持 - `mod.rs`: `try_lower_if_to_joinir()` シグネチャ拡張 (+25行) - `context: Option<&IfPhiContext>` パラメータ追加 - 既存呼び出し箇所6箇所修正完了 - `loop_builder.rs`: JoinIR経路実装 (+43行) - `NYASH_JOINIR_IF_SELECT=1` で試行 - フォールバック設計(PhiBuilderBox経路保持) - デバッグログ完備 ## テスト結果 - ✅ loopform テスト 14/14 PASS(退行なし) - ✅ ビルド成功(エラー0件) - ✅ Borrow Checker 問題解決 ## コード変更量 - 新規: +135行 - 拡張: +136行 - 削除: -18行 - 純増: +253行(インフラ投資、Phase 61-3で-226行削減予定) ## 次のステップ Phase 61-2: join_inst dry-run実装で実際のPHI生成を行う 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
245 lines
9.3 KiB
Rust
245 lines
9.3 KiB
Rust
//! JoinIR Lowering Functions
|
||
//!
|
||
//! Phase 27.9: Modular separation of MIR → JoinIR lowering implementations.
|
||
//!
|
||
//! このモジュールは各種 MIR 関数を JoinIR に変換する lowering 関数を提供します。
|
||
//!
|
||
//! ## 構成:
|
||
//! - `common.rs`: CFG sanity checks と lowering 共通ユーティリティ(Phase 27.10)
|
||
//! - `value_id_ranges.rs`: ValueId 範囲管理(Phase 27.13+)
|
||
//! - `min_loop.rs`: JoinIrMin.main/0 専用の最小ループ lowering
|
||
//! - `skip_ws.rs`: Main.skip/1 の空白スキップ lowering(手書き版+MIR自動解析版)
|
||
//! - `funcscanner_trim.rs`: FuncScannerBox.trim/1 の trim lowering
|
||
//! - `stage1_using_resolver.rs`: Stage1UsingResolverBox.resolve_for_source entries loop lowering(Phase 27.12)
|
||
//! - `funcscanner_append_defs.rs`: FuncScannerBox._append_defs/2 の配列結合 lowering(Phase 27.14)
|
||
//! - `if_select.rs`: Phase 33 If/Else → Select lowering
|
||
//! - `if_dry_runner.rs`: Phase 33-10 If lowering dry-run スキャナー(箱化版)
|
||
|
||
pub mod common;
|
||
pub mod exit_args_resolver;
|
||
pub mod funcscanner_append_defs;
|
||
pub mod funcscanner_trim;
|
||
pub mod generic_case_a;
|
||
pub mod if_dry_runner; // Phase 33-10.0
|
||
pub mod if_merge; // Phase 33-7
|
||
pub mod if_phi_context; // Phase 61-1
|
||
pub mod if_select; // Phase 33
|
||
pub mod loop_form_intake;
|
||
pub mod loop_scope_shape;
|
||
pub mod loop_to_join;
|
||
pub mod min_loop;
|
||
pub mod skip_ws;
|
||
pub mod stage1_using_resolver;
|
||
pub mod stageb_body;
|
||
pub mod stageb_funcscanner;
|
||
pub mod value_id_ranges;
|
||
|
||
// Re-export public lowering functions
|
||
pub use funcscanner_append_defs::lower_funcscanner_append_defs_to_joinir;
|
||
pub use funcscanner_trim::lower_funcscanner_trim_to_joinir;
|
||
// Phase 31: LoopToJoinLowerer 統一箱
|
||
pub use loop_to_join::LoopToJoinLowerer;
|
||
// Phase 30 F-3: 旧 lower_case_a_loop_to_joinir_for_minimal_skip_ws は _with_scope に置き換え済みのため削除
|
||
pub use min_loop::lower_min_loop_to_joinir;
|
||
pub use skip_ws::lower_skip_ws_to_joinir;
|
||
pub use stage1_using_resolver::lower_stage1_usingresolver_to_joinir;
|
||
pub use stageb_body::lower_stageb_body_to_joinir;
|
||
pub use stageb_funcscanner::lower_stageb_funcscanner_to_joinir;
|
||
|
||
// Phase 33: If/Else → Select lowering entry point
|
||
use crate::mir::join_ir::JoinInst;
|
||
use crate::mir::{BasicBlockId, MirFunction};
|
||
|
||
/// Phase 33-9.1: Loop lowering対象関数の判定
|
||
///
|
||
/// これらの関数は Phase 32/33 で LoopToJoinLowerer によって処理されます。
|
||
/// If lowering (Select/IfMerge) の対象から除外することで、Loop/If の責務を明確に分離します。
|
||
///
|
||
/// ## 対象関数(6本)
|
||
/// - Main.skip/1: 空白スキップループ
|
||
/// - FuncScannerBox.trim/1: 前後空白削除ループ
|
||
/// - FuncScannerBox.append_defs/2: 配列結合ループ
|
||
/// - Stage1UsingResolverBox.resolve_for_source/5: using解析ループ
|
||
/// - StageBBodyExtractorBox.build_body_src/2: Stage-B本体抽出ループ
|
||
/// - StageBFuncScannerBox.scan_all_boxes/1: Stage-B Box走査ループ
|
||
///
|
||
/// ## 将来の拡張
|
||
/// NYASH_JOINIR_LOWER_GENERIC=1 で汎用 Case-A ループにも拡張可能
|
||
pub(crate) fn is_loop_lowered_function(name: &str) -> bool {
|
||
const LOOP_LOWERED_FUNCTIONS: &[&str] = &[
|
||
"Main.skip/1",
|
||
"FuncScannerBox.trim/1",
|
||
"FuncScannerBox.append_defs/2",
|
||
"Stage1UsingResolverBox.resolve_for_source/5",
|
||
"StageBBodyExtractorBox.build_body_src/2",
|
||
"StageBFuncScannerBox.scan_all_boxes/1",
|
||
];
|
||
|
||
LOOP_LOWERED_FUNCTIONS.contains(&name)
|
||
}
|
||
|
||
/// Phase 33-7: Try to lower if/else to JoinIR Select/IfMerge instruction
|
||
///
|
||
/// Scope:
|
||
/// - Only applies to whitelisted functions:
|
||
/// - IfSelectTest.* (Phase 33-2/33-3)
|
||
/// - IfMergeTest.* (Phase 33-7)
|
||
/// - JsonShapeToMap._read_value_from_pair/1 (Phase 33-4 Stage-1)
|
||
/// - Stage1JsonScannerBox.value_start_after_key_pos/2 (Phase 33-4 Stage-B)
|
||
/// - Requires NYASH_JOINIR_IF_SELECT=1 environment variable
|
||
/// - Falls back to traditional if_phi on pattern mismatch
|
||
///
|
||
/// Pattern selection:
|
||
/// - 1 variable → Select
|
||
/// - 2+ variables → IfMerge
|
||
///
|
||
/// Phase 61-1: If-in-loop support
|
||
/// - `context` parameter: If-in-loop context (carrier_names for loop variables)
|
||
/// - None = Pure If, Some(_) = If-in-loop
|
||
///
|
||
/// Returns Some(JoinInst::Select) or Some(JoinInst::IfMerge) if pattern matched, None otherwise.
|
||
pub fn try_lower_if_to_joinir(
|
||
func: &MirFunction,
|
||
block_id: BasicBlockId,
|
||
debug: bool,
|
||
context: Option<&if_phi_context::IfPhiContext>, // Phase 61-1: If-in-loop context
|
||
) -> Option<JoinInst> {
|
||
// 1. dev トグルチェック
|
||
if !crate::config::env::joinir_if_select_enabled() {
|
||
return None;
|
||
}
|
||
|
||
// Phase 33-9.1: Loop専任関数の除外(Loop/If責務分離)
|
||
// Loop lowering対象関数はIf loweringの対象外にすることで、
|
||
// 1関数につき1 loweringの原則を保証します
|
||
if is_loop_lowered_function(&func.signature.name) {
|
||
return None;
|
||
}
|
||
|
||
// Phase 33-8: デバッグログレベル取得(0-3)
|
||
let debug_level = crate::config::env::joinir_debug_level();
|
||
let _debug = debug || debug_level >= 1;
|
||
|
||
// 2. Phase 33-8: 関数名ガード拡張(テスト + Stage-1 rollout + 明示承認)
|
||
let is_allowed =
|
||
// Test functions (always enabled)
|
||
func.signature.name.starts_with("IfSelectTest.") ||
|
||
func.signature.name.starts_with("IfSelectLocalTest.") || // Phase 33-10 test
|
||
func.signature.name.starts_with("IfMergeTest.") ||
|
||
func.signature.name.starts_with("Stage1JsonScannerTestBox.") || // Phase 33-5 test
|
||
|
||
// Stage-1 rollout (env-controlled)
|
||
(crate::config::env::joinir_stage1_enabled() &&
|
||
func.signature.name.starts_with("Stage1")) ||
|
||
|
||
// Explicit approvals (Phase 33-4で検証済み, always on)
|
||
matches!(func.signature.name.as_str(),
|
||
"JsonShapeToMap._read_value_from_pair/1" |
|
||
"Stage1JsonScannerBox.value_start_after_key_pos/2"
|
||
);
|
||
|
||
if !is_allowed {
|
||
if debug_level >= 2 {
|
||
eprintln!(
|
||
"[try_lower_if_to_joinir] skipping non-allowed function: {}",
|
||
func.signature.name
|
||
);
|
||
}
|
||
return None;
|
||
}
|
||
|
||
if debug_level >= 1 {
|
||
eprintln!(
|
||
"[try_lower_if_to_joinir] trying to lower {}",
|
||
func.signature.name
|
||
);
|
||
}
|
||
|
||
// 3. Phase 33-7: IfMerge を優先的に試行(複数変数パターン)
|
||
// IfMerge が成功すればそれを返す、失敗したら Select を試行
|
||
// Phase 61-1: context がある場合は with_context() を使用
|
||
let if_merge_lowerer = if let Some(ctx) = context {
|
||
if_merge::IfMergeLowerer::with_context(debug_level, ctx.clone())
|
||
} else {
|
||
if_merge::IfMergeLowerer::new(debug_level)
|
||
};
|
||
|
||
if if_merge_lowerer.can_lower_to_if_merge(func, block_id) {
|
||
if let Some(result) = if_merge_lowerer.lower_if_to_if_merge(func, block_id) {
|
||
if debug_level >= 1 {
|
||
eprintln!(
|
||
"[try_lower_if_to_joinir] ✅ IfMerge lowering used for {}",
|
||
func.signature.name
|
||
);
|
||
}
|
||
return Some(result);
|
||
}
|
||
}
|
||
|
||
// 4. IfMerge が失敗したら Select を試行(単一変数パターン)
|
||
// Phase 61-1: context がある場合は with_context() を使用
|
||
let if_select_lowerer = if let Some(ctx) = context {
|
||
if_select::IfSelectLowerer::with_context(debug_level, ctx.clone())
|
||
} else {
|
||
if_select::IfSelectLowerer::new(debug_level)
|
||
};
|
||
|
||
if !if_select_lowerer.can_lower_to_select(func, block_id) {
|
||
if debug_level >= 1 {
|
||
eprintln!(
|
||
"[try_lower_if_to_joinir] pattern not matched for {}",
|
||
func.signature.name
|
||
);
|
||
}
|
||
return None;
|
||
}
|
||
|
||
let result = if_select_lowerer.lower_if_to_select(func, block_id);
|
||
|
||
if result.is_some() && debug_level >= 1 {
|
||
eprintln!(
|
||
"[try_lower_if_to_joinir] ✅ Select lowering used for {}",
|
||
func.signature.name
|
||
);
|
||
}
|
||
|
||
result
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
/// Phase 33-9.1: is_loop_lowered_function() の動作確認
|
||
#[test]
|
||
fn test_is_loop_lowered_function() {
|
||
// Loop 専任関数(6本)は true を返す
|
||
assert!(is_loop_lowered_function("Main.skip/1"));
|
||
assert!(is_loop_lowered_function("FuncScannerBox.trim/1"));
|
||
assert!(is_loop_lowered_function("FuncScannerBox.append_defs/2"));
|
||
assert!(is_loop_lowered_function(
|
||
"Stage1UsingResolverBox.resolve_for_source/5"
|
||
));
|
||
assert!(is_loop_lowered_function(
|
||
"StageBBodyExtractorBox.build_body_src/2"
|
||
));
|
||
assert!(is_loop_lowered_function(
|
||
"StageBFuncScannerBox.scan_all_boxes/1"
|
||
));
|
||
|
||
// If lowering 対象関数は false を返す
|
||
assert!(!is_loop_lowered_function("IfSelectTest.simple_return/0"));
|
||
assert!(!is_loop_lowered_function("IfMergeTest.multiple_true/0"));
|
||
assert!(!is_loop_lowered_function(
|
||
"JsonShapeToMap._read_value_from_pair/1"
|
||
));
|
||
assert!(!is_loop_lowered_function(
|
||
"Stage1JsonScannerBox.value_start_after_key_pos/2"
|
||
));
|
||
|
||
// 一般的な関数も false を返す
|
||
assert!(!is_loop_lowered_function("SomeBox.some_method/3"));
|
||
assert!(!is_loop_lowered_function("Main.main/0"));
|
||
}
|
||
}
|