use vize_l0::pass::{PassEvent, PassObserver};
use vize_l0::side_table::SideTable;
use vize_l0::{Allocator, ArenaStamp};
use crate::dump::Page as L2Page;
#[cfg(debug_assertions)]
use super::{Rigor, Violation, verify, verify_table};
#[cfg(debug_assertions)]
use vize_l0::pass::PassKind;
#[cfg(debug_assertions)]
use vize_l0::{String, cstr};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct VerifyObserver {
#[cfg(debug_assertions)]
rigor: Rigor,
}
#[cfg(debug_assertions)]
const _: () = assert!(size_of::<VerifyObserver>() == 1);
#[cfg(not(debug_assertions))]
const _: () = assert!(size_of::<VerifyObserver>() == 0);
impl VerifyObserver {
#[must_use]
pub const fn new() -> Self {
Self {
#[cfg(debug_assertions)]
rigor: Rigor::Raw,
}
}
#[cfg(debug_assertions)]
#[must_use]
pub const fn rigor(&self) -> Rigor {
self.rigor
}
pub fn note(&mut self, event: &PassEvent<'_>) {
#[cfg(debug_assertions)]
if event.desc().kind == PassKind::MandatoryLowering {
self.rigor = Rigor::Canonical;
}
#[cfg(not(debug_assertions))]
let _ = event;
}
pub fn check(&self, event: &PassEvent<'_>, folio: &L2Page) {
#[cfg(debug_assertions)]
{
fail(event, &verify(folio, self.rigor));
}
#[cfg(not(debug_assertions))]
let _ = (event, folio);
}
pub fn check_table<T>(&self, event: &PassEvent<'_>, folio: &L2Page, table: &SideTable<T>) {
#[cfg(debug_assertions)]
{
fail(event, &verify_table(folio, table));
}
#[cfg(not(debug_assertions))]
let _ = (event, folio, table);
}
pub fn check_live(&self, event: &PassEvent<'_>, allocator: &Allocator, stamp: ArenaStamp) {
#[cfg(debug_assertions)]
{
let _ = event;
allocator.assert_stamp_current(stamp);
}
#[cfg(not(debug_assertions))]
let _ = (event, allocator, stamp);
}
}
impl Default for VerifyObserver {
fn default() -> Self {
Self::new()
}
}
impl PassObserver for VerifyObserver {
fn after_pass(&mut self, event: &PassEvent<'_>) {
self.note(event);
}
}
#[cfg(debug_assertions)]
#[expect(
clippy::panic,
reason = "debug-only verifier: a violated invariant must abort the pass"
)]
fn fail(event: &PassEvent<'_>, violations: &[Violation]) {
if violations.is_empty() {
return;
}
let report = report(event, violations);
panic!("{report}");
}
#[cfg(debug_assertions)]
fn report(event: &PassEvent<'_>, violations: &[Violation]) -> String {
use core::fmt::Write;
let mut text = cstr!(
"L2 verifier: {} violation(s) after `{}.{}`",
violations.len(),
event.pipeline.stage,
event.desc().name
);
for violation in violations {
let _ = write!(text, "\n{violation}");
}
text
}