|
| 1 | +//! Core dataflow analysis traits. |
| 2 | +//! |
| 3 | +//! This module provides a unified trait for both forward and backward dataflow analysis. |
| 4 | +//! |
| 5 | +//! # Design |
| 6 | +//! |
| 7 | +//! The `DataflowAnalyzer` trait provides a layered API: |
| 8 | +//! |
| 9 | +//! 1. **Core methods** (must implement): `initial_info`, `merge` |
| 10 | +//! 2. **Transfer granularity** (choose one): |
| 11 | +//! - Block-level: override `transfer_block` for coarse-grained analysis |
| 12 | +//! - Statement-level: override `transfer_stmt` (default `transfer_block` iterates statements) |
| 13 | +//! 3. **Variable tracking** (optional): `apply_remapping` - override if tracking per-variable state |
| 14 | +//! 4. **Advanced hooks** (optional): `merge_match`, `split_match`, etc. - override for special |
| 15 | +//! cases |
| 16 | +//! |
| 17 | +//! The framework handles control flow mechanics automatically: |
| 18 | +//! - Goto with remapping: calls `apply_remapping` |
| 19 | +//! - Match (backward): calls `merge_match` which defaults to `merge` |
| 20 | +//! - Match (forward): calls `split_match` which defaults to cloning |
| 21 | +
|
| 22 | +use crate::{Block, BlockEnd, BlockId, MatchInfo, Statement, VarRemapping}; |
| 23 | + |
| 24 | +/// Location of a lowering statement inside a block. |
| 25 | +#[allow(dead_code)] |
| 26 | +pub type StatementLocation = (BlockId, usize); |
| 27 | + |
| 28 | +/// The direction of dataflow analysis. |
| 29 | +#[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| 30 | +#[allow(dead_code)] |
| 31 | +pub enum Direction { |
| 32 | + Forward, |
| 33 | + Backward, |
| 34 | +} |
| 35 | + |
| 36 | +/// Unified analyzer trait for dataflow analysis. |
| 37 | +/// |
| 38 | +/// Implementors specify the direction via `DIRECTION` and implement the core methods. |
| 39 | +/// The framework handles control flow mechanics (remapping, match arms) automatically, |
| 40 | +/// with sensible defaults that can be overridden for complex analyses. |
| 41 | +/// |
| 42 | +/// # Transfer Granularity |
| 43 | +/// |
| 44 | +/// You can work at either block or statement level: |
| 45 | +/// - **Block-level**: Override `transfer_block` for coarse-grained analysis |
| 46 | +/// - **Statement-level**: Override `transfer_stmt`; default `transfer_block` iterates statements |
| 47 | +/// |
| 48 | +/// # Lifetime parameters |
| 49 | +/// - `'db`: The database lifetime (for interned types). |
| 50 | +/// - `'a`: The lifetime of borrowed lowering data. |
| 51 | +/// |
| 52 | +/// # Example (statement-level forward analysis) |
| 53 | +/// ```ignore |
| 54 | +/// impl<'db, 'a> DataflowAnalyzer<'db, 'a> for MyAnalyzer { |
| 55 | +/// type Info = HashSet<VariableId>; |
| 56 | +/// const DIRECTION: Direction = Direction::Forward; |
| 57 | +/// |
| 58 | +/// fn initial_info(&mut self) -> Self::Info { HashSet::new() } |
| 59 | +/// fn transfer_stmt(&mut self, info: &mut Self::Info, ..) { /* update info */ } |
| 60 | +/// fn merge(&mut self, infos: impl Iterator<Item = Self::Info>) -> Self::Info { |
| 61 | +/// infos.fold(HashSet::new(), |mut acc, i| { acc.extend(i); acc }) |
| 62 | +/// } |
| 63 | +/// } |
| 64 | +/// ``` |
| 65 | +/// |
| 66 | +/// # Example (block-level analysis) |
| 67 | +/// ```ignore |
| 68 | +/// impl<'db, 'a> DataflowAnalyzer<'db, 'a> for BlockCounter { |
| 69 | +/// type Info = usize; |
| 70 | +/// const DIRECTION: Direction = Direction::Forward; |
| 71 | +/// |
| 72 | +/// fn initial_info(&mut self) -> Self::Info { 0 } |
| 73 | +/// fn transfer_block(&mut self, info: &mut Self::Info, block_id: BlockId, block: &Block<'db>) { |
| 74 | +/// *info += 1; // Just count blocks |
| 75 | +/// } |
| 76 | +/// fn merge(&mut self, infos: impl Iterator<Item = Self::Info>) -> Self::Info { |
| 77 | +/// infos.max().unwrap_or(0) |
| 78 | +/// } |
| 79 | +/// } |
| 80 | +/// ``` |
| 81 | +#[allow(dead_code)] |
| 82 | +pub trait DataflowAnalyzer<'db, 'a> { |
| 83 | + /// The analysis state/info type. |
| 84 | + type Info: Clone; |
| 85 | + |
| 86 | + /// The direction of this analysis. |
| 87 | + const DIRECTION: Direction; |
| 88 | + |
| 89 | + // ======================================================================== |
| 90 | + // Core methods (must implement) |
| 91 | + // ======================================================================== |
| 92 | + |
| 93 | + /// Create the initial analysis state at a terminal block. |
| 94 | + /// |
| 95 | + /// - Backward: called at return/panic blocks (what we know at function exit) |
| 96 | + /// - Forward: called at function entry |
| 97 | + /// |
| 98 | + /// For backward analysis, `block_end` provides access to return variables or panic data. |
| 99 | + /// For forward analysis, this is typically called once at the root block. |
| 100 | + fn initial_info(&mut self, block_id: BlockId, block_end: &'a BlockEnd<'db>) -> Self::Info; |
| 101 | + |
| 102 | + /// Merge/join states from multiple control flow paths. |
| 103 | + /// Called at join points (match merge for backward, block entry for forward). |
| 104 | + /// |
| 105 | + /// - `statement_location`: where the merge occurs in the CFG |
| 106 | + /// - `infos`: iterator of (source_block_id, info) pairs from each incoming path |
| 107 | + fn merge( |
| 108 | + &mut self, |
| 109 | + statement_location: StatementLocation, |
| 110 | + infos: impl Iterator<Item = (BlockId, Self::Info)>, |
| 111 | + ) -> Self::Info; |
| 112 | + |
| 113 | + // ======================================================================== |
| 114 | + // Transfer functions (choose granularity) |
| 115 | + // ======================================================================== |
| 116 | + |
| 117 | + /// Transfer function for an entire block. |
| 118 | + /// - Backward: transforms post-block state to pre-block state |
| 119 | + /// - Forward: transforms pre-block state to post-block state |
| 120 | + /// |
| 121 | + /// Default implementation iterates statements and calls `transfer_stmt`. |
| 122 | + /// Override this for block-level analysis (ignoring individual statements). |
| 123 | + fn transfer_block(&mut self, info: &mut Self::Info, block_id: BlockId, block: &'a Block<'db>) { |
| 124 | + match Self::DIRECTION { |
| 125 | + Direction::Forward => { |
| 126 | + for (i, stmt) in block.statements.iter().enumerate() { |
| 127 | + self.transfer_stmt(info, (block_id, i), stmt); |
| 128 | + } |
| 129 | + } |
| 130 | + Direction::Backward => { |
| 131 | + for (i, stmt) in block.statements.iter().enumerate().rev() { |
| 132 | + self.transfer_stmt(info, (block_id, i), stmt); |
| 133 | + } |
| 134 | + } |
| 135 | + } |
| 136 | + } |
| 137 | + |
| 138 | + /// Transfer function for a single statement. |
| 139 | + /// - Backward: transforms post-state to pre-state |
| 140 | + /// - Forward: transforms pre-state to post-state |
| 141 | + /// |
| 142 | + /// Default is no-op. Override this for statement-level analysis. |
| 143 | + fn transfer_stmt( |
| 144 | + &mut self, |
| 145 | + _info: &mut Self::Info, |
| 146 | + _statement_location: StatementLocation, |
| 147 | + _stmt: &'a Statement<'db>, |
| 148 | + ) { |
| 149 | + } |
| 150 | + |
| 151 | + // ======================================================================== |
| 152 | + // Variable tracking (optional - override if tracking per-variable state) |
| 153 | + // ======================================================================== |
| 154 | + |
| 155 | + /// Apply variable remapping to the state. |
| 156 | + /// Called by the framework when crossing a goto with remapping. |
| 157 | + /// |
| 158 | + /// - Backward: translates demands on destination vars to demands on source vars |
| 159 | + /// - Forward: translates state of source vars to state of destination vars |
| 160 | + /// |
| 161 | + /// Default is no-op (fine for analyses that don't track per-variable state). |
| 162 | + fn apply_remapping( |
| 163 | + &mut self, |
| 164 | + _info: &mut Self::Info, |
| 165 | + _statement_location: StatementLocation, |
| 166 | + _target_block_id: BlockId, |
| 167 | + _remapping: &'a VarRemapping<'db>, |
| 168 | + ) { |
| 169 | + } |
| 170 | + |
| 171 | + // ======================================================================== |
| 172 | + // Block boundary hooks (optional) |
| 173 | + // ======================================================================== |
| 174 | + |
| 175 | + /// Called when entering a block during traversal (before transfer_block). |
| 176 | + fn visit_block_start( |
| 177 | + &mut self, |
| 178 | + _info: &mut Self::Info, |
| 179 | + _block_id: BlockId, |
| 180 | + _block: &Block<'db>, |
| 181 | + ) { |
| 182 | + } |
| 183 | + |
| 184 | + // ======================================================================== |
| 185 | + // Match handling (optional - have sensible defaults) |
| 186 | + // ======================================================================== |
| 187 | + |
| 188 | + /// Backward: merge states from match arms. |
| 189 | + /// Default implementation calls `merge` with arm block IDs. |
| 190 | + /// Override for special handling (e.g., tracking arm-specific metadata). |
| 191 | + fn merge_match( |
| 192 | + &mut self, |
| 193 | + statement_location: StatementLocation, |
| 194 | + match_info: &'a MatchInfo<'db>, |
| 195 | + arm_infos: impl Iterator<Item = Self::Info>, |
| 196 | + ) -> Self::Info { |
| 197 | + let infos_with_blocks = match_info.arms().iter().map(|arm| arm.block_id).zip(arm_infos); |
| 198 | + self.merge(statement_location, infos_with_blocks) |
| 199 | + } |
| 200 | + |
| 201 | + /// Forward: split state for match arms. |
| 202 | + /// Default implementation clones state for each arm. |
| 203 | + /// Override to refine state based on match conditions. |
| 204 | + fn split_match( |
| 205 | + &mut self, |
| 206 | + info: &Self::Info, |
| 207 | + _statement_location: StatementLocation, |
| 208 | + match_info: &'a MatchInfo<'db>, |
| 209 | + ) -> Vec<Self::Info> { |
| 210 | + match_info.arms().iter().map(|_| info.clone()).collect() |
| 211 | + } |
| 212 | +} |
0 commit comments