- RoutingDecision の missing_caps を Vec<CapabilityTag> に変更(型安全化) - error_tags は to_tag() メソッドで自動生成 - 全 callsite を enum variant に修正 - capability_tags モジュール(文字列定数群)を完全削除 - 全テスト PASS(型安全性向上を確認) - フォーマット適用
229 lines
10 KiB
Rust
229 lines
10 KiB
Rust
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)
|
||
}
|
||
}
|