v4 / 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