gruel_air/sema/mod.rs
1//! Semantic analysis - RIR to AIR conversion.
2//!
3//! Sema performs type checking and converts untyped RIR to typed AIR.
4//! This is analogous to Zig's Sema phase.
5//!
6//! # Module Organization
7//!
8//! This module is split into several submodules for maintainability:
9//!
10//! - [`context`] - Analysis context and helper types (LocalVar, AnalysisContext, etc.)
11//! - [`declarations`] - Declaration gathering (register_type_names, resolve_declarations)
12//! - [`builtins`] - Built-in type injection (String, etc.)
13//! - [`typeck`] - Type resolution and checking helpers
14//! - [`analysis`] - Function analysis and type inference coordination
15//! - [`airgen`] - RIR instruction to AIR instruction lowering
16//! - [`info`] - Function, method, and constant info types
17//! - [`gather`] - Declaration gathering output
18//! - [`output`] - Semantic analysis output types
19//! - [`inference_ctx`] - Pre-computed type information for inference
20//! - [`visibility`] - Module visibility checking
21//! - [`imports`] - Import resolution and const evaluation
22//! - [`anon_structs`] - Anonymous struct structural equality
23//! - [`anon_enums`] - Anonymous enum structural equality
24//! - [`sema_ctx_builder`] - SemaContext builder for parallel analysis
25//! - [`file_paths`] - File path management for multi-file compilation
26//!
27//! The main entry points are:
28//! - [`Sema::new`] - Create a new semantic analyzer
29//! - [`Sema::analyze_all`] - Perform full semantic analysis
30//! - [`Sema::analyze_all_bodies`] - Analyze function bodies after declarations
31
32mod airgen;
33mod analysis;
34mod analyze_ops;
35mod anon_enums;
36mod anon_structs;
37mod builtins;
38mod context;
39mod declarations;
40mod file_paths;
41mod gather;
42mod imports;
43mod inference_ctx;
44mod info;
45mod known_symbols;
46mod module_path;
47mod output;
48mod sema_ctx_builder;
49mod typeck;
50mod visibility;
51
52// Public re-exports
53pub use context::ConstValue;
54pub use gather::GatherOutput;
55pub use inference_ctx::InferenceContext;
56pub use info::{AnonMethodSig, ConstInfo, FunctionInfo, MethodInfo};
57pub use known_symbols::KnownSymbols;
58pub use output::{AnalyzedFunction, SemaOutput};
59
60use std::collections::HashMap;
61
62use gruel_error::{CompileErrors, MultiErrorResult, PreviewFeatures};
63use gruel_rir::Rir;
64use gruel_span::FileId;
65use lasso::{Spur, ThreadedRodeo};
66
67use crate::intern_pool::TypeInternPool;
68use crate::param_arena::ParamArena;
69use crate::types::{EnumId, StructId, Type};
70
71use context::ComptimeHeapItem;
72
73/// Semantic analyzer that converts RIR to AIR.
74pub struct Sema<'a> {
75 pub(crate) rir: &'a Rir,
76 pub(crate) interner: &'a ThreadedRodeo,
77 /// Function table: maps function name symbols to their info
78 pub(crate) functions: HashMap<Spur, FunctionInfo>,
79 /// Struct table: maps struct name symbols to their StructId
80 pub(crate) structs: HashMap<Spur, StructId>,
81 /// Enum table: maps enum name symbols to their EnumId
82 pub(crate) enums: HashMap<Spur, EnumId>,
83 /// Method table: maps (struct_id, method_name) to method info
84 pub(crate) methods: HashMap<(StructId, Spur), MethodInfo>,
85 /// Enum method table: maps (enum_id, method_name) to method info
86 pub(crate) enum_methods: HashMap<(EnumId, Spur), MethodInfo>,
87 /// Constant table: maps const name symbol to const info
88 pub(crate) constants: HashMap<Spur, ConstInfo>,
89 /// Enabled preview features
90 pub(crate) preview_features: PreviewFeatures,
91 /// StructId of the synthetic String type.
92 pub(crate) builtin_string_id: Option<StructId>,
93 /// EnumId of the synthetic Arch enum (for @target_arch intrinsic).
94 pub(crate) builtin_arch_id: Option<EnumId>,
95 /// EnumId of the synthetic Os enum (for @target_os intrinsic).
96 pub(crate) builtin_os_id: Option<EnumId>,
97 /// EnumId of the synthetic TypeKind enum (for @typeInfo intrinsic).
98 pub(crate) builtin_typekind_id: Option<EnumId>,
99 /// Pre-interned known symbols for fast comparison.
100 pub(crate) known: KnownSymbols,
101 /// Type intern pool for unified type representation (ADR-0024 Phase 1).
102 pub(crate) type_pool: TypeInternPool,
103 /// Module registry for tracking imported modules (Phase 1 modules).
104 pub(crate) module_registry: crate::sema_context::ModuleRegistry,
105 /// Maps FileId to source file paths (for module resolution).
106 pub(crate) file_paths: HashMap<FileId, String>,
107 /// Arena storage for function/method parameter data.
108 pub(crate) param_arena: ParamArena,
109 /// Method signatures for anonymous structs, used for structural equality comparison.
110 pub(crate) anon_struct_method_sigs: HashMap<StructId, Vec<AnonMethodSig>>,
111 /// Captured comptime values for anonymous structs.
112 /// When an anonymous struct with methods is created inside a comptime function,
113 /// the comptime parameter values (e.g., N=42 in FixedBuffer(comptime N: i32)) are
114 /// stored here, keyed by StructId. These values become part of type identity:
115 /// FixedBuffer(42) and FixedBuffer(100) are different types.
116 pub(crate) anon_struct_captured_values: HashMap<StructId, HashMap<Spur, ConstValue>>,
117 /// Method signatures for anonymous enums, used for structural equality comparison.
118 pub(crate) anon_enum_method_sigs: HashMap<EnumId, Vec<AnonMethodSig>>,
119 /// Captured comptime values for anonymous enums (same semantics as anonymous structs).
120 pub(crate) anon_enum_captured_values: HashMap<EnumId, HashMap<Spur, ConstValue>>,
121 /// Loop iteration counter for the current comptime block evaluation.
122 /// Reset to 0 at the start of each `evaluate_comptime_block` call.
123 /// Incremented once per loop iteration; triggers an error when it exceeds
124 /// `COMPTIME_MAX_STEPS` to prevent infinite loops at compile time.
125 pub(crate) comptime_steps_used: u64,
126 /// Pending return value for the comptime interpreter.
127 /// Set by `Ret` instructions inside comptime function bodies; consumed
128 /// immediately by the enclosing `Call` handler in `evaluate_comptime_inst`.
129 pub(crate) comptime_return_value: Option<ConstValue>,
130 /// Current call stack depth in the comptime interpreter.
131 /// Incremented on each comptime `Call`, decremented on return.
132 /// Triggers an error if it exceeds `COMPTIME_CALL_DEPTH_LIMIT`.
133 pub(crate) comptime_call_depth: u32,
134 /// Comptime heap: stores composite values (structs, arrays) created during
135 /// comptime evaluation. `ConstValue::Struct(idx)` and `ConstValue::Array(idx)`
136 /// index into this vec. Cleared at the start of each `evaluate_comptime_block`.
137 pub(crate) comptime_heap: Vec<ComptimeHeapItem>,
138 /// Type overrides for the comptime interpreter during generic function calls.
139 /// When a comptime generic call is executing, type parameters are stored here
140 /// so that enum/struct resolution can find them. Checked before `ctx.comptime_type_vars`.
141 pub(crate) comptime_type_overrides: HashMap<Spur, Type>,
142 /// Buffer for `@dbg` output collected during comptime evaluation.
143 /// Each entry is one formatted line (without trailing newline), matching
144 /// the format of the runtime `__gruel_dbg_*` functions.
145 pub(crate) comptime_dbg_output: Vec<String>,
146 /// Pending warnings for comptime `@dbg` calls. Each entry is (message, span).
147 pub(crate) comptime_log_output: Vec<(String, gruel_span::Span)>,
148 /// When true, comptime `@dbg` does not print to stderr on-the-fly. The output
149 /// is still appended to `comptime_dbg_output` and a warning is still emitted.
150 /// Set by the `--capture-comptime-dbg` CLI flag (used by the fuzzer).
151 pub(crate) suppress_comptime_dbg_print: bool,
152}
153
154impl<'a> Sema<'a> {
155 /// Create a new semantic analyzer.
156 pub fn new(
157 rir: &'a Rir,
158 interner: &'a ThreadedRodeo,
159 preview_features: PreviewFeatures,
160 ) -> Self {
161 Self {
162 rir,
163 interner,
164 functions: HashMap::new(),
165 structs: HashMap::new(),
166 enums: HashMap::new(),
167 methods: HashMap::new(),
168 enum_methods: HashMap::new(),
169 constants: HashMap::new(),
170 preview_features,
171 builtin_string_id: None,
172 builtin_arch_id: None,
173 builtin_os_id: None,
174 builtin_typekind_id: None,
175 known: KnownSymbols::new(interner),
176 type_pool: TypeInternPool::new(),
177 module_registry: crate::sema_context::ModuleRegistry::new(),
178 file_paths: HashMap::new(),
179 param_arena: ParamArena::new(),
180 anon_struct_method_sigs: HashMap::new(),
181 anon_struct_captured_values: HashMap::new(),
182 anon_enum_method_sigs: HashMap::new(),
183 anon_enum_captured_values: HashMap::new(),
184 comptime_steps_used: 0,
185 comptime_return_value: None,
186 comptime_call_depth: 0,
187 comptime_heap: Vec::new(),
188 comptime_type_overrides: HashMap::new(),
189 comptime_dbg_output: Vec::new(),
190 comptime_log_output: Vec::new(),
191 suppress_comptime_dbg_print: false,
192 }
193 }
194
195 /// Configure whether comptime `@dbg` prints to stderr on-the-fly.
196 /// When suppressed, output is still buffered into `comptime_dbg_output`
197 /// and warnings are still emitted.
198 pub fn set_suppress_comptime_dbg_print(&mut self, suppress: bool) {
199 self.suppress_comptime_dbg_print = suppress;
200 }
201
202 /// Perform semantic analysis on the RIR.
203 ///
204 /// This is the main entry point for semantic analysis. It returns analyzed
205 /// functions, struct definitions, enum definitions, and any warnings.
206 pub fn analyze_all(mut self) -> MultiErrorResult<SemaOutput> {
207 // Phase 0: Inject built-in types (String, etc.) before user code
208 self.inject_builtin_types();
209
210 // Phase 1: Register type names
211 // Phase 2: Resolve all declarations
212 self.register_type_names().map_err(CompileErrors::from)?;
213 self.resolve_declarations().map_err(CompileErrors::from)?;
214
215 // Phase 2.5: Evaluate const initializers (e.g., const x = @import(...))
216 self.evaluate_const_initializers()
217 .map_err(CompileErrors::from)?;
218
219 // Delegate to the analysis module for function body analysis
220 analysis::analyze_all_function_bodies(self)
221 }
222
223 /// Analyze all function bodies, assuming declarations are already collected.
224 pub fn analyze_all_bodies(self) -> MultiErrorResult<SemaOutput> {
225 analysis::analyze_all_function_bodies(self)
226 }
227}
228
229#[cfg(test)]
230mod consistency_tests;
231#[cfg(test)]
232mod tests;