@@ -293,7 +293,8 @@ fn (mut g FlatGen) collect_fn_gen_candidates_range(nodes []i32, start int, end i
293293 qfn := g.qualified_fn_name_in_module_c(item_module, node.value)
294294 is_program_specialization := g.is_program_specialization_fn_node_with_qfn(node,
295295 i, qfn, item_file)
296- if !g.should_emit_fn_node_in_module_known(node, item_module, item_file, qfn, is_program_specialization) {
296+ if !g.should_emit_fn_node_in_module_known(node, i, item_module, item_file, qfn,
297+ is_program_specialization) {
297298 continue
298299 }
299300 preferred_name := g.fn_c_name_in_module(item_module, node.value)
@@ -323,11 +324,13 @@ fn (g &FlatGen) fn_gen_selection_info() (DirectArrayAccessFns, DirectArrayAccess
323324 mut program_modules := map[string]bool{}
324325 mut non_program_modules := map[string]bool{}
325326 mut scan_file_is_program := false
327+ // Resolving a path is a syscall per file; there is nothing to match when no
328+ // program files are cached, and each file is resolved once for both passes.
329+ mut program_file_flags := map[string]bool{}
326330 for directive_idx in g.top_level_nodes() {
327331 directive := g.a.nodes[directive_idx]
328332 if directive.kind == .file {
329- scan_file_is_program = g.cache_program_files[directive.value]
330- || g.cache_program_files[os.real_path(directive.value)]
333+ scan_file_is_program = g.file_is_cache_program_file(directive.value, mut program_file_flags)
331334 continue
332335 }
333336 if directive.kind == .module_decl && !scan_file_is_program {
@@ -338,8 +341,7 @@ fn (g &FlatGen) fn_gen_selection_info() (DirectArrayAccessFns, DirectArrayAccess
338341 for directive_idx in g.top_level_nodes() {
339342 directive := g.a.nodes[directive_idx]
340343 if directive.kind == .file {
341- cur_file_is_program = g.cache_program_files[directive.value]
342- || g.cache_program_files[os.real_path(directive.value)]
344+ cur_file_is_program = g.file_is_cache_program_file(directive.value, mut program_file_flags)
343345 continue
344346 }
345347 if directive.kind == .module_decl {
@@ -520,6 +522,20 @@ fn (mut g FlatGen) gen_fn_items(items []FlatFnGenItem) {
520522 }
521523}
522524
525+ // file_is_cache_program_file reports whether `file`, as written or resolved,
526+ // is one of the cached program files, memoizing the answer per file.
527+ fn (g &FlatGen) file_is_cache_program_file(file string, mut memo map[string]bool) bool {
528+ if g.cache_program_files.len == 0 {
529+ return false
530+ }
531+ if known := memo[file] {
532+ return known
533+ }
534+ is_program := g.cache_program_files[file] || g.cache_program_files[os.real_path(file)]
535+ memo[file] = is_program
536+ return is_program
537+ }
538+
523539fn c_backend_fn_file_rank(file string) int {
524540 if file.ends_with('.c.v') {
525541 return 1
@@ -821,10 +837,11 @@ fn (mut g FlatGen) should_emit_fn_node_in_module(node flat.Node, node_index int,
821837 qfn := g.qualified_fn_name_in_module_c(module_name, node.value)
822838 is_program_specialization := g.is_program_specialization_fn_node_with_qfn(node, node_index,
823839 qfn, file_name)
824- return g.should_emit_fn_node_in_module_known(node, module_name, file_name, qfn, is_program_specialization)
840+ return g.should_emit_fn_node_in_module_known(node, node_index, module_name, file_name,
841+ qfn, is_program_specialization)
825842}
826843
827- fn (mut g FlatGen) should_emit_fn_node_in_module_known(node flat.Node, module_name string, file_name string, qfn string, is_program_specialization bool) bool {
844+ fn (mut g FlatGen) should_emit_fn_node_in_module_known(node flat.Node, node_index int, module_name string, file_name string, qfn string, is_program_specialization bool) bool {
828845 if g.should_rename_user_main_for_tests(module_name, node.value) {
829846 return true
830847 }
@@ -882,7 +899,7 @@ fn (mut g FlatGen) should_emit_fn_node_in_module_known(node flat.Node, module_na
882899 && g.specialization_signature_has_missing_nominal(node, module_name) {
883900 return false
884901 }
885- if g.fn_node_is_open_generic_template(node, module_name) {
902+ if g.fn_node_is_open_generic_template(node, node_index, module_name) {
886903 return false
887904 }
888905 // Every concrete specialization materialized from the combined
@@ -981,13 +998,19 @@ fn (g &FlatGen) type_has_missing_qualified_nominal(t types.Type) bool {
981998 }
982999}
9831000
984- fn (g &FlatGen) fn_node_is_open_generic_template(node flat.Node, module_name string) bool {
1001+ fn (g &FlatGen) fn_node_is_open_generic_template(node flat.Node, node_index int, module_name string) bool {
9851002 if node.generic_params().len > 0 {
9861003 return true
9871004 }
9881005 if node.value.index_u8(`.`) < 0 {
9891006 return false
9901007 }
1008+ // A monomorphized clone substitutes every type parameter, so its receiver
1009+ // arguments are concrete even when a type is spelled with one capital letter
1010+ // (`Encoder[F].encode`, specialized for a user `struct F`).
1011+ if g.a.specialized_fn_nodes[node_index] {
1012+ return false
1013+ }
9911014 receiver := node.value.all_before_last('.')
9921015 // This declaration gate must inspect the source spelling authoritatively. The
9931016 // shared expression cache can already contain a negative result for the same
@@ -13485,6 +13508,11 @@ fn (mut g FlatGen) gen_arg_for_expected_type(arg_id flat.NodeId, expected types.
1348513508 if g.gen_mut_sum_lvalue_arg(arg_id, expected) {
1348613509 return
1348713510 }
13511+ // A `mut e &T` param is `T**` in C. Transformed method calls reach here
13512+ // instead of gen_call_args, so pass the caller's slot the same way.
13513+ if g.gen_mut_pointer_slot_arg(arg_id, arg_node, expected) {
13514+ return
13515+ }
1348813516 mut needs_addr := false
1348913517 if expected is types.Pointer && !(arg_node.kind == .prefix && arg_node.op == .amp)
1349013518 && !g.arg_is_null_pointer_literal(arg_id, arg_node) {
0 commit comments