v2 / vlib / v / gen / c / utils.v
1804 lines · 1750 sloc · 61.01 KB · 142f665ddd1b56426ca611b16c8dc7088aa05190
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 v.ast
7
8const cgen_resolution_hash_prime = u64(1099511628211)
9const cgen_resolution_hash_seed = u64(14695981039346656037)
10const cgen_unwrap_generic_cache_salt = u64(0x9e3779b185ebca87)
11const cgen_scope_var_type_cache_salt = u64(0xc2b2ae3d27d4eb4f)
12
13@[inline]
14fn cgen_resolution_hash_mix(key u64, value u64) u64 {
15 return (key ^ value) * cgen_resolution_hash_prime
16}
17
18fn (mut g Gen) clear_type_resolution_caches() {
19 g.unwrap_generic_cache.clear()
20 g.resolved_scope_var_type_cache.clear()
21}
22
23fn (mut g Gen) current_sumtype_match_variant_type(ident ast.Ident, sumtype_type ast.Type) ast.Type {
24 if g.cur_fn == unsafe { nil } || g.cur_concrete_types.len == 0 {
25 return ast.Type(0)
26 }
27 mut branch_type := ast.Type(0)
28 if typ := g.type_resolver.type_map[ident.name] {
29 branch_type = typ
30 }
31 if branch_type == 0 {
32 cname := c_name(ident.name)
33 if cname != ident.name {
34 if typ := g.type_resolver.type_map[cname] {
35 branch_type = typ
36 }
37 }
38 }
39 if branch_type == 0 || branch_type == ast.void_type {
40 return ast.Type(0)
41 }
42 variant_type := g.unwrap_generic(g.recheck_concrete_type(branch_type))
43 if variant_type == 0 || variant_type == ast.void_type || variant_type.has_flag(.generic)
44 || g.type_has_unresolved_generic_parts(variant_type) {
45 return ast.Type(0)
46 }
47 mut parent_type := g.unwrap_generic(g.recheck_concrete_type(sumtype_type)).set_nr_muls(0)
48 if parent_type != 0 && g.table.final_sym(parent_type).kind == .sum_type
49 && g.table.sumtype_has_variant(parent_type, variant_type, false) {
50 return variant_type
51 }
52 parent_type = g.resolve_current_fn_generic_param_type(ident.name).set_nr_muls(0)
53 if parent_type == 0 || g.table.final_sym(parent_type).kind != .sum_type
54 || !g.table.sumtype_has_variant(parent_type, variant_type, false) {
55 return ast.Type(0)
56 }
57 return variant_type
58}
59
60fn (g &Gen) type_resolution_context_key() u64 {
61 mut key := cgen_resolution_hash_seed
62 if g.inside_struct_init {
63 key = cgen_resolution_hash_mix(key, 1)
64 }
65 key = cgen_resolution_hash_mix(key, u64(g.cur_struct_init_typ))
66 key = cgen_resolution_hash_mix(key, u64(g.cur_concrete_types.len))
67 for concrete_type in g.cur_concrete_types {
68 key = cgen_resolution_hash_mix(key, u64(concrete_type))
69 }
70 key = cgen_resolution_hash_mix(key, u64(g.active_call_concrete_types.len))
71 for concrete_type in g.active_call_concrete_types {
72 key = cgen_resolution_hash_mix(key, u64(concrete_type))
73 }
74 if g.comptime != unsafe { nil } {
75 key = cgen_resolution_hash_mix(key, u64(g.comptime.comptime_loop_id))
76 key = cgen_resolution_hash_mix(key, u64(g.comptime.comptime_for_field_type))
77 key = cgen_resolution_hash_mix(key, u64(g.comptime.comptime_for_method_ret_type))
78 }
79 return key
80}
81
82@[inline]
83fn (g &Gen) type_resolution_cache_key(typ ast.Type, salt u64) u64 {
84 return cgen_resolution_hash_mix(g.type_resolution_context_key(), u64(typ)) ^ salt
85}
86
87@[inline]
88fn (g &Gen) expr_resolution_cache_key(pos int, default_typ ast.Type, salt u64) u64 {
89 if pos <= 0 {
90 return 0
91 }
92 mut key := g.type_resolution_context_key()
93 key = cgen_resolution_hash_mix(key, u64(g.fid + 2))
94 key = cgen_resolution_hash_mix(key, u64(pos))
95 key = cgen_resolution_hash_mix(key, u64(default_typ))
96 return key ^ salt
97}
98
99@[inline]
100fn (g &Gen) type_is_known_concrete(typ ast.Type) bool {
101 if typ == 0 || typ.has_flag(.generic) {
102 return false
103 }
104 idx := typ.idx()
105 return idx <= ast.nil_type_idx
106 || (idx < g.generic_parts_cache.len && g.generic_parts_cache[idx] == 1)
107}
108
109@[inline]
110fn (mut g Gen) unwrap_generic(typ ast.Type) ast.Type {
111 if typ == 0 {
112 return typ
113 }
114 if !typ.has_flag(.generic) {
115 idx := typ.idx()
116 if idx <= ast.nil_type_idx
117 || (idx < g.generic_parts_cache.len && g.generic_parts_cache[idx] == 1) {
118 return typ
119 }
120 }
121 return g.unwrap_generic_slow(typ)
122}
123
124fn (mut g Gen) unwrap_generic_slow(typ ast.Type) ast.Type {
125 cache_key := g.type_resolution_cache_key(typ, cgen_unwrap_generic_cache_salt)
126 if cached := g.unwrap_generic_cache[cache_key] {
127 return cached
128 }
129 mut resolved_typ := g.recheck_concrete_type(typ)
130 if resolved_typ == 0 {
131 resolved_typ = typ
132 }
133 if !resolved_typ.has_flag(.generic) {
134 resolved_idx := resolved_typ.idx()
135 if resolved_idx <= ast.nil_type_idx
136 || (resolved_idx < g.generic_parts_cache.len
137 && g.generic_parts_cache[resolved_idx] == 1)
138 || !g.type_has_unresolved_generic_parts(resolved_typ) {
139 g.unwrap_generic_cache[cache_key] = resolved_typ
140 return resolved_typ
141 }
142 }
143 // NOTE: `convert_generic_type` should not mutate the table.
144 //
145 // It mutates if the generic type is for example `[]T` and the concrete
146 // type is an array type that has not been registered yet.
147 //
148 // This should have already happened in the checker, since it also calls
149 // `convert_generic_type`. `g.table` is made non-mut to make sure
150 // no one else can accidentally mutates the table.
151 current_generic_names := g.current_fn_generic_names()
152 if current_generic_names.len > 0 && current_generic_names.len == g.cur_concrete_types.len {
153 if t_typ := g.table.convert_generic_type(resolved_typ, current_generic_names,
154 g.cur_concrete_types)
155 {
156 g.unwrap_generic_cache[cache_key] = t_typ
157 return t_typ
158 }
159 } else if g.inside_struct_init {
160 if g.cur_struct_init_typ != 0 {
161 sym := g.table.sym(g.cur_struct_init_typ)
162 if sym.info is ast.Struct {
163 if sym.info.generic_types.len > 0 {
164 generic_names := sym.info.generic_types.map(g.table.sym(it).name)
165 mut concrete_types := sym.info.concrete_types.clone()
166 if concrete_types.len == 0 && sym.generic_types.len == generic_names.len
167 && sym.generic_types != sym.info.generic_types {
168 concrete_types = sym.generic_types.clone()
169 }
170 if t_typ := g.table.convert_generic_type(resolved_typ, generic_names,
171 concrete_types)
172 {
173 g.unwrap_generic_cache[cache_key] = t_typ
174 return t_typ
175 }
176 }
177 }
178 }
179 } else if resolved_typ != 0 && g.table.sym(resolved_typ).kind == .struct {
180 // resolve selector `a.foo` where `a` is struct[T] on non generic function
181 sym := g.table.sym(resolved_typ)
182 if sym.info is ast.Struct {
183 if sym.info.generic_types.len > 0 {
184 generic_names := sym.info.generic_types.map(g.table.sym(it).name)
185 mut concrete_types := sym.info.concrete_types.clone()
186 if concrete_types.len == 0 && sym.generic_types.len == generic_names.len
187 && sym.generic_types != sym.info.generic_types {
188 concrete_types = sym.generic_types.clone()
189 }
190 if t_typ := g.table.convert_generic_type(resolved_typ, generic_names,
191 concrete_types)
192 {
193 g.unwrap_generic_cache[cache_key] = t_typ
194 return t_typ
195 }
196
197 if t_typ := g.table.convert_generic_type(resolved_typ, generic_names,
198 g.cur_concrete_types)
199 {
200 g.unwrap_generic_cache[cache_key] = t_typ
201 return t_typ
202 }
203 }
204 }
205 }
206 if typ.has_flag(.generic) {
207 if t_typ := g.type_resolver.resolve_bound_generic_type(typ) {
208 g.unwrap_generic_cache[cache_key] = t_typ
209 return t_typ
210 }
211 }
212 g.unwrap_generic_cache[cache_key] = resolved_typ
213 return resolved_typ
214}
215
216// Promotes literal element types in arrays (e.g. []int_literal -> []int, []float_literal -> []f64)
217// so that array comparisons use the correct registered array type.
218fn (mut g Gen) promote_literal_array_type(typ ast.Type) ast.Type {
219 sym := g.table.sym(typ)
220 if sym.info is ast.Array {
221 promoted_elem := ast.mktyp(sym.info.elem_type)
222 if promoted_elem != sym.info.elem_type {
223 idx := g.table.find_or_register_array(promoted_elem)
224 if idx > 0 {
225 return ast.new_type(idx).derive(typ)
226 }
227 }
228 }
229 return typ
230}
231
232fn (mut g Gen) infer_branch_expr_type(stmts []ast.Stmt) ast.Type {
233 if stmts.len == 0 {
234 return ast.void_type
235 }
236 last_stmt := stmts.last()
237 if last_stmt !is ast.ExprStmt {
238 return ast.void_type
239 }
240 expr_stmt := last_stmt as ast.ExprStmt
241 mut default_typ := expr_stmt.typ
242 if default_typ == 0 || default_typ == ast.void_type {
243 default_typ = expr_stmt.expr.type()
244 }
245 mut resolved_typ := g.resolved_expr_type(expr_stmt.expr, default_typ)
246 if resolved_typ == 0 || resolved_typ == ast.void_type {
247 resolved_typ = g.type_resolver.get_type_or_default(expr_stmt.expr, default_typ)
248 }
249 if resolved_typ == 0 || resolved_typ == ast.void_type {
250 resolved_typ = default_typ
251 }
252 if resolved_typ == 0 || resolved_typ == ast.void_type {
253 return ast.void_type
254 }
255 return g.unwrap_generic(g.recheck_concrete_type(resolved_typ))
256}
257
258fn (mut g Gen) expected_rhs_type_for_expr(pos int, node_type ast.Type) ast.Type {
259 if pos < 0 || node_type == 0 || node_type == ast.void_type || !node_type.has_option_or_result() {
260 return ast.void_type
261 }
262 expected_type := g.expected_rhs_type_by_pos[pos] or { return ast.void_type }
263 if expected_type == 0 || expected_type == ast.void_type || expected_type.has_option_or_result() {
264 return ast.void_type
265 }
266 resolved_expected_type := g.unwrap_generic(g.recheck_concrete_type(expected_type))
267 if resolved_expected_type == 0 || resolved_expected_type == ast.void_type
268 || resolved_expected_type.has_option_or_result() {
269 return ast.void_type
270 }
271 return resolved_expected_type
272}
273
274fn (mut g Gen) infer_if_expr_type(node ast.IfExpr) ast.Type {
275 if g.inside_return && g.inside_struct_init {
276 for branch in node.branches {
277 branch_typ := g.infer_branch_expr_type(branch.stmts)
278 if branch_typ != 0 && branch_typ != ast.void_type {
279 return branch_typ
280 }
281 }
282 }
283 if node.typ != 0 && node.typ != ast.void_type {
284 expected_rhs_type := g.expected_rhs_type_for_expr(node.pos.pos, node.typ)
285 if expected_rhs_type != ast.void_type {
286 return expected_rhs_type
287 }
288 resolved := g.unwrap_generic(g.recheck_concrete_type(node.typ))
289 // In generic functions, node.typ may have been mutated by the checker
290 // to a concrete type from the last processed instantiation. When the
291 // if-expr is used as a return expression, use the function's
292 // return type instead, which correctly resolves via cur_concrete_types.
293 // Only apply this override when the function's return type is actually
294 // generic — otherwise the if-expression type is concrete and correct.
295 if g.inside_return_expr && !g.inside_struct_init && g.cur_fn != unsafe { nil }
296 && g.cur_concrete_types.len > 0 && g.cur_fn.return_type.has_flag(.generic) {
297 fn_ret := g.unwrap_generic(g.recheck_concrete_type(g.cur_fn.return_type))
298 if fn_ret != 0 && fn_ret != ast.void_type {
299 if node.typ.has_flag(.result) && !fn_ret.has_flag(.result) {
300 return fn_ret.set_flag(.result)
301 } else if node.typ.has_flag(.option) && !fn_ret.has_flag(.option) {
302 return fn_ret.set_flag(.option)
303 }
304 return fn_ret
305 }
306 }
307 return resolved
308 }
309 for branch in node.branches {
310 branch_typ := g.infer_branch_expr_type(branch.stmts)
311 if branch_typ != 0 && branch_typ != ast.void_type {
312 return branch_typ
313 }
314 }
315 return ast.void_type
316}
317
318fn (mut g Gen) infer_match_expr_type(node ast.MatchExpr) ast.Type {
319 if g.inside_return && g.inside_struct_init {
320 for branch in node.branches {
321 branch_typ := g.infer_branch_expr_type(branch.stmts)
322 if branch_typ != 0 && branch_typ != ast.void_type {
323 return branch_typ
324 }
325 }
326 }
327 if node.return_type != 0 && node.return_type != ast.void_type {
328 expected_rhs_type := g.expected_rhs_type_for_expr(node.pos.pos, node.return_type)
329 if expected_rhs_type != ast.void_type {
330 return expected_rhs_type
331 }
332 resolved := g.unwrap_generic(g.recheck_concrete_type(node.return_type))
333 // In generic functions, node.return_type may have been mutated by the checker
334 // to a concrete type from the last processed instantiation. When the match is
335 // used as a return expression, use the function's return type
336 // instead, which correctly resolves via cur_concrete_types.
337 // Only apply this override when the function's return type is actually
338 // generic — otherwise the match expression type is concrete and correct.
339 if g.inside_return_expr && !g.inside_struct_init && g.cur_fn != unsafe { nil }
340 && g.cur_concrete_types.len > 0 && g.cur_fn.return_type.has_flag(.generic) {
341 fn_ret := g.unwrap_generic(g.recheck_concrete_type(g.cur_fn.return_type))
342 if fn_ret != 0 && fn_ret != ast.void_type {
343 // Preserve option/result flags from the match's return_type
344 if node.return_type.has_flag(.result) && !fn_ret.has_flag(.result) {
345 return fn_ret.set_flag(.result)
346 } else if node.return_type.has_flag(.option) && !fn_ret.has_flag(.option) {
347 return fn_ret.set_flag(.option)
348 }
349 return fn_ret
350 }
351 }
352 return resolved
353 }
354 for branch in node.branches {
355 branch_typ := g.infer_branch_expr_type(branch.stmts)
356 if branch_typ != 0 && branch_typ != ast.void_type {
357 return branch_typ
358 }
359 }
360 return ast.void_type
361}
362
363fn (mut g Gen) recheck_concrete_type(typ ast.Type) ast.Type {
364 if typ == 0 {
365 return typ
366 }
367 if g.cur_fn == unsafe { nil } || g.cur_concrete_types.len == 0 {
368 return typ
369 }
370 idx := typ.idx()
371 if idx <= ast.nil_type_idx || (!typ.has_flag(.generic) && idx < g.generic_parts_cache.len
372 && g.generic_parts_cache[idx] == 1) {
373 return typ
374 }
375 sym := g.table.sym(typ)
376 match sym.info {
377 ast.Struct, ast.Interface, ast.SumType {
378 if sym.info.concrete_types.len > 0
379 && !sym.info.concrete_types.any(it.has_flag(.generic)) {
380 return typ
381 }
382 }
383 ast.GenericInst {
384 if sym.info.concrete_types.len > 0
385 && !sym.info.concrete_types.any(it.has_flag(.generic)) {
386 return typ
387 }
388 }
389 else {}
390 }
391
392 if !typ.has_flag(.generic) && !g.type_has_unresolved_generic_parts(typ) {
393 return typ
394 }
395 if g.cur_fn == unsafe { nil } || g.cur_concrete_types.len == 0 {
396 return typ
397 }
398 generic_names := g.current_fn_generic_names()
399 if generic_names.len == 0 || generic_names.len != g.cur_concrete_types.len {
400 return typ
401 }
402 concrete_types := g.cur_concrete_types
403 if resolved_typ := g.table.convert_generic_type(typ, generic_names, concrete_types) {
404 return resolved_typ
405 }
406 mut muttable := unsafe { &ast.Table(g.table) }
407 unwrapped_typ := muttable.unwrap_generic_type_ex(typ, generic_names, concrete_types, true)
408 if unwrapped_typ != typ {
409 return unwrapped_typ
410 }
411 return typ
412}
413
414@[inline]
415fn (g &Gen) has_current_generic_context() bool {
416 return g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0
417}
418
419@[inline]
420fn (mut g Gen) type_needs_generic_resolution(typ ast.Type) bool {
421 if typ == 0 {
422 return false
423 }
424 if typ.has_flag(.generic) {
425 return true
426 }
427 if typ.idx() <= ast.nil_type_idx {
428 return false
429 }
430 if (g.cur_fn == unsafe { nil } || g.cur_concrete_types.len == 0)
431 && !g.has_active_call_generic_context() {
432 return false
433 }
434 idx := typ.idx()
435 if idx <= ast.nil_type_idx
436 || (idx < g.generic_parts_cache.len && g.generic_parts_cache[idx] == 1) {
437 return false
438 }
439 return g.type_has_unresolved_generic_parts(typ)
440}
441
442// is_expr_smartcast_to_sumtype checks if expr is a smartcast variable/field
443// whose original type is the given sumtype. This is used to prevent sumtype
444// variant unwrapping when passing a smartcast expression to a function
445// that expects the original sumtype.
446fn (mut g Gen) is_expr_smartcast_to_sumtype(expr ast.Expr, expected_sumtype ast.Type) bool {
447 scope := g.file.scope.innermost(expr.pos().pos)
448 if expr is ast.SelectorExpr {
449 v := scope.find_struct_field(expr.expr.str(), expr.expr_type, expr.field_name)
450 if v != unsafe { nil } && v.smartcasts.len > 0 {
451 orig_type := g.unwrap_generic(g.recheck_concrete_type(v.orig_type))
452 resolved_expected_sumtype := g.unwrap_generic(g.recheck_concrete_type(expected_sumtype))
453 return orig_type == resolved_expected_sumtype
454 }
455 } else if expr is ast.Ident {
456 if v := scope.find_var(expr.name) {
457 if v.smartcasts.len > 0 && v.orig_type == expected_sumtype {
458 return true
459 }
460 }
461 }
462 return false
463}
464
465// expr_has_or_block checks if an expression has an `or {}` block that
466// unwraps an option/result type.
467fn (g Gen) expr_has_or_block(expr ast.Expr) bool {
468 return match expr {
469 ast.CallExpr { expr.or_block.kind != .absent }
470 ast.Ident { expr.or_expr.kind != .absent }
471 ast.IndexExpr { expr.or_expr.kind != .absent }
472 ast.SelectorExpr { expr.or_block.kind != .absent }
473 ast.PrefixExpr { expr.or_block.kind != .absent }
474 ast.InfixExpr { expr.or_block.kind != .absent }
475 ast.ComptimeCall { expr.or_block.kind != .absent }
476 ast.ComptimeSelector { expr.or_block.kind != .absent }
477 else { false }
478 }
479}
480
481fn (g &Gen) is_auto_deref_source_ident(expr ast.Expr) bool {
482 if expr is ast.Ident {
483 ident := expr as ast.Ident
484 if ident.obj is ast.Var && ident.obj.is_auto_deref {
485 return true
486 }
487 if source_var := ident.scope.find_var(ident.name) {
488 return source_var.is_auto_deref
489 }
490 }
491 return false
492}
493
494fn (g &Gen) auto_deref_source_type_is_pointer(expr ast.Expr) bool {
495 if expr !is ast.Ident || g.cur_fn == unsafe { nil } || !expr.is_auto_deref_var() {
496 return false
497 }
498 ident := expr as ast.Ident
499 for param in g.cur_fn.params {
500 if param.name == ident.name {
501 source_typ := if param.orig_typ != 0 { param.orig_typ } else { param.typ }
502 return source_typ.is_any_kind_of_pointer()
503 }
504 }
505 return false
506}
507
508fn (mut g Gen) resolved_scope_var_type(expr ast.Ident) ast.Type {
509 if g.has_active_call_generic_context() {
510 return g.resolved_scope_var_type_uncached(expr)
511 }
512 cache_key := g.expr_resolution_cache_key(expr.pos.pos, 0, cgen_scope_var_type_cache_salt)
513 if cache_key != 0 {
514 if cached := g.resolved_scope_var_type_cache[cache_key] {
515 return cached
516 }
517 }
518 resolved := g.resolved_scope_var_type_uncached(expr)
519 if cache_key != 0 && resolved != 0 {
520 g.resolved_scope_var_type_cache[cache_key] = resolved
521 }
522 return resolved
523}
524
525fn (mut g Gen) resolved_scope_var_type_uncached(expr ast.Ident) ast.Type {
526 mut scope := if expr.scope != unsafe { nil } {
527 expr.scope.innermost(expr.pos.pos)
528 } else {
529 expr.scope
530 }
531 if scope == unsafe { nil } || scope.find_var(expr.name) == none {
532 scope = if g.file.scope != unsafe { nil } {
533 g.file.scope.innermost(expr.pos.pos)
534 } else {
535 expr.scope
536 }
537 }
538 if scope == unsafe { nil } {
539 return 0
540 }
541 if mut v := scope.find_var(expr.name) {
542 mut refreshed_expr_type := ast.Type(0)
543 if v.generic_typ != 0 {
544 refreshed_generic_type := g.unwrap_generic(g.recheck_concrete_type(v.generic_typ))
545 if refreshed_generic_type != 0 {
546 v.typ = refreshed_generic_type
547 v.orig_type = ast.no_type
548 v.smartcasts = []
549 v.is_unwrapped = false
550 }
551 }
552 if !v.is_arg && v.expr !is ast.EmptyExpr && v.pos.pos > 0 && v.pos.pos < expr.pos.pos
553 && !(v.expr is ast.Ident && v.expr.name == expr.name)
554 && ((g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0)
555 || v.typ.has_flag(.generic)
556 || g.type_has_unresolved_generic_parts(v.typ)) {
557 resolved_expr_type := g.resolved_expr_type(v.expr, v.typ)
558 if resolved_expr_type != 0 {
559 refreshed_expr_type = g.unwrap_generic(g.recheck_concrete_type(resolved_expr_type))
560 if g.type_has_unresolved_generic_parts(refreshed_expr_type) {
561 call_like_type := g.resolved_call_like_expr_type(v.expr)
562 if call_like_type != 0 && !call_like_type.has_flag(.generic)
563 && !g.type_has_unresolved_generic_parts(call_like_type) {
564 refreshed_expr_type = call_like_type
565 }
566 }
567 // Keep `mut x := param` as a value copy when re-resolving locals,
568 // unless the original mut parameter type was already a pointer.
569 if g.is_auto_deref_source_ident(v.expr) && refreshed_expr_type.is_ptr()
570 && !g.auto_deref_source_type_is_pointer(v.expr) {
571 refreshed_expr_type = refreshed_expr_type.deref()
572 }
573 // If the variable was initialized with an `or {}` block that
574 // unwraps the option/result, clear the flag from the resolved type
575 if refreshed_expr_type.has_option_or_result() && g.expr_has_or_block(v.expr) {
576 refreshed_expr_type = refreshed_expr_type.clear_option_and_result()
577 }
578 $if trace_ci_fixes ? {
579 if g.file.path.contains('comptime_for_in_options_struct_test.v')
580 && expr.name in ['v', 'w'] {
581 current_name := if v.typ == 0 { '0' } else { g.table.type_to_str(v.typ) }
582 resolved_name := if refreshed_expr_type == 0 {
583 '0'
584 } else {
585 g.table.type_to_str(refreshed_expr_type)
586 }
587 orig_name := if v.orig_type == 0 {
588 '0'
589 } else {
590 g.table.type_to_str(v.orig_type)
591 }
592 eprintln('resolved_scope_var_type refresh ${expr.name}: current=${current_name} expr=${typeof(v.expr).name} resolved=${resolved_name} unwrapped=${v.is_unwrapped} orig=${orig_name}')
593 }
594 }
595 if v.is_unwrapped && refreshed_expr_type.has_option_or_result() {
596 v.orig_type = refreshed_expr_type
597 v.typ = refreshed_expr_type.clear_option_and_result()
598 } else {
599 v.typ = refreshed_expr_type
600 if !v.is_unwrapped && v.smartcasts.len == 0 {
601 v.orig_type = ast.no_type
602 }
603 }
604 }
605 }
606 if (v.is_unwrapped || v.smartcasts.len > 0) && scope.parent != unsafe { nil } {
607 if mut parent_v := scope.parent.find_var(expr.name) {
608 if parent_v.generic_typ != 0 {
609 refreshed_parent_type :=
610 g.unwrap_generic(g.recheck_concrete_type(parent_v.generic_typ))
611 if refreshed_parent_type != 0 {
612 parent_v.typ = refreshed_parent_type
613 }
614 }
615 if !parent_v.is_arg && parent_v.expr !is ast.EmptyExpr && parent_v.pos.pos > 0
616 && parent_v.pos.pos < expr.pos.pos && !(parent_v.expr is ast.Ident
617 && parent_v.expr.name == expr.name)
618 && ((g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0)
619 || parent_v.typ.has_flag(.generic)
620 || g.type_has_unresolved_generic_parts(parent_v.typ)) {
621 resolved_parent_expr_type := g.resolved_expr_type(parent_v.expr, parent_v.typ)
622 if resolved_parent_expr_type != 0 {
623 mut refreshed_parent_type :=
624 g.unwrap_generic(g.recheck_concrete_type(resolved_parent_expr_type))
625 if g.type_has_unresolved_generic_parts(parent_v.typ) {
626 call_like_type := g.resolved_call_like_expr_type(parent_v.expr)
627 if call_like_type != 0 && !call_like_type.has_flag(.generic)
628 && !g.type_has_unresolved_generic_parts(call_like_type) {
629 parent_v.typ = call_like_type
630 }
631 }
632 if g.is_auto_deref_source_ident(parent_v.expr)
633 && refreshed_parent_type.is_ptr()
634 && !g.auto_deref_source_type_is_pointer(parent_v.expr) {
635 refreshed_parent_type = refreshed_parent_type.deref()
636 }
637 parent_v.typ = refreshed_parent_type
638 }
639 }
640 if v.is_unwrapped {
641 parent_orig_type := if parent_v.orig_type != ast.no_type
642 && parent_v.orig_type.has_option_or_result() {
643 parent_v.orig_type
644 } else {
645 parent_v.typ
646 }
647 if parent_orig_type.has_option_or_result() {
648 v.orig_type = parent_orig_type
649 v.typ = parent_orig_type.clear_option_and_result()
650 }
651 } else if v.smartcasts.len > 0 && parent_v.typ != 0 && v.orig_type == ast.no_type {
652 v.orig_type = if parent_v.orig_type != ast.no_type {
653 parent_v.orig_type
654 } else {
655 parent_v.typ
656 }
657 }
658 }
659 }
660 if v.is_inherited && scope.parent != unsafe { nil } {
661 if mut parent_v := scope.parent.find_var(expr.name) {
662 by_value_auto_deref_capture := !v.is_auto_deref && parent_v.is_auto_deref
663 && parent_v.typ.is_ptr()
664 if by_value_auto_deref_capture {
665 if parent_v.generic_typ != 0 {
666 refreshed_parent_type :=
667 g.unwrap_generic(g.recheck_concrete_type(parent_v.generic_typ))
668 if refreshed_parent_type != 0 {
669 parent_v.typ = refreshed_parent_type
670 }
671 }
672 if parent_v.expr !is ast.EmptyExpr
673 && ((g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0)
674 || parent_v.typ.has_flag(.generic)
675 || g.type_has_unresolved_generic_parts(parent_v.typ)) {
676 resolved_parent_type := g.resolved_expr_type(parent_v.expr, parent_v.typ)
677 if resolved_parent_type != 0 {
678 parent_v.typ =
679 g.unwrap_generic(g.recheck_concrete_type(resolved_parent_type))
680 }
681 }
682 if parent_v.typ != 0 {
683 resolved_parent_type :=
684 g.unwrap_generic(g.recheck_concrete_type(parent_v.typ))
685 if resolved_parent_type != 0 {
686 return if resolved_parent_type.is_ptr() {
687 resolved_parent_type.deref()
688 } else {
689 resolved_parent_type
690 }
691 }
692 }
693 } else {
694 if parent_v.generic_typ != 0 {
695 refreshed_parent_type :=
696 g.unwrap_generic(g.recheck_concrete_type(parent_v.generic_typ))
697 if refreshed_parent_type != 0 {
698 parent_v.typ = refreshed_parent_type
699 }
700 }
701 if parent_v.smartcasts.len > 0 {
702 smartcast_type := if parent_v.ct_type_var == .smartcast {
703 g.type_resolver.get_type(expr)
704 } else {
705 g.exposed_smartcast_type(parent_v.orig_type,
706 parent_v.smartcasts.last(), parent_v.is_mut)
707 }
708 return g.unwrap_generic(g.recheck_concrete_type(smartcast_type))
709 }
710 if parent_v.expr !is ast.EmptyExpr
711 && ((g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0)
712 || parent_v.typ.has_flag(.generic)
713 || g.type_has_unresolved_generic_parts(parent_v.typ)) {
714 resolved_parent_type := g.resolved_expr_type(parent_v.expr, parent_v.typ)
715 if resolved_parent_type != 0 {
716 resolved_parent :=
717 g.unwrap_generic(g.recheck_concrete_type(resolved_parent_type))
718 if g.type_has_unresolved_generic_parts(resolved_parent) {
719 call_like_type := g.resolved_call_like_expr_type(parent_v.expr)
720 if call_like_type != 0 && !call_like_type.has_flag(.generic)
721 && !g.type_has_unresolved_generic_parts(call_like_type) {
722 return call_like_type
723 }
724 }
725 return resolved_parent
726 }
727 }
728 if parent_v.typ != 0 {
729 return g.unwrap_generic(g.recheck_concrete_type(parent_v.typ))
730 }
731 }
732 }
733 }
734 if v.smartcasts.len > 0 {
735 smartcast_type := if v.ct_type_var == .smartcast {
736 g.type_resolver.get_type(expr)
737 } else {
738 g.exposed_smartcast_type(v.orig_type, v.smartcasts.last(), v.is_mut)
739 }
740 return g.unwrap_generic(g.recheck_concrete_type(smartcast_type))
741 }
742 if v.is_unwrapped {
743 $if trace_ci_fixes ? {
744 if g.file.path.contains('comptime_for_in_options_struct_test.v')
745 && expr.name in ['v', 'w'] {
746 typ_name := if v.typ == 0 { '0' } else { g.table.type_to_str(v.typ) }
747 orig_name := if v.orig_type == 0 {
748 '0'
749 } else {
750 g.table.type_to_str(v.orig_type)
751 }
752 refreshed_name := if refreshed_expr_type == 0 {
753 '0'
754 } else {
755 g.table.type_to_str(refreshed_expr_type)
756 }
757 eprintln('resolved_scope_var_type unwrapped ${expr.name}: typ=${typ_name} orig=${orig_name} refreshed=${refreshed_name}')
758 }
759 }
760 if refreshed_expr_type != 0 && refreshed_expr_type.has_option_or_result() {
761 return g.unwrap_generic(g.recheck_concrete_type(refreshed_expr_type.clear_option_and_result()))
762 }
763 unwrapped_type := if v.orig_type != ast.no_type && v.orig_type.has_option_or_result() {
764 v.orig_type.clear_option_and_result()
765 } else {
766 v.typ.clear_option_and_result()
767 }
768 if unwrapped_type != 0 && unwrapped_type != ast.void_type {
769 return g.unwrap_generic(g.recheck_concrete_type(unwrapped_type))
770 }
771 }
772 if v.typ != 0 {
773 return g.unwrap_generic(g.recheck_concrete_type(v.typ))
774 }
775 }
776 return 0
777}
778
779fn (mut g Gen) resolved_ident_is_auto_heap_not_stack(expr ast.Ident) bool {
780 if expr.obj is ast.Var {
781 if expr.obj.is_auto_heap && !expr.obj.is_stack_obj {
782 return true
783 }
784 }
785 if expr.scope != unsafe { nil } {
786 if v := expr.scope.find_var(expr.name) {
787 return v.is_auto_heap && !v.is_stack_obj
788 }
789 }
790 return false
791}
792
793fn (mut g Gen) resolved_ident_is_auto_heap(expr ast.Ident) bool {
794 if expr.obj is ast.Var && expr.obj.is_auto_heap {
795 return true
796 }
797 if expr.scope != unsafe { nil } {
798 if v := expr.scope.find_var(expr.name) {
799 return v.is_auto_heap
800 }
801 }
802 return false
803}
804
805fn (g &Gen) resolved_ident_is_auto_deref(expr ast.Ident) bool {
806 if expr.scope != unsafe { nil } {
807 if v := expr.scope.find_var(expr.name) {
808 return v.is_auto_deref
809 }
810 }
811 if expr.obj is ast.Var {
812 return expr.obj.is_auto_deref
813 }
814 return false
815}
816
817fn (g &Gen) resolved_ident_is_by_value_auto_deref_capture(expr ast.Ident) bool {
818 if expr.scope == unsafe { nil } || expr.scope.parent == unsafe { nil } {
819 return false
820 }
821 scope_var := expr.scope.find_var(expr.name) or { return false }
822 if !scope_var.is_inherited || scope_var.is_auto_deref {
823 return false
824 }
825 parent_var := expr.scope.parent.find_var(expr.name) or { return false }
826 return parent_var.is_auto_deref && parent_var.typ.is_ptr()
827}
828
829fn (g &Gen) expr_is_auto_deref_var(expr ast.Expr) bool {
830 return match expr {
831 ast.Ident { g.resolved_ident_is_auto_deref(expr) }
832 else { expr.is_auto_deref_var() }
833 }
834}
835
836// scope_ident_is_auto_heap reports whether `expr`'s scope variable has
837// is_auto_heap set. Unlike `resolved_ident_is_auto_heap`, this ignores
838// `expr.obj.is_auto_heap` because a use-site Ident's obj copy may have been
839// toggled by `mark_as_referenced` even when the declaration was emitted as
840// a value (e.g. vars declared inside nested scopes where fn_scope.find_var
841// misses them).
842fn (mut g Gen) scope_ident_is_auto_heap(expr ast.Ident) bool {
843 if expr.scope != unsafe { nil } {
844 if v := expr.scope.find_var(expr.name) {
845 return v.is_auto_heap
846 }
847 }
848 return false
849}
850
851fn (mut g Gen) resolved_ident_array_elem_type(expr ast.Ident) ast.Type {
852 scope_type := g.resolved_scope_var_type(expr)
853 if scope_type != 0 {
854 elem_type := g.recheck_concrete_type(g.table.value_type(g.unwrap_generic(scope_type)))
855 if elem_type != 0 {
856 return g.unwrap_generic(elem_type)
857 }
858 }
859 if expr.obj !is ast.Var {
860 return 0
861 }
862 var_obj := expr.obj as ast.Var
863 if var_obj.expr is ast.ArrayInit {
864 array_init := var_obj.expr as ast.ArrayInit
865 if array_init.elem_type != 0 {
866 return g.unwrap_generic(g.recheck_concrete_type(array_init.elem_type))
867 }
868 if array_init.typ != 0 {
869 elem_type :=
870 g.recheck_concrete_type(g.table.value_type(g.unwrap_generic(array_init.typ)))
871 if elem_type != 0 {
872 return g.unwrap_generic(elem_type)
873 }
874 }
875 }
876 if var_obj.typ != 0 {
877 elem_type := g.recheck_concrete_type(g.table.value_type(g.unwrap_generic(var_obj.typ)))
878 if elem_type != 0 {
879 return g.unwrap_generic(elem_type)
880 }
881 }
882 return 0
883}
884
885fn (mut g Gen) resolved_ident_map_key_type(expr ast.Ident) ast.Type {
886 scope_type := g.resolved_scope_var_type(expr)
887 if scope_type != 0 {
888 typ_sym := g.table.final_sym(g.unwrap_generic(scope_type))
889 if typ_sym.kind == .map {
890 key_type := g.recheck_concrete_type(typ_sym.map_info().key_type)
891 if key_type != 0 {
892 return g.unwrap_generic(key_type)
893 }
894 }
895 }
896 if expr.obj !is ast.Var {
897 return 0
898 }
899 var_obj := expr.obj as ast.Var
900 if var_obj.expr is ast.MapInit {
901 map_init := var_obj.expr as ast.MapInit
902 if map_init.key_type != 0 {
903 return g.unwrap_generic(g.recheck_concrete_type(map_init.key_type))
904 }
905 }
906 if var_obj.typ != 0 {
907 typ_sym := g.table.final_sym(g.unwrap_generic(var_obj.typ))
908 if typ_sym.kind == .map {
909 key_type := g.recheck_concrete_type(typ_sym.map_info().key_type)
910 if key_type != 0 {
911 return g.unwrap_generic(key_type)
912 }
913 }
914 }
915 return 0
916}
917
918fn (mut g Gen) resolved_ident_map_value_type(expr ast.Ident) ast.Type {
919 scope_type := g.resolved_scope_var_type(expr)
920 if scope_type != 0 {
921 typ_sym := g.table.final_sym(g.unwrap_generic(scope_type))
922 if typ_sym.kind == .map {
923 val_type := g.recheck_concrete_type(typ_sym.map_info().value_type)
924 if val_type != 0 {
925 return g.unwrap_generic(val_type)
926 }
927 }
928 }
929 if expr.obj !is ast.Var {
930 return 0
931 }
932 var_obj := expr.obj as ast.Var
933 if var_obj.expr is ast.MapInit {
934 map_init := var_obj.expr as ast.MapInit
935 if map_init.value_type != 0 {
936 return g.unwrap_generic(g.recheck_concrete_type(map_init.value_type))
937 }
938 }
939 if var_obj.typ != 0 {
940 typ_sym := g.table.final_sym(g.unwrap_generic(var_obj.typ))
941 if typ_sym.kind == .map {
942 val_type := g.recheck_concrete_type(typ_sym.map_info().value_type)
943 if val_type != 0 {
944 return g.unwrap_generic(val_type)
945 }
946 }
947 }
948 return 0
949}
950
951fn (mut g Gen) resolved_array_elem_type_from_name(name string) ast.Type {
952 if !name.starts_with('[]') {
953 return 0
954 }
955 elem_type := g.table.find_type(name[2..])
956 if elem_type != 0 {
957 return g.unwrap_generic(g.recheck_concrete_type(elem_type))
958 }
959 return 0
960}
961
962fn split_map_type_name(name string) (string, string) {
963 if !name.starts_with('map[') {
964 return '', ''
965 }
966 mut depth := 1
967 mut i := 4
968 for i < name.len {
969 ch := name[i]
970 if ch == `[` {
971 depth++
972 } else if ch == `]` {
973 depth--
974 if depth == 0 {
975 break
976 }
977 }
978 i++
979 }
980 if depth != 0 || i >= name.len {
981 return '', ''
982 }
983 return name[4..i], name[i + 1..]
984}
985
986fn (mut g Gen) resolved_map_types_from_name(name string) (ast.Type, ast.Type) {
987 key_name, val_name := split_map_type_name(name)
988 if key_name.len == 0 || val_name.len == 0 {
989 return ast.Type(0), ast.Type(0)
990 }
991 key_type := g.table.find_type(key_name)
992 val_type := g.table.find_type(val_name)
993 return g.unwrap_generic(g.recheck_concrete_type(key_type)), g.unwrap_generic(g.recheck_concrete_type(val_type))
994}
995
996fn (mut g Gen) resolved_call_like_expr_type(expr ast.Expr) ast.Type {
997 match expr {
998 ast.CallExpr {
999 resolved := g.resolve_return_type(expr)
1000 if resolved != ast.void_type {
1001 return g.unwrap_generic(g.recheck_concrete_type(resolved)).clear_option_and_result()
1002 }
1003 }
1004 ast.PostfixExpr {
1005 resolved := g.resolved_call_like_expr_type(expr.expr)
1006 if resolved != 0 {
1007 return g.unwrap_generic(resolved).clear_option_and_result()
1008 }
1009 }
1010 ast.UnsafeExpr {
1011 return g.resolved_call_like_expr_type(expr.expr)
1012 }
1013 else {}
1014 }
1015
1016 return 0
1017}
1018
1019// resolved_expr_type recomputes the concrete type for expr nodes that can keep
1020// stale generic metadata across concrete rechecks/codegen.
1021fn (mut g Gen) resolved_or_block_value_type(or_expr ast.OrExpr) ast.Type {
1022 if or_expr.stmts.len == 0 {
1023 return 0
1024 }
1025 last_or_stmt := or_expr.stmts.last()
1026 if last_or_stmt is ast.ExprStmt && last_or_stmt.typ != ast.void_type {
1027 resolved_or_type := g.resolved_expr_type(last_or_stmt.expr, last_or_stmt.typ)
1028 if resolved_or_type != 0 && resolved_or_type != ast.void_type {
1029 return g.unwrap_generic(g.recheck_concrete_type(resolved_or_type))
1030 }
1031 }
1032 return 0
1033}
1034
1035// resolve_selector_smartcast_type resolves the final smartcast type for a
1036// selector expression in generic contexts. When a field like `val.field` has
1037// nested smartcasts (e.g., option unwrap then sumtype variant), the scope
1038// stores these smartcasts but they may reference stale types from a previous
1039// generic instantiation. This function re-resolves them using current concrete
1040// types to determine the correct final type.
1041fn (mut g Gen) resolve_selector_smartcast_type(node ast.SelectorExpr) ast.Type {
1042 scope := g.file.scope.innermost(node.pos.pos)
1043 field := scope.find_struct_field(node.expr.str(), node.expr_type, node.field_name)
1044 if field != unsafe { nil } && field.smartcasts.len > 0 {
1045 smartcast_type := field.smartcasts.last()
1046 field_unwrapped_type := field.orig_type.clear_option_and_result()
1047 if field.orig_type.has_option_or_result() && !smartcast_type.has_option_or_result()
1048 && smartcast_type == field_unwrapped_type {
1049 left_default := if node.expr_type != 0 { node.expr_type } else { field.struct_type }
1050 left_type := g.resolved_expr_type(node.expr, left_default)
1051 resolved_field_type := g.resolved_selector_field_type(node, left_type)
1052 if resolved_field_type != 0 {
1053 return resolved_field_type.clear_option_and_result()
1054 }
1055 }
1056 resolved_sc := g.unwrap_generic(g.recheck_concrete_type(g.exposed_smartcast_type(field.orig_type,
1057 smartcast_type, field.is_mut)))
1058 if resolved_sc != 0 {
1059 return resolved_sc
1060 }
1061 }
1062 return 0
1063}
1064
1065fn (mut g Gen) resolved_selector_field_type(node ast.SelectorExpr, receiver_type ast.Type) ast.Type {
1066 if receiver_type == 0 {
1067 return 0
1068 }
1069 sym := g.table.sym(g.unwrap_generic(receiver_type))
1070 if field := g.table.find_field_with_embeds(sym, node.field_name) {
1071 mut field_type := field.typ
1072 match sym.info {
1073 ast.Struct, ast.Interface, ast.SumType {
1074 mut generic_names := sym.info.generic_types.map(g.table.sym(it).name)
1075 mut concrete_types := sym.info.concrete_types.clone()
1076 if concrete_types.len == 0 && sym.generic_types.len == generic_names.len
1077 && sym.generic_types != sym.info.generic_types {
1078 concrete_types = sym.generic_types.clone()
1079 }
1080 mut source_field_type := field.typ
1081 if sym.info.parent_type.has_flag(.generic) {
1082 parent_sym := g.table.sym(sym.info.parent_type)
1083 if parent_field := g.table.find_field_with_embeds(parent_sym, node.field_name) {
1084 source_field_type = parent_field.typ
1085 match parent_sym.info {
1086 ast.Struct, ast.Interface, ast.SumType {
1087 generic_names =
1088 parent_sym.info.generic_types.map(g.table.sym(it).name)
1089 }
1090 else {}
1091 }
1092 }
1093 }
1094 if generic_names.len == concrete_types.len && concrete_types.len > 0 {
1095 mut muttable := unsafe { &ast.Table(g.table) }
1096 resolved_field_type := muttable.unwrap_generic_type_ex(source_field_type,
1097 generic_names, concrete_types, true)
1098 if resolved_field_type != source_field_type {
1099 field_type = resolved_field_type
1100 } else {
1101 if converted_field_type := muttable.convert_generic_type(source_field_type,
1102 generic_names, concrete_types)
1103 {
1104 field_type = converted_field_type
1105 }
1106 }
1107 }
1108 }
1109 ast.GenericInst {
1110 parent_sym := g.table.sym(ast.new_type(sym.info.parent_idx))
1111 mut source_field_type := field.typ
1112 if parent_field := g.table.find_field_with_embeds(parent_sym, node.field_name) {
1113 source_field_type = parent_field.typ
1114 }
1115 match parent_sym.info {
1116 ast.Struct, ast.Interface, ast.SumType {
1117 generic_names := parent_sym.info.generic_types.map(g.table.sym(it).name)
1118 if generic_names.len == sym.info.concrete_types.len
1119 && sym.info.concrete_types.len > 0 {
1120 mut muttable := unsafe { &ast.Table(g.table) }
1121 resolved_field_type := muttable.unwrap_generic_type_ex(source_field_type,
1122 generic_names, sym.info.concrete_types, true)
1123 if resolved_field_type != source_field_type {
1124 field_type = resolved_field_type
1125 } else {
1126 if converted_field_type := muttable.convert_generic_type(source_field_type,
1127 generic_names, sym.info.concrete_types)
1128 {
1129 field_type = converted_field_type
1130 }
1131 }
1132 }
1133 }
1134 else {}
1135 }
1136 }
1137 else {}
1138 }
1139
1140 $if trace_ci_fixes ? {
1141 if g.file.path.contains('binary_search_tree.v') && node.expr is ast.Ident
1142 && node.expr.name == 'tree' {
1143 eprintln('resolved selector ${node.expr.name}.${node.field_name} left=${g.table.type_to_str(receiver_type)} field=${g.table.type_to_str(field.typ)} final=${g.table.type_to_str(field_type)} expr_typ=${g.table.type_to_str(node.typ)}')
1144 }
1145 }
1146 return g.unwrap_generic(g.recheck_concrete_type(field_type))
1147 }
1148 return 0
1149}
1150
1151fn (mut g Gen) resolved_expr_type(expr ast.Expr, default_typ ast.Type) ast.Type {
1152 match expr {
1153 ast.ParExpr {
1154 return g.resolved_expr_type(expr.expr, default_typ)
1155 }
1156 ast.CTempVar {
1157 if expr.typ != 0 {
1158 return g.unwrap_generic(g.recheck_concrete_type(expr.typ))
1159 }
1160 return g.resolved_expr_type(expr.orig, default_typ)
1161 }
1162 ast.Ident {
1163 if expr.obj is ast.Var {
1164 if expr.obj.typ != 0 && expr.obj.generic_typ == 0 && !expr.obj.is_inherited
1165 && !expr.obj.is_unwrapped && !expr.obj.is_assignment_smartcast
1166 && !expr.obj.is_or && expr.obj.orig_type == ast.no_type
1167 && expr.obj.smartcasts.len == 0 && expr.obj.ct_type_var == .no_comptime
1168 && !g.has_current_generic_context() && !g.has_active_call_generic_context() {
1169 if g.type_is_known_concrete(expr.obj.typ) {
1170 return expr.obj.typ
1171 }
1172 }
1173 if g.cur_fn != unsafe { nil } && g.cur_fn.is_method
1174 && expr.name == g.cur_fn.receiver.name {
1175 // In generic contexts, prefer resolving from the receiver declaration
1176 // since scope types may be stale from a previous checker instantiation
1177 if g.cur_concrete_types.len > 0 && (g.cur_fn.receiver.typ.has_flag(.generic)
1178 || g.type_has_unresolved_generic_parts(g.cur_fn.receiver.typ)) {
1179 resolved_receiver_type :=
1180 g.unwrap_generic(g.recheck_concrete_type(g.cur_fn.receiver.typ))
1181 if resolved_receiver_type != 0 {
1182 return resolved_receiver_type
1183 }
1184 }
1185 scope_type := g.resolved_scope_var_type(expr)
1186 if scope_type != 0 {
1187 return scope_type
1188 }
1189 resolved_receiver_type :=
1190 g.unwrap_generic(g.recheck_concrete_type(g.cur_fn.receiver.typ))
1191 if resolved_receiver_type != 0 {
1192 return resolved_receiver_type
1193 }
1194 }
1195 if g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0
1196 && expr.obj.expr is ast.Ident {
1197 ident_expr := expr.obj.expr as ast.Ident
1198 if ident_expr.or_expr.kind != .absent {
1199 resolved_or_type := g.resolved_or_block_value_type(ident_expr.or_expr)
1200 if resolved_or_type != 0 {
1201 return resolved_or_type
1202 }
1203 }
1204 }
1205 if expr.obj.ct_type_var == .generic_param {
1206 resolved := g.resolve_current_fn_generic_param_type(expr.name)
1207 if resolved != 0 {
1208 return g.unwrap_generic(g.recheck_concrete_type(resolved))
1209 }
1210 }
1211 // In generic contexts, if the variable has smartcasts with generic
1212 // types (preserved in node.obj), resolve from those instead of
1213 // relying on scope types which may be stale from a different
1214 // generic instantiation.
1215 if g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0
1216 && expr.obj.smartcasts.len > 0 && expr.obj.smartcasts.any(it.has_flag(.generic)
1217 || g.type_has_unresolved_generic_parts(it)) {
1218 obj_smartcast_type := g.exposed_smartcast_type(expr.obj.orig_type,
1219 expr.obj.smartcasts.last(), expr.obj.is_mut)
1220 resolved_sc := g.unwrap_generic(g.recheck_concrete_type(obj_smartcast_type))
1221 if resolved_sc != 0 {
1222 return resolved_sc
1223 }
1224 }
1225 scope_type := g.resolved_scope_var_type(expr)
1226 if scope_type != 0 && !scope_type.has_flag(.generic)
1227 && !g.type_has_unresolved_generic_parts(scope_type) {
1228 if !expr.obj.is_arg
1229 || (g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0) {
1230 // For generic_var in generic contexts, prefer expression-based
1231 // resolution first (scope types may be stale from a previous
1232 // instantiation), but fall back to scope type if expr resolution
1233 // fails.
1234 if g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0
1235 && expr.obj.ct_type_var == .generic_var
1236 && expr.obj.typ.has_flag(.generic) {
1237 // Try expression-based resolution first
1238 if expr.obj.expr !is ast.EmptyExpr && !(expr.obj.expr is ast.Ident
1239 && expr.obj.expr.name == expr.name) {
1240 resolved := g.resolved_expr_type(expr.obj.expr, expr.obj.typ)
1241 if resolved != 0 {
1242 return g.unwrap_generic(resolved)
1243 }
1244 }
1245 }
1246 // In generic contexts, scope types may be stale from a previous
1247 // checker instantiation. If the variable's init expression is a
1248 // struct init whose type name matches a generic parameter, re-resolve
1249 // using the current concrete types.
1250 if g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0
1251 && expr.obj.expr is ast.StructInit {
1252 generic_names := g.current_fn_generic_names()
1253 struct_init_typ_str := expr.obj.expr.typ_str.all_after_last('.')
1254 idx := generic_names.index(struct_init_typ_str)
1255 if idx >= 0 && idx < g.cur_concrete_types.len {
1256 return g.cur_concrete_types[idx]
1257 }
1258 }
1259 return scope_type
1260 }
1261 }
1262 if expr.obj.expr !is ast.EmptyExpr
1263 && (expr.obj.ct_type_var == .generic_var || expr.obj.typ.has_flag(.generic)
1264 || g.type_has_unresolved_generic_parts(expr.obj.typ)
1265 || ((g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0)
1266 && expr.obj.expr !in [ast.IntegerLiteral, ast.FloatLiteral, ast.StringLiteral, ast.BoolLiteral, ast.CharLiteral])) {
1267 if !(expr.obj.expr is ast.Ident && expr.obj.expr.name == expr.name) {
1268 mut resolved := g.resolved_expr_type(expr.obj.expr, expr.obj.typ)
1269 if resolved != 0 {
1270 resolved = g.unwrap_generic(g.recheck_concrete_type(resolved))
1271 if expr.obj.typ != 0 {
1272 resolved_obj_type :=
1273 g.unwrap_generic(g.recheck_concrete_type(expr.obj.typ))
1274 if resolved_obj_type != 0
1275 && !g.type_has_unresolved_generic_parts(resolved_obj_type)
1276 && resolved.has_option_or_result()
1277 && resolved.clear_option_and_result() == resolved_obj_type {
1278 return resolved_obj_type
1279 }
1280 }
1281 if g.type_has_unresolved_generic_parts(resolved) {
1282 call_like_type := g.resolved_call_like_expr_type(expr.obj.expr)
1283 if call_like_type != 0 && !call_like_type.has_flag(.generic)
1284 && !g.type_has_unresolved_generic_parts(call_like_type) {
1285 return call_like_type
1286 }
1287 }
1288 return g.unwrap_generic(resolved)
1289 }
1290 }
1291 }
1292 if expr.obj.ct_type_var != .no_comptime && expr.obj.ct_type_var != .generic_param {
1293 ctyp := g.type_resolver.get_type(expr)
1294 if ctyp != ast.void_type {
1295 return g.unwrap_generic(ctyp)
1296 }
1297 }
1298 }
1299 if g.cur_fn != unsafe { nil } && g.cur_fn.generic_names.len > 0
1300 && g.cur_concrete_types.len > 0 {
1301 resolved := g.resolve_current_fn_generic_param_type(expr.name)
1302 if resolved != 0 {
1303 return g.unwrap_generic(g.recheck_concrete_type(resolved))
1304 }
1305 }
1306 if expr.obj is ast.Var && expr.obj.typ != 0 {
1307 resolved_obj_type := g.unwrap_generic(g.recheck_concrete_type(expr.obj.typ))
1308 if resolved_obj_type != 0 && (expr.obj.is_arg || expr.obj.typ.has_flag(.generic)
1309 || g.type_has_unresolved_generic_parts(expr.obj.typ)) {
1310 return resolved_obj_type
1311 }
1312 }
1313 scope_type := g.resolved_scope_var_type(expr)
1314 if scope_type != 0 && expr.obj is ast.Var
1315 && (expr.obj.is_unwrapped || expr.obj.orig_type != 0 || expr.obj.smartcasts.len > 0) {
1316 return scope_type
1317 }
1318 default_resolved_type := g.unwrap_generic(g.recheck_concrete_type(default_typ))
1319 resolver_type := g.unwrap_generic(g.recheck_concrete_type(g.type_resolver.get_type_or_default(expr,
1320 default_typ)))
1321 if resolver_type != 0 && !g.type_has_unresolved_generic_parts(resolver_type)
1322 && (resolver_type != default_resolved_type || (expr.obj is ast.Var
1323 && (expr.obj.is_unwrapped || expr.obj.orig_type != 0
1324 || expr.obj.smartcasts.len > 0))) {
1325 return resolver_type
1326 }
1327 if scope_type != 0 {
1328 return scope_type
1329 }
1330 }
1331 ast.SelectorExpr {
1332 // If this selector has been smart-cast in the current scope (e.g.
1333 // `if mut w.face is X { ... w.face ... }`), use the smart-cast type
1334 // rather than the field's declared type.
1335 smartcast_typ := g.resolve_selector_smartcast_type(expr)
1336 if smartcast_typ != 0 {
1337 return smartcast_typ
1338 }
1339 left_default := if expr.expr_type != 0 { expr.expr_type } else { default_typ }
1340 left_type := g.recheck_concrete_type(g.resolved_expr_type(expr.expr, left_default))
1341 if left_type != 0 {
1342 mut resolved_type := g.resolved_selector_field_type(expr, left_type)
1343 if resolved_type != 0 {
1344 if expr.or_block.kind != .absent {
1345 resolved_type = resolved_type.clear_option_and_result()
1346 }
1347 return resolved_type
1348 }
1349 }
1350 if expr.typ != 0 {
1351 mut resolved_type := g.unwrap_generic(g.recheck_concrete_type(expr.typ))
1352 if expr.or_block.kind != .absent {
1353 resolved_type = resolved_type.clear_option_and_result()
1354 }
1355 return resolved_type
1356 }
1357 }
1358 ast.IndexExpr {
1359 left_default := if expr.left_type != 0 { expr.left_type } else { default_typ }
1360 left_type := g.recheck_concrete_type(g.resolved_expr_type(expr.left, left_default))
1361 if left_type != 0 {
1362 if expr.index is ast.RangeExpr {
1363 mut slice_type := ast.Type(0)
1364 if expr.left is ast.Ident {
1365 resolved_left_type :=
1366 g.resolve_current_fn_generic_param_type(expr.left.name)
1367 if resolved_left_type != 0 {
1368 slice_type = g.unwrap_generic(resolved_left_type)
1369 }
1370 }
1371 if slice_type == 0 {
1372 slice_type = g.unwrap_generic(left_type)
1373 }
1374 // Slicing returns a new array by value; strip pointer
1375 // from mut params.
1376 slice_type = slice_type.set_nr_muls(0)
1377 // Slicing a fixed array yields a dynamic array.
1378 slice_sym := g.table.final_sym(slice_type)
1379 if slice_sym.info is ast.ArrayFixed {
1380 return ast.new_type(g.table.find_or_register_array(slice_sym.info.elem_type))
1381 }
1382 return slice_type
1383 }
1384 if expr.left is ast.Ident {
1385 resolved_value_type :=
1386 g.resolve_current_fn_generic_param_value_type(expr.left.name)
1387 if resolved_value_type != 0 {
1388 return g.unwrap_generic(resolved_value_type)
1389 }
1390 }
1391 value_type :=
1392 g.recheck_concrete_type(g.table.value_type(g.unwrap_generic(left_type)))
1393 if value_type != 0 {
1394 return g.unwrap_generic(value_type)
1395 }
1396 if expr.typ != 0 && !expr.typ.has_flag(.generic)
1397 && !g.type_has_unresolved_generic_parts(expr.typ) {
1398 return g.unwrap_generic(expr.typ)
1399 }
1400 }
1401 }
1402 ast.InfixExpr {
1403 if expr.op in [.eq, .ne, .gt, .ge, .lt, .le, .logical_or, .and, .key_in, .not_in, .key_is,
1404 .not_is] {
1405 return ast.bool_type
1406 }
1407 // In generic contexts, promoted_type may be stale from a different
1408 // checker instantiation pass. Compute from operand types instead.
1409 if expr.promoted_type != 0 && !expr.promoted_type.has_flag(.generic)
1410 && !g.type_has_unresolved_generic_parts(expr.promoted_type)
1411 && (g.cur_fn == unsafe { nil } || g.cur_concrete_types.len == 0) {
1412 return g.unwrap_generic(g.recheck_concrete_type(expr.promoted_type))
1413 }
1414 left_default := if expr.left_type != 0 { expr.left_type } else { default_typ }
1415 right_default := if expr.right_type != 0 { expr.right_type } else { default_typ }
1416 left_type := g.resolved_expr_type(expr.left, left_default)
1417 right_type := g.resolved_expr_type(expr.right, right_default)
1418 if expr.op in [.plus, .minus, .mul, .power, .div, .mod] {
1419 if left_type == right_type && left_type != 0
1420 && left_type !in [ast.int_literal_type, ast.float_literal_type] {
1421 return g.unwrap_generic(left_type)
1422 }
1423 promoted := g.type_resolver.promote_type(g.unwrap_generic(left_type),
1424 g.unwrap_generic(right_type))
1425 if promoted != ast.void_type {
1426 return g.unwrap_generic(promoted)
1427 }
1428 }
1429 if expr.op in [.left_shift, .right_shift, .amp, .pipe, .xor] && left_type != 0 {
1430 return g.unwrap_generic(left_type)
1431 }
1432 }
1433 ast.IfExpr {
1434 inferred_typ := g.infer_if_expr_type(expr)
1435 if inferred_typ != 0 && inferred_typ != ast.void_type {
1436 return inferred_typ
1437 }
1438 }
1439 ast.MatchExpr {
1440 inferred_typ := g.infer_match_expr_type(expr)
1441 if inferred_typ != 0 && inferred_typ != ast.void_type {
1442 return inferred_typ
1443 }
1444 }
1445 ast.CallExpr {
1446 if expr.kind == .type_name {
1447 return ast.string_type
1448 }
1449 if expr.kind == .type_idx {
1450 return ast.int_type
1451 }
1452 resolved := g.resolve_return_type(expr)
1453 if resolved != ast.void_type {
1454 return if expr.or_block.kind == .absent {
1455 g.unwrap_generic(g.recheck_concrete_type(resolved))
1456 } else {
1457 g.unwrap_generic(g.recheck_concrete_type(resolved)).clear_option_and_result()
1458 }
1459 }
1460 // When resolve_return_type fails (e.g. fn field call where no method exists),
1461 // try resolving from return_type_generic using receiver generic type names.
1462 // This handles cases like Procedure[T,U].handle() calling p.function(p.value)
1463 // where return_type is contaminated from the last checker pass but
1464 // return_type_generic preserves the original generic return type (!U).
1465 if expr.return_type_generic != 0 && expr.return_type_generic.has_flag(.generic)
1466 && g.cur_fn != unsafe { nil } && g.cur_fn.is_method
1467 && g.cur_fn.receiver.typ.has_flag(.generic) && g.cur_concrete_types.len > 0 {
1468 receiver_generic_names := g.table.generic_type_names(g.cur_fn.receiver.typ)
1469 if receiver_generic_names.len == g.cur_concrete_types.len {
1470 if gen_type := g.table.convert_generic_type(expr.return_type_generic,
1471 receiver_generic_names, g.cur_concrete_types)
1472 {
1473 if !gen_type.has_flag(.generic) {
1474 return if expr.or_block.kind == .absent {
1475 gen_type
1476 } else {
1477 gen_type.clear_option_and_result()
1478 }
1479 }
1480 }
1481 }
1482 }
1483 if expr.return_type != 0 {
1484 return if expr.or_block.kind == .absent {
1485 g.unwrap_generic(g.recheck_concrete_type(expr.return_type))
1486 } else {
1487 g.unwrap_generic(g.recheck_concrete_type(expr.return_type)).clear_option_and_result()
1488 }
1489 }
1490 }
1491 ast.ComptimeCall {
1492 if expr.kind == .method && g.comptime.comptime_for_method != unsafe { nil } {
1493 sym := g.table.sym(g.unwrap_generic(expr.left_type))
1494 if m := sym.find_method(g.comptime.comptime_for_method.name) {
1495 return m.return_type
1496 }
1497 }
1498 }
1499 ast.ComptimeSelector {
1500 if expr.is_method {
1501 ctyp := g.type_resolver.get_comptime_selector_type(expr, ast.void_type)
1502 if ctyp != ast.void_type {
1503 return g.unwrap_generic(ctyp)
1504 }
1505 if expr.typ != ast.void_type && expr.typ != 0 {
1506 return g.unwrap_generic(expr.typ)
1507 }
1508 } else if expr.typ_key != '' {
1509 ctyp := g.type_resolver.get_ct_type_or_default(expr.typ_key, default_typ)
1510 if ctyp != ast.void_type {
1511 return g.unwrap_generic(ctyp)
1512 }
1513 }
1514 if expr.left_type != 0 {
1515 return g.unwrap_generic(expr.left_type)
1516 }
1517 }
1518 ast.CastExpr {
1519 if expr.typ != 0 {
1520 return g.unwrap_generic(g.recheck_concrete_type(expr.typ))
1521 }
1522 }
1523 ast.ArrayInit {
1524 base_array_typ := if expr.generic_typ != 0 { expr.generic_typ } else { expr.typ }
1525 base_elem_typ := if expr.generic_elem_type != 0 {
1526 expr.generic_elem_type
1527 } else {
1528 expr.elem_type
1529 }
1530 if g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0 && base_array_typ != 0
1531 && expr.exprs.len > 0 && !expr.is_fixed {
1532 array_type := g.unwrap_generic(g.recheck_concrete_type(base_array_typ))
1533 if g.table.final_sym(array_type).kind == .array {
1534 mut inferred_elem_type := ast.void_type
1535 for i, elem_expr in expr.exprs {
1536 mut default_elem_type := base_elem_typ
1537 if default_elem_type == 0 {
1538 default_elem_type = g.table.value_type(array_type)
1539 }
1540 if inferred_elem_type != ast.void_type {
1541 default_elem_type = inferred_elem_type
1542 }
1543 mut resolved_elem_type := ast.void_type
1544 if elem_expr is ast.Ident {
1545 resolved_elem_type = g.resolved_scope_var_type(elem_expr)
1546 }
1547 if resolved_elem_type == ast.void_type {
1548 expr_default_type := if expr.expr_types.len > i
1549 && expr.expr_types[i] != 0 {
1550 expr.expr_types[i]
1551 } else {
1552 default_elem_type
1553 }
1554 resolved_elem_type = g.unwrap_generic(g.recheck_concrete_type(g.resolved_expr_type(elem_expr,
1555 expr_default_type)))
1556 }
1557 if resolved_elem_type != 0 && resolved_elem_type != ast.void_type {
1558 inferred_elem_type = resolved_elem_type
1559 }
1560 }
1561 if inferred_elem_type != ast.void_type {
1562 return g.table.find_or_register_array(g.unwrap_generic(inferred_elem_type))
1563 }
1564 }
1565 }
1566 if base_array_typ != 0 {
1567 return g.unwrap_generic(g.recheck_concrete_type(base_array_typ))
1568 }
1569 }
1570 ast.StructInit {
1571 // For `T{}` where T is a generic parameter, typ_str preserves the
1572 // original name (e.g. 'main.T') even after the type has been resolved
1573 // to a concrete type. Use it to look up the current concrete type.
1574 if expr.typ_str.len > 0 && g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0 {
1575 generic_names := g.current_fn_generic_names()
1576 short_name := expr.typ_str.all_after_last('.')
1577 idx := generic_names.index(short_name)
1578 if idx >= 0 && idx < g.cur_concrete_types.len {
1579 return g.cur_concrete_types[idx]
1580 }
1581 }
1582 // In generic contexts, use generic_typ to allow resolution via
1583 // recheck_concrete_type/unwrap_generic only when the struct init type
1584 // itself still has unresolved generic parts (short syntax or generic flag).
1585 // For explicitly typed inits (e.g. LinkedList[StructFieldInfo]{}),
1586 // preserve expr.typ to avoid incorrectly substituting the inner struct's
1587 // generic parameter with the enclosing function's concrete type.
1588 base_struct_typ := if expr.generic_typ != 0 && g.cur_fn != unsafe { nil }
1589 && g.cur_concrete_types.len > 0 {
1590 if expr.is_short_syntax || expr.typ.has_flag(.generic) || expr.typ == ast.void_type {
1591 expr.generic_typ
1592 } else {
1593 expr.typ
1594 }
1595 } else if expr.typ != 0 {
1596 expr.typ
1597 } else {
1598 expr.generic_typ
1599 }
1600 if base_struct_typ != 0 {
1601 return g.unwrap_generic(g.recheck_concrete_type(base_struct_typ))
1602 }
1603 }
1604 ast.MapInit {
1605 if expr.typ != 0 {
1606 return g.unwrap_generic(g.recheck_concrete_type(expr.typ))
1607 }
1608 }
1609 ast.AsCast {
1610 return g.unwrap_generic(g.recheck_concrete_type(expr.typ))
1611 }
1612 ast.UnsafeExpr {
1613 return g.resolved_expr_type(expr.expr, default_typ)
1614 }
1615 ast.PrefixExpr {
1616 right_default := if expr.right_type != 0 { expr.right_type } else { default_typ }
1617 inner_type := g.resolved_expr_type(expr.right, right_default)
1618 return match expr.op {
1619 .amp {
1620 // When the inner expr is an auto-deref var (e.g. mut param),
1621 // codegen skips emitting & since the var is already a pointer.
1622 // Don't add .ref() to match.
1623 if expr.right.is_auto_deref_var() {
1624 g.unwrap_generic(inner_type)
1625 } else {
1626 g.unwrap_generic(inner_type).ref()
1627 }
1628 }
1629 .mul {
1630 resolved_inner_type := g.unwrap_generic(inner_type)
1631 if resolved_inner_type.is_ptr() {
1632 resolved_inner_type.deref()
1633 } else {
1634 resolved_right_type :=
1635 g.unwrap_generic(g.recheck_concrete_type(right_default))
1636 if resolved_right_type.is_ptr() {
1637 resolved_right_type.deref()
1638 } else {
1639 resolved_inner_type
1640 }
1641 }
1642 }
1643 .arrow {
1644 right_sym := g.table.final_sym(g.unwrap_generic(inner_type))
1645 if right_sym.kind == .chan {
1646 g.unwrap_generic(right_sym.chan_info().elem_type)
1647 } else {
1648 g.unwrap_generic(inner_type)
1649 }
1650 }
1651 else {
1652 g.unwrap_generic(inner_type)
1653 }
1654 }
1655 }
1656 ast.PostfixExpr {
1657 inner_default := if expr.typ != 0 { expr.typ } else { default_typ }
1658 inner_type := g.resolved_expr_type(expr.expr, inner_default)
1659 return if expr.op == .question {
1660 mut resolved_postfix_type := g.unwrap_generic(inner_type)
1661 if resolved_postfix_type != 0 && g.table.sym(resolved_postfix_type).kind == .alias {
1662 unaliased_postfix_type := g.table.unaliased_type(resolved_postfix_type)
1663 if unaliased_postfix_type.has_option_or_result() {
1664 resolved_postfix_type = g.unwrap_generic(unaliased_postfix_type)
1665 }
1666 }
1667 resolved_postfix_type.clear_option_and_result()
1668 } else {
1669 g.unwrap_generic(inner_type)
1670 }
1671 }
1672 else {}
1673 }
1674
1675 resolved := g.type_resolver.get_type_or_default(expr, default_typ)
1676 if resolved != 0 {
1677 return g.unwrap_generic(resolved)
1678 }
1679 return g.unwrap_generic(default_typ)
1680}
1681
1682struct Type {
1683 // typ is the original type
1684 typ ast.Type @[required]
1685 sym &ast.TypeSymbol @[required]
1686 // unaliased is `typ` once aliased have been resolved
1687 // it may not contain information such as flags and nr_muls
1688 unaliased ast.Type @[required]
1689 unaliased_sym &ast.TypeSymbol @[required]
1690}
1691
1692// unwrap returns the following variants of a type:
1693// * generics unwrapped
1694// * alias unwrapped
1695fn (mut g Gen) unwrap(typ ast.Type) Type {
1696 no_generic := g.unwrap_generic(typ)
1697 no_generic_sym := g.table.sym(no_generic)
1698 if no_generic_sym.kind != .alias {
1699 return Type{
1700 typ: no_generic
1701 sym: no_generic_sym
1702 unaliased: no_generic
1703 unaliased_sym: no_generic_sym
1704 }
1705 }
1706 return Type{
1707 typ: no_generic
1708 sym: no_generic_sym
1709 unaliased: no_generic_sym.parent_idx
1710 unaliased_sym: g.table.sym(ast.idx_to_type(no_generic_sym.parent_idx))
1711 }
1712}
1713
1714// generate function variable definition, e.g. `void (*var_name) (int, string)`
1715fn (mut g Gen) fn_var_signature(var_type ast.Type, return_type ast.Type, arg_types []ast.Type, var_name string) string {
1716 if var_type.has_flag(.option) || var_type.has_flag(.result) {
1717 return '${g.styp(var_type)} ${c_name(var_name)}'
1718 }
1719 ret_styp := g.styp(return_type)
1720 nr_muls := var_type.nr_muls()
1721 mut sig := '${ret_styp} (${'*'.repeat(nr_muls + 1)}${c_name(var_name)}) ('
1722 for j, arg_typ in arg_types {
1723 arg_sym := g.table.sym(arg_typ)
1724 if arg_sym.info is ast.FnType {
1725 func := arg_sym.info.func
1726 arg_sig := g.fn_var_signature(arg_typ, func.return_type, func.params.map(it.typ), '')
1727 sig += arg_sig
1728 } else {
1729 arg_styp := g.styp(arg_typ)
1730 sig += arg_styp
1731 }
1732 if j < arg_types.len - 1 {
1733 sig += ', '
1734 }
1735 }
1736 sig += ')'
1737 return sig
1738}
1739
1740// generate anon fn cname, e.g. `anon_fn_void_int_string`, `anon_fn_void_int_ptr_string`
1741fn (mut g Gen) anon_fn_cname(return_type ast.Type, arg_types []ast.Type) string {
1742 ret_styp := g.styp(return_type).replace('*', '_ptr')
1743 mut sig := 'anon_fn_${ret_styp}_'
1744 for j, arg_typ in arg_types {
1745 arg_sym := g.table.sym(arg_typ)
1746 if arg_sym.info is ast.FnType {
1747 sig += g.anon_fn_cname(arg_sym.info.func.return_type,
1748 arg_sym.info.func.params.map(it.typ))
1749 } else {
1750 sig += g.styp(arg_typ).replace('*', '_ptr')
1751 }
1752 if j < arg_types.len - 1 {
1753 sig += '_'
1754 }
1755 }
1756 return sig
1757}
1758
1759// escape quotes for string
1760fn escape_quotes(val string) string {
1761 bs := '\\'
1762 unescaped_val := val.replace('${bs}${bs}', '\x01').replace_each([
1763 "${bs}'",
1764 "'",
1765 '${bs}"',
1766 '"',
1767 ])
1768 return unescaped_val.replace_each(['\x01', '${bs}${bs}', "'", "${bs}'", '"', '${bs}"'])
1769}
1770
1771@[inline]
1772fn (mut g Gen) dot_or_ptr(val_type ast.Type) string {
1773 return if val_type.has_flag(.shared_f) {
1774 '->val.'
1775 } else if val_type.is_ptr() {
1776 '->'
1777 } else {
1778 '.'
1779 }
1780}
1781
1782fn (mut g Gen) unwrap_option_type(typ ast.Type, name string, is_auto_heap bool) {
1783 styp := g.base_type(typ)
1784 if is_auto_heap {
1785 g.write('(*(${styp}*)${name}->data)')
1786 } else {
1787 type_sym := g.table.sym(typ)
1788 if type_sym.kind == .alias {
1789 // Alias to Option type
1790 parent_typ := (type_sym.info as ast.Alias).parent_type
1791 if parent_typ.has_flag(.option) {
1792 g.write('*((${g.base_type(parent_typ)}*)')
1793 }
1794 g.write('(*(${styp}*)${name}.data)')
1795 if parent_typ.has_flag(.option) {
1796 g.write('.data)')
1797 }
1798 } else if typ.has_flag(.option_mut_param_t) {
1799 g.write('(*(${styp}*)${name}->data)')
1800 } else {
1801 g.write('(*(${styp}*)${name}.data)')
1802 }
1803 }
1804}
1805