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_row_schema: None,
29 last_expr_type_info: None,
30 type_tracker: TypeTracker::with_stdlib(),
31 last_expr_schema: None,
32 last_expr_numeric_type: None,
33 current_expr_result_mode: ExprResultMode::Value,
34 last_expr_reference_result: ExprReferenceResult::default(),
35 local_callable_pass_modes: HashMap::new(),
36 local_callable_return_reference_summaries: HashMap::new(),
37 module_binding_callable_pass_modes: HashMap::new(),
38 module_binding_callable_return_reference_summaries: HashMap::new(),
39 function_return_reference_summaries: HashMap::new(),
40 current_function_return_reference_summary: None,
41 type_inference: shape_runtime::type_system::inference::TypeInferenceEngine::new(),
42 type_aliases: HashMap::new(),
43 current_line: 1,
44 current_file_id: 0,
45 source_text: None,
46 source_lines: Vec::new(),
47 imported_names: HashMap::new(),
48 imported_annotations: HashMap::new(),
49 module_builtin_functions: HashMap::new(),
50 module_namespace_bindings: HashSet::new(),
51 module_scope_sources: HashMap::new(),
52 module_scope_stack: Vec::new(),
53 known_exports: HashMap::new(),
54 function_arity_bounds: HashMap::new(),
55 function_const_params: HashMap::new(),
56 function_defs: HashMap::new(),
57 foreign_function_defs: HashMap::new(),
58 const_specializations: HashMap::new(),
59 next_const_specialization_id: 0,
60 specialization_const_bindings: HashMap::new(),
61 struct_types: HashMap::new(),
62 struct_generic_info: HashMap::new(),
63 native_layout_types: HashSet::new(),
64 generated_native_conversion_pairs: HashSet::new(),
65 current_function_is_async: false,
66 source_dir: None,
67 errors: Vec::new(),
68 hoisted_fields: HashMap::new(),
69 pending_variable_name: None,
70 future_reference_use_name_scopes: Vec::new(),
71 known_traits: std::collections::HashSet::new(),
72 trait_defs: HashMap::new(),
73 extension_registry: None,
74 comptime_fields: HashMap::new(),
75 type_diagnostic_mode: TypeDiagnosticMode::ReliableOnly,
76 compile_diagnostic_mode: CompileDiagnosticMode::FailFast,
77 comptime_mode: false,
78 removed_functions: HashSet::new(),
79 allow_internal_comptime_namespace: false,
80 method_table: MethodTable::new(),
81 ref_locals: HashSet::new(),
82 exclusive_ref_locals: HashSet::new(),
83 inferred_ref_locals: HashSet::new(),
84 reference_value_locals: HashSet::new(),
85 exclusive_reference_value_locals: HashSet::new(),
86 const_locals: HashSet::new(),
87 const_module_bindings: HashSet::new(),
88 immutable_locals: HashSet::new(),
89 param_locals: HashSet::new(),
90 immutable_module_bindings: HashSet::new(),
91 reference_value_module_bindings: HashSet::new(),
92 exclusive_reference_value_module_bindings: HashSet::new(),
93 call_arg_module_binding_ref_writebacks: Vec::new(),
94 inferred_ref_params: HashMap::new(),
95 inferred_ref_mutates: HashMap::new(),
96 inferred_param_pass_modes: HashMap::new(),
97 inferred_param_type_hints: HashMap::new(),
98 drop_locals: Vec::new(),
99 drop_type_info: HashMap::new(),
100 drop_module_bindings: Vec::new(),
101 mutable_closure_captures: HashMap::new(),
102 boxed_locals: HashSet::new(),
103 permission_set: None,
104 current_blob_builder: None,
105 completed_blobs: Vec::new(),
106 blob_name_to_hash: HashMap::new(),
107 content_addressed_program: None,
108 function_hashes_by_id: Vec::new(),
109 blob_cache: None,
110 function_aliases: HashMap::new(),
111 current_function_params: Vec::new(),
112 stdlib_function_names: HashSet::new(),
113 allow_internal_builtins: false,
114 native_resolution_context: None,
115 non_function_mir_context_stack: Vec::new(),
116 mir_functions: HashMap::new(),
117 mir_borrow_analyses: HashMap::new(),
118 mir_storage_plans: HashMap::new(),
119 function_borrow_summaries: HashMap::new(),
120 mir_span_to_point: HashMap::new(),
121 mir_field_analyses: HashMap::new(),
122 graph_namespace_map: HashMap::new(),
123 module_graph: None,
124 }
125 }
126
127 pub fn set_comptime_mode(&mut self, enabled: bool) {
129 self.comptime_mode = enabled;
130 }
131
132 pub fn set_blob_cache(&mut self, cache: BlobCache) {
138 self.blob_cache = Some(cache);
139 }
140
141 pub(crate) fn finalize_current_blob(&mut self, func_idx: usize) {
147 let entry = self.program.functions[func_idx].entry_point;
149 let end = self.program.instructions.len();
150 self.program.functions[func_idx].body_length = end - entry;
151
152 if let Some(builder) = self.current_blob_builder.take() {
153 let instr_end = self.program.instructions.len();
154 let func = &self.program.functions[func_idx];
155 let blob = builder.finalize(&self.program, func, &self.blob_name_to_hash, instr_end);
156 self.blob_name_to_hash
157 .insert(blob.name.clone(), blob.content_hash);
158
159 if let Some(ref mut cache) = self.blob_cache {
161 cache.put_blob(&blob);
162 }
163
164 if self.function_hashes_by_id.len() <= func_idx {
165 self.function_hashes_by_id.resize(func_idx + 1, None);
166 }
167 self.function_hashes_by_id[func_idx] = Some(blob.content_hash);
168
169 self.completed_blobs.push(blob);
170 }
171 }
172
173 pub(crate) fn record_blob_call(&mut self, func_idx: u16) {
176 if let Some(ref mut blob) = self.current_blob_builder {
177 let callee_name = self.program.functions[func_idx as usize].name.clone();
178 blob.record_call(&callee_name);
179 }
180 }
181
182 pub(crate) fn record_blob_permissions(&mut self, module: &str, function: &str) {
185 if let Some(ref mut blob) = self.current_blob_builder {
186 let perms =
187 shape_runtime::stdlib::capability_tags::required_permissions(module, function);
188 if !perms.is_empty() {
189 blob.record_permissions(&perms);
190 }
191 }
192 }
193
194 pub(super) fn build_content_addressed_program(&mut self) {
197 use crate::bytecode::Function;
198
199 if let Some(main_builder) = self.current_blob_builder.take() {
203 let instr_end = self.program.instructions.len();
204
205 let main_func = Function {
207 name: "__main__".to_string(),
208 arity: 0,
209 param_names: Vec::new(),
210 locals_count: self.next_local,
211 entry_point: main_builder.instr_start,
212 body_length: instr_end - main_builder.instr_start,
213 is_closure: false,
214 captures_count: 0,
215 is_async: false,
216 ref_params: Vec::new(),
217 ref_mutates: Vec::new(),
218 mutable_captures: Vec::new(),
219 frame_descriptor: self.program.top_level_frame.clone(),
220 osr_entry_points: Vec::new(),
221 };
222
223 let blob = main_builder.finalize(
224 &self.program,
225 &main_func,
226 &self.blob_name_to_hash,
227 instr_end,
228 );
229 self.blob_name_to_hash
230 .insert("__main__".to_string(), blob.content_hash);
231 let mut main_hash = blob.content_hash;
232
233 if let Some(ref mut cache) = self.blob_cache {
235 cache.put_blob(&blob);
236 }
237
238 self.completed_blobs.push(blob);
239
240 let mut function_store = HashMap::new();
242 for blob in &self.completed_blobs {
243 function_store.insert(blob.content_hash, blob.clone());
244 }
245
246 let mut call_edges: std::collections::HashSet<(String, String)> =
259 std::collections::HashSet::new();
260 for blob in function_store.values() {
261 for callee in &blob.callee_names {
262 if callee != &blob.name {
263 call_edges.insert((blob.name.clone(), callee.clone()));
264 }
265 }
266 }
267 let mut mutual_edges: std::collections::HashSet<(String, String)> =
268 std::collections::HashSet::new();
269 for (a, b) in &call_edges {
270 if call_edges.contains(&(b.clone(), a.clone())) {
271 mutual_edges.insert((a.clone(), b.clone()));
272 }
273 }
274
275 let max_iterations = 10;
276 for _iteration in 0..max_iterations {
277 let mut any_changed = false;
278 let mut recomputed: Vec<(FunctionHash, FunctionHash, FunctionBlob)> = Vec::new();
279
280 for blob in function_store.values() {
281 let mut updated = blob.clone();
282 let mut deps_changed = false;
283
284 for (i, dep) in updated.dependencies.iter_mut().enumerate() {
285 if let Some(name) = blob.callee_names.get(i) {
286 if name == &blob.name {
290 continue;
291 }
292 if mutual_edges.contains(&(blob.name.clone(), name.clone())) {
297 if *dep != FunctionHash::ZERO {
298 *dep = FunctionHash::ZERO;
299 deps_changed = true;
300 }
301 continue;
302 }
303 if let Some(¤t) = self.blob_name_to_hash.get(name) {
304 if *dep != current {
305 *dep = current;
306 deps_changed = true;
307 }
308 }
309 }
310 }
311
312 if deps_changed {
313 let old_hash = updated.content_hash;
314 updated.finalize();
315 if updated.content_hash != old_hash {
316 recomputed.push((old_hash, updated.content_hash, updated));
317 any_changed = true;
318 }
319 }
320 }
321
322 for (old_hash, new_hash, blob) in recomputed {
323 function_store.remove(&old_hash);
324 function_store.insert(new_hash, blob.clone());
325 self.blob_name_to_hash.insert(blob.name.clone(), new_hash);
326 for slot in &mut self.function_hashes_by_id {
327 if *slot == Some(old_hash) {
328 *slot = Some(new_hash);
329 }
330 }
331
332 if let Some(ref mut cache) = self.blob_cache {
334 cache.put_blob(&blob);
335 }
336 }
337
338 if !any_changed {
339 break;
340 }
341 }
342
343 for _perm_iter in 0..max_iterations {
348 let mut perm_changed = false;
349 let mut updates: Vec<(FunctionHash, shape_abi_v1::PermissionSet)> = Vec::new();
350
351 for (hash, blob) in function_store.iter() {
352 let mut accumulated = blob.required_permissions.clone();
353 for dep_hash in &blob.dependencies {
354 if let Some(dep_blob) = function_store.get(dep_hash) {
355 let unioned = accumulated.union(&dep_blob.required_permissions);
356 if unioned != accumulated {
357 accumulated = unioned;
358 }
359 }
360 }
361 if accumulated != blob.required_permissions {
362 updates.push((*hash, accumulated));
363 perm_changed = true;
364 }
365 }
366
367 let mut rehashed: Vec<(FunctionHash, FunctionBlob)> = Vec::new();
368 for (hash, perms) in updates {
369 if let Some(blob) = function_store.get_mut(&hash) {
370 blob.required_permissions = perms;
371 let old_hash = blob.content_hash;
372 blob.finalize();
373 if blob.content_hash != old_hash {
374 rehashed.push((old_hash, blob.clone()));
375 }
376 }
377 }
378
379 for (old_hash, blob) in rehashed {
380 function_store.remove(&old_hash);
381 self.blob_name_to_hash
382 .insert(blob.name.clone(), blob.content_hash);
383 for slot in &mut self.function_hashes_by_id {
384 if *slot == Some(old_hash) {
385 *slot = Some(blob.content_hash);
386 }
387 }
388 if let Some(ref mut cache) = self.blob_cache {
389 cache.put_blob(&blob);
390 }
391 function_store.insert(blob.content_hash, blob);
392 }
393
394 if !perm_changed {
395 break;
396 }
397 }
398
399 if let Some(&updated_main) = self.blob_name_to_hash.get("__main__") {
401 main_hash = updated_main;
402 }
403
404 let mut module_binding_names = vec![String::new(); self.module_bindings.len()];
406 for (name, &idx) in &self.module_bindings {
407 module_binding_names[idx as usize] = name.clone();
408 }
409
410 self.content_addressed_program = Some(ContentAddressedProgram {
411 entry: main_hash,
412 function_store,
413 top_level_locals_count: self.next_local,
414 top_level_local_storage_hints: self.program.top_level_local_storage_hints.clone(),
415 module_binding_names,
416 module_binding_storage_hints: self.program.module_binding_storage_hints.clone(),
417 function_local_storage_hints: self.program.function_local_storage_hints.clone(),
418 data_schema: self.program.data_schema.clone(),
419 type_schema_registry: self.type_tracker.schema_registry().clone(),
420 trait_method_symbols: self.program.trait_method_symbols.clone(),
421 foreign_functions: self.program.foreign_functions.clone(),
422 native_struct_layouts: self.program.native_struct_layouts.clone(),
423 debug_info: self.program.debug_info.clone(),
424 top_level_frame: None,
425 });
426 }
427 }
428
429 pub(crate) fn collect_comptime_helpers(&self) -> Vec<FunctionDef> {
431 let mut helpers: Vec<FunctionDef> = self
432 .function_defs
433 .values()
434 .filter(|def| def.is_comptime)
435 .cloned()
436 .collect();
437 helpers.sort_by(|a, b| a.name.cmp(&b.name));
438 helpers
439 }
440
441 pub fn register_known_export(&mut self, function_name: &str, module_path: &str) {
446 self.known_exports
447 .insert(function_name.to_string(), module_path.to_string());
448 }
449
450 pub fn register_known_exports(&mut self, exports: &HashMap<String, String>) {
452 for (name, path) in exports {
453 self.known_exports.insert(name.clone(), path.clone());
454 }
455 }
456
457 pub fn suggest_import(&self, function_name: &str) -> Option<&str> {
459 self.known_exports.get(function_name).map(|s| s.as_str())
460 }
461
462 pub fn set_source(&mut self, source: &str) {
464 self.source_text = Some(source.to_string());
465 self.source_lines = source.lines().map(|s| s.to_string()).collect();
466 }
467
468 pub fn set_source_with_file(&mut self, source: &str, file_name: &str) {
470 self.source_text = Some(source.to_string());
471 self.source_lines = source.lines().map(|s| s.to_string()).collect();
472 self.current_file_id = self
474 .program
475 .debug_info
476 .source_map
477 .add_file(file_name.to_string());
478 self.program
479 .debug_info
480 .source_map
481 .set_source_text(self.current_file_id, source.to_string());
482 }
483
484 pub fn set_line(&mut self, line: u32) {
486 self.current_line = line;
487 }
488
489 pub fn set_line_from_span(&mut self, span: shape_ast::ast::Span) {
491 if let Some(source) = &self.source_text {
492 let line = source[..span.start.min(source.len())]
494 .chars()
495 .filter(|c| *c == '\n')
496 .count() as u32
497 + 1;
498 self.current_line = line;
499 }
500 }
501
502 pub fn get_source_line(&self, line: usize) -> Option<&str> {
504 self.source_lines
505 .get(line.saturating_sub(1))
506 .map(|s| s.as_str())
507 }
508
509 pub(crate) fn span_to_source_location(
511 &self,
512 span: shape_ast::ast::Span,
513 ) -> shape_ast::error::SourceLocation {
514 let (line, column) = if let Some(source) = &self.source_text {
515 let clamped = span.start.min(source.len());
516 let line = source[..clamped].chars().filter(|c| *c == '\n').count() + 1;
517 let last_nl = source[..clamped].rfind('\n').map(|p| p + 1).unwrap_or(0);
518 let column = clamped - last_nl + 1;
519 (line, column)
520 } else {
521 (1, 1)
522 };
523 let source_line = self.source_lines.get(line.saturating_sub(1)).cloned();
524 let mut loc = shape_ast::error::SourceLocation::new(line, column);
525 if span.end > span.start {
526 loc = loc.with_length(span.end - span.start);
527 }
528 if let Some(sl) = source_line {
529 loc = loc.with_source_line(sl);
530 }
531 if let Some(file) = self
532 .program
533 .debug_info
534 .source_map
535 .get_file(self.current_file_id)
536 {
537 loc = loc.with_file(file.to_string());
538 }
539 loc
540 }
541
542 pub fn register_known_bindings(&mut self, names: &[String]) {
548 for name in names {
549 if !name.is_empty() && !self.module_bindings.contains_key(name) {
550 let idx = self.next_global;
551 self.module_bindings.insert(name.clone(), idx);
552 self.next_global += 1;
553 self.register_extension_module_schema(name);
554 let module_schema_name = format!("__mod_{}", name);
555 if self
556 .type_tracker
557 .schema_registry()
558 .get(&module_schema_name)
559 .is_some()
560 {
561 self.set_module_binding_type_info(idx, &module_schema_name);
562 self.module_namespace_bindings.insert(name.clone());
563 }
564 }
565 }
566 }
567
568 pub fn with_schema(schema: crate::bytecode::DataFrameSchema) -> Self {
571 let mut compiler = Self::new();
572 compiler.program.data_schema = Some(schema);
573 compiler
574 }
575
576 pub fn set_schema(&mut self, schema: crate::bytecode::DataFrameSchema) {
579 self.program.data_schema = Some(schema);
580 }
581
582 pub fn set_source_dir(&mut self, dir: std::path::PathBuf) {
584 self.source_dir = Some(dir);
585 }
586
587 pub fn with_extensions(
589 mut self,
590 extensions: Vec<shape_runtime::module_exports::ModuleExports>,
591 ) -> Self {
592 self.extension_registry = Some(Arc::new(extensions));
593 self
594 }
595
596 pub fn set_type_diagnostic_mode(&mut self, mode: TypeDiagnosticMode) {
598 self.type_diagnostic_mode = mode;
599 }
600
601 pub fn set_compile_diagnostic_mode(&mut self, mode: CompileDiagnosticMode) {
603 self.compile_diagnostic_mode = mode;
604 }
605
606 pub fn set_permission_set(&mut self, permissions: Option<shape_abi_v1::PermissionSet>) {
611 self.permission_set = permissions;
612 }
613
614 pub(crate) fn should_recover_compile_diagnostics(&self) -> bool {
615 matches!(
616 self.compile_diagnostic_mode,
617 CompileDiagnosticMode::RecoverAll
618 )
619 }
620
621 pub(super) fn type_error_with_location_to_shape(error: TypeErrorWithLocation) -> ShapeError {
622 let mut location = SourceLocation::new(error.line.max(1), error.column.max(1));
623 if let Some(file) = error.file {
624 location = location.with_file(file);
625 }
626 if let Some(source_line) = error.source_line {
627 location = location.with_source_line(source_line);
628 }
629
630 ShapeError::SemanticError {
631 message: error.error.to_string(),
632 location: Some(location),
633 }
634 }
635
636 pub(super) fn type_errors_to_shape(errors: Vec<TypeErrorWithLocation>) -> ShapeError {
637 let mut mapped: Vec<ShapeError> = errors
638 .into_iter()
639 .map(Self::type_error_with_location_to_shape)
640 .collect();
641 if mapped.len() == 1 {
642 return mapped.pop().unwrap_or_else(|| ShapeError::SemanticError {
643 message: "Type analysis failed".to_string(),
644 location: None,
645 });
646 }
647 ShapeError::MultiError(mapped)
648 }
649
650 pub(super) fn should_emit_type_diagnostic(error: &TypeError) -> bool {
651 matches!(error, TypeError::UnknownProperty(_, _))
652 }
653
654 pub(super) fn collect_program_functions(
655 program: &Program,
656 ) -> HashMap<String, shape_ast::ast::FunctionDef> {
657 let mut out = HashMap::new();
658 Self::collect_program_functions_recursive(&program.items, None, &mut out);
659 out
660 }
661
662 pub(super) fn collect_program_functions_recursive(
663 items: &[shape_ast::ast::Item],
664 module_prefix: Option<&str>,
665 out: &mut HashMap<String, shape_ast::ast::FunctionDef>,
666 ) {
667 for item in items {
668 match item {
669 shape_ast::ast::Item::Function(func, _) => {
670 let mut qualified = func.clone();
671 if let Some(prefix) = module_prefix {
672 qualified.name = format!("{}::{}", prefix, func.name);
673 }
674 out.insert(qualified.name.clone(), qualified);
675 }
676 shape_ast::ast::Item::Export(export, _) => {
677 if let shape_ast::ast::ExportItem::Function(func) = &export.item {
678 let mut qualified = func.clone();
679 if let Some(prefix) = module_prefix {
680 qualified.name = format!("{}::{}", prefix, func.name);
681 }
682 out.insert(qualified.name.clone(), qualified);
683 }
684 }
685 shape_ast::ast::Item::Module(module_def, _) => {
686 let prefix = if let Some(parent) = module_prefix {
687 format!("{}::{}", parent, module_def.name)
688 } else {
689 module_def.name.clone()
690 };
691 Self::collect_program_functions_recursive(
692 &module_def.items,
693 Some(prefix.as_str()),
694 out,
695 );
696 }
697 _ => {}
698 }
699 }
700 }
701
702 pub(super) fn is_primitive_value_type_name(name: &str) -> bool {
703 matches!(
704 name,
705 "int"
706 | "integer"
707 | "i64"
708 | "number"
709 | "float"
710 | "f64"
711 | "decimal"
712 | "bool"
713 | "boolean"
714 | "void"
715 | "unit"
716 | "none"
717 | "null"
718 | "undefined"
719 | "never"
720 )
721 }
722
723 pub(super) fn annotation_is_heap_like(ann: &TypeAnnotation) -> bool {
724 match ann {
725 TypeAnnotation::Basic(name) => !Self::is_primitive_value_type_name(name),
726 TypeAnnotation::Reference(name) => !Self::is_primitive_value_type_name(name),
727 TypeAnnotation::Array(_)
728 | TypeAnnotation::Tuple(_)
729 | TypeAnnotation::Object(_)
730 | TypeAnnotation::Function { .. }
731 | TypeAnnotation::Generic { .. }
732 | TypeAnnotation::Dyn(_) => true,
733 TypeAnnotation::Union(types) | TypeAnnotation::Intersection(types) => {
734 types.iter().any(Self::annotation_is_heap_like)
735 }
736 TypeAnnotation::Void
737 | TypeAnnotation::Never
738 | TypeAnnotation::Null
739 | TypeAnnotation::Undefined => false,
740 }
741 }
742
743 pub(super) fn type_is_heap_like(ty: &Type) -> bool {
744 match ty {
745 Type::Concrete(ann) => Self::annotation_is_heap_like(ann),
746 Type::Function { .. } => false,
747 Type::Generic { .. } => true,
748 Type::Variable(_) | Type::Constrained { .. } => false,
749 }
750 }
751
752 pub(crate) fn pass_mode_from_ref_flags(
753 ref_params: &[bool],
754 ref_mutates: &[bool],
755 idx: usize,
756 ) -> ParamPassMode {
757 if !ref_params.get(idx).copied().unwrap_or(false) {
758 ParamPassMode::ByValue
759 } else if ref_mutates.get(idx).copied().unwrap_or(false) {
760 ParamPassMode::ByRefExclusive
761 } else {
762 ParamPassMode::ByRefShared
763 }
764 }
765
766 pub(crate) fn pass_modes_from_ref_flags(
767 ref_params: &[bool],
768 ref_mutates: &[bool],
769 ) -> Vec<ParamPassMode> {
770 let len = ref_params.len().max(ref_mutates.len());
771 (0..len)
772 .map(|idx| Self::pass_mode_from_ref_flags(ref_params, ref_mutates, idx))
773 .collect()
774 }
775
776 pub(crate) fn build_param_pass_mode_map(
777 program: &Program,
778 inferred_ref_params: &HashMap<String, Vec<bool>>,
779 inferred_ref_mutates: &HashMap<String, Vec<bool>>,
780 ) -> HashMap<String, Vec<ParamPassMode>> {
781 let funcs = Self::collect_program_functions(program);
782 let mut by_function = HashMap::new();
783
784 for (name, func) in funcs {
785 let inferred_refs = inferred_ref_params.get(&name).cloned().unwrap_or_default();
786 let inferred_mutates = inferred_ref_mutates.get(&name).cloned().unwrap_or_default();
787 let mut modes = Vec::with_capacity(func.params.len());
788
789 for (idx, param) in func.params.iter().enumerate() {
790 let explicit_ref = param.is_reference;
791 let inferred_ref = inferred_refs.get(idx).copied().unwrap_or(false);
792 if !(explicit_ref || inferred_ref) {
793 modes.push(ParamPassMode::ByValue);
794 continue;
795 }
796
797 if inferred_mutates.get(idx).copied().unwrap_or(false) {
798 modes.push(ParamPassMode::ByRefExclusive);
799 } else {
800 modes.push(ParamPassMode::ByRefShared);
801 }
802 }
803
804 by_function.insert(name, modes);
805 }
806
807 by_function
808 }
809}