use crate::ids::LocationId;
use crate::{
Block, BlockEnd, BlockId, Lowered, MatchArm, MatchInfo, Statement, VarRemapping, VarUsage,
};
pub type StatementLocation = (BlockId, usize);
#[expect(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Direction {
Forward,
Backward,
}
#[derive(Debug)]
#[expect(dead_code)]
pub enum Edge<'db, 'a> {
Goto { target: BlockId, remapping: &'a VarRemapping<'db> },
MatchArm { arm: &'a MatchArm<'db>, match_info: &'a MatchInfo<'db> },
Return { vars: &'a [VarUsage<'db>], location: LocationId<'db> },
Panic { var: VarUsage<'db> },
}
#[expect(dead_code)]
pub trait DataflowAnalyzer<'db, 'a> {
type Info: Clone;
const DIRECTION: Direction;
fn initial_info(&mut self, block_id: BlockId, block_end: &'a BlockEnd<'db>) -> Self::Info;
fn merge(
&mut self,
lowered: &Lowered<'db>,
statement_location: StatementLocation,
info1: Self::Info,
info2: Self::Info,
) -> Self::Info;
fn transfer_block(&mut self, info: &mut Self::Info, block_id: BlockId, block: &'a Block<'db>) {
match Self::DIRECTION {
Direction::Forward => {
for (i, stmt) in block.statements.iter().enumerate() {
self.transfer_stmt(info, (block_id, i), stmt);
}
}
Direction::Backward => {
for (i, stmt) in block.statements.iter().enumerate().rev() {
self.transfer_stmt(info, (block_id, i), stmt);
}
}
}
}
fn transfer_stmt(
&mut self,
_info: &mut Self::Info,
_statement_location: StatementLocation,
_stmt: &'a Statement<'db>,
) {
}
fn transfer_edge(&mut self, info: &Self::Info, _edge: &Edge<'db, 'a>) -> Self::Info {
info.clone()
}
fn visit_block_start(
&mut self,
_info: &mut Self::Info,
_block_id: BlockId,
_block: &Block<'db>,
) {
}
fn block_entry_location(&self, lowered: &Lowered<'db>, block_id: BlockId) -> LocationId<'db> {
match Self::DIRECTION {
Direction::Backward => {
if let BlockEnd::Match { info } = &lowered.blocks[block_id].end {
*info.location()
} else {
unreachable!("In a backward analysis a merge should always be a match end.")
}
}
Direction::Forward => lowered.blocks[block_id]
.statements
.iter()
.find_map(|s| s.location())
.or(lowered.blocks[block_id].end.location())
.unwrap_or_else(|| {
let BlockEnd::Goto(next, _) = &lowered.blocks[block_id].end else {
unreachable!("Only goto end has no location")
};
self.block_entry_location(lowered, *next)
}),
}
}
}