Files
hakorune/src/mir/builder/phi.rs
nyash-codex e404746612 refactor(mir): Phase 139-P3-B - RoutingDecision を enum 対応 + レガシー削除
- RoutingDecision の missing_caps を Vec<CapabilityTag> に変更(型安全化)
- error_tags は to_tag() メソッドで自動生成
- 全 callsite を enum variant に修正
- capability_tags モジュール(文字列定数群)を完全削除
- 全テスト PASS(型安全性向上を確認)
- フォーマット適用
2025-12-16 07:02:14 +09:00

229 lines
10 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use super::MirBuilder;
use crate::ast::ASTNode;
use crate::mir::{BasicBlockId, MirInstruction, ValueId};
use std::collections::BTreeMap; // Phase 25.1: 決定性確保
// Phase 84-5: if_phi.rs deleted, type inference now handled by GenericTypeResolver/PhiTypeResolver
impl MirBuilder {
/// Merge all variables modified in then/else relative to pre_if_snapshot.
/// In PHI-off mode inserts edge copies from branch exits to merge. In PHI-on mode emits Phi.
/// `skip_var` allows skipping a variable already merged elsewhere (e.g., bound to an expression result).
pub(super) fn merge_modified_vars(
&mut self,
_then_block: super::BasicBlockId,
_else_block: super::BasicBlockId,
then_exit_block_opt: Option<super::BasicBlockId>,
else_exit_block_opt: Option<super::BasicBlockId>,
pre_if_snapshot: &BTreeMap<String, super::ValueId>, // Phase 25.1: BTreeMap化
then_map_end: &BTreeMap<String, super::ValueId>, // Phase 25.1: BTreeMap化
else_map_end_opt: &Option<BTreeMap<String, super::ValueId>>, // Phase 25.1: BTreeMap化
skip_var: Option<&str>,
) -> Result<(), String> {
// 📦 Phase 25.1q: Use PhiMergeHelper for unified PHI insertion
// 📦 Phase 57: ConservativeMerge 冗長呼び出し削除
// - 以前: ここで ConservativeMerge::analyze を呼び、merge_all_vars 内でも呼んでいた2回
// - 現在: merge_all_vars が changed_vars を返すので、1回で済む
use std::collections::HashSet;
// Use PhiMergeHelper for unified variable merging
let mut helper =
super::phi_merge::PhiMergeHelper::new(self, then_exit_block_opt, else_exit_block_opt);
let changed_set: HashSet<String> =
helper.merge_all_vars(pre_if_snapshot, then_map_end, else_map_end_opt, skip_var)?;
// Ensure pinned synthetic slots ("__pin$...") have a block-local definition at the merge,
// even if their values did not change across branches. This avoids undefined uses when
// subsequent blocks re-use pinned values without modifications.
for (pin_name, pre_val) in pre_if_snapshot.iter() {
if !pin_name.starts_with("__pin$") {
continue;
}
if skip_var.map(|s| s == pin_name.as_str()).unwrap_or(false) {
continue;
}
if changed_set.contains(pin_name) {
continue;
}
let then_v = then_map_end
.get(pin_name.as_str())
.copied()
.unwrap_or(*pre_val);
let else_v = else_map_end_opt
.as_ref()
.and_then(|m| m.get(pin_name.as_str()).copied())
.unwrap_or(*pre_val);
let mut inputs: Vec<(super::BasicBlockId, super::ValueId)> = Vec::new();
if let Some(tp) = then_exit_block_opt {
inputs.push((tp, then_v));
}
if let Some(ep) = else_exit_block_opt {
inputs.push((ep, else_v));
}
match inputs.len() {
0 => {}
1 => {
let (_pred, v) = inputs[0];
self.variable_ctx.variable_map.insert(pin_name.clone(), v);
}
_ => {
if let Some(func) = self.scope_ctx.current_function.as_mut() {
func.update_cfg();
}
if let (Some(func), Some(cur_bb)) =
(&self.scope_ctx.current_function, self.current_block)
{
crate::mir::phi_core::common::debug_verify_phi_inputs(
func, cur_bb, &inputs,
);
}
let merged = self.next_value_id();
if let (Some(func), Some(cur_bb)) =
(self.scope_ctx.current_function.as_mut(), self.current_block)
{
crate::mir::ssot::cf_common::insert_phi_at_head_spanned(
func,
cur_bb,
merged,
inputs,
self.metadata_ctx.current_span(),
);
} else {
self.emit_instruction(MirInstruction::Phi {
dst: merged,
inputs,
type_hint: None, // Phase 63-6: Legacy path, no type hint
})?;
}
self.variable_ctx
.variable_map
.insert(pin_name.clone(), merged);
}
}
}
Ok(())
}
/// Normalize Phi creation for if/else constructs.
/// This handles variable reassignment patterns and ensures a single exit value.
pub(super) fn normalize_if_else_phi(
&mut self,
_then_block: BasicBlockId,
_else_block: BasicBlockId,
then_exit_block_opt: Option<BasicBlockId>,
else_exit_block_opt: Option<BasicBlockId>,
then_value_raw: ValueId,
else_value_raw: ValueId,
pre_if_var_map: &BTreeMap<String, ValueId>, // Phase 25.1: BTreeMap化
_then_ast_for_analysis: &ASTNode,
_else_ast_for_analysis: &Option<ASTNode>,
then_var_map_end: &BTreeMap<String, ValueId>, // Phase 25.1: BTreeMap化
else_var_map_end_opt: &Option<BTreeMap<String, ValueId>>, // Phase 25.1: BTreeMap化
pre_then_var_value: Option<ValueId>,
) -> Result<ValueId, String> {
// If only the then-branch assigns a variable (e.g., `if c { x = ... }`) and the else
// does not assign the same variable, bind that variable to a Phi of (then_value, pre_if_value).
// Phase 38: Pre-analysis removed (JoinIR AST lowering handles assignment detection)
let assigned_var_then: Option<String> = None;
let assigned_var_else: Option<String> = None;
let result_val = self.next_value_id();
// フェーズM: no_phi_mode分岐削除常にPHI命令を使用
if let Some(var_name) = assigned_var_then.clone() {
let else_assigns_same = assigned_var_else
.as_ref()
.map(|s| s == &var_name)
.unwrap_or(false);
// Resolve branch-end values for the assigned variable
let then_value_for_var = then_var_map_end
.get(&var_name)
.copied()
.unwrap_or(then_value_raw);
// Check if else branch actually modified the variable (even if not as last expression)
let else_modified_var = else_var_map_end_opt
.as_ref()
.and_then(|m| m.get(&var_name).copied());
let else_value_for_var = if else_assigns_same {
else_var_map_end_opt
.as_ref()
.and_then(|m| m.get(&var_name).copied())
.unwrap_or(else_value_raw)
} else if let Some(else_modified) = else_modified_var {
// Else modifies the variable (even if not as the last expression)
else_modified
} else {
// Else doesn't modify the variable: use pre-if value if available
pre_then_var_value.unwrap_or(else_value_raw)
};
// Build inputs from reachable predecessors only
let mut inputs: Vec<(BasicBlockId, ValueId)> = Vec::new();
if let Some(tp) = then_exit_block_opt {
inputs.push((tp, then_value_for_var));
}
if let Some(ep) = else_exit_block_opt {
inputs.push((ep, else_value_for_var));
}
match inputs.len() {
0 => {}
1 => {
// Direct bind (no PHI needed)
self.variable_ctx.variable_map = pre_if_var_map.clone();
self.variable_ctx.variable_map.insert(var_name, inputs[0].1);
return Ok(inputs[0].1);
}
_ => {
if let Some(func) = self.scope_ctx.current_function.as_mut() {
func.update_cfg();
}
if let (Some(func), Some(cur_bb)) =
(&self.scope_ctx.current_function, self.current_block)
{
crate::mir::phi_core::common::debug_verify_phi_inputs(
func, cur_bb, &inputs,
);
}
self.insert_phi_with_dst(result_val, inputs)?;
}
}
self.variable_ctx.variable_map = pre_if_var_map.clone();
self.variable_ctx.variable_map.insert(var_name, result_val);
} else {
// No variable assignment pattern detected just emit Phi for expression result
let mut inputs: Vec<(BasicBlockId, ValueId)> = Vec::new();
if let Some(tp) = then_exit_block_opt {
inputs.push((tp, then_value_raw));
}
if let Some(ep) = else_exit_block_opt {
inputs.push((ep, else_value_raw));
}
match inputs.len() {
0 => {
/* leave result_val as fresh, but unused; synthesize void */
let v = crate::mir::builder::emission::constant::emit_void(self);
return Ok(v);
}
1 => {
return Ok(inputs[0].1);
}
_ => {
if let Some(func) = self.scope_ctx.current_function.as_mut() {
func.update_cfg();
}
if let (Some(func), Some(cur_bb)) =
(&self.scope_ctx.current_function, self.current_block)
{
crate::mir::phi_core::common::debug_verify_phi_inputs(
func, cur_bb, &inputs,
);
}
self.insert_phi_with_dst(result_val, inputs)?;
}
}
// Merge variable map conservatively to pre-if snapshot (no new bindings)
self.variable_ctx.variable_map = pre_if_var_map.clone();
}
Ok(result_val)
}
}