diff --git a/src/mir/builder/calls/lowering.rs b/src/mir/builder/calls/lowering.rs index cb0018b2..eaab73a3 100644 --- a/src/mir/builder/calls/lowering.rs +++ b/src/mir/builder/calls/lowering.rs @@ -199,18 +199,43 @@ impl MirBuilder { &tree, &func_name, strict, dev, )?; - // Phase 121: StepTree→Normalized shadow lowering (dev-only) + // Phase 121/122: StepTree→Normalized shadow lowering (dev-only) if dev { use crate::mir::control_tree::normalized_shadow::StepTreeNormalizedShadowLowererBox; + use crate::mir::control_tree::normalized_shadow::parity; // Try shadow lowering (if-only scope) let shadow_result = StepTreeNormalizedShadowLowererBox::try_lower_if_only(&tree); match shadow_result { - Ok(Some((_module, _meta))) => { - // Shadow lowering succeeded - let status = StepTreeNormalizedShadowLowererBox::get_status_string(&tree); - trace.dev("phase121/shadow", &status); + Ok(Some((module, _meta))) => { + // Phase 122: Verify Normalized JoinModule structure + let expected_env_fields = tree.contract.writes.len(); + let verify_result = parity::verify_normalized_structure(&module, expected_env_fields); + + if !verify_result.ok { + let msg = format!( + "phase122/emit: structure verification failed for {}: {}", + func_name, + verify_result.hint.unwrap_or_else(|| "".to_string()) + ); + if strict { + return Err(format!( + "Phase122 Normalized structure verification failed (strict mode): {}", + msg + )); + } + trace.dev("phase122/emit/error", &msg); + } else { + // Shadow lowering succeeded + structure verified + let status = format!( + "module_emitted=true funcs={} env_fields={} step_tree_sig={}", + module.functions.len(), + expected_env_fields, + tree.signature_basis_string() + ); + trace.dev("phase122/emit", &status); + } } Ok(None) => { // Out of scope (e.g., contains loops) diff --git a/src/mir/control_tree/normalized_shadow/parity.rs b/src/mir/control_tree/normalized_shadow/parity.rs index a3ea0571..de899f41 100644 --- a/src/mir/control_tree/normalized_shadow/parity.rs +++ b/src/mir/control_tree/normalized_shadow/parity.rs @@ -137,6 +137,62 @@ pub fn check_full_parity( ShadowParityResult::ok() } +/// Phase 122: Verify Normalized JoinModule structure +/// +/// ## Contract +/// +/// - Checks function count, continuation count, tail-call form, env args +/// - Does NOT check actual execution (RC comparison is optional) +/// - Returns mismatch with hint if structure is invalid +pub fn verify_normalized_structure( + module: &crate::mir::join_ir::JoinModule, + expected_env_fields: usize, +) -> ShadowParityResult { + use crate::mir::join_ir::JoinIrPhase; + + // Check phase + if !module.is_normalized() { + let hint = format!( + "module phase is not Normalized: {:?}", + module.phase + ); + return ShadowParityResult::mismatch(MismatchKind::UnsupportedKind, hint); + } + + // Check function count (Phase 122 minimal: 1 main function) + if module.functions.is_empty() { + let hint = "no functions in module".to_string(); + return ShadowParityResult::mismatch(MismatchKind::UnsupportedKind, hint); + } + + // Check entry point + if module.entry.is_none() { + let hint = "no entry point in module".to_string(); + return ShadowParityResult::mismatch(MismatchKind::UnsupportedKind, hint); + } + + // Check main function exists + let entry_id = module.entry.unwrap(); + let main_func = module.functions.get(&entry_id); + if main_func.is_none() { + let hint = format!("entry function {:?} not found", entry_id); + return ShadowParityResult::mismatch(MismatchKind::UnsupportedKind, hint); + } + + // Check env args count (Phase 122: writes only) + let main_func = main_func.unwrap(); + if main_func.params.len() != expected_env_fields { + let hint = format!( + "env args mismatch: expected {}, got {}", + expected_env_fields, + main_func.params.len() + ); + return ShadowParityResult::mismatch(MismatchKind::UnsupportedKind, hint); + } + + ShadowParityResult::ok() +} + #[cfg(test)] mod tests { use super::*;