gruel_air/
analysis_state.rs

1//! Per-function mutable state for semantic analysis.
2//!
3//! This module contains state that is mutated during function analysis.
4//! Each function can have its own `FunctionAnalysisState`, which is then
5//! merged after parallel analysis completes.
6//!
7//! # Array Type Handling (ADR-0024)
8//!
9//! Array types are handled by the shared `TypeInternPool` in `SemaContext`,
10//! which is thread-safe and handles deduplication automatically. Per-function
11//! array tracking has been removed - array types created during function analysis
12//! go directly to the shared pool.
13
14use std::collections::HashMap;
15
16use gruel_error::CompileWarning;
17
18/// Per-function mutable state during semantic analysis.
19///
20/// This struct contains all mutable state that is modified during function
21/// body analysis. For parallel analysis, each function gets its own instance,
22/// and results are merged afterward.
23///
24/// # Contents
25///
26/// - String literals encountered
27/// - Warnings generated
28///
29/// # Note on Array Types
30///
31/// Array types are handled by the shared `TypeInternPool` in `SemaContext`.
32/// They are no longer tracked per-function.
33///
34/// # Merging
35///
36/// After parallel analysis, use `merge_into` to combine results:
37/// - Strings are deduplicated
38/// - Warnings are concatenated
39#[derive(Debug, Default)]
40pub struct FunctionAnalysisState {
41    /// String table for deduplication.
42    pub string_table: HashMap<String, u32>,
43    /// String literals in order of creation.
44    pub strings: Vec<String>,
45    /// Warnings collected during analysis.
46    pub warnings: Vec<CompileWarning>,
47}
48
49impl FunctionAnalysisState {
50    /// Create a new empty analysis state.
51    pub fn new() -> Self {
52        Self::default()
53    }
54
55    /// Add a string to the string table, returning its index.
56    /// Deduplicates identical strings.
57    pub fn add_string(&mut self, content: String) -> u32 {
58        use std::collections::hash_map::Entry;
59        match self.string_table.entry(content) {
60            Entry::Occupied(e) => *e.get(),
61            Entry::Vacant(e) => {
62                let id = self.strings.len() as u32;
63                self.strings.push(e.key().clone());
64                e.insert(id);
65                id
66            }
67        }
68    }
69
70    /// Add a warning.
71    pub fn add_warning(&mut self, warning: CompileWarning) {
72        self.warnings.push(warning);
73    }
74}
75
76/// Merged state from multiple function analyses.
77///
78/// This is the result of merging all `FunctionAnalysisState` instances
79/// after parallel analysis completes.
80///
81/// # Note on Array Types
82///
83/// Array types are handled by the shared `TypeInternPool` in `SemaContext`.
84/// They are no longer merged here.
85#[derive(Debug, Default)]
86pub struct MergedAnalysisState {
87    /// All string literals (deduplicated).
88    pub strings: Vec<String>,
89    /// Mapping from string content to final index.
90    pub string_map: HashMap<String, u32>,
91    /// All warnings from all functions.
92    pub warnings: Vec<CompileWarning>,
93}
94
95impl MergedAnalysisState {
96    /// Create a new empty merged state.
97    pub fn new() -> Self {
98        Self::default()
99    }
100
101    /// Merge a function's analysis state into this merged state.
102    ///
103    /// Returns a remapping for string indices so the function's AIR
104    /// can be updated with the final IDs.
105    ///
106    /// # Note
107    ///
108    /// Array type merging is no longer needed.
109    /// Array types go directly to the shared `TypeInternPool`.
110    pub fn merge_function_state(&mut self, state: FunctionAnalysisState) -> AnalysisStateRemapping {
111        let mut string_remap = HashMap::new();
112
113        // Merge strings (deduplicate by content)
114        for (content, old_id) in state.string_table {
115            let new_id = if let Some(&id) = self.string_map.get(&content) {
116                id
117            } else {
118                let id = self.strings.len() as u32;
119                self.strings.push(content.clone());
120                self.string_map.insert(content, id);
121                id
122            };
123            if old_id != new_id {
124                string_remap.insert(old_id, new_id);
125            }
126        }
127
128        // Merge warnings (no deduplication needed)
129        self.warnings.extend(state.warnings);
130
131        AnalysisStateRemapping { string_remap }
132    }
133}
134
135/// Remapping information for updating AIR after merging.
136///
137/// When function analysis states are merged, IDs may change due to
138/// deduplication. This struct provides the mapping from old to new IDs.
139///
140/// # Note
141///
142/// Array type remapping is no longer needed.
143/// Array types use the shared `TypeInternPool` which handles deduplication.
144#[derive(Debug, Default)]
145pub struct AnalysisStateRemapping {
146    /// Mapping from old string index to new string index.
147    /// Only contains entries where the index changed.
148    pub string_remap: HashMap<u32, u32>,
149}
150
151impl AnalysisStateRemapping {
152    /// Check if any remapping is needed.
153    pub fn is_empty(&self) -> bool {
154        self.string_remap.is_empty()
155    }
156
157    /// Remap a string index if needed.
158    pub fn remap_string(&self, id: u32) -> u32 {
159        self.string_remap.get(&id).copied().unwrap_or(id)
160    }
161}