use super::{LoopBuilder, ValueId}; use crate::ast::ASTNode; use crate::mir::control_form::{is_control_form_trace_on, ControlForm, IfShape}; use crate::mir::utils::{capture_actual_predecessor_and_jump, is_current_block_terminated}; use crate::mir::{BasicBlockId, ConstValue}; use std::collections::{BTreeMap, BTreeSet}; impl<'a> LoopBuilder<'a> { /// Lower an if-statement inside a loop, preserving continue/break semantics and emitting PHIs per assigned variable. pub(super) fn lower_if_in_loop( &mut self, condition: ASTNode, then_body: Vec, else_body: Option>, ) -> Result { // Reserve a deterministic join id for debug region labeling (nested inside loop) let join_id = self.parent_builder.debug_next_join_id(); // Pre-pin heuristic was deprecated; leave operands untouched for clarity. // Evaluate condition and create blocks let cond_val = self.parent_builder.build_expression(condition)?; let then_bb = self.new_block(); let else_bb = self.new_block(); let merge_bb = self.new_block(); let pre_branch_bb = self.current_block()?; self.emit_branch(cond_val, then_bb, else_bb)?; // Capture pre-if variable map (used for phi normalization) let pre_if_var_map = self.get_current_variable_map(); let trace_if = std::env::var("NYASH_IF_TRACE").ok().as_deref() == Some("1"); // (legacy) kept for earlier merge style; now unified helpers compute deltas directly. // then branch self.set_current_block(then_bb)?; // Debug region: join then-branch (inside loop) self.parent_builder .debug_push_region(format!("join#{}", join_id) + "/then"); // Materialize all variables at entry via single-pred Phi (correctness-first) let names_then: Vec = self .parent_builder .variable_map .keys() .filter(|n| !n.starts_with("__pin$")) .cloned() .collect(); for name in names_then { if let Some(&pre_v) = pre_if_var_map.get(&name) { let phi_val = self.new_value(); self.emit_phi_at_block_start(then_bb, phi_val, vec![(pre_branch_bb, pre_v)])?; let name_for_log = name.clone(); self.update_variable(name, phi_val); if trace_if { eprintln!( "[if-trace] then-entry phi var={} pre={:?} -> dst={:?}", name_for_log, pre_v, phi_val ); } } } for s in then_body.iter().cloned() { let _ = self.build_statement(s)?; // フェーズS修正:統一終端検出ユーティリティ使用 if is_current_block_terminated(self.parent_builder)? { break; } } let then_var_map_end = self.get_current_variable_map(); // フェーズS修正:最強モード指摘の「実到達predecessor捕捉」を統一 let _then_pred_to_merge = capture_actual_predecessor_and_jump(self.parent_builder, merge_bb)?; // Pop then-branch debug region self.parent_builder.debug_pop_region(); // else branch self.set_current_block(else_bb)?; // Debug region: join else-branch (inside loop) self.parent_builder .debug_push_region(format!("join#{}", join_id) + "/else"); // Materialize all variables at entry via single-pred Phi (correctness-first) let names2: Vec = self .parent_builder .variable_map .keys() .filter(|n| !n.starts_with("__pin$")) .cloned() .collect(); for name in names2 { if let Some(&pre_v) = pre_if_var_map.get(&name) { let phi_val = self.new_value(); self.emit_phi_at_block_start(else_bb, phi_val, vec![(pre_branch_bb, pre_v)])?; let name_for_log = name.clone(); self.update_variable(name, phi_val); if trace_if { eprintln!( "[if-trace] else-entry phi var={} pre={:?} -> dst={:?}", name_for_log, pre_v, phi_val ); } } } let mut else_var_map_end_opt: Option> = None; if let Some(es) = else_body.clone() { for s in es.into_iter() { let _ = self.build_statement(s)?; // フェーズS修正:統一終端検出ユーティリティ使用 if is_current_block_terminated(self.parent_builder)? { break; } } else_var_map_end_opt = Some(self.get_current_variable_map()); } // フェーズS修正:else branchでも統一実到達predecessor捕捉 let _else_pred_to_merge = capture_actual_predecessor_and_jump(self.parent_builder, merge_bb)?; // Pop else-branch debug region self.parent_builder.debug_pop_region(); // Continue at merge self.set_current_block(merge_bb)?; // Debug region: join merge (inside loop) self.parent_builder .debug_push_region(format!("join#{}", join_id) + "/join"); // Phase 25.1: HashSet → BTreeSet(決定性確保) // Phase 40-4.1: JoinIR経路をデフォルト化(collect_assigned_vars削除) let _vars: BTreeSet = crate::mir::phi_core::if_phi::collect_assigned_vars_via_joinir( &then_body, else_body.as_ref(), ); // Phase 26-E: PhiBuilderOps trait 実装(箱理論統一) struct Ops<'b, 'a>(&'b mut LoopBuilder<'a>); // Phase 26-E: PhiBuilderOps trait 実装(箱理論統一) impl<'b, 'a> crate::mir::phi_core::phi_builder_box::PhiBuilderOps for Ops<'b, 'a> { fn new_value(&mut self) -> ValueId { self.0.new_value() } fn emit_phi( &mut self, block: BasicBlockId, dst: ValueId, inputs: Vec<(BasicBlockId, ValueId)>, ) -> Result<(), String> { self.0.emit_phi_at_block_start(block, dst, inputs) } fn update_var(&mut self, name: String, value: ValueId) { self.0.parent_builder.variable_map.insert(name, value); } fn get_block_predecessors(&self, block: BasicBlockId) -> Vec { if let Some(ref func) = self.0.parent_builder.current_function { func.blocks .get(&block) .map(|bb| bb.predecessors.iter().copied().collect()) .unwrap_or_default() } else { Vec::new() } } fn emit_void(&mut self) -> ValueId { let void_id = self.0.new_value(); let _ = self.0.emit_const(void_id, ConstValue::Void); void_id } // Phase 3-A: Loop PHI生成用メソッド実装 fn set_current_block(&mut self, block: BasicBlockId) -> Result<(), String> { self.0.parent_builder.current_block = Some(block); Ok(()) } fn block_exists(&self, block: BasicBlockId) -> bool { if let Some(ref func) = self.0.parent_builder.current_function { func.blocks.contains_key(&block) } else { false } } } // Phase 25.1h: ControlForm統合版に切り替え let if_shape = IfShape { cond_block: pre_branch_bb, then_block: then_bb, else_block: Some(else_bb), merge_block: merge_bb, }; let form = ControlForm::from_if(if_shape.clone()); // Region/GC 観測レイヤ(Phase 25.1l): // NYASH_REGION_TRACE=1 のときだけ、Stage‑B 周辺 If 構造の // Region 情報(entry/exit/slots)をログに出すよ。 crate::mir::region::observer::observe_control_form(self.parent_builder, &form); // Phase 61-1: If-in-loop JoinIR化(開発フラグ制御) // carrier_namesを作成(両経路で共通) let carrier_names: BTreeSet = pre_if_var_map .keys() .filter(|name| !name.starts_with("__pin$")) // 一時変数除外 .cloned() .collect(); // Phase 62-B: JoinIRIfPhiSelector箱化(-60行の簡潔化) let joinir_result = if crate::config::env::joinir_if_select_enabled() { if let Some(ref func) = self.parent_builder.current_function { let selector = super::JoinIRIfPhiSelector::new(func, pre_branch_bb, carrier_names.clone()); selector.try_lower() } else { super::JoinIRResult { success: false, phi_spec: None, join_inst: None, } } } else { super::JoinIRResult { success: false, phi_spec: None, join_inst: None, } }; let joinir_success = joinir_result.success; let joinir_phi_spec_opt = joinir_result.phi_spec; let mut ops = Ops(self); // Phase 61-3: JoinIR本番経路(IfInLoopPhiEmitter) if joinir_success { if let Some(ref joinir_spec) = joinir_phi_spec_opt { // IfInLoopPhiEmitter を使用してPHI生成 let else_snap_opt = else_var_map_end_opt.as_ref(); let phi_count = super::IfInLoopPhiEmitter::emit_header_phis( joinir_spec, &pre_if_var_map, &then_var_map_end, else_snap_opt, &carrier_names, &mut ops, &if_shape, )?; if crate::config::env::joinir_if_in_loop_dryrun_enabled() { eprintln!( "[Phase 61-3] ✅ IfInLoopPhiEmitter generated {} PHIs", phi_count ); } } } // フォールバック: PhiBuilderBox経路(既存) if !joinir_success { // Phase 26-E: PhiBuilderBox SSOT統合(If PHI生成) // Legacy: merge_modified_with_control() → New: PhiBuilderBox::generate_phis() // Phase 61-6.1: set_if_context削除、直接IfPhiContextを生成 let mut phi_builder = crate::mir::phi_core::phi_builder_box::PhiBuilderBox::new(); phi_builder.if_context = Some(crate::mir::phi_core::phi_builder_box::IfPhiContext { in_loop_body: true, loop_carrier_names: carrier_names.clone(), }); // Phase 35-5: if_body_local_merge.rs削除、ロジックはPhiBuilderBox内に統合 let post_snapshots = if let Some(ref else_map) = else_var_map_end_opt { vec![then_var_map_end.clone(), else_map.clone()] } else { vec![then_var_map_end.clone()] }; phi_builder.generate_phis(&mut ops, &form, &pre_if_var_map, &post_snapshots)?; // Phase 61-6.2: A/B比較削除(JoinIR経路完全動作確認済み) // Phase 61-3でJoinIR経路が完全動作したため、観察コード削除 // SSOT: JoinIR → compute_phi_spec_from_joinir() } // Phase 26-E-4: PHI生成後に variable_map をリセット(ChatGPT/Task先生指示) // 理由: else_var_map_end_opt が正しい snapshot を保持したまま PHI 生成に渡す必要がある // 修正前: PHI生成前にリセット → else ブロック内定義変数が消失 → domination error // 修正後: PHI生成後にリセット → 正しいPHI入力 → SSA保証 self.parent_builder.variable_map = pre_if_var_map.clone(); // ControlForm 観測: 環境フラグ(未設定時は既定ON)のとき IfShape をダンプ if is_control_form_trace_on() { form.debug_dump(); #[cfg(debug_assertions)] if let Some(ref func) = self.parent_builder.current_function { if_shape.debug_validate(func); } } let void_id = self.new_value(); self.emit_const(void_id, ConstValue::Void)?; // Pop merge debug region self.parent_builder.debug_pop_region(); Ok(void_id) } }