vxx2 / vlib / v / gen / c / fn.v
7673 lines · 7504 sloc · 263.2 KB · 24120e7a80d798a1625a08d5cc02b96e17148bdb
Raw
1// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4module c
5
6import strings
7import v.ast
8import v.util
9
10// has[int]() => has_T_int()
11// has[int, string]() => has_T_int_string()
12const c_fn_name_escape_seq = ['[', '_T_', ', ', '_', ']', '']
13
14fn collect_function_defer_stmts(node &ast.FnDecl) []ast.DeferStmt {
15 mut defer_stmts := []ast.DeferStmt{cap: node.defer_stmts.len}
16 for defer_stmt in node.defer_stmts {
17 if defer_stmt.mode == .function {
18 defer_stmts << defer_stmt
19 }
20 }
21 return defer_stmts
22}
23
24fn (g &Gen) method_decl_fkey(method ast.Fn) string {
25 if method.source_fn != unsafe { nil } {
26 fndecl := unsafe { &ast.FnDecl(method.source_fn) }
27 return fndecl.fkey()
28 }
29 return method.fkey()
30}
31
32fn (g &Gen) receiver_exact_method_for_type(typ ast.Type, method_name string) ?ast.Fn {
33 if typ == 0 {
34 return none
35 }
36 sym := g.table.sym(typ)
37 // A `mut` receiver is stored as a pointer (`&T`), so compare the base types
38 // (ignoring the pointer level) to match such a method against both the value
39 // and pointer forms of `typ`. See #27465.
40 typ_base := typ.set_nr_muls(0)
41 for method in sym.methods {
42 if method.name == method_name && method.receiver_type.set_nr_muls(0) == typ_base {
43 return method
44 }
45 }
46 if sym.info is ast.Alias {
47 parent_type := sym.info.parent_type.derive(typ)
48 parent_base := parent_type.set_nr_muls(0)
49 parent_sym := g.table.sym(parent_type)
50 for method in parent_sym.methods {
51 if method.name == method_name && method.receiver_type.set_nr_muls(0) == parent_base {
52 return method
53 }
54 }
55 }
56 return none
57}
58
59fn (g &Gen) generic_parent_method_fkey(sym ast.TypeSymbol, method_name string) string {
60 match sym.info {
61 ast.Struct, ast.Interface, ast.SumType {
62 if sym.info.parent_type.has_flag(.generic) {
63 parent_sym := g.table.sym(sym.info.parent_type)
64 if method := parent_sym.find_method(method_name) {
65 return g.method_decl_fkey(method)
66 }
67 }
68 }
69 ast.GenericInst {
70 if sym.info.parent_idx > 0 {
71 parent_sym := g.table.sym(ast.idx_to_type(sym.info.parent_idx))
72 if method := parent_sym.find_method(method_name) {
73 return g.method_decl_fkey(method)
74 }
75 }
76 }
77 else {}
78 }
79
80 return ''
81}
82
83fn (g &Gen) resolve_method_decl_fkey_for_type(typ ast.Type, method_name string) string {
84 mut candidate_types := []ast.Type{}
85 for candidate in [typ] {
86 if candidate == 0 {
87 continue
88 }
89 if candidate !in candidate_types {
90 candidate_types << candidate
91 }
92 unaliased := g.table.unaliased_type(candidate)
93 if unaliased != 0 && unaliased != candidate && unaliased !in candidate_types {
94 candidate_types << unaliased
95 }
96 if !candidate.is_ptr() {
97 ref_typ := candidate.ref()
98 if ref_typ != 0 && ref_typ !in candidate_types {
99 candidate_types << ref_typ
100 }
101 }
102 if candidate.is_ptr() {
103 deref_typ := candidate.deref()
104 if deref_typ != 0 && deref_typ !in candidate_types {
105 candidate_types << deref_typ
106 }
107 }
108 }
109 for candidate in candidate_types {
110 sym := g.table.sym(candidate)
111 parent_fkey := g.generic_parent_method_fkey(sym, method_name)
112 if parent_fkey != '' {
113 return parent_fkey
114 }
115 if method := sym.find_method_with_generic_parent(method_name) {
116 return g.method_decl_fkey(method)
117 }
118 if method := g.table.find_method(sym, method_name) {
119 return g.method_decl_fkey(method)
120 }
121 method, embed_types := g.table.find_method_from_embeds(g.table.final_sym(candidate),
122 method_name) or { ast.Fn{}, []ast.Type{} }
123 if embed_types.len != 0 {
124 return g.method_decl_fkey(method)
125 }
126 }
127 return ''
128}
129
130fn free_method_calls_free_on_receiver(method ast.Fn) bool {
131 if method.source_fn == unsafe { nil } {
132 return false
133 }
134 fn_decl := unsafe { &ast.FnDecl(method.source_fn) }
135 if fn_decl.receiver.name == '' {
136 return false
137 }
138 root := ast.Node(ast.Stmt(*fn_decl))
139 return free_method_calls_free_on_receiver_walk(root, fn_decl.receiver.name)
140}
141
142fn free_method_calls_free_on_receiver_walk(node ast.Node, receiver_name string) bool {
143 if node is ast.Expr {
144 match node {
145 ast.AnonFn, ast.LambdaExpr {
146 return false
147 }
148 ast.CallExpr {
149 if node.name == 'free' && node.args.len == 1
150 && free_method_matches_receiver_expr(node.args[0].expr, receiver_name) {
151 return true
152 }
153 }
154 else {}
155 }
156 }
157 for child in node.children() {
158 if free_method_calls_free_on_receiver_walk(child, receiver_name) {
159 return true
160 }
161 }
162 return false
163}
164
165fn free_method_matches_receiver_expr(expr ast.Expr, receiver_name string) bool {
166 return match expr {
167 ast.Ident { expr.name == receiver_name }
168 ast.CastExpr { free_method_matches_receiver_expr(expr.expr, receiver_name) }
169 ast.ParExpr { free_method_matches_receiver_expr(expr.expr, receiver_name) }
170 else { false }
171 }
172}
173
174fn (g &Gen) prefers_msvc_compatible_code() bool {
175 return g.is_cc_msvc || g.pref.os == .windows
176}
177
178fn (mut g Gen) node_decl_fkey(node ast.FnDecl) string {
179 if node.is_method && !node.name.contains('_T_') {
180 if !node.receiver.typ.has_flag(.generic)
181 && !g.type_has_unresolved_generic_parts(node.receiver.typ) {
182 if structured_method := g.table.find_structured_receiver_method_with_types(node.receiver.typ,
183 node.name)
184 {
185 if structured_method.method.receiver_type != node.receiver.typ {
186 if exact_method := g.receiver_exact_method_for_type(node.receiver.typ,
187 node.name)
188 {
189 if g.method_decl_fkey(exact_method) == node.fkey() {
190 return node.fkey()
191 }
192 }
193 }
194 }
195 }
196 receiver_sym := g.table.sym(node.receiver.typ)
197 receiver_has_concrete_generics := match receiver_sym.info {
198 ast.Struct, ast.Interface, ast.SumType {
199 receiver_sym.info.concrete_types.len > 0 || receiver_sym.generic_types.len > 0
200 }
201 ast.GenericInst {
202 true
203 }
204 else {
205 false
206 }
207 }
208
209 needs_generic_method_fkey := node.generic_names.len > 0 || node.ninstances > 0
210 || node.receiver.typ.has_flag(.generic)
211 || g.type_has_unresolved_generic_parts(node.receiver.typ)
212 || receiver_has_concrete_generics
213 if !needs_generic_method_fkey {
214 return node.fkey()
215 }
216 resolved_fkey := g.resolve_method_decl_fkey_for_type(node.receiver.typ, node.name)
217 if resolved_fkey != '' {
218 $if trace_decl_fkey ? {
219 if node.name in ['call_generic_fn', 'collect', 'next', 'next1', 'next2', 'next3',
220 'method', 'another_method'] {
221 eprintln('>>> node_decl_fkey resolved: ${node.name} | receiver: ${g.table.type_to_str(node.receiver.typ)} | fkey: ${resolved_fkey}')
222 }
223 }
224 return resolved_fkey
225 }
226 if generic_method := g.matching_generic_file_method(node) {
227 $if trace_decl_fkey ? {
228 if node.name in ['call_generic_fn', 'collect', 'next', 'next1', 'next2', 'next3',
229 'method', 'another_method'] {
230 eprintln('>>> node_decl_fkey matched file generic: ${node.name} | receiver: ${g.table.type_to_str(node.receiver.typ)} | matched receiver: ${g.table.type_to_str(generic_method.receiver.typ)} | fkey: ${generic_method.fkey()}')
231 }
232 }
233 return generic_method.fkey()
234 }
235 }
236 return node.fkey()
237}
238
239fn (mut g Gen) method_receiver_signature(typ ast.Type) string {
240 if typ == 0 {
241 return ''
242 }
243 base_typ := g.table.unaliased_type(g.unwrap_generic(typ))
244 sym := g.table.final_sym(base_typ)
245 return '${sym.ngname}|${base_typ.nr_muls()}'
246}
247
248fn (mut g Gen) matching_generic_file_method(node ast.FnDecl) ?ast.FnDecl {
249 if !node.is_method {
250 return none
251 }
252 node_receiver_sig := g.method_receiver_signature(node.receiver.typ)
253 if node_receiver_sig == '' {
254 return none
255 }
256 for generic_fn in g.file.generic_fns {
257 if !generic_fn.is_method || generic_fn.name != node.name {
258 continue
259 }
260 $if trace_decl_fkey ? {
261 if node.name in ['call_generic_fn', 'collect', 'next', 'next1', 'next2', 'next3',
262 'method', 'another_method'] {
263 eprintln('>>> matching_generic_file_method: ${node.name} | node receiver: ${g.table.type_to_str(node.receiver.typ)} | node sig: ${node_receiver_sig} | generic receiver: ${g.table.type_to_str(generic_fn.receiver.typ)} | generic sig: ${g.method_receiver_signature(generic_fn.receiver.typ)} | generic fkey: ${generic_fn.fkey()}')
264 }
265 }
266 if g.method_receiver_signature(generic_fn.receiver.typ) == node_receiver_sig {
267 return ast.FnDecl{
268 ...*generic_fn
269 }
270 }
271 }
272 return none
273}
274
275fn (g &Gen) generic_file_fn_by_fkey(fkey string) ?ast.FnDecl {
276 for generic_fn in g.file.generic_fns {
277 if generic_fn.fkey() == fkey {
278 return ast.FnDecl{
279 ...*generic_fn
280 }
281 }
282 }
283 return none
284}
285
286// find_all_receiver_concrete_types finds all concrete type instantiations
287// of the receiver's generic type by looking up GenericInst types in the type table.
288// find_all_receiver_concrete_types finds all concrete type instantiations
289// of the receiver's generic type by examining registered concrete types of
290// sibling methods on the same receiver that have complete type sets.
291fn (mut g Gen) find_all_receiver_concrete_types(node ast.FnDecl, receiver_generic_count int) [][]ast.Type {
292 receiver_typ_int := int(node.receiver.typ)
293 prefix := '${receiver_typ_int}.'
294 mut result := [][]ast.Type{}
295 mut seen := map[string]bool{}
296 // Look through all registered method generic types on this receiver
297 for fkey, type_sets in g.table.fn_generic_types {
298 if !fkey.starts_with(prefix) {
299 continue
300 }
301 for concrete_types in type_sets {
302 if concrete_types.any(it.has_flag(.generic) || g.type_has_unresolved_generic_parts(it)) {
303 continue
304 }
305 // We need sets that have both receiver + method generics
306 if concrete_types.len > receiver_generic_count {
307 receiver_part := concrete_types[..receiver_generic_count]
308 key := receiver_part.str()
309 if key !in seen {
310 seen[key] = true
311 result << receiver_part
312 }
313 }
314 }
315 }
316 // Fallback: if no sibling methods have complete type sets, look for
317 // concrete instantiations of the receiver type in the type table
318 // (GenericInst types whose parent matches the receiver).
319 if result.len == 0 {
320 receiver_parent := node.receiver.typ.set_nr_muls(0).set_flag(.generic)
321 receiver_parent_sym := g.table.sym(receiver_parent)
322 receiver_base_name := receiver_parent_sym.name.all_before('<')
323 for _, sym in g.table.type_symbols {
324 if sym.info is ast.Struct {
325 if sym.info.concrete_types.len == receiver_generic_count
326 && (sym.info.parent_type == receiver_parent
327 || (sym.info.parent_type != 0
328 && g.table.sym(sym.info.parent_type).name.all_before('<') == receiver_base_name)) {
329 cts := sym.info.concrete_types
330 if !cts.any(it.has_flag(.generic) || g.type_has_unresolved_generic_parts(it)) {
331 key := cts.str()
332 if key !in seen {
333 seen[key] = true
334 result << cts.clone()
335 }
336 }
337 }
338 }
339 }
340 }
341 return result
342}
343
344fn (mut g Gen) effective_fn_generic_names(node ast.FnDecl) []string {
345 if node.generic_names.len > 0 {
346 return node.generic_names.clone()
347 }
348 if !node.is_method {
349 return []string{}
350 }
351 resolved_fkey := g.node_decl_fkey(node)
352 if generic_method := g.generic_file_fn_by_fkey(resolved_fkey) {
353 if generic_method.generic_names.len > 0 {
354 $if trace_decl_fkey ? {
355 if node.name in ['call_generic_fn', 'collect', 'next', 'next1', 'next2', 'next3',
356 'method', 'another_method'] {
357 eprintln('>>> effective_fn_generic_names from file generic: ${node.name} | receiver: ${g.table.type_to_str(node.receiver.typ)} | generic_names: ${generic_method.generic_names}')
358 }
359 }
360 return generic_method.generic_names.clone()
361 }
362 receiver_generic_names := g.table.generic_type_names(generic_method.receiver.typ)
363 if receiver_generic_names.len > 0 {
364 $if trace_decl_fkey ? {
365 if node.name in ['call_generic_fn', 'collect', 'next', 'next1', 'next2', 'next3',
366 'method', 'another_method'] {
367 eprintln('>>> effective_fn_generic_names from generic receiver: ${node.name} | receiver: ${g.table.type_to_str(generic_method.receiver.typ)} | generic_names: ${receiver_generic_names}')
368 }
369 }
370 return receiver_generic_names
371 }
372 }
373 receiver_generic_names := g.table.generic_type_names(node.receiver.typ)
374 if receiver_generic_names.len == 0 {
375 return []string{}
376 }
377 if g.table.fn_generic_types[g.node_decl_fkey(node)].len > 0 {
378 return receiver_generic_names
379 }
380 return []string{}
381}
382
383// method_concrete_types_for_name returns the concrete types for the generic
384// name suffix in c_fn_name. For methods where cur_concrete_types includes
385// merged receiver + method types that were synthesized in gen_fn_decl
386// (because the checker only registered method-own types), strip the
387// receiver's types from the suffix since they're already encoded in the
388// receiver type name (e.g., Foo_T_string).
389fn (mut g Gen) method_concrete_types_for_name(node &ast.FnDecl) []ast.Type {
390 if !node.is_method || g.cur_concrete_types.len == 0 {
391 return g.cur_concrete_types
392 }
393 receiver_generic_names := g.table.generic_type_names(node.receiver.typ)
394 if receiver_generic_names.len == 0 || g.cur_concrete_types.len <= receiver_generic_names.len {
395 return g.cur_concrete_types
396 }
397 // Check if the checker registered incomplete types (method-only) or
398 // complete types (receiver + method). If the registered types are
399 // already complete, don't strip anything.
400 // Always use the full concrete types (receiver + method). The call site
401 // is responsible for generating matching names via method_name_concrete_types.
402 return g.cur_concrete_types
403}
404
405fn (mut g Gen) resolve_current_fn_generic_type(typ ast.Type) ast.Type {
406 if typ == 0 {
407 return 0
408 }
409 _, generic_names := g.current_fn_generic_params()
410 if generic_names.len == 0 || g.cur_concrete_types.len == 0 {
411 return g.unwrap_generic(typ)
412 }
413 concrete_types := g.cur_concrete_types.map(ast.mktyp(it))
414 mut muttable := unsafe { &ast.Table(g.table) }
415 if resolved := muttable.convert_generic_type(typ, generic_names, concrete_types) {
416 return g.unwrap_generic(resolved)
417 }
418 return g.unwrap_generic(g.recheck_concrete_type(typ))
419}
420
421fn (mut g Gen) resolved_call_concrete_type(typ ast.Type) ast.Type {
422 return ast.mktyp(g.resolve_current_fn_generic_type(typ))
423}
424
425fn (mut g Gen) receiver_concrete_types_for_type(typ ast.Type) []ast.Type {
426 if typ == 0 {
427 return []ast.Type{}
428 }
429 sym := g.table.sym(typ)
430 match sym.info {
431 ast.Struct, ast.Interface, ast.SumType {
432 mut concrete_types := sym.info.concrete_types.clone()
433 if concrete_types.len == 0 && sym.generic_types.len == sym.info.generic_types.len
434 && sym.generic_types != sym.info.generic_types {
435 concrete_types = sym.generic_types.clone()
436 }
437 return concrete_types.map(g.unwrap_generic(it))
438 }
439 ast.GenericInst {
440 return sym.info.concrete_types.map(g.unwrap_generic(it))
441 }
442 else {
443 return []ast.Type{}
444 }
445 }
446}
447
448fn (mut g Gen) method_receiver_generic_names(method ast.Fn) []string {
449 if method.params.len == 0 {
450 return []string{}
451 }
452 structured_receiver_generic_names :=
453 g.table.structured_receiver_generic_pattern_names(method.params[0].typ)
454 if structured_receiver_generic_names.len > 0 {
455 return structured_receiver_generic_names
456 }
457 return g.table.generic_type_names(method.params[0].typ)
458}
459
460fn (mut g Gen) method_receiver_only_specialization_types(method ast.Fn, concrete_receiver_type ast.Type) []ast.Type {
461 mut receiver_concrete_types := g.receiver_concrete_types_for_type(concrete_receiver_type)
462 if receiver_concrete_types.len == 0 {
463 receiver_concrete_types =
464 g.receiver_concrete_types_for_type(g.recheck_concrete_type(concrete_receiver_type))
465 }
466 mut parent_method := ast.Fn{}
467 _, parent_method = g.receiver_generic_call_context(concrete_receiver_type, method.name)
468 if parent_method.params.len == 0 {
469 _, parent_method = g.receiver_generic_call_context(g.recheck_concrete_type(concrete_receiver_type),
470 method.name)
471 }
472 method_receiver_generic_names := if method.params.len > 0 {
473 g.table.generic_type_names(method.params[0].typ)
474 } else {
475 []string{}
476 }
477 parent_receiver_generic_names := if parent_method.params.len > 0 {
478 g.table.generic_type_names(parent_method.params[0].typ)
479 } else {
480 []string{}
481 }
482 full_method := if parent_method.generic_names.len > method.generic_names.len
483 || (method_receiver_generic_names.len == 0 && parent_receiver_generic_names.len > 0) {
484 parent_method
485 } else {
486 method
487 }
488 if receiver_concrete_types.len == 0 || full_method.params.len == 0 {
489 return []ast.Type{}
490 }
491 receiver_generic_names := g.table.generic_type_names(full_method.params[0].typ)
492 if receiver_generic_names.len == 0 {
493 if receiver_concrete_types.len > 0
494 && full_method.generic_names.len == receiver_concrete_types.len {
495 return receiver_concrete_types.clone()
496 }
497 return []ast.Type{}
498 }
499 if full_method.generic_names.len == 0
500 || (full_method.generic_names.len == receiver_generic_names.len
501 && full_method.generic_names == receiver_generic_names) {
502 return receiver_concrete_types.clone()
503 }
504 return []ast.Type{}
505}
506
507fn (mut g Gen) specialized_method_name_from_receiver(method ast.Fn, concrete_receiver_type ast.Type, base_name string) string {
508 specialization_types := g.method_receiver_only_specialization_types(method,
509 concrete_receiver_type)
510 if specialization_types.len == 0 {
511 return base_name
512 }
513 specialized_suffix := g.generic_fn_name(specialization_types, '')
514 if specialized_suffix != '' && base_name.ends_with(specialized_suffix) {
515 return base_name
516 }
517 return g.generic_fn_name(specialization_types, base_name)
518}
519
520fn (mut g Gen) specialized_method_name_from_receiver_context(method ast.Fn, receiver_concrete_types []ast.Type, parent_method ast.Fn, base_name string) string {
521 if receiver_concrete_types.len == 0 || method.params.len == 0 {
522 return base_name
523 }
524 method_receiver_generic_names := g.table.generic_type_names(method.params[0].typ)
525 parent_receiver_generic_names := if parent_method.params.len > 0 {
526 g.table.generic_type_names(parent_method.params[0].typ)
527 } else {
528 []string{}
529 }
530 full_method := if parent_method.generic_names.len > method.generic_names.len
531 || (method_receiver_generic_names.len == 0 && parent_receiver_generic_names.len > 0) {
532 parent_method
533 } else {
534 method
535 }
536 receiver_generic_names := g.table.generic_type_names(full_method.params[0].typ)
537 if receiver_generic_names.len == 0 {
538 if full_method.generic_names.len != receiver_concrete_types.len {
539 return base_name
540 }
541 } else if full_method.generic_names.len > 0
542 && (full_method.generic_names.len != receiver_generic_names.len
543 || full_method.generic_names != receiver_generic_names) {
544 return base_name
545 }
546 specialized_suffix := g.generic_fn_name(receiver_concrete_types, '')
547 if specialized_suffix != '' && base_name.ends_with(specialized_suffix) {
548 return base_name
549 }
550 return g.generic_fn_name(receiver_concrete_types, base_name)
551}
552
553fn (mut g Gen) recover_method_call_concrete_types_from_name(raw_method_name string, method_name string, receiver_type ast.Type, method_for_generics ast.Fn, parent_generic_method ast.Fn) []ast.Type {
554 if raw_method_name == '' || method_name == ''
555 || !raw_method_name.starts_with(method_name + '_T_') {
556 return []ast.Type{}
557 }
558 method_receiver_generic_names := if method_for_generics.params.len > 0 {
559 g.table.generic_type_names(method_for_generics.params[0].typ)
560 } else {
561 []string{}
562 }
563 parent_receiver_generic_names := if parent_generic_method.params.len > 0 {
564 g.table.generic_type_names(parent_generic_method.params[0].typ)
565 } else {
566 []string{}
567 }
568 full_method := if
569 parent_generic_method.generic_names.len > method_for_generics.generic_names.len
570 || (method_receiver_generic_names.len == 0 && parent_receiver_generic_names.len > 0) {
571 parent_generic_method
572 } else {
573 method_for_generics
574 }
575 mut full_fkey := g.resolve_method_decl_fkey_for_type(receiver_type, method_name)
576 if full_fkey == '' {
577 full_fkey = g.method_decl_fkey(full_method)
578 }
579 if full_fkey == '' {
580 return []ast.Type{}
581 }
582 for candidate in g.table.fn_generic_types[full_fkey] {
583 if candidate.any(it.has_flag(.generic) || g.type_has_unresolved_generic_parts(it)) {
584 continue
585 }
586 name_concrete_types := candidate.map(g.unwrap_generic(it))
587 if g.generic_fn_name(name_concrete_types, method_name) == raw_method_name {
588 return candidate.map(g.unwrap_generic(it))
589 }
590 }
591 return []ast.Type{}
592}
593
594fn (mut g Gen) method_name_concrete_types(full_fkey string, concrete_types []ast.Type, receiver_concrete_types []ast.Type) []ast.Type {
595 if full_fkey == '' || concrete_types.len == 0 {
596 return concrete_types.clone()
597 }
598 normalized_concrete_types := concrete_types.map(g.unwrap_generic(it))
599 normalized_receiver_types := receiver_concrete_types.map(g.unwrap_generic(it))
600 mut matching_partial_candidate := []ast.Type{}
601 for candidate in g.table.fn_generic_types[full_fkey] {
602 if candidate.any(it.has_flag(.generic) || g.type_has_unresolved_generic_parts(it)) {
603 continue
604 }
605 normalized_candidate := candidate.map(g.unwrap_generic(it))
606 if normalized_candidate == normalized_concrete_types {
607 return normalized_candidate
608 }
609 if normalized_receiver_types.len > 0
610 && normalized_candidate.len + normalized_receiver_types.len == normalized_concrete_types.len
611 && normalized_concrete_types[..normalized_receiver_types.len] == normalized_receiver_types
612 && normalized_concrete_types[normalized_receiver_types.len..] == normalized_candidate {
613 // Return the full types (receiver + method) to match the function
614 // definition which always uses complete types.
615 return normalized_concrete_types
616 }
617 if normalized_candidate.len > normalized_concrete_types.len
618 && normalized_candidate[..normalized_concrete_types.len] == normalized_concrete_types {
619 if matching_partial_candidate.len == 0 {
620 matching_partial_candidate = normalized_candidate.clone()
621 } else if matching_partial_candidate != normalized_candidate {
622 return normalized_concrete_types
623 }
624 }
625 }
626 if matching_partial_candidate.len > 0 {
627 return matching_partial_candidate
628 }
629 return normalized_concrete_types
630}
631
632fn (mut g Gen) raw_method_name_concrete_types(raw_method_name string, method_name string, raw_concrete_types []ast.Type, receiver_concrete_types []ast.Type) []ast.Type {
633 if raw_method_name == '' || method_name == '' || raw_concrete_types.len == 0
634 || !raw_method_name.starts_with(method_name + '_T_') {
635 return []ast.Type{}
636 }
637 mut resolved_raw_concrete_types := []ast.Type{}
638 for concrete_type in raw_concrete_types {
639 resolved_concrete_type := g.resolved_call_concrete_type(concrete_type)
640 if resolved_concrete_type == 0 || resolved_concrete_type.has_flag(.generic)
641 || g.type_has_unresolved_generic_parts(resolved_concrete_type) {
642 return []ast.Type{}
643 }
644 resolved_raw_concrete_types << resolved_concrete_type
645 }
646 if g.generic_fn_name(resolved_raw_concrete_types, method_name) == raw_method_name {
647 return resolved_raw_concrete_types
648 }
649 if receiver_concrete_types.len > 0
650 && resolved_raw_concrete_types.len > receiver_concrete_types.len {
651 method_only_types := resolved_raw_concrete_types[receiver_concrete_types.len..].clone()
652 if g.generic_fn_name(method_only_types, method_name) == raw_method_name {
653 return method_only_types
654 }
655 }
656 if raw_concrete_types.any(it.has_flag(.generic) || g.type_has_unresolved_generic_parts(it)) {
657 return resolved_raw_concrete_types
658 }
659 return []ast.Type{}
660}
661
662fn (g &Gen) raw_method_name_suffix(raw_method_name string, method_name string, generic_names []string) string {
663 if raw_method_name == '' || method_name == ''
664 || !raw_method_name.starts_with(method_name + '_T_') {
665 return ''
666 }
667 suffix_tokens := raw_method_name[method_name.len + 3..].split('_').filter(it.len > 0)
668 for suffix_token in suffix_tokens {
669 if suffix_token in generic_names {
670 return ''
671 }
672 }
673 return raw_method_name[method_name.len..]
674}
675
676fn file_has_c_includes(file &ast.File) bool {
677 if file == unsafe { nil } {
678 return false
679 }
680 for stmt in file.stmts {
681 if stmt is ast.HashStmt && stmt.kind in ['include', 'preinclude'] {
682 return true
683 }
684 }
685 return false
686}
687
688fn file_links_c_source(file &ast.File) bool {
689 if file == unsafe { nil } {
690 return false
691 }
692 for stmt in file.stmts {
693 if stmt is ast.HashStmt && stmt.kind == 'flag' {
694 if stmt.main.contains('.c ') || stmt.main.ends_with('.c') || stmt.main.contains('.cpp')
695 || stmt.main.contains('.cc') || stmt.main.contains('.o ')
696 || stmt.main.ends_with('.o') {
697 return true
698 }
699 }
700 }
701 return false
702}
703
704fn (g &Gen) should_emit_c_extern_decl(node ast.FnDecl) bool {
705 if node.language != .c || node.is_c_extern {
706 return false
707 }
708 if node.source_file == unsafe { nil } {
709 return false
710 }
711 if file_has_c_includes(node.source_file) {
712 return false
713 }
714 if file_links_c_source(node.source_file) {
715 return true
716 }
717 if node.mod in g.mods_with_c_libs && node.mod !in g.mods_with_c_includes {
718 return true
719 }
720 if g.pref.os == .vinix && !node.source_file.path.starts_with(g.pref.vlib) {
721 c_sym_name := node.name.after('.')
722 if c_sym_name.starts_with('__builtin_') {
723 return false
724 }
725 if c_sym_name in ['text_start', 'text_end', 'rodata_start', 'rodata_end', 'data_start',
726 'data_end', 'interrupt_thunks'] {
727 return false
728 }
729 return true
730 }
731 return false
732}
733
734fn (mut g Gen) ensure_extern_sig_type_decls(node ast.FnDecl) {
735 g.ensure_extern_sig_type_decl(node.return_type)
736 for param in node.params {
737 g.ensure_extern_sig_type_decl(param.typ)
738 }
739}
740
741fn (mut g Gen) ensure_extern_sig_type_decl(typ_ ast.Type) {
742 if typ_ == 0 {
743 return
744 }
745 typ := typ_.clear_option_and_result().set_nr_muls(0).clear_flags(.generic, .variadic)
746 if typ == 0 {
747 return
748 }
749 sym := g.table.sym(typ)
750 if sym.idx in g.emitted_extern_sig_typedefs {
751 return
752 }
753 g.emitted_extern_sig_typedefs[sym.idx] = true
754 if sym.is_builtin || sym.name in ['byte', 'i32', 'C.FILE'] {
755 return
756 }
757 match sym.info {
758 ast.FnType {
759 // A function-pointer parameter has its own params/return type, which
760 // may also reference struct types that are otherwise unreferenced in V.
761 g.ensure_extern_sig_type_decl(sym.info.func.return_type)
762 for param in sym.info.func.params {
763 g.ensure_extern_sig_type_decl(param.typ)
764 }
765 return
766 }
767 ast.Array {
768 g.ensure_extern_sig_type_decl(sym.info.elem_type)
769 return
770 }
771 ast.ArrayFixed {
772 g.ensure_extern_sig_type_decl(sym.info.elem_type)
773 return
774 }
775 ast.Map {
776 g.ensure_extern_sig_type_decl(sym.info.key_type)
777 g.ensure_extern_sig_type_decl(sym.info.value_type)
778 return
779 }
780 ast.Alias {
781 g.ensure_extern_sig_type_decl(sym.info.parent_type)
782 return
783 }
784 else {}
785 }
786
787 if sym.info is ast.Struct {
788 if sym.language == .c && sym.cname.starts_with('C__') && !sym.info.is_anon {
789 // Empty `struct C.Foo {}` decls without a backing header (e.g. translated
790 // code) get no struct typedef from `write_types`. Without a file-scope
791 // forward decl, each prototype's `struct Foo*` introduces a prototype-
792 // scoped tag, and clang rejects pairs of decls as `conflicting types`.
793 c_struct_name := sym.cname[3..]
794 if c_struct_name != 'va_list' {
795 if sym.info.is_typedef {
796 g.typedefs.writeln('typedef struct ${c_struct_name} ${c_struct_name};')
797 } else {
798 g.typedefs.writeln('struct ${c_struct_name};')
799 }
800 }
801 } else if sym.idx !in g.table.used_features.used_syms {
802 g.typedefs.writeln('typedef struct ${sym.cname} ${sym.cname};')
803 }
804 }
805}
806
807fn (mut g Gen) is_used_by_main(node ast.FnDecl) bool {
808 $if trace_unused_by_main ? {
809 defer(fn) {
810 used_by_main := $res()
811 if !used_by_main {
812 fkey := node.fkey()
813 println('> trace_unused_by_main: mod: ${node.mod} | ${node.name} | fkey: ${fkey} | line_nr: ${node.pos.line_nr}')
814 }
815 }
816 }
817 if g.should_emit_c_extern_decl(node) {
818 return true
819 }
820 if node.is_c_extern {
821 return true
822 }
823 if node.is_method && node.name in ['[]', '[]='] {
824 return true
825 }
826 if node.is_test && node.name.all_after_last('.') in ['before_each', 'after_each'] {
827 return true
828 }
829 if node.mod == 'builtin' && node.name in ['print', 'println', 'eprint', 'eprintln'] {
830 return true
831 }
832 mut is_used_by_main := true
833 if g.pref.skip_unused {
834 if node.is_markused {
835 // TODO for some reason markused walker doesn't set used_fns[key] true for
836 // [markused] fndecls
837 return true
838 }
839 fkey := g.node_decl_fkey(node)
840 is_used_by_main = g.table.used_features.used_fns[fkey]
841 if !is_used_by_main && node.is_method && !node.receiver.typ.has_flag(.generic)
842 && !g.type_has_unresolved_generic_parts(node.receiver.typ) {
843 if structured_method := g.table.find_structured_receiver_method_with_types(node.receiver.typ,
844 node.name)
845 {
846 if structured_method.method.receiver_type != node.receiver.typ {
847 if exact_method := g.receiver_exact_method_for_type(node.receiver.typ,
848 node.name)
849 {
850 is_used_by_main = g.method_decl_fkey(exact_method) == node.fkey()
851 }
852 }
853 }
854 }
855 if !is_used_by_main && g.effective_fn_generic_names(node).len > 0
856 && g.table.fn_generic_types[fkey].len > 0 {
857 is_used_by_main = true
858 }
859 if !is_used_by_main && node.generic_names.len == 0 {
860 recovered_generic_names, recovered_concrete_types :=
861 g.recover_specialized_generic_context_for(node.name)
862 if recovered_generic_names.len > 0
863 && recovered_generic_names.len == recovered_concrete_types.len {
864 for generic_fn in g.file.generic_fns {
865 if generic_fn.generic_names.len == 0 {
866 continue
867 }
868 for base_name in [generic_fn.name, generic_fn.fkey()] {
869 if g.generic_fn_name(recovered_concrete_types, base_name) == node.name {
870 is_used_by_main = g.table.used_features.used_fns[generic_fn.fkey()]
871 if is_used_by_main {
872 break
873 }
874 }
875 }
876 if is_used_by_main {
877 break
878 }
879 }
880 }
881 if !is_used_by_main && node.name.contains('_T_') {
882 is_used_by_main = true
883 }
884 }
885 $if trace_skip_unused_fns ? {
886 println('> is_used_by_main: ${is_used_by_main} | node.name: ${node.name} | fkey: ${fkey} | node.is_method: ${node.is_method}')
887 }
888 if !is_used_by_main && node.is_method {
889 receiver_type := g.table.final_type(node.receiver.typ.set_nr_muls(0))
890 for isym in g.table.type_symbols {
891 if isym.kind != .interface || isym.info !is ast.Interface {
892 continue
893 }
894 if isym.idx !in g.table.used_features.used_syms {
895 continue
896 }
897 inter_info := isym.info as ast.Interface
898 impl_types := inter_info.implementor_types(true)
899 if receiver_type !in impl_types {
900 continue
901 }
902 mut interface_requires_method := false
903 if interface_method := isym.find_method(node.name) {
904 interface_requires_method = interface_method.no_body
905 } else if interface_method := isym.find_method_with_generic_parent(node.name) {
906 interface_requires_method = interface_method.no_body
907 }
908 if interface_requires_method {
909 is_used_by_main = true
910 break
911 }
912 }
913 }
914 if !is_used_by_main {
915 $if trace_skip_unused_fns_in_c_code ? {
916 g.writeln('// trace_skip_unused_fns_in_c_code, ${node.name}, fkey: ${fkey}')
917 }
918 }
919 } else {
920 $if trace_skip_unused_fns_in_c_code ? {
921 g.writeln('// trace_skip_unused_fns_in_c_code, ${node.name}, fkey: ${node.fkey()}')
922 }
923 }
924 return is_used_by_main
925}
926
927fn (mut g Gen) fn_decl(node ast.FnDecl) {
928 $if trace_cgen_fn_decl ? {
929 eprintln('> g.tid: ${g.tid} | g.fid: ${g.fid:3} | g.file.path: ${g.file.path} | fn_decl: ${node.name}')
930 }
931 if node.should_be_skipped {
932 return
933 }
934 if node.is_test {
935 g.test_function_names << node.name
936 }
937 effective_generic_names := g.effective_fn_generic_names(node)
938 if node.ninstances == 0 && effective_generic_names.len > 0
939 && g.table.fn_generic_types[g.node_decl_fkey(node)].len == 0 {
940 $if trace_generics ? {
941 eprintln('skipping generic fn with no concrete instances: ${node.mod} ${node.name}')
942 }
943 return
944 }
945 if !g.is_used_by_main(node) {
946 return
947 }
948 if g.is_builtin_mod && g.pref.gc_mode == .boehm_leak && node.kind == .malloc {
949 g.definitions.write_string('#define builtin___v_malloc GC_MALLOC\n')
950 return
951 }
952
953 if g.pref.parallel_cc {
954 if node.is_anon {
955 // g.write('static ')
956 // g.definitions.write_string('static ')
957 }
958 if !node.is_anon {
959 g.out_fn_start_pos << g.out.len
960 }
961 }
962 prev_is_direct_array_access := g.is_direct_array_access
963 g.is_direct_array_access = node.is_direct_arr || g.pref.no_bounds_checking
964 defer {
965 g.is_direct_array_access = prev_is_direct_array_access
966 }
967
968 // handle `@[ignore_overflow] fn abc() {}` and -check-overflow :
969 prev_do_int_overflow_checks := g.do_int_overflow_checks
970 g.do_int_overflow_checks = g.pref.is_check_overflow && !g.is_builtin_overflow_mod
971 && !node.is_ignore_overflow
972 defer {
973 g.do_int_overflow_checks = prev_do_int_overflow_checks
974 }
975
976 // handle `@[c_extern] fn C.some_name() int` declarations:
977 old_inside_c_extern := g.inside_c_extern
978 defer {
979 g.inside_c_extern = old_inside_c_extern
980 }
981 if node.language == .c && (node.is_c_extern || g.should_emit_c_extern_decl(node)) {
982 g.inside_c_extern = true
983 if g.pref.skip_unused {
984 g.ensure_extern_sig_type_decls(node)
985 }
986 } else if g.pref.skip_unused && node.no_body && node.language == .v {
987 // Body-less V fns (e.g. `@[c: 'NAME'] fn foo()`) emit a forward decl whose
988 // signature may reference opaque C structs that `write_types` skips.
989 g.ensure_extern_sig_type_decls(node)
990 }
991
992 g.gen_attrs(node.attrs)
993 mut skip := false
994 pos := g.out.len
995 defs_pos := g.definitions.len
996 should_bundle_module := util.should_bundle_module(node.mod)
997 if g.pref.build_mode == .build_module {
998 // TODO: true for not just "builtin"
999 // TODO: clean this up
1000 mod := if g.is_builtin_mod { 'builtin' } else { node.name.all_before_last('.') }
1001 module_built_short := g.module_built.all_after_last('/').all_after_last('.')
1002 // for now dont skip generic functions as they are being marked as static
1003 // when -usecache is enabled, until a better solution is implemented.
1004 if ((mod != g.module_built && node.mod != g.module_built && node.mod != module_built_short)
1005 || should_bundle_module) && node.generic_names.len == 0 {
1006 // Skip functions that don't have to be generated for this module.
1007 // println('skip bm ${node.name} mod=${node.mod} module_built=${g.module_built}')
1008 skip = true
1009 }
1010 if g.is_builtin_mod && g.module_built == 'builtin' && node.mod == 'builtin' {
1011 skip = false
1012 }
1013 if !skip && g.pref.is_verbose {
1014 println('build module `${g.module_built}` fn `${node.name}`')
1015 }
1016 }
1017 if g.pref.use_cache {
1018 // We are using prebuilt modules, we do not need to generate
1019 // their functions in main.c.
1020 if node.mod != 'main' && node.mod != 'help' && !should_bundle_module && !g.pref.is_test
1021 && node.generic_names.len == 0 {
1022 skip = true
1023 }
1024 }
1025 keep_fn_decl := g.fn_decl
1026 unsafe {
1027 g.fn_decl = &node
1028 }
1029 if node.is_main {
1030 g.has_main = true
1031 }
1032 // TODO: PERF remove this from here
1033 is_backtrace := node.name.starts_with('backtrace')
1034 && node.name in ['backtrace_symbols', 'backtrace', 'backtrace_symbols_fd']
1035 if is_backtrace {
1036 g.write('\n#ifndef __cplusplus\n')
1037 }
1038 g.gen_fn_decl(node, skip)
1039 if is_backtrace {
1040 g.write('\n#endif\n')
1041 }
1042 g.fn_decl = keep_fn_decl
1043 if skip {
1044 g.go_back_to(pos)
1045 if g.pref.build_mode != .build_module && !g.pref.use_cache {
1046 g.definitions.go_back_to(defs_pos)
1047 }
1048 }
1049 if !g.pref.skip_unused {
1050 if node.language != .c {
1051 g.writeln('')
1052 }
1053 }
1054 // Write the next function into another parallel C file
1055 // g.out_idx++
1056 // if g.out_idx >= g.out_parallel.len {
1057 // g.out_idx = 0
1058 //}
1059}
1060
1061fn (mut g Gen) post_process_generic_fns_for_files(files []&ast.File) {
1062 old_file := g.file
1063 old_fid := g.fid
1064 old_cur_concrete_types := g.cur_concrete_types.clone()
1065 mut emitted_generic_specializations := map[string]bool{}
1066 for {
1067 mut emitted_this_round := false
1068 for fid, file in files {
1069 g.fid = fid
1070 g.file = file
1071 for generic_fn in file.generic_fns {
1072 if generic_fn.is_anon {
1073 continue
1074 }
1075 effective_generic_names := g.effective_fn_generic_names(*generic_fn)
1076 if effective_generic_names.len == 0 {
1077 continue
1078 }
1079 fkey := g.node_decl_fkey(*generic_fn)
1080 generic_types_by_fn := g.table.fn_generic_types[fkey].clone()
1081 if generic_types_by_fn.len == 0 {
1082 continue
1083 }
1084 mut receiver_generic_names := []string{}
1085 mut all_receiver_cts := [][]ast.Type{}
1086 if generic_fn.is_method {
1087 receiver_generic_names = g.table.generic_type_names(generic_fn.receiver.typ)
1088 if receiver_generic_names.len > 0
1089 && receiver_generic_names.len < effective_generic_names.len {
1090 all_receiver_cts = g.find_all_receiver_concrete_types(*generic_fn,
1091 receiver_generic_names.len)
1092 }
1093 }
1094 for concrete_types in generic_types_by_fn {
1095 if concrete_types.any(it.has_flag(.generic)
1096 || g.type_has_unresolved_generic_parts(it))
1097 {
1098 continue
1099 }
1100 mut pending_specializations := [][]ast.Type{}
1101 if concrete_types.len == effective_generic_names.len {
1102 pending_specializations << concrete_types.clone()
1103 } else if concrete_types.len < effective_generic_names.len
1104 && all_receiver_cts.len > 0
1105 && concrete_types.len + receiver_generic_names.len == effective_generic_names.len {
1106 for receiver_cts in all_receiver_cts {
1107 mut merged := receiver_cts.clone()
1108 merged << concrete_types
1109 pending_specializations << merged
1110 }
1111 }
1112 for concrete_specialization in pending_specializations {
1113 specialization_key := g.generic_fn_name(concrete_specialization, fkey)
1114 if specialization_key in emitted_generic_specializations {
1115 continue
1116 }
1117 emitted_generic_specializations[specialization_key] = true
1118 g.cur_concrete_types = concrete_specialization.clone()
1119 g.fn_decl(*generic_fn)
1120 emitted_this_round = true
1121 }
1122 }
1123 }
1124 }
1125 if !emitted_this_round {
1126 break
1127 }
1128 g.cur_concrete_types = []
1129 }
1130 g.cur_concrete_types = old_cur_concrete_types
1131 g.file = old_file
1132 g.fid = old_fid
1133}
1134
1135fn (mut g Gen) gen_fn_decl(node &ast.FnDecl, skip bool) {
1136 $if trace_cgen_gen_fn_decl ? {
1137 eprintln('> g.tid: ${g.tid} | g.fid: ${g.fid:3} | g.file.path: ${g.file.path} | gen_fn_decl: ${node.name} | skip: ${skip}')
1138 }
1139 // TODO: For some reason, build fails with autofree with this line
1140 // as it's only informative, comment it for now
1141 // g.gen_attrs(it.attrs)
1142 if node.language == .c && !g.inside_c_extern {
1143 return
1144 }
1145 c_sym_name := node.name.all_after_first('C__').all_after_first('C.')
1146 if node.language == .c && c_sym_name in ['va_start', 'va_arg', 'va_end', 'va_copy'] {
1147 return
1148 }
1149 old_is_vlines_enabled := g.is_vlines_enabled
1150 g.is_vlines_enabled = true
1151 defer {
1152 g.is_vlines_enabled = old_is_vlines_enabled
1153 }
1154 function_defer_stmts := collect_function_defer_stmts(node)
1155
1156 tmp_defer_vars := g.defer_vars // must be here because of workflow
1157 if g.anon_fn == unsafe { nil } {
1158 g.defer_vars = []string{}
1159 } else {
1160 if function_defer_stmts.len > 0 {
1161 g.defer_vars = []string{}
1162 defer(fn) {
1163 g.defer_vars = tmp_defer_vars
1164 }
1165 }
1166 }
1167 // Skip [if xxx] if xxx is not defined
1168 /*
1169 for attr in node.attrs {
1170 if !attr.is_comptime_define {
1171 continue
1172 }
1173 if attr.name !in g.pref.compile_defines_all {
1174 // println('skipping [if]')
1175 return
1176 }
1177 }
1178 */
1179
1180 g.returned_var_names.clear()
1181 old_g_autofree := g.is_autofree
1182 if node.is_manualfree {
1183 g.is_autofree = false
1184 }
1185 defer {
1186 g.is_autofree = old_g_autofree
1187 }
1188 effective_generic_names := g.effective_fn_generic_names(*node)
1189 if effective_generic_names.len > 0 && g.cur_concrete_types.len == 0 {
1190 return
1191 }
1192 // For methods with their own generics (e.g. fn (c Calc[S]) next[T](input T))
1193 // where cur_concrete_types only contains receiver generics (S=TypeA),
1194 // skip if cur_concrete_types doesn't match the expected number of generic names.
1195 if effective_generic_names.len > 0 && g.cur_concrete_types.len > 0
1196 && g.cur_concrete_types.len != effective_generic_names.len {
1197 return
1198 }
1199 cur_fn_save := g.cur_fn
1200 cur_concrete_types_save := g.cur_concrete_types.clone()
1201 defer {
1202 g.cur_fn = cur_fn_save
1203 g.cur_concrete_types = cur_concrete_types_save
1204 g.clear_type_resolution_caches()
1205 }
1206 mut effective_cur_fn := *node
1207 if g.cur_concrete_types.len > 0 && effective_generic_names.len == g.cur_concrete_types.len
1208 && effective_cur_fn.generic_names != effective_generic_names {
1209 effective_cur_fn = ast.FnDecl{
1210 ...effective_cur_fn
1211 generic_names: effective_generic_names.clone()
1212 }
1213 }
1214 if node.generic_names.len == 0 && g.cur_concrete_types.len == 0
1215 && (!node.is_method || node.receiver.typ.has_flag(.generic)
1216 || node.name.contains('_T_')) {
1217 recovered_generic_names, recovered_concrete_types :=
1218 g.recover_specialized_generic_context_for(node.name)
1219 if recovered_generic_names.len > 0
1220 && recovered_generic_names.len == recovered_concrete_types.len {
1221 effective_cur_fn = ast.FnDecl{
1222 ...*node
1223 generic_names: recovered_generic_names.clone()
1224 }
1225 g.cur_concrete_types = recovered_concrete_types.clone()
1226 }
1227 }
1228 unsafe {
1229 // TODO: remove unsafe
1230 g.cur_fn = &effective_cur_fn
1231 }
1232 g.clear_type_resolution_caches()
1233 g.refresh_current_generic_fn_scope_vars(g.cur_fn)
1234 fn_start_pos := g.out.len
1235 is_closure := node.scope.has_inherited_vars()
1236 mut cur_closure_ctx := ''
1237 if is_closure {
1238 cur_closure_ctx = g.closure_ctx(node)
1239 // declare the struct before its implementation
1240 g.definitions.write_string(cur_closure_ctx)
1241 g.definitions.writeln(';')
1242 }
1243
1244 g.write_v_source_line_info_stmt(node)
1245 fn_attrs := g.write_fn_attrs(node.attrs)
1246 // Live
1247 is_livefn := node.attrs.contains('live')
1248 is_livemain := g.pref.is_livemain && is_livefn
1249 is_liveshared := g.pref.is_liveshared && is_livefn
1250 is_livemode := g.pref.is_livemain || g.pref.is_liveshared
1251 is_live_wrap := is_livefn && is_livemode
1252 if is_livefn && !is_livemode {
1253 eprintln('INFO: compile with `v -live ${g.pref.path} `, if you want to use the @[live] function ${node.name} .')
1254 }
1255
1256 mut name := g.c_fn_name(node)
1257 type_name := g.ret_styp(g.unwrap_generic(node.return_type))
1258 // Live functions are protected by a mutex, because otherwise they
1259 // can be changed by the live reload thread, *while* they are
1260 // running, with unpredictable results (usually just crashing).
1261 // For this purpose, the actual body of the live function,
1262 // is put under a non publicly accessible function, that is prefixed
1263 // with 'impl_live_' .
1264 if is_livemode {
1265 if is_livefn {
1266 g.hotcode_fn_names << name
1267 }
1268 g.hotcode_fpaths << g.file.path
1269 }
1270 mut impl_fn_name := name
1271 if is_live_wrap {
1272 impl_fn_name = 'impl_live_${name}'
1273 }
1274 last_fn_c_name_save := g.last_fn_c_name
1275 defer {
1276 g.last_fn_c_name = last_fn_c_name_save
1277 }
1278 g.last_fn_c_name = impl_fn_name
1279
1280 if !g.inside_c_extern && node.trace_fns.len > 0 {
1281 for trace_fn, call_fn in node.trace_fns {
1282 if trace_fn in g.trace_fn_definitions {
1283 continue
1284 }
1285 trace_fn_ret_type := g.styp(call_fn.return_type)
1286
1287 g.write('VV_LOC ${trace_fn_ret_type} ${c_name(trace_fn)}(')
1288 g.definitions.write_string('VV_LOC ${trace_fn_ret_type} ${c_name(trace_fn)}(')
1289
1290 mut trace_call_args := []string{}
1291 if call_fn.is_fn_var {
1292 sig := g.fn_var_signature(ast.void_type, call_fn.func.return_type,
1293 call_fn.func.params.map(it.typ), call_fn.name)
1294 g.write(sig)
1295 g.definitions.write_string(sig)
1296 if call_fn.func.params.len > 0 {
1297 g.write(', ')
1298 g.definitions.write_string(', ')
1299 trace_call_args, _, _ = g.fn_decl_params(call_fn.func.params, unsafe { nil },
1300 call_fn.func.is_variadic, call_fn.func.is_c_variadic)
1301 }
1302 } else {
1303 trace_call_args, _, _ = g.fn_decl_params(call_fn.func.params, unsafe { nil },
1304 call_fn.func.is_variadic, call_fn.func.is_c_variadic)
1305 }
1306
1307 g.writeln(') {')
1308 g.definitions.write_string(');\n')
1309
1310 orig_fn_args := trace_call_args.join(', ')
1311 add_trace_hook := g.pref.is_trace
1312 && call_fn.name !in ['v.debug.add_after_call', 'v.debug.add_before_call', 'v.debug.remove_after_call', 'v.debug.remove_before_call']
1313 if g.pref.is_callstack {
1314 if g.cur_fn.is_method || g.cur_fn.is_static_type_method {
1315 g.writeln('\tbuiltin__array_push((array*)&g_callstack, _MOV((v__debug__FnTrace[]){ ((v__debug__FnTrace){.name = _S("${g.table.type_to_str(g.cur_fn.receiver.typ)}.${g.cur_fn.name.all_after_last('__static__')}"),.file = _S("${call_fn.file}"),.line = ${call_fn.line},}) }));')
1316 } else {
1317 g.writeln('\tbuiltin__array_push((array*)&g_callstack, _MOV((v__debug__FnTrace[]){ ((v__debug__FnTrace){.name = _S("${g.cur_fn.name}"),.file = _S("${call_fn.file}"),.line = ${call_fn.line},}) }));')
1318 }
1319 }
1320 mut method_name := c_name(call_fn.name)
1321 if call_fn.name.contains('_') {
1322 parts := call_fn.name.split('_')
1323 if parts.len >= 2 {
1324 receiver_type_name := parts[0]
1325 if resolved_sym := g.table.find_sym(receiver_type_name) {
1326 if resolved_sym.is_builtin() {
1327 method_name = 'builtin__${method_name}'
1328 }
1329 }
1330 }
1331 }
1332 if call_fn.return_type == 0 || call_fn.return_type == ast.void_type {
1333 if add_trace_hook {
1334 g.writeln('\tif (!g_trace.in_hook) {')
1335 g.writeln('\t\tv__debug__before_call_hook(_S("${call_fn.name}"));')
1336 g.writeln('\t}')
1337 }
1338 g.writeln('\t${method_name}(${orig_fn_args});')
1339 if add_trace_hook {
1340 g.writeln('\tif (!g_trace.in_hook) {')
1341 g.writeln('\t\tv__debug__after_call_hook(_S("${call_fn.name}"));')
1342 g.writeln('\t}')
1343 }
1344 if g.pref.is_callstack {
1345 g.writeln('\tbuiltin__array_pop((array*)&g_callstack);')
1346 }
1347 } else {
1348 if add_trace_hook {
1349 g.writeln('\tif (!g_trace.in_hook) {')
1350 g.writeln('\t\tv__debug__before_call_hook(_S("${call_fn.name}"));')
1351 g.writeln('\t}')
1352 }
1353 g.writeln('\t${g.styp(call_fn.return_type)} ret = ${method_name}(${orig_fn_args});')
1354 if g.pref.is_callstack {
1355 g.writeln('\tbuiltin__array_pop((array*)&g_callstack);')
1356 }
1357 if add_trace_hook {
1358 g.writeln('\tif (!g_trace.in_hook) {')
1359 g.writeln('\t\tv__debug__after_call_hook(_S("${call_fn.name}"));')
1360 g.writeln('\t}')
1361 }
1362 g.writeln('\treturn ret;')
1363 }
1364 g.writeln2('}', '')
1365 g.trace_fn_definitions << trace_fn
1366 }
1367 }
1368
1369 if is_live_wrap {
1370 if is_livemain {
1371 g.definitions.write_string('${type_name} (* ${impl_fn_name})(')
1372 g.write('${type_name} no_impl_${name}(')
1373 }
1374 if is_liveshared {
1375 if g.pref.os == .windows {
1376 g.export_funcs << impl_fn_name
1377 g.definitions.write_string('VV_EXP ${type_name} ${impl_fn_name}(')
1378 g.write('VV_EXP ${type_name} ${impl_fn_name}(')
1379 } else {
1380 g.definitions.write_string('${type_name} ${impl_fn_name}(')
1381 g.write('${type_name} ${impl_fn_name}(')
1382 }
1383 }
1384 } else if g.inside_c_extern {
1385 extern_name := name.all_after_first('C__')
1386 // Guard with #ifndef so the declaration is skipped when the C symbol
1387 // is actually a preprocessor macro (e.g. WIFSTOPPED from <sys/wait.h>).
1388 g.definitions.writeln('#ifndef ${extern_name}')
1389 c_extern_fn_header := 'extern ${type_name} ${fn_attrs}${extern_name}('
1390 g.definitions.write_string(c_extern_fn_header)
1391 } else {
1392 if !(node.is_pub || g.pref.is_debug) {
1393 // Private functions need to marked as static so that they are not exportable in the
1394 // binaries
1395 if g.pref.build_mode != .build_module && !g.pref.use_cache {
1396 // If we are building vlib/builtin, we need all private functions like array_get
1397 // to be public, so that all V programs can access them.
1398 if !(node.is_anon && g.pref.parallel_cc) {
1399 g.write('VV_LOC ')
1400 // g.definitions.write_string('${g.static_modifier} VV_LOC ')
1401 g.definitions.write_string('VV_LOC ')
1402 }
1403 }
1404 }
1405 // as a temp solution generic functions are marked static
1406 // when -usecache is enabled to fix duplicate symbols with clang
1407 // TODO: implement a better sulution
1408 needs_object_local_linkage := g.should_use_object_local_linkage(node.mod)
1409 && (node.is_pub || g.pref.is_debug)
1410 visibility_kw := if needs_object_local_linkage {
1411 'static '
1412 } else if g.cur_concrete_types.len > 0
1413 && (g.pref.build_mode == .build_module || g.pref.use_cache) {
1414 'static '
1415 } else {
1416 ''
1417 }
1418 fn_header := '${visibility_kw}${type_name} ${fn_attrs}${name}('
1419 g.definitions.write_string(fn_header)
1420 g.write(fn_header)
1421 }
1422 arg_start_pos := g.out.len
1423 is_c_variadic := node.is_c_variadic || (node.language == .c && node.is_variadic)
1424 fargs, fargtypes, heap_promoted := g.fn_decl_params(node.params, node.scope, node.is_variadic,
1425 is_c_variadic)
1426 if is_closure {
1427 g.nr_closures++
1428 if g.pref.no_closures {
1429 g.error('a closure was generated for function', node.pos)
1430 }
1431 }
1432 arg_str := g.out.after(arg_start_pos)
1433 if node.no_body || ((g.pref.use_cache && g.pref.build_mode != .build_module) && node.is_builtin
1434 && !g.pref.is_test) || skip {
1435 // Just a function header. Builtin function bodies are defined in builtin.o
1436 g.definitions.writeln(');') // NO BODY')
1437 if g.inside_c_extern {
1438 g.definitions.writeln('#endif')
1439 } else {
1440 g.writeln(');')
1441 }
1442 return
1443 }
1444 if node.params.len == 0 {
1445 g.definitions.write_string('void')
1446 }
1447 if attr := node.attrs.find_first('_linker_section') {
1448 g.definitions.writeln(') __attribute__ ((section ("${attr.arg}")));')
1449 } else {
1450 g.definitions.writeln(');')
1451 }
1452 g.writeln(') {')
1453 if is_closure {
1454 g.writeln('${cur_closure_ctx}* ${closure_ctx} = g_closure.closure_get_data();')
1455 }
1456 for i, is_promoted in heap_promoted {
1457 if is_promoted {
1458 g.writeln('${fargtypes[i]}* ${fargs[i]} = HEAP(${fargtypes[i]}, _v_toheap_${fargs[i]});')
1459 }
1460 }
1461 g.indent++
1462 for defer_stmt in function_defer_stmts {
1463 if defer_stmt.mode != .function {
1464 continue
1465 }
1466 g.writeln('bool ${g.defer_flag_var(defer_stmt)} = false;')
1467 for var in defer_stmt.defer_vars {
1468 if var.name in fargs || var.kind == .constant {
1469 continue
1470 }
1471 if var.kind == .variable {
1472 if var.name !in g.defer_vars {
1473 g.defer_vars << var.name
1474 mut deref := ''
1475 if v := var.scope.find_var(var.name) {
1476 if v.is_auto_heap {
1477 deref = '*'
1478 }
1479 }
1480 info := var.obj as ast.Var
1481 if g.table.sym(info.typ).kind != .function {
1482 if info.is_static {
1483 g.write('static ')
1484 }
1485 if info.is_volatile {
1486 g.write('volatile ')
1487 }
1488 decl_typ := if deref == '*' {
1489 info.typ.ref()
1490 } else {
1491 info.typ
1492 }
1493 default_expr := g.type_default(decl_typ)
1494 if g.type_default_vars.len > 0 {
1495 for decl in g.type_default_vars.str().trim_right('\n').split_into_lines() {
1496 g.writeln(decl)
1497 }
1498 g.type_default_vars.clear()
1499 }
1500 g.writeln('${g.styp(info.typ)}${deref} ${c_name(var.name)} = ${default_expr};')
1501 }
1502 }
1503 }
1504 }
1505 }
1506 g.indent--
1507 if is_live_wrap {
1508 // The live function just calls its implementation dual, while ensuring
1509 // that the call is wrapped by the mutex lock & unlock calls.
1510 // Adding the mutex lock/unlock inside the body of the implementation
1511 // function is not reliable, because the implementation function can do
1512 // an early exit, which will leave the mutex locked.
1513 live_mutex_ptr_type := if g.pref.os == .windows {
1514 'HANDLE*'
1515 } else {
1516 'pthread_mutex_t*'
1517 }
1518 mut fn_args_list := []string{}
1519 for ia, fa in fargs {
1520 fn_args_list << '${fargtypes[ia]} ${fa}'
1521 }
1522 mut live_fncall := '${impl_fn_name}(' + fargs.join(', ') + ');'
1523 mut live_fnreturn := ''
1524 if type_name != 'void' {
1525 live_fncall = '${type_name} res = ${live_fncall}'
1526 live_fnreturn = 'return res;'
1527 }
1528 if is_livemain {
1529 g.definitions.writeln('${type_name} no_impl_${name}(' + fn_args_list.join(', ') + ');')
1530 }
1531 g.definitions.writeln('${type_name} ${name}(' + fn_args_list.join(', ') + ');')
1532 g.hotcode_definitions.writeln('${type_name} ${name}(' + fn_args_list.join(', ') + '){')
1533 g.hotcode_definitions.writeln(' ${live_mutex_ptr_type} live_fn_mutex_ptr = v_live_fn_mutex_ptr();')
1534 g.hotcode_definitions.writeln(' pthread_mutex_lock(live_fn_mutex_ptr);')
1535 g.hotcode_definitions.writeln(' ${live_fncall}')
1536 g.hotcode_definitions.writeln(' pthread_mutex_unlock(live_fn_mutex_ptr);')
1537 g.hotcode_definitions.writeln(' ${live_fnreturn}')
1538 g.hotcode_definitions.writeln('}')
1539 }
1540 // Profiling mode? Start counting at the beginning of the function (save current time).
1541 if g.pref.is_prof && g.pref.build_mode != .build_module {
1542 g.profile_fn(node)
1543 }
1544 // we could be in an anon fn so save outer fn defer stmts
1545 prev_defer_stmts := g.defer_stmts
1546 g.defer_stmts = []
1547 ctmp := g.tmp_count
1548 g.tmp_count = 0
1549 defer {
1550 g.tmp_count = ctmp
1551 }
1552 prev_inside_ternary := g.inside_ternary
1553 g.inside_ternary = 0
1554 prev_indent := g.indent
1555 g.indent = 0
1556 defer {
1557 g.indent = prev_indent
1558 }
1559 g.stmts(node.stmts)
1560 g.inside_ternary = prev_inside_ternary
1561 if node.is_noreturn {
1562 g.writeln('\twhile(1);')
1563 }
1564 // clear g.fn_mut_arg_names
1565
1566 if !node.has_return {
1567 g.write_defer_stmts_when_needed(node.scope, false, node.name_pos)
1568 }
1569 if node.is_anon {
1570 g.defer_stmts = prev_defer_stmts
1571 } else {
1572 g.defer_stmts = []
1573 }
1574 if node.return_type != ast.void_type && node.stmts.len > 0 && node.stmts.last() !is ast.Return
1575 && !node.attrs.contains('_naked') {
1576 default_expr := g.type_default(node.return_type)
1577 // TODO: perf?
1578 if default_expr == '{0}' {
1579 g.writeln('\treturn (${type_name})${default_expr};')
1580 } else {
1581 g.writeln('\treturn ${default_expr};')
1582 }
1583 }
1584 g.writeln('}')
1585 if g.pref.printfn_list.len > 0 && g.last_fn_c_name in g.pref.printfn_list {
1586 println(g.out.after(fn_start_pos))
1587 }
1588 weak := if node.is_weak { 'VWEAK ' } else { '' }
1589 for attr in node.attrs {
1590 if attr.name == 'export' {
1591 g.writeln('// export alias: ${attr.arg} -> ${name}')
1592 g.export_funcs << attr.arg
1593 export_alias := '${weak}${type_name} ${fn_attrs}${attr.arg}(${arg_str})'
1594 g.definitions.writeln('VV_EXP ${export_alias}; // exported fn ${node.name}')
1595 g.writeln('${export_alias} {')
1596 if !g.pref.is_shared && 'no_main' in g.table.modules {
1597 g.writeln('\t_vno_main_init_caller();')
1598 }
1599 g.write2('\treturn ${name}(', fargs.join(', '))
1600 g.writeln2(');', '}')
1601 }
1602 }
1603}
1604
1605fn (mut g Gen) c_fn_name(node &ast.FnDecl) string {
1606 mut name := node.name
1607 if name in ['+', '-', '*', '**', '/', '%', '<', '==', '[]', '[]='] {
1608 name = util.replace_op(name)
1609 }
1610 if node.is_method {
1611 unwrapped_rec_typ := g.unwrap_generic(node.receiver.typ)
1612 name = g.cc_type(unwrapped_rec_typ, false) + '_' + name
1613 receiver_sym := g.table.sym(unwrapped_rec_typ)
1614 if receiver_sym.is_builtin() {
1615 name = 'builtin__${name}'
1616 }
1617 if receiver_sym.kind == .placeholder {
1618 name = name.replace_each(c_fn_name_escape_seq)
1619 }
1620 }
1621 if node.is_anon && g.anon_fn != unsafe { nil } && g.anon_fn.has_ct_var {
1622 name = '${name}_${g.comptime.comptime_loop_id}'
1623 }
1624 if node.language == .c {
1625 name = util.no_dots(name)
1626 } else {
1627 name = c_fn_name(name)
1628 }
1629 if !node.is_method && node.mod == 'builtin' && !name.starts_with('builtin__') {
1630 name = 'builtin__${name}'
1631 }
1632
1633 if node.generic_names.len > 0 {
1634 name = g.generic_fn_name(g.method_concrete_types_for_name(node), name)
1635 name = name.replace_each(c_fn_name_escape_seq)
1636 }
1637
1638 if g.pref.translated || g.file.is_translated || node.is_file_translated {
1639 if cattr := node.attrs.find_first('c') {
1640 // This fixes unknown symbols errors when building separate .c => .v files into .o files
1641 // example:
1642 // @[c: 'P_TryMove'] fn p_trymove(thing &Mobj_t, x int, y int) bool
1643 // translates to:
1644 // bool P_TryMove(main__Mobj_t* thing, int x, int y);
1645 // In fn_call every time `p_trymove` is called, `P_TryMove` will be generated instead.
1646 name = cattr.arg
1647 }
1648 }
1649 return name
1650}
1651
1652const closure_ctx = '_V_closure_ctx'
1653
1654fn (g &Gen) anon_fn_generic_names(generic_names []string) []string {
1655 if g.cur_fn == unsafe { nil } || g.cur_fn.generic_names.len == 0
1656 || g.cur_concrete_types.len == 0 {
1657 return generic_names.clone()
1658 }
1659 mut names := g.cur_fn.generic_names.clone()
1660 for generic_name in generic_names {
1661 if generic_name !in names {
1662 names << generic_name
1663 }
1664 }
1665 return names
1666}
1667
1668fn (mut g Gen) gen_closure_fn_name(node ast.AnonFn) string {
1669 mut fn_name := node.decl.name
1670 if g.anon_fn_generic_names(node.decl.generic_names).len > 0 {
1671 fn_name = g.generic_fn_name(g.cur_concrete_types, fn_name)
1672 }
1673 if node.has_ct_var {
1674 fn_name += '_${g.comptime.comptime_loop_id}'
1675 }
1676 return fn_name
1677}
1678
1679fn (mut g Gen) c_call_alias_signature(node ast.CallExpr, name string) string {
1680 ret_styp := g.styp(node.return_type)
1681 mut sig := '${ret_styp} (*${name})('
1682 if node.args.len == 0 {
1683 if !node.is_c_variadic {
1684 sig += 'void'
1685 }
1686 } else {
1687 for i, arg in node.args {
1688 arg_typ := if arg.typ != 0 {
1689 arg.typ
1690 } else if i < node.expected_arg_types.len {
1691 node.expected_arg_types[i]
1692 } else {
1693 ast.voidptr_type
1694 }
1695 sig += g.styp(arg_typ)
1696 if i < node.args.len - 1 || node.is_c_variadic {
1697 sig += ', '
1698 }
1699 }
1700 if node.is_c_variadic {
1701 sig += '...'
1702 }
1703 }
1704 sig += ')'
1705 return sig
1706}
1707
1708fn (mut g Gen) c_call_name(node ast.CallExpr, cname string) string {
1709 if node.scope == unsafe { nil } {
1710 return cname
1711 }
1712 if node.scope.find_var(cname) == none && node.scope.find_global(cname) == none {
1713 return cname
1714 }
1715 g.global_tmp_count++
1716 alias_name := '__v_c_fn_${g.global_tmp_count}_${cname}'
1717 alias_sig := g.c_call_alias_signature(node, alias_name)
1718 cast_sig := g.c_call_alias_signature(node, '')
1719 g.definitions.writeln('${g.static_non_parallel}${alias_sig} = (${cast_sig})${cname};')
1720 if g.pref.parallel_cc {
1721 g.extern_out.writeln('extern ${alias_sig};')
1722 }
1723 return alias_name
1724}
1725
1726fn (mut g Gen) closure_ctx(node ast.FnDecl) string {
1727 mut fn_name := node.name
1728 generic_names := if node.is_anon {
1729 g.anon_fn_generic_names(node.generic_names)
1730 } else {
1731 node.generic_names
1732 }
1733 if generic_names.len > 0 {
1734 fn_name = g.generic_fn_name(g.cur_concrete_types, fn_name)
1735 }
1736 return 'struct _V_${fn_name}_Ctx'
1737}
1738
1739fn (mut g Gen) is_mut_closure_fixed_array(var ast.Param) bool {
1740 resolved_typ := g.recheck_concrete_type(var.typ)
1741 return var.is_mut && g.table.final_sym(resolved_typ).kind == .array_fixed
1742}
1743
1744fn (mut g Gen) mut_closure_fixed_array_field_styp(var ast.Param) string {
1745 resolved_typ := g.recheck_concrete_type(var.typ)
1746 info := g.table.final_sym(resolved_typ).info as ast.ArrayFixed
1747 resolved_elem_type := g.recheck_concrete_type(info.elem_type)
1748 mut elem_styp := g.styp(resolved_elem_type.set_nr_muls(0))
1749 if resolved_elem_type.is_ptr() {
1750 elem_styp += '*'.repeat(resolved_elem_type.nr_muls())
1751 }
1752 return '${elem_styp}*'
1753}
1754
1755fn (mut g Gen) closure_inherited_var_type(node ast.AnonFn, var ast.Param) ast.Type {
1756 resolved := g.resolve_current_fn_generic_param_type(var.name)
1757 if resolved != 0 {
1758 return g.unwrap_generic(g.recheck_concrete_type(resolved))
1759 }
1760 if node.decl.scope != unsafe { nil } && node.decl.scope.parent != unsafe { nil } {
1761 if scope_var := node.decl.scope.parent.find_var(var.name) {
1762 // If the scope variable has smartcasts and is a sumtype, use the
1763 // smartcast variant type instead of the original sumtype.
1764 if scope_var.smartcasts.len > 0 && g.table.type_kind(scope_var.typ) == .sum_type {
1765 return g.unwrap_generic(g.recheck_concrete_type(scope_var.smartcasts.last()))
1766 }
1767 // Only use resolved_expr_type for generic type resolution.
1768 // For non-generic types, scope_var.typ is already correct and
1769 // resolved_expr_type can produce wrong pointer levels (e.g. adding
1770 // an extra ref() for &receiver expressions).
1771 if scope_var.typ.has_flag(.generic)
1772 || g.type_has_unresolved_generic_parts(scope_var.typ) {
1773 if scope_var.expr !is ast.EmptyExpr {
1774 resolved_expr_typ := g.resolved_expr_type(scope_var.expr, scope_var.typ)
1775 if resolved_expr_typ != 0 {
1776 return g.unwrap_generic(g.recheck_concrete_type(resolved_expr_typ))
1777 }
1778 }
1779 }
1780 if scope_var.typ != 0 {
1781 return g.unwrap_generic(g.recheck_concrete_type(scope_var.typ))
1782 }
1783 }
1784 }
1785 return g.unwrap_generic(g.recheck_concrete_type(var.typ))
1786}
1787
1788fn (g &Gen) variadic_voidptr_promoted_type(typ ast.Type) ast.Type {
1789 base_typ := g.table.unaliased_type(typ).clear_flags()
1790 if base_typ in ast.int_promoted_type_idxs || base_typ == ast.bool_type {
1791 return ast.int_type
1792 }
1793 if base_typ == ast.f32_type {
1794 return ast.f64_type
1795 }
1796 return ast.no_type
1797}
1798
1799fn (mut g Gen) gen_anon_fn(mut node ast.AnonFn) {
1800 is_amp := g.is_amp
1801 g.is_amp = false
1802 defer {
1803 g.is_amp = is_amp
1804 }
1805 g.gen_anon_fn_decl(mut node)
1806 fn_name := g.gen_closure_fn_name(node)
1807 if !node.decl.scope.has_inherited_vars() {
1808 g.write(fn_name)
1809 return
1810 }
1811 ctx_struct := g.closure_ctx(node.decl)
1812 // it may be possible to optimize `memdup` out if the closure never leaves current scope
1813 // TODO: in case of an assignment, this should only call "closure_set_data" and "closure_set_function" (and free the former data)
1814 g.write('builtin__closure__closure_create_with_data(${fn_name}, (${ctx_struct}*) builtin__memdup(&(${ctx_struct}){')
1815 g.indent++
1816 for var in node.inherited_vars {
1817 mut has_inherited := false
1818 mut is_ptr := false
1819 var_name := c_name(var.name)
1820 // `ref_name` is how the captured variable is referenced in the enclosing
1821 // scope. For fn-typed local variables `fn_var_signature` emits `c_fn_name`,
1822 // so the RHS reference must match. The struct field name (LHS) stays as
1823 // `c_name(var.name)` to match `closure_field_cname` reads inside the body.
1824 mut ref_name := var_name
1825 if parent_obj := node.decl.scope.parent.find_var(var.name) {
1826 ref_name = g.var_cname(parent_obj)
1827 }
1828 if g.is_mut_closure_fixed_array(var) {
1829 if obj := node.decl.scope.find_var(var.name) {
1830 if obj.has_inherited {
1831 has_inherited = true
1832 g.writeln('.${var_name} = ${closure_ctx}->${var_name},')
1833 }
1834 }
1835 if !has_inherited {
1836 g.writeln('.${var_name} = ${ref_name},')
1837 }
1838 continue
1839 }
1840 if obj := node.decl.scope.find_var(var.name) {
1841 is_ptr = obj.typ.is_ptr()
1842 if obj.has_inherited {
1843 has_inherited = true
1844 resolved_var_typ := g.closure_inherited_var_type(node, var)
1845 var_sym := g.table.sym(resolved_var_typ)
1846 if var_sym.info is ast.ArrayFixed {
1847 g.write('.${var_name} = {')
1848 for i in 0 .. var_sym.info.size {
1849 g.write('${closure_ctx}->${var_name}[${i}],')
1850 }
1851 g.writeln('},')
1852 } else if g.resolved_ident_is_by_value_auto_deref_capture(ast.Ident{
1853 name: var.name
1854 scope: node.decl.scope
1855 })
1856 {
1857 g.writeln('.${var_name} = *${closure_ctx}->${var_name},')
1858 } else {
1859 g.writeln('.${var_name} = ${closure_ctx}->${var_name},')
1860 }
1861 }
1862 }
1863 if !has_inherited {
1864 resolved_var_typ := g.closure_inherited_var_type(node, var)
1865 var_sym := g.table.sym(resolved_var_typ)
1866 if var_sym.info is ast.ArrayFixed {
1867 g.write('.${var_name} = {')
1868 for i in 0 .. var_sym.info.size {
1869 g.write('${ref_name}[${i}],')
1870 }
1871 g.writeln('},')
1872 } else if g.is_autofree && !var.is_mut && var_sym.info is ast.Array {
1873 g.writeln('.${var_name} = builtin__array_clone(&${ref_name}),')
1874 } else if g.is_autofree && !var.is_mut && var_sym.kind == .string {
1875 g.writeln('.${var_name} = builtin__string_clone(${ref_name}),')
1876 } else {
1877 mut is_auto_heap := false
1878 mut is_auto_deref_capture := false
1879 mut field_name := ''
1880 if obj := node.decl.scope.parent.find(var.name) {
1881 if obj is ast.Var {
1882 is_auto_heap = !obj.is_stack_obj && obj.is_auto_heap
1883 is_auto_deref_capture = obj.is_auto_deref
1884 && (!var.is_mut || g.table.sym(obj.typ).kind == .alias)
1885 && (is_ptr || obj.typ.is_ptr())
1886 if obj.smartcasts.len > 0 {
1887 if g.table.type_kind(obj.typ) == .sum_type {
1888 cast_sym := g.table.sym(obj.smartcasts.last())
1889 field_name += '._${cast_sym.cname}'
1890 }
1891 }
1892 }
1893 }
1894 if (is_auto_heap && !is_ptr) || is_auto_deref_capture || field_name != '' {
1895 g.writeln('.${var_name} = *${ref_name}${field_name},')
1896 } else {
1897 g.writeln('.${var_name} = ${ref_name},')
1898 }
1899 }
1900 }
1901 }
1902 g.indent--
1903 g.write('}, sizeof(${ctx_struct})), true)')
1904
1905 g.empty_line = false
1906}
1907
1908fn (mut g Gen) gen_anon_fn_decl(mut node ast.AnonFn) {
1909 mut fn_name := g.gen_closure_fn_name(node)
1910 if node.has_gen[fn_name] {
1911 return
1912 }
1913 mut builder := strings.new_builder(256)
1914 // Generate a closure struct
1915 if node.inherited_vars.len > 0 {
1916 ctx_struct := g.closure_ctx(node.decl)
1917 if ctx_struct !in g.closure_structs {
1918 g.closure_structs << ctx_struct
1919 g.definitions.writeln('${ctx_struct} {')
1920 for var in node.inherited_vars {
1921 mut resolved_var_typ := g.closure_inherited_var_type(node, var)
1922 // When the variable is auto-heap promoted, the closure struct
1923 // stores the dereferenced value (the init code does `*var`),
1924 // so strip one pointer level from the field type.
1925 // Similarly, when a `mut` parameter (is_auto_deref) is captured
1926 // without `mut`, the closure stores the dereferenced value.
1927 // For alias types (`type AppRef = &App`), the deref is needed for
1928 // `mut` captures too, since the alias adds an indirection level.
1929 if node.decl.scope != unsafe { nil } && node.decl.scope.parent != unsafe { nil } {
1930 if scope_var := node.decl.scope.parent.find_var(var.name) {
1931 if scope_var.is_auto_heap && !scope_var.is_stack_obj
1932 && resolved_var_typ.is_ptr() {
1933 resolved_var_typ = resolved_var_typ.deref()
1934 } else if scope_var.is_auto_deref
1935 && (!var.is_mut || g.table.sym(scope_var.typ).kind == .alias)
1936 && resolved_var_typ.is_ptr() {
1937 resolved_var_typ = resolved_var_typ.deref()
1938 }
1939 }
1940 }
1941 var_sym := g.table.sym(resolved_var_typ)
1942 if g.is_mut_closure_fixed_array(var) {
1943 g.definitions.writeln('\t${g.mut_closure_fixed_array_field_styp(var)} ${c_name(var.name)};')
1944 } else if var_sym.info is ast.FnType {
1945 resolved_ret_type := g.recheck_concrete_type(var_sym.info.func.return_type)
1946 resolved_param_types :=
1947 var_sym.info.func.params.map(g.recheck_concrete_type(it.typ))
1948 mut sig := g.fn_var_signature(resolved_var_typ, resolved_ret_type,
1949 resolved_param_types, c_name(var.name))
1950 g.definitions.writeln('\t' + sig + ';')
1951 } else {
1952 styp := g.styp(resolved_var_typ)
1953 g.definitions.writeln('\t${styp} ${c_name(var.name)};')
1954 }
1955 }
1956 g.definitions.writeln('};\n')
1957 }
1958 }
1959 pos := g.out.len
1960 was_anon_fn := g.anon_fn
1961 prev_stmt_path_pos := g.stmt_path_pos.clone()
1962 prev_skip_stmt_pos := g.skip_stmt_pos
1963 decl := ast.FnDecl{
1964 ...node.decl
1965 generic_names: g.anon_fn_generic_names(node.decl.generic_names)
1966 }
1967 g.stmt_path_pos = []
1968 g.skip_stmt_pos = false
1969 g.anon_fn = node
1970 old_inside_return := g.inside_return
1971 old_inside_return_expr := g.inside_return_expr
1972 old_inside_lambda := g.inside_lambda
1973 old_inside_lambda_autofree_tmp := g.inside_lambda_autofree_tmp
1974 old_track_lambda_autofree_tmp_arg_vars := g.track_lambda_autofree_tmp_arg_vars
1975 g.inside_return = false
1976 g.inside_return_expr = false
1977 g.inside_lambda = false
1978 g.inside_lambda_autofree_tmp = false
1979 g.track_lambda_autofree_tmp_arg_vars = false
1980 g.fn_decl(decl)
1981 g.track_lambda_autofree_tmp_arg_vars = old_track_lambda_autofree_tmp_arg_vars
1982 g.inside_lambda_autofree_tmp = old_inside_lambda_autofree_tmp
1983 g.inside_lambda = old_inside_lambda
1984 g.inside_return_expr = old_inside_return_expr
1985 g.inside_return = old_inside_return
1986 g.anon_fn = was_anon_fn
1987 g.skip_stmt_pos = prev_skip_stmt_pos
1988 g.stmt_path_pos = prev_stmt_path_pos
1989 builder.write_string(g.out.cut_to(pos))
1990 out := builder.str()
1991 if out.len == 0 {
1992 return
1993 }
1994 node.has_gen[fn_name] = true
1995 g.anon_fn_definitions << out
1996 if g.pref.parallel_cc {
1997 g.extern_out.writeln('extern ${out.all_before(' {')};')
1998 }
1999}
2000
2001fn (mut g Gen) fn_decl_params(params []ast.Param, scope &ast.Scope, is_variadic bool, is_c_variadic bool) ([]string, []string, []bool) {
2002 mut fparams := []string{}
2003 mut fparamtypes := []string{}
2004 mut heap_promoted := []bool{}
2005 param_count := if is_c_variadic && is_variadic && params.len > 0
2006 && params.last().typ.has_flag(.variadic) {
2007 params.len - 1
2008 } else {
2009 params.len
2010 }
2011 if param_count == 0 {
2012 // in C, `()` is untyped, unlike `(void)`
2013 if !g.inside_c_extern && !is_c_variadic {
2014 g.write('void')
2015 }
2016 }
2017 for i, param in params {
2018 if i >= param_count {
2019 break
2020 }
2021 mut typ := if !param.typ.has_flag(.variadic) && g.cur_concrete_types.len > 0
2022 && param.orig_typ != 0 && (param.orig_typ.has_flag(.generic)
2023 || g.type_has_unresolved_generic_parts(param.orig_typ)) {
2024 g.resolve_current_fn_generic_type(param.orig_typ)
2025 } else {
2026 g.unwrap_generic(param.typ)
2027 }
2028 if g.pref.translated && g.file.is_translated && param.typ.has_flag(.variadic) {
2029 typ = g.table.sym(typ).array_info().elem_type.set_flag(.variadic)
2030 }
2031 // A `mut x T` parameter is passed by reference, so its C type must be a pointer to
2032 // the concrete type. The normal solver keeps `.generic` on `param.typ` here, but the
2033 // new generic solver pre-resolves `param.typ` to the concrete type (dropping the flag),
2034 // so it would otherwise miss the indirection. `param.orig_typ` still carries the
2035 // generic `T`, which resolves correctly via `cur_concrete_types`.
2036 if param.is_mut && param.orig_typ != 0 && param.orig_typ.has_flag(.generic)
2037 && (param.typ.has_flag(.generic) || g.pref.new_generic_solver) {
2038 mut surface_typ := g.unwrap_generic(param.orig_typ)
2039 // Only use ref() when the pointer comes from the generic type argument
2040 // (T=&int), not from the param signature (&T / ?&T).
2041 orig_was_ptr := param.orig_typ.nr_muls() > 0
2042 typ = if surface_typ.is_ptr() && !orig_was_ptr {
2043 surface_typ.ref()
2044 } else {
2045 surface_typ.set_nr_muls(1)
2046 }
2047 if typ.has_flag(.option) {
2048 typ = typ.set_flag(.option_mut_param_t)
2049 }
2050 }
2051 if param.is_mut && param.typ.has_flag(.generic) && typ.has_flag(.option) {
2052 typ = typ.set_flag(.option_mut_param_t).set_nr_muls(param.typ.nr_muls() - 1)
2053 }
2054 if param.is_mut && param.orig_typ != 0 && param.orig_typ.has_flag(.option)
2055 && typ.has_flag(.option_mut_param_t) && typ.nr_muls() == 1
2056 && !g.mut_option_param_assigned_directly(param.name) {
2057 typ = typ.ref()
2058 }
2059 param_type_sym := g.table.sym(typ)
2060 mut caname := if param.name in ['', '_'] {
2061 '_d${i + 1}'
2062 } else if param_type_sym.kind == .function && !typ.has_flag(.option) {
2063 c_fn_name(param.name)
2064 } else {
2065 c_name(param.name)
2066 }
2067 mut param_type_name := g.styp(typ)
2068 if param.typ.has_flag(.generic) {
2069 param_type_name = param_type_name.replace_each(c_fn_name_escape_seq)
2070 }
2071 if param_type_sym.kind == .function && !typ.has_flag(.option) {
2072 info := param_type_sym.info as ast.FnType
2073 func := info.func
2074 if !g.inside_c_extern {
2075 g.write('${g.ret_styp(func.return_type)} (*${caname})(')
2076 }
2077 g.definitions.write_string('${g.ret_styp(func.return_type)} (*${caname})(')
2078 g.fn_decl_params(func.params, unsafe { nil }, func.is_variadic, func.is_c_variadic)
2079 if !g.inside_c_extern {
2080 g.write(')')
2081 }
2082 g.definitions.write_string(')')
2083 fparams << caname
2084 fparamtypes << param_type_name
2085 heap_promoted << false
2086 } else {
2087 mut heap_prom := false
2088 if scope != unsafe { nil } {
2089 if param.name != '_' {
2090 if v := scope.find_var(param.name) {
2091 if !v.is_stack_obj && v.is_auto_heap {
2092 heap_prom = true
2093 }
2094 }
2095 }
2096 }
2097 var_name_prefix := if heap_prom { '_v_toheap_' } else { '' }
2098 const_prefix := if param.typ.is_any_kind_of_pointer() && !param.is_mut
2099 && param.name.starts_with('const_') {
2100 'const '
2101 } else {
2102 ''
2103 }
2104 const_param_type_name := if const_prefix != '' {
2105 g.const_pointer_param_type_name(typ, param_type_name)
2106 } else {
2107 param_type_name
2108 }
2109 s := '${const_prefix}${const_param_type_name} ${var_name_prefix}${caname}'
2110 if !g.inside_c_extern {
2111 g.write(s)
2112 }
2113 g.definitions.write_string(s)
2114 fparams << caname
2115 fparamtypes << param_type_name
2116 heap_promoted << heap_prom
2117 }
2118 if i < param_count - 1 {
2119 if !g.inside_c_extern {
2120 g.write(', ')
2121 }
2122 g.definitions.write_string(', ')
2123 }
2124 }
2125 if ((g.pref.translated && is_variadic) || is_c_variadic) && (!is_c_variadic || param_count > 0) {
2126 if param_count > 0 {
2127 if !g.inside_c_extern {
2128 g.write(', ')
2129 }
2130 g.definitions.write_string(', ')
2131 }
2132 if !g.inside_c_extern {
2133 g.write('... ')
2134 }
2135 g.definitions.write_string('... ')
2136 }
2137 return fparams, fparamtypes, heap_promoted
2138}
2139
2140fn (g &Gen) mut_option_param_assigned_directly(name string) bool {
2141 if g.cur_fn == unsafe { nil } {
2142 return false
2143 }
2144 for stmt in g.cur_fn.stmts {
2145 if stmt is ast.AssignStmt {
2146 for left in stmt.left {
2147 if left is ast.Ident && left.name == name {
2148 return true
2149 }
2150 }
2151 }
2152 }
2153 return false
2154}
2155
2156fn (mut g Gen) const_pointer_param_type_name(typ ast.Type, param_type_name string) string {
2157 unwrapped_typ := g.unwrap_generic(typ)
2158 return match unwrapped_typ {
2159 ast.voidptr_type { 'void*' }
2160 ast.byteptr_type { 'u8*' }
2161 ast.charptr_type { 'char*' }
2162 else { param_type_name }
2163 }
2164}
2165
2166fn (mut g Gen) get_anon_fn_type_name(mut node ast.AnonFn, var_name string) string {
2167 mut builder := strings.new_builder(64)
2168 return_styp := g.styp(node.decl.return_type)
2169 builder.write_string('${return_styp} (*${var_name}) (')
2170 if node.decl.params.len == 0 {
2171 builder.write_string('void)')
2172 } else {
2173 for i, param in node.decl.params {
2174 param_styp := g.styp(param.typ)
2175 builder.write_string('${param_styp} ${param.name}')
2176 if i != node.decl.params.len - 1 {
2177 builder.write_string(', ')
2178 }
2179 }
2180 builder.write_string(')')
2181 }
2182 return builder.str()
2183}
2184
2185fn (mut g Gen) call_expr(node ast.CallExpr) {
2186 if node.should_be_skipped {
2187 return
2188 }
2189 if node.is_c_type_cast {
2190 g.cast_expr(ast.CastExpr{
2191 typ: node.return_type
2192 typname: g.table.sym(g.unwrap_generic(node.return_type)).name
2193 expr: node.args[0].expr
2194 expr_type: node.args[0].typ
2195 pos: node.pos
2196 })
2197 return
2198 }
2199 is_shared := g.is_shared
2200 g.is_shared = false
2201 defer {
2202 g.is_shared = is_shared
2203 }
2204 // NOTE: everything could be done this way
2205 // see my comment in parser near anon_fn
2206 mut tmp_anon_fn_var := ''
2207 mut tmp_fn_result_var := ''
2208 mut needs_tmp_fn_result_cleanup := false
2209 if node.left is ast.AnonFn {
2210 if node.left.inherited_vars.len > 0 {
2211 tmp_anon_fn_var = g.new_tmp_var()
2212 fn_type := g.fn_var_signature(ast.void_type, node.left.decl.return_type,
2213 node.left.decl.params.map(it.typ), tmp_anon_fn_var)
2214 line := g.go_before_last_stmt().trim_space()
2215 g.empty_line = true
2216 g.write('${fn_type} = ')
2217 g.expr(ast.Expr(node.left))
2218 g.writeln(';')
2219 g.set_current_pos_as_last_stmt_pos()
2220 g.write(line)
2221 if node.or_block.kind == .absent {
2222 if g.out.last_n(1) != '\n' {
2223 g.writeln('')
2224 }
2225 g.write(tmp_anon_fn_var)
2226 }
2227 } else if node.or_block.kind == .absent {
2228 g.expr(ast.Expr(node.left))
2229 }
2230 } else if !g.inside_curry_call && node.left is ast.IndexExpr && node.name == '' {
2231 if node.or_block.kind == .absent {
2232 old_is_fn_index_call := g.is_fn_index_call
2233 g.is_fn_index_call = true
2234 g.expr(ast.Expr(node.left))
2235 g.is_fn_index_call = old_is_fn_index_call
2236 } else {
2237 // map1['key']() handling
2238 line := g.go_before_last_stmt()
2239 g.empty_line = true
2240
2241 // temp var for map1['key'] where value is a fn to be called
2242 left_typ := g.table.value_type(node.left.left_type)
2243 tmp_res := g.new_tmp_var()
2244 fn_sym := g.table.sym(left_typ).info as ast.FnType
2245 fn_type := g.fn_var_signature(ast.void_type, fn_sym.func.return_type,
2246 fn_sym.func.params.map(it.typ), tmp_res)
2247
2248 old_is_fn_index_call := g.is_fn_index_call
2249 g.is_fn_index_call = true
2250 g.write('${fn_type} = ')
2251 g.expr(ast.Expr(node.left))
2252 g.is_fn_index_call = old_is_fn_index_call
2253 g.writeln(';')
2254
2255 tmp_res2 := g.new_tmp_var()
2256 // uses the `tmp_res` as fn name (where it is a ptr to fn var)
2257 g.write('${g.styp(node.return_type)} ${tmp_res2} = ${tmp_res}')
2258 g.last_tmp_call_var << tmp_res2
2259 old_inside_curry_call := g.inside_curry_call
2260 g.inside_curry_call = true
2261 // map1['key']()() handling
2262 g.expr(node)
2263 g.inside_curry_call = old_inside_curry_call
2264 g.write2(line, '*(${g.base_type(node.return_type)}*)${tmp_res2}.data')
2265 return
2266 }
2267 } else if !g.inside_curry_call && node.left is ast.CallExpr && node.name == '' {
2268 if node.or_block.kind == .absent {
2269 left_return_typ := g.recheck_concrete_type(g.unwrap_generic(node.left.return_type))
2270 if g.table.used_features.anon_fn && g.table.final_sym(left_return_typ).kind == .function {
2271 tmp_fn_result_var = g.new_tmp_var()
2272 fn_sym := g.table.final_sym(left_return_typ).info as ast.FnType
2273 fn_type := g.fn_var_signature(ast.void_type, fn_sym.func.return_type,
2274 fn_sym.func.params.map(it.typ), tmp_fn_result_var)
2275 line := g.go_before_last_stmt().trim_space()
2276 g.empty_line = true
2277 g.write('${fn_type} = ')
2278 g.expr(ast.Expr(node.left))
2279 g.writeln(';')
2280 g.set_current_pos_as_last_stmt_pos()
2281 g.write(line)
2282 if g.out.last_n(1) != '\n' {
2283 g.writeln('')
2284 }
2285 g.write(tmp_fn_result_var)
2286 needs_tmp_fn_result_cleanup = true
2287 } else {
2288 g.expr(ast.Expr(node.left))
2289 }
2290 } else {
2291 ret_typ := node.return_type
2292
2293 line := g.go_before_last_stmt()
2294 g.empty_line = true
2295
2296 tmp_res := g.new_tmp_var()
2297 g.write('${g.styp(ret_typ)} ${tmp_res} = ')
2298
2299 g.last_tmp_call_var << tmp_res
2300 g.expr(ast.Expr(node.left))
2301
2302 old_inside_curry_call := g.inside_curry_call
2303 g.inside_curry_call = true
2304 g.expr(node)
2305 g.inside_curry_call = old_inside_curry_call
2306 g.write2(line, '*(${g.base_type(ret_typ)}*)${tmp_res}.data')
2307 return
2308 }
2309 } else if !g.inside_curry_call && node.left is ast.SelectorExpr && node.name == '' {
2310 if node.or_block.kind == .absent {
2311 g.expr(ast.Expr(node.left))
2312 } else {
2313 ret_typ := node.return_type
2314
2315 line := g.go_before_last_stmt()
2316 g.empty_line = true
2317
2318 tmp_res := g.new_tmp_var()
2319 g.write('${g.styp(ret_typ)} ${tmp_res} = ')
2320
2321 g.last_tmp_call_var << tmp_res
2322 g.expr(ast.Expr(node.left))
2323
2324 old_inside_curry_call := g.inside_curry_call
2325 g.inside_curry_call = true
2326 g.expr(node)
2327 g.inside_curry_call = old_inside_curry_call
2328 g.write2(line, '*(${g.base_type(ret_typ)}*)${tmp_res}.data')
2329 return
2330 }
2331 }
2332 closure_frame_arg_tmps := g.prepare_closure_lifetime_arg_tmps(node)
2333 defer {
2334 for pos in closure_frame_arg_tmps.keys() {
2335 g.closure_frame_arg_tmps.delete(pos)
2336 }
2337 }
2338 old_inside_call := g.inside_call
2339 g.inside_call = true
2340 // Reset inside_selector_lhs so that receiver expressions inside method
2341 // calls generate proper auto-heap dereferences. Without this, when a
2342 // method call is nested inside a selector (e.g. exa.payload()!.len),
2343 // the selector's inside_selector_lhs flag would leak into the receiver
2344 // generation and suppress the (*(exa)) deref.
2345 old_inside_selector_lhs := g.inside_selector_lhs
2346 g.inside_selector_lhs = false
2347 defer {
2348 g.inside_call = old_inside_call
2349 g.inside_selector_lhs = old_inside_selector_lhs
2350 }
2351 gen_keep_alive := node.is_keep_alive && node.return_type != ast.void_type
2352 && g.pref.gc_mode in [.boehm_full, .boehm_incr, .boehm_full_opt, .boehm_incr_opt]
2353 gen_or := node.or_block.kind != .absent // && !g.is_autofree
2354 is_gen_or_and_assign_rhs := gen_or && !g.discard_or_result
2355 mut cur_line := if !g.inside_curry_call && (is_gen_or_and_assign_rhs || gen_keep_alive) { // && !g.is_autofree {
2356 // `x := foo() or { ...}`
2357 // cut everything that has been generated to prepend option variable creation
2358 line := g.go_before_last_stmt()
2359 g.out.write_string(util.tabs(g.indent))
2360 line
2361 } else {
2362 ''
2363 }
2364 // g.write('/*EE line="${cur_line}"*/')
2365 tmp_opt := if gen_or || gen_keep_alive {
2366 if g.inside_curry_call && g.last_tmp_call_var.len > 0 {
2367 g.last_tmp_call_var.pop()
2368 } else if !g.inside_or_block {
2369 new_tmp := g.new_tmp_var()
2370 g.last_tmp_call_var << new_tmp
2371 new_tmp
2372 } else {
2373 g.new_tmp_var()
2374 }
2375 } else {
2376 ''
2377 }
2378 mut tmp_fn_result_call_value := ''
2379 mut tmp_fn_result_call_line := ''
2380 if needs_tmp_fn_result_cleanup && !gen_or && !gen_keep_alive && !g.inside_curry_call
2381 && node.return_type != 0 && node.return_type != ast.void_type {
2382 tmp_fn_result_call_line = g.go_before_last_stmt()
2383 if tmp_fn_result_call_line.ends_with(tmp_fn_result_var) {
2384 tmp_fn_result_call_line = tmp_fn_result_call_line[..tmp_fn_result_call_line.len - tmp_fn_result_var.len]
2385 }
2386 g.empty_line = true
2387 tmp_fn_result_call_value = g.new_tmp_var()
2388 g.write('${g.styp(node.return_type)} ${tmp_fn_result_call_value} = ${tmp_fn_result_var}')
2389 }
2390 mut effective_return_type := node.return_type
2391 if (gen_or || gen_keep_alive) && node.return_type != 0 {
2392 mut ret_typ := effective_return_type
2393 resolved_ret_typ := g.resolve_return_type(node)
2394 if resolved_ret_typ != ast.void_type && !resolved_ret_typ.has_flag(.generic)
2395 && !g.type_has_unresolved_generic_parts(resolved_ret_typ) {
2396 if node.return_type.has_flag(.result) && !resolved_ret_typ.has_flag(.result) {
2397 ret_typ = resolved_ret_typ.set_flag(.result)
2398 } else if node.return_type.has_flag(.option) && !resolved_ret_typ.has_flag(.option) {
2399 ret_typ = resolved_ret_typ.set_flag(.option)
2400 } else {
2401 ret_typ = resolved_ret_typ
2402 }
2403 } else if g.table.sym(ret_typ).kind == .alias {
2404 unaliased_type := g.table.unaliased_type(ret_typ)
2405 if unaliased_type.has_option_or_result() {
2406 ret_typ = unaliased_type
2407 }
2408 } else if node.return_type_generic != 0 {
2409 unwrapped_ret_typ := g.unwrap_generic(node.return_type_generic)
2410 if !unwrapped_ret_typ.has_flag(.generic) {
2411 ret_sym := g.table.sym(unwrapped_ret_typ)
2412 if ret_sym.info is ast.Array && g.table.sym(node.return_type_generic).kind == .array {
2413 // Make []T returns T type when array was supplied to T
2414 if g.table.type_to_str(node.return_type_generic).count('[]') < g.table.type_to_str(unwrapped_ret_typ).count('[]') {
2415 ret_typ = g.unwrap_generic(ret_sym.info.elem_type).derive(unwrapped_ret_typ)
2416 }
2417 }
2418 } else if g.cur_fn != unsafe { nil } && g.cur_fn.is_method
2419 && g.cur_fn.receiver.typ.has_flag(.generic) && g.cur_concrete_types.len > 0 {
2420 // unwrap_generic failed because cur_fn.generic_names is empty
2421 // (generics come from the receiver, not the method itself).
2422 // Use the receiver's generic type names to resolve.
2423 receiver_generic_names := g.table.generic_type_names(g.cur_fn.receiver.typ)
2424 if receiver_generic_names.len == g.cur_concrete_types.len {
2425 if gen_type := g.table.convert_generic_type(node.return_type_generic,
2426 receiver_generic_names, g.cur_concrete_types)
2427 {
2428 if !gen_type.has_flag(.generic) {
2429 ret_typ = gen_type
2430 }
2431 }
2432 }
2433 }
2434 }
2435 effective_return_type = ret_typ
2436 mut styp := g.styp(ret_typ)
2437 if gen_or && !is_gen_or_and_assign_rhs {
2438 cur_line = g.go_before_last_stmt()
2439 }
2440 if gen_or && g.infix_left_var_name.len > 0 {
2441 g.writeln('${styp} ${tmp_opt};')
2442 g.writeln('if (${g.infix_left_var_name}) {')
2443 g.indent++
2444 g.write('${tmp_opt} = ')
2445 } else if !g.inside_curry_call {
2446 if g.assign_ct_type[node.pos.pos] != 0 && node.or_block.kind != .absent {
2447 styp = g.styp(g.assign_ct_type[node.pos.pos].derive(ret_typ))
2448 }
2449 g.write('${styp} ${tmp_opt} = ')
2450 if node.left is ast.AnonFn {
2451 if node.left.inherited_vars.len > 0 {
2452 g.write(tmp_anon_fn_var)
2453 } else {
2454 g.expr(ast.Expr(node.left))
2455 }
2456 }
2457 }
2458 }
2459 if node.is_method && !node.is_field {
2460 if g.pref.experimental && node.args.len > 0 && node.kind == .writeln
2461 && node.args[0].expr is ast.StringInterLiteral
2462 && g.table.sym(node.receiver_type).name == 'strings.Builder' {
2463 g.string_inter_literal_sb_optimized(node)
2464 } else {
2465 g.method_call(node)
2466 }
2467 } else {
2468 g.fn_call(node)
2469 }
2470 if closure_frame_arg_tmps.len > 0 {
2471 g.writeln(';')
2472 for _, tmp in closure_frame_arg_tmps {
2473 g.writeln('builtin__closure__closure_try_destroy((voidptr)${tmp});')
2474 }
2475 g.set_current_pos_as_last_stmt_pos()
2476 }
2477 if needs_tmp_fn_result_cleanup && !gen_or && !gen_keep_alive && !g.inside_curry_call {
2478 if node.return_type == ast.void_type {
2479 g.writeln(';')
2480 g.write('builtin__closure__closure_try_destroy((voidptr)${tmp_fn_result_var})')
2481 g.set_current_pos_as_last_stmt_pos()
2482 } else if tmp_fn_result_call_value != '' {
2483 g.writeln(';')
2484 g.writeln('builtin__closure__closure_try_destroy((voidptr)${tmp_fn_result_var});')
2485 g.write2(tmp_fn_result_call_line, tmp_fn_result_call_value)
2486 }
2487 }
2488 if gen_or && node.return_type != 0 {
2489 g.or_block(tmp_opt, node.or_block, effective_return_type)
2490 mut unwrapped_typ := effective_return_type.clear_option_and_result()
2491 if g.table.sym(unwrapped_typ).kind == .alias {
2492 unaliased_type := g.table.unaliased_type(unwrapped_typ)
2493 if unaliased_type.has_option_or_result() {
2494 unwrapped_typ = unaliased_type.clear_option_and_result()
2495 }
2496 }
2497 mut unwrapped_styp := g.styp(unwrapped_typ)
2498 if g.infix_left_var_name.len > 0 {
2499 g.indent--
2500 g.writeln('}')
2501 g.set_current_pos_as_last_stmt_pos()
2502 }
2503 if unwrapped_typ == ast.void_type {
2504 g.write('\n ${cur_line}')
2505 } else if !g.inside_curry_call {
2506 if !g.inside_const_opt_or_res {
2507 if g.assign_ct_type[node.pos.pos] != 0 && node.or_block.kind != .absent {
2508 unwrapped_styp =
2509 g.styp(g.assign_ct_type[node.pos.pos].derive(effective_return_type).clear_option_and_result())
2510 }
2511 if g.table.sym(effective_return_type).kind == .array_fixed
2512 && unwrapped_styp.starts_with('_v_') {
2513 unwrapped_styp = unwrapped_styp[3..]
2514 }
2515 // Synthesized/transformed call nodes may lose `is_return_used`
2516 // even though the enclosing C generation path is still
2517 // emitting a value expression (struct fields, call args, etc).
2518 // In those cases `is_gen_or_and_assign_rhs` still tells us that
2519 // the unwrapped result is needed in the current expression.
2520 if node.is_return_used || is_gen_or_and_assign_rhs {
2521 is_fn := g.table.final_sym(unwrapped_typ).kind == .function
2522 // return value is used, so we need to write the unwrapped temporary var
2523 if is_fn && unwrapped_typ.nr_muls() > 0 {
2524 g.write('\n ${cur_line}((${unwrapped_styp}*)${tmp_opt}.data)')
2525 } else {
2526 g.write('\n ${cur_line}(*(${unwrapped_styp}*)${tmp_opt}.data)')
2527 }
2528 } else {
2529 g.write('\n ${cur_line}')
2530 }
2531 } else {
2532 if !g.inside_or_block && g.last_tmp_call_var.len > 0 && !cur_line.contains(' = ') {
2533 g.write('\n\t*(${unwrapped_styp}*)${g.last_tmp_call_var.pop()}.data = ${cur_line}(*(${unwrapped_styp}*)${tmp_opt}.data)')
2534 } else {
2535 g.write('\n ${cur_line}(*(${unwrapped_styp}*)${tmp_opt}.data)')
2536 }
2537 }
2538 }
2539 } else if gen_keep_alive && node.return_type != 0 {
2540 if node.return_type == ast.void_type {
2541 g.write('\n ${cur_line}')
2542 } else {
2543 g.write('\n ${cur_line} ${tmp_opt}')
2544 }
2545 }
2546 if node.is_noreturn {
2547 if g.inside_ternary == 0 {
2548 g.writeln(';')
2549 g.write('VUNREACHABLE()')
2550 } else {
2551 $if msvc {
2552 // MSVC has no support for the statement expressions used below
2553 } $else {
2554 g.write(', ({VUNREACHABLE();})')
2555 }
2556 }
2557 }
2558}
2559
2560fn (mut g Gen) is_closure_lifetime_callback_call(node ast.CallExpr) bool {
2561 method_name := node.name.all_after_last('.')
2562 if !node.is_method || method_name !in ['frame', 'suspend', 'untracked'] || node.args.len != 1 {
2563 return false
2564 }
2565 mut receiver_type := g.unwrap_generic(node.receiver_type)
2566 if receiver_type == 0 {
2567 receiver_type = g.unwrap_generic(node.left_type)
2568 }
2569 if receiver_type == 0 {
2570 return false
2571 }
2572 if receiver_type.is_ptr() {
2573 receiver_type = receiver_type.deref()
2574 }
2575 return g.table.sym(receiver_type).name == 'builtin.closure.Lifetime'
2576}
2577
2578fn (mut g Gen) closure_lifetime_arg_needs_tmp(expr ast.Expr) bool {
2579 unwrapped_expr := expr.remove_par()
2580 return match unwrapped_expr {
2581 ast.AnonFn {
2582 unwrapped_expr.inherited_vars.len > 0
2583 }
2584 ast.SelectorExpr {
2585 unwrapped_expr.has_hidden_receiver
2586 }
2587 else {
2588 false
2589 }
2590 }
2591}
2592
2593fn (mut g Gen) call_needs_closure_lifetime_arg_tmp(node ast.CallExpr) bool {
2594 return g.is_closure_lifetime_callback_call(node)
2595 && g.closure_lifetime_arg_needs_tmp(node.args[0].expr)
2596}
2597
2598fn (mut g Gen) closure_lifetime_fn_type_tmp_signature(fn_types []ast.Type, tmp string) ?string {
2599 for raw_fn_typ in fn_types {
2600 fn_typ := g.unwrap_generic(g.recheck_concrete_type(raw_fn_typ))
2601 if fn_typ == 0 {
2602 continue
2603 }
2604 fn_sym := g.table.final_sym(fn_typ)
2605 if fn_sym.info is ast.FnType {
2606 func := fn_sym.info.func
2607 return g.fn_var_signature(ast.void_type, func.return_type, func.params.map(it.typ), tmp)
2608 }
2609 }
2610 return none
2611}
2612
2613fn (mut g Gen) closure_lifetime_arg_tmp_signature(node ast.CallExpr, arg ast.CallArg, tmp string) ?string {
2614 unwrapped_expr := arg.expr.remove_par()
2615 match unwrapped_expr {
2616 ast.AnonFn {
2617 return g.fn_var_signature(ast.void_type, unwrapped_expr.decl.return_type,
2618 unwrapped_expr.decl.params.map(it.typ), tmp)
2619 }
2620 ast.SelectorExpr {
2621 mut fn_types := []ast.Type{}
2622 if arg.typ != 0 {
2623 fn_types << arg.typ
2624 }
2625 if unwrapped_expr.typ != 0 {
2626 fn_types << unwrapped_expr.typ
2627 }
2628 if node.expected_arg_types.len > 0 && node.expected_arg_types[0] != 0 {
2629 fn_types << node.expected_arg_types[0]
2630 }
2631 return g.closure_lifetime_fn_type_tmp_signature(fn_types, tmp)
2632 }
2633 else {}
2634 }
2635
2636 return none
2637}
2638
2639fn (mut g Gen) prepare_closure_lifetime_arg_tmps(node ast.CallExpr) map[int]string {
2640 mut tmps := map[int]string{}
2641 if !g.call_needs_closure_lifetime_arg_tmp(node) {
2642 return tmps
2643 }
2644 arg := node.args[0]
2645 tmp := g.new_tmp_var()
2646 fn_type := g.closure_lifetime_arg_tmp_signature(node, arg, tmp) or { return tmps }
2647 line := g.go_before_last_stmt().trim_space()
2648 g.empty_line = true
2649 g.write('${fn_type} = ')
2650 g.expr(ast.Expr(arg.expr))
2651 g.writeln(';')
2652 g.set_current_pos_as_last_stmt_pos()
2653 g.write(line)
2654 tmps[arg.pos.pos] = tmp
2655 g.closure_frame_arg_tmps[arg.pos.pos] = tmp
2656 return tmps
2657}
2658
2659fn (mut g Gen) conversion_function_call(prefix string, postfix string, node ast.CallExpr) {
2660 g.write('${prefix}( (')
2661 g.expr(node.left)
2662 dot := if node.left_type.is_ptr() { '->' } else { '.' }
2663 g.write(')${dot}_typ )${postfix}')
2664}
2665
2666@[inline]
2667fn (mut g Gen) write_raw_receiver_expr(node ast.Expr) {
2668 old_inside_selector_lhs := g.inside_selector_lhs
2669 g.inside_selector_lhs = true
2670 defer {
2671 g.inside_selector_lhs = old_inside_selector_lhs
2672 }
2673 g.expr(node)
2674}
2675
2676@[inline]
2677fn (mut g Gen) gen_arg_from_type(node_type ast.Type, node ast.Expr) {
2678 is_auto_heap_ident := node is ast.Ident && g.resolved_ident_is_auto_heap(node)
2679 if node_type.has_flag(.shared_f) {
2680 if node_type.is_ptr() || is_auto_heap_ident {
2681 g.write('&')
2682 }
2683 if is_auto_heap_ident {
2684 g.write_raw_receiver_expr(node)
2685 } else {
2686 g.expr(node)
2687 }
2688 g.write('->val')
2689 } else {
2690 if node_type.is_ptr() || is_auto_heap_ident {
2691 if is_auto_heap_ident {
2692 g.write_raw_receiver_expr(node)
2693 } else {
2694 g.expr(node)
2695 }
2696 } else if !node.is_lvalue()
2697 || (node is ast.Ident && g.table.is_interface_smartcast(node.obj)) {
2698 g.write('ADDR(${g.styp(node_type)}, ')
2699 g.expr(node)
2700 g.write(')')
2701 } else {
2702 g.write('&')
2703 g.expr(node)
2704 }
2705 }
2706}
2707
2708fn (mut g Gen) gen_map_method_call(node ast.CallExpr, left_type ast.Type, left_sym ast.TypeSymbol) bool {
2709 match node.kind {
2710 .reserve {
2711 g.write('builtin__map_reserve(')
2712 g.gen_arg_from_type(left_type, node.left)
2713 g.write(', ')
2714 g.expr(node.args[0].expr)
2715 g.write(')')
2716 }
2717 .delete {
2718 elem_type_str := if left_sym.info is ast.Map {
2719 g.styp(left_sym.info.key_type)
2720 } else {
2721 // In generic contexts, the left type may resolve to the base
2722 // `map` struct rather than a concrete `map[K]V`. Resolve the
2723 // key type from the argument expression instead.
2724 arg_type := g.unwrap_generic(g.resolved_expr_type(node.args[0].expr,
2725 node.args[0].typ))
2726 g.styp(arg_type)
2727 }
2728 g.write('builtin__map_delete(')
2729 g.gen_arg_from_type(left_type, node.left)
2730 g.write(', &(${elem_type_str}[]){')
2731 g.expr(node.args[0].expr)
2732 g.write('})')
2733 }
2734 .free, .clear, .keys, .values {
2735 g.write('builtin__map_${node.name}(')
2736 g.gen_arg_from_type(left_type, node.left)
2737 g.write(')')
2738 }
2739 else {
2740 return false
2741 }
2742 }
2743
2744 return true
2745}
2746
2747fn (mut g Gen) gen_array_method_call(node ast.CallExpr, left_type ast.Type, left_sym ast.TypeSymbol) bool {
2748 if node.name == 'get' {
2749 g.gen_array_get(node)
2750 return true
2751 }
2752 match node.kind {
2753 .filter {
2754 g.gen_array_filter(node)
2755 }
2756 .sort {
2757 g.gen_array_sort(node)
2758 }
2759 .sorted {
2760 g.gen_array_sorted(node)
2761 }
2762 .sort_with_compare {
2763 if !g.gen_array_sort_with_compare(node) {
2764 return false
2765 }
2766 }
2767 .sorted_with_compare {
2768 if !g.gen_array_sorted_with_compare(node) {
2769 return false
2770 }
2771 }
2772 .insert {
2773 g.gen_array_insert(node)
2774 }
2775 .map {
2776 g.gen_array_map(node)
2777 }
2778 .prepend {
2779 g.gen_array_prepend(node)
2780 }
2781 .contains {
2782 g.gen_array_contains(left_type, node.left, node.args[0].typ, node.args[0].expr)
2783 }
2784 .index {
2785 g.gen_array_index(node, false)
2786 }
2787 .last_index {
2788 g.gen_array_index(node, true)
2789 }
2790 .wait {
2791 g.gen_array_wait(node)
2792 }
2793 .any {
2794 g.gen_array_any(node)
2795 }
2796 .count {
2797 g.gen_array_count(node)
2798 }
2799 .all {
2800 g.gen_array_all(node)
2801 }
2802 .delete, .drop, .delete_last, .delete_many {
2803 g.write('builtin__array_${node.name}(')
2804 g.gen_arg_from_type(left_type, node.left)
2805 if node.kind != .delete_last {
2806 g.write(', ')
2807 g.expr(node.args[0].expr)
2808 if node.kind == .delete_many {
2809 g.write(', ')
2810 g.expr(node.args[1].expr)
2811 }
2812 }
2813 g.write(')')
2814 }
2815 .grow_cap, .grow_len {
2816 g.write('builtin__array_${node.name}(')
2817 g.gen_arg_from_type(left_type, node.left)
2818 g.write(', ')
2819 g.expr(node.args[0].expr)
2820 g.write(')')
2821 }
2822 .first, .last, .pop_left, .pop {
2823 mut noscan := ''
2824 array_info := left_sym.info as ast.Array
2825 if node.kind in [.pop_left, .pop] {
2826 noscan = g.check_noscan(array_info.elem_type)
2827 }
2828 return_type := g.resolved_array_builtin_method_return_type(node, left_type,
2829 node.return_type)
2830 return_type_str := g.styp(return_type)
2831 g.write('(*(${return_type_str}*)builtin__array_${node.name}${noscan}(')
2832 if node.kind in [.pop_left, .pop] {
2833 g.gen_arg_from_type(left_type, node.left)
2834 } else {
2835 if node.left_type.is_ptr() || node.left.is_auto_deref_var() {
2836 g.write('(*')
2837 g.expr(node.left)
2838 g.write(')')
2839 } else {
2840 g.expr(node.left)
2841 }
2842 if left_type.has_flag(.shared_f) {
2843 g.write('.val')
2844 }
2845 }
2846 g.write('))')
2847 }
2848 .clone, .repeat {
2849 array_info := left_sym.info as ast.Array
2850 elem_sym :=
2851 g.table.final_sym(g.table.unaliased_type(g.unwrap_generic(array_info.elem_type)))
2852 if node.kind == .repeat && elem_sym.kind == .interface {
2853 fn_name := g.register_array_interface_repeat_fn(g.resolve_return_type(node))
2854 g.write('${fn_name}(')
2855 if node.left_type.is_ptr() {
2856 g.write('*'.repeat(node.left_type.nr_muls()))
2857 }
2858 g.expr(ast.Expr(node.left))
2859 g.write(', ')
2860 g.expr(node.args[0].expr)
2861 g.write(')')
2862 return true
2863 }
2864 array_depth := g.get_array_depth(array_info.elem_type)
2865 to_depth := if array_depth >= 0 { '_to_depth' } else { '' }
2866 mut is_range_slice := false
2867 if node.left is ast.IndexExpr && node.left.index is ast.RangeExpr && node.kind == .clone {
2868 is_range_slice = true
2869 }
2870 to_static := if is_range_slice { '_static' } else { '' }
2871 g.write('builtin__array_${node.name}${to_static}${to_depth}(')
2872 if node.kind == .clone {
2873 if is_range_slice {
2874 if node.left_type.is_ptr() {
2875 g.write('*'.repeat(node.left_type.nr_muls()))
2876 }
2877 g.expr(node.left)
2878 } else {
2879 g.gen_arg_from_type(left_type, node.left)
2880 }
2881 } else {
2882 if node.left_type.is_ptr() {
2883 g.write('*'.repeat(node.left_type.nr_muls()))
2884 }
2885 g.expr(ast.Expr(node.left))
2886 }
2887 if node.kind == .repeat {
2888 g.write(', ')
2889 g.expr(node.args[0].expr)
2890 }
2891 if array_depth >= 0 {
2892 g.write(', ${array_depth}')
2893 }
2894 g.write(')')
2895 }
2896 else {
2897 return false
2898 }
2899 }
2900
2901 return true
2902}
2903
2904fn (mut g Gen) gen_fixed_array_method_call(node ast.CallExpr, left_type ast.Type) bool {
2905 match node.kind {
2906 .filter {
2907 g.gen_array_filter(node)
2908 }
2909 .index {
2910 g.gen_array_index(node, false)
2911 }
2912 .last_index {
2913 g.gen_array_index(node, true)
2914 }
2915 .contains {
2916 g.gen_array_contains(left_type, node.left, node.args[0].typ, node.args[0].expr)
2917 }
2918 .any {
2919 g.gen_array_any(node)
2920 }
2921 .count {
2922 g.gen_array_count(node)
2923 }
2924 .all {
2925 g.gen_array_all(node)
2926 }
2927 .map {
2928 g.gen_array_map(node)
2929 }
2930 .sort {
2931 g.gen_array_sort(node)
2932 }
2933 .sorted {
2934 g.gen_array_sorted(node)
2935 }
2936 .sort_with_compare {
2937 if !g.gen_array_sort_with_compare(node) {
2938 return false
2939 }
2940 }
2941 .sorted_with_compare {
2942 if !g.gen_array_sorted_with_compare(node) {
2943 return false
2944 }
2945 }
2946 .reverse {
2947 g.gen_fixed_array_reverse(node)
2948 }
2949 .reverse_in_place {
2950 g.gen_fixed_array_reverse_in_place(node)
2951 }
2952 else {
2953 return false
2954 }
2955 }
2956
2957 return true
2958}
2959
2960fn (mut g Gen) gen_to_str_method_call(node ast.CallExpr, unwrapped_rec_type ast.Type) bool {
2961 left_node := node.left
2962 mut rec_type := g.unwrap_generic(g.type_resolver.get_type_or_default(left_node,
2963 left_node.type()))
2964 if rec_type in [ast.void_type, ast.no_type] {
2965 rec_type = unwrapped_rec_type
2966 }
2967 if rec_type.has_flag(.shared_f) {
2968 rec_type = rec_type.clear_flag(.shared_f).set_nr_muls(0)
2969 }
2970 if left_node is ast.ComptimeSelector {
2971 if left_node.typ_key != '' {
2972 rec_type = g.type_resolver.get_ct_type_or_default(left_node.typ_key, rec_type)
2973 g.gen_expr_to_string(left_node, rec_type)
2974 return true
2975 }
2976 } else if left_node is ast.PostfixExpr {
2977 rec_type = g.type_resolver.get_type_or_default(left_node.expr, rec_type)
2978 if left_node.op == .question {
2979 rec_type = rec_type.clear_flag(.option)
2980 }
2981 g.gen_expr_to_string(left_node, rec_type)
2982 return true
2983 } else if left_node is ast.ComptimeCall {
2984 if left_node.kind == .method {
2985 sym := g.table.sym(g.unwrap_generic(left_node.left_type))
2986 if m := sym.find_method(g.comptime.comptime_for_method.name) {
2987 rec_type = m.return_type
2988 g.gen_expr_to_string(left_node, rec_type)
2989 return true
2990 }
2991 }
2992 } else if left_node is ast.Ident {
2993 if left_node.obj is ast.Var {
2994 if left_node.obj.ct_type_var != .no_comptime {
2995 rec_type = g.type_resolver.get_type(left_node)
2996 // In generic contexts, scope var types may be stale
2997 if g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0
2998 && left_node.obj.ct_type_var == .generic_param {
2999 resolved := g.resolved_expr_type(left_node, rec_type)
3000 if resolved != 0 {
3001 rec_type = resolved
3002 }
3003 }
3004 g.gen_expr_to_string(left_node, rec_type)
3005 return true
3006 } else if left_node.obj.smartcasts.len > 0 {
3007 rec_type = g.unwrap_generic(left_node.obj.smartcasts.last())
3008 cast_sym := g.table.sym(rec_type)
3009 if cast_sym.info is ast.Aggregate {
3010 rec_type = cast_sym.info.types[g.aggregate_type_idx]
3011 }
3012 g.gen_expr_to_string(left_node, rec_type)
3013 return true
3014 } else if left_node.or_expr.kind == .propagate_option {
3015 g.gen_expr_to_string(left_node, g.unwrap_generic(node.left_type))
3016 return true
3017 }
3018 }
3019 } else if left_node is ast.None {
3020 g.gen_expr_to_string(left_node, ast.none_type)
3021 return true
3022 } else if node.left_type.has_flag(.option) {
3023 g.gen_expr_to_string(left_node, g.unwrap_generic(node.left_type))
3024 return true
3025 }
3026 if g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0 {
3027 resolved_rec_type := g.resolved_expr_type(left_node, node.left_type)
3028 if resolved_rec_type != 0 && resolved_rec_type != ast.void_type {
3029 g.gen_expr_to_string(left_node,
3030 g.unwrap_generic(g.recheck_concrete_type(resolved_rec_type)))
3031 return true
3032 }
3033 }
3034 rec_sym := g.table.sym(rec_type)
3035 if g.alias_uses_parent_str(rec_sym) {
3036 rec_type = (rec_sym.info as ast.Alias).parent_type
3037 }
3038 g.get_str_fn(rec_type)
3039 return false
3040}
3041
3042fn (g &Gen) alias_uses_parent_str(sym ast.TypeSymbol) bool {
3043 if sym.info !is ast.Alias || sym.has_method('str') {
3044 return false
3045 }
3046 alias_info := sym.info as ast.Alias
3047 parent_sym := g.table.sym(alias_info.parent_type)
3048 return parent_sym.is_string() || parent_sym.is_number()
3049 || parent_sym.kind in [.bool, .char, .byteptr, .charptr, .voidptr, .map]
3050}
3051
3052fn (g &Gen) alias_keeps_parent_method_dispatch(sym ast.TypeSymbol, method_name string, kind ast.CallKind) bool {
3053 if sym.info !is ast.Alias || sym.has_method(method_name) {
3054 return false
3055 }
3056 return kind != .str || g.alias_uses_parent_str(sym)
3057}
3058
3059// resolve_return_type resolves the generic return type of CallExpr
3060fn (mut g Gen) resolve_return_type(node ast.CallExpr) ast.Type {
3061 if node.is_method {
3062 if node.kind == .map && node.return_type != 0 && !node.return_type.has_flag(.generic)
3063 && !g.type_has_unresolved_generic_parts(node.return_type)
3064 && !(g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0) {
3065 return if node.or_block.kind == .absent {
3066 g.unwrap_generic(g.recheck_concrete_type(node.return_type))
3067 } else {
3068 g.unwrap_generic(g.recheck_concrete_type(node.return_type)).clear_option_and_result()
3069 }
3070 }
3071 mut left_type := g.resolved_expr_type(node.left, node.left_type)
3072 if left_type == 0 || left_type == ast.void_type {
3073 left_type = node.left_type
3074 }
3075 left_type = g.recheck_concrete_type(left_type)
3076 if left_type == 0 || left_type == ast.void_type {
3077 return ast.void_type
3078 }
3079 left_sym := g.table.sym(left_type)
3080 final_left_sym := g.table.final_sym(g.unwrap_generic(left_type))
3081 if final_left_sym.kind == .map && node.kind in [.keys, .values] {
3082 map_info := final_left_sym.info as ast.Map
3083 return_type := if node.kind == .keys {
3084 ast.idx_to_type(g.table.find_or_register_array(map_info.key_type))
3085 } else {
3086 ast.idx_to_type(g.table.find_or_register_array(map_info.value_type))
3087 }
3088 return if node.or_block.kind == .absent {
3089 return_type
3090 } else {
3091 return_type.clear_option_and_result()
3092 }
3093 }
3094 if final_left_sym.kind == .map && node.name in ['clone', 'move'] {
3095 return g.unwrap_generic(left_type)
3096 }
3097 if final_left_sym.kind in [.array, .array_fixed] && !(left_sym.has_method(node.name)
3098 || left_sym.has_method_with_generic_parent(node.name)) && (node.name == 'get'
3099 || node.kind in [.first, .last, .pop_left, .pop, .map, .filter, .reverse, .clone, .clone_to_depth, .repeat, .trim, .slice, .sorted, .sorted_with_compare]) {
3100 return_type := g.resolved_array_builtin_method_return_type(node, left_type,
3101 node.return_type)
3102 return if node.or_block.kind == .absent {
3103 return_type
3104 } else {
3105 return_type.clear_option_and_result()
3106 }
3107 }
3108 if node.is_field {
3109 selector := ast.SelectorExpr{
3110 expr: node.left
3111 expr_type: left_type
3112 field_name: node.name
3113 }
3114 fn_typ := g.resolved_selector_field_type(selector, left_type)
3115 if fn_typ != 0 {
3116 fn_sym := g.table.final_sym(fn_typ.clear_option_and_result())
3117 if fn_sym.info is ast.FnType {
3118 return_type :=
3119 g.unwrap_generic(g.recheck_concrete_type(fn_sym.info.func.return_type))
3120 if return_type != 0 {
3121 return if node.or_block.kind == .absent {
3122 return_type
3123 } else {
3124 return_type.clear_option_and_result()
3125 }
3126 }
3127 }
3128 }
3129 }
3130 func := left_sym.find_method_with_generic_parent(node.name) or {
3131 g.table.find_method(left_sym, node.name) or { return ast.void_type }
3132 }
3133 if func.return_type != 0 && !func.return_type.has_flag(.generic)
3134 && !g.type_has_unresolved_generic_parts(func.return_type) {
3135 return if node.or_block.kind == .absent {
3136 func.return_type
3137 } else {
3138 func.return_type.clear_option_and_result()
3139 }
3140 }
3141 receiver_concrete_types, parent_method := g.receiver_generic_call_context(left_type,
3142 node.name)
3143 if func.generic_names.len > 0 {
3144 mut concrete_types := if node.concrete_types.len == func.generic_names.len
3145 && node.concrete_types.all(it != 0 && !it.has_flag(.generic)
3146 && !g.type_has_unresolved_generic_parts(it)) {
3147 node.concrete_types.map(g.unwrap_generic(it))
3148 } else if node.raw_concrete_types.len > 0 {
3149 node.raw_concrete_types.map(g.unwrap_generic(it))
3150 } else {
3151 node.concrete_types.map(g.unwrap_generic(it))
3152 }
3153 if concrete_types.len == 0 && receiver_concrete_types.len > 0 {
3154 concrete_types = receiver_concrete_types.clone()
3155 } else if receiver_concrete_types.len > 0 && concrete_types.len < func.generic_names.len
3156 && receiver_concrete_types.len + concrete_types.len == func.generic_names.len {
3157 method_concrete_types := concrete_types.clone()
3158 concrete_types = receiver_concrete_types.clone()
3159 concrete_types << method_concrete_types
3160 }
3161 if receiver_concrete_types.len > 0 {
3162 for i, receiver_ct in receiver_concrete_types {
3163 if i < concrete_types.len {
3164 concrete_types[i] = receiver_ct
3165 }
3166 }
3167 }
3168 mut rec_len := 0
3169 if left_type.has_flag(.generic) {
3170 rec_sym := g.table.final_sym(g.unwrap_generic(left_type))
3171 match rec_sym.info {
3172 ast.Struct, ast.Interface, ast.SumType {
3173 rec_len += rec_sym.info.generic_types.len
3174 }
3175 else {}
3176 }
3177 }
3178 if g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0 {
3179 offset := if func.is_method { 1 } else { 0 }
3180 receiver_param_type := if func.params.len > 0 {
3181 func.params[0].typ
3182 } else {
3183 ast.no_type
3184 }
3185 mut generic_arg_idx := 0
3186 for i, arg in node.args {
3187 param := if func.is_variadic && i >= func.params.len - (offset + 1) {
3188 func.params.last()
3189 } else {
3190 func.params[offset + i]
3191 }
3192 if !param.typ.has_flag(.generic) || param.typ == receiver_param_type {
3193 continue
3194 }
3195 slot := rec_len + generic_arg_idx
3196 generic_arg_idx++
3197 // Only override if the current concrete type is still unresolved.
3198 // The checker already resolves concrete types correctly (e.g. T=int for []T with []int arg),
3199 // so we should not overwrite already-resolved values with the full argument type
3200 // (which would turn T=int into T=[]int, causing []T to resolve to [][]int).
3201 if slot < concrete_types.len {
3202 current_type := concrete_types[slot]
3203 if current_type != 0 && current_type != ast.void_type
3204 && !current_type.has_flag(.generic)
3205 && !g.type_has_unresolved_generic_parts(current_type) {
3206 // When the arg is a generic_param variable, the checker's concrete type
3207 // may be stale (from a different instantiation). Re-resolve from
3208 // g.cur_concrete_types and override if different.
3209 if arg.expr is ast.Ident && arg.expr.obj is ast.Var
3210 && (arg.expr.obj as ast.Var).ct_type_var == .generic_param && g.table.sym(param.typ).name in func.generic_names {
3211 mut resolved :=
3212 g.resolve_current_fn_generic_param_type(arg.expr.name)
3213 if (arg.is_mut || (arg.expr.obj as ast.Var).is_mut)
3214 && resolved.is_ptr() {
3215 resolved = resolved.deref()
3216 }
3217 if resolved != 0 && resolved != ast.void_type
3218 && resolved != current_type {
3219 concrete_types[slot] = resolved
3220 }
3221 }
3222 continue
3223 }
3224 }
3225 mut resolved_arg_type := ast.void_type
3226 if arg.expr is ast.Ident && arg.expr.obj is ast.Var
3227 && (arg.expr.obj as ast.Var).ct_type_var == .generic_param {
3228 resolved_arg_type = g.resolve_current_fn_generic_param_type(arg.expr.name)
3229 if (arg.is_mut || (arg.expr.obj as ast.Var).is_mut)
3230 && resolved_arg_type.is_ptr() {
3231 resolved_arg_type = resolved_arg_type.deref()
3232 }
3233 }
3234 if resolved_arg_type == ast.void_type {
3235 resolved_arg_type = g.resolved_generic_call_arg_type(arg)
3236 }
3237 if arg.expr.is_auto_deref_var() && resolved_arg_type.is_ptr()
3238 && !param.typ.is_ptr() {
3239 resolved_arg_type = resolved_arg_type.deref()
3240 }
3241 if resolved_arg_type.clear_option_and_result() == ast.none_type {
3242 continue
3243 }
3244 if resolved_arg_type != 0 && resolved_arg_type != ast.void_type
3245 && !resolved_arg_type.has_flag(.generic)
3246 && !g.type_has_unresolved_generic_parts(resolved_arg_type)
3247 && slot < concrete_types.len {
3248 g.infer_generic_call_concrete_types_from_types(func.generic_names, mut
3249 concrete_types, arg, param.typ, resolved_arg_type)
3250 }
3251 }
3252 }
3253
3254 mut call_ := unsafe { node }
3255 comptime_args := g.type_resolver.resolve_args(g.cur_fn, func, mut call_, concrete_types)
3256 if concrete_types.len > 0 {
3257 for k, v in comptime_args {
3258 slot := rec_len + k
3259 if slot < concrete_types.len {
3260 current_type := concrete_types[slot]
3261 if current_type == ast.void_type || current_type.has_flag(.generic)
3262 || g.type_has_unresolved_generic_parts(current_type) {
3263 concrete_types[slot] = g.unwrap_generic(v)
3264 }
3265 }
3266 }
3267 }
3268 // Prefer the resolved method declaration over the cached call metadata here.
3269 // Generic rechecks can leave `node.return_type_generic` stale even when the
3270 // method's concrete type arguments have since been corrected.
3271 mut return_type_candidates := []ast.Type{}
3272 if func.return_type != 0 {
3273 return_type_candidates << func.return_type
3274 }
3275 if node.return_type_generic != ast.void_type && node.return_type_generic != 0
3276 && node.return_type_generic !in return_type_candidates {
3277 return_type_candidates << node.return_type_generic
3278 }
3279 if parent_method.params.len > 0 && parent_method.return_type != 0
3280 && parent_method.return_type !in return_type_candidates {
3281 return_type_candidates << parent_method.return_type
3282 }
3283 for return_type_generic in return_type_candidates {
3284 if gen_type := g.table.convert_generic_type(return_type_generic,
3285 func.generic_names, concrete_types)
3286 {
3287 if !gen_type.has_flag(.generic) {
3288 return if node.or_block.kind == .absent {
3289 gen_type
3290 } else {
3291 gen_type.clear_option_and_result()
3292 }
3293 }
3294 }
3295 }
3296 }
3297 if parent_method.params.len > 0 && receiver_concrete_types.len > 0 {
3298 parent_return_type_generic := if node.return_type_generic != ast.void_type
3299 && node.return_type_generic != 0 {
3300 node.return_type_generic
3301 } else {
3302 parent_method.return_type
3303 }
3304 receiver_generic_names := g.table.generic_type_names(parent_method.params[0].typ)
3305 if receiver_generic_names.len == receiver_concrete_types.len {
3306 if gen_type := g.table.convert_generic_type(parent_return_type_generic,
3307 receiver_generic_names, receiver_concrete_types)
3308 {
3309 if !gen_type.has_flag(.generic) {
3310 return if node.or_block.kind == .absent {
3311 gen_type
3312 } else {
3313 gen_type.clear_option_and_result()
3314 }
3315 }
3316 }
3317 }
3318 }
3319 } else if node.is_static_method {
3320 // For static method calls like T.from(s) or T.parse(mut p) in generic contexts,
3321 // node.return_type may be stale from a previous checker instantiation.
3322 // Resolve from left_type which retains the .generic flag.
3323 if node.left_type.has_flag(.generic) && g.cur_fn != unsafe { nil }
3324 && g.cur_concrete_types.len > 0 {
3325 resolved_left := g.unwrap_generic(g.recheck_concrete_type(node.left_type))
3326 if resolved_left != ast.void_type && resolved_left != 0 {
3327 mut ret := resolved_left
3328 if node.return_type.has_flag(.result) {
3329 ret = ret.set_flag(.result)
3330 } else if node.return_type.has_flag(.option) {
3331 ret = ret.set_flag(.option)
3332 }
3333 return if node.or_block.kind == .absent {
3334 ret
3335 } else {
3336 ret.clear_option_and_result()
3337 }
3338 }
3339 }
3340 ret_type := g.unwrap_generic(g.recheck_concrete_type(node.return_type))
3341 return if node.or_block.kind == .absent {
3342 ret_type
3343 } else {
3344 ret_type.clear_option_and_result()
3345 }
3346 } else {
3347 mut fn_var_type := ast.void_type
3348 lookup_name := if node.left is ast.Ident { node.left.name } else { node.name }
3349 resolved_current_type := g.resolve_current_fn_generic_param_type(lookup_name)
3350 if resolved_current_type != 0
3351 && g.table.final_sym(g.unwrap_generic(resolved_current_type)).kind == .function {
3352 fn_var_type = g.unwrap_generic(g.recheck_concrete_type(resolved_current_type))
3353 } else if node.is_fn_var {
3354 fn_var_type = g.unwrap_generic(g.recheck_concrete_type(node.fn_var_type))
3355 }
3356 if fn_var_type == 0 {
3357 if obj := node.scope.find_var(lookup_name) {
3358 if g.table.final_sym(g.unwrap_generic(obj.typ)).kind == .function {
3359 fn_var_type = g.unwrap_generic(g.recheck_concrete_type(obj.typ))
3360 }
3361 }
3362 }
3363 if fn_var_type != 0 {
3364 fn_sym := g.table.final_sym(fn_var_type)
3365 if fn_sym.info is ast.FnType {
3366 return_type :=
3367 g.unwrap_generic(g.recheck_concrete_type(fn_sym.info.func.return_type))
3368 if return_type != 0 {
3369 return if node.or_block.kind == .absent {
3370 return_type
3371 } else {
3372 return_type.clear_option_and_result()
3373 }
3374 }
3375 }
3376 }
3377 if func := g.table.find_fn(node.name) {
3378 if func.generic_names.len > 0 {
3379 mut concrete_types := g.generic_fn_call_concrete_types(func, node)
3380 mut call_ := unsafe { node }
3381 comptime_args := g.type_resolver.resolve_args(g.cur_fn, func, mut call_,
3382 concrete_types)
3383 if concrete_types.len > 0 {
3384 for k, v in comptime_args {
3385 if k < concrete_types.len {
3386 current_type := concrete_types[k]
3387 if current_type == ast.void_type || current_type.has_flag(.generic)
3388 || g.type_has_unresolved_generic_parts(current_type) {
3389 concrete_types[k] = g.unwrap_generic(v)
3390 }
3391 }
3392 }
3393 }
3394 return_type_generic := if node.return_type_generic != ast.void_type {
3395 node.return_type_generic
3396 } else {
3397 func.return_type
3398 }
3399 if gen_type := g.table.convert_generic_type(return_type_generic,
3400 func.generic_names, concrete_types)
3401 {
3402 if !gen_type.has_flag(.generic) {
3403 return if node.or_block.kind == .absent {
3404 gen_type
3405 } else {
3406 gen_type.clear_option_and_result()
3407 }
3408 }
3409 }
3410 }
3411 }
3412 }
3413 return ast.void_type
3414}
3415
3416fn (mut g Gen) resolved_array_builtin_method_return_type(node ast.CallExpr, left_type ast.Type, fallback ast.Type) ast.Type {
3417 mut return_type := g.unwrap_generic(g.recheck_concrete_type(fallback))
3418 resolved_left_type := g.recheck_concrete_type(g.resolved_expr_type(node.left, left_type))
3419 if resolved_left_type == 0 {
3420 return return_type
3421 }
3422 resolved_left_sym := g.table.final_sym(g.unwrap_generic(resolved_left_type))
3423 if resolved_left_sym.kind !in [.array, .array_fixed] {
3424 return return_type
3425 }
3426 if node.name == 'get' {
3427 elem_type := if resolved_left_sym.info is ast.Array {
3428 resolved_left_sym.info.elem_type
3429 } else if resolved_left_sym.info is ast.ArrayFixed {
3430 resolved_left_sym.info.elem_type
3431 } else {
3432 return return_type
3433 }
3434 if elem_type == 0 {
3435 return return_type
3436 }
3437 return g.unwrap_generic(g.recheck_concrete_type(elem_type.set_flag(.option)))
3438 }
3439 match node.kind {
3440 .map {
3441 if node.args.len == 0 {
3442 return return_type
3443 }
3444 arg := node.args[0]
3445 arg_type := g.unwrap_generic(g.recheck_concrete_type(g.resolved_expr_type(arg.expr,
3446 arg.typ)))
3447 if arg_type == 0 {
3448 return return_type
3449 }
3450 arg_sym := g.table.final_sym(g.unwrap_generic(arg_type))
3451 mapped_type := match arg_sym.info {
3452 ast.FnType {
3453 if arg.expr is ast.SelectorExpr {
3454 arg_type
3455 } else {
3456 arg_sym.info.func.return_type
3457 }
3458 }
3459 else {
3460 arg_type
3461 }
3462 }
3463
3464 resolved_mapped_type := g.unwrap_generic(g.recheck_concrete_type(mapped_type))
3465 if resolved_mapped_type == 0 {
3466 return return_type
3467 }
3468 return g.table.find_or_register_array(resolved_mapped_type)
3469 }
3470 .filter, .reverse, .clone, .clone_to_depth, .repeat, .trim, .slice, .sorted,
3471 .sorted_with_compare {
3472 // These methods return a new array by value, never a pointer.
3473 // Strip pointer flags from mut receivers.
3474 mut ret := g.unwrap_generic(resolved_left_type).set_nr_muls(0)
3475 // .filter on a fixed array returns a dynamic array since the
3476 // result size is determined at runtime.
3477 if node.kind == .filter && resolved_left_sym.kind == .array_fixed {
3478 info := resolved_left_sym.info as ast.ArrayFixed
3479 ret = ast.new_type(g.table.find_or_register_array(info.elem_type))
3480 }
3481 return ret
3482 }
3483 .first, .last, .pop_left, .pop {
3484 elem_type := if resolved_left_sym.info is ast.Array {
3485 resolved_left_sym.info.elem_type
3486 } else if resolved_left_sym.info is ast.ArrayFixed {
3487 resolved_left_sym.info.elem_type
3488 } else {
3489 return return_type
3490 }
3491 if elem_type == 0 {
3492 return return_type
3493 }
3494 return g.unwrap_generic(g.recheck_concrete_type(elem_type))
3495 }
3496 else {}
3497 }
3498
3499 return return_type
3500}
3501
3502fn (mut g Gen) resolve_receiver_name(node ast.CallExpr, unwrapped_rec_type ast.Type, final_left_sym ast.TypeSymbol,
3503 left_sym ast.TypeSymbol, typ_sym ast.TypeSymbol) string {
3504 mut receiver_type_name := util.no_dots(g.cc_type(unwrapped_rec_type, false))
3505 if final_left_sym.kind == .map && node.kind in [.clone, .move] {
3506 receiver_type_name = 'map'
3507 }
3508 if final_left_sym.kind == .array && !(left_sym.kind == .alias && left_sym.has_method(node.name))
3509 && node.kind in [.clear, .repeat, .sort_with_compare, .sorted_with_compare, .push_many, .trim, .first, .last, .pop_left, .pop, .clone, .clone_to_depth, .reverse, .slice, .pointers] {
3510 if !(left_sym.info is ast.Alias && typ_sym.has_method(node.name)) {
3511 // `array_Xyz_clone` => `builtin__array_clone`
3512 receiver_type_name = 'array'
3513 }
3514 }
3515 return receiver_type_name
3516}
3517
3518fn (mut g Gen) receiver_generic_call_context(left_type ast.Type, method_name string) ([]ast.Type, ast.Fn) {
3519 mut exact_method := ast.Fn{}
3520 mut exact_method_exists := false
3521 if left_type != 0 {
3522 if method := g.receiver_exact_method_for_type(left_type, method_name) {
3523 exact_method = method
3524 exact_method_exists = true
3525 }
3526 }
3527 mut has_structured_method_for_exact := false
3528 if structured_method := g.table.find_structured_receiver_method_with_types(left_type,
3529 method_name)
3530 {
3531 if exact_method_exists {
3532 has_structured_method_for_exact = true
3533 } else {
3534 return structured_method.concrete_types.map(g.unwrap_generic(it)), structured_method.method
3535 }
3536 }
3537 rec_sym := g.table.final_sym(left_type)
3538 mut receiver_concrete_types := []ast.Type{}
3539 mut parent_method := ast.Fn{}
3540 // A method defined directly on an alias type (e.g. `fn (c Candidates) set()` where
3541 // `type Candidates = BitSet[u32]`) takes priority over the generic parent method
3542 // inherited from the aliased type. Treat it as the resolved (non-generic) method so
3543 // no generic instantiation suffix (`_T_u32`) is appended to its C name. See #27465.
3544 exact_method_on_alias := exact_method_exists && exact_method.receiver_type != 0
3545 && g.table.sym(left_type).info is ast.Alias
3546 && g.unwrap_generic(exact_method.receiver_type).set_nr_muls(0) == g.unwrap_generic(left_type).set_nr_muls(0)
3547 if has_structured_method_for_exact || exact_method_on_alias {
3548 parent_method = exact_method
3549 }
3550 keep_alias_method := exact_method_on_alias && exact_method.generic_names.len == 0
3551 match rec_sym.info {
3552 ast.Struct, ast.Interface, ast.SumType {
3553 if rec_sym.info.concrete_types.len > 0 {
3554 receiver_concrete_types = rec_sym.info.concrete_types.map(g.unwrap_generic(it))
3555 } else if rec_sym.generic_types.len == rec_sym.info.generic_types.len
3556 && rec_sym.generic_types != rec_sym.info.generic_types {
3557 receiver_concrete_types = rec_sym.generic_types.map(g.unwrap_generic(it))
3558 }
3559 if rec_sym.info.parent_type.has_flag(.generic) {
3560 parent_sym := g.table.sym(rec_sym.info.parent_type)
3561 if m := parent_sym.find_method(method_name) {
3562 if !has_structured_method_for_exact && !keep_alias_method {
3563 parent_method = m
3564 }
3565 }
3566 }
3567 }
3568 ast.GenericInst {
3569 receiver_concrete_types = rec_sym.info.concrete_types.map(g.unwrap_generic(it))
3570 if rec_sym.info.parent_idx > 0 {
3571 parent_sym := g.table.sym(ast.idx_to_type(rec_sym.info.parent_idx))
3572 if m := parent_sym.find_method(method_name) {
3573 if !has_structured_method_for_exact && !keep_alias_method {
3574 parent_method = m
3575 }
3576 }
3577 }
3578 }
3579 else {}
3580 }
3581
3582 // For generic fn-type aliases (e.g. `type ParseFunction[T] = fn(string) !T`),
3583 // the concrete instantiation is stored as FnType with generic_types holding
3584 // the concrete types. Check generic_types on the sym and its parent.
3585 if receiver_concrete_types.len == 0 {
3586 non_final_sym := g.table.sym(left_type)
3587 if non_final_sym.info is ast.GenericInst {
3588 receiver_concrete_types = non_final_sym.info.concrete_types.map(g.unwrap_generic(it))
3589 if non_final_sym.info.parent_idx > 0 {
3590 parent_sym := g.table.sym(ast.idx_to_type(non_final_sym.info.parent_idx))
3591 if m := parent_sym.find_method(method_name) {
3592 if !has_structured_method_for_exact {
3593 parent_method = m
3594 }
3595 }
3596 }
3597 } else if non_final_sym.generic_types.len > 0 && non_final_sym.parent_idx > 0
3598 && !non_final_sym.generic_types.any(it.has_flag(.generic)) {
3599 // Concrete generic instantiation (e.g. ParseFunction[string])
3600 // where generic_types holds the resolved concrete types.
3601 receiver_concrete_types = non_final_sym.generic_types.map(g.unwrap_generic(it))
3602 parent_sym := g.table.sym(ast.idx_to_type(non_final_sym.parent_idx))
3603 if m := parent_sym.find_method(method_name) {
3604 if !has_structured_method_for_exact {
3605 parent_method = m
3606 }
3607 }
3608 }
3609 }
3610 return receiver_concrete_types, parent_method
3611}
3612
3613fn (mut g Gen) resolved_generic_call_arg_type(arg ast.CallArg) ast.Type {
3614 mut arg_type := g.resolved_expr_type(arg.expr, arg.typ)
3615 if arg.expr is ast.ComptimeCall && arg.expr.method_name == 'method'
3616 && g.comptime.comptime_for_method != unsafe { nil } {
3617 sym := g.table.sym(g.unwrap_generic(arg.expr.left_type))
3618 if m := sym.find_method(g.comptime.comptime_for_method.name) {
3619 return m.return_type
3620 }
3621 }
3622 mut has_current_generic_type := false
3623 mut resolved_current_type := ast.void_type
3624 if arg.expr is ast.Ident {
3625 // In comptime variant loops, a smartcast variable's type is dynamically
3626 // resolved per variant iteration. Don't override it with the generic param
3627 // resolution (which gives the sumtype, not the variant).
3628 is_comptime_smartcast := arg.expr.obj is ast.Var && arg.expr.obj.ct_type_var == .smartcast
3629 && g.comptime.comptime_for_variant_var.len > 0
3630 if !is_comptime_smartcast {
3631 resolved_current_type = g.resolve_current_fn_generic_param_type(arg.expr.name)
3632 }
3633 if resolved_current_type != 0 {
3634 has_current_generic_type = true
3635 is_mut_source_arg := arg.expr.obj is ast.Var && arg.expr.obj.is_arg
3636 && arg.expr.obj.is_mut
3637 arg_type = if (arg.is_mut || is_mut_source_arg) && resolved_current_type.is_ptr() {
3638 resolved_current_type.deref()
3639 } else {
3640 resolved_current_type
3641 }
3642 }
3643 if arg.expr.obj is ast.Var {
3644 scope_type := g.resolved_scope_var_type(arg.expr)
3645 keep_source_generic_type := has_current_generic_type && arg.expr.obj.is_arg
3646 && arg.expr.obj.is_mut && scope_type.is_ptr() && !arg_type.is_ptr()
3647 // When the generic param type was resolved from cur_concrete_types
3648 // and is fully concrete, trust it over potentially stale scope types.
3649 skip_scope_override := has_current_generic_type && arg_type != 0
3650 && !arg_type.has_flag(.generic) && !g.type_has_unresolved_generic_parts(arg_type)
3651 if scope_type != 0 && !keep_source_generic_type && !skip_scope_override
3652 && (arg_type == 0 || arg_type.has_flag(.generic)
3653 || g.type_has_unresolved_generic_parts(arg_type)
3654 || has_current_generic_type
3655 || g.unwrap_generic(arg_type) != g.unwrap_generic(scope_type)
3656 || arg_type == ast.usize_type
3657 || (arg.expr.obj as ast.Var).smartcasts.len > 0) {
3658 arg_type = scope_type
3659 }
3660 }
3661 }
3662 if !arg.is_mut && arg.expr.is_auto_deref_var() && arg_type.is_ptr() {
3663 arg_type = arg_type.deref()
3664 }
3665 return g.unwrap_generic(g.recheck_concrete_type(arg_type))
3666}
3667
3668fn (mut g Gen) generic_param_infer_type(param ast.Param) ast.Type {
3669 if param.orig_typ != 0 && (param.orig_typ.has_flag(.generic)
3670 || g.type_has_unresolved_generic_parts(param.orig_typ)) {
3671 return param.orig_typ
3672 }
3673 return param.typ
3674}
3675
3676fn (mut g Gen) infer_generic_call_concrete_types_from_types(generic_names []string, mut concrete_types []ast.Type, arg ast.CallArg, param_typ ast.Type, arg_typ ast.Type) {
3677 g.infer_generic_call_concrete_types_from_types_inner(generic_names, mut concrete_types, arg,
3678 param_typ, arg_typ, false)
3679}
3680
3681fn (mut g Gen) infer_generic_call_concrete_types_from_types_inner(generic_names []string, mut concrete_types []ast.Type, arg ast.CallArg, param_typ ast.Type, arg_typ ast.Type, from_composite bool) {
3682 if param_typ == 0 || arg_typ == 0 {
3683 return
3684 }
3685 param_sym := g.table.sym(param_typ)
3686 arg_sym := g.table.final_sym(g.unwrap_generic(arg_typ))
3687 if (param_typ.has_flag(.option) && arg_typ.has_flag(.option))
3688 || (param_typ.has_flag(.result) && arg_typ.has_flag(.result)) {
3689 param_inner := param_typ.clear_option_and_result()
3690 if param_inner.has_flag(.generic) {
3691 gt_name := g.table.sym(param_inner).name
3692 mut inferred_type := arg_typ.clear_option_and_result()
3693 if param_inner.nr_muls() > 0 && inferred_type.nr_muls() > 0 {
3694 inferred_type = inferred_type.set_nr_muls(0)
3695 }
3696 g.set_generic_call_concrete_type(generic_names, mut concrete_types, gt_name,
3697 inferred_type)
3698 }
3699 return
3700 }
3701 if param_typ.has_flag(.generic) && param_sym.name in generic_names {
3702 mut inferred_type := if arg_typ == ast.nil_type { ast.voidptr_type } else { arg_typ }
3703 mut strip_param_ptr_levels := true
3704 // `mut` lowers only the outer call argument to a pointer. Once inference
3705 // recurses through composite wrappers like `[]T`, nested generic types
3706 // should keep their original pointer indirection.
3707 if !from_composite && arg.is_mut && !param_typ.is_ptr() && inferred_type.is_ptr() {
3708 inferred_type = inferred_type.deref()
3709 }
3710 if arg.expr is ast.PrefixExpr && arg.expr.op == .amp && param_typ.nr_muls() > 0 {
3711 inner_type := g.resolved_expr_type(arg.expr.right, arg.expr.right_type)
3712 if inner_type != 0 {
3713 inferred_type = g.unwrap_generic(g.recheck_concrete_type(inner_type))
3714 if arg.expr.right.is_auto_deref_var() && inferred_type.is_ptr() {
3715 inferred_type = inferred_type.deref()
3716 }
3717 strip_param_ptr_levels = false
3718 }
3719 } else if arg.expr.is_auto_deref_var() && inferred_type.is_ptr() {
3720 inferred_type = inferred_type.deref()
3721 }
3722 if strip_param_ptr_levels && param_typ.nr_muls() > 0 && inferred_type.nr_muls() > 0 {
3723 param_muls := param_typ.nr_muls()
3724 arg_muls := inferred_type.nr_muls()
3725 inferred_type = if arg_muls >= param_muls {
3726 inferred_type.set_nr_muls(arg_muls - param_muls)
3727 } else {
3728 inferred_type.set_nr_muls(0)
3729 }
3730 }
3731 g.set_generic_call_concrete_type(generic_names, mut concrete_types, param_sym.name,
3732 inferred_type)
3733 return
3734 }
3735 if param_sym.kind == .array && arg_sym.info is ast.Array {
3736 param_info := param_sym.array_info()
3737 g.infer_generic_call_concrete_types_from_types_inner(generic_names, mut concrete_types,
3738 arg, param_info.elem_type, arg_sym.info.elem_type, true)
3739 return
3740 }
3741 if param_sym.kind == .array_fixed && arg_sym.info is ast.ArrayFixed {
3742 param_info := param_sym.info as ast.ArrayFixed
3743 g.infer_generic_call_concrete_types_from_types_inner(generic_names, mut concrete_types,
3744 arg, param_info.elem_type, arg_sym.info.elem_type, true)
3745 return
3746 }
3747 if param_sym.kind == .map && arg_sym.info is ast.Map {
3748 param_info := param_sym.map_info()
3749 g.infer_generic_call_concrete_types_from_types_inner(generic_names, mut concrete_types,
3750 arg, param_info.key_type, arg_sym.info.key_type, true)
3751 g.infer_generic_call_concrete_types_from_types_inner(generic_names, mut concrete_types,
3752 arg, param_info.value_type, arg_sym.info.value_type, true)
3753 return
3754 }
3755 if param_sym.info is ast.FnType && arg_sym.info is ast.FnType {
3756 g.update_generic_call_concrete_types_from_fn_types(generic_names, mut concrete_types, arg,
3757 param_sym.info, arg_sym.info)
3758 return
3759 }
3760 match arg_sym.info {
3761 ast.Struct {
3762 mut generic_types := []ast.Type{}
3763 mut arg_concrete_types := []ast.Type{}
3764 if param_sym.generic_types.len > 0 {
3765 generic_types = param_sym.generic_types.clone()
3766 } else {
3767 generic_types = arg_sym.info.generic_types.clone()
3768 }
3769 arg_concrete_types = arg_sym.info.concrete_types.clone()
3770 if generic_types.len == arg_concrete_types.len {
3771 for i, gt in generic_types {
3772 gt_name := g.table.sym(gt).name
3773 g.set_generic_call_concrete_type(generic_names, mut concrete_types, gt_name,
3774 arg_concrete_types[i])
3775 }
3776 }
3777 }
3778 ast.Interface {
3779 mut generic_types := []ast.Type{}
3780 mut arg_concrete_types := []ast.Type{}
3781 if param_sym.generic_types.len > 0 {
3782 generic_types = param_sym.generic_types.clone()
3783 } else {
3784 generic_types = arg_sym.info.generic_types.clone()
3785 }
3786 arg_concrete_types = arg_sym.info.concrete_types.clone()
3787 if generic_types.len == arg_concrete_types.len {
3788 for i, gt in generic_types {
3789 gt_name := g.table.sym(gt).name
3790 g.set_generic_call_concrete_type(generic_names, mut concrete_types, gt_name,
3791 arg_concrete_types[i])
3792 }
3793 }
3794 }
3795 ast.SumType {
3796 mut generic_types := []ast.Type{}
3797 mut arg_concrete_types := []ast.Type{}
3798 if param_sym.generic_types.len > 0 {
3799 generic_types = param_sym.generic_types.clone()
3800 } else {
3801 generic_types = arg_sym.info.generic_types.clone()
3802 }
3803 arg_concrete_types = arg_sym.info.concrete_types.clone()
3804 if generic_types.len == arg_concrete_types.len {
3805 for i, gt in generic_types {
3806 gt_name := g.table.sym(gt).name
3807 g.set_generic_call_concrete_type(generic_names, mut concrete_types, gt_name,
3808 arg_concrete_types[i])
3809 }
3810 }
3811 }
3812 ast.GenericInst {
3813 parent_sym := g.table.sym(ast.new_type(arg_sym.info.parent_idx))
3814 match parent_sym.info {
3815 ast.Struct {
3816 generic_types := parent_sym.info.generic_types.clone()
3817 if generic_types.len == arg_sym.info.concrete_types.len {
3818 for i, gt in generic_types {
3819 gt_name := g.table.sym(gt).name
3820 g.set_generic_call_concrete_type(generic_names, mut concrete_types,
3821 gt_name, arg_sym.info.concrete_types[i])
3822 }
3823 }
3824 }
3825 ast.Interface {
3826 generic_types := parent_sym.info.generic_types.clone()
3827 if generic_types.len == arg_sym.info.concrete_types.len {
3828 for i, gt in generic_types {
3829 gt_name := g.table.sym(gt).name
3830 g.set_generic_call_concrete_type(generic_names, mut concrete_types,
3831 gt_name, arg_sym.info.concrete_types[i])
3832 }
3833 }
3834 }
3835 ast.SumType {
3836 generic_types := parent_sym.info.generic_types.clone()
3837 if generic_types.len == arg_sym.info.concrete_types.len {
3838 for i, gt in generic_types {
3839 gt_name := g.table.sym(gt).name
3840 g.set_generic_call_concrete_type(generic_names, mut concrete_types,
3841 gt_name, arg_sym.info.concrete_types[i])
3842 }
3843 }
3844 }
3845 else {}
3846 }
3847 }
3848 else {}
3849 }
3850}
3851
3852fn (mut g Gen) set_generic_call_concrete_type(generic_names []string, mut concrete_types []ast.Type, gt_name string, typ ast.Type) {
3853 idx := generic_names.index(gt_name)
3854 if idx < 0 || idx >= concrete_types.len {
3855 return
3856 }
3857 resolved_type := ast.mktyp(g.unwrap_generic(g.recheck_concrete_type(typ)))
3858 if resolved_type == 0 || resolved_type == ast.void_type || resolved_type.has_flag(.generic)
3859 || g.type_has_unresolved_generic_parts(resolved_type) {
3860 return
3861 }
3862 current_type := concrete_types[idx]
3863 if current_type == 0 || current_type == ast.void_type || current_type.has_flag(.generic)
3864 || g.type_has_unresolved_generic_parts(current_type) || current_type == resolved_type {
3865 concrete_types[idx] = resolved_type
3866 }
3867}
3868
3869fn (mut g Gen) update_generic_call_concrete_types_from_fn_types(generic_names []string, mut concrete_types []ast.Type, arg ast.CallArg, param_fn ast.FnType, arg_fn ast.FnType) {
3870 if param_fn.func.params.len != arg_fn.func.params.len {
3871 return
3872 }
3873 for i, fn_param in param_fn.func.params {
3874 if fn_param.typ.has_flag(.generic) {
3875 gt_name := g.table.sym(fn_param.typ).name
3876 mut inferred := arg_fn.func.params[i].typ
3877 // Strip pointer muls that come from the generic param (e.g. &T -> &int => T=int)
3878 if fn_param.typ.nr_muls() > 0 && inferred.nr_muls() > 0 {
3879 param_muls := fn_param.typ.nr_muls()
3880 arg_muls := inferred.nr_muls()
3881 inferred = if arg_muls >= param_muls {
3882 inferred.set_nr_muls(arg_muls - param_muls)
3883 } else {
3884 inferred.set_nr_muls(0)
3885 }
3886 }
3887 g.set_generic_call_concrete_type(generic_names, mut concrete_types, gt_name, inferred)
3888 }
3889 }
3890 if param_fn.func.return_type.has_flag(.generic) {
3891 gt_name := g.table.sym(param_fn.func.return_type).name
3892 mut return_type := arg_fn.func.return_type
3893 if arg.expr is ast.LambdaExpr && return_type.has_flag(.generic) {
3894 return_type = g.type_resolver.unwrap_generic_expr(arg.expr.expr, return_type)
3895 if return_type.has_flag(.generic) {
3896 idx := generic_names.index(g.table.type_to_str(return_type))
3897 if idx >= 0 && idx < concrete_types.len {
3898 return_type = concrete_types[idx]
3899 } else {
3900 return_type = ast.void_type
3901 }
3902 }
3903 }
3904 g.set_generic_call_concrete_type(generic_names, mut concrete_types, gt_name, return_type)
3905 }
3906}
3907
3908fn (g &Gen) call_expr_depends_on_comptime_context(node ast.CallExpr) bool {
3909 if g.type_resolver.info.is_comptime(node.left) {
3910 return true
3911 }
3912 for arg in node.args {
3913 if arg.ct_expr || g.type_resolver.info.is_comptime(arg.expr) {
3914 return true
3915 }
3916 }
3917 return false
3918}
3919
3920fn (mut g Gen) generic_fn_call_concrete_types(func ast.Fn, node ast.CallExpr) []ast.Type {
3921 mut concrete_types := []ast.Type{}
3922 if node.raw_concrete_types.len > 0 {
3923 concrete_types = node.raw_concrete_types.map(g.resolved_call_concrete_type(it))
3924 } else if node.concrete_types.len == func.generic_names.len && node.concrete_types.all(it != 0
3925 && !it.has_flag(.generic) && !g.type_has_unresolved_generic_parts(it)) {
3926 concrete_types = node.concrete_types.map(g.resolved_call_concrete_type(it))
3927 }
3928 explicit_raw_concrete_types := node.raw_concrete_types.len == func.generic_names.len
3929 && concrete_types.len == func.generic_names.len && concrete_types.all(it != 0
3930 && !it.has_flag(.generic) && !g.type_has_unresolved_generic_parts(it))
3931 mut trust_node_concrete_types := concrete_types.len == func.generic_names.len
3932 && concrete_types.len > 0
3933 if trust_node_concrete_types && !explicit_raw_concrete_types
3934 && g.call_expr_depends_on_comptime_context(node) {
3935 trust_node_concrete_types = false
3936 }
3937 if trust_node_concrete_types && !explicit_raw_concrete_types {
3938 if g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0 {
3939 trust_node_concrete_types = false
3940 }
3941 }
3942 if trust_node_concrete_types && !explicit_raw_concrete_types {
3943 mut muttable := unsafe { &ast.Table(g.table) }
3944 for i, arg in node.args {
3945 param := if func.is_variadic && i >= func.params.len - 1 {
3946 func.params.last()
3947 } else {
3948 func.params[i]
3949 }
3950 param_infer_typ := g.generic_param_infer_type(param)
3951 if !param_infer_typ.has_flag(.generic)
3952 && !g.type_has_unresolved_generic_parts(param_infer_typ) {
3953 continue
3954 }
3955 arg_type := g.resolved_generic_call_arg_type(arg)
3956 if arg_type == 0 {
3957 continue
3958 }
3959 if arg_type in [ast.int_literal_type, ast.float_literal_type] {
3960 continue
3961 }
3962 param_sym := g.table.sym(param_infer_typ)
3963 if (arg.expr is ast.PrefixExpr && arg.expr.op == .amp && param_infer_typ.nr_muls() > 0)
3964 || param_sym.info is ast.FnType {
3965 trust_node_concrete_types = false
3966 break
3967 }
3968 if param_sym.info is ast.FnType
3969 && (arg_type.has_flag(.generic) || g.type_has_unresolved_generic_parts(arg_type)) {
3970 continue
3971 }
3972 if resolved_param_type := muttable.convert_generic_type(param_infer_typ,
3973 func.generic_names, concrete_types)
3974 {
3975 // For variadic params, the resolved type is []T_resolved (array wrapper),
3976 // but arg_type is just T_resolved (element). Extract element type to compare.
3977 mut cmp_type := g.unwrap_generic(resolved_param_type)
3978 if param_infer_typ.has_flag(.variadic) {
3979 cmp_sym := g.table.final_sym(cmp_type)
3980 if cmp_sym.info is ast.Array {
3981 cmp_type = cmp_sym.info.elem_type
3982 }
3983 }
3984 resolved_arg_type := g.unwrap_generic(arg_type)
3985 if cmp_type != resolved_arg_type {
3986 if g.table.final_sym(cmp_type).kind == .interface
3987 && g.table.does_type_implement_interface(resolved_arg_type, cmp_type) {
3988 continue
3989 }
3990 trust_node_concrete_types = false
3991 break
3992 }
3993 } else {
3994 trust_node_concrete_types = false
3995 break
3996 }
3997 }
3998 }
3999 if !trust_node_concrete_types {
4000 concrete_types = []ast.Type{len: func.generic_names.len, init: ast.void_type}
4001 mut node_ := unsafe { node }
4002 comptime_args := g.type_resolver.resolve_args(g.cur_fn, func, mut node_, concrete_types)
4003 mut generic_arg_idx := 0
4004 for i, arg in node.args {
4005 param := if func.is_variadic && i >= func.params.len - 1 {
4006 func.params.last()
4007 } else {
4008 func.params[i]
4009 }
4010 param_infer_typ := g.generic_param_infer_type(param)
4011 if !param_infer_typ.has_flag(.generic)
4012 && !g.type_has_unresolved_generic_parts(param_infer_typ) {
4013 continue
4014 }
4015 arg_type := g.resolved_generic_call_arg_type(arg)
4016 if arg_type != 0 && !arg_type.has_flag(.generic)
4017 && !g.type_has_unresolved_generic_parts(arg_type) {
4018 if generic_arg_idx < concrete_types.len
4019 && arg_type in [ast.int_literal_type, ast.float_literal_type] {
4020 current_type := concrete_types[generic_arg_idx]
4021 if current_type != 0 && current_type != ast.void_type
4022 && !current_type.has_flag(.generic)
4023 && !g.type_has_unresolved_generic_parts(current_type) {
4024 generic_arg_idx++
4025 continue
4026 }
4027 }
4028 g.infer_generic_call_concrete_types_from_types(func.generic_names, mut
4029 concrete_types, arg, param_infer_typ, arg_type)
4030 if param_infer_typ.has_flag(.generic) && generic_arg_idx < concrete_types.len {
4031 generic_arg_idx++
4032 }
4033 }
4034 }
4035 for k, v in comptime_args {
4036 if k < concrete_types.len {
4037 current_type := concrete_types[k]
4038 if current_type == ast.void_type || current_type.has_flag(.generic)
4039 || g.type_has_unresolved_generic_parts(current_type) {
4040 concrete_types[k] = ast.mktyp(g.unwrap_generic(v))
4041 }
4042 }
4043 }
4044 // Fallback: use checker's inferred concrete types for any remaining void types
4045 // (e.g. when a generic return type R can't be re-inferred from a lambda arg in cgen)
4046 for i, ct in concrete_types {
4047 if ct == ast.void_type && i < node.concrete_types.len {
4048 fallback_type := g.resolved_call_concrete_type(node.concrete_types[i])
4049 if fallback_type != 0 && fallback_type != ast.void_type
4050 && !fallback_type.has_flag(.generic)
4051 && !g.type_has_unresolved_generic_parts(fallback_type) {
4052 concrete_types[i] = fallback_type
4053 }
4054 }
4055 }
4056 }
4057 return concrete_types.filter(it != ast.void_type)
4058}
4059
4060fn (mut g Gen) current_fn_generic_params() ([]ast.Param, []string) {
4061 if g.cur_fn == unsafe { nil } || g.cur_fn.generic_names.len == 0
4062 || g.cur_concrete_types.len == 0 {
4063 return []ast.Param{}, []string{}
4064 }
4065 if g.cur_fn.name.contains('_T_') {
4066 for generic_fn in g.file.generic_fns {
4067 if generic_fn.generic_names.len == 0 {
4068 continue
4069 }
4070 if g.generic_fn_name(g.cur_concrete_types, generic_fn.name) == g.cur_fn.name {
4071 return generic_fn.params, generic_fn.generic_names
4072 }
4073 }
4074 return g.cur_fn.params, g.cur_fn.generic_names
4075 }
4076 if func := g.table.find_fn(g.cur_fn.name) {
4077 if func.generic_names.len > 0 {
4078 return func.params, func.generic_names
4079 }
4080 return func.params, g.cur_fn.generic_names
4081 } else if g.cur_fn.mod != '' && !g.cur_fn.name.contains('.') {
4082 if func := g.table.find_fn('${g.cur_fn.mod}.${g.cur_fn.name}') {
4083 if func.generic_names.len > 0 {
4084 return func.params, func.generic_names
4085 }
4086 return func.params, g.cur_fn.generic_names
4087 }
4088 }
4089 return g.cur_fn.params, g.cur_fn.generic_names
4090}
4091
4092fn (mut g Gen) current_fn_generic_names() []string {
4093 _, generic_names := g.current_fn_generic_params()
4094 return generic_names
4095}
4096
4097fn (g &Gen) has_active_call_generic_context() bool {
4098 return g.active_call_generic_names.len > 0
4099 && g.active_call_generic_names.len == g.active_call_concrete_types.len
4100}
4101
4102fn (g &Gen) active_call_generic_concrete_types(generic_names []string) []ast.Type {
4103 if generic_names.len == 0 || !g.has_active_call_generic_context() {
4104 return []ast.Type{}
4105 }
4106 mut concrete_types := []ast.Type{cap: generic_names.len}
4107 for generic_name in generic_names {
4108 idx := g.active_call_generic_names.index(generic_name)
4109 if idx < 0 || idx >= g.active_call_concrete_types.len {
4110 return []ast.Type{}
4111 }
4112 concrete_types << g.active_call_concrete_types[idx]
4113 }
4114 return concrete_types
4115}
4116
4117fn (g &Gen) active_call_lambda_concrete_types(node ast.LambdaExpr) []ast.Type {
4118 if node.func == unsafe { nil } || node.func.decl.generic_names.len == 0 {
4119 return []ast.Type{}
4120 }
4121 concrete_types := g.active_call_generic_concrete_types(node.func.decl.generic_names)
4122 if concrete_types.len > 0 {
4123 return concrete_types
4124 }
4125 if node.call_ctx != unsafe { nil }
4126 && node.call_ctx.concrete_types.len == node.func.decl.generic_names.len {
4127 return node.call_ctx.concrete_types.clone()
4128 }
4129 return []ast.Type{}
4130}
4131
4132fn (mut g Gen) resolve_active_call_concrete_type(typ ast.Type) ast.Type {
4133 if typ == 0 {
4134 return 0
4135 }
4136 if g.has_active_call_generic_context() {
4137 mut muttable := unsafe { &ast.Table(g.table) }
4138 resolved_typ := muttable.unwrap_generic_type_ex(typ, g.active_call_generic_names,
4139 g.active_call_concrete_types, true)
4140 if resolved_typ != typ {
4141 return g.unwrap_generic(resolved_typ)
4142 }
4143 if converted_typ := muttable.convert_generic_type(typ, g.active_call_generic_names,
4144 g.active_call_concrete_types)
4145 {
4146 return g.unwrap_generic(converted_typ)
4147 }
4148 }
4149 return g.resolve_current_fn_generic_type(typ)
4150}
4151
4152fn (mut g Gen) call_args_with_context(node ast.CallExpr, generic_names []string, concrete_types []ast.Type) {
4153 old_generic_names := g.active_call_generic_names
4154 old_concrete_types := g.active_call_concrete_types
4155 if generic_names.len > 0 && generic_names.len == concrete_types.len {
4156 g.active_call_generic_names = generic_names
4157 g.active_call_concrete_types = concrete_types
4158 } else {
4159 g.active_call_generic_names = []string{}
4160 g.active_call_concrete_types = []ast.Type{}
4161 }
4162 defer {
4163 g.active_call_generic_names = old_generic_names
4164 g.active_call_concrete_types = old_concrete_types
4165 }
4166 g.call_args(node)
4167}
4168
4169fn (mut g Gen) refresh_current_generic_fn_scope_vars(node &ast.FnDecl) {
4170 if node == unsafe { nil } || node.scope == unsafe { nil } || g.cur_concrete_types.len == 0
4171 || node.generic_names.len != g.cur_concrete_types.len {
4172 return
4173 }
4174 if node.is_method {
4175 receiver_type := g.recheck_concrete_type(node.receiver.typ)
4176 if mut receiver_var := node.scope.find_var(node.receiver.name) {
4177 receiver_var.typ = receiver_type
4178 receiver_var.orig_type = ast.no_type
4179 receiver_var.smartcasts = []
4180 receiver_var.is_unwrapped = false
4181 }
4182 }
4183 for param in node.params {
4184 param_type := g.recheck_concrete_type(param.typ)
4185 if mut param_var := node.scope.find_var(param.name) {
4186 param_var.typ = param_type
4187 param_var.orig_type = ast.no_type
4188 param_var.smartcasts = []
4189 param_var.is_unwrapped = false
4190 }
4191 }
4192 g.refresh_current_generic_local_scope_vars(node.scope)
4193}
4194
4195fn (mut g Gen) refresh_current_generic_local_scope_vars(scope &ast.Scope) {
4196 if scope == unsafe { nil } {
4197 return
4198 }
4199 mut scope_ := unsafe { &ast.Scope(scope) }
4200 for name, obj in scope_.objects {
4201 match obj {
4202 ast.Var {
4203 if obj.is_arg {
4204 continue
4205 }
4206 mut var := obj
4207 if var.generic_typ != 0 {
4208 refreshed_generic_type :=
4209 g.unwrap_generic(g.recheck_concrete_type(var.generic_typ))
4210 if refreshed_generic_type != 0 {
4211 var.typ = refreshed_generic_type
4212 }
4213 }
4214 has_smartcast_state := var.orig_type != ast.no_type || var.smartcasts.len > 0
4215 || var.is_unwrapped
4216 if var.is_inherited && scope_.parent != unsafe { nil } && !has_smartcast_state {
4217 if parent_var := scope_.parent.find_var(name) {
4218 var.typ = parent_var.typ
4219 var.orig_type = parent_var.orig_type
4220 var.smartcasts = parent_var.smartcasts.clone()
4221 var.is_unwrapped = parent_var.is_unwrapped
4222 }
4223 } else if var.expr !is ast.EmptyExpr {
4224 should_resolve_expr_type := var.typ == 0
4225 || var.typ == ast.void_type || var.typ.has_flag(.generic)
4226 || g.type_has_unresolved_generic_parts(var.typ)
4227 || (var.expr is ast.StructInit && var.expr.typ_str.len > 0
4228 && g.current_fn_generic_names().index(var.expr.typ_str.all_after_last('.')) >= 0)
4229 if should_resolve_expr_type && !(var.expr is ast.Ident && var.expr.name == name) {
4230 mut resolved_type := g.resolved_expr_type(var.expr, var.typ)
4231 if resolved_type != 0 {
4232 // Mirror the checker's `:=` behavior for auto-deref vars.
4233 // Generic cgen refreshes re-evaluate initializer expressions,
4234 // so `mut x := param` must stay a value copy here as well.
4235 if g.is_auto_deref_source_ident(var.expr) && resolved_type.is_ptr() {
4236 resolved_type = resolved_type.deref()
4237 }
4238 var.typ = g.unwrap_generic(g.recheck_concrete_type(resolved_type))
4239 }
4240 } else if var.typ != 0 {
4241 var.typ = g.unwrap_generic(g.recheck_concrete_type(var.typ))
4242 }
4243 if !has_smartcast_state {
4244 var.orig_type = ast.no_type
4245 var.smartcasts = []
4246 var.is_unwrapped = false
4247 }
4248 } else if var.typ != 0
4249 && (var.typ.has_flag(.generic) || g.type_has_unresolved_generic_parts(var.typ)) {
4250 var.typ = g.unwrap_generic(g.recheck_concrete_type(var.typ))
4251 }
4252 if has_smartcast_state {
4253 if var.typ != 0 {
4254 var.typ = g.unwrap_generic(g.recheck_concrete_type(var.typ))
4255 }
4256 if var.orig_type != ast.no_type {
4257 var.orig_type = g.recheck_concrete_type(var.orig_type)
4258 }
4259 if var.smartcasts.len > 0 {
4260 var.smartcasts =
4261 var.smartcasts.filter(it != 0).map(g.recheck_concrete_type(it))
4262 }
4263 }
4264 scope_.objects[name] = var
4265 }
4266 else {}
4267 }
4268 }
4269 for child in scope_.children {
4270 g.refresh_current_generic_local_scope_vars(child)
4271 }
4272}
4273
4274fn (mut g Gen) resolve_generic_param_type_from_params(name string, params []ast.Param, generic_names []string) ast.Type {
4275 if params.len == 0 || generic_names.len == 0 {
4276 return 0
4277 }
4278 for param in params {
4279 if param.name != name {
4280 continue
4281 }
4282 mut muttable := unsafe { &ast.Table(g.table) }
4283 if resolved := muttable.convert_generic_type(param.typ, generic_names, g.cur_concrete_types) {
4284 return g.unwrap_generic(resolved)
4285 }
4286 }
4287 return 0
4288}
4289
4290fn (mut g Gen) resolve_current_fn_generic_param_type(name string) ast.Type {
4291 if g.cur_fn == unsafe { nil } || g.cur_fn.generic_names.len == 0
4292 || g.cur_concrete_types.len == 0 {
4293 return 0
4294 }
4295 if g.cur_fn.name.contains('_T_') {
4296 for generic_fn in g.file.generic_fns {
4297 if generic_fn.generic_names.len == 0 {
4298 continue
4299 }
4300 if g.generic_fn_name(g.cur_concrete_types, generic_fn.name) == g.cur_fn.name {
4301 return g.resolve_generic_param_type_from_params(name, generic_fn.params,
4302 generic_fn.generic_names)
4303 }
4304 }
4305 return g.resolve_generic_param_type_from_params(name, g.cur_fn.params,
4306 g.cur_fn.generic_names)
4307 }
4308 if func := g.table.find_fn(g.cur_fn.name) {
4309 if func.generic_names.len > 0 {
4310 return g.resolve_generic_param_type_from_params(name, func.params, func.generic_names)
4311 }
4312 return g.resolve_generic_param_type_from_params(name, func.params, g.cur_fn.generic_names)
4313 } else if g.cur_fn.mod != '' && !g.cur_fn.name.contains('.') {
4314 if func := g.table.find_fn('${g.cur_fn.mod}.${g.cur_fn.name}') {
4315 if func.generic_names.len > 0 {
4316 return g.resolve_generic_param_type_from_params(name, func.params,
4317 func.generic_names)
4318 }
4319 return g.resolve_generic_param_type_from_params(name, func.params,
4320 g.cur_fn.generic_names)
4321 }
4322 }
4323 return g.resolve_generic_param_type_from_params(name, g.cur_fn.params, g.cur_fn.generic_names)
4324}
4325
4326fn (mut g Gen) resolve_current_fn_generic_param_value_type(name string) ast.Type {
4327 params, generic_names := g.current_fn_generic_params()
4328 if params.len == 0 || generic_names.len == 0 {
4329 return 0
4330 }
4331 for param in params {
4332 if param.name != name {
4333 continue
4334 }
4335 mut muttable := unsafe { &ast.Table(g.table) }
4336 resolved_param_type := if resolved := muttable.convert_generic_type(param.typ,
4337 generic_names, g.cur_concrete_types)
4338 {
4339 g.unwrap_generic(resolved)
4340 } else {
4341 g.unwrap_generic(g.recheck_concrete_type(param.typ))
4342 }
4343 base_type := g.table.value_type(resolved_param_type)
4344 if base_type == 0 {
4345 return 0
4346 }
4347 if resolved := muttable.convert_generic_type(base_type, generic_names, g.cur_concrete_types) {
4348 return g.unwrap_generic(resolved)
4349 }
4350 return g.unwrap_generic(g.recheck_concrete_type(base_type))
4351 }
4352 return 0
4353}
4354
4355fn (mut g Gen) resolve_current_fn_generic_param_key_type(name string) ast.Type {
4356 params, generic_names := g.current_fn_generic_params()
4357 if params.len == 0 || generic_names.len == 0 {
4358 return 0
4359 }
4360 for param in params {
4361 if param.name != name {
4362 continue
4363 }
4364 mut muttable := unsafe { &ast.Table(g.table) }
4365 resolved_param_type := if resolved := muttable.convert_generic_type(param.typ,
4366 generic_names, g.cur_concrete_types)
4367 {
4368 g.unwrap_generic(resolved)
4369 } else {
4370 g.unwrap_generic(g.recheck_concrete_type(param.typ))
4371 }
4372 param_sym := g.table.final_sym(resolved_param_type)
4373 base_type := match param_sym.kind {
4374 .map { param_sym.map_info().key_type }
4375 .array, .array_fixed, .string { ast.int_type }
4376 else { ast.Type(0) }
4377 }
4378
4379 if base_type == 0 {
4380 return 0
4381 }
4382 if resolved := muttable.convert_generic_type(base_type, generic_names, g.cur_concrete_types) {
4383 return g.unwrap_generic(resolved)
4384 }
4385 return g.unwrap_generic(g.recheck_concrete_type(base_type))
4386 }
4387 return 0
4388}
4389
4390fn (mut g Gen) unwrap_receiver_type(node ast.CallExpr) (ast.Type, &ast.TypeSymbol) {
4391 mut left_type := g.unwrap_generic(node.left_type)
4392 mut match_variant_type := ast.Type(0)
4393 if node.left is ast.Ident {
4394 match_variant_type = g.current_sumtype_match_variant_type(node.left, node.left_type)
4395 if match_variant_type != 0 {
4396 left_type = match_variant_type
4397 } else {
4398 resolved_left_type := g.resolve_current_fn_generic_param_type(node.left.name)
4399 if resolved_left_type != 0 {
4400 left_type = resolved_left_type
4401 }
4402 }
4403 } else if node.left is ast.StructInit {
4404 if g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0 {
4405 si_typ := if node.left.generic_typ != 0 {
4406 node.left.generic_typ
4407 } else {
4408 node.left.typ
4409 }
4410 resolved := g.unwrap_generic(si_typ)
4411 if resolved != 0 && !resolved.has_flag(.generic) {
4412 left_type = resolved
4413 }
4414 }
4415 }
4416 if left_type == g.unwrap_generic(node.left_type) {
4417 resolved_left_type := g.type_resolver.get_type_or_default(node.left, node.left_type)
4418 if resolved_left_type != 0 {
4419 left_type = g.unwrap_generic(resolved_left_type)
4420 }
4421 }
4422 mut unwrapped_rec_type := node.receiver_type
4423 if g.cur_fn != unsafe { nil } && g.cur_fn.generic_names.len > 0 { // in generic fn
4424 unwrapped_rec_type = g.unwrap_generic(node.receiver_type)
4425 unwrapped_rec_type = g.type_resolver.unwrap_generic_expr(node.left, unwrapped_rec_type)
4426 } else { // in non-generic fn
4427 sym := g.table.sym(node.receiver_type)
4428 match sym.info {
4429 ast.Struct, ast.Interface, ast.SumType {
4430 generic_names := sym.info.generic_types.map(g.table.sym(it).name)
4431 // see comment at top of vlib/v/gen/c/utils.v
4432 mut muttable := unsafe { &ast.Table(g.table) }
4433 // If receiver_type is the generic parent but left_type is an instantiated version,
4434 // use left_type's concrete_types for the conversion
4435 mut concrete_types := sym.info.concrete_types.clone()
4436 if concrete_types.len == 0 && generic_names.len > 0 {
4437 left_sym := g.table.sym(left_type)
4438 if left_sym.info is ast.Interface {
4439 concrete_types = left_sym.info.concrete_types.clone()
4440 } else if left_sym.info is ast.Struct {
4441 concrete_types = left_sym.info.concrete_types.clone()
4442 } else if left_sym.info is ast.SumType {
4443 concrete_types = left_sym.info.concrete_types.clone()
4444 }
4445 }
4446 if utyp := muttable.convert_generic_type(node.receiver_type, generic_names,
4447 concrete_types)
4448 {
4449 unwrapped_rec_type = utyp
4450 } else if concrete_types.len > 0 && !left_type.has_flag(.generic) {
4451 // Fallback: if convert_generic_type fails (e.g. method uses
4452 // different generic param name than struct), use left_type directly
4453 unwrapped_rec_type = left_type
4454 }
4455 }
4456 else {}
4457 }
4458 }
4459 if node.from_embed_types.len == 0 && node.left is ast.Ident {
4460 if match_variant_type != 0 {
4461 unwrapped_rec_type = match_variant_type
4462 } else if node.left.obj is ast.Var {
4463 if node.left.obj.smartcasts.len > 0 {
4464 if node.left.obj.ct_type_var == .smartcast {
4465 unwrapped_rec_type =
4466 g.unwrap_generic(g.type_resolver.get_type(ast.Expr(node.left)))
4467 } else {
4468 unwrapped_rec_type = g.unwrap_generic(node.left.obj.smartcasts.last())
4469 cast_sym := g.table.sym(unwrapped_rec_type)
4470 if cast_sym.info is ast.Aggregate {
4471 unwrapped_rec_type = cast_sym.info.types[g.aggregate_type_idx]
4472 }
4473 }
4474 }
4475 }
4476 }
4477 mut typ_sym := g.table.sym(unwrapped_rec_type)
4478 mut left_sym := g.table.sym(left_type)
4479 if left_type != 0 && left_type != g.unwrap_generic(node.receiver_type)
4480 && left_sym.kind != .aggregate && left_sym.has_method(node.name)
4481 && node.from_embed_types.len == 0 {
4482 unwrapped_rec_type = left_type
4483 typ_sym = left_sym
4484 }
4485 // non-option alias type that undefined this method (not include `str`) need to use parent type
4486 if !left_type.has_flag(.option) && mut typ_sym.info is ast.Alias && node.kind != .str
4487 && !typ_sym.has_method(node.name) {
4488 unwrapped_rec_type = typ_sym.info.parent_type
4489 typ_sym = g.table.sym(unwrapped_rec_type)
4490 } else if mut typ_sym.info is ast.Array && !typ_sym.has_method(node.name) && node.kind != .str {
4491 typ := g.table.unaliased_type(typ_sym.info.elem_type)
4492 typ_idx := g.table.find_type_idx(g.table.array_name(typ))
4493 if typ_idx > 0 {
4494 unwrapped_rec_type = ast.idx_to_type(typ_idx)
4495 typ_sym = g.table.sym(unwrapped_rec_type)
4496 }
4497 }
4498 // In generic contexts, node.from_embed_types and node.receiver_type may be stale
4499 // from a previous instantiation. Re-resolve embedded methods from the actual left_type.
4500 if g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0 {
4501 left_sym2 := g.table.final_sym(left_type)
4502 if !left_sym2.has_method(node.name) {
4503 _, embed_types := g.table.find_method_from_embeds(left_sym2, node.name) or {
4504 ast.Fn{}, []ast.Type{}
4505 }
4506 if embed_types.len > 0 {
4507 unwrapped_rec_type = embed_types.last()
4508 typ_sym = g.table.sym(unwrapped_rec_type)
4509 }
4510 } else if left_type != unwrapped_rec_type {
4511 // left_type has the method directly; use it instead of stale receiver type
4512 unwrapped_rec_type = left_type
4513 typ_sym = g.table.sym(unwrapped_rec_type)
4514 }
4515 } else if node.from_embed_types.len > 0 && !typ_sym.has_method(node.name) {
4516 unwrapped_rec_type = node.from_embed_types.last()
4517 typ_sym = g.table.sym(unwrapped_rec_type)
4518 }
4519 return unwrapped_rec_type, typ_sym
4520}
4521
4522fn (mut g Gen) is_same_generic_receiver_family(left_type ast.Type, receiver_type ast.Type) bool {
4523 if left_type in [ast.void_type, ast.no_type] || receiver_type in [ast.void_type, ast.no_type] {
4524 return false
4525 }
4526 left_sym := g.table.final_sym(g.unwrap_generic(left_type))
4527 receiver_sym := g.table.final_sym(g.unwrap_generic(receiver_type))
4528 if left_sym.kind != receiver_sym.kind {
4529 return false
4530 }
4531 match left_sym.info {
4532 ast.Struct {
4533 if receiver_sym.info is ast.Struct {
4534 left_parent := left_sym.info.parent_type
4535 receiver_parent := receiver_sym.info.parent_type
4536 return left_parent != 0 && left_parent == receiver_parent
4537 && left_parent.has_flag(.generic)
4538 }
4539 }
4540 ast.Interface {
4541 if receiver_sym.info is ast.Interface {
4542 left_parent := left_sym.info.parent_type
4543 receiver_parent := receiver_sym.info.parent_type
4544 return left_parent != 0 && left_parent == receiver_parent
4545 && left_parent.has_flag(.generic)
4546 }
4547 }
4548 ast.SumType {
4549 if receiver_sym.info is ast.SumType {
4550 left_parent := left_sym.info.parent_type
4551 receiver_parent := receiver_sym.info.parent_type
4552 return left_parent != 0 && left_parent == receiver_parent
4553 && left_parent.has_flag(.generic)
4554 }
4555 }
4556 else {}
4557 }
4558
4559 return false
4560}
4561
4562fn (mut g Gen) method_call(node ast.CallExpr) {
4563 // TODO: there are still due to unchecked exprs (opt/some fn arg)
4564 if node.left_type == 0 {
4565 g.checker_bug('CallExpr.left_type is 0 in method_call', node.pos)
4566 }
4567 if node.receiver_type == 0 {
4568 g.checker_bug('CallExpr.receiver_type is 0 in method_call', node.pos)
4569 }
4570 mut left_type := g.unwrap_generic(node.left_type)
4571 mut receiver_type := node.receiver_type
4572 match node.left {
4573 ast.Ident {
4574 match_variant_type := g.current_sumtype_match_variant_type(node.left, node.left_type)
4575 if match_variant_type != 0 {
4576 left_type = match_variant_type
4577 } else {
4578 resolved_left_type := g.resolve_current_fn_generic_param_type(node.left.name)
4579 if resolved_left_type != 0 {
4580 left_type = resolved_left_type
4581 } else if g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0 {
4582 scope_type := g.resolved_scope_var_type(node.left)
4583 if scope_type != 0 && !scope_type.has_flag(.generic)
4584 && !g.type_has_unresolved_generic_parts(scope_type) {
4585 left_type = scope_type
4586 }
4587 }
4588 }
4589 }
4590 ast.SelectorExpr {
4591 // In generic contexts, the selector's type may be stale from a previous
4592 // instantiation. Re-resolve through the struct field type hierarchy.
4593 if g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0 {
4594 resolved := g.resolved_expr_type(node.left, node.left_type)
4595 if resolved != 0 && resolved != left_type {
4596 left_type = g.unwrap_generic(resolved)
4597 }
4598 }
4599 }
4600 ast.IndexExpr {
4601 if g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0 {
4602 resolved := g.resolved_expr_type(node.left, node.left_type)
4603 if resolved != 0 && resolved != left_type {
4604 left_type = g.unwrap_generic(resolved)
4605 }
4606 }
4607 }
4608 ast.CallExpr, ast.ParExpr, ast.PostfixExpr {
4609 if g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0 {
4610 resolved := g.resolved_expr_type(node.left, node.left_type)
4611 if resolved != 0 && resolved != left_type {
4612 left_type = g.unwrap_generic(resolved)
4613 }
4614 }
4615 }
4616 ast.StructInit {
4617 // In generic contexts, node.left_type may be stale from a previous
4618 // checker instantiation (the checker overwrites AST types on each pass).
4619 // Use the struct init's own .typ which preserves the generic flag.
4620 if g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0 {
4621 si_typ := if node.left.generic_typ != 0 {
4622 node.left.generic_typ
4623 } else {
4624 node.left.typ
4625 }
4626 resolved := g.unwrap_generic(si_typ)
4627 if resolved != 0 && !resolved.has_flag(.generic) {
4628 left_type = resolved
4629 }
4630 }
4631 }
4632 else {}
4633 }
4634
4635 if left_type == g.unwrap_generic(node.left_type) {
4636 resolved_left_type := g.type_resolver.get_type_or_default(node.left, node.left_type)
4637 if resolved_left_type != 0 {
4638 left_type = g.unwrap_generic(resolved_left_type)
4639 }
4640 }
4641 raw_method_name := node.name
4642 mut method_name := node.name
4643 if t_idx := method_name.index('_T_') {
4644 base_method_name := method_name[..t_idx]
4645 if base_method_name.len > 0 && left_type != 0 {
4646 left_type_sym := g.table.sym(left_type)
4647 mut has_base_method := left_type_sym.has_method(base_method_name)
4648 || left_type_sym.has_method_with_generic_parent(base_method_name)
4649 if !has_base_method && !left_type.has_flag(.option) && left_type_sym.info is ast.Alias {
4650 unaliased_left_type := g.table.unaliased_type(left_type)
4651 unaliased_left_sym := g.table.sym(unaliased_left_type)
4652 has_base_method = unaliased_left_sym.has_method(base_method_name)
4653 || unaliased_left_sym.has_method_with_generic_parent(base_method_name)
4654 }
4655 if has_base_method {
4656 method_name = base_method_name
4657 }
4658 }
4659 }
4660 mut unwrapped_rec_type, _ := g.unwrap_receiver_type(node)
4661 if left_type != 0 && (left_type != g.unwrap_generic(node.receiver_type)
4662 || left_type != unwrapped_rec_type) {
4663 resolved_left_sym := g.table.sym(left_type)
4664 if resolved_left_sym.kind != .aggregate && (resolved_left_sym.has_method(method_name)
4665 || resolved_left_sym.has_method_with_generic_parent(method_name)) {
4666 unwrapped_rec_type = left_type
4667 receiver_type = left_type.derive(node.receiver_type).clear_flag(.generic)
4668 }
4669 }
4670 left_sym_for_alias := g.table.sym(left_type)
4671 if left_type != 0 && !left_type.has_flag(.option) && node.kind != .str
4672 && left_sym_for_alias.info is ast.Alias && !left_sym_for_alias.has_method(method_name) {
4673 unaliased_left_type := g.table.unaliased_type(left_type)
4674 unaliased_left_sym := g.table.sym(unaliased_left_type)
4675 if unaliased_left_sym.has_method(method_name)
4676 || unaliased_left_sym.has_method_with_generic_parent(method_name) {
4677 unwrapped_rec_type = unaliased_left_type
4678 receiver_type = unaliased_left_type.derive(node.receiver_type).clear_flag(.generic)
4679 }
4680 }
4681 // For interface methods inherited via embedding, the receiver_type was changed
4682 // to the parent interface, but the C function uses the original defining interface.
4683 unwrapped_rec_sym := g.table.sym(unwrapped_rec_type)
4684 if unwrapped_rec_sym.kind == .interface {
4685 if em := unwrapped_rec_sym.find_method(raw_method_name) {
4686 if em.from_embedded_type != 0 {
4687 unwrapped_rec_type = em.from_embedded_type
4688 }
4689 }
4690 }
4691 typ_sym := g.table.sym(unwrapped_rec_type)
4692 mut receiver_is_mut := false
4693 if method := typ_sym.find_method(method_name) {
4694 if method.params.len > 0 {
4695 receiver_type = receiver_type.derive(method.params[0].typ).clear_flag(.generic)
4696 receiver_is_mut = method.params[0].is_mut
4697 }
4698 }
4699 rec_cc_type := g.cc_type(unwrapped_rec_type, false)
4700 mut receiver_type_name := util.no_dots(rec_cc_type)
4701 if node.kind == .clone && node.left is ast.IndexExpr && node.left.index is ast.RangeExpr {
4702 resolved_return_type := g.unwrap_generic(g.recheck_concrete_type(node.return_type))
4703 resolved_return_sym := g.table.final_sym(resolved_return_type)
4704 if resolved_return_sym.kind == .array {
4705 array_info := resolved_return_sym.info as ast.Array
4706 array_depth := g.get_array_depth(array_info.elem_type)
4707 g.write('builtin__array_clone_static_to_depth(')
4708 g.expr(ast.Expr(node.left))
4709 g.write(', ${array_depth})')
4710 return
4711 }
4712 }
4713 if typ_sym.info is ast.Interface && typ_sym.info.defines_method(method_name) {
4714 // Speaker_name_table[s._interface_idx].speak(s._object)
4715 $if debug_interface_method_call ? {
4716 eprintln('>>> interface typ_sym.name: ${typ_sym.name} | receiver_type_name: ${receiver_type_name} | pos: ${node.pos}')
4717 }
4718
4719 methods_struct_name := g.interface_methods_struct_name(left_type)
4720 g.write('((${methods_struct_name}*)(')
4721 if node.left.is_auto_deref_var() && left_type.nr_muls() > 1 {
4722 g.write2('(', '*'.repeat(left_type.nr_muls() - 1))
4723 g.expr(node.left)
4724 g.write(')')
4725 } else {
4726 g.expr(node.left)
4727 }
4728 dot := g.dot_or_ptr(left_type)
4729 mname := c_fn_name(method_name)
4730 g.write('${dot}_methods))->_method_${mname}(')
4731 if node.left.is_auto_deref_var() && left_type.nr_muls() > 1 {
4732 g.write2('(', '*'.repeat(left_type.nr_muls() - 1))
4733 g.expr(node.left)
4734 g.write(')')
4735 } else {
4736 g.expr(node.left)
4737 }
4738 g.write('${dot}_object')
4739 is_variadic := node.expected_arg_types.len > 0
4740 && node.expected_arg_types.last().has_flag(.variadic)
4741 if node.args.len > 0 || is_variadic {
4742 g.write(', ')
4743 g.call_args(node)
4744 }
4745 g.write(')')
4746 if !node.return_type.has_option_or_result() {
4747 if g.table.final_sym(node.return_type).kind == .array_fixed {
4748 g.write('.ret_arr')
4749 }
4750 }
4751 return
4752 } else {
4753 thread_sym := typ_sym
4754 if thread_sym.info is ast.Thread {
4755 waiter_fn_name := g.gen_gohandle_name(thread_sym.info.return_type)
4756 g.create_waiter_handler(node.return_type, g.styp(thread_sym.info.return_type),
4757 waiter_fn_name)
4758 }
4759 }
4760 left_sym := g.table.sym(left_type)
4761 final_left_sym := g.table.final_sym(left_type)
4762 mut use_builtin_array_sort := false
4763 if final_left_sym.kind == .array && node.kind in [.sort, .sorted] && node.args.len > 0 {
4764 if method := left_sym.find_method(method_name) {
4765 use_builtin_array_sort = method.params.len == 1
4766 }
4767 } else if final_left_sym.kind == .array
4768 && node.kind in [.sort_with_compare, .sorted_with_compare] && node.args.len == 1 {
4769 use_builtin_array_sort = true
4770 }
4771 if final_left_sym.kind == .array && (!(left_sym.has_method(method_name)
4772 || left_sym.has_method_with_generic_parent(method_name))
4773 || use_builtin_array_sort) {
4774 if g.gen_array_method_call(node, left_type, final_left_sym) {
4775 return
4776 }
4777 }
4778 if final_left_sym.kind == .array_fixed && !(left_sym.kind == .alias
4779 && left_sym.has_method(method_name)) {
4780 if g.gen_fixed_array_method_call(node, left_type) {
4781 return
4782 }
4783 }
4784 if (final_left_sym.kind == .map || left_type.idx() == ast.map_type_idx)
4785 && !(left_sym.kind == .alias && left_sym.has_method(method_name)) {
4786 if g.gen_map_method_call(node, left_type, final_left_sym) {
4787 return
4788 }
4789 }
4790 if left_sym.kind == .array_fixed && node.kind == .wait {
4791 g.gen_fixed_array_wait(node)
4792 return
4793 }
4794 if left_sym.kind in [.sum_type, .interface] {
4795 prefix_name := if left_sym.kind == .sum_type { 'sumtype' } else { 'interface' }
4796 match node.kind {
4797 .type_name {
4798 if left_sym.kind == .sum_type {
4799 g.conversion_function_call('builtin__tos3(v_typeof_sumtype_${typ_sym.cname}',
4800 ')', node)
4801 return
4802 } else if left_sym.kind == .interface {
4803 g.conversion_function_call('builtin__charptr_vstring_literal(v_typeof_interface_${typ_sym.cname}',
4804 ')', node)
4805 return
4806 }
4807 }
4808 .type_idx {
4809 if left_sym.kind in [.sum_type, .interface] {
4810 g.conversion_function_call('v_typeof_${prefix_name}_idx_${typ_sym.cname}', '',
4811 node)
4812 return
4813 }
4814 }
4815 else {}
4816 }
4817 }
4818
4819 mut is_free_method := false
4820 if node.kind == .str {
4821 if g.gen_to_str_method_call(node, unwrapped_rec_type) {
4822 return
4823 }
4824 } else if node.kind == .free {
4825 g.register_free_method(receiver_type)
4826 is_free_method = true
4827 }
4828 mut cast_n := 0
4829 old_inside_smartcast := g.inside_smartcast
4830
4831 receiver_type_name = g.resolve_receiver_name(node, unwrapped_rec_type, final_left_sym,
4832 left_sym, typ_sym)
4833 mut name := ''
4834 if is_free_method {
4835 free_method_name := g.get_free_method(unwrapped_rec_type)
4836 name = free_method_name
4837 } else {
4838 name = util.no_dots('${receiver_type_name}_${method_name}')
4839 }
4840 if resolved_sym := g.table.find_sym(receiver_type_name) {
4841 if resolved_sym.is_builtin() && !receiver_type_name.starts_with('_') {
4842 name = 'builtin__${name}'
4843 }
4844 } else if receiver_type_name in ['int_literal', 'float_literal', 'vint_t'] {
4845 name = 'builtin__${name}'
4846 }
4847 if left_sym.kind == .chan && node.kind in [.close, .try_pop, .try_push] {
4848 name = 'sync__Channel_${method_name}'
4849 }
4850 mut is_range_slice := false
4851 if receiver_type.is_ptr() && !left_type.is_ptr() {
4852 if node.left is ast.IndexExpr {
4853 idx := node.left.index
4854 if idx is ast.RangeExpr {
4855 is_range_slice = true
4856 }
4857 }
4858 }
4859 mut receiver_concrete_types, mut parent_generic_method := g.receiver_generic_call_context(left_type,
4860 method_name)
4861 if receiver_concrete_types.len == 0 && receiver_type != 0
4862 && g.unwrap_generic(receiver_type) != left_type {
4863 receiver_concrete_types, parent_generic_method = g.receiver_generic_call_context(g.unwrap_generic(receiver_type),
4864 method_name)
4865 }
4866 parent_method_generic_names_len := parent_generic_method.generic_names.len
4867 $if trace_method_generics ? {
4868 if method_name in ['next', 'next1', 'next2', 'next3', 'method', 'another_method',
4869 'call_generic_fn', 'collect', 'encode_value', 'check_struct_type_valid'] {
4870 eprintln('>>> method_call start: raw_node_name=${raw_method_name} | method_name=${method_name} | left_type: ${g.table.type_to_str(left_type)} | receiver_type: ${g.table.type_to_str(receiver_type)} | unwrapped_rec_type: ${g.table.type_to_str(unwrapped_rec_type)} | receiver_concrete_types: ${receiver_concrete_types} | parent_method_generic_names: ${parent_generic_method.generic_names} | node.raw_concrete_types: ${node.raw_concrete_types.map(g.table.type_to_str(it))} | node.concrete_types: ${node.concrete_types.map(g.table.type_to_str(it))}')
4871 }
4872 }
4873 mut concrete_types := if node.raw_concrete_types.len > 0 {
4874 node.raw_concrete_types.map(g.resolved_call_concrete_type(it))
4875 } else {
4876 []ast.Type{}
4877 }
4878 mut concrete_types_from_resolved_args := false
4879 mut concrete_types_from_receiver := false
4880 mut trust_node_concrete_types := false
4881 mut method_generic_names_len := 0
4882 mut receiver_generic_names := []string{}
4883 mut receiver_generics_in_method := false
4884 mut receiver_method_concrete_types := []ast.Type{}
4885 mut has_method := false
4886 mut method_for_generics := ast.Fn{}
4887 if left_type != g.unwrap_generic(node.left_type)
4888 || unwrapped_rec_type != g.unwrap_generic(receiver_type) {
4889 if concrete_types.any(it == 0 || it.has_flag(.generic)
4890 || g.type_has_unresolved_generic_parts(it))
4891 {
4892 concrete_types = []
4893 }
4894 }
4895 if m := g.table.find_method(g.table.sym(left_type), method_name) {
4896 has_method = true
4897 method_for_generics = if parent_generic_method.generic_names.len > m.generic_names.len {
4898 parent_generic_method
4899 } else {
4900 m
4901 }
4902 } else if unwrapped_rec_type != left_type {
4903 // Method not found on left_type; try unwrapped_rec_type (e.g. from embed)
4904 if m2 := g.table.find_method(g.table.sym(unwrapped_rec_type), method_name) {
4905 has_method = true
4906 method_for_generics = if parent_generic_method.generic_names.len > m2.generic_names.len {
4907 parent_generic_method
4908 } else {
4909 m2
4910 }
4911 }
4912 }
4913 if !has_method && left_type != 0 {
4914 if m3 := g.table.find_method_with_embeds(g.table.sym(left_type), method_name) {
4915 has_method = true
4916 method_for_generics = if parent_generic_method.generic_names.len > m3.generic_names.len {
4917 parent_generic_method
4918 } else {
4919 m3
4920 }
4921 }
4922 }
4923 if !has_method && parent_method_generic_names_len > 0 {
4924 has_method = true
4925 method_for_generics = parent_generic_method
4926 }
4927 if has_method {
4928 if method_for_generics.params.len > 0 {
4929 receiver_type =
4930 receiver_type.derive(method_for_generics.params[0].typ).clear_flag(.generic)
4931 receiver_is_mut = method_for_generics.params[0].is_mut
4932 }
4933 method_generic_names_len = method_for_generics.generic_names.len
4934 if node.concrete_types.len == method_generic_names_len && node.concrete_types.len > 0 {
4935 trust_node_concrete_types = node.raw_concrete_types.len == method_generic_names_len
4936 && node.raw_concrete_types.len > 0
4937 // Inside a comptime $for method loop, the checker's concrete_types
4938 // are from the last iteration (the AST node is shared across
4939 // iterations). Don't trust them when an arg is a ComptimeCall.
4940 if trust_node_concrete_types && g.comptime.comptime_for_method != unsafe { nil } {
4941 if node.args.any(it.expr is ast.ComptimeCall) {
4942 trust_node_concrete_types = false
4943 }
4944 }
4945 if !trust_node_concrete_types {
4946 trust_node_concrete_types = true
4947 mut muttable := unsafe { &ast.Table(g.table) }
4948 for i, arg in node.args {
4949 param := if method_for_generics.is_variadic
4950 && i >= method_for_generics.params.len - 1 {
4951 method_for_generics.params.last()
4952 } else {
4953 method_for_generics.params[i + 1]
4954 }
4955 param_infer_typ := g.generic_param_infer_type(param)
4956 if resolved_param_type := muttable.convert_generic_type(param_infer_typ,
4957 method_for_generics.generic_names, node.concrete_types)
4958 {
4959 mut arg_type := g.resolved_generic_call_arg_type(arg)
4960 if arg_type in [ast.int_literal_type, ast.float_literal_type] {
4961 continue
4962 }
4963 if arg.expr.is_auto_deref_var() && arg_type.is_ptr()
4964 && !param_infer_typ.is_ptr() {
4965 arg_type = arg_type.deref()
4966 }
4967 param_sym := g.table.sym(param_infer_typ)
4968 if (arg.expr is ast.PrefixExpr && arg.expr.op == .amp
4969 && param_infer_typ.nr_muls() > 0)
4970 || (arg.expr.is_auto_deref_var()
4971 && g.resolved_generic_call_arg_type(arg).is_ptr()
4972 && !param_infer_typ.is_ptr())
4973 || param_sym.info is ast.FnType {
4974 trust_node_concrete_types = false
4975 break
4976 }
4977 if g.unwrap_generic(resolved_param_type) != g.unwrap_generic(arg_type) {
4978 trust_node_concrete_types = false
4979 break
4980 }
4981 } else {
4982 trust_node_concrete_types = false
4983 break
4984 }
4985 }
4986 }
4987 }
4988 }
4989 method_receiver_names_for_full := g.method_receiver_generic_names(method_for_generics)
4990 parent_receiver_names_for_full := if parent_generic_method.params.len > 0 {
4991 g.method_receiver_generic_names(parent_generic_method)
4992 } else {
4993 []string{}
4994 }
4995 use_parent_generic_method := parent_method_generic_names_len > method_generic_names_len
4996 || (method_receiver_names_for_full.len == 0 && parent_receiver_names_for_full.len > 0)
4997 full_method_generic_names_len := if use_parent_generic_method {
4998 parent_method_generic_names_len
4999 } else {
5000 method_generic_names_len
5001 }
5002 full_method := if use_parent_generic_method {
5003 parent_generic_method
5004 } else {
5005 method_for_generics
5006 }
5007 full_method_generic_names := if use_parent_generic_method {
5008 parent_generic_method.generic_names.clone()
5009 } else {
5010 method_for_generics.generic_names.clone()
5011 }
5012 if has_method {
5013 receiver_generic_names = g.method_receiver_generic_names(full_method)
5014 receiver_only_generic_method := receiver_generic_names.len > 0
5015 && full_method.generic_names.len == 0
5016 receiver_prefixed_generic_method := receiver_generic_names.len > 0
5017 && full_method.generic_names.len >= receiver_generic_names.len
5018 && full_method.generic_names[..receiver_generic_names.len] == receiver_generic_names
5019 receiver_generics_in_method = receiver_concrete_types.len >= receiver_generic_names.len
5020 && (receiver_only_generic_method || receiver_prefixed_generic_method)
5021 if receiver_generics_in_method {
5022 receiver_method_concrete_types =
5023 receiver_concrete_types[..receiver_generic_names.len].clone()
5024 }
5025 }
5026 if has_method && method_generic_names_len > 0 && g.cur_fn != unsafe { nil }
5027 && g.cur_concrete_types.len > 0 {
5028 mut resolved_arg_types := []ast.Type{}
5029 expected_arg_generic_slots :=
5030 int_max(method_generic_names_len - receiver_method_concrete_types.len, 0)
5031 if expected_arg_generic_slots > 0 {
5032 offset := if method_for_generics.is_method { 1 } else { 0 }
5033 for i, arg in node.args {
5034 param := if method_for_generics.is_variadic
5035 && i >= method_for_generics.params.len - (offset + 1) {
5036 method_for_generics.params.last()
5037 } else {
5038 method_for_generics.params[offset + i]
5039 }
5040 param_infer_typ := g.generic_param_infer_type(param)
5041 if !param_infer_typ.has_flag(.generic)
5042 && !g.type_has_unresolved_generic_parts(param_infer_typ) {
5043 continue
5044 }
5045 param_sym := g.table.sym(param_infer_typ)
5046 if param_sym.name !in method_for_generics.generic_names {
5047 continue
5048 }
5049 if param.typ == method_for_generics.params[0].typ {
5050 continue
5051 }
5052 if arg.expr is ast.Ident && arg.expr.obj is ast.Var
5053 && (arg.expr.obj as ast.Var).ct_type_var == .generic_param {
5054 mut resolved_arg_type := g.resolve_current_fn_generic_param_type(arg.expr.name)
5055 if (arg.is_mut || (arg.expr.obj as ast.Var).is_mut)
5056 && resolved_arg_type.is_ptr() {
5057 resolved_arg_type = resolved_arg_type.deref()
5058 }
5059 if resolved_arg_type != 0 {
5060 resolved_arg_types << resolved_arg_type
5061 }
5062 }
5063 }
5064 }
5065 if !trust_node_concrete_types && resolved_arg_types.len == expected_arg_generic_slots {
5066 concrete_types = receiver_method_concrete_types.clone()
5067 concrete_types << resolved_arg_types
5068 concrete_types_from_resolved_args = true
5069 }
5070 }
5071 if concrete_types.len == 0 && receiver_method_concrete_types.len > 0
5072 && (!has_method || method_generic_names_len == 0 || receiver_generics_in_method) {
5073 concrete_types = receiver_method_concrete_types.clone()
5074 concrete_types_from_receiver = true
5075 }
5076 if has_method {
5077 name = g.specialized_method_name_from_receiver_context(full_method,
5078 receiver_method_concrete_types, parent_generic_method, name)
5079 }
5080 if has_method && full_method_generic_names_len == 0 {
5081 concrete_types = []ast.Type{}
5082 }
5083 recovered_concrete_types := g.recover_method_call_concrete_types_from_name(raw_method_name,
5084 method_name, left_type, method_for_generics, parent_generic_method)
5085 if recovered_concrete_types.len > concrete_types.len || concrete_types.any(it.has_flag(.generic)
5086 || g.type_has_unresolved_generic_parts(it)) {
5087 if recovered_concrete_types.len > 0 {
5088 concrete_types = recovered_concrete_types.clone()
5089 }
5090 }
5091 if concrete_types.len == 0 && has_method && full_method_generic_names_len > 0 {
5092 concrete_types = []ast.Type{len: full_method_generic_names_len, init: ast.void_type}
5093 }
5094 if concrete_types.len > 0 && !concrete_types_from_receiver
5095 && receiver_method_concrete_types.len > 0
5096 && full_method_generic_names_len > concrete_types.len
5097 && receiver_method_concrete_types.len + concrete_types.len == full_method_generic_names_len {
5098 method_concrete_types := concrete_types.clone()
5099 concrete_types = receiver_method_concrete_types.clone()
5100 concrete_types << method_concrete_types
5101 }
5102 if receiver_method_concrete_types.len > 0 {
5103 for i, receiver_ct in receiver_method_concrete_types {
5104 if i < concrete_types.len {
5105 concrete_types[i] = receiver_ct
5106 }
5107 }
5108 }
5109 if concrete_types.len > 0 {
5110 specialized_suffix := g.generic_fn_name(concrete_types, '')
5111 $if trace_method_generics ? {
5112 if method_name in ['next', 'next1', 'next2', 'next3', 'method', 'another_method',
5113 'call_generic_fn', 'collect', 'encode_value', 'check_struct_type_valid'] {
5114 eprintln('>>> method_call pre-name: ${method_name} | concrete_types: ${concrete_types.map(g.table.type_to_str(it))} | receiver_concrete_types: ${receiver_concrete_types.map(g.table.type_to_str(it))} | has_method: ${has_method} | method_generic_names_len: ${method_generic_names_len} | full_method_generic_names: ${full_method_generic_names} | name_before: ${name}')
5115 }
5116 }
5117 name_already_specialized := specialized_suffix != '' && name.ends_with(specialized_suffix)
5118 rec_len := receiver_method_concrete_types.len
5119 if has_method {
5120 expected_method_generic_names_len := if full_method_generic_names_len > method_generic_names_len {
5121 full_method_generic_names_len
5122 } else {
5123 method_generic_names_len
5124 }
5125 if expected_method_generic_names_len > concrete_types.len {
5126 concrete_types << []ast.Type{len: expected_method_generic_names_len - concrete_types.len, init: ast.void_type}
5127 }
5128 mut node_ := unsafe { node }
5129 comptime_args := g.type_resolver.resolve_args(g.cur_fn, method_for_generics, mut node_,
5130 concrete_types)
5131 receiver_param_type := if method_for_generics.params.len > 0 {
5132 method_for_generics.params[0].typ
5133 } else {
5134 ast.no_type
5135 }
5136 offset := if method_for_generics.is_method { 1 } else { 0 }
5137 mut method_generic_param_slots := []int{}
5138 mut raw_generic_idx := -1
5139 for i in 0 .. node.args.len {
5140 param := if method_for_generics.is_variadic
5141 && i >= method_for_generics.params.len - (offset + 1) {
5142 method_for_generics.params.last()
5143 } else {
5144 method_for_generics.params[offset + i]
5145 }
5146 param_infer_typ := g.generic_param_infer_type(param)
5147 if !param_infer_typ.has_flag(.generic)
5148 && !g.type_has_unresolved_generic_parts(param_infer_typ) {
5149 continue
5150 }
5151 raw_generic_idx++
5152 if param.typ == receiver_param_type {
5153 continue
5154 }
5155 method_generic_param_slots << raw_generic_idx
5156 }
5157 mut generic_arg_idx := 0
5158 for i, arg in node.args {
5159 param := if method_for_generics.is_variadic
5160 && i >= method_for_generics.params.len - (offset + 1) {
5161 method_for_generics.params.last()
5162 } else {
5163 method_for_generics.params[offset + i]
5164 }
5165 param_infer_typ := g.generic_param_infer_type(param)
5166 if !param_infer_typ.has_flag(.generic)
5167 && !g.type_has_unresolved_generic_parts(param_infer_typ) {
5168 continue
5169 }
5170 if param.typ == receiver_param_type {
5171 continue
5172 }
5173 slot := rec_len + generic_arg_idx
5174 if slot < concrete_types.len {
5175 mut arg_type := g.resolved_generic_call_arg_type(arg)
5176 if arg.expr.is_auto_deref_var() && arg_type.is_ptr()
5177 && !param_infer_typ.is_ptr() {
5178 arg_type = arg_type.deref()
5179 }
5180 current_type := concrete_types[slot]
5181 if current_type != 0 && current_type != ast.void_type
5182 && !current_type.has_flag(.generic)
5183 && !g.type_has_unresolved_generic_parts(current_type)
5184 && arg_type in [ast.int_literal_type, ast.float_literal_type] {
5185 generic_arg_idx++
5186 continue
5187 }
5188 mut explicit_slot := slot
5189 if receiver_method_concrete_types.len > 0 && node.raw_concrete_types.len > 0
5190 && full_method_generic_names_len > node.raw_concrete_types.len
5191 && receiver_method_concrete_types.len + node.raw_concrete_types.len == full_method_generic_names_len {
5192 explicit_slot = slot - rec_len
5193 }
5194 mut explicit_concrete_type := ast.no_type
5195 if explicit_slot >= 0 && explicit_slot < node.raw_concrete_types.len {
5196 explicit_concrete_type =
5197 g.resolved_call_concrete_type(node.raw_concrete_types[explicit_slot])
5198 } else if slot < node.concrete_types.len {
5199 explicit_concrete_type =
5200 g.resolved_call_concrete_type(node.concrete_types[slot])
5201 }
5202 if arg.expr is ast.PrefixExpr && arg.expr.op == .amp
5203 && param_infer_typ.nr_muls() > 0 {
5204 inner_type := g.resolved_expr_type(arg.expr.right, arg.expr.right_type)
5205 mut resolved_inner_type :=
5206 g.unwrap_generic(g.recheck_concrete_type(inner_type))
5207 if arg.expr.right.is_auto_deref_var() && resolved_inner_type.is_ptr() {
5208 resolved_inner_type = resolved_inner_type.deref()
5209 }
5210 if resolved_inner_type != 0 && !resolved_inner_type.has_flag(.generic)
5211 && !g.type_has_unresolved_generic_parts(resolved_inner_type) {
5212 concrete_types[slot] = resolved_inner_type
5213 generic_arg_idx++
5214 continue
5215 }
5216 }
5217 if explicit_concrete_type != 0 {
5218 node_concrete_type := explicit_concrete_type
5219 if g.type_has_unresolved_generic_parts(param_infer_typ)
5220 && node_concrete_type == arg_type && !trust_node_concrete_types {
5221 // Composite generic params like `fn (T) U` need their inner concrete
5222 // type inferred from the wrapper type instead of reusing the wrapper.
5223 } else if node_concrete_type != 0 && !node_concrete_type.has_flag(.generic)
5224 && !g.type_has_unresolved_generic_parts(node_concrete_type)
5225 && (trust_node_concrete_types || node_concrete_type == arg_type) {
5226 concrete_types[slot] = node_concrete_type
5227 generic_arg_idx++
5228 continue
5229 }
5230 }
5231 if arg_type != 0 && !arg_type.has_flag(.generic)
5232 && !g.type_has_unresolved_generic_parts(arg_type) {
5233 if g.type_has_unresolved_generic_parts(param_infer_typ) {
5234 prev_concrete_types := concrete_types.clone()
5235 g.infer_generic_call_concrete_types_from_types(full_method_generic_names, mut
5236 concrete_types, arg, param_infer_typ, arg_type)
5237 if concrete_types != prev_concrete_types {
5238 generic_arg_idx++
5239 continue
5240 }
5241 inferred_type := concrete_types[slot]
5242 if inferred_type != 0 && !inferred_type.has_flag(.generic)
5243 && !g.type_has_unresolved_generic_parts(inferred_type) {
5244 generic_arg_idx++
5245 continue
5246 }
5247 }
5248 param_sym := g.table.sym(param_infer_typ)
5249 arg_sym := g.table.final_sym(g.unwrap_generic(arg_type))
5250 if param_sym.kind == .array && arg_sym.info is ast.Array {
5251 concrete_types[slot] = g.unwrap_generic(arg_sym.info.elem_type)
5252 generic_arg_idx++
5253 continue
5254 }
5255 if param_sym.kind == .array_fixed && arg_sym.info is ast.ArrayFixed {
5256 concrete_types[slot] = g.unwrap_generic(arg_sym.info.elem_type)
5257 generic_arg_idx++
5258 continue
5259 }
5260 if param_sym.kind == .map && arg_sym.info is ast.Map {
5261 concrete_types[slot] = g.unwrap_generic(arg_sym.info.key_type)
5262 if slot + 1 < concrete_types.len {
5263 concrete_types[slot + 1] = g.unwrap_generic(arg_sym.info.value_type)
5264 }
5265 generic_arg_idx += 2
5266 continue
5267 }
5268 concrete_types[slot] = ast.mktyp(arg_type)
5269 }
5270 }
5271 generic_arg_idx++
5272 }
5273 for raw_k, v in comptime_args {
5274 if raw_k !in method_generic_param_slots {
5275 continue
5276 }
5277 method_slot := method_generic_param_slots.index(raw_k)
5278 if method_slot == -1 {
5279 continue
5280 }
5281 slot := rec_len + method_slot
5282 if slot < concrete_types.len {
5283 current_type := concrete_types[slot]
5284 if !trust_node_concrete_types && !concrete_types_from_resolved_args
5285 && (current_type == ast.void_type || current_type.has_flag(.generic)
5286 || g.type_has_unresolved_generic_parts(current_type)) {
5287 concrete_types[slot] = g.unwrap_generic(v)
5288 }
5289 }
5290 }
5291 // When the receiver's generics are part of the method's generic params,
5292 // arg-resolved concrete types may be stale (scope var types from a
5293 // different checker pass). Validate node.concrete_types against
5294 // receiver_method_concrete_types: if receiver positions match, the checker
5295 // processed this instantiation, so trust the method's own positions.
5296 if receiver_generics_in_method && !trust_node_concrete_types
5297 && !concrete_types_from_resolved_args
5298 && node.concrete_types.len == full_method_generic_names_len && rec_len > 0 {
5299 mut receiver_validated := true
5300 for i in 0 .. rec_len {
5301 if i >= node.concrete_types.len {
5302 receiver_validated = false
5303 break
5304 }
5305 node_ct := ast.mktyp(g.unwrap_generic(node.concrete_types[i]))
5306 if node_ct != receiver_method_concrete_types[i] {
5307 receiver_validated = false
5308 break
5309 }
5310 }
5311 if receiver_validated {
5312 for i in rec_len .. concrete_types.len {
5313 if i < node.concrete_types.len {
5314 node_ct := g.resolved_call_concrete_type(node.concrete_types[i])
5315 if node_ct != 0 && !node_ct.has_flag(.generic)
5316 && !g.type_has_unresolved_generic_parts(node_ct) {
5317 concrete_types[i] = node_ct
5318 }
5319 }
5320 }
5321 }
5322 }
5323 concrete_types = concrete_types.filter(it != ast.void_type)
5324 mut name_fkey := ''
5325 if _ := g.table.find_structured_receiver_method_with_types(left_type, method_name) {
5326 if exact_method := g.receiver_exact_method_for_type(left_type, method_name) {
5327 name_fkey = g.method_decl_fkey(exact_method)
5328 }
5329 }
5330 if name_fkey == '' {
5331 name_fkey = g.resolve_method_decl_fkey_for_type(left_type, method_name)
5332 }
5333 if name_fkey == '' {
5334 name_fkey = g.method_decl_fkey(full_method)
5335 }
5336 if name_fkey != '' && full_method_generic_names_len > 0
5337 && concrete_types.len == full_method_generic_names_len
5338 && concrete_types.all(!it.has_flag(.generic)
5339 && !g.type_has_unresolved_generic_parts(it)) {
5340 g.table.register_fn_concrete_types(name_fkey, concrete_types)
5341 }
5342 raw_method_suffix := g.raw_method_name_suffix(raw_method_name, method_name,
5343 full_method_generic_names)
5344 mut name_concrete_types := []ast.Type{}
5345 mut method_name_already_specialized := false
5346 mut can_use_raw_method_suffix := raw_method_suffix != ''
5347 if can_use_raw_method_suffix {
5348 expected_name_concrete_types := if recovered_concrete_types.len > 0 {
5349 recovered_concrete_types.clone()
5350 } else {
5351 g.method_name_concrete_types(name_fkey, concrete_types,
5352 receiver_method_concrete_types)
5353 }
5354 if expected_name_concrete_types.len > 0
5355 && g.generic_fn_name(expected_name_concrete_types, method_name) != raw_method_name {
5356 can_use_raw_method_suffix = false
5357 }
5358 }
5359 if can_use_raw_method_suffix {
5360 method_name_already_specialized = name.ends_with(raw_method_suffix)
5361 if !name_already_specialized {
5362 name += raw_method_suffix
5363 }
5364 } else {
5365 raw_name_concrete_types := g.raw_method_name_concrete_types(raw_method_name,
5366 method_name, node.raw_concrete_types, receiver_method_concrete_types)
5367 mut placeholder_raw_name_concrete_types := []ast.Type{}
5368 if raw_name_concrete_types.len == 0 && node.raw_concrete_types.len > 0
5369 && full_method_generic_names.any(raw_method_name.contains('_T_${it}')) {
5370 mut resolved_raw_concrete_types := []ast.Type{}
5371 mut all_raw_concrete_types_resolved := true
5372 for concrete_type in node.raw_concrete_types {
5373 resolved_concrete_type := g.resolved_call_concrete_type(concrete_type)
5374 if resolved_concrete_type == 0 || resolved_concrete_type.has_flag(.generic)
5375 || g.type_has_unresolved_generic_parts(resolved_concrete_type) {
5376 all_raw_concrete_types_resolved = false
5377 break
5378 }
5379 resolved_raw_concrete_types << resolved_concrete_type
5380 }
5381 if all_raw_concrete_types_resolved {
5382 placeholder_raw_name_concrete_types = resolved_raw_concrete_types.clone()
5383 }
5384 }
5385 name_concrete_types = if raw_name_concrete_types.len > 0 {
5386 raw_name_concrete_types.clone()
5387 } else if placeholder_raw_name_concrete_types.len > 0 {
5388 placeholder_raw_name_concrete_types
5389 } else if recovered_concrete_types.len > 0 && (concrete_types.len == 0
5390 || concrete_types.any(it == ast.void_type
5391 || it.has_flag(.generic)
5392 || g.type_has_unresolved_generic_parts(it))) {
5393 recovered_concrete_types.clone()
5394 } else {
5395 g.method_name_concrete_types(name_fkey, concrete_types,
5396 receiver_method_concrete_types)
5397 }
5398 method_suffix := g.generic_fn_name(name_concrete_types, '')
5399 method_name_already_specialized = method_suffix != ''
5400 && name.ends_with(method_suffix)
5401 if !name_already_specialized && !method_name_already_specialized
5402 && name_concrete_types.len > 0 {
5403 name = g.generic_fn_name(name_concrete_types, name)
5404 }
5405 }
5406 $if trace_method_generics ? {
5407 if method_name in ['next', 'next1', 'next2', 'next3', 'method', 'another_method',
5408 'call_generic_fn', 'collect', 'encode_value', 'check_struct_type_valid'] {
5409 eprintln('>>> method_call named: ${method_name} | name_concrete_types: ${name_concrete_types.map(g.table.type_to_str(it))} | final_name: ${name}')
5410 }
5411 }
5412 } else if !(node.kind == .str && receiver_concrete_types.len > 0)
5413 && !name_already_specialized {
5414 name = g.generic_fn_name(concrete_types, name)
5415 $if trace_method_generics ? {
5416 if method_name in ['next', 'next1', 'next2', 'next3', 'method', 'another_method',
5417 'call_generic_fn', 'collect', 'encode_value', 'check_struct_type_valid'] {
5418 eprintln('>>> method_call named no-method: ${method_name} | concrete_types: ${concrete_types.map(g.table.type_to_str(it))} | final_name: ${name}')
5419 }
5420 }
5421 }
5422 }
5423 mut free_receiver_heap_copy := ''
5424 if is_free_method && has_method && node.left is ast.Ident && !left_type.is_ptr()
5425 && free_method_calls_free_on_receiver(full_method) {
5426 left_ident := node.left as ast.Ident
5427 if !g.resolved_ident_is_auto_heap(left_ident) {
5428 stmt_line := g.go_before_last_stmt()
5429 alloc_size := '(sizeof(${rec_cc_type}) == 0) ? 1 : sizeof(${rec_cc_type})'
5430 g.empty_line = true
5431 free_receiver_heap_copy = g.new_tmp_var()
5432 g.writeln('${rec_cc_type}* ${free_receiver_heap_copy} = (${rec_cc_type}*)builtin__vcalloc(${alloc_size});')
5433 g.writeln('*${free_receiver_heap_copy} = ${c_name(left_ident.name)};')
5434 g.write(stmt_line)
5435 }
5436 }
5437 receiver_needs_ref := receiver_type.is_ptr() || receiver_is_mut
5438 // g.generate_tmp_autofree_arg_vars(node, name)
5439 if !receiver_needs_ref && left_type.is_ptr() && node.kind == .str {
5440 if left_type.is_int_valptr() {
5441 g.write('builtin__ptr_str(')
5442 } else {
5443 g.gen_expr_to_string(node.left, left_type)
5444 return
5445 }
5446 } else if receiver_needs_ref && left_type.is_ptr() && node.kind == .str
5447 && !left_sym.has_method('str') {
5448 g.gen_expr_to_string(node.left, left_type)
5449 return
5450 } else {
5451 if g.cur_fn != unsafe { nil } && g.cur_fn.trace_fns.len > 0 {
5452 g.gen_trace_call(node, name)
5453 g.write('(')
5454 } else {
5455 g.write('${name}(')
5456 }
5457 }
5458 is_interface := left_sym.kind == .interface && g.table.sym(receiver_type).kind == .interface
5459 mut receiver_expr_is_addressable := node.left.is_lvalue()
5460 match node.left {
5461 ast.IndexExpr {
5462 if node.left.left_type != 0 {
5463 indexed_container_sym := g.table.final_sym(g.unwrap_generic(node.left.left_type))
5464 if indexed_container_sym.kind == .string && !node.left.left_type.is_ptr() {
5465 receiver_expr_is_addressable = false
5466 }
5467 }
5468 }
5469 else {}
5470 }
5471
5472 if free_receiver_heap_copy == '' && receiver_needs_ref && (!left_type.is_ptr()
5473 || node.from_embed_types.len != 0 || (left_type.has_flag(.shared_f) && node.kind != .str)) {
5474 // The receiver is a reference, but the caller provided a value
5475 // Add `&` automatically.
5476 // TODO: same logic in call_args()
5477 if !is_range_slice {
5478 if !receiver_expr_is_addressable {
5479 if node.left.is_as_cast() {
5480 g.inside_smartcast = true
5481 if node.left is ast.SelectorExpr && !left_type.is_ptr() {
5482 g.write('&')
5483 }
5484 } else {
5485 g.write('ADDR(${rec_cc_type}, ')
5486 cast_n++
5487 }
5488 } else if node.left is ast.Ident && g.table.is_interface_smartcast(node.left.obj) {
5489 g.write('ADDR(${rec_cc_type}, ')
5490 cast_n++
5491 } else if !(left_type.has_flag(.shared_f) && g.styp(left_type) == g.styp(receiver_type)) {
5492 g.write('&')
5493 }
5494 } else {
5495 if !left_type.is_ptr() {
5496 g.write('ADDR(${rec_cc_type}, ')
5497 cast_n++
5498 }
5499 }
5500 } else if free_receiver_heap_copy == '' && !receiver_needs_ref && left_type.is_ptr()
5501 && node.kind != .str && node.from_embed_types.len == 0 {
5502 if !left_type.has_flag(.shared_f) {
5503 g.write('*'.repeat(left_type.nr_muls()))
5504 }
5505 } else if free_receiver_heap_copy == '' && !is_range_slice && node.from_embed_types.len == 0
5506 && node.kind != .str {
5507 diff := left_type.nr_muls() - receiver_type.nr_muls()
5508 if diff > 0 {
5509 g.write('*'.repeat(diff))
5510 }
5511 }
5512
5513 if g.is_autofree && node.free_receiver && (!g.inside_lambda || g.inside_lambda_autofree_tmp)
5514 && !g.is_builtin_mod {
5515 // The receiver expression needs to be freed, use the temp var.
5516 fn_name := node.name.replace('.', '_')
5517 arg_name := '_arg_expr_${fn_name}_0_${node.pos.pos}'
5518 g.write('/*af receiver arg*/' + arg_name)
5519 } else {
5520 if free_receiver_heap_copy != '' {
5521 g.write(free_receiver_heap_copy)
5522 } else if node.left is ast.MapInit {
5523 g.write('(map[]){')
5524 g.expr(ast.Expr(node.left))
5525 g.write('}[0]')
5526 } else if !is_interface && node.from_embed_types.len > 0 {
5527 // For mut generic params where the concrete type resolves
5528 // to a multi-pointer (e.g. mut ctx X where X = &Context →
5529 // C param is Context**), fn_decl_params adds .ref() which
5530 // adds an extra pointer level not reflected in left_type.
5531 mut extra_muls := 0
5532 if node.left is ast.Ident && node.left.obj is ast.Var {
5533 obj := node.left.obj
5534 if obj.is_auto_deref && obj.is_arg && obj.generic_typ != 0 {
5535 resolved := g.unwrap_generic(obj.generic_typ)
5536 if resolved.nr_muls() > 1 && g.table.sym(resolved).kind == .struct {
5537 extra_muls = 1
5538 }
5539 }
5540 }
5541 n_ptr := node.left_type.nr_muls() - 1 + extra_muls
5542 if n_ptr > 0 {
5543 g.write2('(', '*'.repeat(n_ptr))
5544 g.expr(ast.Expr(node.left))
5545 g.write(')')
5546 } else {
5547 g.expr(ast.Expr(node.left))
5548 }
5549 } else if is_interface && node.from_embed_types.len > 0 {
5550 if g.out.last_n(1) == '&' {
5551 g.go_back(1)
5552 }
5553 if receiver_type.is_ptr() && left_type.is_ptr() {
5554 // (main__IFoo*)bar
5555 g.write2('(', g.table.sym(node.from_embed_types.last()).cname)
5556 g.write('*)')
5557 g.expr(ast.Expr(node.left))
5558 } else if receiver_type.is_ptr() && !left_type.is_ptr() {
5559 // (main__IFoo*)&bar
5560 g.write2('(', g.table.sym(node.from_embed_types.last()).cname)
5561 g.write('*)&')
5562 g.expr(ast.Expr(node.left))
5563 } else if !receiver_type.is_ptr() && left_type.is_ptr() {
5564 // *((main__IFoo*)bar)
5565 g.write2('*((', g.table.sym(node.from_embed_types.last()).cname)
5566 g.write('*)')
5567 g.expr(ast.Expr(node.left))
5568 g.write(')')
5569 } else {
5570 // *((main__IFoo*)&bar)
5571 g.write2('*((', g.table.sym(node.from_embed_types.last()).cname)
5572 g.write('*)&')
5573 g.expr(node.left)
5574 g.write(')')
5575 }
5576 } else {
5577 if is_free_method && !receiver_type.is_ptr() {
5578 g.write('&')
5579 }
5580 g.expr(node.left)
5581 }
5582 if !is_interface || node.from_embed_types.len == 0 {
5583 mut node_embed_types := node.from_embed_types.clone()
5584 if node.left is ast.Ident && g.comptime.get_ct_type_var(node.left) == .generic_var
5585 && !final_left_sym.has_method(method_name)
5586 && !final_left_sym.has_method_with_generic_parent(method_name) {
5587 _, embed_types := g.table.find_method_from_embeds(final_left_sym, method_name) or {
5588 ast.Fn{}, []ast.Type{}
5589 }
5590 if embed_types.len > 0 {
5591 node_embed_types = embed_types.clone()
5592 }
5593 }
5594 for i, embed in node_embed_types {
5595 embed_sym := g.table.sym(embed)
5596 embed_name := embed_sym.embed_name()
5597 is_left_ptr := if i == 0 {
5598 left_type.is_ptr()
5599 } else {
5600 node_embed_types[i - 1].is_ptr()
5601 }
5602 if is_left_ptr {
5603 g.write('->')
5604 } else {
5605 g.write('.')
5606 }
5607 g.write(embed_name)
5608 }
5609 }
5610 if left_type.has_flag(.shared_f) && g.styp(left_type) != g.styp(receiver_type) {
5611 g.write('->val')
5612 }
5613 }
5614 if cast_n > 0 {
5615 g.write(')'.repeat(cast_n))
5616 }
5617 is_variadic := node.expected_arg_types.len > 0
5618 && node.expected_arg_types.last().has_flag(.variadic)
5619 if node.args.len > 0 || is_variadic {
5620 g.write(', ')
5621 }
5622 g.inside_smartcast = old_inside_smartcast
5623 g.call_args_with_context(node, full_method_generic_names, concrete_types)
5624 g.write(')')
5625 if node.return_type != 0 && !node.return_type.has_option_or_result()
5626 && g.table.final_sym(node.return_type).kind == .array_fixed {
5627 // it's non-option fixed array, requires accessing .ret_arr member to get the array
5628 g.write('.ret_arr')
5629 }
5630}
5631
5632fn (mut g Gen) fn_call(node ast.CallExpr) {
5633 // call struct field with fn type
5634 // TODO: test node.left instead
5635 // left & left_type will be `x` and `x type` in `x.fieldfn()`
5636 // will be `0` for `foo()`
5637 mut is_interface_call := false
5638 mut is_selector_call := false
5639 if node.is_method && node.left_type != 0 {
5640 mut fn_typ := ast.no_type
5641 left_sym := g.table.sym(node.left_type)
5642 if node.is_field {
5643 if field := g.table.find_field_with_embeds(left_sym, node.name) {
5644 fn_typ = g.unwrap_generic(field.typ)
5645 }
5646 if node.is_unwrapped_fn_selector {
5647 fn_typ = fn_typ.clear_option_and_result()
5648 }
5649 }
5650 if left_sym.kind == .interface || fn_typ.is_ptr() {
5651 is_interface_call = true
5652 g.write('(*')
5653 }
5654 if node.is_unwrapped_fn_selector {
5655 callback_sym := g.table.final_sym(fn_typ)
5656 if callback_sym.info is ast.FnType {
5657 g.write('(*(${g.styp(fn_typ)}*)')
5658 }
5659 }
5660 g.expr(node.left)
5661 if node.left_type.is_ptr() {
5662 g.write('->')
5663 } else {
5664 g.write('.')
5665 }
5666 for embed in node.from_embed_types {
5667 embed_sym := g.table.sym(embed)
5668 embed_name := embed_sym.embed_name()
5669 g.write(embed_name)
5670 if embed.is_ptr() {
5671 g.write('->')
5672 } else {
5673 g.write('.')
5674 }
5675 }
5676 is_selector_call = true
5677 }
5678 mut node_name := node.name // name to find on fn table
5679 mut name := node.name
5680 if node.is_static_method {
5681 // resolve static call T.name()
5682 if g.cur_fn != unsafe { nil } {
5683 _, name = g.table.convert_generic_static_type_name(node.name, g.cur_fn.generic_names,
5684 g.cur_concrete_types)
5685 if node.concrete_types.len > 0 {
5686 // Resolves T.from() to real symbol name to search on fn table
5687 node_name = name
5688 }
5689 }
5690 }
5691 mut is_builtin_print := !is_selector_call
5692 && node.kind in [.print, .println, .eprint, .eprintln, .panic]
5693 print_method := name
5694 is_json_encode := node.kind == .json_encode
5695 is_json_encode_pretty := node.kind == .json_encode_pretty
5696 is_json_decode := node.kind == .json_decode
5697 is_json_fn := is_json_encode || is_json_encode_pretty || is_json_decode
5698 is_va_arg := node.kind == .va_arg
5699 mut json_type_str := ''
5700 mut json_obj := ''
5701 if is_json_fn {
5702 g.is_json_fn = true
5703 json_obj = g.new_tmp_var()
5704 mut tmp2 := ''
5705 cur_line := g.go_before_last_stmt()
5706 if is_json_encode || is_json_encode_pretty {
5707 mut unwrapped_typ := g.unwrap_generic(node.args[0].typ)
5708 resolved_json_arg_type := g.resolved_expr_type(node.args[0].expr, node.args[0].typ)
5709 if resolved_json_arg_type != 0 {
5710 unwrapped_typ = g.unwrap_generic(g.recheck_concrete_type(resolved_json_arg_type))
5711 }
5712 g.gen_json_for_type_with_pos(unwrapped_typ, node.args[0].expr.pos())
5713 json_type_str = g.styp(unwrapped_typ)
5714 // `json__encode` => `json__encode_User`
5715 encode_name := js_enc_name(json_type_str)
5716 g.empty_line = true
5717 g.writeln('// json.encode')
5718 g.write('cJSON* ${json_obj} = ${encode_name}(')
5719 g.call_args(node)
5720 g.writeln(');')
5721 tmp2 = if g.is_autofree {
5722 '_arg_expr_${node.name.replace('.', '_')}_${node.pos.pos}'
5723 } else {
5724 g.new_tmp_var()
5725 }
5726 if is_json_encode {
5727 g.writeln('string ${tmp2} = json__json_print(${json_obj});')
5728 } else {
5729 g.writeln('string ${tmp2} = json__json_print_pretty(${json_obj});')
5730 }
5731 } else {
5732 ast_type := node.args[0].expr as ast.TypeNode
5733 // `json.decode(User, s)` => json.decode_User(s)
5734 typ := c_name(g.styp(ast_type.typ))
5735 fn_name := c_fn_name(name) + '_' + typ
5736 g.gen_json_for_type_with_pos(ast_type.typ, node.args[0].expr.pos())
5737 g.empty_line = true
5738 g.writeln('// json.decode')
5739 g.write('cJSON* ${json_obj} = json__json_parse(')
5740 // Skip the first argument in json.decode which is a type
5741 // its name was already used to generate the function call
5742 g.is_js_call = true
5743 g.call_args(node)
5744 g.writeln(');')
5745 tmp2 = g.new_tmp_var()
5746 g.writeln('${result_name}_${typ} ${tmp2} = ${fn_name}(${json_obj});')
5747 }
5748 if !g.is_autofree {
5749 g.write('cJSON_Delete(${json_obj}); // del')
5750 }
5751 g.write('\n${cur_line}')
5752 name = ''
5753 json_obj = tmp2
5754 } else if is_va_arg {
5755 ast_type := node.args[0].expr as ast.TypeNode
5756 typ := g.styp(ast_type.typ)
5757 g.write('va_arg(')
5758 g.expr(node.args[1].expr)
5759 g.write(', ${typ})')
5760 return
5761 }
5762 if node.kind == .addr {
5763 name = '&'
5764 }
5765 if node.language == .c {
5766 // Skip "C."
5767 name = g.c_call_name(node, util.no_dots(name[2..]))
5768 } else {
5769 name = if is_selector_call { c_name(name) } else { c_fn_name(name) }
5770 }
5771 mut call_generic_names := []string{}
5772 mut call_concrete_types := []ast.Type{}
5773 if g.pref.translated || g.file.is_translated || node.is_file_translated {
5774 // For `@[c: 'P_TryMove'] fn p_trymove( ... `
5775 // every time `p_trymove` is called, `P_TryMove` must be generated instead.
5776 if f := g.table.find_fn(node.name) {
5777 // TODO: PERF fn lookup for each fn call in translated mode
5778 if cattr := f.attrs.find_first('c') {
5779 name = cattr.arg
5780 }
5781 }
5782 }
5783 if !is_selector_call {
5784 if func := g.table.find_fn(node_name) {
5785 if is_builtin_print {
5786 is_builtin_print = func.mod == 'builtin'
5787 }
5788 mut concrete_types := g.generic_fn_call_concrete_types(func, node)
5789 mut node_ := unsafe { node }
5790 comptime_args := g.type_resolver.resolve_args(g.cur_fn, func, mut node_, concrete_types)
5791 if concrete_types.len > 0 {
5792 specialized_suffix := g.generic_fn_name(concrete_types, '')
5793 name_already_specialized := specialized_suffix != ''
5794 && name.ends_with(specialized_suffix)
5795 for k, v in comptime_args {
5796 if k < concrete_types.len {
5797 current_type := concrete_types[k]
5798 if current_type == ast.void_type || current_type.has_flag(.generic)
5799 || g.type_has_unresolved_generic_parts(current_type) {
5800 concrete_types[k] = g.unwrap_generic(v)
5801 }
5802 }
5803 }
5804 node_.concrete_types = concrete_types
5805 if func.generic_names.len == concrete_types.len
5806 && concrete_types.all(!it.has_flag(.generic)
5807 && !g.type_has_unresolved_generic_parts(it)) {
5808 g.table.register_fn_concrete_types(func.fkey(), concrete_types)
5809 }
5810 if !name_already_specialized {
5811 name = g.generic_fn_name(concrete_types, name)
5812 name = name.replace_each(c_fn_name_escape_seq)
5813 }
5814 }
5815 if func.generic_names.len > 0 && func.generic_names.len == concrete_types.len {
5816 call_generic_names = func.generic_names.clone()
5817 call_concrete_types = concrete_types.clone()
5818 }
5819 if func.mod == 'builtin' && !name.starts_with('builtin__') && node.language != .c {
5820 name = 'builtin__${name}'
5821 }
5822 } else if is_builtin_print && g.pref.no_builtin {
5823 is_builtin_print = false
5824 }
5825 }
5826 if node.is_fn_a_const {
5827 name = g.c_const_name(node.const_name)
5828 }
5829 // TODO2
5830 // cgen shouldn't modify ast nodes, this should be moved
5831 // g.generate_tmp_autofree_arg_vars(node, name)
5832 // Handle `print(x)`
5833 mut print_auto_str := false
5834 mut print_arg_typ := if is_builtin_print && node.args.len > 0 {
5835 node.args[0].typ
5836 } else {
5837 ast.void_type
5838 }
5839 // In generic contexts, AST-stored types may be stale from a previous
5840 // instantiation. Use resolved_expr_type for Ident expressions.
5841 if is_builtin_print && node.args.len > 0 && g.cur_fn != unsafe { nil }
5842 && g.cur_concrete_types.len > 0 && node.args[0].expr is ast.Ident {
5843 resolved := g.resolved_expr_type(node.args[0].expr, node.args[0].typ)
5844 if resolved != 0 {
5845 print_arg_typ = resolved
5846 }
5847 }
5848 if is_builtin_print && node.args.len > 0 && (print_arg_typ != ast.string_type
5849 || g.comptime.comptime_for_method != unsafe { nil } || node.args[0].ct_expr) {
5850 g.inside_interface_deref = true
5851 defer(fn) {
5852 g.inside_interface_deref = false
5853 }
5854 mut typ := print_arg_typ
5855 if typ == 0 {
5856 g.checker_bug('print arg.typ is 0', node.pos)
5857 }
5858 if typ != ast.string_type || g.comptime.comptime_for_method != unsafe { nil } {
5859 expr := node.args[0].expr
5860 if expr is ast.ComptimeSelector {
5861 if expr.typ_key != '' {
5862 typ = g.type_resolver.get_ct_type_or_default(expr.typ_key, typ)
5863 }
5864 } else if expr is ast.ComptimeCall {
5865 if expr.kind == .method {
5866 sym := g.table.sym(g.unwrap_generic(expr.left_type))
5867 if m := sym.find_method(g.comptime.comptime_for_method.name) {
5868 typ = m.return_type
5869 }
5870 }
5871 } else if expr is ast.Ident && expr.obj is ast.Var {
5872 typ = expr.obj.typ
5873 if expr.name in g.type_resolver.type_map {
5874 resolved := g.type_resolver.get_ct_type_or_default(expr.name, typ)
5875 if resolved != 0 && resolved != ast.void_type {
5876 typ = resolved
5877 }
5878 }
5879 mut ct_resolved := false
5880 if expr.ct_expr || expr.obj.ct_type_var != .no_comptime {
5881 resolved := g.type_resolver.get_type_or_default(ast.Expr(expr), typ)
5882 if resolved != 0 && resolved != ast.void_type {
5883 typ = resolved
5884 ct_resolved = true
5885 }
5886 }
5887 // In generic contexts, scope var types may be stale from a
5888 // previous checker instantiation. When ct_type_var is
5889 // generic_var or generic_param, the comptime resolver above
5890 // may return a stale type (e.g. from shared AST mutation),
5891 // so we also try the generic scope resolution.
5892 if (!ct_resolved || expr.obj.ct_type_var in [.generic_var, .generic_param])
5893 && g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0 {
5894 // For generic_param, prefer resolving from the function's
5895 // parameter declaration to avoid stale AST types.
5896 if expr.obj.ct_type_var == .generic_param {
5897 param_resolved := g.resolve_current_fn_generic_param_type(expr.name)
5898 if param_resolved != 0 {
5899 typ = param_resolved
5900 }
5901 } else {
5902 resolved := g.resolved_expr_type(expr, expr.obj.typ)
5903 if resolved != 0 {
5904 typ = resolved
5905 }
5906 }
5907 }
5908 if expr.obj.smartcasts.len > 0 {
5909 typ = g.unwrap_generic(expr.obj.smartcasts.last())
5910 cast_sym := g.table.sym(typ)
5911 if cast_sym.info is ast.Aggregate {
5912 typ = cast_sym.info.types[g.aggregate_type_idx]
5913 } else if expr.obj.ct_type_var == .smartcast {
5914 typ = g.unwrap_generic(g.type_resolver.get_type(expr))
5915 }
5916 }
5917 // handling println(var or { ... })
5918 if typ.has_flag(.option) && expr.or_expr.kind != .absent {
5919 typ = typ.clear_flag(.option)
5920 }
5921 if typ.has_flag(.generic) || g.type_has_unresolved_generic_parts(typ) {
5922 resolved_typ := g.resolved_expr_type(expr, typ)
5923 if resolved_typ != 0 && resolved_typ != ast.void_type {
5924 typ = resolved_typ
5925 }
5926 }
5927 }
5928 typ_sym := g.table.sym(typ)
5929 needs_tmp_string := !typ.has_option_or_result()
5930 && (g.is_autofree || g.pref.gc_mode == .boehm_leak)
5931 if typ_sym.kind == .interface && (typ_sym.info as ast.Interface).defines_method('str') {
5932 rec_type_name := util.no_dots(g.cc_type(typ, false))
5933 dot := if typ.is_ptr() { '->' } else { '.' }
5934 if needs_tmp_string {
5935 tmp := g.new_tmp_var()
5936 g.write('string ${tmp} = ${c_name(rec_type_name)}_name_table[')
5937 g.expr(expr)
5938 g.write('${dot}_typ]._method_str(')
5939 g.expr(expr)
5940 g.write('${dot}_object')
5941 g.writeln('); builtin__${c_fn_name(print_method)}(${tmp}); builtin__string_free(&${tmp});')
5942 } else {
5943 g.write('builtin__${c_fn_name(print_method)}(')
5944 g.write('${c_name(rec_type_name)}_name_table[')
5945 g.expr(expr)
5946 g.write('${dot}_typ]._method_str(')
5947 g.expr(expr)
5948 g.write('${dot}_object')
5949 g.writeln('));')
5950 }
5951 return
5952 }
5953 if needs_tmp_string {
5954 // Create a temporary variable so that the value can be freed
5955 tmp := g.new_tmp_var()
5956 g.write('string ${tmp} = ')
5957 g.gen_expr_to_string(expr, typ)
5958 g.writeln('; builtin__${c_fn_name(print_method)}(${tmp}); builtin__string_free(&${tmp});')
5959 } else {
5960 g.write('builtin__${c_fn_name(print_method)}(')
5961 g.gen_expr_to_string(expr, typ)
5962 g.write(')')
5963 }
5964 print_auto_str = true
5965 }
5966 }
5967 if !print_auto_str {
5968 if is_builtin_print && g.pref.gc_mode == .boehm_leak && node.args[0].typ == ast.string_type
5969 && node.args[0].expr !in [ast.Ident, ast.StringLiteral, ast.SelectorExpr, ast.ComptimeSelector] {
5970 tmp := g.new_tmp_var()
5971 tmp_init := g.autofree_tmp_arg_init_stmt('string ${tmp} = ', node.args[0].expr)
5972 g.writeln(tmp_init)
5973 g.writeln('builtin__${c_fn_name(print_method)}(${tmp});')
5974 g.writeln('builtin__string_free(&${tmp});')
5975 return
5976 }
5977 if is_builtin_print && node.args[0].expr !is ast.CallExpr {
5978 // only need for `println(err)`
5979 // not need for `println(err.msg())`
5980 g.inside_interface_deref = true
5981 defer(fn) {
5982 g.inside_interface_deref = false
5983 }
5984 }
5985 if g.pref.is_debug && is_builtin_print && node.kind == .panic {
5986 paline, pafile, pamod, pafn := g.panic_debug_info(node.pos)
5987 g.write('builtin__panic_debug(${paline}, builtin__tos3("${pafile}"), builtin__tos3("${pamod}"), builtin__tos3("${pafn}"), ')
5988 g.call_args(node)
5989 g.write(')')
5990 } else if node.name.ends_with('__static__from_string') && !g.table.known_fn(node.name) {
5991 mod_enum_name, idx := g.get_enum_type_idx_from_fn_name(node.name)
5992 fn_mod := mod_enum_name.all_before_last('.')
5993 full_fn_name := '${fn_mod}.${node.name}'
5994 fn_name := util.no_dots(full_fn_name)
5995 lock g.str_fn_names {
5996 if fn_name !in g.str_fn_names {
5997 g.gen_enum_static_from_string(fn_name, mod_enum_name, idx)
5998 g.str_fn_names << fn_name
5999 }
6000 }
6001 g.write('${fn_name}(')
6002 g.call_args(node)
6003 g.write(')')
6004 } else {
6005 // Simple function call
6006 // if free_tmp_arg_vars {
6007 // g.writeln(';')
6008 // g.write(cur_line + ' /* <== af cur line*/')
6009 // }
6010 mut is_fn_var := false
6011 if !is_selector_call {
6012 if obj := node.scope.find_var(node.name) {
6013 // Temp fix generate call fn error when the struct type of sumtype
6014 // has the fn field and is same to the struct name.
6015 mut is_cast_needed := true
6016 mut smartcast_types := obj.smartcasts.clone()
6017 if obj.orig_type.has_flag(.option) {
6018 mut unwrapped_fn_var_type := ast.no_type
6019 mut resolved_parent_value_type := ast.no_type
6020 if node.scope.parent != unsafe { nil } {
6021 if parent_var := node.scope.parent.find_var(node.name) {
6022 resolved_parent_value_type =
6023 g.unwrap_generic(g.recheck_concrete_type(parent_var.typ)).clear_option_and_result()
6024 if resolved_parent_value_type != 0
6025 && g.table.final_sym(resolved_parent_value_type).kind == .function {
6026 unwrapped_fn_var_type = resolved_parent_value_type
6027 }
6028 }
6029 }
6030 mut resolved_expr_value_type := ast.no_type
6031 if unwrapped_fn_var_type == 0 && obj.expr !is ast.EmptyExpr {
6032 resolved_expr_value_type = g.unwrap_generic(g.recheck_concrete_type(g.resolved_expr_type(obj.expr,
6033 obj.typ))).clear_option_and_result()
6034 if resolved_expr_value_type != 0
6035 && g.table.final_sym(resolved_expr_value_type).kind == .function {
6036 unwrapped_fn_var_type = resolved_expr_value_type
6037 }
6038 }
6039 resolved_scope_type := g.resolved_scope_var_type(ast.Ident{
6040 name: node.name
6041 scope: node.scope
6042 })
6043 if unwrapped_fn_var_type == 0 && resolved_scope_type != 0 {
6044 resolved_scope_value_type :=
6045 g.unwrap_generic(g.recheck_concrete_type(resolved_scope_type)).clear_option_and_result()
6046 if resolved_scope_value_type != 0
6047 && g.table.final_sym(resolved_scope_value_type).kind == .function {
6048 unwrapped_fn_var_type = resolved_scope_value_type
6049 }
6050 }
6051 if unwrapped_fn_var_type == 0 {
6052 unwrapped_fn_var_type =
6053 g.unwrap_generic(g.recheck_concrete_type(obj.typ.clear_option_and_result()))
6054 }
6055 if unwrapped_fn_var_type != 0
6056 && g.table.final_sym(unwrapped_fn_var_type).kind == .function {
6057 smartcast_types = [unwrapped_fn_var_type]
6058 }
6059 }
6060 if node.is_method && node.left_type != 0 {
6061 left_sym := g.table.sym(node.left_type)
6062 if left_sym.kind == .struct && node.name == obj.name {
6063 is_cast_needed = false
6064 }
6065 }
6066 if smartcast_types.len > 0 && is_cast_needed {
6067 for typ in smartcast_types {
6068 sym := g.table.sym(g.unwrap_generic(typ))
6069 if obj.orig_type.has_flag(.option) && sym.kind == .function {
6070 g.write('(*(${sym.cname}*)(')
6071 } else {
6072 g.write('(*(${sym.cname})(')
6073 }
6074 }
6075 for i, typ in smartcast_types {
6076 cast_sym := g.table.sym(g.unwrap_generic(typ))
6077 mut is_ptr := false
6078 if i == 0 {
6079 if obj.is_inherited {
6080 g.write(closure_ctx + '->' + c_name(node.name))
6081 } else {
6082 g.write(node.name)
6083 }
6084 if obj.orig_type.is_ptr() {
6085 is_ptr = true
6086 }
6087 }
6088 dot := if is_ptr { '->' } else { '.' }
6089 if cast_sym.info is ast.Aggregate {
6090 sym := g.table.sym(cast_sym.info.types[g.aggregate_type_idx])
6091 g.write('${dot}_${sym.cname}')
6092 } else if cast_sym.kind == .function && obj.orig_type.has_flag(.option) {
6093 g.write('.data')
6094 } else {
6095 g.write('${dot}_${cast_sym.cname}')
6096 }
6097 g.write('))')
6098 }
6099 is_fn_var = true
6100 } else if obj.is_inherited {
6101 g.write(closure_ctx + '->' + c_name(node.name))
6102 is_fn_var = true
6103 }
6104 }
6105 }
6106 if !is_fn_var {
6107 if g.cur_fn != unsafe { nil } && g.cur_fn.trace_fns.len > 0 {
6108 if node.is_fn_var {
6109 g.gen_trace_fn_var_call(node, name, call_generic_names, call_concrete_types)
6110 return
6111 }
6112 g.gen_trace_call(node, name)
6113 } else {
6114 g.write(g.get_ternary_name(name))
6115 }
6116 }
6117 if node.is_unwrapped_fn_selector {
6118 g.write('.data)')
6119 }
6120 if is_interface_call {
6121 g.write(')')
6122 }
6123 mut tmp_cnt_save := -1
6124 if name != '&' {
6125 g.write('(')
6126 }
6127 if is_json_fn {
6128 g.write(json_obj)
6129 } else {
6130 if node.is_keep_alive
6131 && g.pref.gc_mode in [.boehm_full, .boehm_incr, .boehm_full_opt, .boehm_incr_opt] {
6132 cur_line := g.go_before_last_stmt()
6133 tmp_cnt_save = g.keep_alive_call_pregen(node)
6134 g.write(cur_line)
6135 for i in 0 .. node.args.len {
6136 if i > 0 {
6137 g.write(', ')
6138 }
6139 g.write('__tmp_arg_${tmp_cnt_save + i}')
6140 }
6141 } else {
6142 g.call_args_with_context(node, call_generic_names, call_concrete_types)
6143 }
6144 }
6145 if name != '&' {
6146 g.write(')')
6147 }
6148 if node.return_type != 0 && !node.return_type.has_option_or_result()
6149 && g.table.final_sym(node.return_type).kind == .array_fixed {
6150 // it's non-option fixed array, requires accessing .ret_arr member to get the array
6151 g.write('.ret_arr')
6152 }
6153 if tmp_cnt_save >= 0 {
6154 g.writeln(';')
6155 g.keep_alive_call_postgen(node, tmp_cnt_save)
6156 }
6157 }
6158 }
6159 g.is_json_fn = false
6160}
6161
6162// gen_trace_call generates call to the wrapper trace fn if the call is traceable
6163fn (mut g Gen) gen_trace_call(node ast.CallExpr, name string) {
6164 hash_fn, _ := g.table.get_trace_fn_name(g.cur_fn, node)
6165 if _ := g.cur_fn.trace_fns[hash_fn] {
6166 g.write(c_name(hash_fn))
6167 if node.is_fn_var {
6168 g.write('(${node.name})')
6169 }
6170 } else {
6171 g.write(g.get_ternary_name(name))
6172 }
6173}
6174
6175// gen_trace_fn_var_call generates the full traced call for function variables.
6176fn (mut g Gen) gen_trace_fn_var_call(node ast.CallExpr, name string, call_generic_names []string, call_concrete_types []ast.Type) {
6177 hash_fn, _ := g.table.get_trace_fn_name(g.cur_fn, node)
6178 if _ := g.cur_fn.trace_fns[hash_fn] {
6179 g.write('${c_name(hash_fn)}(${g.get_ternary_name(name)}')
6180 if node.args.len > 0 {
6181 g.write(', ')
6182 }
6183 } else {
6184 g.write('${g.get_ternary_name(name)}(')
6185 }
6186 g.call_args_with_context(node, call_generic_names, call_concrete_types)
6187 g.write(')')
6188}
6189
6190fn (mut g Gen) autofree_tmp_arg_init_stmt(prepend string, expr ast.Expr) string {
6191 saved_out := g.out
6192 saved_stmt_path_pos := g.stmt_path_pos.clone()
6193 saved_empty_line := g.empty_line
6194 saved_indent := g.indent
6195 g.out = strings.new_builder(256)
6196 g.stmt_path_pos = [0]
6197 g.empty_line = true
6198 g.indent = 0
6199 defer {
6200 g.out = saved_out
6201 g.stmt_path_pos = saved_stmt_path_pos
6202 g.empty_line = saved_empty_line
6203 g.indent = saved_indent
6204 }
6205 return g.expr_string_surround(prepend, expr, ';').trim_space()
6206}
6207
6208fn (mut g Gen) autofree_call_pregen(node ast.CallExpr) {
6209 // g.writeln('// autofree_call_pregen()')
6210 // Create a temporary var before fn call for each argument in order to free it (only if it's a complex expression,
6211 // like `foo(get_string())` or `foo(a + b)`
6212 mut free_tmp_arg_vars := g.is_autofree && !g.is_builtin_mod && node.args.len > 0
6213 && !node.args[0].typ.has_option_or_result() // TODO: copy pasta checker.v
6214 if !free_tmp_arg_vars {
6215 return
6216 }
6217 if g.is_js_call {
6218 return
6219 }
6220 if g.inside_const {
6221 return
6222 }
6223 free_tmp_arg_vars = false // set the flag to true only if we have at least one arg to free
6224 g.tmp_count_af++
6225 mut scope := g.file.scope.innermost(node.pos.pos)
6226 // prepend the receiver for now (TODO turn the receiver into a CallArg everywhere?)
6227 mut args := [
6228 ast.CallArg{
6229 typ: node.receiver_type
6230 expr: node.left
6231 is_tmp_autofree: node.free_receiver
6232 },
6233 ]
6234 args << node.args
6235 for i, arg in args {
6236 if !arg.is_tmp_autofree {
6237 if arg.expr is ast.CallExpr && arg.expr.name in ['json.encode', 'json.encode_pretty'] {
6238 t := '_arg_expr_${arg.expr.name.replace('.', '_')}_${arg.expr.pos.pos}'
6239 defer(fn) {
6240 g.writeln(';\n\tbuiltin__string_free(&${t});')
6241 }
6242 }
6243 continue
6244 }
6245 if arg.expr is ast.CallExpr {
6246 // Any argument can be an expression that has to be freed. Generate a tmp expression
6247 // for each of those recursively.
6248 g.autofree_call_pregen(arg.expr)
6249 }
6250 free_tmp_arg_vars = true
6251 fn_name := node.name.replace('.', '_') // can't use name...
6252 t := '_arg_expr_${fn_name}_${i}_${node.pos.pos}'
6253 used := false // scope.known_var(t)
6254 mut s := '${t} = '
6255 if used {
6256 // This means this tmp var name was already used (the same function was called and
6257 // `_arg_fnname_1` was already generated).
6258 // We do not need to declare this variable again, so just generate `t = ...`
6259 // instead of `string t = ...`, and we need to mark this variable as unused,
6260 // so that it's freed after the call. (Used tmp arg vars are not freed to avoid double frees).
6261 if mut x := scope.find_var(t) {
6262 x.is_used = false
6263 }
6264 s = '${t} = '
6265 } else {
6266 scope.register(ast.Var{
6267 name: t
6268 typ: arg.typ
6269 is_autofree_tmp: true
6270 pos: node.pos
6271 })
6272 s = '${g.styp(arg.typ)} ${t} = '
6273 }
6274 g.is_autofree_tmp = true
6275 old_is_autofree := g.is_autofree
6276 if arg.expr is ast.CallExpr && arg.expr.is_method && arg.expr.left is ast.CallExpr {
6277 g.is_autofree = false
6278 }
6279 tmp_arg_init := g.autofree_tmp_arg_init_stmt(s, arg.expr)
6280 g.is_autofree = old_is_autofree
6281 g.is_autofree_tmp = false
6282 g.strs_to_free0 << tmp_arg_init
6283 if g.track_lambda_autofree_tmp_arg_vars {
6284 g.lambda_autofree_tmp_arg_vars << t
6285 }
6286 // This tmp arg var will be freed with the rest of the vars at the end of the scope,
6287 // or explicitly after an inline lambda expression that does not run scope cleanup.
6288 }
6289}
6290
6291fn (mut g Gen) call_arg_param(node ast.CallExpr, arg_idx int) ?ast.Param {
6292 base_name := if node.name.contains('_T_') { node.name.all_before('_T_') } else { node.name }
6293 if node.is_method {
6294 left_sym := g.table.sym(node.left_type)
6295 if method := g.table.find_method(left_sym, node.name) {
6296 return if method.is_variadic && arg_idx >= method.params.len - 2 {
6297 method.params.last()
6298 } else {
6299 method.params[arg_idx + 1]
6300 }
6301 }
6302 if method := left_sym.find_method_with_generic_parent(base_name) {
6303 return if method.is_variadic && arg_idx >= method.params.len - 2 {
6304 method.params.last()
6305 } else {
6306 method.params[arg_idx + 1]
6307 }
6308 }
6309 if node.receiver_type != 0 && node.receiver_type != node.left_type {
6310 receiver_sym := g.table.sym(node.receiver_type)
6311 if method := g.table.find_method(receiver_sym, node.name) {
6312 return if method.is_variadic && arg_idx >= method.params.len - 2 {
6313 method.params.last()
6314 } else {
6315 method.params[arg_idx + 1]
6316 }
6317 }
6318 if method := receiver_sym.find_method_with_generic_parent(base_name) {
6319 return if method.is_variadic && arg_idx >= method.params.len - 2 {
6320 method.params.last()
6321 } else {
6322 method.params[arg_idx + 1]
6323 }
6324 }
6325 }
6326 return none
6327 }
6328 if func := g.table.find_fn(node.name) {
6329 return if func.is_variadic && arg_idx >= func.params.len - 1 {
6330 func.params.last()
6331 } else {
6332 func.params[arg_idx]
6333 }
6334 }
6335 if base_name != node.name {
6336 if func := g.table.find_fn(base_name) {
6337 return if func.is_variadic && arg_idx >= func.params.len - 1 {
6338 func.params.last()
6339 } else {
6340 func.params[arg_idx]
6341 }
6342 }
6343 }
6344 return none
6345}
6346
6347fn array_mut_arg_is_addressable(expr ast.Expr) bool {
6348 return match expr {
6349 ast.IndexExpr {
6350 if expr.index is ast.RangeExpr {
6351 false
6352 } else {
6353 expr.left.is_lvalue()
6354 }
6355 }
6356 ast.ParExpr {
6357 array_mut_arg_is_addressable(expr.expr)
6358 }
6359 else {
6360 expr.is_lvalue()
6361 }
6362 }
6363}
6364
6365fn (mut g Gen) call_args(node ast.CallExpr) {
6366 g.expected_fixed_arr = true
6367 defer {
6368 g.expected_fixed_arr = false
6369 }
6370 args := if g.is_js_call {
6371 if node.args.len < 1 {
6372 g.error('node should have at least 1 arg', node.pos)
6373 }
6374 g.is_js_call = false
6375 node.args[1..]
6376 } else {
6377 node.args
6378 }
6379 mut expected_types := node.expected_arg_types.map(g.unwrap_generic(it))
6380 if g.has_active_call_generic_context() {
6381 for i in 0 .. expected_types.len {
6382 resolved_expected_type :=
6383 g.resolve_active_call_concrete_type(node.expected_arg_types[i].set_flag(.generic))
6384 if resolved_expected_type != 0 {
6385 expected_types[i] = resolved_expected_type
6386 }
6387 }
6388 }
6389 // For fn_var calls inside generic functions, the expected_arg_types may have been
6390 // resolved with concrete types from a previous instantiation. Re-resolve them from
6391 // the function pointer's type in the current generic context.
6392 if node.is_fn_var && g.cur_fn != unsafe { nil } && g.cur_fn.generic_names.len > 0
6393 && g.cur_concrete_types.len > 0 {
6394 lookup_name := if node.left is ast.Ident { node.left.name } else { node.name }
6395 resolved_fn_type := g.resolve_current_fn_generic_param_type(lookup_name)
6396 mut fn_var_type := if resolved_fn_type != 0
6397 && g.table.final_sym(g.unwrap_generic(resolved_fn_type)).kind == .function {
6398 g.unwrap_generic(g.recheck_concrete_type(resolved_fn_type))
6399 } else {
6400 g.unwrap_generic(g.recheck_concrete_type(node.fn_var_type))
6401 }
6402 if fn_var_type != 0 {
6403 fn_sym := g.table.final_sym(fn_var_type)
6404 if fn_sym.info is ast.FnType {
6405 for i in 0 .. expected_types.len {
6406 if i < fn_sym.info.func.params.len {
6407 resolved_param_type :=
6408 g.unwrap_generic(g.recheck_concrete_type(fn_sym.info.func.params[i].typ))
6409 if resolved_param_type != 0 {
6410 expected_types[i] = resolved_param_type
6411 }
6412 }
6413 }
6414 }
6415 }
6416 }
6417 // unwrap generics fn/method arguments to concretes
6418 if !g.has_active_call_generic_context() && node.concrete_types.len > 0
6419 && node.concrete_types.all(!it.has_flag(.generic)) {
6420 if node.is_method {
6421 // First try to find method on left_type, then fallback to receiver_type for embedded types
6422 mut func := ast.Fn{}
6423 mut found := false
6424 if f := g.table.find_method(g.table.sym(node.left_type), node.name) {
6425 func = f
6426 found = true
6427 } else if node.receiver_type != 0 && node.receiver_type != node.left_type {
6428 // Method not found on left_type, try receiver_type (for embedded types)
6429 if f := g.table.find_method(g.table.sym(node.receiver_type), node.name) {
6430 func = f
6431 found = true
6432 }
6433 }
6434 if found {
6435 if func.generic_names.len > 0 {
6436 for i in 0 .. expected_types.len {
6437 mut muttable := unsafe { &ast.Table(g.table) }
6438 // Ensure the generic flag is set for conversion
6439 arg_type := node.expected_arg_types[i].set_flag(.generic)
6440 if utyp := muttable.convert_generic_type(arg_type, func.generic_names,
6441 node.concrete_types)
6442 {
6443 expected_types[i] = utyp
6444 }
6445 }
6446 }
6447 }
6448 } else {
6449 if func := g.table.find_fn(node.name) {
6450 if func.generic_names.len > 0 {
6451 for i in 0 .. expected_types.len {
6452 mut muttable := unsafe { &ast.Table(g.table) }
6453 // Ensure the generic flag is set for conversion
6454 arg_type := node.expected_arg_types[i].set_flag(.generic)
6455 if utyp := muttable.convert_generic_type(arg_type, func.generic_names,
6456 node.concrete_types)
6457 {
6458 expected_types[i] = utyp
6459 }
6460 }
6461 }
6462 }
6463 }
6464 }
6465 if node.is_field && node.concrete_types.len == 0 && g.cur_fn != unsafe { nil }
6466 && g.cur_fn.generic_names.len > 0 && g.cur_concrete_types.len > 0 {
6467 left_sym := g.table.sym(node.left_type)
6468 if field := g.table.find_field_with_embeds(left_sym, node.name) {
6469 field_sym := g.table.sym(g.unwrap_generic(field.typ))
6470 if field_sym.kind == .function {
6471 if field_sym.info is ast.FnType {
6472 info := field_sym.info
6473 for i in 0 .. expected_types.len {
6474 if i < info.func.params.len {
6475 mut param_typ := info.func.params[i].typ
6476 if param_typ.has_flag(.generic) {
6477 if utyp := g.table.convert_generic_type(param_typ,
6478 g.cur_fn.generic_names, g.cur_concrete_types)
6479 {
6480 param_typ = utyp
6481 }
6482 }
6483 if expected_types[i] != param_typ {
6484 expected_types[i] = param_typ
6485 }
6486 }
6487 }
6488 }
6489 }
6490 }
6491 }
6492 if node.is_method {
6493 left_sym := g.table.sym(node.left_type)
6494 if left_sym.info is ast.Struct && left_sym.info.generic_types.len > 0
6495 && left_sym.info.concrete_types.len > 0 {
6496 base_type_idx := g.table.find_type_idx(left_sym.name.all_before('['))
6497 mut func := ast.Fn{}
6498 mut found := false
6499 for {
6500 if base_type_idx > 0 {
6501 base_sym := g.table.sym(ast.idx_to_type(base_type_idx))
6502 if base_func := g.table.find_method(base_sym, node.name) {
6503 func = base_func
6504 found = true
6505 break
6506 }
6507 }
6508 if base_func := g.table.find_method(left_sym, node.name) {
6509 func = base_func
6510 found = true
6511 }
6512 break
6513 }
6514 if found && func.generic_names.len > 0 {
6515 for i in 0 .. expected_types.len {
6516 mut muttable := unsafe { &ast.Table(g.table) }
6517 // Ensure the generic flag is set for conversion
6518 arg_type := node.expected_arg_types[i].set_flag(.generic)
6519 if utyp := muttable.convert_generic_type(arg_type, func.generic_names,
6520 left_sym.info.concrete_types)
6521 {
6522 expected_types[i] = utyp
6523 }
6524 }
6525 }
6526 }
6527 }
6528 // Final safeguard: ensure any remaining generic types from the outer function are converted
6529 if g.cur_fn != unsafe { nil } && g.cur_fn.generic_names.len > 0 && g.cur_concrete_types.len > 0 {
6530 for i in 0 .. expected_types.len {
6531 sym := g.table.sym(expected_types[i])
6532 if sym.name in g.cur_fn.generic_names {
6533 mut muttable := unsafe { &ast.Table(g.table) }
6534 if utyp := muttable.convert_generic_type(expected_types[i].set_flag(.generic),
6535 g.cur_fn.generic_names, g.cur_concrete_types)
6536 {
6537 expected_types[i] = utyp
6538 }
6539 }
6540 }
6541 }
6542 // only v variadic, C variadic args will be appended like normal args
6543 is_variadic := node.language == .v && node.is_variadic && expected_types.len > 0
6544 && expected_types.last().has_flag(.variadic)
6545 mut already_decomposed := false
6546 for i, arg in args {
6547 if is_variadic && i == expected_types.len - 1 {
6548 break
6549 }
6550 if tmp := g.closure_frame_arg_tmps[arg.pos.pos] {
6551 g.write(tmp)
6552 if i < args.len - 1 || is_variadic {
6553 g.write(', ')
6554 }
6555 continue
6556 }
6557 mut is_smartcast := false
6558 if arg.expr is ast.Ident {
6559 if arg.expr.obj is ast.Var {
6560 if i < node.expected_arg_types.len && node.expected_arg_types[i].has_flag(.generic)
6561 && arg.expr.obj.ct_type_var !in [.generic_param, .no_comptime]
6562 && (expected_types[i] == 0 || expected_types[i].has_flag(.generic)
6563 || g.type_has_unresolved_generic_parts(expected_types[i])) {
6564 resolved_arg_type :=
6565 g.unwrap_generic(g.type_resolver.get_type(ast.Expr(arg.expr)))
6566 if resolved_arg_type != 0 {
6567 expected_types[i] = resolved_arg_type
6568 }
6569 } else if i < expected_types.len && arg.expr.obj.smartcasts.len > 0 {
6570 exp_sym := g.table.sym(expected_types[i])
6571 orig_sym := g.table.sym(arg.expr.obj.orig_type)
6572 if !expected_types[i].has_option_or_result() && orig_sym.kind != .interface
6573 && (exp_sym.kind !in [.sum_type, .interface]
6574 && expected_types[i] != arg.expr.obj.orig_type) {
6575 expected_types[i] = g.unwrap_generic(arg.expr.obj.smartcasts.last())
6576 cast_sym := g.table.sym(expected_types[i])
6577 if cast_sym.info is ast.Aggregate {
6578 expected_types[i] = cast_sym.info.types[g.aggregate_type_idx]
6579 }
6580 is_smartcast = true
6581 }
6582 }
6583 }
6584 } else if arg.expr is ast.ArrayDecompose {
6585 mut d_count := 0
6586 remaining_params := expected_types.len - i
6587 if node.language == .v && node.is_variadic && arg.expr.expr_type.has_flag(.variadic)
6588 && remaining_params > 0 && i < expected_types.len - 1 {
6589 tmp_array := g.new_tmp_var()
6590 line := g.go_before_last_stmt()
6591 array_typ := g.styp(arg.expr.expr_type)
6592 g.write('\t${array_typ} ${tmp_array} = ')
6593 g.expr(ast.Expr(arg.expr))
6594 g.writeln(';')
6595 g.write(line.trim_left('\t'))
6596 for d_i in i .. expected_types.len - 1 {
6597 g.write('*(${g.styp(expected_types[d_i])}*)builtin__array_get(${tmp_array}, ${d_count}), ')
6598 d_count++
6599 }
6600 g.write('builtin__array_slice(${tmp_array}, ${d_count}, 2147483647)')
6601 } else if !arg.expr.expr_type.has_flag(.variadic) && remaining_params > 0 {
6602 tmp_array := g.new_tmp_var()
6603 line := g.go_before_last_stmt()
6604 array_typ := g.styp(arg.expr.expr_type)
6605 g.write('\t${array_typ} ${tmp_array} = ')
6606 g.expr(arg.expr.expr)
6607 g.writeln(';')
6608 g.writeln('if (${tmp_array}.len < ${remaining_params}) {')
6609 elem_word := if remaining_params == 1 { 'element is' } else { 'elements are' }
6610 tmp_err_msg := g.new_tmp_var()
6611 g.writeln('\tstring ${tmp_err_msg};')
6612 g.writeln('\tif (${tmp_array}.len == 1) {')
6613 g.writeln('\t\t${tmp_err_msg} = builtin__str_intp(2, _MOV((StrIntpData[]){')
6614 g.writeln('\t\t\t{_S("array decompose: array has "), 0xfe07, {.d_i32 = ${tmp_array}.len}, 0, 0, 0},')
6615 g.writeln('\t\t\t{_S(" element but ${remaining_params} ${elem_word} needed"), 0, {0}, 0, 0, 0}}));')
6616 g.writeln('\t} else {')
6617 g.writeln('\t\t${tmp_err_msg} = builtin__str_intp(2, _MOV((StrIntpData[]){')
6618 g.writeln('\t\t\t{_S("array decompose: array has "), 0xfe07, {.d_i32 = ${tmp_array}.len}, 0, 0, 0},')
6619 g.writeln('\t\t\t{_S(" elements but ${remaining_params} ${elem_word} needed"), 0, {0}, 0, 0, 0}}));')
6620 g.writeln('\t}')
6621 g.writeln('\tbuiltin___v_panic(${tmp_err_msg});')
6622 g.writeln('}')
6623 g.write(line.trim_left('\t'))
6624 for d_i in i .. expected_types.len {
6625 g.write('*(${g.styp(expected_types[d_i])}*)builtin__array_get(${tmp_array}, ${d_count})')
6626 if d_i < expected_types.len - 1 {
6627 g.write(', ')
6628 }
6629 d_count++
6630 }
6631 } else {
6632 for d_i in i .. expected_types.len {
6633 g.write('*(${g.styp(expected_types[d_i])}*)builtin__array_get(')
6634 g.expr(ast.Expr(arg.expr))
6635 g.write(', ${d_count})')
6636 if d_i < expected_types.len - 1 {
6637 g.write(', ')
6638 }
6639 d_count++
6640 }
6641 }
6642 already_decomposed = true
6643 continue
6644 } else if arg.expr is ast.ComptimeSelector && i < node.expected_arg_types.len
6645 && node.expected_arg_types[i].has_flag(.generic) {
6646 exp_option := node.expected_arg_types[i].has_flag(.option)
6647 expected_types[i] = g.unwrap_generic(g.type_resolver.get_type(ast.Expr(arg.expr)))
6648 if !exp_option {
6649 expected_types[i] = expected_types[i].clear_flag(.option)
6650 }
6651 } else if arg.expr is ast.CallExpr {
6652 if arg.expr.nr_ret_values > 1 {
6653 line := g.go_before_last_stmt().trim_space()
6654 g.empty_line = true
6655 ret_type := arg.expr.return_type
6656 tmp_var := g.new_tmp_var()
6657 g.write('${g.styp(ret_type)} ${tmp_var} = ')
6658 g.expr(ast.Expr(arg.expr))
6659 g.writeln(';')
6660 g.write(line)
6661 for n in 0 .. arg.expr.nr_ret_values {
6662 if n != arg.expr.nr_ret_values - 1 || i != args.len - 1 {
6663 g.write('${tmp_var}.arg${n}, ')
6664 } else {
6665 g.write('${tmp_var}.arg${n}')
6666 }
6667 }
6668 continue
6669 }
6670 }
6671 use_tmp_var_autofree := g.is_autofree && arg.typ == ast.string_type && arg.is_tmp_autofree
6672 && !g.inside_const && !g.is_builtin_mod && g.inside_ternary == 0
6673 mut effective_arg := arg
6674 if !effective_arg.is_mut {
6675 if param := g.call_arg_param(node, i) {
6676 if param.is_mut {
6677 effective_arg = ast.CallArg{
6678 ...effective_arg
6679 is_mut: true
6680 }
6681 }
6682 }
6683 }
6684 if i < expected_types.len {
6685 if param := g.call_arg_param(node, i) {
6686 param_needs_generic_resolution := g.type_needs_generic_resolution(param.typ)
6687 || g.has_active_call_generic_context()
6688 expected_needs_generic_resolution := expected_types[i] == 0
6689 || g.type_needs_generic_resolution(expected_types[i])
6690 if param_needs_generic_resolution || expected_needs_generic_resolution
6691 || node.concrete_types.len > 0 || g.cur_concrete_types.len > 0 {
6692 resolved_expected_type :=
6693 g.resolve_active_call_concrete_type(param.typ.set_flag(.generic))
6694 if resolved_expected_type != 0 && resolved_expected_type != expected_types[i]
6695 && !g.type_needs_generic_resolution(resolved_expected_type)
6696 && (expected_needs_generic_resolution
6697 || node.concrete_types.len > 0
6698 || g.cur_concrete_types.len > 0) {
6699 expected_types[i] =
6700 g.unwrap_generic(g.recheck_concrete_type(resolved_expected_type))
6701 }
6702 }
6703 }
6704 }
6705 // g.write('/* af=${arg.is_tmp_autofree} */')
6706 // some c fn definitions dont have args (cfns.v) or are not updated in checker
6707 // when these are fixed we wont need this check
6708 if i < expected_types.len {
6709 mut wrote_tmp_arg := false
6710 if use_tmp_var_autofree && arg.is_tmp_autofree { // && !g.is_js_call {
6711 // We saved expressions in temp variables so that they can be freed later.
6712 // `foo(str + str2) => x := str + str2; foo(x); x.free()`
6713 // g.write('_arg_expr_${g.called_fn_name}_${i}')
6714 // Use these variables here.
6715 fn_name := node.name.replace('.', '_')
6716 // name := '_tt${g.tmp_count_af}_arg_expr_${fn_name}_${i}'
6717 name := '_arg_expr_${fn_name}_${i + 1}_${node.pos.pos}'
6718 scope := g.file.scope.innermost(node.pos.pos)
6719 if (!g.is_autofree_tmp && (!g.inside_lambda || g.inside_lambda_autofree_tmp))
6720 || scope.known_var(name) {
6721 tmp_arg := ast.CallArg{
6722 typ: arg.typ
6723 is_mut: effective_arg.is_mut
6724 expr: ast.Ident{
6725 name: name
6726 kind: .variable
6727 info: ast.IdentVar{
6728 typ: arg.typ
6729 is_mut: effective_arg.is_mut
6730 }
6731 }
6732 }
6733 g.write('/*autofree arg*/')
6734 g.ref_or_deref_arg(tmp_arg, expected_types[i], node.language, is_smartcast)
6735 wrote_tmp_arg = true
6736 }
6737 }
6738 if !wrote_tmp_arg {
6739 g.ref_or_deref_arg(effective_arg, expected_types[i], node.language, is_smartcast)
6740 }
6741 } else {
6742 if use_tmp_var_autofree {
6743 n := if node.kind == .json_decode { i + 2 } else { i + 1 }
6744 // TODO: copypasta, move to an inline fn
6745 fn_name := node.name.replace('.', '_')
6746 name := '_arg_expr_${fn_name}_${n}_${node.pos.pos}'
6747 g.write('/*af arg2*/' + name)
6748 } else {
6749 g.expr(arg.expr)
6750 }
6751 }
6752 if i < args.len - 1 || is_variadic {
6753 g.write(', ')
6754 }
6755 }
6756 arg_nr := expected_types.len - 1
6757 if is_variadic {
6758 varg_type := expected_types.last()
6759 variadic_count := args.len - arg_nr
6760 arr_sym := g.table.sym(varg_type)
6761 mut arr_info := arr_sym.info as ast.Array
6762 if varg_type.has_flag(.generic) {
6763 if node.is_method {
6764 left_sym := g.table.sym(node.left_type)
6765 if fn_def := left_sym.find_method_with_generic_parent(node.name) {
6766 mut muttable := unsafe { &ast.Table(g.table) }
6767 if utyp := muttable.convert_generic_type(arr_info.elem_type,
6768 fn_def.generic_names, node.concrete_types)
6769 {
6770 arr_info.elem_type = utyp
6771 }
6772 } else {
6773 g.error('unable to find method ${node.name}', node.pos)
6774 }
6775 } else {
6776 if fn_def := g.table.find_fn(node.name) {
6777 mut muttable := unsafe { &ast.Table(g.table) }
6778 if utyp := muttable.convert_generic_type(arr_info.elem_type,
6779 fn_def.generic_names, node.concrete_types)
6780 {
6781 arr_info.elem_type = utyp
6782 }
6783 } else {
6784 g.error('unable to find function ${node.name}', node.pos)
6785 }
6786 }
6787 }
6788 elem_type := g.styp(arr_info.elem_type)
6789 if (g.pref.translated || g.file.is_translated) && args.len == 1 {
6790 // Handle `foo(c'str')` for `fn foo(args ...&u8)`
6791 // TODOC2V handle this in a better place
6792 g.expr(args[0].expr)
6793 } else if args.len > 0 && args.last().expr is ast.ArrayDecompose {
6794 if !already_decomposed {
6795 array_decompose := args.last().expr as ast.ArrayDecompose
6796 varg_array_type := varg_type.clear_flag(.variadic)
6797 if g.table.unaliased_type(array_decompose.expr_type) == g.table.unaliased_type(varg_array_type) {
6798 g.expr(args.last().expr)
6799 } else {
6800 g.write_converted_variadic_array_decompose_arg(array_decompose,
6801 varg_array_type, arr_info.elem_type, node.language)
6802 }
6803 }
6804 } else {
6805 if variadic_count > 0 {
6806 if node.is_c_variadic {
6807 // Handle passing e.g. C string literals to `...` C varargs:
6808 // void DEH_snprintf(char *buffer, size_t len, const char *fmt, ...)
6809 // deh_snprintf(buffer, 9, c'STCFN%.3d', j++)
6810 for j in arg_nr .. args.len {
6811 g.expr(args[j].expr)
6812 if j < args.len - 1 {
6813 g.write(', ')
6814 }
6815 }
6816 } else {
6817 // passing the current variadic arg to another call which expects the same array type
6818 if variadic_count == 1 && g.is_forwarded_variadic_arg(args[arg_nr])
6819 && ((args[arg_nr].typ.has_flag(.variadic) && args[arg_nr].typ == varg_type)
6820 || (varg_type.has_flag(.variadic)
6821 && args[arg_nr].typ == varg_type.clear_flag(.variadic)
6822 && !g.table.sumtype_has_variant(arr_info.elem_type, args[arg_nr].typ, false))) {
6823 g.ref_or_deref_arg_ex(args[arg_nr], arr_info.elem_type, node.language,
6824 false, true)
6825 } else {
6826 noscan := g.check_noscan(arr_info.elem_type)
6827 is_option := arr_info.elem_type.has_flag(.option)
6828 tmp_var := if is_option { g.new_tmp_var() } else { '' }
6829 base_type := g.base_type(varg_type)
6830 tmp := if is_option { g.go_before_last_stmt() } else { '' }
6831 if is_option {
6832 g.writeln('${g.styp(varg_type)} ${tmp_var};')
6833 g.write('builtin___option_ok((${base_type}[]) {')
6834 }
6835 g.write('builtin__new_array_from_c_array${noscan}(${variadic_count}, ${variadic_count}, sizeof(${elem_type}), _MOV((${elem_type}[${variadic_count}]){')
6836 for j in arg_nr .. args.len {
6837 g.ref_or_deref_arg_ex(args[j], arr_info.elem_type, node.language,
6838 false, true)
6839 if j < args.len - 1 {
6840 g.write(', ')
6841 }
6842 }
6843 g.write('}))')
6844 if is_option {
6845 g.writeln(' }, (${option_name}*)&${tmp_var}, sizeof(${base_type}));')
6846 g.write(tmp)
6847 g.write(tmp_var)
6848 }
6849 }
6850 }
6851 } else {
6852 g.write('builtin____new_array(0, 0, sizeof(${elem_type}))')
6853 }
6854 }
6855 }
6856}
6857
6858fn (g &Gen) is_forwarded_variadic_arg(arg ast.CallArg) bool {
6859 if arg.typ.has_flag(.variadic) {
6860 return true
6861 }
6862 if arg.expr is ast.Ident && g.cur_fn != unsafe { nil } && g.cur_fn.is_variadic
6863 && g.cur_fn.params.len > 0 && arg.expr.obj is ast.Var {
6864 var_obj := arg.expr.obj as ast.Var
6865 return var_obj.is_arg && arg.expr.name == g.cur_fn.params.last().name
6866 }
6867 return false
6868}
6869
6870fn (mut g Gen) write_converted_variadic_array_decompose_arg(node ast.ArrayDecompose, varg_array_type ast.Type,
6871 elem_type ast.Type, lang ast.Language) {
6872 stmt_str := g.go_before_last_stmt().trim_space()
6873 g.empty_line = true
6874 src_array_type := g.unwrap_generic(node.expr_type)
6875 src_array_styp := g.styp(src_array_type)
6876 src_array_var := g.new_tmp_var()
6877 g.write('${src_array_styp} ${src_array_var} = ')
6878 g.expr(node.expr)
6879 g.writeln(';')
6880 elem_styp := g.styp(elem_type)
6881 noscan := g.check_noscan(elem_type)
6882 dst_var := g.new_tmp_var()
6883 g.writeln('${g.styp(varg_array_type)} ${dst_var} = builtin____new_array${noscan}(0, ${src_array_var}.len, sizeof(${elem_styp}));')
6884 src_array_info := g.table.final_sym(src_array_type).array_info()
6885 src_elem_type := src_array_info.elem_type
6886 idx_var := g.new_tmp_var()
6887 g.writeln('for (${ast.int_type_name} ${idx_var} = 0; ${idx_var} < ${src_array_var}.len; ++${idx_var}) {')
6888 g.indent++
6889 src_item_expr := ast.IndexExpr{
6890 left: ast.Ident{
6891 name: src_array_var
6892 kind: .variable
6893 info: ast.IdentVar{
6894 typ: src_array_type
6895 }
6896 }
6897 left_type: src_array_type
6898 index: ast.Ident{
6899 name: idx_var
6900 kind: .variable
6901 info: ast.IdentVar{
6902 typ: ast.int_type
6903 }
6904 }
6905 is_array: true
6906 typ: src_elem_type
6907 }
6908 src_item_arg := ast.CallArg{
6909 typ: src_elem_type
6910 expr: src_item_expr
6911 }
6912 converted_var := g.new_tmp_var()
6913 g.write('${elem_styp} ${converted_var} = ')
6914 g.ref_or_deref_arg(src_item_arg, elem_type, lang, false)
6915 g.writeln(';')
6916 g.writeln('builtin__array_push${noscan}((array*)&${dst_var}, &${converted_var});')
6917 g.indent--
6918 g.writeln('}')
6919 g.write(stmt_str)
6920 g.write(dst_var)
6921}
6922
6923// similar to `autofree_call_pregen()` but only to to handle [keep_args_alive] for C functions
6924fn (mut g Gen) keep_alive_call_pregen(node ast.CallExpr) int {
6925 g.empty_line = true
6926 g.writeln('// keep_alive_call_pregen()')
6927 // reserve the next tmp_vars for arguments
6928 tmp_cnt_save := g.tmp_count + 1
6929 g.tmp_count += node.args.len
6930 for i, arg in node.args {
6931 // save all arguments in temp vars (not only pointers) to make sure the
6932 // evaluation order is preserved
6933 expected_type := node.expected_arg_types[i]
6934 typ_sym := g.table.sym(expected_type)
6935 typ := g.styp(expected_type)
6936 if typ_sym.kind != .array_fixed {
6937 g.write('${typ} __tmp_arg_${tmp_cnt_save + i} = ')
6938 g.ref_or_deref_arg(arg, expected_type, node.language, false)
6939 } else {
6940 g.writeln('${typ} __tmp_arg_${tmp_cnt_save + i} = {0};')
6941 g.write('memcpy(&__tmp_arg_${tmp_cnt_save + i}, ')
6942 g.ref_or_deref_arg(arg, expected_type, node.language, false)
6943 g.writeln(', sizeof(${typ}));')
6944 }
6945 g.writeln(';')
6946 }
6947 g.empty_line = false
6948 return tmp_cnt_save
6949}
6950
6951fn (mut g Gen) keep_alive_call_postgen(node ast.CallExpr, tmp_cnt_save int) {
6952 g.writeln('// keep_alive_call_postgen()')
6953 for i, expected_type in node.expected_arg_types {
6954 if expected_type.is_any_kind_of_pointer() {
6955 g.writeln('GC_reachable_here(__tmp_arg_${tmp_cnt_save + i});')
6956 }
6957 }
6958}
6959
6960fn (mut g Gen) write_multi_ref_arg(arg ast.CallArg, arg_typ ast.Type, expected_type ast.Type, lang ast.Language) bool {
6961 if lang != .v || g.is_json_fn || !expected_type.is_ptr() || expected_type.has_option_or_result()
6962 || expected_type.nr_muls() <= 1 || expected_type.nr_muls() <= arg_typ.nr_muls()
6963 || arg_typ.has_flag(.shared_f) || expected_type.has_flag(.shared_f) {
6964 return false
6965 }
6966 expected_deref_type := expected_type.deref()
6967 expected_deref_sym := g.table.sym(expected_deref_type)
6968 arg_sym := g.table.sym(arg_typ)
6969 if expected_deref_sym.kind in [.interface, .sum_type] || arg_sym.kind == .function
6970 || arg.expr is ast.None {
6971 return false
6972 }
6973 if arg.expr is ast.Ident
6974 && (arg.expr.language == .c || g.table.is_interface_smartcast(arg.expr.obj)) {
6975 return false
6976 }
6977 // Build the first reference level from the original storage when possible,
6978 // then materialize deeper levels through compound-literal temporaries.
6979 extra_refs := expected_type.nr_muls() - arg_typ.nr_muls()
6980 for level := extra_refs - 1; level > 0; level-- {
6981 ref_type := arg_typ.set_nr_muls(arg_typ.nr_muls() + level)
6982 g.write('ADDR(${g.styp(ref_type)}, ')
6983 }
6984 old_arg_no_auto_deref := g.arg_no_auto_deref
6985 if arg.expr.is_auto_deref_var() {
6986 g.arg_no_auto_deref = true
6987 }
6988 defer {
6989 g.arg_no_auto_deref = old_arg_no_auto_deref
6990 }
6991 is_auto_heap_ident := arg.expr is ast.Ident && g.resolved_ident_is_auto_heap(arg.expr)
6992 if is_auto_heap_ident {
6993 g.write_raw_receiver_expr(arg.expr)
6994 } else if arg.expr.is_lvalue() {
6995 g.write('&')
6996 g.expr(arg.expr)
6997 } else {
6998 g.write('ADDR(${g.styp(arg_typ)}, ')
6999 g.expr_with_cast(arg.expr, arg_typ, arg_typ)
7000 g.write(')')
7001 }
7002 g.write(')'.repeat(extra_refs - 1))
7003 return true
7004}
7005
7006fn (mut g Gen) write_mut_unwrapped_option_payload_arg(arg ast.CallArg, expected_type ast.Type) bool {
7007 if !arg.is_mut || !expected_type.is_ptr() || expected_type.has_option_or_result() {
7008 return false
7009 }
7010 if arg.expr !is ast.PostfixExpr {
7011 return false
7012 }
7013 postfix_expr := arg.expr as ast.PostfixExpr
7014 if postfix_expr.op != .question {
7015 return false
7016 }
7017 option_expr := postfix_expr.expr
7018 if option_expr !is ast.ComptimeSelector {
7019 return false
7020 }
7021 mut option_type := g.resolved_expr_type(option_expr, ast.void_type)
7022 if option_type == ast.void_type {
7023 option_type = g.type_resolver.get_type_or_default(option_expr, ast.void_type)
7024 }
7025 option_type = g.unwrap_generic(g.recheck_concrete_type(option_type))
7026 if !option_type.has_flag(.option) {
7027 return false
7028 }
7029 payload_type := g.unwrap_generic(g.recheck_concrete_type(option_type.clear_option_and_result()))
7030 payload_styp := g.base_type(option_type)
7031 option_styp := g.styp(option_type)
7032 default_value := g.type_default(payload_type)
7033 stmt_str := g.go_before_last_stmt().trim_space()
7034 option_ptr_var := g.new_tmp_var()
7035 g.empty_line = true
7036 g.write('${option_styp}* ${option_ptr_var} = &')
7037 g.expr(option_expr)
7038 g.writeln(';')
7039 g.writeln('if (${option_ptr_var}->state == 2) {')
7040 g.writeln('\tbuiltin___option_ok(&(${payload_styp}[]){ ${default_value} }, (${option_styp}*)${option_ptr_var}, sizeof(${payload_styp}));')
7041 g.writeln('}')
7042 if stmt_str != '' {
7043 g.write2(stmt_str, ' ')
7044 }
7045 g.write('(${g.styp(expected_type)})${option_ptr_var}->data')
7046 return true
7047}
7048
7049@[inline]
7050fn (mut g Gen) ref_or_deref_arg(arg ast.CallArg, expected_type_ ast.Type, lang ast.Language, is_smartcast bool) {
7051 g.ref_or_deref_arg_ex(arg, expected_type_, lang, is_smartcast, false)
7052}
7053
7054fn (mut g Gen) ref_or_deref_arg_ex(arg ast.CallArg, expected_type_ ast.Type, lang ast.Language, is_smartcast bool, is_variadic_arg bool) {
7055 expected_type := g.unwrap_generic(expected_type_)
7056 mut arg_typ := if arg.ct_expr {
7057 g.unwrap_generic(g.type_resolver.get_type(arg.expr))
7058 } else {
7059 g.unwrap_generic(arg.typ)
7060 }
7061 in_generic_context := g.has_current_generic_context()
7062 if arg.expr is ast.Ident {
7063 needs_resolved_ident_type := arg_typ == 0
7064 || g.type_needs_generic_resolution(arg_typ) || (arg.expr.obj is ast.Var
7065 && (arg.expr.obj.ct_type_var in [.generic_param, .generic_var]
7066 || arg.expr.obj.is_inherited))
7067 if needs_resolved_ident_type {
7068 resolved_arg_typ := g.resolved_expr_type(ast.Expr(arg.expr), arg_typ)
7069 if resolved_arg_typ != 0 {
7070 resolved_arg_type := g.unwrap_generic(g.recheck_concrete_type(resolved_arg_typ))
7071 skip_inherited_option_storage_type := arg.expr.obj is ast.Var
7072 && arg.expr.obj.is_inherited && !expected_type.has_option_or_result()
7073 && arg_typ != 0 && !arg_typ.has_option_or_result()
7074 && resolved_arg_type.has_option_or_result()
7075 if !skip_inherited_option_storage_type {
7076 arg_typ = resolved_arg_type
7077 }
7078 }
7079 }
7080 if expected_type.has_option_or_result() && !arg_typ.has_option_or_result() {
7081 resolved_scope_type := g.resolved_scope_var_type(arg.expr)
7082 if resolved_scope_type != 0 && resolved_scope_type.has_option_or_result() {
7083 arg_typ = g.unwrap_generic(g.recheck_concrete_type(resolved_scope_type))
7084 }
7085 }
7086 if arg.expr.obj is ast.Var && arg.expr.obj.is_arg && arg.expr.obj.is_mut && !arg.is_mut
7087 && arg_typ.is_ptr() && !expected_type.is_any_kind_of_pointer() && in_generic_context {
7088 resolved_param_type := g.resolve_current_fn_generic_param_type(arg.expr.name)
7089 if resolved_param_type != 0 && !resolved_param_type.is_ptr() {
7090 arg_typ = resolved_param_type
7091 }
7092 }
7093 }
7094 needs_resolved_expr_type := arg.expr is ast.SelectorExpr
7095 || arg.expr is ast.IndexExpr || arg.expr is ast.ComptimeSelector
7096 || (arg.expr is ast.Ident && in_generic_context && (arg_typ == 0
7097 || g.type_needs_generic_resolution(arg_typ) || (arg.expr.obj is ast.Var
7098 && (arg.expr.obj.ct_type_var != .no_comptime || arg.expr.obj.generic_typ != 0
7099 || arg.expr.obj.is_inherited || arg.expr.obj.smartcasts.len > 0
7100 || arg.expr.obj.is_unwrapped))))
7101 if needs_resolved_expr_type {
7102 resolved_arg_typ := g.resolved_expr_type(arg.expr, arg_typ)
7103 if resolved_arg_typ != 0 && (arg_typ == 0 || g.type_needs_generic_resolution(arg_typ)
7104 || in_generic_context
7105 || g.unwrap_generic(resolved_arg_typ) != g.unwrap_generic(arg_typ)) {
7106 resolved_arg_type := g.unwrap_generic(g.recheck_concrete_type(resolved_arg_typ))
7107 skip_inherited_option_storage_type := arg.expr is ast.Ident && arg.expr.obj is ast.Var
7108 && arg.expr.obj.is_inherited && !expected_type.has_option_or_result()
7109 && arg_typ != 0 && !arg_typ.has_option_or_result()
7110 && resolved_arg_type.has_option_or_result()
7111 if !skip_inherited_option_storage_type {
7112 arg_typ = resolved_arg_type
7113 }
7114 }
7115 }
7116 // Array slice expressions (e.g. b[..8]) always return a value in C (via builtin__array_slice),
7117 // even when the container is a pointer (e.g. mut []u8 param). Deref so ref_or_deref_arg
7118 // correctly sees this as a non-pointer arg that needs &.
7119 if arg.expr is ast.IndexExpr && arg.expr.index is ast.RangeExpr && arg_typ.is_ptr() {
7120 arg_typ = arg_typ.deref()
7121 }
7122 if arg.expr is ast.IndexExpr && expected_type.has_flag(.option)
7123 && arg.expr.typ.has_flag(.option) {
7124 arg_typ = g.unwrap_generic(g.recheck_concrete_type(arg.expr.typ))
7125 }
7126 arg_sym := g.table.sym(arg_typ)
7127 exp_is_ptr := expected_type.is_any_kind_of_pointer()
7128 arg_is_ptr := arg_typ.is_any_kind_of_pointer()
7129 if expected_type == 0 {
7130 g.checker_bug('ref_or_deref_arg expected_type is 0', arg.pos)
7131 }
7132 old_expected_arg_mut := g.expected_arg_mut
7133 g.expected_arg_mut = arg.is_mut
7134 defer {
7135 g.expected_arg_mut = old_expected_arg_mut
7136 }
7137 exp_sym := g.table.sym(expected_type)
7138 if g.write_mut_unwrapped_option_payload_arg(arg, expected_type) {
7139 return
7140 }
7141 // Cast function pointer arguments when the expected and actual types differ.
7142 // V's checker allows coercing function types (e.g. Renderer* → voidptr in callbacks),
7143 // but C compilers with -Werror=incompatible-pointer-types reject the mismatch.
7144 // Only cast for V language calls with named function type aliases (not anonymous fn types).
7145 if exp_sym.kind == .function && arg_sym.kind == .function && !arg.is_mut && lang == .v
7146 && !exp_sym.name.starts_with('fn ') && !expected_type.has_flag(.option) {
7147 exp_styp := g.styp(expected_type)
7148 arg_styp := g.styp(arg_typ)
7149 if exp_styp != arg_styp {
7150 g.write('(${exp_styp})')
7151 g.expr(arg.expr)
7152 return
7153 }
7154 }
7155 if arg.is_mut && exp_sym.kind in [.interface, .sum_type] {
7156 expected_wrap_type := if expected_type.is_ptr() {
7157 expected_type.deref()
7158 } else {
7159 expected_type
7160 }
7161 if arg_sym.kind == exp_sym.kind && arg_typ.idx() == expected_wrap_type.idx()
7162 && arg.expr in [ast.Ident, ast.SelectorExpr] {
7163 g.prevent_sum_type_unwrapping_once = g.is_expr_smartcast_to_sumtype(arg.expr,
7164 expected_wrap_type)
7165 if arg_typ.is_ptr() {
7166 g.expr(arg.expr)
7167 } else {
7168 g.write('&')
7169 g.expr(arg.expr)
7170 }
7171 } else {
7172 if exp_sym.kind == .interface {
7173 // A converted `mut` interface argument can escape the callee
7174 // (for example into `[]&Interface`), so the wrapper must outlive
7175 // the current block scope.
7176 g.write('HEAP(${g.table.sym(expected_wrap_type).cname}, ')
7177 } else {
7178 g.write('ADDR(${g.table.sym(expected_wrap_type).cname}, ')
7179 }
7180 g.expr_with_cast(arg.expr, arg_typ, expected_wrap_type)
7181 g.write(')')
7182 }
7183 return
7184 }
7185 if expected_type.is_ptr() && !expected_type.has_option_or_result() {
7186 expected_ref_inner_type := expected_type.deref()
7187 expected_ref_inner_sym := g.table.sym(expected_ref_inner_type)
7188 if expected_ref_inner_sym.kind in [.interface, .sum_type] {
7189 mut effective_arg_expr := arg.expr
7190 mut effective_arg_typ := arg_typ
7191 if expected_ref_inner_sym.kind == .sum_type && arg.expr is ast.CastExpr {
7192 cast_expr := arg.expr
7193 cast_target_type := g.unwrap_generic(if cast_expr.typ != 0 {
7194 cast_expr.typ
7195 } else {
7196 arg_typ
7197 })
7198 if cast_target_type.idx() == expected_ref_inner_type.idx()
7199 && cast_expr.expr_type != 0 {
7200 effective_arg_expr = cast_expr.expr
7201 effective_arg_typ = g.unwrap_generic(cast_expr.expr_type)
7202 }
7203 }
7204 effective_arg_sym := g.table.sym(effective_arg_typ)
7205 effective_arg_is_auto_heap_ident := effective_arg_expr is ast.Ident
7206 && g.resolved_ident_is_auto_heap(effective_arg_expr)
7207 if (effective_arg_typ.is_ptr() && effective_arg_typ.deref() == expected_ref_inner_type)
7208 || (effective_arg_is_auto_heap_ident
7209 && effective_arg_typ.idx() == expected_ref_inner_type.idx()) {
7210 g.prevent_sum_type_unwrapping_once = g.is_expr_smartcast_to_sumtype(effective_arg_expr,
7211 expected_ref_inner_type)
7212 if effective_arg_is_auto_heap_ident {
7213 g.write_raw_receiver_expr(effective_arg_expr)
7214 } else {
7215 g.expr(effective_arg_expr)
7216 }
7217 } else if effective_arg_sym.kind == expected_ref_inner_sym.kind
7218 && effective_arg_typ.idx() == expected_ref_inner_type.idx() {
7219 g.prevent_sum_type_unwrapping_once = g.is_expr_smartcast_to_sumtype(effective_arg_expr,
7220 expected_ref_inner_type)
7221 if effective_arg_expr in [ast.Ident, ast.SelectorExpr] {
7222 g.write('&')
7223 g.expr(effective_arg_expr)
7224 } else {
7225 g.write('ADDR(${g.styp(expected_ref_inner_type)}, ')
7226 g.expr_with_cast(effective_arg_expr, effective_arg_typ, expected_ref_inner_type)
7227 g.write(')')
7228 }
7229 } else if effective_arg_typ == ast.nil_type
7230 || effective_arg_typ.is_voidptr()
7231 || (effective_arg_expr is ast.UnsafeExpr && effective_arg_expr.expr is ast.Nil) {
7232 g.write('((void*)0)')
7233 } else {
7234 if expected_ref_inner_sym.kind == .sum_type {
7235 // `&Variant` -> `&SumType` needs a stable heap-allocated wrapper and
7236 // must preserve aliasing with the original variant payload.
7237 g.expr_with_cast(effective_arg_expr, effective_arg_typ, expected_type)
7238 } else {
7239 // interface conversions don't box by reference, so HEAP is needed to
7240 // ensure the pointer remains valid after the current scope ends.
7241 g.write('HEAP(${expected_ref_inner_sym.cname}, ')
7242 g.expr_with_cast(effective_arg_expr, effective_arg_typ, expected_ref_inner_type)
7243 g.write(')')
7244 }
7245 }
7246 return
7247 }
7248 }
7249 if arg_sym.kind == .interface && exp_sym.kind == .interface && arg_typ != expected_type
7250 && !exp_is_ptr && !arg.is_mut && !expected_type.has_flag(.option) {
7251 g.expr_with_cast(arg.expr, arg_typ, expected_type)
7252 return
7253 }
7254 if g.write_multi_ref_arg(arg, arg_typ, expected_type, lang) {
7255 return
7256 }
7257 is_auto_heap_ident := arg.expr is ast.Ident && g.resolved_ident_is_auto_heap(arg.expr)
7258 mut needs_closing := false
7259 old_inside_smartcast := g.inside_smartcast
7260 if arg.is_mut && arg.expr.is_auto_deref_var() && arg_typ.is_ptr() && expected_type.is_ptr() {
7261 g.arg_no_auto_deref = true
7262 g.expr_with_cast(arg.expr, arg_typ, expected_type)
7263 g.arg_no_auto_deref = false
7264 return
7265 }
7266 if arg.is_mut && !exp_is_ptr {
7267 g.write('&/*mut*/')
7268 } else if arg.is_mut && arg_typ.is_ptr() && expected_type.is_ptr() && exp_sym.kind == .alias
7269 && g.table.unaliased_type(expected_type) == arg_typ {
7270 if arg.expr is ast.PrefixExpr && arg.expr.op == .amp {
7271 g.write('&(${exp_sym.cname}[]){')
7272 g.arg_no_auto_deref = true
7273 g.expr(ast.Expr(arg.expr))
7274 g.arg_no_auto_deref = false
7275 g.write('}[0]')
7276 } else {
7277 g.write('&/*mut alias*/')
7278 g.expr(arg.expr)
7279 }
7280 return
7281 } else if arg.is_mut && arg_typ.is_ptr() && expected_type.is_ptr()
7282 && g.table.sym(arg_typ).kind == .struct && expected_type == arg_typ.ref() {
7283 if arg.expr is ast.PrefixExpr && arg.expr.op == .amp {
7284 g.arg_no_auto_deref = true
7285 g.expr(ast.Expr(arg.expr))
7286 g.arg_no_auto_deref = false
7287 } else {
7288 g.write('&/*mut*/')
7289 g.expr(arg.expr)
7290 }
7291 return
7292 } else if exp_is_ptr && !expected_type.has_option_or_result() && !arg_is_ptr
7293 && !(arg_sym.kind == .alias && g.table.unaliased_type(arg_typ).is_pointer()
7294 && expected_type.is_pointer()) {
7295 if is_auto_heap_ident {
7296 g.write_raw_receiver_expr(arg.expr)
7297 return
7298 }
7299 if arg.is_mut {
7300 if exp_sym.kind == .array {
7301 if array_mut_arg_is_addressable(arg.expr) {
7302 g.write('&')
7303 if expected_type.has_flag(.option_mut_param_t) {
7304 g.expr_with_opt(arg.expr, arg_typ, expected_type)
7305 } else {
7306 g.expr(arg.expr)
7307 }
7308 } else {
7309 // Special case for mutable arrays. We can't `&` function
7310 // results, have to use `(array[]){ expr }[0]` hack.
7311 g.write('&(array[]){')
7312 g.expr(arg.expr)
7313 g.write('}[0]')
7314 }
7315 return
7316 } else if arg_sym.kind == .sum_type && exp_sym.kind == .sum_type
7317 && arg.expr in [ast.Ident, ast.SelectorExpr] {
7318 g.write('&')
7319 g.expr(arg.expr)
7320 return
7321 } else if arg_sym.kind == .interface && exp_sym.kind == .interface
7322 && arg.expr in [ast.Ident, ast.SelectorExpr] {
7323 g.write('&')
7324 g.expr(arg.expr)
7325 return
7326 }
7327 }
7328 if !g.is_json_fn {
7329 if arg_typ == 0 {
7330 g.checker_bug('ref_or_deref_arg arg.typ is 0', arg.pos)
7331 }
7332 arg_typ_sym := g.table.sym(arg_typ)
7333 expected_deref_type := if expected_type.is_ptr() {
7334 expected_type.deref()
7335 } else {
7336 expected_type
7337 }
7338 promoted_variadic_type := if is_variadic_arg && expected_deref_type.is_voidptr() {
7339 g.variadic_voidptr_promoted_type(arg_typ)
7340 } else {
7341 ast.no_type
7342 }
7343 deref_sym := g.table.sym(expected_deref_type)
7344 if arg_typ_sym.kind != .function && deref_sym.kind !in [.sum_type, .interface]
7345 && lang != .c {
7346 if arg.expr.is_lvalue() {
7347 if promoted_variadic_type != ast.no_type {
7348 g.write('(voidptr)ADDR(${g.styp(promoted_variadic_type)}, ')
7349 g.expr_with_cast(arg.expr, arg_typ, promoted_variadic_type)
7350 g.write(')')
7351 return
7352 }
7353 if expected_type.has_flag(.option) {
7354 if expected_type.has_flag(.option_mut_param_t) {
7355 g.write('&')
7356 }
7357 g.expr_with_opt(arg.expr, arg_typ, expected_type)
7358 return
7359 } else if arg.expr is ast.Ident && arg.expr.language == .c {
7360 g.write('(voidptr)')
7361 } else if !(!arg.is_mut && arg_sym.kind == .alias
7362 && g.table.unaliased_type(arg_typ).is_any_kind_of_pointer()) {
7363 g.write('(voidptr)&')
7364 }
7365 } else {
7366 mut atype := expected_deref_type
7367 if atype.has_flag(.generic) {
7368 atype = g.unwrap_generic(atype)
7369 }
7370 if atype.has_flag(.generic) || arg.expr is ast.StructInit {
7371 g.write('(voidptr)&')
7372 } else if arg.expr is ast.None {
7373 g.expr_with_opt(ast.Expr(arg.expr), arg_typ, expected_type)
7374 return
7375 } else if expected_deref_type.is_voidptr() && !arg_typ.is_any_kind_of_pointer() {
7376 boxed_type := if promoted_variadic_type != ast.no_type {
7377 promoted_variadic_type
7378 } else {
7379 arg_typ
7380 }
7381 g.write('(voidptr)ADDR(${g.styp(boxed_type)}, ')
7382 g.expr_with_cast(arg.expr, arg_typ, boxed_type)
7383 g.write(')')
7384 return
7385 } else if arg.expr.is_literal() {
7386 g.write('(voidptr)ADDR(${g.styp(arg_typ)}, ')
7387 g.expr(arg.expr)
7388 g.write(')')
7389 return
7390 } else {
7391 if arg_typ_sym.kind in [.sum_type, .interface] {
7392 atype = arg_typ
7393 }
7394 if arg.expr.is_as_cast() {
7395 g.inside_smartcast = true
7396 } else if arg_typ_sym.is_int() && arg.expr !is ast.CastExpr {
7397 g.write('(voidptr)&')
7398 } else {
7399 g.write('ADDR(${g.styp(atype)}, ')
7400 needs_closing = true
7401 }
7402 }
7403 }
7404 } else if arg_sym.kind == .sum_type && exp_sym.kind == .sum_type {
7405 // Automatically passing sum types by reference if the argument expects it,
7406 // not only the argument is mutable.
7407 if arg.expr is ast.SelectorExpr {
7408 g.write('&')
7409 g.expr(ast.Expr(arg.expr))
7410 return
7411 } else if arg.expr is ast.CastExpr {
7412 g.write('ADDR(${g.styp(expected_deref_type)}, ')
7413 g.expr_with_cast(ast.Expr(arg.expr), arg_typ, expected_type)
7414 g.write(')')
7415 return
7416 }
7417 } else if arg_sym.kind == .interface && exp_sym.kind == .interface {
7418 if exp_is_ptr && !arg_is_ptr {
7419 g.write('&')
7420 }
7421 }
7422 }
7423 } else if arg_typ.has_flag(.shared_f) && !expected_type.has_flag(.shared_f) {
7424 if expected_type.is_ptr() {
7425 g.write('&')
7426 }
7427 g.expr(arg.expr)
7428 g.write('->val')
7429 return
7430 } else if expected_type.has_flag(.option) {
7431 if expected_type.has_flag(.option_mut_param_t)
7432 && arg_typ.nr_muls() <= expected_type.nr_muls() && !(arg.expr is ast.Ident
7433 && (arg.expr.obj is ast.Var && arg.expr.obj.is_inherited)) {
7434 g.write('&')
7435 }
7436 if (arg_sym.info is ast.Alias || exp_sym.info is ast.Alias) && expected_type != arg_typ {
7437 g.expr_opt_with_alias(arg.expr, arg_typ, expected_type)
7438 } else {
7439 if arg.expr is ast.Ident && arg.expr.obj is ast.Var {
7440 if arg.expr.obj.smartcasts.len > 0 && !arg_typ.has_option_or_result() {
7441 arg_typ = arg.expr.obj.smartcasts.last()
7442 }
7443 }
7444 // If the argument is a smartcast variable whose original type is option
7445 // and matches the expected option type, pass the option value directly.
7446 if arg.expr is ast.Ident {
7447 if scope_var := arg.expr.scope.find_var(arg.expr.name) {
7448 if scope_var.smartcasts.len > 0 && scope_var.orig_type.has_flag(.option)
7449 && expected_type.has_flag(.option)
7450 && g.unwrap_generic(scope_var.orig_type).idx() == g.unwrap_generic(expected_type).idx() {
7451 g.write(c_name(arg.expr.name))
7452 return
7453 }
7454 }
7455 }
7456 g.expr_with_opt(arg.expr, arg_typ, expected_type)
7457 }
7458 return
7459 } else if arg.expr is ast.ArrayInit {
7460 if arg.expr.is_fixed {
7461 if !arg.expr.has_index {
7462 g.write('(${g.styp(arg.expr.typ)})')
7463 }
7464 }
7465 } else if arg.expr is ast.ComptimeSelector && arg_typ.has_flag(.option)
7466 && !expected_type.has_flag(.option) {
7467 // allow to pass val.$(filed.name) where T is expected, doing automatic unwrap in this case
7468 styp := g.base_type(arg_typ)
7469 g.write('*(${styp}*)')
7470 g.expr_with_cast(ast.Expr(arg.expr), arg_typ, expected_type)
7471 g.write('.data')
7472 return
7473 } else if arg.expr is ast.Ident && arg_sym.info is ast.Struct && arg_sym.info.is_anon
7474 && !expected_type.has_flag(.generic) {
7475 // make anon struct struct compatible with another anon struct declaration
7476 g.write('*(${g.cc_type(expected_type, false)}*)&')
7477 } else if arg.expr is ast.Ident {
7478 if arg.expr.obj is ast.Var {
7479 if arg.expr.obj.is_arg && arg.expr.obj.is_mut && !arg.is_mut && arg_typ.is_ptr()
7480 && !expected_type.is_any_kind_of_pointer() {
7481 unwrapped_expected := g.unwrap_generic(g.recheck_concrete_type(expected_type))
7482 nr_derefs := arg_typ.nr_muls()
7483 if nr_derefs > 0 && unwrapped_expected == g.unwrap_generic(arg_typ.set_nr_muls(0)) {
7484 g.write('${'*'.repeat(nr_derefs)}')
7485 g.expr(ast.Expr(arg.expr))
7486 return
7487 }
7488 }
7489 } else if arg.expr.kind == .constant && arg_typ.is_ptr()
7490 && !expected_type.is_any_kind_of_pointer()
7491 && g.unwrap_generic(g.recheck_concrete_type(expected_type)).idx() == arg_typ.deref().idx() {
7492 // Const declared with `&` (e.g. `const x = &Foo{}`) is stored as a pointer in C,
7493 // but when passed to a function expecting a value parameter, it must be dereferenced.
7494 g.write('(*')
7495 g.expr(ast.Expr(arg.expr))
7496 g.write(')')
7497 return
7498 }
7499 }
7500 // check if the argument must be dereferenced or not
7501 g.arg_no_auto_deref = is_smartcast && !arg_is_ptr && !exp_is_ptr && arg.should_be_ptr
7502 // When a smartcast selector/ident is passed to a function expecting the original sumtype,
7503 // the type resolution above resolves arg_typ to the sumtype (matching expected_type),
7504 // so expr_with_cast won't handle the sumtype wrapping. We need to prevent the smartcast
7505 // unwrapping in g.expr() since the value is already the correct sumtype at runtime.
7506 if !arg_typ.has_flag(.option) && exp_sym.kind == .sum_type && arg_typ == expected_type {
7507 g.prevent_sum_type_unwrapping_once = g.is_expr_smartcast_to_sumtype(arg.expr, expected_type)
7508 }
7509 if arg_typ.has_flag(.option) {
7510 g.expr_with_opt(arg.expr, arg_typ, expected_type.set_flag(.option))
7511 } else if expected_type.has_flag(.option) && !arg_typ.has_flag(.option) {
7512 g.expr_with_opt(arg.expr, arg_typ, expected_type)
7513 } else {
7514 g.expr_with_cast(arg.expr, arg_typ, expected_type)
7515 }
7516 g.arg_no_auto_deref = false
7517 g.inside_smartcast = old_inside_smartcast
7518 if needs_closing {
7519 g.write(')')
7520 }
7521}
7522
7523fn (mut g Gen) is_gui_app() bool {
7524 match g.pref.subsystem {
7525 .windows { return true }
7526 .console { return false }
7527 .auto {}
7528 }
7529
7530 if g.pref.os == .windows {
7531 if g.force_main_console {
7532 return false
7533 }
7534 for cf in g.table.cflags {
7535 if cf.value.to_lower_ascii() == 'gdi32' {
7536 return true
7537 }
7538 }
7539 }
7540 return false
7541}
7542
7543fn (g &Gen) fileis(s string) bool {
7544 return g.file.path.contains(s)
7545}
7546
7547fn (mut g Gen) write_fn_attrs(attrs []ast.Attr) string {
7548 mut fn_attrs := ''
7549 for attr in attrs {
7550 match attr.name {
7551 'inline' {
7552 g.write('inline ')
7553 }
7554 'noinline' {
7555 // since these are supported by GCC, clang and MSVC, we can consider them officially supported.
7556 g.write('__NOINLINE ')
7557 }
7558 'weak' {
7559 if attrs.any(it.name == 'export') {
7560 // only the exported wrapper should be weak; otherwise x86_64-w64-mingw32-gcc complains
7561 continue
7562 }
7563 // a `@[weak]` tag tells the C compiler, that the next declaration will be weak, i.e. when linking,
7564 // if there is another declaration of a symbol with the same name (a 'strong' one), it should be
7565 // used instead, *without linker errors about duplicate symbols*.
7566 g.write('VWEAK ')
7567 }
7568 'noreturn' {
7569 // a `@[noreturn]` tag tells the compiler, that a function
7570 // *DOES NOT RETURN* to its callsites.
7571 // See: https://en.cppreference.com/w/c/language/_Noreturn
7572 // Such functions should have no return type. They can be used
7573 // in places where `panic(err)` or `exit(0)` can be used.
7574 // panic/1 and exit/0 themselves will also be marked as
7575 // `@[noreturn]` soon.
7576 // These functions should have busy `for{}` loops injected
7577 // at their end, when they do not end by calling other fns
7578 // marked by `@[noreturn]`.
7579 g.write('VNORETURN ')
7580 }
7581 'irq_handler' {
7582 g.write('__IRQHANDLER ')
7583 }
7584 '_cold' {
7585 // GCC/clang attributes
7586 // prefixed by _ to indicate they're for advanced users only and not really supported by V.
7587 // source for descriptions: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes
7588 // The cold attribute on functions is used to inform the compiler that the function is unlikely
7589 // to be executed. The function is optimized for size rather than speed and on many targets it
7590 // is placed into a special subsection of the text section so all cold functions appear close
7591 // together, improving code locality of non-cold parts of program.
7592 g.write('__attribute__((cold)) ')
7593 }
7594 '_constructor' {
7595 // The constructor attribute causes the function to be called automatically before execution
7596 // enters main ().
7597 g.write('__attribute__((constructor)) ')
7598 }
7599 '_destructor' {
7600 // The destructor attribute causes the function to be called automatically after main ()
7601 // completes or exit () is called.
7602 g.write('__attribute__((destructor)) ')
7603 }
7604 '_flatten' {
7605 // Generally, inlining into a function is limited. For a function marked with this attribute,
7606 // every call inside this function is inlined, if possible.
7607 g.write('__attribute__((flatten)) ')
7608 }
7609 '_hot' {
7610 // The hot attribute on a function is used to inform the compiler that the function is a hot
7611 // spot of the compiled program.
7612 g.write('__attribute__((hot)) ')
7613 }
7614 '_malloc' {
7615 // This tells the compiler that a function is malloc-like, i.e., that the pointer P returned by
7616 // the function cannot alias any other pointer valid when the function returns, and moreover no
7617 // pointers to valid objects occur in any storage addressed by P.
7618 g.write('__attribute__((malloc)) ')
7619 }
7620 '_pure' {
7621 // Calls to functions whose return value is not affected by changes to the observable state
7622 // of the program and that have no observable effects on such state other than to return a
7623 // value may lend themselves to optimizations such as common subexpression elimination.
7624 // Declaring such functions with the const attribute allows GCC to avoid emitting some calls in
7625 // repeated invocations of the function with the same argument values.
7626 g.write('__attribute__((const)) ')
7627 }
7628 '_naked' {
7629 g.write('__attribute__((naked)) ')
7630 }
7631 'windows_stdcall' {
7632 // windows attributes (msvc/mingw)
7633 // prefixed by windows to indicate they're for advanced users only and not really supported by V.
7634 fn_attrs += call_convention_attribute('stdcall')
7635 }
7636 '_fastcall' {
7637 fn_attrs += call_convention_attribute('fastcall')
7638 }
7639 'callconv' {
7640 fn_attrs += call_convention_attribute(attr.arg)
7641 }
7642 'console' {
7643 g.force_main_console = true
7644 }
7645 else {
7646 // nothing but keep V happy
7647 }
7648 }
7649 }
7650 return fn_attrs
7651}
7652
7653fn call_convention_attribute(cconvention string) string {
7654 return 'VCALLCONV(${cconvention}) '
7655}
7656
7657fn (mut g Gen) write_fntype_decl(fn_name string, info ast.FnType, nr_muls int) {
7658 ret_styp := g.styp(info.func.return_type)
7659 mut call_conv_attr := ''
7660 for attr in info.func.attrs {
7661 match attr.name {
7662 'callconv' {
7663 call_conv_attr = call_convention_attribute(attr.arg)
7664 }
7665 else {}
7666 }
7667 }
7668 g.write('${ret_styp} (${call_conv_attr}${'*'.repeat(nr_muls + 1)}${fn_name}) (')
7669 def_pos := g.definitions.len
7670 g.fn_decl_params(info.func.params, unsafe { nil }, false, false)
7671 g.definitions.go_back(g.definitions.len - def_pos)
7672 g.write(')')
7673}
7674