/*! * phi_core::common – shared types and invariants (scaffold) * * Phase 1 keeps this minimal; future phases may move debug asserts and * predicate set checks here for both if/loop PHI normalization. */ /// Placeholder for future shared PHI input type alias. /// Using the same tuple form as MIR Phi instruction inputs. pub type PhiInput = (crate::mir::BasicBlockId, crate::mir::ValueId); pub fn debug_verify_phi_inputs( function: &crate::mir::MirFunction, merge_bb: crate::mir::BasicBlockId, inputs: &[(crate::mir::BasicBlockId, crate::mir::ValueId)], ) { use std::collections::HashSet; // Always compute when env toggle is set; otherwise no-op in release use. let verify_on = std::env::var("HAKO_PHI_VERIFY") .ok() .map(|v| v.to_ascii_lowercase()) .map(|v| v == "1" || v == "true" || v == "on") .unwrap_or(false) || std::env::var("NYASH_PHI_VERIFY") .ok() .map(|v| v.to_ascii_lowercase()) .map(|v| v == "1" || v == "true" || v == "on") .unwrap_or(false); if !verify_on { return; } // Rebuild CFG to avoid stale predecessor sets let mut func = function.clone(); func.update_cfg(); // Duplicate check let mut seen = HashSet::new(); for (pred, _v) in inputs.iter() { if *pred == merge_bb { eprintln!("[phi/check][bad-self] merge={:?} pred={:?}", merge_bb, pred); } if !seen.insert(*pred) { eprintln!("[phi/check][dup] merge={:?} pred={:?}", merge_bb, pred); } } // Missing predecessor inputs check if let Some(block) = func.blocks.get(&merge_bb) { for pred in &block.predecessors { let has = inputs.iter().any(|(bb, _)| bb == pred); if !has { eprintln!("[phi/check][missing] merge={:?} pred={:?}", merge_bb, pred); } } } }