use core::any::{Any, type_name};
use core::sync::atomic::{AtomicU64, Ordering};
use super::registry::ErasedTable;
use super::{Demand, FactGroup, FactTable};
use crate::pass::{AnalysisId, MAX_ANALYSES};
pub(crate) const SLOTS: usize = MAX_ANALYSES as usize;
static PRODUCED: [AtomicU64; SLOTS] = [const { AtomicU64::new(0) }; SLOTS];
static UNDECLARED: AtomicU64 = AtomicU64::new(0);
#[must_use]
pub fn produced_count(group: AnalysisId) -> u64 {
PRODUCED
.get(group.index() as usize)
.map_or(0, |count| count.load(Ordering::Relaxed))
}
pub(crate) fn count_production(group: AnalysisId) {
if let Some(count) = PRODUCED.get(group.index() as usize) {
count.fetch_add(1, Ordering::Relaxed);
}
}
#[must_use]
pub fn undeclared_accesses() -> u64 {
UNDECLARED.load(Ordering::Relaxed)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum FactError {
Undeclared {
consumer: &'static str,
group: AnalysisId,
},
Unregistered { group: AnalysisId },
NotComputed { group: AnalysisId },
TypeMismatch {
group: AnalysisId,
expected: &'static str,
},
StalePreserved {
pass: &'static str,
group: AnalysisId,
},
}
pub(crate) struct Tables {
pub(crate) slots: [Option<ErasedTable>; SLOTS],
pub(crate) computed: Demand,
}
impl Tables {
pub(crate) fn new() -> Self {
Self {
slots: [const { None }; SLOTS],
computed: Demand::NONE,
}
}
pub(crate) fn slot(&self, group: AnalysisId) -> Option<&(dyn Any + Send + Sync)> {
self.slots
.get(group.index() as usize)
.and_then(Option::as_deref)
}
pub(crate) fn store(&mut self, group: AnalysisId, table: ErasedTable) {
if let Some(slot) = self.slots.get_mut(group.index() as usize) {
*slot = Some(table);
self.computed = self.computed.with(group);
}
}
pub(crate) fn drop_groups(&mut self, groups: Demand) {
for group in groups.iter() {
if let Some(slot) = self.slots.get_mut(group.index() as usize) {
*slot = None;
}
}
self.computed = self.computed.minus(groups);
}
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct Declared {
#[cfg(debug_assertions)]
consumer: &'static str,
#[cfg(debug_assertions)]
demand: Demand,
}
#[cfg(not(debug_assertions))]
const _: () = assert!(size_of::<Declared>() == 0);
impl Declared {
#[cfg_attr(
not(debug_assertions),
expect(unused_variables, reason = "the detector is empty in release builds")
)]
pub(crate) const fn new(consumer: &'static str, demand: Demand) -> Self {
Self {
#[cfg(debug_assertions)]
consumer,
#[cfg(debug_assertions)]
demand,
}
}
#[cfg_attr(
not(debug_assertions),
expect(unused_variables, reason = "the detector is empty in release builds")
)]
#[inline]
fn check(self, group: AnalysisId) -> Result<(), FactError> {
#[cfg(debug_assertions)]
if !self.demand.contains(group) {
UNDECLARED.fetch_add(1, Ordering::Relaxed);
return Err(FactError::Undeclared {
consumer: self.consumer,
group,
});
}
Ok(())
}
}
#[derive(Clone, Copy)]
pub struct FactView<'m> {
tables: &'m Tables,
declared: Declared,
}
impl<'m> FactView<'m> {
pub(crate) const fn new(tables: &'m Tables, declared: Declared) -> Self {
Self { tables, declared }
}
pub fn get<G: FactGroup>(&self) -> Result<&'m FactTable<G>, FactError> {
self.declared.check(G::ID)?;
let table = self
.tables
.slot(G::ID)
.ok_or(FactError::NotComputed { group: G::ID })?;
table
.downcast_ref::<FactTable<G>>()
.ok_or(FactError::TypeMismatch {
group: G::ID,
expected: type_name::<G>(),
})
}
}