1use super::*;
2
3impl BytecodeCompiler {
4 pub(super) fn collect_namespace_import_bindings(program: &Program) -> Vec<String> {
5 use shape_ast::ast::{ImportItems, Item};
6
7 let mut bindings = Vec::new();
8 for item in &program.items {
9 if let Item::Import(import_stmt, _) = item
10 && let ImportItems::Namespace { name, alias } = &import_stmt.items
11 {
12 bindings.push(alias.clone().unwrap_or_else(|| name.clone()));
13 }
14 }
15 bindings
16 }
17
18 pub fn new() -> Self {
19 Self {
20 program: BytecodeProgram::new(),
21 current_function: None,
22 locals: vec![HashMap::new()],
23 module_bindings: HashMap::new(),
24 next_local: 0,
25 next_global: 0,
26 loop_stack: Vec::new(),
27 closure_counter: 0,
28 closure_function_ids: Vec::new(),
29 closure_registry: shape_value::v2::closure_layout::ClosureRegistry::new(),
30 closure_type_ids: Vec::new(),
31 closure_capture_kinds: Vec::new(),
32 function_type_registry:
33 shape_value::v2::function_type_registry::FunctionTypeRegistry::new(),
34 function_type_ids: Vec::new(),
35 emit_make_closure_heap_next: false,
36 get_prop_native_kinds: HashMap::new(),
37 closure_row_schema: None,
38 pending_closure_param_types: None,
39 last_expr_type_info: None,
40 type_tracker: TypeTracker::with_stdlib(),
41 last_expr_schema: None,
42 last_expr_numeric_type: None,
43 top_level_program_return_kind: None,
44 current_expr_result_mode: ExprResultMode::Value,
45 last_expr_reference_result: ExprReferenceResult::default(),
46 local_callable_pass_modes: HashMap::new(),
47 local_callable_return_reference_summaries: HashMap::new(),
48 module_binding_callable_pass_modes: HashMap::new(),
49 module_binding_callable_return_reference_summaries: HashMap::new(),
50 local_callable_return_types: HashMap::new(),
51 module_binding_callable_return_types: HashMap::new(),
52 local_array_callable_return_types: HashMap::new(),
53 module_binding_array_callable_return_types: HashMap::new(),
54 local_callable_closure_bodies: HashMap::new(),
58 module_binding_callable_closure_bodies: HashMap::new(),
59 dyn_locals: HashMap::new(),
60 dyn_module_bindings: HashMap::new(),
61 function_return_reference_summaries: HashMap::new(),
62 current_function_return_reference_summary: None,
63 type_inference: shape_runtime::type_system::inference::TypeInferenceEngine::new(),
64 type_aliases: HashMap::new(),
65 current_line: 1,
66 current_file_id: 0,
67 source_text: None,
68 source_lines: Vec::new(),
69 imported_names: HashMap::new(),
70 imported_annotations: HashMap::new(),
71 imported_consts: HashMap::new(),
72 module_builtin_functions: HashMap::new(),
73 module_namespace_bindings: HashSet::new(),
74 module_scope_sources: HashMap::new(),
75 module_scope_stack: Vec::new(),
76 known_exports: HashMap::new(),
77 function_arity_bounds: HashMap::new(),
78 function_const_params: HashMap::new(),
79 function_defs: HashMap::new(),
80 foreign_function_defs: HashMap::new(),
81 enum_struct_variant_fields: HashMap::new(),
82 enum_tuple_variant_fields: HashMap::new(),
83 const_specializations: HashMap::new(),
84 next_const_specialization_id: 0,
85 specialization_const_bindings: HashMap::new(),
86 struct_types: HashMap::new(),
87 struct_generic_info: HashMap::new(),
88 native_layout_types: HashSet::new(),
89 generated_native_conversion_pairs: HashSet::new(),
90 current_function_is_async: false,
91 source_dir: None,
92 errors: Vec::new(),
93 hoisted_fields: HashMap::new(),
94 hoisted_field_types: HashMap::new(),
95 pending_variable_name: None,
96 pending_variable_typed_array_kind: None,
97 nested_array_literal_depth: 0,
98 v2_typed_array_locals: HashMap::new(),
99 v2_typed_array_module_bindings: HashMap::new(),
100 comprehension_element_kind: None,
101 comprehension_push_sites: Vec::new(),
102 empty_array_accumulators: HashMap::new(),
103 pending_empty_array_alloc_idx: None,
104 pending_variable_typed_map_kind: None,
105 v2_typed_map_locals: HashMap::new(),
106 v2_typed_map_module_bindings: HashMap::new(),
107 mut_self_container_locals: HashMap::new(),
110 mut_self_container_bindings: HashMap::new(),
111 pending_variable_container_kind: None,
112 map_key_value_types: HashMap::new(),
113 local_map_key_value_types: HashMap::new(),
114 module_binding_map_key_value_types: HashMap::new(),
115 array_element_types: HashMap::new(),
116 local_array_element_types: HashMap::new(),
117 module_binding_array_element_types: HashMap::new(),
118 future_reference_use_name_scopes: Vec::new(),
119 known_traits: std::collections::HashSet::new(),
120 trait_defs: HashMap::new(),
121 comptime_impl_blocks: Vec::new(),
122 comptime_context_struct_defs: HashMap::new(),
123 extension_registry: None,
124 comptime_fields: HashMap::new(),
125 type_diagnostic_mode: TypeDiagnosticMode::ReliableOnly,
126 compile_diagnostic_mode: CompileDiagnosticMode::FailFast,
127 comptime_mode: false,
128 removed_functions: HashSet::new(),
129 allow_internal_comptime_namespace: false,
130 method_table: MethodTable::new(),
131 ref_locals: HashSet::new(),
132 exclusive_ref_locals: HashSet::new(),
133 inferred_ref_locals: HashSet::new(),
134 reference_value_locals: HashSet::new(),
135 exclusive_reference_value_locals: HashSet::new(),
136 const_locals: HashSet::new(),
137 const_module_bindings: HashSet::new(),
138 immutable_locals: HashSet::new(),
139 param_locals: HashSet::new(),
140 immutable_module_bindings: HashSet::new(),
141 reference_value_module_bindings: HashSet::new(),
142 exclusive_reference_value_module_bindings: HashSet::new(),
143 call_arg_module_binding_ref_writebacks: Vec::new(),
144 inferred_ref_params: HashMap::new(),
145 inferred_ref_mutates: HashMap::new(),
146 inferred_param_pass_modes: HashMap::new(),
147 inferred_param_type_hints: HashMap::new(),
148 inferred_param_concrete_types: HashMap::new(),
149 inferred_param_object_fields: HashMap::new(),
150 inferred_return_object_fields: HashMap::new(),
151 function_return_schema_ids: HashMap::new(),
152 drop_locals: Vec::new(),
153 ownership_drop_locals: Vec::new(),
154 drop_type_info: HashMap::new(),
155 drop_module_bindings: Vec::new(),
156 mutable_closure_captures: HashMap::new(),
157 shared_closure_captures: HashMap::new(),
158 owned_mutable_closure_captures: HashMap::new(),
159 owned_mutable_capture_inner_kinds: HashMap::new(),
160 shared_capture_inner_kinds: HashMap::new(),
161 boxed_locals: HashSet::new(),
162 shared_locals: HashSet::new(),
163 owned_mutable_locals: HashSet::new(),
164 captured_let_mut_moved: HashMap::new(),
165 shared_module_bindings: HashSet::new(),
166 shared_drop_locals: Vec::new(),
167 permission_set: None,
168 current_blob_builder: None,
169 completed_blobs: Vec::new(),
170 blob_name_to_hash: HashMap::new(),
171 content_addressed_program: None,
172 function_hashes_by_id: Vec::new(),
173 blob_cache: None,
174 function_aliases: HashMap::new(),
175 current_function_params: Vec::new(),
176 stdlib_function_names: HashSet::new(),
177 allow_internal_builtins: false,
178 native_resolution_context: None,
179 non_function_mir_context_stack: Vec::new(),
180 mir_functions: HashMap::new(),
181 mir_borrow_analyses: HashMap::new(),
182 mir_storage_plans: HashMap::new(),
183 function_borrow_summaries: HashMap::new(),
184 mir_span_to_point: HashMap::new(),
185 mir_field_analyses: HashMap::new(),
186 graph_namespace_map: HashMap::new(),
187 module_graph: None,
188 current_function_local_concrete_types: HashMap::new(),
189 module_binding_concrete_types: HashMap::new(),
190 monomorphization_cache:
191 crate::compiler::monomorphization::cache::MonomorphizationCache::new(),
192 monomorphization_in_progress: std::collections::HashSet::new(),
193 next_monomorphization_id: 0,
194 closure_specialization_count: 0,
195 }
196 }
197
198 pub fn set_comptime_mode(&mut self, enabled: bool) {
200 self.comptime_mode = enabled;
201 }
202
203 pub fn set_blob_cache(&mut self, cache: BlobCache) {
209 self.blob_cache = Some(cache);
210 }
211
212 pub(crate) fn finalize_current_blob(&mut self, func_idx: usize) {
218 let entry = self.program.functions[func_idx].entry_point;
220 let end = self.program.instructions.len();
221 self.program.functions[func_idx].body_length = end - entry;
222
223 if let Some(builder) = self.current_blob_builder.take() {
224 let instr_end = self.program.instructions.len();
225 let func = &self.program.functions[func_idx];
226 let blob = builder.finalize(&self.program, func, &self.blob_name_to_hash, instr_end);
227 self.blob_name_to_hash
228 .insert(blob.name.clone(), blob.content_hash);
229
230 if let Some(ref mut cache) = self.blob_cache {
232 cache.put_blob(&blob);
233 }
234
235 if self.function_hashes_by_id.len() <= func_idx {
236 self.function_hashes_by_id.resize(func_idx + 1, None);
237 }
238 self.function_hashes_by_id[func_idx] = Some(blob.content_hash);
239
240 self.completed_blobs.push(blob);
241 }
242 }
243
244 pub(crate) fn record_blob_call(&mut self, func_idx: u16) {
247 if let Some(ref mut blob) = self.current_blob_builder {
248 let callee_name = self.program.functions[func_idx as usize].name.clone();
249 blob.record_call(&callee_name);
250 }
251 }
252
253 pub(crate) fn record_blob_permissions(&mut self, module: &str, function: &str) {
256 if let Some(ref mut blob) = self.current_blob_builder {
257 let perms =
258 shape_runtime::stdlib::capability_tags::required_permissions(module, function);
259 if !perms.is_empty() {
260 blob.record_permissions(&perms);
261 }
262 }
263 }
264
265 pub(super) fn build_content_addressed_program(&mut self) {
268 use crate::bytecode::Function;
269
270 if let Some(main_builder) = self.current_blob_builder.take() {
274 let instr_end = self.program.instructions.len();
275
276 let main_func = Function {
278 name: "__main__".to_string(),
279 arity: 0,
280 param_names: Vec::new(),
281 locals_count: self.next_local,
282 entry_point: main_builder.instr_start,
283 body_length: instr_end - main_builder.instr_start,
284 is_closure: false,
285 captures_count: 0,
286 is_async: false,
287 ref_params: Vec::new(),
288 ref_mutates: Vec::new(),
289 mutable_captures: Vec::new(),
290 frame_descriptor: self.program.top_level_frame.clone(),
291 osr_entry_points: Vec::new(),
292 mir_data: None,
293 };
294
295 let blob = main_builder.finalize(
296 &self.program,
297 &main_func,
298 &self.blob_name_to_hash,
299 instr_end,
300 );
301 self.blob_name_to_hash
302 .insert("__main__".to_string(), blob.content_hash);
303 let mut main_hash = blob.content_hash;
304
305 if let Some(ref mut cache) = self.blob_cache {
307 cache.put_blob(&blob);
308 }
309
310 self.completed_blobs.push(blob);
311
312 let mut function_store = HashMap::new();
314 for blob in &self.completed_blobs {
315 function_store.insert(blob.content_hash, blob.clone());
316 }
317
318 let mut call_edges: std::collections::HashSet<(String, String)> =
331 std::collections::HashSet::new();
332 for blob in function_store.values() {
333 for callee in &blob.callee_names {
334 if callee != &blob.name {
335 call_edges.insert((blob.name.clone(), callee.clone()));
336 }
337 }
338 }
339 let mut mutual_edges: std::collections::HashSet<(String, String)> =
340 std::collections::HashSet::new();
341 for (a, b) in &call_edges {
342 if call_edges.contains(&(b.clone(), a.clone())) {
343 mutual_edges.insert((a.clone(), b.clone()));
344 }
345 }
346
347 let max_iterations = 10;
348 for _iteration in 0..max_iterations {
349 let mut any_changed = false;
350 let mut recomputed: Vec<(FunctionHash, FunctionHash, FunctionBlob)> = Vec::new();
351
352 for blob in function_store.values() {
353 let mut updated = blob.clone();
354 let mut deps_changed = false;
355
356 for (i, dep) in updated.dependencies.iter_mut().enumerate() {
357 if let Some(name) = blob.callee_names.get(i) {
358 if name == &blob.name {
362 continue;
363 }
364 if mutual_edges.contains(&(blob.name.clone(), name.clone())) {
369 if *dep != FunctionHash::ZERO {
370 *dep = FunctionHash::ZERO;
371 deps_changed = true;
372 }
373 continue;
374 }
375 if let Some(¤t) = self.blob_name_to_hash.get(name) {
376 if *dep != current {
377 *dep = current;
378 deps_changed = true;
379 }
380 }
381 }
382 }
383
384 if deps_changed {
385 let old_hash = updated.content_hash;
386 updated.finalize();
387 if updated.content_hash != old_hash {
388 recomputed.push((old_hash, updated.content_hash, updated));
389 any_changed = true;
390 }
391 }
392 }
393
394 for (old_hash, new_hash, blob) in recomputed {
395 function_store.remove(&old_hash);
396 function_store.insert(new_hash, blob.clone());
397 self.blob_name_to_hash.insert(blob.name.clone(), new_hash);
398 for slot in &mut self.function_hashes_by_id {
399 if *slot == Some(old_hash) {
400 *slot = Some(new_hash);
401 }
402 }
403
404 if let Some(ref mut cache) = self.blob_cache {
406 cache.put_blob(&blob);
407 }
408 }
409
410 if !any_changed {
411 break;
412 }
413 }
414
415 for _perm_iter in 0..max_iterations {
420 let mut perm_changed = false;
421 let mut updates: Vec<(FunctionHash, shape_abi_v1::PermissionSet)> = Vec::new();
422
423 for (hash, blob) in function_store.iter() {
424 let mut accumulated = blob.required_permissions.clone();
425 for dep_hash in &blob.dependencies {
426 if let Some(dep_blob) = function_store.get(dep_hash) {
427 let unioned = accumulated.union(&dep_blob.required_permissions);
428 if unioned != accumulated {
429 accumulated = unioned;
430 }
431 }
432 }
433 if accumulated != blob.required_permissions {
434 updates.push((*hash, accumulated));
435 perm_changed = true;
436 }
437 }
438
439 let mut rehashed: Vec<(FunctionHash, FunctionBlob)> = Vec::new();
440 for (hash, perms) in updates {
441 if let Some(blob) = function_store.get_mut(&hash) {
442 blob.required_permissions = perms;
443 let old_hash = blob.content_hash;
444 blob.finalize();
445 if blob.content_hash != old_hash {
446 rehashed.push((old_hash, blob.clone()));
447 }
448 }
449 }
450
451 for (old_hash, blob) in rehashed {
452 function_store.remove(&old_hash);
453 self.blob_name_to_hash
454 .insert(blob.name.clone(), blob.content_hash);
455 for slot in &mut self.function_hashes_by_id {
456 if *slot == Some(old_hash) {
457 *slot = Some(blob.content_hash);
458 }
459 }
460 if let Some(ref mut cache) = self.blob_cache {
461 cache.put_blob(&blob);
462 }
463 function_store.insert(blob.content_hash, blob);
464 }
465
466 if !perm_changed {
467 break;
468 }
469 }
470
471 if let Some(&updated_main) = self.blob_name_to_hash.get("__main__") {
473 main_hash = updated_main;
474 }
475
476 let mut module_binding_names = vec![String::new(); self.module_bindings.len()];
478 for (name, &idx) in &self.module_bindings {
479 module_binding_names[idx as usize] = name.clone();
480 }
481
482 self.content_addressed_program = Some(ContentAddressedProgram {
483 entry: main_hash,
484 function_store,
485 top_level_locals_count: self.next_local,
486 top_level_local_storage_hints: self.program.top_level_local_storage_hints.clone(),
487 module_binding_names,
488 module_binding_storage_hints: self.program.module_binding_storage_hints.clone(),
489 function_local_storage_hints: self.program.function_local_storage_hints.clone(),
490 data_schema: self.program.data_schema.clone(),
491 type_schema_registry: self.type_tracker.schema_registry().clone(),
492 trait_method_symbols: self.program.trait_method_symbols.clone(),
493 foreign_functions: self.program.foreign_functions.clone(),
494 native_struct_layouts: self.program.native_struct_layouts.clone(),
495 debug_info: self.program.debug_info.clone(),
496 top_level_frame: self.program.top_level_frame.clone(),
501 top_level_local_concrete_types: self
506 .program
507 .top_level_local_concrete_types
508 .clone(),
509 function_local_concrete_types: self
515 .program
516 .function_local_concrete_types
517 .clone(),
518 function_return_concrete_types: self
524 .program
525 .function_return_concrete_types
526 .clone(),
527 monomorphized_method_call_sites: self
534 .program
535 .monomorphized_method_call_sites
536 .clone(),
537 value_call_return_concrete_types: self
544 .program
545 .value_call_return_concrete_types
546 .clone(),
547 operator_trait_dispatch_sites: self
554 .program
555 .operator_trait_dispatch_sites
556 .clone(),
557 closure_function_layouts_by_name: {
563 let mut map: std::collections::HashMap<
564 String,
565 std::sync::Arc<shape_value::v2::closure_layout::ClosureLayout>,
566 > = std::collections::HashMap::new();
567 for (idx, layout_opt) in
568 self.program.closure_function_layouts.iter().enumerate()
569 {
570 if let Some(layout) = layout_opt {
571 if let Some(func) = self.program.functions.get(idx) {
572 map.insert(func.name.clone(), layout.clone());
573 }
574 }
575 }
576 map
577 },
578 trait_vtables: self.program.trait_vtables.clone(),
584 has_imported_const_inline: self.program.has_imported_const_inline,
585 has_w17_marshal_residual: self.program.has_w17_marshal_residual,
586 });
587 }
588 }
589
590 pub(crate) fn collect_comptime_helpers(&self) -> Vec<FunctionDef> {
592 let mut helpers: Vec<FunctionDef> = self
593 .function_defs
594 .values()
595 .filter(|def| def.is_comptime)
596 .cloned()
597 .collect();
598 helpers.sort_by(|a, b| a.name.cmp(&b.name));
599 helpers
600 }
601
602 pub fn register_known_export(&mut self, function_name: &str, module_path: &str) {
607 self.known_exports
608 .insert(function_name.to_string(), module_path.to_string());
609 }
610
611 pub fn register_known_exports(&mut self, exports: &HashMap<String, String>) {
613 for (name, path) in exports {
614 self.known_exports.insert(name.clone(), path.clone());
615 }
616 }
617
618 pub fn suggest_import(&self, function_name: &str) -> Option<&str> {
620 self.known_exports.get(function_name).map(|s| s.as_str())
621 }
622
623 pub fn set_source(&mut self, source: &str) {
625 self.source_text = Some(source.to_string());
626 self.source_lines = source.lines().map(|s| s.to_string()).collect();
627 }
628
629 pub fn set_source_with_file(&mut self, source: &str, file_name: &str) {
631 self.source_text = Some(source.to_string());
632 self.source_lines = source.lines().map(|s| s.to_string()).collect();
633 self.current_file_id = self
635 .program
636 .debug_info
637 .source_map
638 .add_file(file_name.to_string());
639 self.program
640 .debug_info
641 .source_map
642 .set_source_text(self.current_file_id, source.to_string());
643 }
644
645 pub fn set_line(&mut self, line: u32) {
647 self.current_line = line;
648 }
649
650 pub fn set_line_from_span(&mut self, span: shape_ast::ast::Span) {
652 if let Some(source) = &self.source_text {
653 let line = source[..span.start.min(source.len())]
655 .chars()
656 .filter(|c| *c == '\n')
657 .count() as u32
658 + 1;
659 self.current_line = line;
660 }
661 }
662
663 pub fn get_source_line(&self, line: usize) -> Option<&str> {
665 self.source_lines
666 .get(line.saturating_sub(1))
667 .map(|s| s.as_str())
668 }
669
670 pub(crate) fn span_to_source_location(
672 &self,
673 span: shape_ast::ast::Span,
674 ) -> shape_ast::error::SourceLocation {
675 let (line, column) = if let Some(source) = &self.source_text {
676 let clamped = span.start.min(source.len());
677 let line = source[..clamped].chars().filter(|c| *c == '\n').count() + 1;
678 let last_nl = source[..clamped].rfind('\n').map(|p| p + 1).unwrap_or(0);
679 let column = clamped - last_nl + 1;
680 (line, column)
681 } else {
682 (1, 1)
683 };
684 let source_line = self.source_lines.get(line.saturating_sub(1)).cloned();
685 let mut loc = shape_ast::error::SourceLocation::new(line, column);
686 if span.end > span.start {
687 loc = loc.with_length(span.end - span.start);
688 }
689 if let Some(sl) = source_line {
690 loc = loc.with_source_line(sl);
691 }
692 if let Some(file) = self
693 .program
694 .debug_info
695 .source_map
696 .get_file(self.current_file_id)
697 {
698 loc = loc.with_file(file.to_string());
699 }
700 loc
701 }
702
703 pub fn register_known_bindings(&mut self, names: &[String]) {
709 for name in names {
710 if !name.is_empty() && !self.module_bindings.contains_key(name) {
711 let idx = self.next_global;
712 self.module_bindings.insert(name.clone(), idx);
713 self.next_global += 1;
714 self.register_extension_module_schema(name);
715 let module_schema_name = format!("__mod_{}", name);
716 if self
717 .type_tracker
718 .schema_registry()
719 .get(&module_schema_name)
720 .is_some()
721 {
722 self.set_module_binding_type_info(idx, &module_schema_name);
723 self.module_namespace_bindings.insert(name.clone());
724 }
725 }
726 }
727 }
728
729 pub fn register_known_binding_type(&mut self, name: &str, type_name: &str) {
742 if let Some(&idx) = self.module_bindings.get(name) {
743 self.set_module_binding_type_info(idx, type_name);
744 }
745 }
746
747 pub fn seed_persistent_schemas(
764 &mut self,
765 schemas: &[shape_runtime::type_schema::TypeSchema],
766 ) {
767 let registry = self.type_tracker.schema_registry_mut();
768 let mut max_id = 0u32;
769 for schema in schemas {
770 max_id = max_id.max(schema.id);
771 if registry.get(&schema.name).is_none() {
772 registry.register(schema.clone());
773 }
774 }
775 if max_id > 0 {
776 registry.ensure_next_id_above(max_id);
777 }
778 }
779
780 pub fn with_schema(schema: crate::bytecode::DataFrameSchema) -> Self {
783 let mut compiler = Self::new();
784 compiler.program.data_schema = Some(schema);
785 compiler
786 }
787
788 pub fn set_schema(&mut self, schema: crate::bytecode::DataFrameSchema) {
791 self.program.data_schema = Some(schema);
792 }
793
794 pub fn set_source_dir(&mut self, dir: std::path::PathBuf) {
796 self.source_dir = Some(dir);
797 }
798
799 pub fn with_extensions(
801 mut self,
802 extensions: Vec<shape_runtime::module_exports::ModuleExports>,
803 ) -> Self {
804 self.extension_registry = Some(Arc::new(extensions));
805 self
806 }
807
808 pub fn set_type_diagnostic_mode(&mut self, mode: TypeDiagnosticMode) {
810 self.type_diagnostic_mode = mode;
811 }
812
813 pub fn set_compile_diagnostic_mode(&mut self, mode: CompileDiagnosticMode) {
815 self.compile_diagnostic_mode = mode;
816 }
817
818 pub fn set_permission_set(&mut self, permissions: Option<shape_abi_v1::PermissionSet>) {
823 self.permission_set = permissions;
824 }
825
826 pub(crate) fn should_recover_compile_diagnostics(&self) -> bool {
827 matches!(
828 self.compile_diagnostic_mode,
829 CompileDiagnosticMode::RecoverAll
830 )
831 }
832
833 pub(super) fn type_error_with_location_to_shape(error: TypeErrorWithLocation) -> ShapeError {
834 let mut location = SourceLocation::new(error.line.max(1), error.column.max(1));
835 if let Some(file) = error.file {
836 location = location.with_file(file);
837 }
838 if let Some(source_line) = error.source_line {
839 location = location.with_source_line(source_line);
840 }
841
842 ShapeError::SemanticError {
843 message: error.error.to_string(),
844 location: Some(location),
845 }
846 }
847
848 pub(super) fn type_errors_to_shape(errors: Vec<TypeErrorWithLocation>) -> ShapeError {
849 let mut mapped: Vec<ShapeError> = errors
850 .into_iter()
851 .map(Self::type_error_with_location_to_shape)
852 .collect();
853 if mapped.len() == 1 {
854 return mapped.pop().unwrap_or_else(|| ShapeError::SemanticError {
855 message: "Type analysis failed".to_string(),
856 location: None,
857 });
858 }
859 ShapeError::MultiError(mapped)
860 }
861
862 pub(super) fn should_emit_type_diagnostic(error: &TypeError) -> bool {
863 matches!(
864 error,
865 TypeError::UnknownProperty(_, _)
866 | TypeError::ComptimeMethodCallOutsideComptime { .. }
873 | TypeError::ComptimeImplTraitMismatch { .. }
874 )
875 }
876
877 pub(super) fn collect_program_functions(
878 program: &Program,
879 ) -> HashMap<String, shape_ast::ast::FunctionDef> {
880 let mut out = HashMap::new();
881 Self::collect_program_functions_recursive(&program.items, None, &mut out);
882 out
883 }
884
885 pub(super) fn collect_program_functions_recursive(
886 items: &[shape_ast::ast::Item],
887 module_prefix: Option<&str>,
888 out: &mut HashMap<String, shape_ast::ast::FunctionDef>,
889 ) {
890 for item in items {
891 match item {
892 shape_ast::ast::Item::Function(func, _) => {
893 let mut qualified = func.clone();
894 if let Some(prefix) = module_prefix {
895 qualified.name = format!("{}::{}", prefix, func.name);
896 }
897 out.insert(qualified.name.clone(), qualified);
898 }
899 shape_ast::ast::Item::Export(export, _) => {
900 if let shape_ast::ast::ExportItem::Function(func) = &export.item {
901 let mut qualified = func.clone();
902 if let Some(prefix) = module_prefix {
903 qualified.name = format!("{}::{}", prefix, func.name);
904 }
905 out.insert(qualified.name.clone(), qualified);
906 }
907 }
908 shape_ast::ast::Item::Module(module_def, _) => {
909 let prefix = if let Some(parent) = module_prefix {
910 format!("{}::{}", parent, module_def.name)
911 } else {
912 module_def.name.clone()
913 };
914 Self::collect_program_functions_recursive(
915 &module_def.items,
916 Some(prefix.as_str()),
917 out,
918 );
919 }
920 _ => {}
921 }
922 }
923 }
924
925 pub(super) fn is_primitive_value_type_name(name: &str) -> bool {
926 matches!(
927 name,
928 "int"
929 | "integer"
930 | "i64"
931 | "number"
932 | "float"
933 | "f64"
934 | "decimal"
935 | "bool"
936 | "boolean"
937 | "void"
938 | "unit"
939 | "none"
940 | "null"
941 | "undefined"
942 | "never"
943 )
944 }
945
946 pub(super) fn annotation_is_heap_like(ann: &TypeAnnotation) -> bool {
947 match ann {
948 TypeAnnotation::Basic(name) => !Self::is_primitive_value_type_name(name),
949 TypeAnnotation::Reference(name) => !Self::is_primitive_value_type_name(name),
950 TypeAnnotation::Array(_)
951 | TypeAnnotation::Tuple(_)
952 | TypeAnnotation::Object(_)
953 | TypeAnnotation::Function { .. }
954 | TypeAnnotation::Generic { .. }
955 | TypeAnnotation::Dyn(_) => true,
956 TypeAnnotation::Union(types) | TypeAnnotation::Intersection(types) => {
957 types.iter().any(Self::annotation_is_heap_like)
958 }
959 TypeAnnotation::Void
960 | TypeAnnotation::Never
961 | TypeAnnotation::Null
962 | TypeAnnotation::Undefined => false,
963 }
964 }
965
966 pub(super) fn type_is_heap_like(ty: &Type) -> bool {
967 match ty {
968 Type::Concrete(ann) => Self::annotation_is_heap_like(ann),
969 Type::Function { .. } => false,
970 Type::Generic { .. } => true,
971 Type::Variable(_) | Type::Constrained { .. } => false,
972 }
973 }
974
975 pub(crate) fn pass_mode_from_ref_flags(
976 ref_params: &[bool],
977 ref_mutates: &[bool],
978 idx: usize,
979 ) -> ParamPassMode {
980 if !ref_params.get(idx).copied().unwrap_or(false) {
981 ParamPassMode::ByValue
982 } else if ref_mutates.get(idx).copied().unwrap_or(false) {
983 ParamPassMode::ByRefExclusive
984 } else {
985 ParamPassMode::ByRefShared
986 }
987 }
988
989 pub(crate) fn pass_modes_from_ref_flags(
990 ref_params: &[bool],
991 ref_mutates: &[bool],
992 ) -> Vec<ParamPassMode> {
993 let len = ref_params.len().max(ref_mutates.len());
994 (0..len)
995 .map(|idx| Self::pass_mode_from_ref_flags(ref_params, ref_mutates, idx))
996 .collect()
997 }
998
999 pub(crate) fn build_param_pass_mode_map(
1000 program: &Program,
1001 inferred_ref_params: &HashMap<String, Vec<bool>>,
1002 inferred_ref_mutates: &HashMap<String, Vec<bool>>,
1003 ) -> HashMap<String, Vec<ParamPassMode>> {
1004 let funcs = Self::collect_program_functions(program);
1005 let mut by_function = HashMap::new();
1006
1007 for (name, func) in funcs {
1008 let inferred_refs = inferred_ref_params.get(&name).cloned().unwrap_or_default();
1009 let inferred_mutates = inferred_ref_mutates.get(&name).cloned().unwrap_or_default();
1010 let mut modes = Vec::with_capacity(func.params.len());
1011
1012 for (idx, param) in func.params.iter().enumerate() {
1013 let explicit_ref = param.is_reference;
1014 let inferred_ref = inferred_refs.get(idx).copied().unwrap_or(false);
1015 if !(explicit_ref || inferred_ref) {
1016 modes.push(ParamPassMode::ByValue);
1017 continue;
1018 }
1019
1020 if inferred_mutates.get(idx).copied().unwrap_or(false) {
1021 modes.push(ParamPassMode::ByRefExclusive);
1022 } else {
1023 modes.push(ParamPassMode::ByRefShared);
1024 }
1025 }
1026
1027 by_function.insert(name, modes);
1028 }
1029
1030 by_function
1031 }
1032}