vxx2 / vlib / v / gen / c / infix.v
2310 lines · 2260 sloc · 74.81 KB · 45ffbccabad9928f39cab7f767d3f1c3865e6971
Raw
1// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4module c
5
6import v.ast
7import v.token
8import v.util
9
10fn (mut g Gen) safe_shift_fn_name(left_type ast.Type, op token.Kind) string {
11 shift_type := g.table.unalias_num_type(g.unwrap_generic(left_type))
12 prefix := if op == .left_shift { 'v__lshift_' } else { 'v__rshift_' }
13 return prefix + util.no_dots(g.base_type(shift_type))
14}
15
16fn (mut g Gen) gen_safe_shift_expr(node ast.InfixExpr) {
17 left_type := match node.left {
18 ast.CastExpr {
19 node.left.typ
20 }
21 else {
22 g.type_resolver.get_type_or_default(node.left, node.left_type)
23 }
24 }
25
26 g.write(g.safe_shift_fn_name(left_type, node.op))
27 g.write('(')
28 g.expr(node.left)
29 g.write(', (u64)')
30 g.expr(node.right)
31 g.write(')')
32}
33
34fn (mut g Gen) infix_expr(node ast.InfixExpr) {
35 g.expected_fixed_arr = true
36 defer {
37 g.expected_fixed_arr = false
38 }
39 if node.auto_locked != '' {
40 g.writeln('sync__RwMutex_lock(&${node.auto_locked}->mtx);')
41 }
42 match node.op {
43 .arrow {
44 g.infix_expr_arrow_op(node)
45 }
46 .eq, .ne {
47 g.infix_expr_eq_op(node)
48 }
49 .gt, .ge, .lt, .le {
50 g.infix_expr_cmp_op(node)
51 }
52 .key_in, .not_in {
53 g.infix_expr_in_op(node)
54 }
55 .key_is, .not_is {
56 g.infix_expr_is_op(node)
57 }
58 .plus, .minus, .mul, .power, .div, .mod {
59 g.infix_expr_arithmetic_op(node)
60 }
61 .left_shift {
62 // `a << b` can mean many things in V ...
63 // TODO: disambiguate everything in the checker; cgen should not decide all this.
64 // Instead it should be as simple, as the branch for .right_shift is.
65 // `array << val` should have its own separate operation internally.
66 g.infix_expr_left_shift_op(node)
67 }
68 .right_shift {
69 g.gen_safe_shift_expr(node)
70 }
71 .and, .logical_or {
72 g.infix_expr_and_or_op(node)
73 }
74 else {
75 // `x & y == 0` => `(x & y) == 0` in C
76 need_par := node.op in [.amp, .pipe, .xor]
77 if need_par {
78 g.write('(')
79 }
80 g.gen_plain_infix_expr(node)
81 if need_par {
82 g.write(')')
83 }
84 }
85 }
86
87 if node.auto_locked != '' {
88 g.writeln(';')
89 g.write('sync__RwMutex_unlock(&${node.auto_locked}->mtx)')
90 }
91}
92
93// infix_expr_arrow_op generates C code for pushing into channels (chan <- val)
94fn (mut g Gen) infix_expr_arrow_op(node ast.InfixExpr) {
95 mut resolved_left_type := g.resolved_expr_type(node.left, node.left_type)
96 if resolved_left_type == 0 {
97 resolved_left_type = node.left_type
98 }
99 resolved_left_type = g.unwrap_generic(g.recheck_concrete_type(resolved_left_type))
100 mut left := g.unwrap(resolved_left_type)
101 if left.sym.info !is ast.Chan {
102 left = g.unwrap(node.left_type)
103 }
104 g.note_chan_type_definition(left.typ)
105 styp := g.styp(left.typ)
106 elem_type := g.unwrap_generic(g.recheck_concrete_type((left.sym.info as ast.Chan).elem_type))
107 gen_or := node.or_block.kind != .absent
108 tmp_opt := if gen_or { g.new_tmp_var() } else { '' }
109 if gen_or {
110 elem_styp := g.styp(elem_type)
111 g.register_chan_push_option_fn(elem_styp, styp)
112 g.write('${option_name}_void ${tmp_opt} = __Option_${styp}_pushval(')
113 } else {
114 g.write('__${styp}_pushval(')
115 }
116 g.expr(node.left)
117 g.write(', ')
118 if g.table.sym(elem_type).kind in [.sum_type, .interface] {
119 g.expr_with_cast(node.right, node.right_type, elem_type)
120 } else {
121 g.expr(node.right)
122 }
123 g.write(')')
124 if gen_or {
125 g.or_block(tmp_opt, node.or_block, ast.void_type)
126 }
127}
128
129// infix_expr_eq_op generates code for `==` and `!=`
130fn (mut g Gen) infix_expr_eq_op(node ast.InfixExpr) {
131 mut left_type := g.type_resolver.get_type_or_default(node.left, node.left_type)
132 mut right_type := g.type_resolver.get_type_or_default(node.right, node.right_type)
133 if (g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0)
134 || left_type.has_flag(.generic) || right_type.has_flag(.generic)
135 || g.type_has_unresolved_generic_parts(left_type)
136 || g.type_has_unresolved_generic_parts(right_type) {
137 resolved_left := g.resolved_expr_type(node.left, node.left_type)
138 if resolved_left != 0 {
139 // Don't override a smartcasted concrete type (from the checker)
140 // with a sumtype/option-sumtype from resolved_expr_type, which
141 // doesn't check scope smartcasts.
142 resolved_sym := g.table.sym(resolved_left)
143 left_sym := g.table.sym(left_type)
144 is_sumtype_override := resolved_sym.kind in [.sum_type, .interface]
145 && left_sym.kind !in [.sum_type, .interface]
146 is_option_introduction := resolved_left.has_flag(.option)
147 && !left_type.has_flag(.option)
148 // Don't strip option flag when comparing with none — the scope
149 // may have an unwrapped smartcast from a fallthrough guard, but
150 // the none check itself needs the option type.
151 is_option_removal_for_none := left_type.has_flag(.option)
152 && !resolved_left.has_flag(.option) && node.right is ast.None
153 if !is_sumtype_override && !is_option_introduction && !is_option_removal_for_none {
154 left_type = resolved_left
155 }
156 }
157 resolved_right := g.resolved_expr_type(node.right, node.right_type)
158 if resolved_right != 0 {
159 resolved_sym := g.table.sym(resolved_right)
160 right_sym := g.table.sym(right_type)
161 is_sumtype_override := resolved_sym.kind in [.sum_type, .interface]
162 && right_sym.kind !in [.sum_type, .interface]
163 is_option_introduction := resolved_right.has_flag(.option)
164 && !right_type.has_flag(.option)
165 if !is_sumtype_override && !is_option_introduction {
166 right_type = resolved_right
167 }
168 }
169 }
170 // Promote literal element types in arrays (e.g. []int_literal -> []int)
171 // so that equality comparisons match the correct array type.
172 left_type = g.promote_literal_array_type(left_type)
173 right_type = g.promote_literal_array_type(right_type)
174 left := g.unwrap(left_type)
175 right := g.unwrap(right_type)
176 mut has_defined_eq_operator := false
177 mut eq_operator_expects_ptr := false
178 if m := g.table.find_method(left.sym, '==') {
179 // For != on generic struct types, check if the == operator was defined on
180 // a generic parent (receiver type has generic flag or is a different type).
181 // In that case, use _struct_eq for != (full structural comparison) instead
182 // of negating the user-defined ==, matching master's behavior.
183 mut skip_for_generic_ne := false
184 if node.op == .ne && m.receiver_type.has_flag(.generic) {
185 skip_for_generic_ne = true
186 }
187 if !skip_for_generic_ne {
188 has_defined_eq_operator = true
189 eq_operator_expects_ptr = m.receiver_type.is_ptr()
190 }
191 }
192 // TODO: investigate why the following is needed for vlib/v/tests/string_alias_test.v and vlib/v/tests/anon_fn_with_alias_args_test.v
193 has_alias_eq_op_overload := left.sym.info is ast.Alias && left.sym.has_method('==')
194 if g.pref.translated && !g.is_builtin_mod {
195 g.gen_plain_infix_expr(node)
196 return
197 }
198 left_sym := g.table.sym(left_type)
199 right_sym := g.table.sym(right_type)
200 left_is_option := left_type.has_flag(.option) || (left_sym.kind == .alias
201 && left_sym.info is ast.Alias && left_sym.info.parent_type.has_flag(.option))
202 right_is_option := right_type.has_flag(.option) || (right_sym.kind == .alias
203 && right_sym.info is ast.Alias && right_sym.info.parent_type.has_flag(.option))
204 is_none_check := left_is_option && node.right is ast.None
205 if is_none_check {
206 g.gen_is_none_check(node)
207 } else if (left.typ.is_ptr() && right.typ.is_int())
208 || (right.typ.is_ptr() && left.typ.is_int())
209 || (left.typ.is_ptr() && right.typ == ast.nil_type) {
210 g.gen_plain_infix_expr(node)
211 } else if (left.typ.idx() == ast.string_type_idx || (!has_defined_eq_operator
212 && left.unaliased.idx() == ast.string_type_idx)) && node.right is ast.StringLiteral
213 && (node.right.val == '' || (node.left is ast.SelectorExpr
214 || (node.left is ast.Ident && node.left.or_expr.kind == .absent
215 && !(node.left.obj is ast.Var && node.left.obj.ct_type_var == .smartcast
216 && g.table.sym(g.unwrap_generic(node.left.obj.orig_type)).kind == .sum_type)))) {
217 if node.right.val == '' {
218 // `str == ''` -> `str.len == 0` optimization
219 g.write('(')
220 g.expr(ast.Expr(node.left))
221 g.write(')')
222 arrow := if left.typ.is_ptr() { '->' } else { '.' }
223 g.write('${arrow}len ${node.op} 0')
224 } else if node.left is ast.Ident {
225 // vmemcmp(left, "str", sizeof("str")) optimization
226 slit := cescape_nonascii(util.smart_quote(node.right.val, node.right.is_raw))
227 var := g.expr_string(ast.Expr(node.left))
228 arrow := if left.typ.is_ptr() { '->' } else { '.' }
229 if node.op == .eq {
230 g.write('_SLIT_EQ(${var}${arrow}str, ${var}${arrow}len, "${slit}")')
231 } else {
232 g.write('_SLIT_NE(${var}${arrow}str, ${var}${arrow}len, "${slit}")')
233 }
234 } else {
235 // fast_string_eq optimization for string selector comparison to literals
236 if node.op == .ne {
237 g.write('!builtin__fast_string_eq(')
238 } else {
239 g.write('builtin__fast_string_eq(')
240 }
241 g.expr(node.left)
242 g.write(', ')
243 g.expr(ast.Expr(node.right))
244 g.write(')')
245 }
246 } else if has_defined_eq_operator {
247 if node.op == .ne {
248 g.write('!')
249 }
250 if left.sym.kind == .struct && (left.sym.info as ast.Struct).generic_types.len > 0 {
251 concrete_types := (left.sym.info as ast.Struct).concrete_types
252 mut method_name := '${left.sym.cname}__eq'
253 if left.unaliased_sym.is_builtin() {
254 method_name = 'builtin__${method_name}'
255 }
256 method_name = g.generic_fn_name(concrete_types, method_name)
257 g.write(method_name)
258 } else {
259 mut method_name := if has_alias_eq_op_overload {
260 g.styp(left.typ.set_nr_muls(0))
261 } else {
262 g.styp(left.unaliased.set_nr_muls(0))
263 }
264 mut is_builtin_or_alias_to_builtin := left.sym.is_builtin()
265 if !has_alias_eq_op_overload && !is_builtin_or_alias_to_builtin
266 && left.sym.info is ast.Alias {
267 alias_info := left.sym.info as ast.Alias
268 parent_sym := g.table.sym(alias_info.parent_type)
269 is_builtin_or_alias_to_builtin = parent_sym.is_builtin()
270 }
271 if is_builtin_or_alias_to_builtin {
272 method_name = 'builtin__${method_name}'
273 }
274 g.write(method_name)
275 g.write('__eq')
276 }
277 g.write('(')
278 g.write('*'.repeat(left.typ.nr_muls()))
279 if eq_operator_expects_ptr {
280 g.write('&')
281 }
282 if node.left is ast.ArrayInit && g.table.sym(node.left_type).kind == .array_fixed {
283 g.fixed_array_init_with_cast(node.left, node.left_type)
284 } else {
285 g.expr(ast.Expr(node.left))
286 }
287 g.write2(', ', '*'.repeat(right.typ.nr_muls()))
288 if eq_operator_expects_ptr {
289 g.write('&')
290 }
291 if node.right is ast.ArrayInit && g.table.sym(node.right_type).kind == .array_fixed {
292 g.fixed_array_init_with_cast(node.right, node.right_type)
293 } else {
294 g.expr(node.right)
295 }
296 g.write(')')
297 } else if (left.unaliased.idx() == right.unaliased.idx()
298 && left.sym.kind in [.array, .array_fixed, .alias, .map, .struct, .sum_type, .interface])
299 || (left.unaliased_sym.kind == .array_fixed && right.unaliased_sym.kind == .array_fixed) {
300 if g.pref.translated && !g.is_builtin_mod {
301 g.gen_plain_infix_expr(node)
302 return
303 }
304 kind := if left.sym.kind == .alias && right.sym.kind != .alias {
305 left.unaliased_sym.kind
306 } else {
307 left.sym.kind
308 }
309 match kind {
310 .alias {
311 // optimize simple eq/ne operation on numbers
312 if left.unaliased_sym.is_int() {
313 if left.typ.is_ptr() && node.left.is_auto_deref_var() && !right.typ.is_pointer() {
314 g.write('*'.repeat(left.typ.nr_muls()))
315 }
316 g.expr(node.left)
317 g.write(' ${node.op} ')
318 if right.typ.is_ptr() {
319 g.write('*'.repeat(right.typ.nr_muls()))
320 }
321 g.expr(node.right)
322 g.no_eq_method_types[left.typ] = true
323 } else {
324 ptr_typ := g.equality_fn(left.typ)
325 if node.op == .ne {
326 g.write('!')
327 }
328 g.write('${ptr_typ}_alias_eq(')
329 if left.typ.is_ptr() {
330 g.write('*'.repeat(left.typ.nr_muls()))
331 }
332 if node.left is ast.StructInit && left.unaliased_sym.is_primitive_fixed_array() {
333 s := g.styp(left.unaliased)
334 g.write('(${s})')
335 }
336 g.expr(node.left)
337 g.write(', ')
338 if node.right is ast.StructInit
339 && right.unaliased_sym.is_primitive_fixed_array() {
340 s := g.styp(right.unaliased)
341 g.write('(${s})')
342 }
343 if right.typ.is_ptr() {
344 g.write('*'.repeat(right.typ.nr_muls()))
345 }
346 g.expr(node.right)
347 g.write(')')
348 }
349 }
350 .array {
351 ptr_typ := g.equality_fn(left.unaliased.clear_flag(.shared_f))
352 if node.op == .ne {
353 g.write('!')
354 }
355 g.write('${ptr_typ}_arr_eq(')
356 if left.typ.is_ptr() && !left.typ.has_flag(.shared_f) {
357 if node.left !is ast.ArrayInit {
358 g.write('*'.repeat(left.typ.nr_muls()))
359 }
360 }
361 g.expr(ast.Expr(node.left))
362 if left.typ.has_flag(.shared_f) {
363 g.write('->val')
364 }
365 g.write(', ')
366 if right.typ.is_ptr() && !right.typ.has_flag(.shared_f) {
367 if node.right !is ast.ArrayInit {
368 g.write('*'.repeat(right.typ.nr_muls()))
369 }
370 }
371 g.expr(ast.Expr(node.right))
372 if right.typ.has_flag(.shared_f) {
373 g.write('->val')
374 }
375 g.write(')')
376 }
377 .array_fixed {
378 ptr_typ := g.equality_fn(left.unaliased)
379 if node.op == .ne {
380 g.write('!')
381 }
382 g.write('${ptr_typ}_arr_eq(')
383 if left.typ.is_ptr() {
384 g.write('*')
385 }
386 if node.left is ast.ArrayInit {
387 if !node.left.has_index {
388 s := g.styp(left.unaliased)
389 g.write('(${s})')
390 }
391 } else if node.left is ast.StructInit
392 && left.unaliased_sym.is_primitive_fixed_array() {
393 s := g.styp(left.unaliased)
394 g.write('(${s})')
395 }
396 g.expr(node.left)
397 g.write(', ')
398 if node.right is ast.ArrayInit {
399 if !node.right.has_index {
400 s := g.styp(right.unaliased)
401 g.write('(${s})')
402 }
403 } else if node.right is ast.StructInit
404 && right.unaliased_sym.is_primitive_fixed_array() {
405 s := g.styp(right.unaliased)
406 g.write('(${s})')
407 }
408 g.expr(ast.Expr(node.right))
409 g.write(')')
410 }
411 .map {
412 ptr_typ := g.equality_fn(left.unaliased)
413 if node.op == .ne {
414 g.write('!')
415 }
416 g.write('${ptr_typ}_map_eq(')
417 if left.typ.is_ptr() {
418 g.write('*'.repeat(left.typ.nr_muls()))
419 }
420 g.expr(ast.Expr(node.left))
421 g.write(', ')
422 if right.typ.is_ptr() {
423 g.write('*'.repeat(right.typ.nr_muls()))
424 }
425 g.expr(ast.Expr(node.right))
426 g.write(')')
427 }
428 .struct {
429 if left_is_option && right_is_option {
430 bare_typ := g.equality_fn(left.unaliased.clear_flag(.option).set_nr_muls(0))
431 old_inside_opt_or_res := g.inside_opt_or_res
432 g.inside_opt_or_res = true
433 inside_and_rhs := g.infix_left_var_name.len > 0
434 mut lv := ''
435 mut rv := ''
436 if inside_and_rhs {
437 lv = g.expr_string(node.left)
438 rv = g.expr_string(node.right)
439 } else {
440 left_tmp := g.expr_to_ctemp_before_stmt(node.left, left_type)
441 right_tmp := g.expr_to_ctemp_before_stmt(node.right, right_type)
442 lv = left_tmp.name
443 rv = right_tmp.name
444 }
445 if node.op == .eq {
446 g.write('(')
447 } else {
448 g.write('!(')
449 }
450 g.write('(${lv}.state == 2 && ${rv}.state == 2) || ')
451 g.write('(${lv}.state == ${rv}.state && ${lv}.state != 2 && ')
452 if left.typ.is_ptr() {
453 ptr_styp := g.styp(left.unaliased.clear_flag(.option))
454 nr_muls := left.typ.nr_muls()
455 deref := '*'.repeat(nr_muls)
456 lp := '*(${ptr_styp}*)&${lv}.data'
457 rp := '*(${ptr_styp}*)&${rv}.data'
458 g.write('(${lp} == ${rp} || (${lp} != 0 && ${rp} != 0 && ')
459 g.write('${bare_typ}_struct_eq(${deref}${lp}, ${deref}${rp})')
460 g.write('))')
461 } else {
462 styp := g.base_type(left_type)
463 g.write('${bare_typ}_struct_eq(*(${styp}*)&${lv}.data, *(${styp}*)&${rv}.data)')
464 }
465 g.write('))')
466 g.inside_opt_or_res = old_inside_opt_or_res
467 } else {
468 ptr_typ := g.equality_fn(left.unaliased)
469 if left.typ.is_ptr() || right.typ.is_ptr() {
470 left_is_addr_of_lvalue := node.left is ast.PrefixExpr
471 && node.left.op == .amp && node.left.right.is_lvalue()
472 right_is_addr_of_lvalue := node.right is ast.PrefixExpr
473 && node.right.op == .amp && node.right.right.is_lvalue()
474 if left.typ.is_ptr() && right.typ.is_ptr()
475 && (left_is_addr_of_lvalue || right_is_addr_of_lvalue) {
476 g.gen_plain_infix_expr(node)
477 } else {
478 g.gen_struct_pointer_eq_op(node, left_type, right_type, ptr_typ)
479 }
480 } else {
481 if node.op == .ne {
482 g.write('!')
483 }
484 g.write('${ptr_typ}_struct_eq(')
485 g.expr(node.left)
486 g.write(', ')
487 g.expr(node.right)
488 g.write(')')
489 }
490 }
491 }
492 .sum_type {
493 ptr_typ := g.equality_fn(left.unaliased)
494 if node.op == .ne {
495 g.write('!')
496 }
497 tmp_left_is_opt := g.left_is_opt
498 g.left_is_opt = true
499 g.write('${ptr_typ}_sumtype_eq(')
500 if left.typ.is_ptr() {
501 g.write('*'.repeat(left.typ.nr_muls()))
502 }
503 g.expr(ast.Expr(node.left))
504 g.write(', ')
505 if right.typ.is_ptr() {
506 g.write('*'.repeat(right.typ.nr_muls()))
507 }
508 g.expr(node.right)
509 g.write(')')
510 g.left_is_opt = tmp_left_is_opt
511 }
512 .interface {
513 ptr_typ := g.equality_fn(left.unaliased)
514 if node.op == .ne {
515 g.write('!')
516 }
517 g.write('${ptr_typ}_interface_eq(')
518 if left.typ.is_ptr() {
519 g.write('*'.repeat(left.typ.nr_muls()))
520 }
521 g.expr(node.left)
522 g.write(', ')
523 if right.typ.is_ptr() {
524 g.write('*'.repeat(right.typ.nr_muls()))
525 }
526 g.expr(node.right)
527 g.write(')')
528 }
529 else {
530 g.gen_plain_infix_expr(node)
531 }
532 }
533 } else if left.unaliased.idx() in [ast.u32_type_idx, ast.u64_type_idx]
534 && right.unaliased.is_signed() {
535 g.gen_safe_integer_infix_expr(
536 op: node.op
537 unsigned_type: left.unaliased
538 unsigned_expr: node.left
539 signed_type: right.unaliased
540 signed_expr: node.right
541 )
542 } else if right.unaliased.idx() in [ast.u32_type_idx, ast.u64_type_idx]
543 && left.unaliased.is_signed() {
544 g.gen_safe_integer_infix_expr(
545 op: node.op
546 reverse: true
547 unsigned_type: right.unaliased
548 unsigned_expr: node.right
549 signed_type: left.unaliased
550 signed_expr: node.left
551 )
552 } else if left_is_option && right_is_option {
553 old_inside_opt_or_res := g.inside_opt_or_res
554 g.inside_opt_or_res = true
555 if node.op == .eq {
556 g.write('(')
557 } else {
558 g.write('!(')
559 }
560 g.write('(')
561 g.expr(node.left)
562 g.write('.state == 2 && ')
563 g.expr(node.right)
564 g.write('.state == 2) || (')
565 g.expr(node.left)
566 g.write('.state == ')
567 g.expr(node.right)
568 g.write('.state && ')
569 g.expr(node.left)
570 g.write('.state != 2 && !memcmp(&')
571 g.expr(node.left)
572 g.write('.data, &')
573 g.expr(node.right)
574 g.write('.data, sizeof(${g.base_type(left_type)}))))')
575 g.inside_opt_or_res = old_inside_opt_or_res
576 } else {
577 g.gen_plain_infix_expr(node)
578 }
579}
580
581fn (mut g Gen) gen_struct_pointer_eq_op(node ast.InfixExpr, left_type ast.Type, right_type ast.Type, ptr_typ string) {
582 // When inside a short-circuit `&&` condition (infix_left_var_name is set),
583 // do not hoist temp vars before the containing statement, as that would
584 // evaluate expressions (e.g. as-casts from smartcasts) before the
585 // short-circuit check has run. Instead, use inline expressions.
586 inside_and_rhs := g.infix_left_var_name.len > 0
587 mut stmt_str := ''
588 mut restore_stmt := false
589 mut left_expr := ''
590 mut right_expr := ''
591 if left_type.is_ptr() && !node.left.is_lvalue() && !inside_and_rhs {
592 if !restore_stmt {
593 stmt_str = g.go_before_last_stmt().trim_space()
594 g.empty_line = true
595 restore_stmt = true
596 }
597 mut left_tmp := g.new_ctemp_var(node.left, left_type)
598 g.gen_ctemp_var(mut left_tmp)
599 left_expr = left_tmp.name
600 } else {
601 left_expr = g.expr_string(node.left)
602 }
603 if right_type.is_ptr() && !node.right.is_lvalue() && !inside_and_rhs {
604 if !restore_stmt {
605 stmt_str = g.go_before_last_stmt().trim_space()
606 g.empty_line = true
607 restore_stmt = true
608 }
609 mut right_tmp := g.new_ctemp_var(node.right, right_type)
610 g.gen_ctemp_var(mut right_tmp)
611 right_expr = right_tmp.name
612 } else {
613 right_expr = g.expr_string(node.right)
614 }
615 if restore_stmt {
616 g.write(stmt_str)
617 }
618 if node.op == .ne {
619 g.write('!')
620 }
621 g.write('(')
622 if left_type.is_ptr() && right_type.is_ptr() {
623 g.write('${left_expr} == ${right_expr} || (${left_expr} != 0 && ${right_expr} != 0 && ')
624 g.write('${ptr_typ}_struct_eq(')
625 g.write('*'.repeat(left_type.nr_muls()))
626 g.write(left_expr)
627 g.write(', ')
628 g.write('*'.repeat(right_type.nr_muls()))
629 g.write(right_expr)
630 g.write('))')
631 } else if left_type.is_ptr() {
632 g.write('${left_expr} != 0 && ${ptr_typ}_struct_eq(')
633 g.write('*'.repeat(left_type.nr_muls()))
634 g.write(left_expr)
635 g.write(', ${right_expr})')
636 } else {
637 g.write('${right_expr} != 0 && ${ptr_typ}_struct_eq(${left_expr}, ')
638 g.write('*'.repeat(right_type.nr_muls()))
639 g.write('${right_expr})')
640 }
641 g.write(')')
642}
643
644// infix_expr_cmp_op generates code for `<`, `<=`, `>`, `>=`
645// It handles operator overloading when necessary
646fn (mut g Gen) infix_expr_cmp_op(node ast.InfixExpr) {
647 mut left_type := g.resolved_expr_type(node.left, node.left_type)
648 if left_type == 0 {
649 left_type = node.left_type
650 }
651 left_type = g.unwrap_generic(g.recheck_concrete_type(left_type))
652 mut right_type := g.resolved_expr_type(node.right, node.right_type)
653 if right_type == 0 {
654 right_type = node.right_type
655 }
656 right_type = g.unwrap_generic(g.recheck_concrete_type(right_type))
657 left := g.unwrap(left_type)
658 right := g.unwrap(right_type)
659
660 mut has_operator_overloading := false
661 mut operator_expects_ptr := false
662 mut operator_method := ast.Fn{}
663 if m := g.table.find_method(left.sym, '<') {
664 has_operator_overloading = true
665 operator_expects_ptr = m.receiver_type.is_ptr()
666 operator_method = m
667 } else if m := left.sym.find_method_with_generic_parent('<') {
668 has_operator_overloading = true
669 operator_expects_ptr = m.receiver_type.is_ptr()
670 operator_method = m
671 }
672
673 if g.pref.translated && !g.is_builtin_mod {
674 g.gen_plain_infix_expr(node)
675 return
676 }
677 if left.sym.kind == .struct && (left.sym.info as ast.Struct).generic_types.len > 0 {
678 if node.op in [.le, .ge] {
679 g.write('!')
680 }
681 concrete_types := (left.sym.info as ast.Struct).concrete_types
682 mut method_name := '${left.sym.cname}__lt'
683 if left.unaliased_sym.is_builtin() {
684 method_name = 'builtin__${method_name}'
685 }
686 specialized_suffix := g.generic_fn_name(concrete_types, '')
687 if specialized_suffix != '' && !method_name.ends_with(specialized_suffix) {
688 method_name = g.generic_fn_name(concrete_types, method_name)
689 }
690 method_name = g.specialized_method_name_from_receiver(operator_method, left.typ,
691 method_name)
692 g.write(method_name)
693 if node.op in [.lt, .ge] {
694 g.write2('(', '*'.repeat(left.typ.nr_muls()))
695 if operator_expects_ptr {
696 g.write('&')
697 }
698 g.expr(ast.Expr(node.left))
699 g.write2(', ', '*'.repeat(right.typ.nr_muls()))
700 if operator_expects_ptr {
701 g.write('&')
702 }
703 g.expr(node.right)
704 g.write(')')
705 } else {
706 g.write2('(', '*'.repeat(right.typ.nr_muls()))
707 if operator_expects_ptr {
708 g.write('&')
709 }
710 g.expr(node.right)
711 g.write2(', ', '*'.repeat(left.typ.nr_muls()))
712 if operator_expects_ptr {
713 g.write('&')
714 }
715 g.expr(node.left)
716 g.write(')')
717 }
718 } else if left.unaliased_sym.kind == right.unaliased_sym.kind && has_operator_overloading {
719 if node.op in [.le, .ge] {
720 g.write('!')
721 }
722 mut method_name := '${g.styp(left.typ.set_nr_muls(0))}__lt'
723 if left.unaliased_sym.is_builtin() {
724 method_name = 'builtin__${method_name}'
725 }
726 method_name = g.specialized_method_name_from_receiver(operator_method, left.typ,
727 method_name)
728 g.write(method_name)
729 if node.op in [.lt, .ge] {
730 g.write2('(', '*'.repeat(left.typ.nr_muls()))
731 if operator_expects_ptr {
732 g.write('&')
733 }
734 if node.left is ast.ArrayInit && g.table.sym(node.left_type).kind == .array_fixed {
735 g.fixed_array_init_with_cast(node.left, left_type)
736 } else {
737 g.expr(node.left)
738 }
739 g.write2(', ', '*'.repeat(right.typ.nr_muls()))
740 if operator_expects_ptr {
741 g.write('&')
742 }
743 if node.right is ast.ArrayInit && g.table.sym(node.right_type).kind == .array_fixed {
744 g.fixed_array_init_with_cast(node.right, right_type)
745 } else {
746 g.expr(node.right)
747 }
748 g.write(')')
749 } else {
750 g.write2('(', '*'.repeat(right.typ.nr_muls()))
751 if operator_expects_ptr {
752 g.write('&')
753 }
754 g.expr(node.right)
755 g.write2(', ', '*'.repeat(left.typ.nr_muls()))
756 if operator_expects_ptr {
757 g.write('&')
758 }
759 g.expr(node.left)
760 g.write(')')
761 }
762 } else if left.unaliased.idx() in [ast.u32_type_idx, ast.u64_type_idx]
763 && right.unaliased.is_signed() {
764 g.gen_safe_integer_infix_expr(
765 op: node.op
766 unsigned_type: left.unaliased
767 unsigned_expr: node.left
768 signed_type: right.unaliased
769 signed_expr: node.right
770 )
771 } else if right.unaliased.idx() in [ast.u32_type_idx, ast.u64_type_idx]
772 && left.unaliased.is_signed() {
773 g.gen_safe_integer_infix_expr(
774 op: node.op
775 reverse: true
776 unsigned_type: right.unaliased
777 unsigned_expr: node.right
778 signed_type: left.unaliased
779 signed_expr: node.left
780 )
781 } else {
782 g.gen_plain_infix_expr(node)
783 }
784}
785
786fn (mut g Gen) infix_expr_in_sumtype_interface_array(infix_exprs []ast.InfixExpr) {
787 for i in 0 .. infix_exprs.len {
788 g.infix_expr_is_op(infix_exprs[i])
789 if i != infix_exprs.len - 1 {
790 g.write(' || ')
791 }
792 }
793}
794
795// infix_expr_in_op generates code for `in` and `!in`
796fn (mut g Gen) infix_expr_in_op(node ast.InfixExpr) {
797 mut left_type := node.left_type
798 mut right_type := node.right_type
799 // In generic contexts, AST-stored types may be stale from a previous instantiation.
800 if g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0 {
801 resolved_left := g.resolved_expr_type(node.left, node.left_type)
802 if resolved_left != 0 {
803 resolved_sym := g.table.sym(resolved_left)
804 left_sym := g.table.sym(left_type)
805 if resolved_sym.kind !in [.sum_type, .interface]
806 || left_sym.kind in [.sum_type, .interface] {
807 left_type = resolved_left
808 }
809 }
810 resolved_right := g.resolved_expr_type(node.right, node.right_type)
811 if resolved_right != 0 {
812 resolved_sym := g.table.sym(resolved_right)
813 right_sym := g.table.sym(right_type)
814 if resolved_sym.kind !in [.sum_type, .interface]
815 || right_sym.kind in [.sum_type, .interface] {
816 right_type = resolved_right
817 }
818 }
819 }
820 left := g.unwrap(left_type)
821 right := g.unwrap(right_type)
822 if node.op == .not_in {
823 g.write('!')
824 }
825 if right.unaliased_sym.kind == .array {
826 if left.sym.kind in [.sum_type, .interface] {
827 if node.right is ast.ArrayInit {
828 if node.right.exprs.len > 0
829 && g.table.sym(node.right.expr_types[0]).kind !in [.sum_type, .interface] {
830 mut infix_exprs := []ast.InfixExpr{}
831 for i in 0 .. node.right.exprs.len {
832 infix_exprs << ast.InfixExpr{
833 op: .key_is
834 left: node.left
835 left_type: node.left_type
836 right: node.right.exprs[i]
837 right_type: node.right.expr_types[i]
838 }
839 }
840 g.write('(')
841 g.infix_expr_in_sumtype_interface_array(infix_exprs)
842 g.write(')')
843 return
844 }
845 }
846 }
847 if node.right is ast.ArrayInit {
848 elem_type := node.right.elem_type
849 elem_sym := g.table.sym(elem_type)
850 // TODO: replace ast.Ident check with proper side effect analysis
851 if node.right.exprs.len > 0 && (node.left is ast.Ident
852 || node.left is ast.IndexExpr || node.left is ast.SelectorExpr) {
853 // `a in [1,2,3]` optimization => `a == 1 || a == 2 || a == 3`
854 // avoids an allocation
855 g.write('(')
856 if elem_sym.kind == .sum_type && left.sym.kind != .sum_type {
857 if node.left_type in elem_sym.sumtype_info().variants {
858 new_node_left := ast.CastExpr{
859 arg: ast.empty_expr
860 typ: elem_type
861 expr: node.left
862 expr_type: node.left_type
863 }
864 g.infix_expr_in_optimization(new_node_left, node.left_type, node.right)
865 }
866 } else {
867 g.infix_expr_in_optimization(node.left, node.left_type, node.right)
868 }
869 g.write(')')
870 return
871 }
872 }
873 if right.sym.info is ast.Array {
874 elem_type := right.sym.info.elem_type
875 elem_type_ := g.unwrap(elem_type)
876 if elem_type_.sym.kind == .sum_type {
877 if ast.mktyp(node.left_type) in elem_type_.sym.sumtype_info().variants {
878 new_node_left := ast.CastExpr{
879 arg: ast.empty_expr
880 typ: elem_type
881 expr: node.left
882 expr_type: ast.mktyp(node.left_type)
883 }
884 g.write('(')
885 g.gen_array_contains(node.right_type, node.right, elem_type, new_node_left)
886 g.write(')')
887 return
888 }
889 } else if elem_type_.sym.kind == .interface {
890 new_node_left := ast.CastExpr{
891 arg: ast.empty_expr
892 typ: elem_type
893 expr: node.left
894 expr_type: ast.mktyp(node.left_type)
895 }
896 g.write('(')
897 g.gen_array_contains(node.right_type, node.right, elem_type, new_node_left)
898 g.write(')')
899 return
900 }
901 }
902 g.write('(')
903 g.gen_array_contains(right_type, node.right, left_type, node.left)
904 g.write(')')
905 } else if right.unaliased_sym.kind == .map {
906 g.write('_IN_MAP(')
907 if !left.typ.is_ptr() {
908 mut sym_map := g.table.sym(right_type)
909 if sym_map.info is ast.Alias {
910 sym_map = g.table.sym((sym_map.info as ast.Alias).parent_type)
911 }
912 styp := g.styp(if sym_map.info is ast.Map {
913 (sym_map.info as ast.Map).key_type
914 } else {
915 node.left_type
916 })
917 if g.table.final_sym(node.left_type).kind == .array_fixed && node.left is ast.Ident {
918 g.expr(node.left)
919 } else {
920 g.write('ADDR(${styp}, ')
921 g.expr(node.left)
922 g.write(')')
923 }
924 } else {
925 g.expr(node.left)
926 }
927 g.write(', ')
928 if !right.typ.is_ptr() || right.typ.has_flag(.shared_f) {
929 g.write('ADDR(map, ')
930 g.expr(node.right)
931 if right.typ.has_flag(.shared_f) {
932 g.write('->val')
933 }
934 g.write(')')
935 } else {
936 g.expr(node.right)
937 }
938 g.write(')')
939 } else if right.unaliased_sym.kind == .array_fixed {
940 if left.sym.kind in [.sum_type, .interface] {
941 if node.right is ast.ArrayInit {
942 if node.right.exprs.len > 0 {
943 mut infix_exprs := []ast.InfixExpr{}
944 for i in 0 .. node.right.exprs.len {
945 infix_exprs << ast.InfixExpr{
946 op: .key_is
947 left: node.left
948 left_type: node.left_type
949 right: node.right.exprs[i]
950 right_type: node.right.expr_types[i]
951 }
952 }
953 g.write('(')
954 g.infix_expr_in_sumtype_interface_array(infix_exprs)
955 g.write(')')
956 return
957 }
958 }
959 }
960 if node.right is ast.ArrayInit {
961 if node.right.exprs.len > 0 {
962 // `a in [1,2,3]!` optimization => `a == 1 || a == 2 || a == 3`
963 // avoids an allocation
964 g.write('(')
965 g.infix_expr_in_optimization(node.left, node.left_type, node.right)
966 g.write(')')
967 return
968 }
969 }
970 if right.sym.info is ast.ArrayFixed {
971 elem_type := right.sym.info.elem_type
972 elem_type_ := g.unwrap(elem_type)
973 if elem_type_.sym.kind == .sum_type {
974 if ast.mktyp(node.left_type) in elem_type_.sym.sumtype_info().variants {
975 new_node_left := ast.CastExpr{
976 arg: ast.empty_expr
977 typ: elem_type
978 expr: node.left
979 expr_type: ast.mktyp(node.left_type)
980 }
981 g.write('(')
982 g.gen_array_contains(node.right_type, node.right, elem_type, new_node_left)
983 g.write(')')
984 return
985 }
986 }
987 }
988 g.write('(')
989 g.gen_array_contains(right_type, node.right, left_type, node.left)
990 g.write(')')
991 } else if right.unaliased_sym.kind == .string && node.right !is ast.RangeExpr {
992 g.write2('(', 'string_contains(')
993 g.expr(node.right)
994 g.write(', ')
995 g.expr(node.left)
996 g.write('))')
997 } else if node.right is ast.RangeExpr {
998 // call() in min..max
999 if node.left is ast.CallExpr {
1000 line := g.go_before_last_stmt().trim_space()
1001 g.empty_line = true
1002 tmp_var := g.new_tmp_var()
1003 g.write('${g.styp(node.left.return_type)} ${tmp_var} = ')
1004 g.expr(ast.Expr(node.left))
1005 g.writeln(';')
1006 g.write(line)
1007 g.write('(')
1008 g.write('${tmp_var} >= ')
1009 g.expr(node.right.low)
1010 g.write(' && ')
1011 g.write('${tmp_var} < ')
1012 g.expr(node.right.high)
1013 g.write(')')
1014 } else {
1015 g.write('(')
1016 g.expr(node.left)
1017 g.write(' >= ')
1018 g.expr(node.right.low)
1019 g.write(' && ')
1020 g.expr(node.left)
1021 g.write(' < ')
1022 g.expr(node.right.high)
1023 g.write(')')
1024 }
1025 }
1026}
1027
1028// infix_expr_in_optimization optimizes `<var> in <array>` expressions,
1029// and transform them in a series of equality comparison
1030// i.e. `a in [1,2,3]` => `a == 1 || a == 2 || a == 3`
1031fn (mut g Gen) infix_expr_in_optimization(left ast.Expr, left_type ast.Type, right ast.ArrayInit) {
1032 tmp_var := if left is ast.CallExpr { g.new_tmp_var() } else { '' }
1033 mut elem_sym := g.table.sym(right.elem_type)
1034 left_parent_idx := g.table.sym(left_type).parent_idx
1035 for i, array_expr in right.exprs {
1036 match elem_sym.kind {
1037 .string, .alias, .sum_type, .map, .interface, .array, .struct {
1038 if elem_sym.kind == .string {
1039 is_auto_deref_var := left.is_auto_deref_var()
1040 if left is ast.Ident && left.or_expr.kind == .absent
1041 && array_expr is ast.StringLiteral {
1042 var := g.expr_string(left)
1043 slit :=
1044 cescape_nonascii(util.smart_quote(array_expr.val, array_expr.is_raw))
1045 mut needs_deref := false
1046 if left.info is ast.IdentVar && left.obj is ast.Var {
1047 if g.table.sym(left.obj.typ).kind in [.interface, .sum_type] {
1048 needs_deref = left.obj.smartcasts.len == 0
1049 }
1050 }
1051 if is_auto_deref_var || needs_deref {
1052 g.write('_SLIT_EQ(${var}->str, ${var}->len, "${slit}")')
1053 } else {
1054 g.write('_SLIT_EQ(${var}.str, ${var}.len, "${slit}")')
1055 }
1056 if i < right.exprs.len - 1 {
1057 g.write(' || ')
1058 }
1059 continue
1060 } else if array_expr is ast.StringLiteral {
1061 g.write('builtin__fast_string_eq(')
1062 } else {
1063 g.write('builtin__string__eq(')
1064 }
1065 if is_auto_deref_var || (left is ast.Ident && left.info is ast.IdentVar
1066 && g.table.sym(left.obj.typ).kind in [.interface, .sum_type]) {
1067 g.write('*')
1068 }
1069 } else {
1070 ptr_typ := g.equality_fn(right.elem_type)
1071 if elem_sym.kind == .alias {
1072 // optimization for alias to number
1073 if elem_sym.is_int() {
1074 g.expr(left)
1075 g.write(' == ')
1076 if left_parent_idx != 0 && !((array_expr is ast.SelectorExpr
1077 && array_expr.typ == left_type)
1078 || (array_expr is ast.Ident && array_expr.obj.typ == left_type)) {
1079 g.write('(${g.styp(left_parent_idx)})')
1080 }
1081 g.expr(array_expr)
1082 if i < right.exprs.len - 1 {
1083 g.write(' || ')
1084 }
1085 continue
1086 } else {
1087 g.write('${ptr_typ}_alias_eq(')
1088 }
1089 } else if elem_sym.kind == .sum_type {
1090 g.write('${ptr_typ}_sumtype_eq(')
1091 } else if elem_sym.kind == .map {
1092 g.write('${ptr_typ}_map_eq(')
1093 } else if elem_sym.kind == .interface {
1094 g.write('${ptr_typ}_interface_eq(')
1095 } else if elem_sym.kind == .array {
1096 g.write('${ptr_typ}_arr_eq(')
1097 } else if elem_sym.kind == .struct {
1098 g.write('${ptr_typ}_struct_eq(')
1099 }
1100 }
1101 if left is ast.CallExpr {
1102 if i == 0 {
1103 line := g.go_before_last_stmt().trim_space()
1104 g.empty_line = true
1105 g.write('${g.styp(left.return_type)} ${tmp_var} = ')
1106 g.expr(left)
1107 g.writeln(';')
1108 g.write2(line, tmp_var)
1109 } else {
1110 g.write(tmp_var)
1111 }
1112 } else {
1113 g.expr(left)
1114 }
1115 g.write(', ')
1116 g.expr(array_expr)
1117 g.write(')')
1118 }
1119 else { // works in function kind
1120 if left is ast.CallExpr {
1121 if i == 0 {
1122 line := g.go_before_last_stmt().trim_space()
1123 g.empty_line = true
1124 g.write('${g.styp(left.return_type)} ${tmp_var} = ')
1125 g.expr(left)
1126 g.writeln(';')
1127 g.write2(line, tmp_var)
1128 } else {
1129 g.write(tmp_var)
1130 }
1131 } else {
1132 g.expr(left)
1133 }
1134 g.write(' == ')
1135 if elem_sym.kind == .array_fixed {
1136 g.write('(${g.styp(right.elem_type)})')
1137 }
1138 g.expr(array_expr)
1139 }
1140 }
1141
1142 if i < right.exprs.len - 1 {
1143 g.write(' || ')
1144 }
1145 }
1146}
1147
1148fn (mut g Gen) type_tag_expr_for_is_left(node ast.InfixExpr, is_aggregate bool, is_orig_sumtype bool) string {
1149 mut left_expr := ''
1150 if is_aggregate {
1151 left_expr = '${node.left}'
1152 } else {
1153 if is_orig_sumtype {
1154 g.prevent_sum_type_unwrapping_once = true
1155 }
1156 left_expr = g.expr_string(node.left)
1157 }
1158 left_value_expr := if node.left_type.nr_muls() > 1 {
1159 '(${'*'.repeat(node.left_type.nr_muls() - 1)}${left_expr})'
1160 } else {
1161 '(${left_expr})'
1162 }
1163 dot_or_ptr := if node.left_type.is_ptr() { '->' } else { '.' }
1164 return '${left_value_expr}${dot_or_ptr}_typ'
1165}
1166
1167fn (mut g Gen) write_type_tag_expr_for_is_left(node ast.InfixExpr, is_aggregate bool, is_orig_sumtype bool) {
1168 g.write('(')
1169 if node.left_type.nr_muls() > 1 {
1170 g.write('*'.repeat(node.left_type.nr_muls() - 1))
1171 }
1172 if is_aggregate {
1173 g.write('${node.left}')
1174 } else if is_orig_sumtype {
1175 g.prevent_sum_type_unwrapping_once = true
1176 g.expr(node.left)
1177 } else {
1178 g.expr(node.left)
1179 }
1180 g.write(')')
1181 if node.left_type.is_ptr() {
1182 g.write('->')
1183 } else {
1184 g.write('.')
1185 }
1186 g.write('_typ')
1187}
1188
1189fn (mut g Gen) write_is_type_tag_condition(node ast.InfixExpr, is_aggregate bool, is_orig_sumtype bool, cmp_op string, index_exprs []string) {
1190 if index_exprs.len == 0 {
1191 g.write(if cmp_op == '==' { 'false' } else { 'true' })
1192 return
1193 }
1194 if index_exprs.len == 1 {
1195 g.write_type_tag_expr_for_is_left(node, is_aggregate, is_orig_sumtype)
1196 g.write(' ${cmp_op} ${index_exprs[0]}')
1197 return
1198 }
1199 if is_aggregate {
1200 tag_expr := g.type_tag_expr_for_is_left(node, is_aggregate, is_orig_sumtype)
1201 g.write_type_tag_condition(tag_expr, cmp_op, index_exprs)
1202 return
1203 }
1204 tag_tmp := g.new_tmp_var()
1205 if !g.is_cc_msvc {
1206 g.write('({ ${ast.int_type_name} ${tag_tmp} = ')
1207 g.write_type_tag_expr_for_is_left(node, is_aggregate, is_orig_sumtype)
1208 g.write('; ')
1209 g.write_type_tag_condition(tag_tmp, cmp_op, index_exprs)
1210 g.write('; })')
1211 return
1212 }
1213 mut cur_line := if g.inside_ternary > 0 {
1214 g.go_before_ternary().trim_space()
1215 } else {
1216 g.go_before_last_stmt().trim_space()
1217 }
1218 if g.inside_return && cur_line.ends_with('return') {
1219 cur_line += ' '
1220 }
1221 g.empty_line = true
1222 g.write('${ast.int_type_name} ${tag_tmp} = ')
1223 g.write_type_tag_expr_for_is_left(node, is_aggregate, is_orig_sumtype)
1224 g.writeln(';')
1225 g.write(cur_line)
1226 g.write_type_tag_condition(tag_tmp, cmp_op, index_exprs)
1227}
1228
1229// infix_expr_is_op generates code for `is` and `!is`
1230fn (mut g Gen) infix_expr_is_op(node ast.InfixExpr) {
1231 mut left_sym := g.table.final_sym(g.unwrap_generic(g.type_resolver.get_type_or_default(node.left,
1232 node.left_type)))
1233 is_aggregate := node.left is ast.Ident && g.comptime.get_ct_type_var(node.left) == .aggregate
1234 mut right_type := g.unwrap_generic(g.recheck_concrete_type(node.right_type))
1235 if right_type.is_ptr() && g.table.final_sym(right_type.deref()).kind == .interface {
1236 right_type = right_type.deref()
1237 }
1238 right_sym := g.table.final_sym(right_type)
1239 // When the LHS is a smartcast variable whose original type is a sum type,
1240 // use the original sum type so the `is` check works on the tag field.
1241 // But only when the smartcast target is NOT itself a sum type — for nested
1242 // sum types (e.g., Outer→Inner→MyStruct), normal smartcast unwrapping
1243 // must proceed to generate the correct inner dereference.
1244 mut is_orig_sumtype := false
1245 if node.left is ast.Ident && node.left.obj is ast.Var {
1246 v := node.left.obj as ast.Var
1247 if v.smartcasts.len > 0 && v.is_mut && v.orig_type != 0 {
1248 orig_sym := g.table.final_sym(v.orig_type)
1249 smartcast_target_sym := g.table.final_sym(v.smartcasts.last())
1250 if orig_sym.kind == .sum_type && smartcast_target_sym.kind != .sum_type {
1251 left_sym = unsafe { orig_sym }
1252 is_orig_sumtype = true
1253 }
1254 }
1255 }
1256 if (left_sym.kind == .sum_type || is_aggregate) && node.left_type.nr_muls() > 0
1257 && right_type.nr_muls() <= node.left_type.nr_muls() {
1258 right_type = right_type.set_nr_muls(0)
1259 }
1260 if left_sym.kind == .interface && right_sym.kind == .interface {
1261 g.gen_interface_is_op(node)
1262 return
1263 }
1264
1265 cmp_op := if node.op == .key_is { '==' } else { '!=' }
1266 if left_sym.kind == .interface {
1267 sub_type := match node.right {
1268 ast.TypeNode {
1269 right_type
1270 }
1271 ast.None {
1272 ast.idx_to_type(g.table.type_idxs['None__'])
1273 }
1274 else {
1275 ast.no_type
1276 }
1277 }
1278
1279 g.write_is_type_tag_condition(node, is_aggregate, is_orig_sumtype, cmp_op, g.matching_interface_variant_index_exprs(left_sym,
1280 sub_type))
1281 return
1282 } else if left_sym.kind == .sum_type || is_aggregate {
1283 mut aggregate_parent_type := node.left_type
1284 if is_aggregate {
1285 aggregate_sym := g.table.sym(node.left_type)
1286 if aggregate_sym.info is ast.Aggregate {
1287 aggregate_parent_type = aggregate_sym.info.sum_type
1288 }
1289 }
1290 sumtype_parent_type := if is_orig_sumtype && node.left is ast.Ident
1291 && node.left.obj is ast.Var {
1292 (node.left.obj as ast.Var).orig_type
1293 } else if is_aggregate {
1294 aggregate_parent_type
1295 } else {
1296 node.left_type
1297 }
1298 if node.right is ast.None {
1299 g.write_is_type_tag_condition(node, is_aggregate, is_orig_sumtype, cmp_op, [
1300 '${ast.none_type.idx()}',
1301 ])
1302 } else if node.right is ast.Ident && node.right.name == g.comptime.comptime_for_variant_var {
1303 mut variant_idx := g.type_resolver.get_ct_type_or_default('${g.comptime.comptime_for_variant_var}.typ',
1304 ast.void_type)
1305 if (left_sym.kind == .sum_type || is_aggregate) && node.left_type.nr_muls() > 0
1306 && variant_idx.nr_muls() <= node.left_type.nr_muls() {
1307 variant_idx = variant_idx.set_nr_muls(0)
1308 }
1309 g.write_is_type_tag_condition(node, is_aggregate, is_orig_sumtype, cmp_op, g.matching_sumtype_variant_type_idx_exprs(sumtype_parent_type,
1310 variant_idx))
1311 } else if node.right is ast.TypeNode {
1312 g.write_is_type_tag_condition(node, is_aggregate, is_orig_sumtype, cmp_op, g.matching_sumtype_variant_type_idx_exprs(sumtype_parent_type,
1313 right_type))
1314 } else {
1315 g.write_type_tag_expr_for_is_left(node, is_aggregate, is_orig_sumtype)
1316 g.write(' ${cmp_op} ')
1317 g.expr(node.right)
1318 }
1319 return
1320 }
1321 if node.right is ast.None {
1322 g.write_type_tag_expr_for_is_left(node, is_aggregate, is_orig_sumtype)
1323 g.write(' ${cmp_op} ')
1324 g.write('${ast.none_type.idx()}')
1325 } else if node.right is ast.Ident && node.right.name == g.comptime.comptime_for_variant_var {
1326 variant_idx := g.type_resolver.get_ct_type_or_default('${g.comptime.comptime_for_variant_var}.typ',
1327 ast.void_type)
1328 g.write_type_tag_expr_for_is_left(node, is_aggregate, is_orig_sumtype)
1329 g.write(' ${cmp_op} ')
1330 if (left_sym.kind == .sum_type || is_aggregate) && node.left_type.nr_muls() > 0
1331 && variant_idx.nr_muls() <= node.left_type.nr_muls() {
1332 g.write('${int(variant_idx.set_nr_muls(0))}')
1333 } else {
1334 g.write('${int(variant_idx)}')
1335 }
1336 } else if node.right is ast.TypeNode {
1337 g.write_type_tag_expr_for_is_left(node, is_aggregate, is_orig_sumtype)
1338 g.write(' ${cmp_op} ')
1339 g.write('${int(right_type)}')
1340 } else {
1341 g.write_type_tag_expr_for_is_left(node, is_aggregate, is_orig_sumtype)
1342 g.write(' ${cmp_op} ')
1343 g.expr(node.right)
1344 }
1345}
1346
1347fn (mut g Gen) gen_interface_is_op(node ast.InfixExpr) {
1348 mut left_type := g.unwrap_generic(g.recheck_concrete_type(node.left_type))
1349 mut right_type := g.unwrap_generic(g.recheck_concrete_type(node.right_type))
1350 if left_type.is_ptr() && g.table.final_sym(left_type.deref()).kind == .interface {
1351 left_type = left_type.deref()
1352 }
1353 if right_type.is_ptr() && g.table.final_sym(right_type.deref()).kind == .interface {
1354 right_type = right_type.deref()
1355 }
1356 mut left_sym := g.table.final_sym(left_type)
1357 right_sym := g.table.final_sym(right_type)
1358
1359 mut info := left_sym.info as ast.Interface
1360 right_info := right_sym.info as ast.Interface
1361 left_variants := info.implementor_types(true)
1362 right_variants := right_info.implementor_types(true)
1363 lock info.conversions {
1364 common_variants := info.conversions[right_type] or {
1365 c := g.interface_conversion_variants(left_variants, right_variants)
1366 info.conversions[right_type] = c
1367 c
1368 }
1369 left_sym.info = info
1370 if common_variants.len == 0 {
1371 g.write('false')
1372 return
1373 }
1374 }
1375 g.write('I_${left_sym.cname}_is_I_${right_sym.cname}(')
1376 if node.left_type.is_ptr() {
1377 g.write('*')
1378 }
1379 g.expr(node.left)
1380 g.write(')')
1381}
1382
1383// infix_expr_arithmetic_op generates code for `+`, `-`, `*`, `/`, and `%`
1384// It handles operator overloading when necessary
1385fn (mut g Gen) is_string_type(typ ast.Type) bool {
1386 return g.unwrap(typ).unaliased_sym.kind == .string
1387}
1388
1389fn (mut g Gen) is_char_or_rune_string_concat_type(typ ast.Type) bool {
1390 return g.table.unaliased_type(g.unwrap_generic(typ)).clear_flags() in [ast.char_type, ast.rune_type]
1391}
1392
1393fn (mut g Gen) is_string_concat_type(typ ast.Type) bool {
1394 return g.is_string_type(typ) || g.is_char_or_rune_string_concat_type(typ)
1395}
1396
1397fn (mut g Gen) is_string_concat_infix(node ast.InfixExpr) bool {
1398 if node.op != .plus {
1399 return false
1400 }
1401 left_type := g.type_resolver.get_type_or_default(node.left, node.left_type)
1402 right_type := g.type_resolver.get_type_or_default(node.right, node.right_type)
1403 return g.is_string_concat_type(left_type) && g.is_string_concat_type(right_type)
1404 && (g.is_string_type(left_type) || g.is_string_type(right_type))
1405}
1406
1407fn (mut g Gen) collect_string_concat_parts(expr ast.Expr, mut parts []ast.Expr) {
1408 match expr {
1409 ast.InfixExpr {
1410 if g.is_string_concat_infix(expr) {
1411 g.collect_string_concat_parts(expr.left, mut parts)
1412 g.collect_string_concat_parts(expr.right, mut parts)
1413 return
1414 }
1415 }
1416 ast.ParExpr {
1417 g.collect_string_concat_parts(expr.expr, mut parts)
1418 return
1419 }
1420 else {}
1421 }
1422
1423 parts << expr
1424}
1425
1426fn (mut g Gen) gen_string_concat_many(node ast.InfixExpr) bool {
1427 if !g.is_string_concat_infix(node) {
1428 return false
1429 }
1430 mut parts := []ast.Expr{}
1431 g.collect_string_concat_parts(ast.Expr(node), mut parts)
1432 mut needs_plus_many := parts.len >= 3
1433 for part in parts {
1434 part_type := g.type_resolver.get_type_or_default(part, part.type())
1435 if !g.is_string_type(part_type) {
1436 needs_plus_many = true
1437 break
1438 }
1439 }
1440 if !needs_plus_many {
1441 return false
1442 }
1443 g.write('builtin__string_plus_many(${parts.len}, _MOV((string[${parts.len}]){')
1444 for i, part in parts {
1445 part_type := g.type_resolver.get_type_or_default(part, part.type())
1446 g.expr_with_cast(part, part_type, ast.string_type)
1447 if i < parts.len - 1 {
1448 g.write(', ')
1449 }
1450 }
1451 g.write('}))')
1452 return true
1453}
1454
1455fn (mut g Gen) infix_expr_arithmetic_op(node ast.InfixExpr) {
1456 if g.gen_string_concat_many(node) {
1457 return
1458 }
1459 left_type := g.resolved_expr_type(node.left, node.left_type)
1460 right_type := g.resolved_expr_type(node.right, node.right_type)
1461 $if trace_ci_fixes ? {
1462 left_str := g.table.type_to_str(left_type)
1463 right_str := g.table.type_to_str(right_type)
1464 if left_str.contains('MyStruct') || left_str.contains('MyAlias')
1465 || right_str.contains('MyStruct') || right_str.contains('MyAlias') {
1466 eprintln('cgen infix sum left=${g.table.type_to_str(left_type)} right=${g.table.type_to_str(right_type)} node_left=${g.table.type_to_str(node.left_type)} node_right=${g.table.type_to_str(node.right_type)} cur=${g.cur_concrete_types.map(g.table.type_to_str(it))}')
1467 }
1468 }
1469 left := g.unwrap(left_type)
1470 right := g.unwrap(right_type)
1471 if left.sym.info is ast.Struct && left.sym.info.generic_types.len > 0 {
1472 mut method_name := left.sym.cname + '_' + util.replace_op(node.op.str())
1473 mut concrete_types := left.sym.info.concrete_types.clone()
1474 resolved_left_sym := g.table.final_sym(left.typ)
1475 if resolved_left_sym.info is ast.Struct && resolved_left_sym.info.concrete_types.len > 0 {
1476 concrete_types = resolved_left_sym.info.concrete_types.clone()
1477 }
1478 specialized_suffix := g.generic_fn_name(concrete_types, '')
1479 if specialized_suffix != '' && !method_name.ends_with(specialized_suffix) {
1480 method_name = g.generic_fn_name(concrete_types, method_name)
1481 }
1482 if left.sym.is_builtin() {
1483 method_name = 'builtin__${method_name}'
1484 }
1485 g.write2(method_name, '(')
1486 g.expr(node.left)
1487 g.write(', ')
1488 g.expr(node.right)
1489 g.write(')')
1490 } else {
1491 mut method := ast.Fn{}
1492 mut method_name := ''
1493 if left.sym.has_method(node.op.str()) {
1494 method = left.sym.find_method(node.op.str()) or { ast.Fn{} }
1495 method_name = left.sym.cname + '_' + util.replace_op(node.op.str())
1496 if left.sym.is_builtin() {
1497 method_name = 'builtin__${method_name}'
1498 }
1499 } else if left.unaliased_sym.has_method_with_generic_parent(node.op.str()) {
1500 method = left.unaliased_sym.find_method_with_generic_parent(node.op.str()) or {
1501 ast.Fn{}
1502 }
1503 method_name = left.unaliased_sym.cname + '_' + util.replace_op(node.op.str())
1504 if left.unaliased_sym.is_builtin() {
1505 method_name = 'builtin__${method_name}'
1506 }
1507 if left.unaliased_sym.info is ast.Struct
1508 && left.unaliased_sym.info.generic_types.len > 0 {
1509 mut concrete_types := left.unaliased_sym.info.concrete_types.clone()
1510 resolved_left_sym := g.table.final_sym(left.typ)
1511 if resolved_left_sym.info is ast.Struct
1512 && resolved_left_sym.info.concrete_types.len > 0 {
1513 concrete_types = resolved_left_sym.info.concrete_types.clone()
1514 }
1515 method_name = g.generic_fn_name(concrete_types, method_name)
1516 }
1517 } else {
1518 g.gen_plain_infix_expr(node)
1519 return
1520 }
1521
1522 mut right_var := ''
1523 if node.right is ast.Ident && node.right.or_expr.kind != .absent {
1524 cur_line := g.go_before_last_stmt().trim_space()
1525 right_var = g.new_tmp_var()
1526 unwrapped_right_typ := right.typ.clear_option_and_result()
1527 g.write('${g.styp(unwrapped_right_typ)} ${right_var} = ')
1528 g.op_arg(ast.Expr(node.right), method.params[1].typ, unwrapped_right_typ)
1529 g.writeln(';')
1530 g.write(cur_line)
1531 }
1532 g.write2(method_name, '(')
1533 g.op_arg(node.left, method.params[0].typ, left.typ)
1534 if right_var != '' {
1535 g.write(', ${right_var}')
1536 } else {
1537 g.write(', ')
1538 g.op_arg(node.right, method.params[1].typ, right.typ)
1539 }
1540 g.write(')')
1541
1542 if left.typ != 0 && !left.typ.has_option_or_result()
1543 && g.table.final_sym(left.typ).kind == .array_fixed {
1544 // it's non-option fixed array, requires accessing .ret_arr member to get the array
1545 g.write('.ret_arr')
1546 }
1547 }
1548}
1549
1550// infix_expr_left_shift_op generates code for the `<<` operator
1551// This can either be a value pushed into an array or a bit shift
1552fn (mut g Gen) infix_expr_left_shift_op(node ast.InfixExpr) {
1553 mut left_type := if node.left is ast.ComptimeSelector {
1554 g.type_resolver.get_type(ast.Expr(node.left))
1555 } else {
1556 g.recheck_concrete_type(node.left_type)
1557 }
1558 resolved_left_type := g.resolved_expr_type(node.left, node.left_type)
1559 if resolved_left_type != 0 {
1560 left_type = g.unwrap_generic(g.recheck_concrete_type(resolved_left_type))
1561 } else if left_type == 0 || left_type.has_flag(.generic)
1562 || g.type_has_unresolved_generic_parts(left_type) {
1563 left_type = g.resolved_expr_type(node.left, node.left_type)
1564 }
1565 if left_type == 0 {
1566 left_type = node.left_type
1567 }
1568 mut right_type := if node.right is ast.ComptimeSelector {
1569 g.type_resolver.get_type(ast.Expr(node.right))
1570 } else {
1571 node.right_type
1572 }
1573 resolved_node_right_type := g.resolved_expr_type(node.right, node.right_type)
1574 if resolved_node_right_type != 0 {
1575 right_type = g.unwrap_generic(g.recheck_concrete_type(resolved_node_right_type))
1576 } else if right_type == 0 || right_type.has_flag(.generic)
1577 || g.type_has_unresolved_generic_parts(right_type) {
1578 right_type = g.resolved_expr_type(node.right, node.right_type)
1579 }
1580 if right_type == 0 {
1581 right_type = node.right_type
1582 }
1583 left := g.unwrap(left_type)
1584 right := g.unwrap(right_type)
1585 if left.unaliased_sym.kind == .array {
1586 // arr << val
1587 tmp_var := g.new_tmp_var()
1588 mut resolved_left := left
1589 concrete_left_type := g.recheck_concrete_type(left.typ)
1590 if concrete_left_type != 0 {
1591 resolved_left = g.unwrap(concrete_left_type)
1592 }
1593 if node.left is ast.Ident {
1594 scope_left_type := g.resolved_scope_var_type(node.left)
1595 if scope_left_type != 0 {
1596 resolved_left = g.unwrap(scope_left_type)
1597 }
1598 }
1599 array_info := if resolved_left.unaliased_sym.kind == .array {
1600 resolved_left.unaliased_sym.info as ast.Array
1601 } else {
1602 left.unaliased_sym.info as ast.Array
1603 }
1604 mut elem_type := g.unwrap_generic(g.recheck_concrete_type(array_info.elem_type))
1605 if elem_type == 0 || elem_type.has_flag(.generic)
1606 || g.type_has_unresolved_generic_parts(elem_type) {
1607 resolved_elem_type :=
1608 g.recheck_concrete_type(g.table.value_type(g.unwrap_generic(resolved_left.typ)))
1609 if resolved_elem_type != 0 {
1610 elem_type = g.unwrap_generic(resolved_elem_type)
1611 }
1612 }
1613 if elem_type == 0 {
1614 elem_type = array_info.elem_type
1615 }
1616 if node.left is ast.Ident {
1617 ident_elem_type := g.resolved_ident_array_elem_type(node.left)
1618 if ident_elem_type != 0 {
1619 elem_type = ident_elem_type
1620 }
1621 }
1622 if elem_type == ast.usize_type {
1623 name_elem_type := g.resolved_array_elem_type_from_name(resolved_left.sym.name)
1624 if name_elem_type != 0 {
1625 elem_type = name_elem_type
1626 }
1627 }
1628 if elem_type == ast.usize_type {
1629 call_elem_type := g.resolved_call_like_expr_type(node.right)
1630 if call_elem_type != 0
1631 && call_elem_type !in [ast.int_literal_type, ast.float_literal_type]
1632 && g.table.final_sym(call_elem_type).kind !in [.array, .map] {
1633 elem_type = call_elem_type
1634 }
1635 }
1636 if elem_type == ast.usize_type {
1637 mut candidate_elem_type := g.unwrap_generic(g.recheck_concrete_type(g.resolved_expr_type(node.right,
1638 node.right_type)))
1639 if candidate_elem_type == 0 {
1640 candidate_elem_type = g.unwrap_generic(g.recheck_concrete_type(node.right_type))
1641 }
1642 if candidate_elem_type != 0 && candidate_elem_type != ast.void_type
1643 && candidate_elem_type !in [ast.int_literal_type, ast.float_literal_type]
1644 && g.table.final_sym(candidate_elem_type).kind !in [.array, .map] {
1645 elem_type = candidate_elem_type
1646 }
1647 }
1648 mut elem_sym := g.table.final_sym(elem_type)
1649 if node.right is ast.StructInit && elem_sym.kind !in [.interface, .sum_type] {
1650 resolved_right_type := g.unwrap_generic(g.recheck_concrete_type(g.resolved_expr_type(ast.Expr(node.right),
1651 right.typ)))
1652 if resolved_right_type != 0
1653 && g.table.final_sym(resolved_right_type).kind == elem_sym.kind
1654 && g.table.type_to_str(resolved_right_type) == g.table.type_to_str(elem_type) {
1655 elem_type = resolved_right_type
1656 elem_sym = g.table.final_sym(elem_type)
1657 }
1658 }
1659 noscan := g.check_noscan(elem_type)
1660 elem_is_option := elem_type.has_flag(.option)
1661 mut prevent_push_many := g.table.sumtype_has_variant(elem_type, right_type, false)
1662 mut resolved_right_type := g.unwrap_generic(g.recheck_concrete_type(right_type))
1663 if resolved_right_type == 0 {
1664 resolved_right_type = g.unwrap_generic(right_type)
1665 }
1666 needs_explicit_deref := node.right is ast.Ident && resolved_right_type.is_ptr()
1667 && !elem_type.is_ptr() && !elem_type.is_pointer()
1668 rhs_is_any_value := elem_sym.kind == .any
1669 mut rhs_is_interface_value := elem_sym.kind == .interface
1670 && g.table.does_type_implement_interface(resolved_right_type, elem_type)
1671 if rhs_is_interface_value {
1672 // Don't prevent push_many when the right side is an array with matching
1673 // element type (e.g. []Foo << []Foo where Foo is an interface).
1674 resolved_right_sym := g.table.final_sym(resolved_right_type)
1675 if resolved_right_sym.kind == .array {
1676 right_elem := (resolved_right_sym.info as ast.Array).elem_type
1677 if right_elem == elem_type {
1678 rhs_is_interface_value = false
1679 }
1680 }
1681 }
1682 if rhs_is_any_value || rhs_is_interface_value {
1683 prevent_push_many = true
1684 }
1685 if prevent_push_many && node.right is ast.CallExpr && !rhs_is_any_value
1686 && !rhs_is_interface_value {
1687 // Allow concatenation for array-returning calls; avoids nesting for common builder APIs.
1688 prevent_push_many = false
1689 }
1690 if (right.unaliased_sym.kind == .array
1691 || (right.unaliased_sym.kind == .struct && right.unaliased_sym.name == 'array'))
1692 && resolved_left.sym.nr_dims() == right.sym.nr_dims() && elem_type != right.typ
1693 && !elem_is_option && !prevent_push_many {
1694 // push an array => PUSH_MANY, but not if pushing an array to 2d array (`[][]int << []int`)
1695 g.write('_PUSH_MANY${noscan}(')
1696 // The push macro needs the plain array type (not option/result),
1697 // since it declares a temp var of that type.
1698 mut expected_push_many_atype := resolved_left.typ.clear_option_and_result()
1699 is_shared := expected_push_many_atype.has_flag(.shared_f)
1700 if !expected_push_many_atype.is_ptr() {
1701 // fn f(mut a []int) { a << [1,2,3] } -> type of `a` is `array_int*` -> no need for &
1702 g.write('&')
1703 } else {
1704 expected_push_many_atype = expected_push_many_atype.deref()
1705 }
1706 if is_shared {
1707 g.write('&')
1708 }
1709 if is_shared {
1710 expected_push_many_atype = expected_push_many_atype.clear_flag(.shared_f)
1711 }
1712 expected_push_many_sym := g.table.final_sym(g.unwrap_generic(expected_push_many_atype))
1713 if expected_push_many_sym.kind == .array {
1714 mut push_many_elem_type :=
1715 g.unwrap_generic(g.recheck_concrete_type(expected_push_many_sym.array_info().elem_type))
1716 if push_many_elem_type == ast.int_literal_type {
1717 push_many_elem_type = ast.int_type
1718 } else if push_many_elem_type == ast.float_literal_type {
1719 push_many_elem_type = ast.f64_type
1720 }
1721 if push_many_elem_type != expected_push_many_sym.array_info().elem_type {
1722 expected_push_many_atype =
1723 ast.idx_to_type(g.table.find_or_register_array(push_many_elem_type))
1724 }
1725 }
1726 old_inside_left_shift := g.inside_left_shift
1727 g.inside_left_shift = true
1728 g.expr(node.left)
1729 g.inside_left_shift = old_inside_left_shift
1730 if node.left_type.has_flag(.shared_f) {
1731 g.write('->val')
1732 }
1733 if left.typ.is_ptr() && right.typ.is_ptr() {
1734 g.write(', *(')
1735 } else {
1736 g.write(', (')
1737 }
1738 g.expr_with_cast(node.right, right.typ, resolved_left.unaliased.clear_flag(.shared_f))
1739 styp := g.styp(expected_push_many_atype)
1740 g.write('), ${tmp_var}, ${styp})')
1741 } else {
1742 // push a single element
1743 elem_type_str := g.styp(elem_type)
1744 elem_is_array_var := !elem_is_option && elem_sym.kind in [.array, .array_fixed]
1745 && node.right is ast.Ident
1746 g.write('builtin__array_push${noscan}((array*)')
1747 mut needs_addr := false
1748 if !left.typ.is_ptr()
1749 || (node.left_type.has_flag(.shared_f) && !node.left_type.deref().is_ptr()) {
1750 if node.left is ast.CallExpr {
1751 g.write('ADDR(${g.styp(node.left_type)}, ')
1752 needs_addr = true
1753 } else {
1754 g.write('&')
1755 }
1756 }
1757 old_inside_left_shift := g.inside_left_shift
1758 g.inside_left_shift = true
1759 g.expr(node.left)
1760 g.inside_left_shift = old_inside_left_shift
1761 if node.left_type.has_flag(.shared_f) {
1762 g.write('->val')
1763 }
1764 if needs_addr {
1765 g.write(')')
1766 }
1767 if elem_sym.kind == .function {
1768 g.write(', _MOV((voidptr[]){ ')
1769 } else if elem_is_array_var {
1770 addr := if elem_sym.kind == .array_fixed { '' } else { '&' }
1771 g.write(', ${addr}')
1772 } else {
1773 g.write(', _MOV((${elem_type_str}[]){ ')
1774 }
1775 if elem_type.has_flag(.option) {
1776 g.expr_with_opt(node.right, right.typ, elem_type)
1777 } else {
1778 // if g.autofree
1779 needs_clone := !g.is_builtin_mod && elem_type.idx() == ast.string_type_idx
1780 && elem_type.nr_muls() == 0
1781 && node.right !in [ast.StringLiteral, ast.StringInterLiteral, ast.CallExpr, ast.IndexExpr, ast.InfixExpr]
1782 if needs_clone {
1783 g.write('builtin__string_clone(')
1784 }
1785 if node.right is ast.CastExpr && node.right.expr is ast.ArrayInit
1786 && elem_sym.kind != .sum_type {
1787 g.expr(node.right.expr)
1788 } else if elem_sym.info is ast.ArrayFixed
1789 && node.right in [ast.CallExpr, ast.DumpExpr] {
1790 fixed_info := elem_sym.info as ast.ArrayFixed
1791 tmpvar := g.expr_with_var(node.right, elem_type, false)
1792 g.fixed_array_var_init(tmpvar, false, fixed_info.elem_type, fixed_info.size)
1793 } else {
1794 rhs_expr := g.expr_string_with_cast(node.right, right.typ, elem_type)
1795 // Don't dereference when the expression was cast via a
1796 // `_to_sumtype_` function, since that function already takes
1797 // a pointer parameter and returns a value (not a pointer).
1798 if needs_explicit_deref && !rhs_expr.trim_space().starts_with('*')
1799 && !rhs_expr.contains('_to_sumtype_')
1800 && !rhs_expr.contains('_to_Interface_') {
1801 g.write('*')
1802 }
1803 g.write(rhs_expr)
1804 }
1805 if needs_clone {
1806 g.write(')')
1807 }
1808 }
1809 if elem_is_array_var {
1810 g.write(')')
1811 } else {
1812 g.write(' }))')
1813 }
1814 }
1815 } else {
1816 g.gen_safe_shift_expr(node)
1817 }
1818}
1819
1820fn (mut g Gen) need_tmp_var_in_array_call(node ast.Expr) bool {
1821 match node {
1822 ast.CallExpr {
1823 if node.left_type != 0 && g.table.sym(node.left_type).kind == .array
1824 && node.name in ['all', 'any', 'filter', 'map', 'count'] {
1825 return true
1826 }
1827 }
1828 ast.IndexExpr {
1829 return g.need_tmp_var_in_array_call(node.left)
1830 }
1831 ast.InfixExpr {
1832 return g.need_tmp_var_in_array_call(node.left)
1833 || g.need_tmp_var_in_array_call(node.right)
1834 }
1835 ast.ParExpr {
1836 return g.need_tmp_var_in_array_call(node.expr)
1837 }
1838 ast.PostfixExpr {
1839 return g.need_tmp_var_in_array_call(node.expr)
1840 }
1841 ast.PrefixExpr {
1842 return g.need_tmp_var_in_array_call(node.right)
1843 }
1844 ast.RangeExpr {
1845 return g.need_tmp_var_in_array_call(node.low) || g.need_tmp_var_in_array_call(node.high)
1846 }
1847 ast.SelectorExpr {
1848 return g.need_tmp_var_in_array_call(node.expr)
1849 }
1850 else {}
1851 }
1852
1853 return false
1854}
1855
1856fn (mut g Gen) expr_has_lambda_autofree_tmp_arg(expr ast.Expr) bool {
1857 match expr {
1858 ast.CallExpr {
1859 if expr.free_receiver {
1860 return true
1861 }
1862 if expr.args.any(it.typ == ast.string_type && it.is_tmp_autofree) {
1863 return true
1864 }
1865 if g.expr_has_lambda_autofree_tmp_arg(expr.left) {
1866 return true
1867 }
1868 for arg in expr.args {
1869 if g.expr_has_lambda_autofree_tmp_arg(arg.expr) {
1870 return true
1871 }
1872 }
1873 }
1874 ast.CastExpr {
1875 return g.expr_has_lambda_autofree_tmp_arg(expr.expr)
1876 }
1877 ast.IfExpr {
1878 for branch in expr.branches {
1879 if g.expr_has_lambda_autofree_tmp_arg(branch.cond) {
1880 return true
1881 }
1882 for stmt in branch.stmts {
1883 if stmt is ast.ExprStmt && g.expr_has_lambda_autofree_tmp_arg(stmt.expr) {
1884 return true
1885 }
1886 }
1887 }
1888 }
1889 ast.IndexExpr {
1890 return g.expr_has_lambda_autofree_tmp_arg(expr.left)
1891 || g.expr_has_lambda_autofree_tmp_arg(expr.index)
1892 }
1893 ast.InfixExpr {
1894 return g.expr_has_lambda_autofree_tmp_arg(expr.left)
1895 || g.expr_has_lambda_autofree_tmp_arg(expr.right)
1896 }
1897 ast.ParExpr {
1898 return g.expr_has_lambda_autofree_tmp_arg(expr.expr)
1899 }
1900 ast.PostfixExpr {
1901 return g.expr_has_lambda_autofree_tmp_arg(expr.expr)
1902 }
1903 ast.PrefixExpr {
1904 return g.expr_has_lambda_autofree_tmp_arg(expr.right)
1905 }
1906 ast.SelectorExpr {
1907 return g.expr_has_lambda_autofree_tmp_arg(expr.expr)
1908 }
1909 else {}
1910 }
1911
1912 return false
1913}
1914
1915// infix_expr_and_or_op generates code for `&&` and `||`
1916fn (mut g Gen) infix_expr_and_or_op(node ast.InfixExpr) {
1917 rhs_has_lambda_autofree_tmp_arg := g.inside_lambda_autofree_tmp
1918 && g.expr_has_lambda_autofree_tmp_arg(node.right)
1919 if g.need_tmp_var_in_array_call(node.right) && g.inside_ternary == 0 {
1920 // `if a == 0 || arr.any(it.is_letter()) {...}`
1921 tmp := g.new_tmp_var()
1922 cur_line := g.go_before_last_stmt().trim_space()
1923 g.empty_line = true
1924 if g.infix_left_var_name.len > 0 {
1925 g.write('bool ${tmp} = ((${g.infix_left_var_name}) && ')
1926 } else {
1927 g.write('bool ${tmp} = (')
1928 }
1929 g.expr(node.left)
1930 g.writeln(');')
1931 g.set_current_pos_as_last_stmt_pos()
1932 g.write('${cur_line} ${tmp} ${node.op.str()} ')
1933 g.infix_left_var_name = if node.op == .and { tmp } else { '!${tmp}' }
1934 g.expr(node.right)
1935 g.infix_left_var_name = ''
1936 } else if (rhs_has_lambda_autofree_tmp_arg || g.need_tmp_var_in_expr(node.right))
1937 && g.inside_ternary == 0 {
1938 prev_inside_ternary := g.inside_ternary
1939 g.inside_ternary = 0
1940 tmp := g.new_tmp_var()
1941 cur_line := g.go_before_last_stmt().trim_space()
1942 g.empty_line = true
1943 if g.infix_left_var_name.len > 0 {
1944 g.write('bool ${tmp} = ((${g.infix_left_var_name}) && ')
1945 } else {
1946 g.write('bool ${tmp} = (')
1947 }
1948 g.expr(node.left)
1949 g.writeln(');')
1950 // Evaluate the right side with short-circuit: only evaluate if
1951 // left side is true (for &&) or false (for ||).
1952 // Use an if-block to prevent nested go_before_last_stmt() calls
1953 // from grabbing `cur_line` content during right-side evaluation.
1954 cond := if node.op == .and { tmp } else { '!${tmp}' }
1955 g.writeln('if (${cond}) {')
1956 g.indent++
1957 g.set_current_pos_as_last_stmt_pos()
1958 g.infix_left_var_name = ''
1959 lambda_autofree_tmp_arg_vars_start := g.lambda_autofree_tmp_arg_vars.len
1960 g.write('${tmp} = ')
1961 g.expr(node.right)
1962 g.writeln(';')
1963 if rhs_has_lambda_autofree_tmp_arg {
1964 g.write_lambda_autofree_tmp_arg_vars(lambda_autofree_tmp_arg_vars_start)
1965 }
1966 g.indent--
1967 g.writeln('}')
1968 g.set_current_pos_as_last_stmt_pos()
1969 g.write('${cur_line} ${tmp}')
1970 g.inside_ternary = prev_inside_ternary
1971 } else {
1972 g.gen_plain_infix_expr(node)
1973 }
1974}
1975
1976fn (mut g Gen) gen_is_none_check(node ast.InfixExpr) {
1977 if node.left in [ast.Ident, ast.SelectorExpr, ast.IndexExpr, ast.CallExpr, ast.CTempVar, ast.CastExpr] {
1978 // When a sumtype variable has been comptime-smartcast to an option variant
1979 // (e.g. `$if t is ?string { if t == none { ... } }`), we need to access the
1980 // sumtype's variant field directly rather than using .data on the sumtype.
1981 if node.left is ast.Ident && node.left.obj is ast.Var
1982 && node.left.obj.ct_type_var == .smartcast {
1983 obj_sym := g.table.sym(g.unwrap_generic(node.left.obj.typ))
1984 if obj_sym.kind == .sum_type {
1985 ctyp := g.unwrap_generic(g.type_resolver.get_type(node.left))
1986 cur_variant_sym := g.table.sym(ctyp)
1987 variant_name := g.get_sumtype_variant_name(ctyp, cur_variant_sym)
1988 dot := if node.left.obj.orig_type.is_ptr() { '->' } else { '.' }
1989 // Sumtype stores option variants as pointers, so use -> to access state
1990 g.write('${node.left.name}${dot}_${variant_name}->state')
1991 g.write(' ${node.op.str()} 2') // none state
1992 return
1993 }
1994 }
1995 // For Ident nodes that have been unwrapped by a smartcast (e.g. from a
1996 // none-guard fallthrough), write the variable name directly to avoid
1997 // g.expr() generating the unwrapped data access instead of the option wrapper.
1998 if node.left is ast.Ident && node.left.obj is ast.Var && node.left.obj.is_unwrapped {
1999 name := c_name(node.left.name)
2000 if node.left.is_auto_heap() {
2001 g.write('(*${name})')
2002 } else {
2003 g.write(name)
2004 }
2005 } else {
2006 old_inside_opt_or_res := g.inside_opt_or_res
2007 g.inside_opt_or_res = true
2008 g.write('(')
2009 g.expr(node.left)
2010 g.write(')')
2011 g.inside_opt_or_res = old_inside_opt_or_res
2012 }
2013 dot_or_ptr := if !node.left_type.has_flag(.option_mut_param_t) { '.' } else { '->' }
2014 g.write('${dot_or_ptr}state')
2015 } else {
2016 stmt_str := g.go_before_last_stmt().trim_space()
2017 g.empty_line = true
2018 left_var := g.expr_with_opt(node.left, node.left_type, node.left_type)
2019 g.writeln(';')
2020 g.write2(stmt_str, ' ')
2021 dot_or_ptr := if !node.left_type.has_flag(.option_mut_param_t) { '.' } else { '->' }
2022 g.write('${left_var}${dot_or_ptr}state')
2023 }
2024 g.write(' ${node.op.str()} 2') // none state
2025}
2026
2027struct VSafeArithmeticOp {
2028 typ ast.Type
2029 op token.Kind
2030}
2031
2032fn (mut g Gen) normalized_power_result_type(result_type ast.Type, left_type ast.Type, right_type ast.Type) ast.Type {
2033 mut typ :=
2034 g.unwrap_generic(g.recheck_concrete_type(result_type)).clear_flag(.shared_f).clear_flag(.atomic_f)
2035 if typ == 0 || typ == ast.void_type {
2036 typ = g.unwrap_generic(g.type_resolver.promote_type(g.unwrap_generic(left_type),
2037 g.unwrap_generic(right_type))).clear_flag(.shared_f).clear_flag(.atomic_f)
2038 }
2039 if typ == ast.int_literal_type {
2040 if left_type !in [ast.int_literal_type, ast.float_literal_type] {
2041 typ = g.unwrap_generic(left_type)
2042 } else if right_type !in [ast.int_literal_type, ast.float_literal_type] {
2043 typ = g.unwrap_generic(right_type)
2044 } else {
2045 typ = ast.int_type
2046 }
2047 } else if typ == ast.float_literal_type {
2048 if left_type !in [ast.int_literal_type, ast.float_literal_type] {
2049 typ = g.unwrap_generic(left_type)
2050 } else if right_type !in [ast.int_literal_type, ast.float_literal_type] {
2051 typ = g.unwrap_generic(right_type)
2052 } else {
2053 typ = ast.f64_type
2054 }
2055 }
2056 return typ.clear_flag(.shared_f).clear_flag(.atomic_f)
2057}
2058
2059fn (mut g Gen) gen_power_expr_from_types(left ast.Expr, left_type ast.Type, right ast.Expr, right_type ast.Type, result_type ast.Type) {
2060 power_result_type := g.normalized_power_result_type(result_type, left_type, right_type)
2061 builtin_power_type := g.table.unalias_num_type(power_result_type)
2062 result_styp := g.styp(power_result_type)
2063 g.uses_power = true
2064 if builtin_power_type == ast.f32_type {
2065 g.write('(${result_styp})powf(')
2066 g.expr_with_cast(left, left_type, ast.f32_type)
2067 g.write(', ')
2068 g.expr_with_cast(right, right_type, ast.f32_type)
2069 g.write(')')
2070 return
2071 }
2072 if builtin_power_type.is_float() {
2073 g.write('(${result_styp})pow(')
2074 g.expr_with_cast(left, left_type, ast.f64_type)
2075 g.write(', ')
2076 g.expr_with_cast(right, right_type, ast.f64_type)
2077 g.write(')')
2078 return
2079 }
2080 if builtin_power_type.is_unsigned() {
2081 g.uses_power_u64 = true
2082 g.write('(${result_styp})__v_pow_u64(')
2083 g.expr_with_cast(left, left_type, ast.u64_type)
2084 g.write(', ')
2085 g.expr_with_cast(right, right_type, ast.i64_type)
2086 g.write(')')
2087 return
2088 }
2089 g.write('(${result_styp})__v_pow_i64(')
2090 g.expr_with_cast(left, left_type, ast.i64_type)
2091 g.write(', ')
2092 g.expr_with_cast(right, right_type, ast.i64_type)
2093 g.write(')')
2094}
2095
2096// gen_plain_infix_expr generates basic code for infix expressions,
2097// without any overloading of any kind
2098// i.e. v`a + 1` => c`a + 1`
2099// It handles auto dereferencing of variables, as well as automatic casting
2100// (see Gen.expr_with_cast for more details)
2101fn (mut g Gen) gen_plain_infix_expr(node ast.InfixExpr) {
2102 mut resolved_left_type := g.resolved_expr_type(node.left, node.left_type)
2103 mut resolved_right_type := g.resolved_expr_type(node.right, node.right_type)
2104 // Don't override smartcasted concrete types with sumtype/interface from
2105 // resolved_expr_type (which doesn't check scope smartcasts).
2106 if resolved_left_type != 0 {
2107 resolved_sym := g.table.sym(resolved_left_type)
2108 left_sym := g.table.sym(node.left_type)
2109 is_sumtype_override := resolved_sym.kind in [.sum_type, .interface]
2110 && left_sym.kind !in [.sum_type, .interface]
2111 is_option_introduction := resolved_left_type.has_flag(.option)
2112 && !node.left_type.has_flag(.option)
2113 if is_sumtype_override || is_option_introduction {
2114 resolved_left_type = node.left_type
2115 }
2116 }
2117 if resolved_right_type != 0 {
2118 resolved_sym := g.table.sym(resolved_right_type)
2119 right_sym := g.table.sym(node.right_type)
2120 is_sumtype_override := resolved_sym.kind in [.sum_type, .interface]
2121 && right_sym.kind !in [.sum_type, .interface]
2122 is_option_introduction := resolved_right_type.has_flag(.option)
2123 && !node.right_type.has_flag(.option)
2124 if is_sumtype_override || is_option_introduction {
2125 resolved_right_type = node.right_type
2126 }
2127 }
2128 if node.op == .power {
2129 power_left_type := if resolved_left_type != 0 { resolved_left_type } else { node.left_type }
2130 power_right_type := if resolved_right_type != 0 {
2131 resolved_right_type
2132 } else {
2133 node.right_type
2134 }
2135 g.gen_power_expr_from_types(node.left, power_left_type, node.right, power_right_type,
2136 node.promoted_type)
2137 return
2138 }
2139 $if trace_ci_fixes ? {
2140 if g.file.path.contains('binary_search_tree.v') && node.right is ast.SelectorExpr {
2141 if node.right.expr is ast.Ident && node.right.expr.name == 'tree' {
2142 eprintln('plain infix op=${node.op} left=${g.table.type_to_str(resolved_left_type)} right=${g.table.type_to_str(resolved_right_type)} right_ptr=${resolved_right_type.is_ptr()} right_muls=${resolved_right_type.nr_muls()} node_left=${g.table.type_to_str(node.left_type)} node_right=${g.table.type_to_str(node.right_type)} expr=${node.right.expr.name}.${node.right.field_name}')
2143 }
2144 }
2145 }
2146 mut needs_cast := resolved_left_type.is_number() && resolved_right_type.is_number()
2147 && node.op in [.plus, .minus, .mul, .div, .mod] && !(g.pref.translated
2148 || g.file.is_translated)
2149 mut typ := node.promoted_type
2150 mut typ_str := g.styp(typ)
2151 if needs_cast {
2152 typ = if node.left_ct_expr {
2153 g.type_resolver.get_type_or_default(node.left, node.left_type)
2154 } else if node.left !in [ast.Ident, ast.CastExpr] && node.right_ct_expr {
2155 g.type_resolver.get_type_or_default(node.right, node.promoted_type)
2156 } else {
2157 node.promoted_type
2158 }
2159 // In generic contexts, the promoted type may be stale from a previous
2160 // instantiation. Recompute from the resolved operand types.
2161 if g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0 {
2162 resolved_promoted := g.type_resolver.promote_type(g.unwrap_generic(resolved_left_type),
2163 g.unwrap_generic(resolved_right_type))
2164 if resolved_promoted != ast.void_type {
2165 typ = resolved_promoted
2166 }
2167 }
2168 typ_str = g.styp(typ)
2169 // Skip redundant cast when operands already have the same type,
2170 // but keep it for types smaller than int (u8, i8, u16, i16) because
2171 // C integer promotion rules would widen them to int.
2172 if resolved_left_type == resolved_right_type && resolved_left_type == typ {
2173 typ_kind := g.table.type_kind(typ)
2174 if typ_kind !in [.u8, .i8, .u16, .i16] {
2175 needs_cast = false
2176 }
2177 }
2178 if needs_cast {
2179 g.write('(${typ_str})(')
2180 }
2181 }
2182 // do not use promoted_type for overflow detect
2183 left_type := g.unwrap_generic(resolved_left_type)
2184 checkoverflow_op := g.do_int_overflow_checks && left_type.is_int()
2185 is_safe_add := checkoverflow_op && node.op == .plus
2186 is_safe_sub := checkoverflow_op && node.op == .minus
2187 is_safe_mul := checkoverflow_op && node.op == .mul
2188 is_integer_div_mod := g.table.final_sym(g.unwrap_generic(typ)).is_int()
2189 is_safe_div := node.op == .div && is_integer_div_mod
2190 is_safe_mod := node.op == .mod && is_integer_div_mod
2191 if resolved_left_type.is_ptr() && node.left.is_auto_deref_var()
2192 && !resolved_right_type.is_pointer() {
2193 g.write('*')
2194 } else if !g.inside_interface_deref && node.left is ast.Ident
2195 && g.table.is_interface_var(node.left.obj) {
2196 inside_interface_deref_old := g.inside_interface_deref
2197 g.inside_interface_deref = true
2198 defer(fn) {
2199 g.inside_interface_deref = inside_interface_deref_old
2200 }
2201 }
2202 is_ctemp_fixed_ret := node.op in [.eq, .ne] && node.left is ast.CTempVar
2203 && node.left.is_fixed_ret
2204 if is_ctemp_fixed_ret {
2205 if node.op == .eq {
2206 g.write('!')
2207 }
2208 g.write('memcmp(')
2209 }
2210 mut opstr := node.op.str()
2211 if is_safe_add || is_safe_sub || is_safe_mul || is_safe_div || is_safe_mod {
2212 overflow_styp := g.styp(get_overflow_fn_type(left_type))
2213 vsafe_fn_name := match true {
2214 is_safe_add { 'builtin__overflow__add_${overflow_styp}' }
2215 is_safe_sub { 'builtin__overflow__sub_${overflow_styp}' }
2216 is_safe_mul { 'builtin__overflow__mul_${overflow_styp}' }
2217 is_safe_div { 'VSAFE_DIV_${typ_str}' }
2218 is_safe_mod { 'VSAFE_MOD_${typ_str}' }
2219 else { '' }
2220 }
2221
2222 g.write(vsafe_fn_name)
2223 g.write('(')
2224 if is_safe_div || is_safe_mod {
2225 g.vsafe_arithmetic_ops[vsafe_fn_name] = VSafeArithmeticOp{
2226 typ: typ
2227 op: node.op
2228 }
2229 }
2230 opstr = ','
2231 }
2232 g.expr(node.left)
2233 if !is_ctemp_fixed_ret {
2234 g.write(' ')
2235 g.write(opstr)
2236 g.write(' ')
2237 } else {
2238 g.write(', ')
2239 }
2240
2241 if is_ctemp_fixed_ret {
2242 g.write('(${g.styp(resolved_right_type)})')
2243 }
2244 if resolved_right_type.is_ptr() && node.right.is_auto_deref_var()
2245 && !resolved_left_type.is_pointer() {
2246 g.write('*')
2247 g.expr(node.right)
2248 } else {
2249 g.expr_with_cast(node.right, resolved_right_type, resolved_left_type)
2250 }
2251 if is_ctemp_fixed_ret {
2252 g.write(', sizeof(${g.styp(resolved_right_type)}))')
2253 }
2254 if is_safe_add || is_safe_sub || is_safe_mul || is_safe_div || is_safe_mod {
2255 g.write(')')
2256 }
2257 if needs_cast {
2258 g.write(')')
2259 }
2260}
2261
2262fn (mut g Gen) op_arg(expr ast.Expr, expected ast.Type, got ast.Type) {
2263 mut needs_closing := false
2264 mut nr_muls := got.nr_muls()
2265 if expected.is_ptr() {
2266 if nr_muls > 0 {
2267 nr_muls--
2268 } else {
2269 if expr.is_lvalue() {
2270 g.write('&')
2271 } else {
2272 styp := g.styp(got.set_nr_muls(0))
2273 g.write('ADDR(${styp}, ')
2274 needs_closing = true
2275 }
2276 }
2277 }
2278 g.write('*'.repeat(nr_muls))
2279 g.expr(expr)
2280 if needs_closing {
2281 g.write(')')
2282 }
2283}
2284
2285struct GenSafeIntegerCfg {
2286 op token.Kind
2287 reverse bool
2288 unsigned_type ast.Type
2289 unsigned_expr ast.Expr
2290 signed_type ast.Type
2291 signed_expr ast.Expr
2292}
2293
2294// gen_safe_integer_infix_expr generates code for comparison of
2295// unsigned and signed integers
2296fn (mut g Gen) gen_safe_integer_infix_expr(cfg GenSafeIntegerCfg) {
2297 bitsize := if cfg.unsigned_type.idx() == ast.u32_type_idx
2298 && cfg.signed_type.idx() != ast.i64_type_idx {
2299 32
2300 } else {
2301 64
2302 }
2303 op_idx := int(cfg.op) - int(token.Kind.eq)
2304 op_str := if cfg.reverse { cmp_rev[op_idx] } else { cmp_str[op_idx] }
2305 g.write('_us${bitsize}_${op_str}(')
2306 g.expr(cfg.unsigned_expr)
2307 g.write(',')
2308 g.expr(cfg.signed_expr)
2309 g.write(')')
2310}
2311