use super::WitnessError;
use crate::diag::{Verdict, WitnessKeyed, WitnessLink};
use crate::fact::{Demand, FactGroup, FactView};
use crate::pass::AnalysisId;
use vize_l0::Span;
pub trait WitnessGroup: FactGroup<Key: WitnessKeyed> {
fn fact_span(key: &Self::Key, value: &Self::Value) -> Span;
fn verdict(_value: &Self::Value) -> Verdict {
Verdict::Proven
}
}
type CheckFn = fn(&FactView<'_>, usize, &WitnessLink) -> Result<(), WitnessError>;
#[derive(Clone, Copy)]
pub struct WitnessCheck {
pub group: AnalysisId,
pub name: &'static str,
check: CheckFn,
}
impl core::fmt::Debug for WitnessCheck {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("WitnessCheck")
.field("group", &self.group)
.field("name", &self.name)
.finish_non_exhaustive()
}
}
impl WitnessCheck {
#[must_use]
pub const fn of<G: WitnessGroup>() -> Self {
Self {
group: G::ID,
name: G::NAME,
check: check_link::<G>,
}
}
pub fn run(
&self,
facts: &FactView<'_>,
index: usize,
link: &WitnessLink,
) -> Result<(), WitnessError> {
(self.check)(facts, index, link)
}
}
fn check_link<G: WitnessGroup>(
facts: &FactView<'_>,
index: usize,
link: &WitnessLink,
) -> Result<(), WitnessError> {
let table = facts
.get::<G>()
.map_err(|error| WitnessError::Fact { link: index, error })?;
let Some(key) = G::Key::from_witness_key(&link.key) else {
return Err(WitnessError::KeyShape {
link: index,
group: G::ID,
key: link.key.clone(),
});
};
let Some(value) = table.get(&key) else {
return Err(WitnessError::MissingKey {
link: index,
group: G::ID,
key: link.key.clone(),
});
};
let fact = G::fact_span(&key, value);
if fact != link.span {
return Err(WitnessError::SpanMismatch {
link: index,
group: G::ID,
fact,
witness: link.span,
});
}
let verdict = G::verdict(value);
if !verdict.is_proven() {
return Err(WitnessError::NotProven {
link: index,
group: G::ID,
verdict,
});
}
Ok(())
}
#[derive(Debug, Clone, Copy)]
pub struct WitnessChecks {
checks: &'static [WitnessCheck],
groups: Demand,
}
impl WitnessChecks {
#[must_use]
pub const fn new(checks: &'static [WitnessCheck]) -> Self {
let mut groups = Demand::NONE;
let mut rest = checks;
while let [check, tail @ ..] = rest {
let group = check.group;
assert!(
!groups.contains(group),
"a witness registry names each fact group at most once"
);
groups = groups.with(group);
rest = tail;
}
Self { checks, groups }
}
#[must_use]
pub const fn groups(&self) -> Demand {
self.groups
}
#[must_use]
pub fn check(&self, group: AnalysisId) -> Option<&'static WitnessCheck> {
if !self.groups.contains(group) {
return None;
}
self.checks.iter().find(|check| check.group == group)
}
}