vxx2 / vlib / v3 / transform / expr.v
2145 lines · 2040 sloc · 69.46 KB · 2b81918e62db9ab38787518603a7f93bee80868c
Raw
1module transform
2
3import v3.flat
4import v3.types
5
6// transform_infix_string_ops transforms transform infix string ops data for transform.
7fn (mut t Transformer) transform_infix_string_ops(_id flat.NodeId, node flat.Node) ?flat.NodeId {
8 if node.children_count < 2 {
9 return none
10 }
11 match node.op {
12 .plus, .eq, .ne, .lt, .gt, .le, .ge {}
13 else {
14 return none
15 }
16 }
17
18 lhs_id := t.a.children[node.children_start]
19 rhs_id := t.a.children[node.children_start + 1]
20 lhs_raw_type := t.node_type(lhs_id)
21 rhs_raw_type := t.node_type(rhs_id)
22 lhs_clean_type := t.normalize_type_alias(lhs_raw_type)
23 rhs_clean_type := t.normalize_type_alias(rhs_raw_type)
24 lhs_is_string_ptr := t.equality_expr_is_string_pointer(lhs_id, lhs_clean_type)
25 rhs_is_string_ptr := t.equality_expr_is_string_pointer(rhs_id, rhs_clean_type)
26 if node.op in [.plus, .eq, .ne] && (lhs_is_string_ptr || rhs_is_string_ptr) {
27 return none
28 }
29
30 is_string := t.is_string_type(lhs_id) || t.is_string_type(rhs_id) || lhs_is_string_ptr
31 || rhs_is_string_ptr
32
33 if !is_string {
34 return none
35 }
36
37 mut new_lhs := t.transform_expr(lhs_id)
38 mut new_rhs := t.transform_expr(rhs_id)
39 if lhs_is_string_ptr {
40 new_lhs = t.make_prefix(.mul, new_lhs)
41 }
42 if rhs_is_string_ptr {
43 new_rhs = t.make_prefix(.mul, new_rhs)
44 }
45
46 match node.op {
47 .plus {
48 lhs := t.a.nodes[int(new_lhs)]
49 rhs := t.a.nodes[int(new_rhs)]
50 if lhs.kind == .string_literal && rhs.kind == .string_literal {
51 return t.make_string_literal(lhs.value + rhs.value)
52 }
53 return t.make_call('string__plus', arr2(new_lhs, new_rhs))
54 }
55 .eq {
56 return t.make_call('string__eq', arr2(new_lhs, new_rhs))
57 }
58 .ne {
59 eq_call := t.make_call('string__eq', arr2(new_lhs, new_rhs))
60 start := t.a.children.len
61 t.a.children << eq_call
62 return t.a.add_node(flat.Node{
63 kind: .prefix
64 op: .not
65 children_start: start
66 children_count: 1
67 })
68 }
69 .lt {
70 return t.make_call('string__lt', arr2(new_lhs, new_rhs))
71 }
72 .gt {
73 // a > b -> string__lt(b, a)
74 return t.make_call('string__lt', arr2(new_rhs, new_lhs))
75 }
76 .le {
77 // a <= b -> !(b < a) -> !string__lt(rhs, lhs)
78 lt_call := t.make_call('string__lt', arr2(new_rhs, new_lhs))
79 start := t.a.children.len
80 t.a.children << lt_call
81 return t.a.add_node(flat.Node{
82 kind: .prefix
83 op: .not
84 children_start: start
85 children_count: 1
86 })
87 }
88 .ge {
89 // a >= b -> !(a < b) -> !string__lt(lhs, rhs)
90 lt_call := t.make_call('string__lt', arr2(new_lhs, new_rhs))
91 start := t.a.children.len
92 t.a.children << lt_call
93 return t.a.add_node(flat.Node{
94 kind: .prefix
95 op: .not
96 children_start: start
97 children_count: 1
98 })
99 }
100 else {
101 return none
102 }
103 }
104}
105
106// transform_infix_array_ops transforms transform infix array ops data for transform.
107fn (mut t Transformer) transform_infix_array_ops(_id flat.NodeId, node flat.Node) ?flat.NodeId {
108 if node.children_count < 2 || node.op !in [.eq, .ne] {
109 return none
110 }
111 lhs_id := t.a.children[node.children_start]
112 rhs_id := t.a.children[node.children_start + 1]
113 lhs_raw_type := t.node_type(lhs_id)
114 rhs_raw_type := t.node_type(rhs_id)
115 lhs_is_array_ptr := t.equality_type_is_array_pointer(lhs_raw_type)
116 rhs_is_array_ptr := t.equality_type_is_array_pointer(rhs_raw_type)
117 if lhs_is_array_ptr && rhs_is_array_ptr {
118 return none
119 }
120 mut lhs_type := t.membership_container_type(lhs_raw_type)
121 mut rhs_type := t.membership_container_type(rhs_raw_type)
122 lhs_is_fixed := t.is_fixed_array_type(lhs_type)
123 rhs_is_fixed := t.is_fixed_array_type(rhs_type)
124 if lhs_is_fixed || rhs_is_fixed {
125 if !lhs_is_fixed || !rhs_is_fixed {
126 return none
127 }
128 fixed_type := t.resolved_fixed_array_canonical_type(lhs_type)
129 new_lhs := t.transform_expr_for_type(lhs_id, fixed_type)
130 new_rhs := t.transform_expr_for_type(rhs_id, fixed_type)
131 eq_call := t.make_membership_eq_expr(new_lhs, new_rhs, fixed_type)
132 if node.op == .ne {
133 return t.make_prefix(.not, eq_call)
134 }
135 return eq_call
136 }
137 if !(lhs_type.starts_with('[]') || lhs_type == 'array') || !(rhs_type.starts_with('[]')
138 || rhs_type == 'array') {
139 return none
140 }
141 mut elem_type := ''
142 if lhs_type.starts_with('[]') {
143 elem_type = lhs_type[2..]
144 } else if rhs_type.starts_with('[]') {
145 elem_type = rhs_type[2..]
146 }
147 if elem_type.len == 0 {
148 elem_type = 'int'
149 }
150 mut new_lhs := t.transform_expr(lhs_id)
151 mut new_rhs := t.transform_expr(rhs_id)
152 new_lhs_type := t.membership_container_type(t.node_type(new_lhs))
153 new_rhs_type := t.membership_container_type(t.node_type(new_rhs))
154 if new_lhs_type.starts_with('[]') {
155 elem_type = new_lhs_type[2..]
156 lhs_type = new_lhs_type
157 } else if new_rhs_type.starts_with('[]') {
158 elem_type = new_rhs_type[2..]
159 rhs_type = new_rhs_type
160 }
161 if lhs_is_array_ptr {
162 new_lhs = t.make_prefix(.mul, new_lhs)
163 }
164 if rhs_is_array_ptr {
165 new_rhs = t.make_prefix(.mul, new_rhs)
166 }
167 eq_call := if t.array_elem_needs_element_eq(elem_type) {
168 t.make_array_elementwise_eq_call(new_lhs, new_rhs, elem_type, lhs_type, rhs_type, node)
169 } else if elem_type.starts_with('[]') {
170 t.make_call_typed('array_eq_array', arr3(new_lhs, new_rhs,
171 t.make_int_literal(array_nested_eq_depth(lhs_type))), 'bool')
172 } else if elem_type == 'string' {
173 t.make_call_typed('array_eq_string', arr2(new_lhs, new_rhs), 'bool')
174 } else {
175 t.make_call_typed('array_eq_raw', arr3(new_lhs, new_rhs, t.make_sizeof_type(elem_type)),
176 'bool')
177 }
178 if node.op == .ne {
179 return t.make_prefix(.not, eq_call)
180 }
181 return eq_call
182}
183
184// transform_infix_map_ops transforms transform infix map ops data for transform.
185fn (mut t Transformer) transform_infix_map_ops(_id flat.NodeId, node flat.Node) ?flat.NodeId {
186 if node.children_count < 2 || node.op !in [.eq, .ne] {
187 return none
188 }
189 lhs_id := t.a.children[node.children_start]
190 rhs_id := t.a.children[node.children_start + 1]
191 lhs_type := t.map_comparison_expr_type(lhs_id)
192 rhs_type := t.map_comparison_expr_type(rhs_id)
193 if t.equality_type_is_map_pointer(lhs_type) || t.equality_type_is_map_pointer(rhs_type) {
194 return none
195 }
196 mut lhs_map_type := t.clean_map_type(lhs_type)
197 mut rhs_map_type := t.clean_map_type(rhs_type)
198 if !lhs_map_type.starts_with('map[') && rhs_map_type.starts_with('map[')
199 && t.is_empty_map_init(lhs_id) {
200 lhs_map_type = rhs_map_type
201 }
202 if !rhs_map_type.starts_with('map[') && lhs_map_type.starts_with('map[')
203 && t.is_empty_map_init(rhs_id) {
204 rhs_map_type = lhs_map_type
205 }
206 if !lhs_map_type.starts_with('map[') || !rhs_map_type.starts_with('map[') {
207 return none
208 }
209 map_type := lhs_map_type
210 mut new_lhs := t.transform_expr_for_type(lhs_id, map_type)
211 mut new_rhs := t.transform_expr_for_type(rhs_id, map_type)
212 if lhs_type.starts_with('&') {
213 new_lhs = t.make_prefix(.mul, new_lhs)
214 }
215 if rhs_type.starts_with('&') {
216 new_rhs = t.make_prefix(.mul, new_rhs)
217 }
218 _, value_type := t.map_type_parts(map_type)
219 eq_call := if value_type.len > 0 && t.map_value_needs_element_eq(value_type) {
220 t.make_map_elementwise_eq_call(new_lhs, new_rhs, map_type, node)
221 } else {
222 t.make_call_typed('v3_map_map_eq', arr2(new_lhs, new_rhs), 'bool')
223 }
224 if node.op == .ne {
225 return t.make_prefix(.not, eq_call)
226 }
227 return eq_call
228}
229
230fn (mut t Transformer) map_comparison_expr_type(id flat.NodeId) string {
231 if int(id) < 0 {
232 return ''
233 }
234 node := t.a.nodes[int(id)]
235 if node.kind == .call {
236 concrete := t.concrete_generic_call_return_type(id, node)
237 if concrete.len > 0 && t.clean_map_type(concrete).starts_with('map[') {
238 return concrete
239 }
240 }
241 if !isnil(t.tc) {
242 if typ := t.tc.expr_type(id) {
243 name := typ.name()
244 if name.len > 0 && t.clean_map_type(name).starts_with('map[') {
245 return name
246 }
247 }
248 }
249 mut typ := t.node_type(id)
250 if typ.len == 0 && node.kind == .map_init {
251 typ = if node.value.len > 0 { node.value } else { node.typ }
252 }
253 return typ
254}
255
256fn (t &Transformer) is_empty_map_init(id flat.NodeId) bool {
257 if int(id) < 0 {
258 return false
259 }
260 node := t.a.nodes[int(id)]
261 return node.kind == .map_init && node.children_count == 0
262}
263
264// transform_infix_struct_ops transforms transform infix struct ops data for transform.
265fn (mut t Transformer) transform_infix_struct_ops(_id flat.NodeId, node flat.Node) ?flat.NodeId {
266 if node.children_count < 2 {
267 return none
268 }
269 lhs_id := t.a.children[node.children_start]
270 mut lhs_type := t.node_type(lhs_id)
271 checker_lhs_type := t.checker_node_type(lhs_id)
272 lhs_key := t.expr_key(lhs_id)
273 mut has_smartcast := false
274 if lhs_key.len > 0 {
275 if sc := t.find_smartcast(lhs_key) {
276 has_smartcast = true
277 lhs_type = t.smartcast_target_type(sc)
278 }
279 }
280 if lhs_type.len == 0 {
281 lhs_type = checker_lhs_type
282 }
283 if lhs_type.starts_with('&') {
284 return none
285 }
286 mut struct_type := t.struct_lookup_name(lhs_type)
287 if struct_type.len == 0 {
288 // Generic-struct instance operand (e.g. `Vec4[f32]`): keep the instance type
289 // so the operator lowers to the monomorphized method (`vec__Vec4_f32__plus`).
290 struct_type = t.generic_struct_instance_name(lhs_type)
291 }
292 mut is_alias_operator := false
293 if struct_type.len == 0 {
294 if _ := t.struct_operator_call_info(lhs_type, node.op) {
295 struct_type = lhs_type
296 is_alias_operator = true
297 }
298 }
299 if struct_type.len == 0 {
300 return none
301 }
302 // Skip the checker/transformer agreement guard for generic-struct instances:
303 // they resolve reliably, and an alias name (`SimdFloat4`) vs the resolved form
304 // (`vec.Vec4[f32]`) would otherwise spuriously fail the comparison.
305 if checker_lhs_type.len > 0 && !has_smartcast && !struct_type.contains('[')
306 && !is_alias_operator {
307 checker_struct_type := t.struct_lookup_name(checker_lhs_type)
308 if checker_struct_type.len > 0 && checker_struct_type != struct_type {
309 return none
310 }
311 if checker_struct_type.len == 0 && checker_lhs_type != lhs_type {
312 return none
313 }
314 }
315 if call_info := t.struct_operator_call_info(struct_type, node.op) {
316 if t.is_disabled_fn_name(call_info.name) {
317 ret_type := t.struct_operator_return_type(call_info.name)
318 if ret_type.len == 0 || ret_type == 'void' {
319 return t.make_empty()
320 }
321 return t.zero_value_for_type(ret_type)
322 }
323 lhs := t.transform_expr(lhs_id)
324 rhs := t.transform_expr(t.a.children[node.children_start + 1])
325 mut call_lhs := lhs
326 mut call_rhs := rhs
327 if call_info.reverse {
328 call_lhs = t.stable_transformed_expr_for_reuse(lhs, lhs_type, 'op_lhs')
329 call_rhs = t.stable_transformed_expr_for_reuse(rhs, t.node_type(t.a.children[
330 node.children_start + 1]), 'op_rhs')
331 }
332 args := if call_info.reverse {
333 arr2(call_rhs, call_lhs)
334 } else {
335 arr2(call_lhs, call_rhs)
336 }
337 t.mark_fn_used_name(call_info.name)
338 call := t.make_call_typed(call_info.name, args, node.typ)
339 if call_info.negate {
340 return t.make_prefix(.not, call)
341 }
342 return call
343 }
344 if node.op != .eq && node.op != .ne {
345 return none
346 }
347 if !t.has_struct_operator_fn(struct_type, '==') {
348 lhs := t.stable_expr_for_reuse(lhs_id)
349 rhs := t.stable_expr_for_reuse(t.a.children[node.children_start + 1])
350 if field_eq := t.make_struct_field_eq_expr(lhs, rhs, struct_type) {
351 if node.op == .ne {
352 return t.make_prefix(.not, field_eq)
353 }
354 return field_eq
355 }
356 cmp := t.make_call_typed('memcmp', arr3(t.make_prefix(.amp, lhs), t.make_prefix(.amp, rhs),
357 t.make_sizeof_type(struct_type)), 'int')
358 return t.make_infix(node.op, cmp, t.make_int_literal(0))
359 }
360 if eq_fn := t.struct_operator_fn_name(struct_type, '==') {
361 if t.is_disabled_fn_name(eq_fn) {
362 return t.make_bool_literal(node.op == .ne)
363 }
364 lhs := t.transform_expr(lhs_id)
365 rhs := t.transform_expr(t.a.children[node.children_start + 1])
366 t.mark_fn_used_name(eq_fn)
367 eq_call := t.make_call_typed(eq_fn, arr2(lhs, rhs), 'bool')
368 if node.op == .ne {
369 return t.make_prefix(.not, eq_call)
370 }
371 return eq_call
372 }
373 return none
374}
375
376fn (mut t Transformer) transform_transformed_struct_eq(node flat.Node, lhs flat.NodeId, rhs flat.NodeId) ?flat.NodeId {
377 mut lhs_type := t.node_type(lhs)
378 if lhs_type.starts_with('&') {
379 lhs_type = lhs_type[1..]
380 }
381 mut rhs_type := t.node_type(rhs)
382 if rhs_type.starts_with('&') {
383 rhs_type = rhs_type[1..]
384 }
385 lhs_struct_type := t.struct_lookup_name(lhs_type)
386 rhs_struct_type := t.struct_lookup_name(rhs_type)
387 if lhs_struct_type.len == 0 || rhs_struct_type.len == 0 {
388 return none
389 }
390 struct_type := lhs_struct_type
391 if lhs_struct_type != rhs_struct_type
392 && lhs_struct_type.all_after_last('.') != rhs_struct_type.all_after_last('.') {
393 return none
394 }
395 if call_info := t.struct_operator_call_info(struct_type, node.op) {
396 if t.is_disabled_fn_name(call_info.name) {
397 ret_type := t.struct_operator_return_type(call_info.name)
398 if ret_type.len == 0 || ret_type == 'void' {
399 return t.make_empty()
400 }
401 return t.zero_value_for_type(ret_type)
402 }
403 call_lhs := t.stable_transformed_expr_for_reuse(lhs, lhs_type, 'op_lhs')
404 call_rhs := t.stable_transformed_expr_for_reuse(rhs, rhs_type, 'op_rhs')
405 args := if call_info.reverse {
406 arr2(call_rhs, call_lhs)
407 } else {
408 arr2(call_lhs, call_rhs)
409 }
410 t.mark_fn_used_name(call_info.name)
411 call := t.make_call_typed(call_info.name, args, node.typ)
412 if call_info.negate {
413 return t.make_prefix(.not, call)
414 }
415 return call
416 }
417 if node.op != .eq && node.op != .ne {
418 return none
419 }
420 if eq_fn := t.struct_operator_fn_name(struct_type, '==') {
421 if t.is_disabled_fn_name(eq_fn) {
422 return t.make_bool_literal(node.op == .ne)
423 }
424 call_lhs := t.stable_transformed_expr_for_reuse(lhs, lhs_type, 'eq_lhs')
425 call_rhs := t.stable_transformed_expr_for_reuse(rhs, rhs_type, 'eq_rhs')
426 t.mark_fn_used_name(eq_fn)
427 eq_call := t.make_call_typed(eq_fn, arr2(call_lhs, call_rhs), 'bool')
428 if node.op == .ne {
429 return t.make_prefix(.not, eq_call)
430 }
431 return eq_call
432 }
433 cmp_lhs := t.stable_transformed_expr_for_reuse(lhs, lhs_type, 'eq_lhs')
434 cmp_rhs := t.stable_transformed_expr_for_reuse(rhs, rhs_type, 'eq_rhs')
435 cmp := t.make_call_typed('memcmp', arr3(t.make_prefix(.amp, cmp_lhs), t.make_prefix(.amp,
436 cmp_rhs), t.make_sizeof_type(struct_type)), 'int')
437 return t.make_infix(node.op, cmp, t.make_int_literal(0))
438}
439
440fn (t &Transformer) checker_node_type(id flat.NodeId) string {
441 if isnil(t.tc) || int(id) < 0 {
442 return ''
443 }
444 typ := t.tc.expr_type(id) or { t.tc.resolve_type(id) }
445 name := typ.name()
446 if name.len == 0 || name == 'void' {
447 return ''
448 }
449 return t.normalize_type_alias(name)
450}
451
452// StructOperatorCallInfo stores struct operator call info metadata used by transform.
453struct StructOperatorCallInfo {
454 name string
455 reverse bool
456 negate bool
457}
458
459// struct_operator_call_info supports struct operator call info handling for Transformer.
460fn (t &Transformer) struct_operator_call_info(struct_type string, op flat.Op) ?StructOperatorCallInfo {
461 if op_name := struct_operator_symbol(op) {
462 if method_name := t.struct_operator_fn_name(struct_type, op_name) {
463 return StructOperatorCallInfo{
464 name: method_name
465 }
466 }
467 }
468 match op {
469 .gt {
470 if method_name := t.struct_operator_fn_name(struct_type, '<') {
471 return StructOperatorCallInfo{
472 name: method_name
473 reverse: true
474 }
475 }
476 }
477 .ge {
478 if method_name := t.struct_operator_fn_name(struct_type, '<') {
479 return StructOperatorCallInfo{
480 name: method_name
481 negate: true
482 }
483 }
484 }
485 .le {
486 if method_name := t.struct_operator_fn_name(struct_type, '<') {
487 return StructOperatorCallInfo{
488 name: method_name
489 reverse: true
490 negate: true
491 }
492 }
493 }
494 .ne {
495 if method_name := t.struct_operator_fn_name(struct_type, '==') {
496 return StructOperatorCallInfo{
497 name: method_name
498 negate: true
499 }
500 }
501 }
502 else {}
503 }
504
505 return none
506}
507
508// struct_operator_symbol supports struct operator symbol handling for transform.
509fn struct_operator_symbol(op flat.Op) ?string {
510 match op {
511 .plus { return '+' }
512 .minus { return '-' }
513 .mul { return '*' }
514 .div { return '/' }
515 .mod { return '%' }
516 .eq { return '==' }
517 .ne { return '!=' }
518 .lt { return '<' }
519 .gt { return '>' }
520 .le { return '<=' }
521 .ge { return '>=' }
522 else {}
523 }
524
525 return none
526}
527
528// struct_operator_fn_name supports struct operator fn name handling for Transformer.
529fn (t &Transformer) struct_operator_fn_name(struct_type string, op_name string) ?string {
530 require_used := op_name in ['==', '!=']
531 for receiver in t.operator_receiver_candidates(struct_type) {
532 method_name := '${receiver}.${op_name}'
533 if t.is_known_operator_fn_name(method_name, require_used) {
534 return method_name
535 }
536 cmethod_name := c_name(method_name)
537 if t.is_known_operator_fn_name(cmethod_name, require_used) {
538 return cmethod_name
539 }
540 if name := t.generic_struct_operator_fn_name(receiver, op_name) {
541 return name
542 }
543 }
544 return none
545}
546
547fn (t &Transformer) operator_receiver_candidates(struct_type string) []string {
548 mut candidates := []string{cap: 2}
549 if struct_type.len == 0 {
550 return candidates
551 }
552 candidates << struct_type
553 if !struct_type.contains('.') && t.cur_module.len > 0 && t.cur_module != 'main'
554 && t.cur_module != 'builtin' {
555 candidates << '${t.cur_module}.${struct_type}'
556 }
557 return candidates
558}
559
560// generic_struct_instance_name returns the resolved generic-struct instance type
561// for `type_name` (its base is a known generic struct), else ''. A type alias to a
562// generic instance (`SimdFloat4` -> `vec.Vec4[f32]`) is normalized first, so an
563// operand typed by its alias still lowers to the monomorphized operator.
564fn (t &Transformer) generic_struct_instance_name(type_name string) string {
565 if isnil(t.tc) {
566 return ''
567 }
568 normalized := t.normalize_type_alias(type_name)
569 base, _, ok := generic_app_parts(normalized)
570 if !ok {
571 return ''
572 }
573 if base in t.tc.struct_generic_params {
574 return normalized
575 }
576 return ''
577}
578
579// generic_struct_operator_fn_name handles operator overloads on a generic-struct
580// instance (e.g. `Vec4[f32] + Vec4[f32]`). The operator is declared on the generic
581// form (`Vec4[T].+`) and specialized to `vec__Vec4_f32__plus` by the monomorphizer
582// (which runs after this lowering). When the generic operator exists, anticipate
583// the specialized C name so the infix lowers to that call.
584fn (t &Transformer) generic_struct_operator_fn_name(struct_type string, op_name string) ?string {
585 if isnil(t.tc) {
586 return none
587 }
588 base, _, ok := generic_app_parts(struct_type)
589 if !ok {
590 return none
591 }
592 params := t.tc.struct_generic_params[base] or { return none }
593 if params.len == 0 {
594 return none
595 }
596 generic_key := '${base}[${params.join(', ')}].${op_name}'
597 if generic_key in t.tc.fn_ret_types || generic_key in t.tc.fn_param_types {
598 return c_name('${struct_type}.${op_name}')
599 }
600 return none
601}
602
603fn (t &Transformer) is_known_operator_fn_name(name string, require_used bool) bool {
604 if !t.is_known_fn_name(name) {
605 return false
606 }
607 if !require_used || t.used_fns.len == 0 {
608 return true
609 }
610 return name in t.used_fns || c_name(name) in t.used_fns
611}
612
613// has_struct_operator_fn reports whether has struct operator fn applies in transform.
614fn (t &Transformer) has_struct_operator_fn(struct_type string, op_name string) bool {
615 if _ := t.struct_operator_fn_name(struct_type, op_name) {
616 return true
617 }
618 return false
619}
620
621// struct_operator_return_type supports struct operator return type handling for Transformer.
622fn (t &Transformer) struct_operator_return_type(fn_name string) string {
623 if ret := t.fn_ret_types[fn_name] {
624 return t.normalize_type_alias(ret)
625 }
626 if !isnil(t.tc) {
627 if ret := t.tc.fn_ret_types[fn_name] {
628 return t.normalize_type_alias(ret.name())
629 }
630 }
631 return ''
632}
633
634// infix_struct_operator_result_type
635// supports helper handling in transform.
636fn (t &Transformer) infix_struct_operator_result_type(node flat.Node, lhs_type_in string) string {
637 if node.children_count < 2 {
638 return ''
639 }
640 // `lhs_type_in` is the already-resolved type of the left operand; the caller passes
641 // it so we don't re-resolve the operand (operator-overload checks run on every infix).
642 lhs_type := if lhs_type_in.starts_with('&') { lhs_type_in[1..] } else { lhs_type_in }
643 struct_type := t.struct_lookup_name(lhs_type)
644 if struct_type.len == 0 {
645 // Generic-struct instance (`Vec4[f32]`): the specialized operator method is
646 // not registered until monomorphization, so derive the result from the
647 // generic operator's (substituted) return type.
648 if rt := t.generic_struct_operator_return_type(t.generic_struct_instance_name(lhs_type),
649 node.op)
650 {
651 return rt
652 }
653 return ''
654 }
655 if call_info := t.struct_operator_call_info(struct_type, node.op) {
656 ret_type := t.struct_operator_return_type(call_info.name)
657 if ret_type.len > 0 {
658 return ret_type
659 }
660 }
661 return ''
662}
663
664// generic_struct_operator_return_type returns the result type of an operator on a
665// generic-struct instance (`Vec4[f32]`), derived from the generic operator's
666// declared return type (`Vec4[T].- -> Vec4[T]`) with the instance's type arguments
667// substituted (`-> Vec4[f32]`), qualified with the struct's module so the outer
668// expression resolves to the monomorphized operator.
669fn (t &Transformer) generic_struct_operator_return_type(struct_type string, op flat.Op) ?string {
670 if struct_type.len == 0 || isnil(t.tc) {
671 return none
672 }
673 op_name := struct_operator_symbol(op) or { return none }
674 full_base, args, ok := generic_app_parts(struct_type)
675 if !ok {
676 return none
677 }
678 params := t.tc.struct_generic_params[full_base] or { return none }
679 generic_key := '${full_base}[${params.join(', ')}].${op_name}'
680 mut ret := ''
681 if r := t.fn_ret_types[generic_key] {
682 ret = r
683 } else if r := t.tc.fn_ret_types[generic_key] {
684 ret = r.name()
685 } else {
686 return none
687 }
688 substituted := substitute_generic_type_text_with_params(ret, args, params)
689 // Qualify a returned instance of the same struct family with the struct's module.
690 rbase, _, rok := generic_app_parts(substituted)
691 if rok && full_base.contains('.') && !rbase.contains('.')
692 && rbase == full_base.all_after_last('.') {
693 return '${full_base.all_before_last('.')}.${substituted}'
694 }
695 return substituted
696}
697
698// transform_infix_sum_ops transforms transform infix sum ops data for transform.
699fn (mut t Transformer) transform_infix_sum_ops(_id flat.NodeId, node flat.Node) ?flat.NodeId {
700 if node.op !in [.eq, .ne] || node.children_count < 2 {
701 return none
702 }
703 lhs_id := t.a.children[node.children_start]
704 rhs_id := t.a.children[node.children_start + 1]
705 lhs_raw_type := t.node_type(lhs_id)
706 rhs_raw_type := t.node_type(rhs_id)
707 mut lhs_type := lhs_raw_type
708 mut rhs_type := rhs_raw_type
709 lhs_is_ptr := lhs_type.starts_with('&')
710 rhs_is_ptr := rhs_type.starts_with('&')
711 if lhs_is_ptr {
712 lhs_type = lhs_type[1..]
713 }
714 if rhs_is_ptr {
715 rhs_type = rhs_type[1..]
716 }
717 if !t.is_sum_type_name(lhs_type) || !t.is_sum_type_name(rhs_type) {
718 return none
719 }
720 sum_type := if t.is_sum_type_name(lhs_type) {
721 t.resolve_sum_name(lhs_type)
722 } else if t.is_sum_type_name(rhs_type) {
723 t.resolve_sum_name(rhs_type)
724 } else {
725 ''
726 }
727 if sum_type.len == 0 {
728 return none
729 }
730 mut lhs := t.stable_expr_for_reuse(lhs_id)
731 mut rhs := t.stable_expr_for_reuse(rhs_id)
732 if lhs_is_ptr {
733 lhs = t.make_prefix(.mul, lhs)
734 t.a.nodes[int(lhs)].typ = lhs_type
735 }
736 if rhs_is_ptr {
737 rhs = t.make_prefix(.mul, rhs)
738 t.a.nodes[int(rhs)].typ = rhs_type
739 }
740 tag_eq := t.make_infix(.eq, t.make_selector(lhs, 'typ', 'int'), t.make_selector(rhs, 'typ',
741 'int'))
742 cmp := t.make_call_typed('memcmp', arr3(t.make_prefix(.amp, lhs), t.make_prefix(.amp, rhs),
743 t.make_sizeof_type(sum_type)), 'int')
744 value_eq := t.make_infix(.eq, cmp, t.make_int_literal(0))
745 eq := t.make_infix(.logical_and, tag_eq, value_eq)
746 if node.op == .ne {
747 return t.make_prefix(.not, t.make_paren(eq))
748 }
749 return eq
750}
751
752// transform_infix_optional_none_ops supports transform_infix_optional_none_ops handling.
753fn (mut t Transformer) transform_infix_optional_none_ops(_id flat.NodeId, node flat.Node) ?flat.NodeId {
754 if node.op !in [.eq, .ne] || node.children_count < 2 {
755 return none
756 }
757 lhs_id := t.a.children[node.children_start]
758 rhs_id := t.a.children[node.children_start + 1]
759 lhs := t.a.nodes[int(lhs_id)]
760 rhs := t.a.nodes[int(rhs_id)]
761 mut opt_id := flat.empty_node
762 if lhs.kind == .none_expr {
763 opt_id = rhs_id
764 } else if rhs.kind == .none_expr {
765 opt_id = lhs_id
766 } else {
767 return none
768 }
769 opt_type := t.node_type(opt_id)
770 if !t.is_optional_type_name(opt_type) {
771 return none
772 }
773 ok := t.make_selector(t.transform_expr(opt_id), 'ok', 'bool')
774 if node.op == .eq {
775 return t.make_prefix(.not, ok)
776 }
777 return ok
778}
779
780// struct_lookup_name supports struct lookup name handling for Transformer.
781fn (t &Transformer) struct_lookup_name(type_name string) string {
782 if type_name.len == 0 {
783 return ''
784 }
785 // Primitives, arrays and maps are never struct names. Bail before the qualified-name
786 // concatenation below — this runs for every infix operand, so the saved allocation
787 // matters. (Behaviour is unchanged: these always resolved to '' anyway.)
788 first := type_name[0]
789 if first == `[`
790 || (first >= `a` && first <= `z` && types.is_builtin_type_name(type_name))
791 || type_name.starts_with('map[') {
792 return ''
793 }
794 if type_name.contains('.') {
795 if type_name in t.structs {
796 return type_name
797 }
798 short_type := type_name.all_after_last('.')
799 type_mod := type_name.all_before_last('.')
800 if info := t.structs[short_type] {
801 if info.module == type_mod {
802 return short_type
803 }
804 }
805 if info := t.structs[type_name] {
806 if info.module == type_mod {
807 return type_name
808 }
809 }
810 return ''
811 }
812 if type_name in t.structs {
813 if info := t.structs[type_name] {
814 if info.module.len == 0 || info.module == t.cur_module {
815 return type_name
816 }
817 }
818 }
819 if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
820 qtype := '${t.cur_module}.${type_name}'
821 if qtype in t.structs {
822 return qtype
823 }
824 }
825 return ''
826}
827
828// transform_in_expr transforms transform in expr data for transform.
829fn (mut t Transformer) transform_in_expr(id flat.NodeId, node flat.Node) flat.NodeId {
830 if node.children_count < 2 {
831 return id
832 }
833 lhs_id := t.a.children[node.children_start]
834 rhs_id := t.a.children[node.children_start + 1]
835 rhs := t.a.nodes[int(rhs_id)]
836
837 is_not_in := node.value == '!in'
838
839 mut result := id
840 if rhs.kind == .range {
841 // x in low..high -> x >= low && x < high
842 if rhs.children_count >= 2 {
843 new_lhs := t.stable_expr_for_reuse(lhs_id)
844 low_id := t.a.children[rhs.children_start]
845 high_id := t.a.children[rhs.children_start + 1]
846 new_low := t.transform_expr(low_id)
847 new_high := t.transform_expr(high_id)
848
849 ge_cmp := t.make_infix(.ge, new_lhs, new_low)
850 lt_cmp := t.make_infix(.lt, new_lhs, new_high)
851 result = t.make_infix(.logical_and, ge_cmp, lt_cmp)
852 }
853 } else if rhs.kind == .array_literal {
854 if type_checks := t.lower_type_pattern_membership(lhs_id, rhs, is_not_in) {
855 return type_checks
856 }
857 // x in [a, b, c] -> x == a || x == b || x == c
858 if rhs.children_count == 0 {
859 result = t.make_bool_literal(false)
860 } else {
861 new_lhs := t.stable_expr_for_reuse(lhs_id)
862 is_str := t.is_string_type(lhs_id)
863 mut or_chain := flat.empty_node
864 for i in 0 .. rhs.children_count {
865 elem_id := t.a.children[rhs.children_start + i]
866 new_elem := t.transform_expr(elem_id)
867 eq_cmp := if is_str {
868 t.make_call('string__eq', arr2(new_lhs, new_elem))
869 } else {
870 t.make_infix(.eq, new_lhs, new_elem)
871 }
872 if int(or_chain) < 0 {
873 or_chain = eq_cmp
874 } else {
875 or_chain = t.make_infix(.logical_or, or_chain, eq_cmp)
876 }
877 }
878 result = or_chain
879 }
880 } else {
881 rhs_type := t.node_type(rhs_id)
882 clean_rhs_type := t.membership_container_type(rhs_type)
883 rhs_is_ptr_array := t.membership_container_is_pointer_array(rhs_type)
884 if clean_rhs_type.starts_with('[]') || clean_rhs_type == 'array' {
885 if lowered := t.lower_array_membership_expr(rhs_id, lhs_id, rhs_type, false, node) {
886 result = lowered
887 } else {
888 // dynamic array membership -> array_contains_int/string(arr, val)
889 new_lhs := t.transform_expr(lhs_id)
890 mut new_rhs := t.transform_expr(rhs_id)
891 if rhs_is_ptr_array {
892 new_rhs = t.make_prefix(.mul, new_rhs)
893 }
894 mut elem := if clean_rhs_type.starts_with('[]') { clean_rhs_type[2..] } else { '' }
895 if elem.len == 0 {
896 elem = t.node_type(lhs_id)
897 }
898 fn_name := array_contains_fn_name(elem)
899 result = t.make_call_typed(fn_name, arr2(new_rhs, new_lhs), 'bool')
900 }
901 } else if rhs.kind in [.ident, .selector] && (rhs_type.len == 0 || rhs_type == 'unknown') {
902 new_lhs := t.transform_expr(lhs_id)
903 new_rhs := t.transform_expr(rhs_id)
904 mut elem := t.node_type(lhs_id)
905 lhs := t.a.nodes[int(lhs_id)]
906 if elem.len == 0 && lhs.kind == .selector {
907 elem = t.selector_field_type(lhs)
908 }
909 if elem.len == 0 && rhs.kind == .selector {
910 elem = t.selector_array_elem_type(rhs)
911 }
912 if elem.len > 0 {
913 fn_name := array_contains_fn_name(elem)
914 result = t.make_call_typed(fn_name, arr2(new_rhs, new_lhs), 'bool')
915 }
916 } else if t.is_fixed_array_type(clean_rhs_type) {
917 // fixed array membership -> fixed_array_contains_int/string(arr, len, val)
918 new_lhs := t.transform_expr(lhs_id)
919 new_rhs := t.transform_expr(rhs_id)
920 elem := fixed_array_elem_type(clean_rhs_type)
921 fn_name := fixed_array_contains_fn_name(elem)
922 len_expr := t.make_fixed_array_len_expr(clean_rhs_type)
923 result = t.make_call_typed(fn_name, arr3(new_rhs, len_expr, new_lhs), 'bool')
924 } else if clean_rhs_type == 'string' {
925 new_lhs := t.transform_expr(lhs_id)
926 new_rhs := t.transform_expr(rhs_id)
927 fn_name := if t.node_type(lhs_id) in ['u8', 'byte'] {
928 'string__contains_u8'
929 } else {
930 'string__contains'
931 }
932 result = t.make_call_typed(fn_name, arr2(new_rhs, new_lhs), 'bool')
933 } else if clean_rhs_type.starts_with('map[') || clean_rhs_type == 'map' {
934 if lowered := t.lower_map_membership_expr(rhs_id, lhs_id, rhs_type) {
935 result = lowered
936 }
937 } else {
938 // Unknown containment is kept as in_expr so the backend can reject or
939 // handle genuinely unresolved cases.
940 new_lhs := t.transform_expr(lhs_id)
941 new_rhs := t.transform_expr(rhs_id)
942 in_start := t.a.children.len
943 t.a.children << new_lhs
944 t.a.children << new_rhs
945 result = t.a.add_node(flat.Node{
946 kind: .in_expr
947 op: node.op
948 children_start: in_start
949 children_count: 2
950 pos: node.pos
951 value: 'in'
952 typ: node.typ
953 })
954 }
955 }
956
957 if is_not_in && result != id {
958 start := t.a.children.len
959 t.a.children << t.make_paren(result)
960 return t.a.add_node(flat.Node{
961 kind: .prefix
962 op: .not
963 children_start: start
964 children_count: 1
965 })
966 }
967 return result
968}
969
970// lower_type_pattern_membership builds lower type pattern membership data for transform.
971fn (mut t Transformer) lower_type_pattern_membership(lhs_id flat.NodeId, rhs flat.Node, is_not_in bool) ?flat.NodeId {
972 if rhs.children_count == 0 {
973 return none
974 }
975 mut patterns := []string{cap: int(rhs.children_count)}
976 mut sum_name := t.trim_pointer_type(t.original_expr_type(lhs_id))
977 if !t.is_sum_type_name(sum_name) {
978 sum_name = t.trim_pointer_type(t.node_type(lhs_id))
979 }
980 for i in 0 .. rhs.children_count {
981 elem_id := t.a.child(&rhs, i)
982 pattern := t.type_pattern_name(elem_id)
983 if pattern.len == 0 {
984 return none
985 }
986 if !t.is_sum_type_name(sum_name) {
987 pattern_sum := t.find_sum_type_for_variant(pattern)
988 if pattern_sum.len == 0 {
989 return none
990 }
991 sum_name = pattern_sum
992 } else if t.sum_variant_path(sum_name, pattern).len == 0 {
993 return none
994 }
995 patterns << pattern
996 }
997 if !t.is_sum_type_name(sum_name) {
998 return none
999 }
1000 lhs_type := t.original_expr_type(lhs_id)
1001 base := t.stable_expr_for_reuse(lhs_id)
1002 mut chain := flat.empty_node
1003 for pattern in patterns {
1004 cmp := t.make_sum_type_pattern_check(base, lhs_type, sum_name, pattern) or { return none }
1005 if int(chain) < 0 {
1006 chain = cmp
1007 } else {
1008 chain = t.make_infix(.logical_or, chain, cmp)
1009 }
1010 }
1011 if is_not_in {
1012 return t.make_prefix(.not, t.make_paren(chain))
1013 }
1014 return chain
1015}
1016
1017// type_pattern_name returns type pattern name data for Transformer.
1018fn (t &Transformer) type_pattern_name(id flat.NodeId) string {
1019 if int(id) < 0 {
1020 return ''
1021 }
1022 node := t.a.nodes[int(id)]
1023 match node.kind {
1024 .ident {
1025 return node.value
1026 }
1027 .selector {
1028 if node.children_count == 0 {
1029 return node.value
1030 }
1031 base := t.type_pattern_name(t.a.child(&node, 0))
1032 if base.len == 0 {
1033 return node.value
1034 }
1035 return '${base}.${node.value}'
1036 }
1037 .array_init {
1038 if node.value.len == 0 {
1039 return ''
1040 }
1041 return '[]${node.value}'
1042 }
1043 .array_literal {
1044 if node.value.len == 0 {
1045 return ''
1046 }
1047 return '[${node.children_count}]${node.value}'
1048 }
1049 else {
1050 return ''
1051 }
1052 }
1053}
1054
1055// lower_array_membership_expr builds lower array membership expr data for transform.
1056fn (mut t Transformer) lower_array_membership_expr(base_id flat.NodeId, needle_id flat.NodeId, base_type string, receiver_first bool, src flat.Node) ?flat.NodeId {
1057 clean_base_type := t.membership_container_type(base_type)
1058 if !clean_base_type.starts_with('[]') && clean_base_type != 'array' {
1059 return none
1060 }
1061 mut elem_type := if clean_base_type.starts_with('[]') { clean_base_type[2..] } else { '' }
1062 if elem_type.len == 0 {
1063 elem_type = t.membership_container_type(t.node_type(needle_id))
1064 }
1065 if elem_type.len == 0 {
1066 return none
1067 }
1068 mut base := flat.empty_node
1069 mut needle := flat.empty_node
1070 if receiver_first {
1071 base = t.stable_array_expr_for_membership(base_id, base_type, clean_base_type)
1072 needle = t.stable_expr_for_reuse(needle_id)
1073 } else {
1074 needle = t.stable_expr_for_reuse(needle_id)
1075 base = t.stable_array_expr_for_membership(base_id, base_type, clean_base_type)
1076 }
1077 mut prefix := []flat.NodeId{}
1078 t.drain_pending(mut prefix)
1079 result_name := t.new_temp('contains')
1080 idx_name := t.new_temp('contains_idx')
1081 prefix << t.make_decl_assign_typed(result_name, t.make_bool_literal(false), 'bool')
1082 init := t.make_decl_assign_typed(idx_name, t.make_int_literal(0), 'int')
1083 cond := t.make_infix(.lt, t.make_ident(idx_name), t.make_selector(base, 'len', 'int'))
1084 post := t.make_expr_stmt(t.make_postfix(t.make_ident(idx_name), .inc))
1085 elem_expr := t.array_get_value(base, t.make_ident(idx_name), elem_type)
1086 pending_start := t.pending_stmts.len
1087 eq_expr := t.make_membership_eq_expr(elem_expr, needle, elem_type)
1088 mut loop_body := t.pending_stmts[pending_start..].clone()
1089 t.pending_stmts = t.pending_stmts[..pending_start].clone()
1090 assign_true := t.make_assign(t.make_ident(result_name), t.make_bool_literal(true))
1091 then_block := t.make_block(arr1(assign_true))
1092 loop_body << t.make_if(eq_expr, then_block, t.make_empty())
1093 prefix << t.make_for_stmt(init, cond, post, loop_body, src)
1094 for stmt in prefix {
1095 t.pending_stmts << stmt
1096 }
1097 return t.make_ident(result_name)
1098}
1099
1100// lower_array_index_expr builds lower array index expr data for transform.
1101fn (mut t Transformer) lower_array_index_expr(base_id flat.NodeId, needle_id flat.NodeId, base_type string, receiver_first bool, src flat.Node) ?flat.NodeId {
1102 clean_base_type := t.membership_container_type(base_type)
1103 if !clean_base_type.starts_with('[]') && clean_base_type != 'array' {
1104 return none
1105 }
1106 mut elem_type := if clean_base_type.starts_with('[]') { clean_base_type[2..] } else { '' }
1107 if elem_type.len == 0 {
1108 elem_type = t.membership_container_type(t.node_type(needle_id))
1109 }
1110 if elem_type.len == 0 {
1111 return none
1112 }
1113 mut base := flat.empty_node
1114 mut needle := flat.empty_node
1115 if receiver_first {
1116 base = t.stable_array_expr_for_membership(base_id, base_type, clean_base_type)
1117 needle = t.stable_expr_for_reuse(needle_id)
1118 } else {
1119 needle = t.stable_expr_for_reuse(needle_id)
1120 base = t.stable_array_expr_for_membership(base_id, base_type, clean_base_type)
1121 }
1122 mut prefix := []flat.NodeId{}
1123 t.drain_pending(mut prefix)
1124 result_name := t.new_temp('index')
1125 idx_name := t.new_temp('index_idx')
1126 prefix << t.make_decl_assign_typed(result_name, t.make_int_literal(-1), 'int')
1127 init := t.make_decl_assign_typed(idx_name, t.make_int_literal(0), 'int')
1128 cond := t.make_infix(.lt, t.make_ident(idx_name), t.make_selector(base, 'len', 'int'))
1129 post := t.make_expr_stmt(t.make_postfix(t.make_ident(idx_name), .inc))
1130 elem_expr := t.array_get_value(base, t.make_ident(idx_name), elem_type)
1131 pending_start := t.pending_stmts.len
1132 eq_expr := t.make_membership_eq_expr(elem_expr, needle, elem_type)
1133 mut loop_body := t.pending_stmts[pending_start..].clone()
1134 t.pending_stmts = t.pending_stmts[..pending_start].clone()
1135 not_found := t.make_infix(.lt, t.make_ident(result_name), t.make_int_literal(0))
1136 found_cond := t.make_infix(.logical_and, not_found, eq_expr)
1137 assign_idx := t.make_assign(t.make_ident(result_name), t.make_ident(idx_name))
1138 loop_body << t.make_if(found_cond, t.make_block(arr1(assign_idx)), t.make_empty())
1139 prefix << t.make_for_stmt(init, cond, post, loop_body, src)
1140 for stmt in prefix {
1141 t.pending_stmts << stmt
1142 }
1143 return t.make_ident(result_name)
1144}
1145
1146// lower_array_last_index_expr builds lower array last index expr data for transform.
1147fn (mut t Transformer) lower_array_last_index_expr(base_id flat.NodeId, needle_id flat.NodeId, base_type string, receiver_first bool, src flat.Node) ?flat.NodeId {
1148 clean_base_type := t.membership_container_type(base_type)
1149 if !clean_base_type.starts_with('[]') && clean_base_type != 'array' {
1150 return none
1151 }
1152 mut elem_type := if clean_base_type.starts_with('[]') { clean_base_type[2..] } else { '' }
1153 if elem_type.len == 0 {
1154 elem_type = t.membership_container_type(t.node_type(needle_id))
1155 }
1156 if elem_type.len == 0 {
1157 return none
1158 }
1159 mut base := flat.empty_node
1160 mut needle := flat.empty_node
1161 if receiver_first {
1162 base = t.stable_array_expr_for_membership(base_id, base_type, clean_base_type)
1163 needle = t.stable_expr_for_reuse(needle_id)
1164 } else {
1165 needle = t.stable_expr_for_reuse(needle_id)
1166 base = t.stable_array_expr_for_membership(base_id, base_type, clean_base_type)
1167 }
1168 mut prefix := []flat.NodeId{}
1169 t.drain_pending(mut prefix)
1170 result_name := t.new_temp('last_index')
1171 idx_name := t.new_temp('last_index_idx')
1172 prefix << t.make_decl_assign_typed(result_name, t.make_int_literal(-1), 'int')
1173 init := t.make_decl_assign_typed(idx_name, t.make_infix(.minus, t.make_selector(base, 'len',
1174 'int'), t.make_int_literal(1)), 'int')
1175 cond := t.make_infix(.ge, t.make_ident(idx_name), t.make_int_literal(0))
1176 post := t.make_expr_stmt(t.make_postfix(t.make_ident(idx_name), .dec))
1177 elem_expr := t.array_get_value(base, t.make_ident(idx_name), elem_type)
1178 pending_start := t.pending_stmts.len
1179 eq_expr := t.make_membership_eq_expr(elem_expr, needle, elem_type)
1180 mut loop_body := t.pending_stmts[pending_start..].clone()
1181 t.pending_stmts = t.pending_stmts[..pending_start].clone()
1182 not_found := t.make_infix(.lt, t.make_ident(result_name), t.make_int_literal(0))
1183 found_cond := t.make_infix(.logical_and, not_found, eq_expr)
1184 assign_idx := t.make_assign(t.make_ident(result_name), t.make_ident(idx_name))
1185 loop_body << t.make_if(found_cond, t.make_block(arr1(assign_idx)), t.make_empty())
1186 prefix << t.make_for_stmt(init, cond, post, loop_body, src)
1187 for stmt in prefix {
1188 t.pending_stmts << stmt
1189 }
1190 return t.make_ident(result_name)
1191}
1192
1193// stable_array_expr_for_membership
1194// supports helper handling in transform.
1195fn (mut t Transformer) stable_array_expr_for_membership(id flat.NodeId, raw_type string, clean_type string) flat.NodeId {
1196 mut expr := t.transform_expr(id)
1197 if t.membership_container_is_pointer_array(raw_type) {
1198 expr = t.make_prefix(.mul, expr)
1199 }
1200 return t.stable_transformed_expr_for_reuse(expr, clean_type, 'in_arr')
1201}
1202
1203// make_membership_eq_expr builds make membership eq expr data for transform.
1204fn (mut t Transformer) make_membership_eq_expr(lhs flat.NodeId, rhs flat.NodeId, elem_type string) flat.NodeId {
1205 return t.make_membership_eq_expr_with_seen(lhs, rhs, elem_type, []string{})
1206}
1207
1208fn (mut t Transformer) make_membership_eq_expr_with_seen(lhs flat.NodeId, rhs flat.NodeId, elem_type string, seen []string) flat.NodeId {
1209 if t.membership_type_is_pointer(elem_type) {
1210 return t.make_infix(.eq, lhs, rhs)
1211 }
1212 mut clean := t.membership_container_type(elem_type)
1213 if clean == 'string' {
1214 return t.make_call_typed('string__eq', arr2(lhs, rhs), 'bool')
1215 }
1216 map_type := t.clean_map_type(clean)
1217 if map_type.starts_with('map[') {
1218 _, value_type := t.map_type_parts(map_type)
1219 if value_type.len > 0 && t.map_value_needs_element_eq(value_type) {
1220 src := if int(lhs) >= 0 && int(lhs) < t.a.nodes.len {
1221 t.a.nodes[int(lhs)]
1222 } else {
1223 flat.Node{}
1224 }
1225 return t.make_map_elementwise_eq_call_with_seen(lhs, rhs, map_type, src, seen)
1226 }
1227 return t.make_call_typed('v3_map_map_eq', arr2(lhs, rhs), 'bool')
1228 }
1229 if clean.starts_with('[]') {
1230 inner := clean[2..]
1231 if inner == 'string' {
1232 return t.make_call_typed('array_eq_string', arr2(lhs, rhs), 'bool')
1233 }
1234 if t.array_elem_needs_element_eq(inner) {
1235 src := if int(lhs) >= 0 && int(lhs) < t.a.nodes.len {
1236 t.a.nodes[int(lhs)]
1237 } else {
1238 flat.Node{}
1239 }
1240 return t.make_array_elementwise_eq_call_with_seen(lhs, rhs, inner, clean, clean, src,
1241 seen)
1242 }
1243 if inner.starts_with('[]') {
1244 return t.make_call_typed('array_eq_array', arr3(lhs, rhs,
1245 t.make_int_literal(array_nested_eq_depth(clean))), 'bool')
1246 }
1247 return t.make_call_typed('array_eq_raw', arr3(lhs, rhs, t.make_sizeof_type(inner)), 'bool')
1248 }
1249 if t.is_fixed_array_type(clean) {
1250 clean = t.resolved_fixed_array_canonical_type(clean)
1251 if t.type_needs_semantic_eq(clean) {
1252 if fixed_eq := t.make_fixed_array_elementwise_eq_expr_with_seen(lhs, rhs, clean, seen) {
1253 return fixed_eq
1254 }
1255 }
1256 lhs_value := t.stable_transformed_expr_for_reuse(lhs, clean, 'fixed_eq_lhs')
1257 rhs_value := t.stable_transformed_expr_for_reuse(rhs, clean, 'fixed_eq_rhs')
1258 cmp := t.make_call_typed('memcmp', arr3(t.make_prefix(.amp, lhs_value), t.make_prefix(.amp,
1259 rhs_value), t.make_sizeof_type(clean)), 'int')
1260 return t.make_infix(.eq, cmp, t.make_int_literal(0))
1261 }
1262 struct_type := t.struct_lookup_name(clean)
1263 if struct_type.len > 0 {
1264 method_name := '${struct_type}.=='
1265 if t.is_known_fn_name(method_name) {
1266 t.mark_fn_used_name(method_name)
1267 return t.make_call(method_name, arr2(lhs, rhs))
1268 }
1269 if struct_type in seen {
1270 cmp := t.make_call_typed('memcmp', arr3(t.make_prefix(.amp, lhs),
1271 t.make_prefix(.amp, rhs), t.make_sizeof_type(struct_type)), 'int')
1272 return t.make_infix(.eq, cmp, t.make_int_literal(0))
1273 }
1274 if field_eq := t.make_struct_field_eq_expr_with_seen(lhs, rhs, struct_type, seen) {
1275 return field_eq
1276 }
1277 cmp := t.make_call_typed('memcmp', arr3(t.make_prefix(.amp, lhs), t.make_prefix(.amp, rhs),
1278 t.make_sizeof_type(struct_type)), 'int')
1279 return t.make_infix(.eq, cmp, t.make_int_literal(0))
1280 }
1281 return t.make_infix(.eq, lhs, rhs)
1282}
1283
1284fn (t &Transformer) array_elem_needs_element_eq(elem_type string) bool {
1285 if t.membership_type_is_pointer(elem_type) {
1286 return false
1287 }
1288 clean := t.membership_container_type(elem_type)
1289 return clean != 'string' && t.type_needs_semantic_eq(clean)
1290}
1291
1292fn (t &Transformer) map_value_needs_element_eq(value_type string) bool {
1293 if t.membership_type_is_pointer(value_type) {
1294 return false
1295 }
1296 clean := t.membership_container_type(value_type)
1297 if t.is_fixed_array_type(clean) {
1298 return true
1299 }
1300 return t.type_needs_semantic_eq(clean)
1301}
1302
1303fn (t &Transformer) type_needs_semantic_eq(typ string) bool {
1304 if t.membership_type_is_pointer(typ) {
1305 return false
1306 }
1307 clean := t.membership_container_type(typ)
1308 if clean == 'string' {
1309 return true
1310 }
1311 if t.clean_map_type(clean).starts_with('map[') {
1312 return true
1313 }
1314 if clean.starts_with('[]') {
1315 return t.type_needs_semantic_eq(clean[2..])
1316 }
1317 if t.is_fixed_array_type(clean) {
1318 elem_type := fixed_array_elem_type(clean)
1319 return elem_type.len > 0 && t.type_needs_semantic_eq(elem_type)
1320 }
1321 return t.struct_lookup_name(clean).len > 0
1322}
1323
1324fn (mut t Transformer) make_array_elementwise_eq_call(lhs flat.NodeId, rhs flat.NodeId, elem_type string, lhs_type string, rhs_type string, src flat.Node) flat.NodeId {
1325 return t.make_array_elementwise_eq_call_with_seen(lhs, rhs, elem_type, lhs_type, rhs_type, src,
1326 []string{})
1327}
1328
1329fn (mut t Transformer) make_array_elementwise_eq_call_with_seen(lhs flat.NodeId, rhs flat.NodeId, elem_type string, lhs_type string, rhs_type string, src flat.Node, seen []string) flat.NodeId {
1330 mut clean_elem := t.membership_container_type(elem_type)
1331 if t.is_fixed_array_type(clean_elem) {
1332 clean_elem = t.resolved_fixed_array_canonical_type(clean_elem)
1333 }
1334 lhs_value := t.stable_transformed_expr_for_reuse(lhs, lhs_type, 'arr_eq_lhs')
1335 rhs_value := t.stable_transformed_expr_for_reuse(rhs, rhs_type, 'arr_eq_rhs')
1336 result_name := t.new_temp('arr_eq')
1337 idx_name := t.new_temp('arr_eq_idx')
1338 len_eq := t.make_infix(.eq, t.make_selector(lhs_value, 'len', 'int'), t.make_selector(rhs_value,
1339 'len', 'int'))
1340 t.pending_stmts << t.make_decl_assign_typed(result_name, len_eq, 'bool')
1341 init := t.make_decl_assign_typed(idx_name, t.make_int_literal(0), 'int')
1342 in_bounds := t.make_infix(.lt, t.make_ident(idx_name), t.make_selector(lhs_value, 'len', 'int'))
1343 cond := t.make_infix(.logical_and, t.make_ident(result_name), in_bounds)
1344 post := t.make_expr_stmt(t.make_postfix(t.make_ident(idx_name), .inc))
1345 lhs_elem := t.array_get_value(lhs_value, t.make_ident(idx_name), clean_elem)
1346 rhs_elem := t.array_get_value(rhs_value, t.make_ident(idx_name), clean_elem)
1347 pending_start := t.pending_stmts.len
1348 elem_eq := t.make_membership_eq_expr_with_seen(lhs_elem, rhs_elem, clean_elem, seen)
1349 mut body_stmts := t.pending_stmts[pending_start..].clone()
1350 t.pending_stmts = t.pending_stmts[..pending_start].clone()
1351 set_false := t.make_assign(t.make_ident(result_name), t.make_bool_literal(false))
1352 body_stmts << t.make_if(t.make_prefix(.not, t.make_paren(elem_eq)),
1353 t.make_block(arr1(set_false)), t.make_empty())
1354 t.pending_stmts << t.make_for_stmt(init, cond, post, body_stmts, src)
1355 result := t.make_ident(result_name)
1356 t.a.nodes[int(result)].typ = 'bool'
1357 return result
1358}
1359
1360fn (mut t Transformer) make_fixed_array_elementwise_eq_expr(lhs flat.NodeId, rhs flat.NodeId, fixed_type string) ?flat.NodeId {
1361 return t.make_fixed_array_elementwise_eq_expr_with_seen(lhs, rhs, fixed_type, []string{})
1362}
1363
1364fn (mut t Transformer) make_fixed_array_elementwise_eq_expr_with_seen(lhs flat.NodeId, rhs flat.NodeId, fixed_type string, seen []string) ?flat.NodeId {
1365 clean_fixed_type := t.resolved_fixed_array_canonical_type(fixed_type)
1366 elem_type := fixed_array_elem_type(clean_fixed_type)
1367 if elem_type.len == 0 {
1368 return none
1369 }
1370 lhs_value := t.stable_transformed_expr_for_reuse(lhs, clean_fixed_type, 'fixed_eq_lhs')
1371 rhs_value := t.stable_transformed_expr_for_reuse(rhs, clean_fixed_type, 'fixed_eq_rhs')
1372 result_name := t.new_temp('fixed_eq')
1373 idx_name := t.new_temp('fixed_eq_idx')
1374 t.pending_stmts << t.make_decl_assign_typed(result_name, t.make_bool_literal(true), 'bool')
1375 init := t.make_decl_assign_typed(idx_name, t.make_int_literal(0), 'int')
1376 cond := t.make_infix(.lt, t.make_ident(idx_name), t.make_fixed_array_len_expr(clean_fixed_type))
1377 post := t.make_expr_stmt(t.make_postfix(t.make_ident(idx_name), .inc))
1378 lhs_elem := t.make_index(lhs_value, t.make_ident(idx_name), elem_type)
1379 rhs_elem := t.make_index(rhs_value, t.make_ident(idx_name), elem_type)
1380 pending_start := t.pending_stmts.len
1381 elem_eq := t.make_membership_eq_expr_with_seen(lhs_elem, rhs_elem, elem_type, seen)
1382 mut body_stmts := t.pending_stmts[pending_start..].clone()
1383 t.pending_stmts = t.pending_stmts[..pending_start].clone()
1384 set_false := t.make_assign(t.make_ident(result_name), t.make_bool_literal(false))
1385 body_stmts << t.make_if(t.make_prefix(.not, t.make_paren(elem_eq)),
1386 t.make_block(arr1(set_false)), t.make_empty())
1387 t.pending_stmts << t.make_for_stmt(init,
1388 t.make_infix(.logical_and, t.make_ident(result_name), cond), post, body_stmts, flat.Node{})
1389 result := t.make_ident(result_name)
1390 t.a.nodes[int(result)].typ = 'bool'
1391 return result
1392}
1393
1394fn (mut t Transformer) make_map_elementwise_eq_call(lhs flat.NodeId, rhs flat.NodeId, map_type string, src flat.Node) flat.NodeId {
1395 return t.make_map_elementwise_eq_call_with_seen(lhs, rhs, map_type, src, []string{})
1396}
1397
1398fn (mut t Transformer) make_map_elementwise_eq_call_with_seen(lhs flat.NodeId, rhs flat.NodeId, map_type string, src flat.Node, seen []string) flat.NodeId {
1399 key_type, raw_value_type := t.map_type_parts(map_type)
1400 value_type := if t.is_fixed_array_type(raw_value_type) {
1401 t.resolved_fixed_array_canonical_type(raw_value_type)
1402 } else {
1403 raw_value_type
1404 }
1405 if key_type.len == 0 || value_type.len == 0 {
1406 return t.make_call_typed('v3_map_map_eq', arr2(lhs, rhs), 'bool')
1407 }
1408 lhs_value := t.stable_transformed_expr_for_reuse(lhs, map_type, 'map_eq_lhs')
1409 rhs_value := t.stable_transformed_expr_for_reuse(rhs, map_type, 'map_eq_rhs')
1410 result_name := t.new_temp('map_eq')
1411 zero_name := t.new_temp('map_eq_zero')
1412 key_name := t.new_temp('map_eq_key')
1413 lhs_val_name := t.new_temp('map_eq_val')
1414 len_eq := t.make_infix(.eq, t.make_selector(lhs_value, 'len', 'int'), t.make_selector(rhs_value,
1415 'len', 'int'))
1416 t.pending_stmts << t.make_decl_assign_typed(result_name, len_eq, 'bool')
1417 t.pending_stmts << t.make_decl_assign_typed(zero_name, t.zero_value_for_type(value_type),
1418 value_type)
1419 t.set_var_type(key_name, key_type)
1420 t.set_var_type(lhs_val_name, value_type)
1421 key_ident := t.make_ident(key_name)
1422 val_ident := t.make_ident(lhs_val_name)
1423 rhs_exists := t.make_map_exists_expr(rhs_value, map_type, key_name)
1424 rhs_val := t.make_map_get_expr(rhs_value, map_type, key_name, zero_name, value_type)
1425 pending_start := t.pending_stmts.len
1426 value_eq := t.make_membership_eq_expr_with_seen(t.make_ident(lhs_val_name), rhs_val,
1427 value_type, seen)
1428 mut body := t.pending_stmts[pending_start..].clone()
1429 t.pending_stmts = t.pending_stmts[..pending_start].clone()
1430 missing := t.make_prefix(.not, t.make_paren(rhs_exists))
1431 value_diff := t.make_prefix(.not, t.make_paren(value_eq))
1432 failed := t.make_infix(.logical_or, missing, value_diff)
1433 active := t.make_infix(.logical_and, t.make_ident(result_name), failed)
1434 set_false := t.make_assign(t.make_ident(result_name), t.make_bool_literal(false))
1435 body << t.make_if(active, t.make_block(arr1(set_false)), t.make_empty())
1436 start := t.a.children.len
1437 t.a.children << key_ident
1438 t.a.children << val_ident
1439 t.a.children << lhs_value
1440 for stmt in body {
1441 t.a.children << stmt
1442 }
1443 t.pending_stmts << t.a.add_node(flat.Node{
1444 kind: .for_in_stmt
1445 children_start: start
1446 children_count: flat.child_count(3 + body.len)
1447 pos: src.pos
1448 value: '3'
1449 })
1450 result := t.make_ident(result_name)
1451 t.a.nodes[int(result)].typ = 'bool'
1452 return result
1453}
1454
1455fn (mut t Transformer) make_struct_field_eq_expr(lhs flat.NodeId, rhs flat.NodeId, struct_type string) ?flat.NodeId {
1456 return t.make_struct_field_eq_expr_with_seen(lhs, rhs, struct_type, []string{})
1457}
1458
1459fn (mut t Transformer) make_struct_field_eq_expr_with_seen(lhs flat.NodeId, rhs flat.NodeId, struct_type string, seen []string) ?flat.NodeId {
1460 info := t.lookup_struct_info(struct_type) or { return none }
1461 mut next_seen := seen.clone()
1462 next_seen << struct_type
1463 mut eq := flat.empty_node
1464 for field in info.fields {
1465 field_type := if field.typ.len > 0 { field.typ } else { field.raw_typ }
1466 lhs_field := t.make_selector(lhs, field.name, field_type)
1467 rhs_field := t.make_selector(rhs, field.name, field_type)
1468 field_eq := if t.membership_type_is_pointer(field_type) {
1469 t.make_infix(.eq, lhs_field, rhs_field)
1470 } else {
1471 t.make_membership_eq_expr_with_seen(lhs_field, rhs_field, field_type, next_seen)
1472 }
1473 eq = if int(eq) < 0 { field_eq } else { t.make_infix(.logical_and, eq, field_eq) }
1474 }
1475 if int(eq) < 0 {
1476 return t.make_bool_literal(true)
1477 }
1478 return eq
1479}
1480
1481// selector_field_type supports selector field type handling for Transformer.
1482fn (t &Transformer) selector_field_type(node flat.Node) string {
1483 if node.value.len == 0 {
1484 return ''
1485 }
1486 for _, info in t.structs {
1487 for field in info.fields {
1488 if field.name == node.value {
1489 return t.normalize_type_alias(field.typ)
1490 }
1491 }
1492 }
1493 return ''
1494}
1495
1496// selector_array_elem_type supports selector array elem type handling for Transformer.
1497fn (t &Transformer) selector_array_elem_type(node flat.Node) string {
1498 if node.value.len == 0 {
1499 return ''
1500 }
1501 for _, info in t.structs {
1502 for field in info.fields {
1503 clean_typ := t.membership_container_type(field.typ)
1504 if field.name == node.value && clean_typ.starts_with('[]') {
1505 return t.normalize_type_alias(clean_typ[2..])
1506 }
1507 }
1508 }
1509 return ''
1510}
1511
1512// membership_container_type supports membership container type handling for Transformer.
1513fn (t &Transformer) membership_container_type(typ string) string {
1514 mut clean := t.normalize_type_alias(typ).trim_space()
1515 for {
1516 if clean.starts_with('shared ') {
1517 clean = clean[7..].trim_space()
1518 continue
1519 }
1520 if clean.starts_with('&') {
1521 clean = clean[1..].trim_space()
1522 continue
1523 }
1524 if clean.starts_with('...') {
1525 clean = '[]' + clean[3..].trim_space()
1526 continue
1527 }
1528 break
1529 }
1530 return t.normalize_type_alias(clean)
1531}
1532
1533fn (t &Transformer) membership_type_is_pointer(typ string) bool {
1534 mut clean := t.normalize_type_alias(typ).trim_space()
1535 for {
1536 if clean.starts_with('shared ') {
1537 clean = clean[7..].trim_space()
1538 continue
1539 }
1540 if clean.starts_with('mut ') {
1541 clean = clean[4..].trim_space()
1542 continue
1543 }
1544 break
1545 }
1546 return clean.starts_with('&')
1547}
1548
1549fn (t &Transformer) equality_type_is_array_pointer(typ string) bool {
1550 mut clean := typ.trim_space()
1551 for clean.starts_with('shared ') {
1552 clean = clean[7..].trim_space()
1553 }
1554 if clean.starts_with('mut ') {
1555 return false
1556 }
1557 clean = t.normalize_type_alias(clean).trim_space()
1558 for clean.starts_with('shared ') {
1559 clean = clean[7..].trim_space()
1560 }
1561 if !clean.starts_with('&') {
1562 return false
1563 }
1564 container := t.membership_container_type(clean)
1565 return container.starts_with('[]') || container == 'array'
1566}
1567
1568fn (t &Transformer) equality_expr_is_string_pointer(id flat.NodeId, typ string) bool {
1569 if int(id) < 0 || int(id) >= t.a.nodes.len {
1570 return false
1571 }
1572 node := t.a.nodes[int(id)]
1573 if node.kind in [.string_literal, .string_interp] {
1574 return false
1575 }
1576 mut clean := typ.trim_space()
1577 for clean.starts_with('shared ') {
1578 clean = clean[7..].trim_space()
1579 }
1580 if clean.starts_with('mut ') {
1581 return false
1582 }
1583 clean = t.normalize_type_alias(clean).trim_space()
1584 for clean.starts_with('shared ') {
1585 clean = clean[7..].trim_space()
1586 }
1587 return clean == '&string'
1588}
1589
1590fn (t &Transformer) equality_type_is_map_pointer(typ string) bool {
1591 mut clean := typ.trim_space()
1592 for clean.starts_with('shared ') {
1593 clean = clean[7..].trim_space()
1594 }
1595 if clean.starts_with('mut ') {
1596 return false
1597 }
1598 clean = t.normalize_type_alias(clean).trim_space()
1599 for clean.starts_with('shared ') {
1600 clean = clean[7..].trim_space()
1601 }
1602 return clean.starts_with('&') && t.clean_map_type(clean).starts_with('map[')
1603}
1604
1605// membership_container_is_pointer_array supports membership_container_is_pointer_array handling.
1606fn (t &Transformer) membership_container_is_pointer_array(typ string) bool {
1607 mut clean := t.normalize_type_alias(typ).trim_space()
1608 for clean.starts_with('shared ') {
1609 clean = clean[7..].trim_space()
1610 }
1611 if !clean.starts_with('&') {
1612 return false
1613 }
1614 clean = t.membership_container_type(clean)
1615 return clean.starts_with('[]') || clean == 'array'
1616}
1617
1618// array_contains_fn_name reports whether array contains fn name applies in transform.
1619fn array_contains_fn_name(elem string) string {
1620 return match elem {
1621 'string' { 'array_contains_string' }
1622 'u8', 'byte' { 'array_contains_u8' }
1623 else { 'array_contains_int' }
1624 }
1625}
1626
1627// fixed_array_contains_fn_name reports whether fixed array contains fn name applies in transform.
1628fn fixed_array_contains_fn_name(elem string) string {
1629 return match elem {
1630 'string' { 'fixed_array_contains_string' }
1631 'u8', 'byte' { 'fixed_array_contains_u8' }
1632 else { 'fixed_array_contains_int' }
1633 }
1634}
1635
1636// stable_expr_for_reuse supports stable expr for reuse handling for Transformer.
1637fn (mut t Transformer) stable_expr_for_reuse(id flat.NodeId) flat.NodeId {
1638 expr := t.transform_expr(id)
1639 if t.is_stable_expr_for_reuse(expr) {
1640 return expr
1641 }
1642 tmp_name := t.new_temp('in_lhs')
1643 mut tmp_typ := t.node_type(expr)
1644 if tmp_typ.len == 0 {
1645 tmp_typ = t.node_type(id)
1646 }
1647 decl := t.make_decl_assign(tmp_name, expr)
1648 if tmp_typ.len > 0 {
1649 t.a.nodes[int(decl)].typ = tmp_typ
1650 t.set_var_type(tmp_name, tmp_typ)
1651 }
1652 t.pending_stmts << decl
1653 return t.make_ident(tmp_name)
1654}
1655
1656// stable_transformed_expr_for_reuse
1657// supports helper handling in transform.
1658fn (mut t Transformer) stable_transformed_expr_for_reuse(expr flat.NodeId, typ string, prefix string) flat.NodeId {
1659 if t.is_stable_expr_for_reuse(expr) {
1660 return expr
1661 }
1662 tmp_name := t.new_temp(prefix)
1663 t.pending_stmts << t.make_decl_assign_typed(tmp_name, expr, typ)
1664 return t.make_ident(tmp_name)
1665}
1666
1667// is_stable_expr_for_reuse reports whether is stable expr for reuse applies in transform.
1668fn (t &Transformer) is_stable_expr_for_reuse(id flat.NodeId) bool {
1669 if int(id) < 0 {
1670 return true
1671 }
1672 node := t.a.nodes[int(id)]
1673 return match node.kind {
1674 .ident, .int_literal, .float_literal, .bool_literal, .char_literal, .string_literal,
1675 .nil_literal, .none_expr, .enum_val, .sizeof_expr, .typeof_expr {
1676 true
1677 }
1678 .selector {
1679 node.children_count > 0 && t.is_stable_expr_for_reuse(t.a.children[node.children_start])
1680 }
1681 .field_init {
1682 node.children_count == 0
1683 || t.is_stable_expr_for_reuse(t.a.children[node.children_start])
1684 }
1685 .struct_init {
1686 mut stable := true
1687 for i in 0 .. node.children_count {
1688 if !t.is_stable_expr_for_reuse(t.a.child(&node, i)) {
1689 stable = false
1690 break
1691 }
1692 }
1693 stable
1694 }
1695 .cast_expr {
1696 node.children_count == 0
1697 || t.is_stable_expr_for_reuse(t.a.children[node.children_start])
1698 }
1699 .index {
1700 node.children_count >= 2
1701 && t.is_stable_expr_for_reuse(t.a.children[node.children_start])
1702 && t.is_stable_expr_for_reuse(t.a.children[node.children_start + 1])
1703 }
1704 else {
1705 false
1706 }
1707 }
1708}
1709
1710// transform_fixed_array_len transforms transform fixed array len data for transform.
1711fn (mut t Transformer) transform_fixed_array_len(_id flat.NodeId, node flat.Node) ?flat.NodeId {
1712 if node.value != 'len' || node.children_count == 0 {
1713 return none
1714 }
1715 base_id := t.a.children[node.children_start]
1716 base_type := t.node_type(base_id)
1717 if !t.is_fixed_array_type(base_type) {
1718 return none
1719 }
1720 return t.make_fixed_array_len_expr(base_type)
1721}
1722
1723// clean_map_type transforms clean map type data for transform.
1724fn (t &Transformer) clean_map_type(typ string) string {
1725 mut clean := t.normalize_type_alias(typ).trim_space()
1726 for {
1727 if clean.starts_with('shared ') {
1728 clean = clean[7..].trim_space()
1729 continue
1730 }
1731 if clean.starts_with('&') {
1732 clean = clean[1..].trim_space()
1733 continue
1734 }
1735 break
1736 }
1737 return t.normalize_type_alias(clean)
1738}
1739
1740// runtime_addr supports runtime addr handling for Transformer.
1741fn (mut t Transformer) runtime_addr(expr flat.NodeId, typ string) flat.NodeId {
1742 if typ.starts_with('&') {
1743 return expr
1744 }
1745 if !t.expr_can_take_address(expr) {
1746 stable := t.stable_transformed_expr_for_reuse(expr, typ, 'addr')
1747 return t.make_prefix(.amp, stable)
1748 }
1749 return t.make_prefix(.amp, expr)
1750}
1751
1752// transform_enum_shorthand transforms transform enum shorthand data for transform.
1753fn (mut t Transformer) transform_enum_shorthand(id flat.NodeId, node flat.Node, expected_enum string) flat.NodeId {
1754 resolved_enum := t.enum_type_name_for_expected(expected_enum, '')
1755 if resolved_enum.len == 0 {
1756 return id
1757 }
1758 short_name := node.value.trim_left('.')
1759 if fields := t.enum_types[resolved_enum] {
1760 for f in fields {
1761 if f == short_name {
1762 return t.a.add_node(flat.Node{
1763 kind: .enum_val
1764 value: '${resolved_enum}.${short_name}'
1765 typ: resolved_enum
1766 })
1767 }
1768 }
1769 }
1770 return id
1771}
1772
1773// enum_type_name_for_expected supports enum type name for expected handling for Transformer.
1774fn (t &Transformer) enum_type_name_for_expected(expected_enum string, owner_mod string) string {
1775 if expected_enum.len == 0 {
1776 return ''
1777 }
1778 mut clean := expected_enum
1779 if clean.starts_with('&') {
1780 clean = clean[1..]
1781 }
1782 if clean in t.enum_types {
1783 return clean
1784 }
1785 if clean.contains('.') {
1786 return ''
1787 }
1788 for mod_name in [owner_mod, t.cur_module] {
1789 if mod_name.len == 0 || mod_name == 'main' || mod_name == 'builtin' {
1790 continue
1791 }
1792 qname := '${mod_name}.${clean}'
1793 if qname in t.enum_types {
1794 return qname
1795 }
1796 }
1797 mut found := ''
1798 for enum_name, _ in t.enum_types {
1799 if enum_name.all_after_last('.') != clean {
1800 continue
1801 }
1802 if found.len > 0 && found != enum_name {
1803 return ''
1804 }
1805 found = enum_name
1806 }
1807 return found
1808}
1809
1810// make_call builds make call data for transform.
1811pub fn (mut t Transformer) make_call(fn_name string, args []flat.NodeId) flat.NodeId {
1812 return t.make_call_typed(fn_name, args, '')
1813}
1814
1815// make_call_typed builds make call typed data for transform.
1816pub fn (mut t Transformer) make_call_typed(fn_name string, args []flat.NodeId, typ string) flat.NodeId {
1817 fn_ident := t.make_ident(fn_name)
1818 return t.make_call_expr_typed(fn_ident, args, typ)
1819}
1820
1821fn (mut t Transformer) mark_fn_used(fn_name string) {
1822 if fn_name.len == 0 || t.used_fns.len == 0 {
1823 return
1824 }
1825 t.used_fns[fn_name] = true
1826 t.used_fns[c_name(fn_name)] = true
1827}
1828
1829// make_call_expr_typed builds make call expr typed data for transform.
1830pub fn (mut t Transformer) make_call_expr_typed(fn_expr flat.NodeId, args []flat.NodeId, typ string) flat.NodeId {
1831 start := t.a.children.len
1832 t.a.children << fn_expr
1833 for arg in args {
1834 t.a.children << arg
1835 }
1836 return t.a.add_node(flat.Node{
1837 kind: .call
1838 children_start: start
1839 children_count: flat.child_count(1 + args.len)
1840 typ: typ
1841 })
1842}
1843
1844// make_empty builds make empty data for transform.
1845pub fn (mut t Transformer) make_empty() flat.NodeId {
1846 return t.a.add(.empty)
1847}
1848
1849// make_method_call builds make method call data for transform.
1850pub fn (mut t Transformer) make_method_call(receiver flat.NodeId, method_name string, args []flat.NodeId) flat.NodeId {
1851 // Build selector: receiver.method_name
1852 sel_start := t.a.children.len
1853 t.a.children << receiver
1854 selector := t.a.add_node(flat.Node{
1855 kind: .selector
1856 value: method_name
1857 children_start: sel_start
1858 children_count: 1
1859 })
1860
1861 start := t.a.children.len
1862 t.a.children << selector
1863 for arg in args {
1864 t.a.children << arg
1865 }
1866 return t.a.add_node(flat.Node{
1867 kind: .call
1868 children_start: start
1869 children_count: flat.child_count(1 + args.len)
1870 })
1871}
1872
1873// make_selector builds make selector data for transform.
1874pub fn (mut t Transformer) make_selector(base flat.NodeId, field string, typ string) flat.NodeId {
1875 return t.make_selector_op(base, field, typ, .dot)
1876}
1877
1878// make_selector_op builds make selector op data for transform.
1879pub fn (mut t Transformer) make_selector_op(base flat.NodeId, field string, typ string, op flat.Op) flat.NodeId {
1880 start := t.a.children.len
1881 t.a.children << base
1882 return t.a.add_node(flat.Node{
1883 kind: .selector
1884 op: op
1885 children_start: start
1886 children_count: 1
1887 value: field
1888 typ: typ
1889 })
1890}
1891
1892// make_index builds make index data for transform.
1893pub fn (mut t Transformer) make_index(base flat.NodeId, index flat.NodeId, typ string) flat.NodeId {
1894 start := t.a.children.len
1895 t.a.children << base
1896 t.a.children << index
1897 return t.a.add_node(flat.Node{
1898 kind: .index
1899 children_start: start
1900 children_count: 2
1901 typ: typ
1902 })
1903}
1904
1905// make_cast builds make cast data for transform.
1906pub fn (mut t Transformer) make_cast(target_type string, expr flat.NodeId, typ string) flat.NodeId {
1907 start := t.a.children.len
1908 t.a.children << expr
1909 return t.a.add_node(flat.Node{
1910 kind: .cast_expr
1911 children_start: start
1912 children_count: 1
1913 value: target_type
1914 typ: typ
1915 })
1916}
1917
1918// make_postfix builds make postfix data for transform.
1919pub fn (mut t Transformer) make_postfix(expr flat.NodeId, op flat.Op) flat.NodeId {
1920 start := t.a.children.len
1921 t.a.children << expr
1922 return t.a.add_node(flat.Node{
1923 kind: .postfix
1924 op: op
1925 children_start: start
1926 children_count: 1
1927 })
1928}
1929
1930// make_struct_init builds make struct init data for transform.
1931pub fn (mut t Transformer) make_struct_init(name string) flat.NodeId {
1932 return t.a.add_node(flat.Node{
1933 kind: .struct_init
1934 value: name
1935 typ: name
1936 })
1937}
1938
1939// make_array_init builds make array init data for transform.
1940pub fn (mut t Transformer) make_array_init(elem_type string) flat.NodeId {
1941 return t.a.add_node(flat.Node{
1942 kind: .array_init
1943 value: elem_type
1944 typ: '[]${elem_type}'
1945 })
1946}
1947
1948// make_map_init builds make map init data for transform.
1949pub fn (mut t Transformer) make_map_init(map_type string) flat.NodeId {
1950 return t.a.add_node(flat.Node{
1951 kind: .map_init
1952 value: map_type
1953 typ: map_type
1954 })
1955}
1956
1957// make_string_literal builds make string literal data for transform.
1958pub fn (mut t Transformer) make_string_literal(value string) flat.NodeId {
1959 return t.a.add_val(.string_literal, value)
1960}
1961
1962// make_int_literal builds make int literal data for transform.
1963pub fn (mut t Transformer) make_int_literal(value int) flat.NodeId {
1964 return t.a.add_val(.int_literal, '${value}')
1965}
1966
1967pub fn (mut t Transformer) make_int_literal_typed(value string, typ string) flat.NodeId {
1968 return t.a.add_node(flat.Node{
1969 kind: .int_literal
1970 value: value
1971 typ: typ
1972 })
1973}
1974
1975// make_float_literal builds make float literal data for transform.
1976pub fn (mut t Transformer) make_float_literal(value string) flat.NodeId {
1977 return t.a.add_val(.float_literal, value)
1978}
1979
1980pub fn (mut t Transformer) make_float_literal_typed(value string, typ string) flat.NodeId {
1981 return t.a.add_node(flat.Node{
1982 kind: .float_literal
1983 value: value
1984 typ: typ
1985 })
1986}
1987
1988// make_bool_literal builds make bool literal data for transform.
1989pub fn (mut t Transformer) make_bool_literal(value bool) flat.NodeId {
1990 return t.a.add_val(.bool_literal, if value { 'true' } else { 'false' })
1991}
1992
1993// make_sizeof_type builds make sizeof type data for transform.
1994pub fn (mut t Transformer) make_sizeof_type(type_name string) flat.NodeId {
1995 return t.a.add_node(flat.Node{
1996 kind: .sizeof_expr
1997 value: type_name
1998 typ: 'usize'
1999 })
2000}
2001
2002// is_fixed_array_type reports whether a v-type string denotes a fixed array
2003// like `int[5]` (as opposed to a dynamic array `[]int` or a map `map[...]...`).
2004fn (t &Transformer) is_fixed_array_type(s string) bool {
2005 if s.starts_with('[]') || s.starts_with('map[') {
2006 return false
2007 }
2008 if s.starts_with('[') {
2009 return s.contains(']')
2010 }
2011 if !s.contains('[') || !s.ends_with(']') {
2012 return false
2013 }
2014 len_text := fixed_array_len_text(s)
2015 if is_decimal_text(len_text) {
2016 return true
2017 }
2018 // A postfix fixed-array name (`ArrayFixed.name()`) can carry a non-decimal length — a const
2019 // (`int[seg_count]`) or an expression (`int[segs + 1]`) — once the checker round-trips
2020 // `[n]int` to `int[n]`. Accept it when the bracket text resolves to an integer constant,
2021 // which distinguishes it from a generic instance (`vec.Vec4[f32]`, whose bracket is a type).
2022 if len_text.contains(',') || isnil(t.tc) {
2023 return false
2024 }
2025 return t.tc.const_int_value_in_module(len_text, t.cur_module, []string{}) != none
2026}
2027
2028// fixed_array_len supports fixed array len handling for transform.
2029fn fixed_array_len(s string) int {
2030 return fixed_array_len_text(s).int()
2031}
2032
2033// fixed_array_len_text supports fixed array len text handling for transform.
2034fn fixed_array_len_text(s string) string {
2035 if !s.starts_with('[') {
2036 _, dims := transform_postfix_fixed_array_parts(s)
2037 if dims.len > 0 {
2038 return dims[0]
2039 }
2040 }
2041 return s.all_after('[').all_before(']').trim_space()
2042}
2043
2044// fixed_array_elem_type supports fixed array elem type handling for transform.
2045fn fixed_array_elem_type(s string) string {
2046 if s.starts_with('[') {
2047 return s.all_after(']')
2048 }
2049 elem, dims := transform_postfix_fixed_array_parts(s)
2050 if dims.len == 0 {
2051 return s.all_before('[')
2052 }
2053 mut out := elem
2054 for i := dims.len - 1; i > 0; i-- {
2055 out += '[${dims[i]}]'
2056 }
2057 return out
2058}
2059
2060fn fixed_array_canonical_type(s string) string {
2061 if !s.starts_with('[') {
2062 return s
2063 }
2064 elem_type := fixed_array_canonical_type(fixed_array_elem_type(s))
2065 len_text := fixed_array_len_text(s)
2066 return '${elem_type}[${len_text}]'
2067}
2068
2069fn (t &Transformer) resolved_fixed_array_canonical_type(s string) string {
2070 clean := fixed_array_canonical_type(s)
2071 if !t.is_fixed_array_type(clean) {
2072 return clean
2073 }
2074 elem_type := fixed_array_elem_type(clean)
2075 resolved_elem := if t.is_fixed_array_type(elem_type) {
2076 t.resolved_fixed_array_canonical_type(elem_type)
2077 } else {
2078 elem_type
2079 }
2080 len_text := fixed_array_len_text(clean)
2081 if is_decimal_text(len_text) || isnil(t.tc) {
2082 return '${resolved_elem}[${len_text}]'
2083 }
2084 if v := t.tc.const_int_value_in_module(len_text, t.cur_module, []string{}) {
2085 return '${resolved_elem}[${v}]'
2086 }
2087 return '${resolved_elem}[${len_text}]'
2088}
2089
2090// is_decimal_text reports whether is decimal text applies in transform.
2091fn is_decimal_text(s string) bool {
2092 if s.len == 0 {
2093 return false
2094 }
2095 for ch in s {
2096 if ch < `0` || ch > `9` {
2097 return false
2098 }
2099 }
2100 return true
2101}
2102
2103// make_fixed_array_len_expr builds make fixed array len expr data for transform.
2104fn (mut t Transformer) make_fixed_array_len_expr(s string) flat.NodeId {
2105 len_text := fixed_array_len_text(s)
2106 if is_decimal_text(len_text) {
2107 return t.make_int_literal(len_text.int())
2108 }
2109 // Fold a const length — a bare const (`SEGS`) or an expression (`segs + 1`) — to
2110 // its integer value. A fixed array's `.len` is a compile-time constant; emitting
2111 // the raw expression as an ident would c_name-mangle it into an undeclared
2112 // identifier such as `segs_+_1`.
2113 if !isnil(t.tc) {
2114 if v := t.tc.const_int_value_in_module(len_text, t.cur_module, []string{}) {
2115 return t.make_int_literal(v)
2116 }
2117 }
2118 if len_text.contains('.') {
2119 base := len_text.all_before_last('.')
2120 field := len_text.all_after_last('.')
2121 return t.make_selector(t.make_ident(base), field, 'int')
2122 }
2123 return t.make_ident(len_text)
2124}
2125
2126const c_reserved_words = ['auto', 'break', 'case', 'char', 'const', 'continue', 'copy', 'default',
2127 'do', 'double', 'else', 'enum', 'extern', 'float', 'for', 'goto', 'if', 'inline', 'int', 'long',
2128 'register', 'restrict', 'return', 'short', 'signed', 'sizeof', 'static', 'struct', 'switch',
2129 'typedef', 'union', 'unsigned', 'void', 'volatile', 'while']
2130
2131// c_name converts c name data for transform.
2132fn c_name(name string) string {
2133 if name.starts_with('C.') {
2134 return name[2..]
2135 }
2136 n := name.replace('[]', 'Array_').replace('.-', '__minus').replace('.+', '__plus').replace('.==',
2137 '__eq').replace('.!=', '__ne').replace('.<=', '__le').replace('.>=', '__ge').replace('.<',
2138 '__lt').replace('.>', '__gt').replace('.*', '__mul').replace('./', '__div').replace('.%',
2139 '__mod').replace('.&', '__and').replace('.|', '__or').replace('.^', '__xor').replace('.<<',
2140 '__left_shift').replace('.>>', '__right_shift').replace('&', 'ptr').replace('[', '_').replace(']', '').replace(',', '_').replace(' ', '_').replace('.', '__')
2141 if n in c_reserved_words {
2142 return 'v_${n}'
2143 }
2144 return n
2145}
2146