use super::ids::{
BINDINGS, COMPONENT_USAGES, REACTIVITY, RENDER_TREE, UNDEFINED_REFS, UNUSED_BINDINGS,
};
use super::manager::{FactManager, compute_into};
use super::view::Tables;
use super::{Demand, FactError};
use crate::pass::{AnalysisId, PassDesc, Preserved};
pub const PRESERVE_STRUCTURE: Preserved = Preserved::NONE.with(COMPONENT_USAGES).with(RENDER_TREE);
pub const PRESERVE_BINDINGS: Preserved = Preserved::NONE
.with(BINDINGS)
.with(UNDEFINED_REFS)
.with(UNUSED_BINDINGS)
.with(REACTIVITY);
pub trait FactVerify {
const ENABLED: bool;
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct FactVerifyObserver;
impl FactVerify for FactVerifyObserver {
const ENABLED: bool = cfg!(debug_assertions);
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct NoFactVerify;
impl FactVerify for NoFactVerify {
const ENABLED: bool = false;
}
const _: () = assert!(size_of::<FactVerifyObserver>() == 0);
const _: () = assert!(size_of::<NoFactVerify>() == 0);
impl<A: ?Sized + 'static> FactManager<'_, A> {
pub fn after_pass<V: FactVerify>(
&mut self,
artifact: &A,
desc: &PassDesc,
) -> Result<Demand, FactError> {
let kept = self.tables.computed.surviving(desc.preserved);
let dropped = self.tables.computed.minus(kept);
self.tables.drop_groups(dropped);
if V::ENABLED
&& let Some((first, stale)) = self.stale(artifact, kept)
{
self.tables.drop_groups(stale);
return Err(FactError::StalePreserved {
pass: desc.name,
group: first,
});
}
Ok(dropped)
}
fn stale(&self, artifact: &A, kept: Demand) -> Option<(AnalysisId, Demand)> {
if kept.is_empty() {
return None;
}
let mut fresh = Tables::new();
compute_into(
self.registry,
&mut fresh,
artifact,
self.registry.closure(kept),
false,
);
let producers = self.registry.producers();
let top = producers.iter().map(|entry| entry.desc.stratum).max()?;
let mut first = None;
let mut stale = Demand::NONE;
for stratum in 0..=top {
for entry in producers {
let id = entry.desc.id;
if entry.desc.stratum != stratum || !kept.contains(id) {
continue;
}
let same = match (self.tables.slot(id), fresh.slot(id)) {
(Some(old), Some(new)) => (entry.eq)(old, new),
_ => false,
};
if !same {
first.get_or_insert(id);
stale = stale.with(id);
}
}
}
first.map(|first| (first, stale))
}
}