Files
hakorune/src/mir/builder/control_flow/plan/verifier.rs
tomoaki 1b7fd7a0ff refactor(plan): Phase 273 P3+ - Legacy code removal
Phase 273 P3+ 完成: レガシーコード削除 + 未使用 import 整理

Removed Legacy Items:
1. emit_scan_with_init_edgecfg() - Pattern6 固有の emission 関数
   - File deleted: src/mir/builder/emission/loop_scan_with_init.rs (~144 lines)
   - Replaced by: generalized Frag API (Phase 273 P2)

2. CoreCarrierInfo struct - Legacy carrier representation
   - Removed from: src/mir/builder/control_flow/plan/mod.rs (~15 lines)
   - Replaced by: CorePhiInfo (generalized PHI representation)

3. verify_carrier() function - CoreCarrierInfo validator
   - Removed from: src/mir/builder/control_flow/plan/verifier.rs (~15 lines)
   - Replaced by: generalized PHI verification (V7-V9)

Code Cleanup:
- cargo fix applied: unused imports removed (~30 files)
- Verifier invariants updated: V1→V2-V9 (carrier→PHI model)
- Module declaration cleanup in emission/mod.rs

Impact:
- Total lines removed: ~174 lines (net reduction)
- Pattern-agnostic architecture strengthened
- All legacy Pattern6 references eliminated

Tests:
-  VM tests PASS (phase254/256/258)
-  LLVM tests PASS (phase256/258)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-23 00:11:20 +09:00

278 lines
10 KiB
Rust

