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}