use crate::{
spec::law::{canonical_law_id, AlgebraicLaw},
spec::OpSpec,
};
pub use crate::proof::algebra::mandatory_inference::infer_and_seal;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LawInferenceRejection {
pub op_id: String,
pub law: String,
pub fix: String,
}
impl core::fmt::Display for LawInferenceRejection {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"{}: law `{}` was not inferred. {}",
self.op_id, self.law, self.fix
)
}
}
impl std::error::Error for LawInferenceRejection {}
#[inline]
pub fn validate_declared_laws(spec: &OpSpec) -> Result<(), Vec<LawInferenceRejection>> {
let is_binary = spec.signature.inputs.len() == 2;
let inferred = infer_and_seal(spec.id, spec.cpu_fn, is_binary);
let rejections: Vec<LawInferenceRejection> = spec
.laws
.iter()
.filter(|declared| !law_is_inferred(declared, &inferred))
.map(|declared| LawInferenceRejection {
op_id: spec.id.to_string(),
law: declared.name().to_string(),
fix: "Fix: remove the hand-written law claim or change the reference function so exhaustive inference proves it.".to_string(),
})
.collect();
if rejections.is_empty() {
Ok(())
} else {
Err(rejections)
}
}
fn law_is_inferred(declared: &AlgebraicLaw, inferred: &[AlgebraicLaw]) -> bool {
inferred
.iter()
.any(|candidate| canonical_law_id(candidate) == canonical_law_id(declared))
}
pub struct Layer2LawInferenceEnforcer;
impl crate::enforce::EnforceGate for Layer2LawInferenceEnforcer {
fn id(&self) -> &'static str {
"layer2_law_inference"
}
fn name(&self) -> &'static str {
"layer2_law_inference"
}
fn run(&self, _ctx: &crate::enforce::EnforceCtx<'_>) -> Vec<crate::enforce::Finding> {
let messages = Vec::new();
crate::enforce::finding_result(self.id(), messages)
}
}
pub const REGISTERED: Layer2LawInferenceEnforcer = Layer2LawInferenceEnforcer;