#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Soundness {
MayOver,
MustUnder,
Exact,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum PrecisionContract {
ZeroFalsePositive,
RecallDriven,
FalseNegativesAccepted,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct PrimitiveSoundness {
pub op_id: &'static str,
pub soundness: Soundness,
pub sanitizer_filter: bool,
}
impl PrimitiveSoundness {
#[must_use]
pub const fn new(op_id: &'static str, soundness: Soundness) -> Self {
Self {
op_id,
soundness,
sanitizer_filter: false,
}
}
#[must_use]
pub const fn with_sanitizer_filter(mut self) -> Self {
self.sanitizer_filter = true;
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct SoundnessViolation {
pub op_id: &'static str,
pub soundness: Soundness,
pub contract: PrecisionContract,
pub fix: &'static str,
}
impl Soundness {
#[must_use]
pub const fn join(self, other: Soundness) -> Soundness {
match (self, other) {
(Soundness::MayOver, _) | (_, Soundness::MayOver) => Soundness::MayOver,
(Soundness::MustUnder, Soundness::MustUnder) => Soundness::MustUnder,
(Soundness::MustUnder, Soundness::Exact) | (Soundness::Exact, Soundness::MustUnder) => {
Soundness::MustUnder
}
(Soundness::Exact, Soundness::Exact) => Soundness::Exact,
}
}
}
pub fn validate_pipeline(
contract: PrecisionContract,
primitives: &[PrimitiveSoundness],
) -> Result<Soundness, SoundnessViolation> {
let mut joined = Soundness::Exact;
for primitive in primitives {
validate_primitive(contract, *primitive)?;
joined = joined.join(primitive.soundness);
}
Ok(joined)
}
pub fn validate_primitive(
contract: PrecisionContract,
primitive: PrimitiveSoundness,
) -> Result<(), SoundnessViolation> {
match (contract, primitive.soundness, primitive.sanitizer_filter) {
(PrecisionContract::ZeroFalsePositive, Soundness::Exact, _)
| (PrecisionContract::ZeroFalsePositive, Soundness::MayOver, true)
| (PrecisionContract::RecallDriven, Soundness::Exact | Soundness::MayOver, _)
| (PrecisionContract::FalseNegativesAccepted, _, _) => Ok(()),
(PrecisionContract::ZeroFalsePositive, Soundness::MayOver, false) => {
Err(SoundnessViolation {
op_id: primitive.op_id,
soundness: primitive.soundness,
contract,
fix: "Fix: add an explicit sanitizer filter or use only Exact primitives.",
})
}
(PrecisionContract::ZeroFalsePositive, Soundness::MustUnder, _) => {
Err(SoundnessViolation {
op_id: primitive.op_id,
soundness: primitive.soundness,
contract,
fix: "Fix: zero-FP pipelines require Exact primitives or filtered MayOver primitives.",
})
}
(PrecisionContract::RecallDriven, Soundness::MustUnder, _) => Err(SoundnessViolation {
op_id: primitive.op_id,
soundness: primitive.soundness,
contract,
fix: "Fix: recall-driven pipelines cannot include under-approximating primitives.",
}),
}
}
pub trait SoundnessTagged {
fn soundness(&self) -> Soundness;
}