//! Phase 273 P3: PlanVerifier - CorePlan 不変条件検証 (fail-fast)
//!
//! # Responsibilities
//!
//! - Validate CorePlan invariants before lowering
//! - Fail fast on close-but-unsupported patterns
//! - Prevent silent miscompilation
//!
//! # Invariants (V2-V9)
//!
//! - V2: Condition validity (valid ValueId)
//! - V3: Exit validity (Return in function, Break/Continue in loop)
//! - V4: Seq non-empty
//! - V5: If completeness (then_plans non-empty)
//! - V6: ValueId validity (all ValueIds pre-generated)
//! - V7: PHI non-empty (loops require at least one carrier)
//! - V8: Frag entry matches header_bb
//! - V9: block_effects contains header_bb
//!
//! Phase 273 P3: V1 (Carrier completeness) removed with CoreCarrierInfo
use super::{CoreEffectPlan, CoreExitPlan, CoreIfPlan, CoreLoopPlan, CorePlan};
use crate::mir::ValueId;
/// Phase 273 P1: PlanVerifier - CorePlan 不変条件検証 (fail-fast)
pub(in crate::mir::builder) struct PlanVerifier;
impl PlanVerifier {
/// Verify CorePlan invariants
///
/// Returns Ok(()) if all invariants hold, Err with details otherwise.
pub(in crate::mir::builder) fn verify(plan: &CorePlan) -> Result<(), String> {
Self::verify_plan(plan, 0, false)
}
fn verify_plan(plan: &CorePlan, depth: usize, in_loop: bool) -> Result<(), String> {
match plan {
CorePlan::Seq(plans) => Self::verify_seq(plans, depth, in_loop),
CorePlan::Loop(loop_plan) => Self::verify_loop(loop_plan, depth),
CorePlan::If(if_plan) => Self::verify_if(if_plan, depth, in_loop),
CorePlan::Effect(effect) => Self::verify_effect(effect, depth),
CorePlan::Exit(exit) => Self::verify_exit(exit, depth, in_loop),
}
}
/// V4: Seq non-empty
fn verify_seq(plans: &[CorePlan], depth: usize, in_loop: bool) -> Result<(), String> {
if plans.is_empty() {
return Err(format!(
"[V4] Empty Seq at depth {} (Seq must have at least one plan)",
depth
));
}
for (i, plan) in plans.iter().enumerate() {
Self::verify_plan(plan, depth + 1, in_loop).map_err(|e| {
format!("[Seq[{}]] {}", i, e)
})?;
}
Ok(())
}
/// Phase 273 P3: Verify loop with generalized fields
///
/// Invariants:
/// - V2: Condition validity (cond_loop, cond_match)
/// - V7: PHI non-empty (at least one carrier)
/// - V8: Frag entry matches header_bb
/// - V9: block_effects contains header_bb
fn verify_loop(loop_plan: &CoreLoopPlan, depth: usize) -> Result<(), String> {
// V2: Condition validity (basic check - ValueId should be non-zero for safety)
Self::verify_value_id_basic(loop_plan.cond_loop, depth, "cond_loop")?;
Self::verify_value_id_basic(loop_plan.cond_match, depth, "cond_match")?;
// V7: PHI non-empty (loops must have at least one carrier)
if loop_plan.phis.is_empty() {
return Err(format!(
"[V7] Loop at depth {} has no PHI nodes (loops require at least one carrier)",
depth
));
}
// V8: Frag entry matches header_bb (loop entry SSOT)
if loop_plan.frag.entry != loop_plan.header_bb {
return Err(format!(
"[V8] Loop at depth {} has frag.entry {:?} != header_bb {:?}",
depth, loop_plan.frag.entry, loop_plan.header_bb
));
}
// V9: block_effects contains header_bb
let has_header = loop_plan.block_effects.iter().any(|(bb, _)| *bb == loop_plan.header_bb);
if !has_header {
return Err(format!(
"[V9] Loop at depth {} block_effects missing header_bb {:?}",
depth, loop_plan.header_bb
));
}
// Verify block_effects
for (i, (bb, effects)) in loop_plan.block_effects.iter().enumerate() {
for (j, effect) in effects.iter().enumerate() {
Self::verify_effect(effect, depth).map_err(|e| {
format!("[Loop.block_effects[{}={:?}][{}]] {}", i, bb, j, e)
})?;
}
}
// Verify body plans (now in_loop = true)
for (i, plan) in loop_plan.body.iter().enumerate() {
Self::verify_plan(plan, depth + 1, true).map_err(|e| {
format!("[Loop.body[{}]] {}", i, e)
})?;
}
// Verify PHIs
for (i, phi) in loop_plan.phis.iter().enumerate() {
Self::verify_value_id_basic(phi.dst, depth, &format!("phi[{}].dst", i))?;
for (j, (_, val)) in phi.inputs.iter().enumerate() {
Self::verify_value_id_basic(*val, depth, &format!("phi[{}].inputs[{}]", i, j))?;
}
}
// Verify final_values
for (i, (name, val)) in loop_plan.final_values.iter().enumerate() {
if name.is_empty() {
return Err(format!(
"[V6] final_values[{}] at depth {} has empty name",
i, depth
));
}
Self::verify_value_id_basic(*val, depth, &format!("final_values[{}]", i))?;
}
Ok(())
}
// Phase 273 P3: verify_carrier() removed (CoreCarrierInfo replaced by CorePhiInfo)
/// V5: If completeness
fn verify_if(if_plan: &CoreIfPlan, depth: usize, in_loop: bool) -> Result<(), String> {
// V2: Condition validity
Self::verify_value_id_basic(if_plan.condition, depth, "if.condition")?;
// V5: then_plans non-empty
if if_plan.then_plans.is_empty() {
return Err(format!(
"[V5] If at depth {} has empty then_plans",
depth
));
}
for (i, plan) in if_plan.then_plans.iter().enumerate() {
Self::verify_plan(plan, depth + 1, in_loop).map_err(|e| {
format!("[If.then[{}]] {}", i, e)
})?;
}
if let Some(else_plans) = &if_plan.else_plans {
for (i, plan) in else_plans.iter().enumerate() {
Self::verify_plan(plan, depth + 1, in_loop).map_err(|e| {
format!("[If.else[{}]] {}", i, e)
})?;
}
}
Ok(())
}
/// V6: Effect ValueId validity
fn verify_effect(effect: &CoreEffectPlan, depth: usize) -> Result<(), String> {
match effect {
CoreEffectPlan::MethodCall { dst, object, method, args, effects: _ } => {
// P2: dst is now Option<ValueId>
if let Some(dst_val) = dst {
Self::verify_value_id_basic(*dst_val, depth, "MethodCall.dst")?;
}
Self::verify_value_id_basic(*object, depth, "MethodCall.object")?;
if method.is_empty() {
return Err(format!(
"[V6] MethodCall at depth {} has empty method name",
depth
));
}
for (i, arg) in args.iter().enumerate() {
Self::verify_value_id_basic(*arg, depth, &format!("MethodCall.args[{}]", i))?;
}
}
CoreEffectPlan::BinOp { dst, lhs, op: _, rhs } => {
Self::verify_value_id_basic(*dst, depth, "BinOp.dst")?;
Self::verify_value_id_basic(*lhs, depth, "BinOp.lhs")?;
Self::verify_value_id_basic(*rhs, depth, "BinOp.rhs")?;
}
CoreEffectPlan::Compare { dst, lhs, op: _, rhs } => {
Self::verify_value_id_basic(*dst, depth, "Compare.dst")?;
Self::verify_value_id_basic(*lhs, depth, "Compare.lhs")?;
Self::verify_value_id_basic(*rhs, depth, "Compare.rhs")?;
}
CoreEffectPlan::Const { dst, value: _ } => {
Self::verify_value_id_basic(*dst, depth, "Const.dst")?;
}
}
Ok(())
}
/// V3: Exit validity
fn verify_exit(exit: &CoreExitPlan, depth: usize, in_loop: bool) -> Result<(), String> {
match exit {
CoreExitPlan::Return(opt_val) => {
if let Some(val) = opt_val {
Self::verify_value_id_basic(*val, depth, "Return.value")?;
}
// Return is always valid (in function context)
}
CoreExitPlan::Break => {
if !in_loop {
return Err(format!(
"[V3] Break at depth {} outside of loop",
depth
));
}
}
CoreExitPlan::Continue => {
if !in_loop {
return Err(format!(
"[V3] Continue at depth {} outside of loop",
depth
));
}
}
}
Ok(())
}
/// V6: Basic ValueId validity check
///
/// Note: This is a basic check. Full validity would require builder context.
fn verify_value_id_basic(value_id: ValueId, depth: usize, context: &str) -> Result<(), String> {
// ValueId(0) might be valid in some contexts, so we don't check for zero
// This is a placeholder for more sophisticated checks if needed
let _ = (value_id, depth, context);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mir::{BasicBlockId, BinaryOp, CompareOp, ConstValue, ValueId};
#[test]
fn test_verify_empty_seq_fails() {
let plan = CorePlan::Seq(vec![]);
let result = PlanVerifier::verify(&plan);
assert!(result.is_err());
assert!(result.unwrap_err().contains("[V4]"));
}
#[test]
fn test_verify_break_outside_loop_fails() {
let plan = CorePlan::Exit(CoreExitPlan::Break);
let result = PlanVerifier::verify(&plan);
assert!(result.is_err());
assert!(result.unwrap_err().contains("[V3]"));
}
#[test]
fn test_verify_const_effect_succeeds() {
let plan = CorePlan::Effect(CoreEffectPlan::Const {
dst: ValueId(1),
value: ConstValue::Integer(42),
});
let result = PlanVerifier::verify(&plan);
assert!(result.is_ok());
}
}