Skip to main content

shape_vm/compiler/
mod.rs

1//! Bytecode compiler - translates AST to bytecode
2
3use shape_ast::error::{Result, ShapeError, SourceLocation};
4use std::collections::{HashMap, HashSet};
5use std::sync::Arc;
6
7use crate::blob_cache_v2::BlobCache;
8use crate::borrow_checker::BorrowMode;
9use crate::bytecode::{
10    BytecodeProgram, Constant, FunctionBlob, FunctionHash, Instruction, OpCode,
11    Program as ContentAddressedProgram,
12};
13use crate::type_tracking::{TypeTracker, VariableTypeInfo};
14use shape_ast::ast::{FunctionDef, Program, TypeAnnotation};
15use shape_runtime::type_schema::SchemaId;
16use shape_runtime::type_system::{
17    Type, TypeAnalysisMode, TypeError, TypeErrorWithLocation, analyze_program_with_mode,
18    checking::MethodTable,
19};
20
21// Sub-modules
22pub(crate) mod comptime;
23pub(crate) mod comptime_builtins;
24pub(crate) mod comptime_target;
25mod control_flow;
26mod expressions;
27mod functions;
28mod helpers;
29mod literals;
30mod loops;
31mod patterns;
32mod statements;
33pub mod string_interpolation;
34
35/// Loop compilation context
36pub(crate) struct LoopContext {
37    /// Break jump targets
38    pub(crate) break_jumps: Vec<usize>,
39    /// Continue jump target
40    pub(crate) continue_target: usize,
41    /// Optional local to store break values for expression loops
42    pub(crate) break_value_local: Option<u16>,
43    /// Whether a for-in iterator is on the stack (break must pop it)
44    pub(crate) iterator_on_stack: bool,
45    /// Drop scope depth when the loop was entered (for break/continue early exit drops)
46    pub(crate) drop_scope_depth: usize,
47}
48
49/// Information about an imported symbol (fields used for diagnostics/LSP)
50#[derive(Debug, Clone)]
51#[allow(dead_code)]
52pub(crate) struct ImportedSymbol {
53    /// Original name in the source module
54    pub original_name: String,
55    /// Module path the symbol was imported from
56    pub module_path: String,
57}
58
59#[derive(Debug, Clone)]
60pub(crate) struct StructGenericInfo {
61    pub type_params: Vec<shape_ast::ast::TypeParam>,
62    pub runtime_field_types: HashMap<String, shape_ast::ast::TypeAnnotation>,
63}
64
65/// Whether a type's Drop impl is sync-only, async-only, or both.
66#[derive(Debug, Clone, Copy, PartialEq, Eq)]
67pub(crate) enum DropKind {
68    SyncOnly,
69    AsyncOnly,
70    Both,
71}
72
73/// Canonical compile-time parameter passing contract.
74///
75/// This is the single source of truth used by compiler lowering and LSP rendering.
76#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
77pub enum ParamPassMode {
78    ByValue,
79    ByRefShared,
80    ByRefExclusive,
81}
82
83impl ParamPassMode {
84    pub const fn is_reference(self) -> bool {
85        !matches!(self, Self::ByValue)
86    }
87
88    pub const fn is_exclusive(self) -> bool {
89        matches!(self, Self::ByRefExclusive)
90    }
91}
92
93/// Per-function blob builder for content-addressed compilation.
94///
95/// Uses a **snapshot** strategy: records the global instruction/constant/string
96/// pool sizes at the start of function compilation, then at finalization
97/// extracts the delta and remaps global indices to blob-local indices.
98pub(crate) struct FunctionBlobBuilder {
99    /// Function name.
100    pub name: String,
101    /// Global instruction index where this function's code starts.
102    pub instr_start: usize,
103    /// Global constant pool size when this function started compiling.
104    #[allow(dead_code)]
105    pub const_start: usize,
106    /// Global string pool size when this function started compiling.
107    #[allow(dead_code)]
108    pub string_start: usize,
109    /// Names of functions called by this function (for dependency tracking).
110    pub called_functions: Vec<String>,
111    /// Type schema names this function constructs.
112    pub type_schemas: Vec<String>,
113    /// Accumulated permissions required by this function's direct calls.
114    pub required_permissions: shape_abi_v1::PermissionSet,
115}
116
117impl FunctionBlobBuilder {
118    pub fn new(name: String, instr_start: usize, const_start: usize, string_start: usize) -> Self {
119        Self {
120            name,
121            instr_start,
122            const_start,
123            string_start,
124            called_functions: Vec::new(),
125            type_schemas: Vec::new(),
126            required_permissions: shape_abi_v1::PermissionSet::pure(),
127        }
128    }
129
130    /// Record that this function calls another function by name.
131    pub fn record_call(&mut self, callee_name: &str) {
132        if !self.called_functions.iter().any(|n| n == callee_name) {
133            self.called_functions.push(callee_name.to_owned());
134        }
135    }
136
137    /// Record that this function requires the given permissions
138    /// (e.g., from a stdlib module call identified by capability_tags).
139    pub fn record_permissions(&mut self, perms: &shape_abi_v1::PermissionSet) {
140        self.required_permissions = self.required_permissions.union(perms);
141    }
142
143    /// Finalize this builder into a FunctionBlob by extracting the delta from
144    /// the global program pools and remapping indices to blob-local ones.
145    pub fn finalize(
146        &self,
147        program: &crate::bytecode::BytecodeProgram,
148        func: &crate::bytecode::Function,
149        blob_name_to_hash: &HashMap<String, FunctionHash>,
150        instr_end: usize,
151    ) -> FunctionBlob {
152        use crate::bytecode::Operand;
153
154        // Extract global-indexed instructions for this function.
155        let global_instructions = &program.instructions[self.instr_start..instr_end];
156
157        // Build constant remap: global index -> local index.
158        let mut const_remap: HashMap<u16, u16> = HashMap::new();
159        let mut local_constants: Vec<Constant> = Vec::new();
160        // Build string remap similarly.
161        let mut string_remap: HashMap<u16, u16> = HashMap::new();
162        let mut local_strings: Vec<String> = Vec::new();
163        // Build function operand remap: global function index -> dependency-local index.
164        let mut func_remap: HashMap<u16, u16> = HashMap::new();
165        // Start from explicitly recorded call dependencies, then augment with
166        // function-value references found in constants/operands.
167        let mut called_functions = self.called_functions.clone();
168
169        let mut ensure_called = |callee_name: &str| -> u16 {
170            if let Some(dep_idx) = called_functions.iter().position(|n| n == callee_name) {
171                dep_idx as u16
172            } else {
173                called_functions.push(callee_name.to_owned());
174                (called_functions.len() - 1) as u16
175            }
176        };
177
178        // Scan instructions for all constant/string references and build
179        // blob-local pools with remapped indices.
180        for instr in global_instructions {
181            if let Some(ref operand) = instr.operand {
182                match operand {
183                    Operand::Const(idx) => {
184                        if !const_remap.contains_key(idx) {
185                            let local_idx = local_constants.len() as u16;
186                            const_remap.insert(*idx, local_idx);
187                            let mut constant = program.constants[*idx as usize].clone();
188                            if let Constant::Function(fid) = constant {
189                                let global_idx = fid as usize;
190                                if let Some(callee) = program.functions.get(global_idx) {
191                                    let dep_idx = ensure_called(&callee.name);
192                                    constant = Constant::Function(dep_idx);
193                                }
194                            }
195                            local_constants.push(constant);
196                        }
197                    }
198                    Operand::Property(idx) => {
199                        if !string_remap.contains_key(idx) {
200                            let local_idx = local_strings.len() as u16;
201                            string_remap.insert(*idx, local_idx);
202                            local_strings.push(program.strings[*idx as usize].clone());
203                        }
204                    }
205                    Operand::Name(sid) => {
206                        let gidx = sid.0 as u16;
207                        if !string_remap.contains_key(&gidx) {
208                            let local_idx = local_strings.len() as u16;
209                            string_remap.insert(gidx, local_idx);
210                            local_strings.push(program.strings[gidx as usize].clone());
211                        }
212                    }
213                    Operand::MethodCall { name, .. } => {
214                        let gidx = name.0 as u16;
215                        if !string_remap.contains_key(&gidx) {
216                            let local_idx = local_strings.len() as u16;
217                            string_remap.insert(gidx, local_idx);
218                            local_strings.push(program.strings[gidx as usize].clone());
219                        }
220                    }
221                    Operand::TypedMethodCall { string_id, .. } => {
222                        let gidx = *string_id;
223                        if !string_remap.contains_key(&gidx) {
224                            let local_idx = local_strings.len() as u16;
225                            string_remap.insert(gidx, local_idx);
226                            local_strings.push(program.strings[gidx as usize].clone());
227                        }
228                    }
229                    Operand::Function(fid) => {
230                        let global_idx = fid.0 as usize;
231                        if !func_remap.contains_key(&fid.0) {
232                            // Map global function index -> dependency-local index.
233                            // If this call target was not explicitly recorded (e.g. emitted via
234                            // function-valued constants), add it so content-addressed linking can
235                            // remap stable function IDs correctly.
236                            if let Some(callee) = program.functions.get(global_idx) {
237                                let dep_idx = ensure_called(&callee.name);
238                                func_remap.insert(fid.0, dep_idx);
239                            }
240                        }
241                    }
242                    _ => {}
243                }
244            }
245        }
246
247        // Remap instructions to use local indices.
248        let local_instructions: Vec<Instruction> = global_instructions
249            .iter()
250            .map(|instr| {
251                let mut remapped = instr.clone();
252                if let Some(operand) = &mut remapped.operand {
253                    match operand {
254                        Operand::Const(idx) => {
255                            if let Some(&local) = const_remap.get(idx) {
256                                *idx = local;
257                            }
258                        }
259                        Operand::Property(idx) => {
260                            if let Some(&local) = string_remap.get(idx) {
261                                *idx = local;
262                            }
263                        }
264                        Operand::Name(sid) => {
265                            if let Some(&local) = string_remap.get(&(sid.0 as u16)) {
266                                sid.0 = local as u32;
267                            }
268                        }
269                        Operand::MethodCall { name, arg_count: _ } => {
270                            if let Some(&local) = string_remap.get(&(name.0 as u16)) {
271                                name.0 = local as u32;
272                            }
273                        }
274                        Operand::TypedMethodCall { string_id, .. } => {
275                            if let Some(&local) = string_remap.get(string_id) {
276                                *string_id = local;
277                            }
278                        }
279                        Operand::Function(fid) => {
280                            if let Some(&local) = func_remap.get(&fid.0) {
281                                fid.0 = local;
282                            }
283                        }
284                        _ => {}
285                    }
286                }
287                remapped
288            })
289            .collect();
290
291        // Build dependency list from called function names.
292        // Use FunctionHash::ZERO as sentinel for forward references (not yet compiled).
293        let dependencies: Vec<FunctionHash> = called_functions
294            .iter()
295            .map(|callee| {
296                blob_name_to_hash
297                    .get(callee)
298                    .copied()
299                    .unwrap_or(FunctionHash::ZERO)
300            })
301            .collect();
302
303        // Build source map from global debug info.
304        let source_map: Vec<(usize, u32, u32)> = program
305            .debug_info
306            .line_numbers
307            .iter()
308            .filter(|(idx, _, _)| *idx >= self.instr_start && *idx < instr_end)
309            .map(|(idx, fid, line)| (idx - self.instr_start, *fid as u32, *line))
310            .collect();
311
312        // Scan instructions for CallForeign operands and collect content hashes
313        // from the program's foreign_functions table.
314        let mut foreign_deps: Vec<[u8; 32]> = Vec::new();
315        for instr in &local_instructions {
316            if instr.opcode == crate::bytecode::OpCode::CallForeign {
317                if let Some(Operand::ForeignFunction(idx)) = instr.operand {
318                    if let Some(entry) = program.foreign_functions.get(idx as usize) {
319                        if let Some(hash) = entry.content_hash {
320                            foreign_deps.push(hash);
321                        }
322                    }
323                }
324            }
325        }
326        foreign_deps.sort();
327        foreign_deps.dedup();
328
329        let mut blob = FunctionBlob {
330            content_hash: FunctionHash::ZERO,
331            name: self.name.clone(),
332            arity: func.arity,
333            param_names: func.param_names.clone(),
334            locals_count: func.locals_count,
335            is_closure: func.is_closure,
336            captures_count: func.captures_count,
337            is_async: func.is_async,
338            ref_params: func.ref_params.clone(),
339            ref_mutates: func.ref_mutates.clone(),
340            mutable_captures: func.mutable_captures.clone(),
341            required_permissions: self.required_permissions.clone(),
342            instructions: local_instructions,
343            constants: local_constants,
344            strings: local_strings,
345            dependencies,
346            callee_names: called_functions,
347            type_schemas: self.type_schemas.clone(),
348            foreign_dependencies: foreign_deps,
349            source_map,
350        };
351        blob.finalize();
352        blob
353    }
354}
355
356#[derive(Debug, Clone, Copy, PartialEq, Eq)]
357pub enum TypeDiagnosticMode {
358    ReliableOnly,
359    Strict,
360    RecoverAll,
361}
362
363#[derive(Debug, Clone, Copy, PartialEq, Eq)]
364pub enum CompileDiagnosticMode {
365    FailFast,
366    RecoverAll,
367}
368
369/// Compiler state
370pub struct BytecodeCompiler {
371    /// The program being built
372    pub(crate) program: BytecodeProgram,
373
374    /// Current function being compiled
375    pub(crate) current_function: Option<usize>,
376
377    /// Local variable mappings (name -> index)
378    pub(crate) locals: Vec<HashMap<String, u16>>,
379
380    /// ModuleBinding variable mappings (name -> index)
381    pub(crate) module_bindings: HashMap<String, u16>,
382
383    /// Next local variable index
384    pub(crate) next_local: u16,
385
386    /// Next module_binding variable index
387    pub(crate) next_global: u16,
388
389    /// Loop context stack for break/continue
390    pub(crate) loop_stack: Vec<LoopContext>,
391
392    /// Counter for synthetic closure function names
393    pub(crate) closure_counter: u64,
394
395    /// When compiling a DataTable closure method (e.g. dt.filter(row => ...)),
396    /// this holds the (schema_id, type_name) to tag the closure's row parameter as RowView.
397    pub(crate) closure_row_schema: Option<(u32, String)>,
398
399    /// Unified type metadata for the last compiled expression.
400    ///
401    /// This is the single source for relational/value kind propagation
402    /// (Table<T>, Indexed<T>, known object schema, etc.).
403    pub(crate) last_expr_type_info: Option<VariableTypeInfo>,
404
405    /// Type tracker for optimized field access
406    pub(crate) type_tracker: TypeTracker,
407
408    /// Schema ID of the last compiled expression (if it's a TypedObject).
409    /// Used for compile-time typed merge optimization.
410    pub(crate) last_expr_schema: Option<SchemaId>,
411
412    /// Numeric type of the last compiled expression (for typed opcode emission).
413    /// Set by literal compilation, variable loads, and other expression compilers.
414    /// Read by binary op compilation to emit typed opcodes (e.g., MulInt).
415    pub(crate) last_expr_numeric_type: Option<crate::type_tracking::NumericType>,
416
417    /// Type inference engine for match exhaustiveness and type checking
418    pub(crate) type_inference: shape_runtime::type_system::inference::TypeInferenceEngine,
419
420    /// Track type aliases defined in the program
421    /// Maps alias name -> target type (for type validation)
422    pub(crate) type_aliases: HashMap<String, String>,
423
424    /// Current source line being compiled (for debug info)
425    pub(crate) current_line: u32,
426
427    /// Current source file ID (for multi-file debug info)
428    pub(crate) current_file_id: u16,
429
430    /// Source text (for error messages)
431    pub(crate) source_text: Option<String>,
432
433    /// Source lines (split from source_text for quick access)
434    pub(crate) source_lines: Vec<String>,
435
436    /// Imported symbols: local_name -> ImportedSymbol
437    pub(crate) imported_names: HashMap<String, ImportedSymbol>,
438    /// Module namespace bindings introduced by `use module.path`.
439    /// Used to avoid UFCS rewrites for module calls like `duckdb.connect(...)`.
440    pub(crate) module_namespace_bindings: HashSet<String>,
441    /// Active lexical module scope stack while compiling `mod Name { ... }`.
442    pub(crate) module_scope_stack: Vec<String>,
443
444    /// Known exports for import suggestions: function_name -> module_path
445    /// Used to provide helpful error messages like "Did you mean to import from...?"
446    pub(crate) known_exports: HashMap<String, String>,
447    /// Function arity bounds keyed by function name: (required_params, total_params).
448    /// Required params are non-default parameters. Defaults are only allowed
449    /// in trailing positions.
450    pub(crate) function_arity_bounds: HashMap<String, (usize, usize)>,
451    /// Function const parameter indices keyed by function name.
452    /// Const parameters must receive compile-time constant arguments at call sites.
453    pub(crate) function_const_params: HashMap<String, Vec<usize>>,
454    /// Original function definitions keyed by function name.
455    /// Used for const-template specialization at call sites.
456    pub(crate) function_defs: HashMap<String, FunctionDef>,
457    /// Foreign function definitions keyed by function name.
458    /// Used to resolve the effective (Result-wrapped) return type at call sites.
459    pub(crate) foreign_function_defs: HashMap<String, shape_ast::ast::ForeignFunctionDef>,
460    /// Cached const specializations keyed by `(base_name + const-arg fingerprint)`.
461    pub(crate) const_specializations: HashMap<String, usize>,
462    /// Monotonic counter for unique specialization symbol names.
463    pub(crate) next_const_specialization_id: u64,
464    /// Const-parameter bindings for specialized function symbols.
465    /// These bindings are exposed to comptime handlers as typed module_bindings.
466    pub(crate) specialization_const_bindings:
467        HashMap<String, Vec<(String, shape_value::ValueWord)>>,
468
469    /// Struct type definitions: type_name -> (field_names in order, definition span)
470    pub(crate) struct_types: HashMap<String, (Vec<String>, shape_ast::ast::Span)>,
471    /// Generic metadata for struct types used to instantiate runtime type names
472    /// (e.g. `MyType<number>`) at struct-literal construction sites.
473    pub(crate) struct_generic_info: HashMap<String, StructGenericInfo>,
474    /// Names of `type C` declarations with native layout metadata.
475    pub(crate) native_layout_types: HashSet<String>,
476    /// Generated conversion pair cache keys: `c_type::object_type`.
477    pub(crate) generated_native_conversion_pairs: HashSet<String>,
478
479    /// Whether the current function being compiled is async
480    pub(crate) current_function_is_async: bool,
481
482    /// Directory of the source file being compiled (for resolving relative source paths)
483    pub(crate) source_dir: Option<std::path::PathBuf>,
484
485    /// Collected compilation errors (for multi-error reporting)
486    pub(crate) errors: Vec<shape_ast::error::ShapeError>,
487
488    /// Hoisted fields from optimistic hoisting pre-pass.
489    /// Maps variable name → list of property names assigned later (e.g., a.y = 2 → "a" → ["y"]).
490    /// Used to include future property assignments in inline object schemas at compile time.
491    pub(crate) hoisted_fields: HashMap<String, Vec<String>>,
492
493    /// When compiling a variable initializer, the name of the variable being assigned to.
494    /// Used by compile_typed_object_literal to include hoisted fields in the schema.
495    pub(crate) pending_variable_name: Option<String>,
496
497    /// Known trait names (populated in the first pass so meta definitions can reference traits)
498    pub(crate) known_traits: std::collections::HashSet<String>,
499
500    /// Full trait definitions keyed by trait name.
501    /// Used to install default method implementations for impl blocks that omit them.
502    pub(crate) trait_defs: HashMap<String, shape_ast::ast::types::TraitDef>,
503
504    /// Extension registry for comptime execution
505    pub(crate) extension_registry: Option<Arc<Vec<shape_runtime::module_exports::ModuleExports>>>,
506
507    /// Comptime field values per type: type_name -> (field_name -> ValueWord)
508    /// These are type-level constants baked at compile time with zero runtime cost.
509    pub(crate) comptime_fields: HashMap<String, HashMap<String, shape_value::ValueWord>>,
510    /// Type diagnostic mode for shared analyzer diagnostics.
511    pub(crate) type_diagnostic_mode: TypeDiagnosticMode,
512    /// Expression compilation diagnostic mode.
513    pub(crate) compile_diagnostic_mode: CompileDiagnosticMode,
514    /// Whether this compiler instance is compiling code for comptime execution.
515    /// Enables comptime-only builtins and comptime-specific statement semantics.
516    pub(crate) comptime_mode: bool,
517    /// Internal guard for compiler-synthesized `__comptime__` helper calls.
518    /// User source must never access `__comptime__` directly.
519    pub(crate) allow_internal_comptime_namespace: bool,
520    /// Method table for data-driven method signature queries.
521    /// Used to replace hardcoded heuristics (e.g., is_type_preserving_table_method)
522    /// with MethodTable lookups (is_self_returning, takes_closure_with_receiver_param).
523    pub(crate) method_table: MethodTable,
524    /// Borrow checker for reference lifetime tracking.
525    pub(crate) borrow_checker: crate::borrow_checker::BorrowChecker,
526    /// Locals that are reference-typed in the current function.
527    pub(crate) ref_locals: HashSet<u16>,
528    /// Subset of ref_locals that hold exclusive (`&mut`) borrows.
529    /// Used to enforce the three concurrency rules at task boundaries.
530    pub(crate) exclusive_ref_locals: HashSet<u16>,
531    /// Local variable indices declared as `const` (immutable binding).
532    pub(crate) const_locals: HashSet<u16>,
533    /// Module binding indices declared as `const` (immutable binding).
534    pub(crate) const_module_bindings: HashSet<u16>,
535    /// Local variable indices declared as immutable `let` (not `let mut` or `var`).
536    pub(crate) immutable_locals: HashSet<u16>,
537    /// Local variable indices that are function parameters (first N locals in a function).
538    /// Used to avoid trusting inferred type hints for params with no explicit annotation.
539    pub(crate) param_locals: HashSet<u16>,
540    /// Module binding indices declared as immutable `let`.
541    pub(crate) immutable_module_bindings: HashSet<u16>,
542    /// True while compiling function call arguments (allows `&` references).
543    pub(crate) in_call_args: bool,
544    /// Borrow mode for the argument currently being compiled.
545    pub(crate) current_call_arg_borrow_mode: Option<BorrowMode>,
546    /// ModuleBinding-ref writebacks collected while compiling current call args.
547    pub(crate) call_arg_module_binding_ref_writebacks: Vec<Vec<(u16, u16)>>,
548    /// Inferred reference parameters for untyped params: function -> per-param flag.
549    pub(crate) inferred_ref_params: HashMap<String, Vec<bool>>,
550    /// Inferred mutating-reference params: function -> per-param flag.
551    pub(crate) inferred_ref_mutates: HashMap<String, Vec<bool>>,
552    /// Effective per-parameter pass mode (explicit + inferred), by function name.
553    pub(crate) inferred_param_pass_modes: HashMap<String, Vec<ParamPassMode>>,
554    /// Inferred parameter type hints for unannotated params.
555    /// Keyed by function name; each entry is a per-param optional type string.
556    pub(crate) inferred_param_type_hints: HashMap<String, Vec<Option<String>>>,
557    /// Stack of scopes, each containing locals that need Drop calls at scope exit.
558    /// Each entry is (local_index, is_async).
559    pub(crate) drop_locals: Vec<Vec<(u16, bool)>>,
560    /// Per-type drop kind: tracks whether each type has sync, async, or both drop impls.
561    /// Populated during the first-pass registration of impl blocks.
562    pub(crate) drop_type_info: HashMap<String, DropKind>,
563    /// Mutable closure captures in the current function being compiled.
564    /// Maps captured variable name -> upvalue index (for LoadClosure/StoreClosure).
565    /// Only populated while compiling a closure body that has mutable captures.
566    pub(crate) mutable_closure_captures: HashMap<String, u16>,
567
568    /// Variables in the current scope that have been boxed into SharedCells
569    /// by a mutable closure capture. When a subsequent closure captures one
570    /// of these variables (even immutably), it must use the SharedCell path
571    /// so it shares the same mutable cell.
572    pub(crate) boxed_locals: HashSet<String>,
573
574    /// Active permission set for capability checking.
575    ///
576    /// When set, imported stdlib functions are checked against capability_tags.
577    /// If a function requires a permission not in this set, a compile error is
578    /// emitted and the function never enters bytecode.
579    ///
580    /// `None` means no checking (backwards-compatible default).
581    pub(crate) permission_set: Option<shape_abi_v1::PermissionSet>,
582
583    // -- Content-addressed blob tracking --
584    /// Active blob builder (set while compiling a function body).
585    pub(crate) current_blob_builder: Option<FunctionBlobBuilder>,
586    /// Completed function blobs (finalized with content hash).
587    pub(crate) completed_blobs: Vec<FunctionBlob>,
588    /// Map from function name to content hash (populated after finalization).
589    pub(crate) blob_name_to_hash: HashMap<String, FunctionHash>,
590    /// The content-addressed program produced alongside BytecodeProgram.
591    pub(crate) content_addressed_program: Option<ContentAddressedProgram>,
592    /// Content hash per compiled function index (function_id -> blob hash).
593    /// This is the stable identity bridge for the flat runtime format.
594    pub(crate) function_hashes_by_id: Vec<Option<FunctionHash>>,
595
596    /// Optional blob-level cache for incremental compilation.
597    /// When set, compiled blobs are stored after finalization and looked up
598    /// by content hash to avoid redundant work across compilations.
599    pub(crate) blob_cache: Option<BlobCache>,
600
601    /// Temporary function name aliases for comptime replace body.
602    /// Maps alias (e.g., `__original__`) to actual function name (e.g., `__original__myFunc`).
603    /// Set before compiling a replacement body and cleared after.
604    pub(crate) function_aliases: HashMap<String, String>,
605
606    /// Parameters of the function currently being compiled.
607    /// Used by match exhaustiveness checking to fall back to type annotations
608    /// when the type inference engine cannot resolve a parameter's type.
609    pub(crate) current_function_params: Vec<shape_ast::ast::FunctionParameter>,
610}
611
612impl Default for BytecodeCompiler {
613    fn default() -> Self {
614        Self::new()
615    }
616}
617
618mod compiler_impl_part1;
619mod compiler_impl_part2;
620mod compiler_impl_part3;
621mod compiler_impl_part4;
622
623/// Infer effective reference parameters and mutation behavior without compiling bytecode.
624///
625/// Returns `(inferred_ref_params, inferred_ref_mutates)` keyed by function name.
626/// - `inferred_ref_params[f][i] == true` means parameter `i` of `f` is inferred/treated as ref.
627/// - `inferred_ref_mutates[f][i] == true` means that reference parameter is mutating (`&mut`).
628pub fn infer_reference_model(
629    program: &Program,
630) -> (HashMap<String, Vec<bool>>, HashMap<String, Vec<bool>>) {
631    let (inferred_ref_params, inferred_ref_mutates, _) =
632        BytecodeCompiler::infer_reference_model(program);
633    (inferred_ref_params, inferred_ref_mutates)
634}
635
636/// Infer effective parameter pass modes (`ByValue` / `ByRefShared` / `ByRefExclusive`)
637/// keyed by function name.
638pub fn infer_param_pass_modes(program: &Program) -> HashMap<String, Vec<ParamPassMode>> {
639    let (inferred_ref_params, inferred_ref_mutates, _) =
640        BytecodeCompiler::infer_reference_model(program);
641    BytecodeCompiler::build_param_pass_mode_map(
642        program,
643        &inferred_ref_params,
644        &inferred_ref_mutates,
645    )
646}
647
648#[cfg(test)]
649#[path = "compiler_tests.rs"]
650mod tests;
651
652#[cfg(test)]
653#[path = "borrow_deep_tests.rs"]
654mod borrow_deep_tests;