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