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;