v2 / vlib / v / gen / c / infix.v
2100 lines · 2055 sloc · 68.07 KB · 1c34ac6eda831af9cea273ff6c6a2844ef3fcf96
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 ptr_typ := g.equality_fn(left.unaliased)
430 if left.typ.is_ptr() || right.typ.is_ptr() {
431 // `&lvalue` on either side means the user is comparing addresses; skip the deep `_struct_eq` (`&StructInit{}` still does deep eq).
432 left_is_addr_of_lvalue := node.left is ast.PrefixExpr && node.left.op == .amp
433 && node.left.right.is_lvalue()
434 right_is_addr_of_lvalue := node.right is ast.PrefixExpr && node.right.op == .amp
435 && node.right.right.is_lvalue()
436 if left.typ.is_ptr() && right.typ.is_ptr()
437 && (left_is_addr_of_lvalue || right_is_addr_of_lvalue) {
438 g.gen_plain_infix_expr(node)
439 } else {
440 g.gen_struct_pointer_eq_op(node, left_type, right_type, ptr_typ)
441 }
442 } else {
443 if node.op == .ne {
444 g.write('!')
445 }
446 g.write('${ptr_typ}_struct_eq(')
447 g.expr(node.left)
448 g.write(', ')
449 g.expr(node.right)
450 g.write(')')
451 }
452 }
453 .sum_type {
454 ptr_typ := g.equality_fn(left.unaliased)
455 if node.op == .ne {
456 g.write('!')
457 }
458 tmp_left_is_opt := g.left_is_opt
459 g.left_is_opt = true
460 g.write('${ptr_typ}_sumtype_eq(')
461 if left.typ.is_ptr() {
462 g.write('*'.repeat(left.typ.nr_muls()))
463 }
464 g.expr(ast.Expr(node.left))
465 g.write(', ')
466 if right.typ.is_ptr() {
467 g.write('*'.repeat(right.typ.nr_muls()))
468 }
469 g.expr(node.right)
470 g.write(')')
471 g.left_is_opt = tmp_left_is_opt
472 }
473 .interface {
474 ptr_typ := g.equality_fn(left.unaliased)
475 if node.op == .ne {
476 g.write('!')
477 }
478 g.write('${ptr_typ}_interface_eq(')
479 if left.typ.is_ptr() {
480 g.write('*'.repeat(left.typ.nr_muls()))
481 }
482 g.expr(node.left)
483 g.write(', ')
484 if right.typ.is_ptr() {
485 g.write('*'.repeat(right.typ.nr_muls()))
486 }
487 g.expr(node.right)
488 g.write(')')
489 }
490 else {
491 g.gen_plain_infix_expr(node)
492 }
493 }
494 } else if left.unaliased.idx() in [ast.u32_type_idx, ast.u64_type_idx]
495 && right.unaliased.is_signed() {
496 g.gen_safe_integer_infix_expr(
497 op: node.op
498 unsigned_type: left.unaliased
499 unsigned_expr: node.left
500 signed_type: right.unaliased
501 signed_expr: node.right
502 )
503 } else if right.unaliased.idx() in [ast.u32_type_idx, ast.u64_type_idx]
504 && left.unaliased.is_signed() {
505 g.gen_safe_integer_infix_expr(
506 op: node.op
507 reverse: true
508 unsigned_type: right.unaliased
509 unsigned_expr: node.right
510 signed_type: left.unaliased
511 signed_expr: node.left
512 )
513 } else if left_is_option && right_is_option {
514 old_inside_opt_or_res := g.inside_opt_or_res
515 g.inside_opt_or_res = true
516 if node.op == .eq {
517 g.write('(')
518 } else {
519 g.write('!(')
520 }
521 g.write('(')
522 g.expr(node.left)
523 g.write('.state == 2 && ')
524 g.expr(node.right)
525 g.write('.state == 2) || (')
526 g.expr(node.left)
527 g.write('.state == ')
528 g.expr(node.right)
529 g.write('.state && ')
530 g.expr(node.left)
531 g.write('.state != 2 && !memcmp(&')
532 g.expr(node.left)
533 g.write('.data, &')
534 g.expr(node.right)
535 g.write('.data, sizeof(${g.base_type(left_type)}))))')
536 g.inside_opt_or_res = old_inside_opt_or_res
537 } else {
538 g.gen_plain_infix_expr(node)
539 }
540}
541
542fn (mut g Gen) gen_struct_pointer_eq_op(node ast.InfixExpr, left_type ast.Type, right_type ast.Type, ptr_typ string) {
543 // When inside a short-circuit `&&` condition (infix_left_var_name is set),
544 // do not hoist temp vars before the containing statement, as that would
545 // evaluate expressions (e.g. as-casts from smartcasts) before the
546 // short-circuit check has run. Instead, use inline expressions.
547 inside_and_rhs := g.infix_left_var_name.len > 0
548 mut stmt_str := ''
549 mut restore_stmt := false
550 mut left_expr := ''
551 mut right_expr := ''
552 if left_type.is_ptr() && !node.left.is_lvalue() && !inside_and_rhs {
553 if !restore_stmt {
554 stmt_str = g.go_before_last_stmt().trim_space()
555 g.empty_line = true
556 restore_stmt = true
557 }
558 mut left_tmp := g.new_ctemp_var(node.left, left_type)
559 g.gen_ctemp_var(mut left_tmp)
560 left_expr = left_tmp.name
561 } else {
562 left_expr = g.expr_string(node.left)
563 }
564 if right_type.is_ptr() && !node.right.is_lvalue() && !inside_and_rhs {
565 if !restore_stmt {
566 stmt_str = g.go_before_last_stmt().trim_space()
567 g.empty_line = true
568 restore_stmt = true
569 }
570 mut right_tmp := g.new_ctemp_var(node.right, right_type)
571 g.gen_ctemp_var(mut right_tmp)
572 right_expr = right_tmp.name
573 } else {
574 right_expr = g.expr_string(node.right)
575 }
576 if restore_stmt {
577 g.write(stmt_str)
578 }
579 if node.op == .ne {
580 g.write('!')
581 }
582 g.write('(')
583 if left_type.is_ptr() && right_type.is_ptr() {
584 g.write('${left_expr} == ${right_expr} || (${left_expr} != 0 && ${right_expr} != 0 && ')
585 g.write('${ptr_typ}_struct_eq(')
586 g.write('*'.repeat(left_type.nr_muls()))
587 g.write(left_expr)
588 g.write(', ')
589 g.write('*'.repeat(right_type.nr_muls()))
590 g.write(right_expr)
591 g.write('))')
592 } else if left_type.is_ptr() {
593 g.write('${left_expr} != 0 && ${ptr_typ}_struct_eq(')
594 g.write('*'.repeat(left_type.nr_muls()))
595 g.write(left_expr)
596 g.write(', ${right_expr})')
597 } else {
598 g.write('${right_expr} != 0 && ${ptr_typ}_struct_eq(${left_expr}, ')
599 g.write('*'.repeat(right_type.nr_muls()))
600 g.write('${right_expr})')
601 }
602 g.write(')')
603}
604
605// infix_expr_cmp_op generates code for `<`, `<=`, `>`, `>=`
606// It handles operator overloading when necessary
607fn (mut g Gen) infix_expr_cmp_op(node ast.InfixExpr) {
608 mut left_type := g.resolved_expr_type(node.left, node.left_type)
609 if left_type == 0 {
610 left_type = node.left_type
611 }
612 left_type = g.unwrap_generic(g.recheck_concrete_type(left_type))
613 mut right_type := g.resolved_expr_type(node.right, node.right_type)
614 if right_type == 0 {
615 right_type = node.right_type
616 }
617 right_type = g.unwrap_generic(g.recheck_concrete_type(right_type))
618 left := g.unwrap(left_type)
619 right := g.unwrap(right_type)
620
621 mut has_operator_overloading := false
622 mut operator_expects_ptr := false
623 mut operator_method := ast.Fn{}
624 if m := g.table.find_method(left.sym, '<') {
625 has_operator_overloading = true
626 operator_expects_ptr = m.receiver_type.is_ptr()
627 operator_method = m
628 } else if m := left.sym.find_method_with_generic_parent('<') {
629 has_operator_overloading = true
630 operator_expects_ptr = m.receiver_type.is_ptr()
631 operator_method = m
632 }
633
634 if g.pref.translated && !g.is_builtin_mod {
635 g.gen_plain_infix_expr(node)
636 return
637 }
638 if left.sym.kind == .struct && (left.sym.info as ast.Struct).generic_types.len > 0 {
639 if node.op in [.le, .ge] {
640 g.write('!')
641 }
642 concrete_types := (left.sym.info as ast.Struct).concrete_types
643 mut method_name := '${left.sym.cname}__lt'
644 if left.unaliased_sym.is_builtin() {
645 method_name = 'builtin__${method_name}'
646 }
647 specialized_suffix := g.generic_fn_name(concrete_types, '')
648 if specialized_suffix != '' && !method_name.ends_with(specialized_suffix) {
649 method_name = g.generic_fn_name(concrete_types, method_name)
650 }
651 method_name = g.specialized_method_name_from_receiver(operator_method, left.typ,
652 method_name)
653 g.write(method_name)
654 if node.op in [.lt, .ge] {
655 g.write2('(', '*'.repeat(left.typ.nr_muls()))
656 if operator_expects_ptr {
657 g.write('&')
658 }
659 g.expr(ast.Expr(node.left))
660 g.write2(', ', '*'.repeat(right.typ.nr_muls()))
661 if operator_expects_ptr {
662 g.write('&')
663 }
664 g.expr(node.right)
665 g.write(')')
666 } else {
667 g.write2('(', '*'.repeat(right.typ.nr_muls()))
668 if operator_expects_ptr {
669 g.write('&')
670 }
671 g.expr(node.right)
672 g.write2(', ', '*'.repeat(left.typ.nr_muls()))
673 if operator_expects_ptr {
674 g.write('&')
675 }
676 g.expr(node.left)
677 g.write(')')
678 }
679 } else if left.unaliased_sym.kind == right.unaliased_sym.kind && has_operator_overloading {
680 if node.op in [.le, .ge] {
681 g.write('!')
682 }
683 mut method_name := '${g.styp(left.typ.set_nr_muls(0))}__lt'
684 if left.unaliased_sym.is_builtin() {
685 method_name = 'builtin__${method_name}'
686 }
687 method_name = g.specialized_method_name_from_receiver(operator_method, left.typ,
688 method_name)
689 g.write(method_name)
690 if node.op in [.lt, .ge] {
691 g.write2('(', '*'.repeat(left.typ.nr_muls()))
692 if operator_expects_ptr {
693 g.write('&')
694 }
695 if node.left is ast.ArrayInit && g.table.sym(node.left_type).kind == .array_fixed {
696 g.fixed_array_init_with_cast(node.left, left_type)
697 } else {
698 g.expr(node.left)
699 }
700 g.write2(', ', '*'.repeat(right.typ.nr_muls()))
701 if operator_expects_ptr {
702 g.write('&')
703 }
704 if node.right is ast.ArrayInit && g.table.sym(node.right_type).kind == .array_fixed {
705 g.fixed_array_init_with_cast(node.right, right_type)
706 } else {
707 g.expr(node.right)
708 }
709 g.write(')')
710 } else {
711 g.write2('(', '*'.repeat(right.typ.nr_muls()))
712 if operator_expects_ptr {
713 g.write('&')
714 }
715 g.expr(node.right)
716 g.write2(', ', '*'.repeat(left.typ.nr_muls()))
717 if operator_expects_ptr {
718 g.write('&')
719 }
720 g.expr(node.left)
721 g.write(')')
722 }
723 } else if left.unaliased.idx() in [ast.u32_type_idx, ast.u64_type_idx]
724 && right.unaliased.is_signed() {
725 g.gen_safe_integer_infix_expr(
726 op: node.op
727 unsigned_type: left.unaliased
728 unsigned_expr: node.left
729 signed_type: right.unaliased
730 signed_expr: node.right
731 )
732 } else if right.unaliased.idx() in [ast.u32_type_idx, ast.u64_type_idx]
733 && left.unaliased.is_signed() {
734 g.gen_safe_integer_infix_expr(
735 op: node.op
736 reverse: true
737 unsigned_type: right.unaliased
738 unsigned_expr: node.right
739 signed_type: left.unaliased
740 signed_expr: node.left
741 )
742 } else {
743 g.gen_plain_infix_expr(node)
744 }
745}
746
747fn (mut g Gen) infix_expr_in_sumtype_interface_array(infix_exprs []ast.InfixExpr) {
748 for i in 0 .. infix_exprs.len {
749 g.infix_expr_is_op(infix_exprs[i])
750 if i != infix_exprs.len - 1 {
751 g.write(' || ')
752 }
753 }
754}
755
756// infix_expr_in_op generates code for `in` and `!in`
757fn (mut g Gen) infix_expr_in_op(node ast.InfixExpr) {
758 mut left_type := node.left_type
759 mut right_type := node.right_type
760 // In generic contexts, AST-stored types may be stale from a previous instantiation.
761 if g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0 {
762 resolved_left := g.resolved_expr_type(node.left, node.left_type)
763 if resolved_left != 0 {
764 resolved_sym := g.table.sym(resolved_left)
765 left_sym := g.table.sym(left_type)
766 if resolved_sym.kind !in [.sum_type, .interface]
767 || left_sym.kind in [.sum_type, .interface] {
768 left_type = resolved_left
769 }
770 }
771 resolved_right := g.resolved_expr_type(node.right, node.right_type)
772 if resolved_right != 0 {
773 resolved_sym := g.table.sym(resolved_right)
774 right_sym := g.table.sym(right_type)
775 if resolved_sym.kind !in [.sum_type, .interface]
776 || right_sym.kind in [.sum_type, .interface] {
777 right_type = resolved_right
778 }
779 }
780 }
781 left := g.unwrap(left_type)
782 right := g.unwrap(right_type)
783 if node.op == .not_in {
784 g.write('!')
785 }
786 if right.unaliased_sym.kind == .array {
787 if left.sym.kind in [.sum_type, .interface] {
788 if node.right is ast.ArrayInit {
789 if node.right.exprs.len > 0
790 && g.table.sym(node.right.expr_types[0]).kind !in [.sum_type, .interface] {
791 mut infix_exprs := []ast.InfixExpr{}
792 for i in 0 .. node.right.exprs.len {
793 infix_exprs << ast.InfixExpr{
794 op: .key_is
795 left: node.left
796 left_type: node.left_type
797 right: node.right.exprs[i]
798 right_type: node.right.expr_types[i]
799 }
800 }
801 g.write('(')
802 g.infix_expr_in_sumtype_interface_array(infix_exprs)
803 g.write(')')
804 return
805 }
806 }
807 }
808 if node.right is ast.ArrayInit {
809 elem_type := node.right.elem_type
810 elem_sym := g.table.sym(elem_type)
811 // TODO: replace ast.Ident check with proper side effect analysis
812 if node.right.exprs.len > 0 && (node.left is ast.Ident
813 || node.left is ast.IndexExpr || node.left is ast.SelectorExpr) {
814 // `a in [1,2,3]` optimization => `a == 1 || a == 2 || a == 3`
815 // avoids an allocation
816 g.write('(')
817 if elem_sym.kind == .sum_type && left.sym.kind != .sum_type {
818 if node.left_type in elem_sym.sumtype_info().variants {
819 new_node_left := ast.CastExpr{
820 arg: ast.empty_expr
821 typ: elem_type
822 expr: node.left
823 expr_type: node.left_type
824 }
825 g.infix_expr_in_optimization(new_node_left, node.left_type, node.right)
826 }
827 } else {
828 g.infix_expr_in_optimization(node.left, node.left_type, node.right)
829 }
830 g.write(')')
831 return
832 }
833 }
834 if right.sym.info is ast.Array {
835 elem_type := right.sym.info.elem_type
836 elem_type_ := g.unwrap(elem_type)
837 if elem_type_.sym.kind == .sum_type {
838 if ast.mktyp(node.left_type) in elem_type_.sym.sumtype_info().variants {
839 new_node_left := ast.CastExpr{
840 arg: ast.empty_expr
841 typ: elem_type
842 expr: node.left
843 expr_type: ast.mktyp(node.left_type)
844 }
845 g.write('(')
846 g.gen_array_contains(node.right_type, node.right, elem_type, new_node_left)
847 g.write(')')
848 return
849 }
850 } else if elem_type_.sym.kind == .interface {
851 new_node_left := ast.CastExpr{
852 arg: ast.empty_expr
853 typ: elem_type
854 expr: node.left
855 expr_type: ast.mktyp(node.left_type)
856 }
857 g.write('(')
858 g.gen_array_contains(node.right_type, node.right, elem_type, new_node_left)
859 g.write(')')
860 return
861 }
862 }
863 g.write('(')
864 g.gen_array_contains(right_type, node.right, left_type, node.left)
865 g.write(')')
866 } else if right.unaliased_sym.kind == .map {
867 g.write('_IN_MAP(')
868 if !left.typ.is_ptr() {
869 mut sym_map := g.table.sym(right_type)
870 if sym_map.info is ast.Alias {
871 sym_map = g.table.sym((sym_map.info as ast.Alias).parent_type)
872 }
873 styp := g.styp(if sym_map.info is ast.Map {
874 (sym_map.info as ast.Map).key_type
875 } else {
876 node.left_type
877 })
878 if g.table.final_sym(node.left_type).kind == .array_fixed && node.left is ast.Ident {
879 g.expr(node.left)
880 } else {
881 g.write('ADDR(${styp}, ')
882 g.expr(node.left)
883 g.write(')')
884 }
885 } else {
886 g.expr(node.left)
887 }
888 g.write(', ')
889 if !right.typ.is_ptr() || right.typ.has_flag(.shared_f) {
890 g.write('ADDR(map, ')
891 g.expr(node.right)
892 if right.typ.has_flag(.shared_f) {
893 g.write('->val')
894 }
895 g.write(')')
896 } else {
897 g.expr(node.right)
898 }
899 g.write(')')
900 } else if right.unaliased_sym.kind == .array_fixed {
901 if left.sym.kind in [.sum_type, .interface] {
902 if node.right is ast.ArrayInit {
903 if node.right.exprs.len > 0 {
904 mut infix_exprs := []ast.InfixExpr{}
905 for i in 0 .. node.right.exprs.len {
906 infix_exprs << ast.InfixExpr{
907 op: .key_is
908 left: node.left
909 left_type: node.left_type
910 right: node.right.exprs[i]
911 right_type: node.right.expr_types[i]
912 }
913 }
914 g.write('(')
915 g.infix_expr_in_sumtype_interface_array(infix_exprs)
916 g.write(')')
917 return
918 }
919 }
920 }
921 if node.right is ast.ArrayInit {
922 if node.right.exprs.len > 0 {
923 // `a in [1,2,3]!` optimization => `a == 1 || a == 2 || a == 3`
924 // avoids an allocation
925 g.write('(')
926 g.infix_expr_in_optimization(node.left, node.left_type, node.right)
927 g.write(')')
928 return
929 }
930 }
931 if right.sym.info is ast.ArrayFixed {
932 elem_type := right.sym.info.elem_type
933 elem_type_ := g.unwrap(elem_type)
934 if elem_type_.sym.kind == .sum_type {
935 if ast.mktyp(node.left_type) in elem_type_.sym.sumtype_info().variants {
936 new_node_left := ast.CastExpr{
937 arg: ast.empty_expr
938 typ: elem_type
939 expr: node.left
940 expr_type: ast.mktyp(node.left_type)
941 }
942 g.write('(')
943 g.gen_array_contains(node.right_type, node.right, elem_type, new_node_left)
944 g.write(')')
945 return
946 }
947 }
948 }
949 g.write('(')
950 g.gen_array_contains(right_type, node.right, left_type, node.left)
951 g.write(')')
952 } else if right.unaliased_sym.kind == .string && node.right !is ast.RangeExpr {
953 g.write2('(', 'string_contains(')
954 g.expr(node.right)
955 g.write(', ')
956 g.expr(node.left)
957 g.write('))')
958 } else if node.right is ast.RangeExpr {
959 // call() in min..max
960 if node.left is ast.CallExpr {
961 line := g.go_before_last_stmt().trim_space()
962 g.empty_line = true
963 tmp_var := g.new_tmp_var()
964 g.write('${g.styp(node.left.return_type)} ${tmp_var} = ')
965 g.expr(ast.Expr(node.left))
966 g.writeln(';')
967 g.write(line)
968 g.write('(')
969 g.write('${tmp_var} >= ')
970 g.expr(node.right.low)
971 g.write(' && ')
972 g.write('${tmp_var} < ')
973 g.expr(node.right.high)
974 g.write(')')
975 } else {
976 g.write('(')
977 g.expr(node.left)
978 g.write(' >= ')
979 g.expr(node.right.low)
980 g.write(' && ')
981 g.expr(node.left)
982 g.write(' < ')
983 g.expr(node.right.high)
984 g.write(')')
985 }
986 }
987}
988
989// infix_expr_in_optimization optimizes `<var> in <array>` expressions,
990// and transform them in a series of equality comparison
991// i.e. `a in [1,2,3]` => `a == 1 || a == 2 || a == 3`
992fn (mut g Gen) infix_expr_in_optimization(left ast.Expr, left_type ast.Type, right ast.ArrayInit) {
993 tmp_var := if left is ast.CallExpr { g.new_tmp_var() } else { '' }
994 mut elem_sym := g.table.sym(right.elem_type)
995 left_parent_idx := g.table.sym(left_type).parent_idx
996 for i, array_expr in right.exprs {
997 match elem_sym.kind {
998 .string, .alias, .sum_type, .map, .interface, .array, .struct {
999 if elem_sym.kind == .string {
1000 is_auto_deref_var := left.is_auto_deref_var()
1001 if left is ast.Ident && left.or_expr.kind == .absent
1002 && array_expr is ast.StringLiteral {
1003 var := g.expr_string(left)
1004 slit :=
1005 cescape_nonascii(util.smart_quote(array_expr.val, array_expr.is_raw))
1006 mut needs_deref := false
1007 if left.info is ast.IdentVar && left.obj is ast.Var {
1008 if g.table.sym(left.obj.typ).kind in [.interface, .sum_type] {
1009 needs_deref = left.obj.smartcasts.len == 0
1010 }
1011 }
1012 if is_auto_deref_var || needs_deref {
1013 g.write('_SLIT_EQ(${var}->str, ${var}->len, "${slit}")')
1014 } else {
1015 g.write('_SLIT_EQ(${var}.str, ${var}.len, "${slit}")')
1016 }
1017 if i < right.exprs.len - 1 {
1018 g.write(' || ')
1019 }
1020 continue
1021 } else if array_expr is ast.StringLiteral {
1022 g.write('builtin__fast_string_eq(')
1023 } else {
1024 g.write('builtin__string__eq(')
1025 }
1026 if is_auto_deref_var || (left is ast.Ident && left.info is ast.IdentVar
1027 && g.table.sym(left.obj.typ).kind in [.interface, .sum_type]) {
1028 g.write('*')
1029 }
1030 } else {
1031 ptr_typ := g.equality_fn(right.elem_type)
1032 if elem_sym.kind == .alias {
1033 // optimization for alias to number
1034 if elem_sym.is_int() {
1035 g.expr(left)
1036 g.write(' == ')
1037 if left_parent_idx != 0 && !((array_expr is ast.SelectorExpr
1038 && array_expr.typ == left_type)
1039 || (array_expr is ast.Ident && array_expr.obj.typ == left_type)) {
1040 g.write('(${g.styp(left_parent_idx)})')
1041 }
1042 g.expr(array_expr)
1043 if i < right.exprs.len - 1 {
1044 g.write(' || ')
1045 }
1046 continue
1047 } else {
1048 g.write('${ptr_typ}_alias_eq(')
1049 }
1050 } else if elem_sym.kind == .sum_type {
1051 g.write('${ptr_typ}_sumtype_eq(')
1052 } else if elem_sym.kind == .map {
1053 g.write('${ptr_typ}_map_eq(')
1054 } else if elem_sym.kind == .interface {
1055 g.write('${ptr_typ}_interface_eq(')
1056 } else if elem_sym.kind == .array {
1057 g.write('${ptr_typ}_arr_eq(')
1058 } else if elem_sym.kind == .struct {
1059 g.write('${ptr_typ}_struct_eq(')
1060 }
1061 }
1062 if left is ast.CallExpr {
1063 if i == 0 {
1064 line := g.go_before_last_stmt().trim_space()
1065 g.empty_line = true
1066 g.write('${g.styp(left.return_type)} ${tmp_var} = ')
1067 g.expr(left)
1068 g.writeln(';')
1069 g.write2(line, tmp_var)
1070 } else {
1071 g.write(tmp_var)
1072 }
1073 } else {
1074 g.expr(left)
1075 }
1076 g.write(', ')
1077 g.expr(array_expr)
1078 g.write(')')
1079 }
1080 else { // works in function kind
1081 if left is ast.CallExpr {
1082 if i == 0 {
1083 line := g.go_before_last_stmt().trim_space()
1084 g.empty_line = true
1085 g.write('${g.styp(left.return_type)} ${tmp_var} = ')
1086 g.expr(left)
1087 g.writeln(';')
1088 g.write2(line, tmp_var)
1089 } else {
1090 g.write(tmp_var)
1091 }
1092 } else {
1093 g.expr(left)
1094 }
1095 g.write(' == ')
1096 if elem_sym.kind == .array_fixed {
1097 g.write('(${g.styp(right.elem_type)})')
1098 }
1099 g.expr(array_expr)
1100 }
1101 }
1102
1103 if i < right.exprs.len - 1 {
1104 g.write(' || ')
1105 }
1106 }
1107}
1108
1109// infix_expr_is_op generates code for `is` and `!is`
1110fn (mut g Gen) infix_expr_is_op(node ast.InfixExpr) {
1111 mut left_sym := g.table.final_sym(g.unwrap_generic(g.type_resolver.get_type_or_default(node.left,
1112 node.left_type)))
1113 is_aggregate := node.left is ast.Ident && g.comptime.get_ct_type_var(node.left) == .aggregate
1114 mut right_type := g.unwrap_generic(g.recheck_concrete_type(node.right_type))
1115 if right_type.is_ptr() && g.table.final_sym(right_type.deref()).kind == .interface {
1116 right_type = right_type.deref()
1117 }
1118 right_sym := g.table.final_sym(right_type)
1119 // When the LHS is a smartcast variable whose original type is a sum type,
1120 // use the original sum type so the `is` check works on the tag field.
1121 // But only when the smartcast target is NOT itself a sum type — for nested
1122 // sum types (e.g., Outer→Inner→MyStruct), normal smartcast unwrapping
1123 // must proceed to generate the correct inner dereference.
1124 mut is_orig_sumtype := false
1125 if node.left is ast.Ident && node.left.obj is ast.Var {
1126 v := node.left.obj as ast.Var
1127 if v.smartcasts.len > 0 && v.is_mut && v.orig_type != 0 {
1128 orig_sym := g.table.final_sym(v.orig_type)
1129 smartcast_target_sym := g.table.final_sym(v.smartcasts.last())
1130 if orig_sym.kind == .sum_type && smartcast_target_sym.kind != .sum_type {
1131 left_sym = unsafe { orig_sym }
1132 is_orig_sumtype = true
1133 }
1134 }
1135 }
1136 if (left_sym.kind == .sum_type || is_aggregate) && node.left_type.nr_muls() > 0
1137 && right_type.nr_muls() <= node.left_type.nr_muls() {
1138 right_type = right_type.set_nr_muls(0)
1139 }
1140 if left_sym.kind == .interface && right_sym.kind == .interface {
1141 g.gen_interface_is_op(node)
1142 return
1143 }
1144
1145 cmp_op := if node.op == .key_is { '==' } else { '!=' }
1146 g.write('(')
1147 if node.left_type.nr_muls() > 1 {
1148 g.write('*'.repeat(node.left_type.nr_muls() - 1))
1149 }
1150 if is_aggregate {
1151 g.write('${node.left}')
1152 } else if is_orig_sumtype {
1153 g.prevent_sum_type_unwrapping_once = true
1154 g.expr(node.left)
1155 } else {
1156 g.expr(node.left)
1157 }
1158 g.write(')')
1159 if node.left_type.is_ptr() {
1160 g.write('->')
1161 } else {
1162 g.write('.')
1163 }
1164 if left_sym.kind == .interface {
1165 g.write('_typ ${cmp_op} ')
1166 // `_Animal_Dog_index`
1167 sub_type := match node.right {
1168 ast.TypeNode {
1169 g.unwrap_generic(node.right.typ)
1170 }
1171 ast.None {
1172 ast.idx_to_type(g.table.type_idxs['None__'])
1173 }
1174 else {
1175 ast.no_type
1176 }
1177 }
1178
1179 sub_sym := g.table.sym(sub_type)
1180 g.write('_${left_sym.cname}_${sub_sym.cname}_index')
1181 return
1182 } else if left_sym.kind == .sum_type || is_aggregate {
1183 g.write('_typ ${cmp_op} ')
1184 }
1185 if node.right is ast.None {
1186 g.write('${ast.none_type.idx()}')
1187 } else if node.right is ast.Ident && node.right.name == g.comptime.comptime_for_variant_var {
1188 variant_idx := g.type_resolver.get_ct_type_or_default('${g.comptime.comptime_for_variant_var}.typ',
1189 ast.void_type)
1190 if (left_sym.kind == .sum_type || is_aggregate) && node.left_type.nr_muls() > 0
1191 && variant_idx.nr_muls() <= node.left_type.nr_muls() {
1192 g.write('${int(variant_idx.set_nr_muls(0))}')
1193 } else {
1194 g.write('${int(variant_idx)}')
1195 }
1196 } else if node.right is ast.TypeNode {
1197 g.write('${int(right_type)}')
1198 } else {
1199 g.expr(node.right)
1200 }
1201}
1202
1203fn (mut g Gen) gen_interface_is_op(node ast.InfixExpr) {
1204 mut left_type := g.unwrap_generic(g.recheck_concrete_type(node.left_type))
1205 mut right_type := g.unwrap_generic(g.recheck_concrete_type(node.right_type))
1206 if left_type.is_ptr() && g.table.final_sym(left_type.deref()).kind == .interface {
1207 left_type = left_type.deref()
1208 }
1209 if right_type.is_ptr() && g.table.final_sym(right_type.deref()).kind == .interface {
1210 right_type = right_type.deref()
1211 }
1212 mut left_sym := g.table.final_sym(left_type)
1213 right_sym := g.table.final_sym(right_type)
1214
1215 mut info := left_sym.info as ast.Interface
1216 right_info := right_sym.info as ast.Interface
1217 left_variants := info.implementor_types(true)
1218 right_variants := right_info.implementor_types(true)
1219 lock info.conversions {
1220 common_variants := info.conversions[right_type] or {
1221 c := g.interface_conversion_variants(left_variants, right_variants)
1222 info.conversions[right_type] = c
1223 c
1224 }
1225 left_sym.info = info
1226 if common_variants.len == 0 {
1227 g.write('false')
1228 return
1229 }
1230 }
1231 g.write('I_${left_sym.cname}_is_I_${right_sym.cname}(')
1232 if node.left_type.is_ptr() {
1233 g.write('*')
1234 }
1235 g.expr(node.left)
1236 g.write(')')
1237}
1238
1239// infix_expr_arithmetic_op generates code for `+`, `-`, `*`, `/`, and `%`
1240// It handles operator overloading when necessary
1241fn (mut g Gen) is_string_type(typ ast.Type) bool {
1242 return g.unwrap(typ).unaliased_sym.kind == .string
1243}
1244
1245fn (mut g Gen) is_char_or_rune_string_concat_type(typ ast.Type) bool {
1246 return g.table.unaliased_type(g.unwrap_generic(typ)).clear_flags() in [ast.char_type, ast.rune_type]
1247}
1248
1249fn (mut g Gen) is_string_concat_type(typ ast.Type) bool {
1250 return g.is_string_type(typ) || g.is_char_or_rune_string_concat_type(typ)
1251}
1252
1253fn (mut g Gen) is_string_concat_infix(node ast.InfixExpr) bool {
1254 if node.op != .plus {
1255 return false
1256 }
1257 left_type := g.type_resolver.get_type_or_default(node.left, node.left_type)
1258 right_type := g.type_resolver.get_type_or_default(node.right, node.right_type)
1259 return g.is_string_concat_type(left_type) && g.is_string_concat_type(right_type)
1260 && (g.is_string_type(left_type) || g.is_string_type(right_type))
1261}
1262
1263fn (mut g Gen) collect_string_concat_parts(expr ast.Expr, mut parts []ast.Expr) {
1264 match expr {
1265 ast.InfixExpr {
1266 if g.is_string_concat_infix(expr) {
1267 g.collect_string_concat_parts(expr.left, mut parts)
1268 g.collect_string_concat_parts(expr.right, mut parts)
1269 return
1270 }
1271 }
1272 ast.ParExpr {
1273 g.collect_string_concat_parts(expr.expr, mut parts)
1274 return
1275 }
1276 else {}
1277 }
1278
1279 parts << expr
1280}
1281
1282fn (mut g Gen) gen_string_concat_many(node ast.InfixExpr) bool {
1283 if !g.is_string_concat_infix(node) {
1284 return false
1285 }
1286 mut parts := []ast.Expr{}
1287 g.collect_string_concat_parts(ast.Expr(node), mut parts)
1288 mut needs_plus_many := parts.len >= 3
1289 for part in parts {
1290 part_type := g.type_resolver.get_type_or_default(part, part.type())
1291 if !g.is_string_type(part_type) {
1292 needs_plus_many = true
1293 break
1294 }
1295 }
1296 if !needs_plus_many {
1297 return false
1298 }
1299 g.write('builtin__string_plus_many(${parts.len}, _MOV((string[${parts.len}]){')
1300 for i, part in parts {
1301 part_type := g.type_resolver.get_type_or_default(part, part.type())
1302 g.expr_with_cast(part, part_type, ast.string_type)
1303 if i < parts.len - 1 {
1304 g.write(', ')
1305 }
1306 }
1307 g.write('}))')
1308 return true
1309}
1310
1311fn (mut g Gen) infix_expr_arithmetic_op(node ast.InfixExpr) {
1312 if g.gen_string_concat_many(node) {
1313 return
1314 }
1315 left_type := g.resolved_expr_type(node.left, node.left_type)
1316 right_type := g.resolved_expr_type(node.right, node.right_type)
1317 $if trace_ci_fixes ? {
1318 left_str := g.table.type_to_str(left_type)
1319 right_str := g.table.type_to_str(right_type)
1320 if left_str.contains('MyStruct') || left_str.contains('MyAlias')
1321 || right_str.contains('MyStruct') || right_str.contains('MyAlias') {
1322 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))}')
1323 }
1324 }
1325 left := g.unwrap(left_type)
1326 right := g.unwrap(right_type)
1327 if left.sym.info is ast.Struct && left.sym.info.generic_types.len > 0 {
1328 mut method_name := left.sym.cname + '_' + util.replace_op(node.op.str())
1329 mut concrete_types := left.sym.info.concrete_types.clone()
1330 resolved_left_sym := g.table.final_sym(left.typ)
1331 if resolved_left_sym.info is ast.Struct && resolved_left_sym.info.concrete_types.len > 0 {
1332 concrete_types = resolved_left_sym.info.concrete_types.clone()
1333 }
1334 specialized_suffix := g.generic_fn_name(concrete_types, '')
1335 if specialized_suffix != '' && !method_name.ends_with(specialized_suffix) {
1336 method_name = g.generic_fn_name(concrete_types, method_name)
1337 }
1338 if left.sym.is_builtin() {
1339 method_name = 'builtin__${method_name}'
1340 }
1341 g.write2(method_name, '(')
1342 g.expr(node.left)
1343 g.write(', ')
1344 g.expr(node.right)
1345 g.write(')')
1346 } else {
1347 mut method := ast.Fn{}
1348 mut method_name := ''
1349 if left.sym.has_method(node.op.str()) {
1350 method = left.sym.find_method(node.op.str()) or { ast.Fn{} }
1351 method_name = left.sym.cname + '_' + util.replace_op(node.op.str())
1352 if left.sym.is_builtin() {
1353 method_name = 'builtin__${method_name}'
1354 }
1355 } else if left.unaliased_sym.has_method_with_generic_parent(node.op.str()) {
1356 method = left.unaliased_sym.find_method_with_generic_parent(node.op.str()) or {
1357 ast.Fn{}
1358 }
1359 method_name = left.unaliased_sym.cname + '_' + util.replace_op(node.op.str())
1360 if left.unaliased_sym.is_builtin() {
1361 method_name = 'builtin__${method_name}'
1362 }
1363 if left.unaliased_sym.info is ast.Struct
1364 && left.unaliased_sym.info.generic_types.len > 0 {
1365 mut concrete_types := left.unaliased_sym.info.concrete_types.clone()
1366 resolved_left_sym := g.table.final_sym(left.typ)
1367 if resolved_left_sym.info is ast.Struct
1368 && resolved_left_sym.info.concrete_types.len > 0 {
1369 concrete_types = resolved_left_sym.info.concrete_types.clone()
1370 }
1371 method_name = g.generic_fn_name(concrete_types, method_name)
1372 }
1373 } else {
1374 g.gen_plain_infix_expr(node)
1375 return
1376 }
1377
1378 mut right_var := ''
1379 if node.right is ast.Ident && node.right.or_expr.kind != .absent {
1380 cur_line := g.go_before_last_stmt().trim_space()
1381 right_var = g.new_tmp_var()
1382 unwrapped_right_typ := right.typ.clear_option_and_result()
1383 g.write('${g.styp(unwrapped_right_typ)} ${right_var} = ')
1384 g.op_arg(ast.Expr(node.right), method.params[1].typ, unwrapped_right_typ)
1385 g.writeln(';')
1386 g.write(cur_line)
1387 }
1388 g.write2(method_name, '(')
1389 g.op_arg(node.left, method.params[0].typ, left.typ)
1390 if right_var != '' {
1391 g.write(', ${right_var}')
1392 } else {
1393 g.write(', ')
1394 g.op_arg(node.right, method.params[1].typ, right.typ)
1395 }
1396 g.write(')')
1397
1398 if left.typ != 0 && !left.typ.has_option_or_result()
1399 && g.table.final_sym(left.typ).kind == .array_fixed {
1400 // it's non-option fixed array, requires accessing .ret_arr member to get the array
1401 g.write('.ret_arr')
1402 }
1403 }
1404}
1405
1406// infix_expr_left_shift_op generates code for the `<<` operator
1407// This can either be a value pushed into an array or a bit shift
1408fn (mut g Gen) infix_expr_left_shift_op(node ast.InfixExpr) {
1409 mut left_type := if node.left is ast.ComptimeSelector {
1410 g.type_resolver.get_type(ast.Expr(node.left))
1411 } else {
1412 g.recheck_concrete_type(node.left_type)
1413 }
1414 resolved_left_type := g.resolved_expr_type(node.left, node.left_type)
1415 if resolved_left_type != 0 {
1416 left_type = g.unwrap_generic(g.recheck_concrete_type(resolved_left_type))
1417 } else if left_type == 0 || left_type.has_flag(.generic)
1418 || g.type_has_unresolved_generic_parts(left_type) {
1419 left_type = g.resolved_expr_type(node.left, node.left_type)
1420 }
1421 if left_type == 0 {
1422 left_type = node.left_type
1423 }
1424 mut right_type := if node.right is ast.ComptimeSelector {
1425 g.type_resolver.get_type(ast.Expr(node.right))
1426 } else {
1427 node.right_type
1428 }
1429 resolved_node_right_type := g.resolved_expr_type(node.right, node.right_type)
1430 if resolved_node_right_type != 0 {
1431 right_type = g.unwrap_generic(g.recheck_concrete_type(resolved_node_right_type))
1432 } else if right_type == 0 || right_type.has_flag(.generic)
1433 || g.type_has_unresolved_generic_parts(right_type) {
1434 right_type = g.resolved_expr_type(node.right, node.right_type)
1435 }
1436 if right_type == 0 {
1437 right_type = node.right_type
1438 }
1439 left := g.unwrap(left_type)
1440 right := g.unwrap(right_type)
1441 if left.unaliased_sym.kind == .array {
1442 // arr << val
1443 tmp_var := g.new_tmp_var()
1444 mut resolved_left := left
1445 concrete_left_type := g.recheck_concrete_type(left.typ)
1446 if concrete_left_type != 0 {
1447 resolved_left = g.unwrap(concrete_left_type)
1448 }
1449 if node.left is ast.Ident {
1450 scope_left_type := g.resolved_scope_var_type(node.left)
1451 if scope_left_type != 0 {
1452 resolved_left = g.unwrap(scope_left_type)
1453 }
1454 }
1455 array_info := if resolved_left.unaliased_sym.kind == .array {
1456 resolved_left.unaliased_sym.info as ast.Array
1457 } else {
1458 left.unaliased_sym.info as ast.Array
1459 }
1460 mut elem_type := g.unwrap_generic(g.recheck_concrete_type(array_info.elem_type))
1461 if elem_type == 0 || elem_type.has_flag(.generic)
1462 || g.type_has_unresolved_generic_parts(elem_type) {
1463 resolved_elem_type :=
1464 g.recheck_concrete_type(g.table.value_type(g.unwrap_generic(resolved_left.typ)))
1465 if resolved_elem_type != 0 {
1466 elem_type = g.unwrap_generic(resolved_elem_type)
1467 }
1468 }
1469 if elem_type == 0 {
1470 elem_type = array_info.elem_type
1471 }
1472 if node.left is ast.Ident {
1473 ident_elem_type := g.resolved_ident_array_elem_type(node.left)
1474 if ident_elem_type != 0 {
1475 elem_type = ident_elem_type
1476 }
1477 }
1478 if elem_type == ast.usize_type {
1479 name_elem_type := g.resolved_array_elem_type_from_name(resolved_left.sym.name)
1480 if name_elem_type != 0 {
1481 elem_type = name_elem_type
1482 }
1483 }
1484 if elem_type == ast.usize_type {
1485 call_elem_type := g.resolved_call_like_expr_type(node.right)
1486 if call_elem_type != 0
1487 && call_elem_type !in [ast.int_literal_type, ast.float_literal_type]
1488 && g.table.final_sym(call_elem_type).kind !in [.array, .map] {
1489 elem_type = call_elem_type
1490 }
1491 }
1492 if elem_type == ast.usize_type {
1493 mut candidate_elem_type := g.unwrap_generic(g.recheck_concrete_type(g.resolved_expr_type(node.right,
1494 node.right_type)))
1495 if candidate_elem_type == 0 {
1496 candidate_elem_type = g.unwrap_generic(g.recheck_concrete_type(node.right_type))
1497 }
1498 if candidate_elem_type != 0 && candidate_elem_type != ast.void_type
1499 && candidate_elem_type !in [ast.int_literal_type, ast.float_literal_type]
1500 && g.table.final_sym(candidate_elem_type).kind !in [.array, .map] {
1501 elem_type = candidate_elem_type
1502 }
1503 }
1504 mut elem_sym := g.table.final_sym(elem_type)
1505 if node.right is ast.StructInit && elem_sym.kind !in [.interface, .sum_type] {
1506 resolved_right_type := g.unwrap_generic(g.recheck_concrete_type(g.resolved_expr_type(ast.Expr(node.right),
1507 right.typ)))
1508 if resolved_right_type != 0
1509 && g.table.final_sym(resolved_right_type).kind == elem_sym.kind
1510 && g.table.type_to_str(resolved_right_type) == g.table.type_to_str(elem_type) {
1511 elem_type = resolved_right_type
1512 elem_sym = g.table.final_sym(elem_type)
1513 }
1514 }
1515 noscan := g.check_noscan(elem_type)
1516 elem_is_option := elem_type.has_flag(.option)
1517 mut prevent_push_many := g.table.sumtype_has_variant(elem_type, right_type, false)
1518 mut resolved_right_type := g.unwrap_generic(g.recheck_concrete_type(right_type))
1519 if resolved_right_type == 0 {
1520 resolved_right_type = g.unwrap_generic(right_type)
1521 }
1522 needs_explicit_deref := node.right is ast.Ident && resolved_right_type.is_ptr()
1523 && !elem_type.is_ptr() && !elem_type.is_pointer()
1524 rhs_is_any_value := elem_sym.kind == .any
1525 mut rhs_is_interface_value := elem_sym.kind == .interface
1526 && g.table.does_type_implement_interface(resolved_right_type, elem_type)
1527 if rhs_is_interface_value {
1528 // Don't prevent push_many when the right side is an array with matching
1529 // element type (e.g. []Foo << []Foo where Foo is an interface).
1530 resolved_right_sym := g.table.final_sym(resolved_right_type)
1531 if resolved_right_sym.kind == .array {
1532 right_elem := (resolved_right_sym.info as ast.Array).elem_type
1533 if right_elem == elem_type {
1534 rhs_is_interface_value = false
1535 }
1536 }
1537 }
1538 if rhs_is_any_value || rhs_is_interface_value {
1539 prevent_push_many = true
1540 }
1541 if prevent_push_many && node.right is ast.CallExpr && !rhs_is_any_value
1542 && !rhs_is_interface_value {
1543 // Allow concatenation for array-returning calls; avoids nesting for common builder APIs.
1544 prevent_push_many = false
1545 }
1546 if (right.unaliased_sym.kind == .array
1547 || (right.unaliased_sym.kind == .struct && right.unaliased_sym.name == 'array'))
1548 && resolved_left.sym.nr_dims() == right.sym.nr_dims() && elem_type != right.typ
1549 && !elem_is_option && !prevent_push_many {
1550 // push an array => PUSH_MANY, but not if pushing an array to 2d array (`[][]int << []int`)
1551 g.write('_PUSH_MANY${noscan}(')
1552 // The push macro needs the plain array type (not option/result),
1553 // since it declares a temp var of that type.
1554 mut expected_push_many_atype := resolved_left.typ.clear_option_and_result()
1555 is_shared := expected_push_many_atype.has_flag(.shared_f)
1556 if !expected_push_many_atype.is_ptr() {
1557 // fn f(mut a []int) { a << [1,2,3] } -> type of `a` is `array_int*` -> no need for &
1558 g.write('&')
1559 } else {
1560 expected_push_many_atype = expected_push_many_atype.deref()
1561 }
1562 if is_shared {
1563 g.write('&')
1564 }
1565 if is_shared {
1566 expected_push_many_atype = expected_push_many_atype.clear_flag(.shared_f)
1567 }
1568 expected_push_many_sym := g.table.final_sym(g.unwrap_generic(expected_push_many_atype))
1569 if expected_push_many_sym.kind == .array {
1570 mut push_many_elem_type :=
1571 g.unwrap_generic(g.recheck_concrete_type(expected_push_many_sym.array_info().elem_type))
1572 if push_many_elem_type == ast.int_literal_type {
1573 push_many_elem_type = ast.int_type
1574 } else if push_many_elem_type == ast.float_literal_type {
1575 push_many_elem_type = ast.f64_type
1576 }
1577 if push_many_elem_type != expected_push_many_sym.array_info().elem_type {
1578 expected_push_many_atype =
1579 ast.idx_to_type(g.table.find_or_register_array(push_many_elem_type))
1580 }
1581 }
1582 old_inside_left_shift := g.inside_left_shift
1583 g.inside_left_shift = true
1584 g.expr(node.left)
1585 g.inside_left_shift = old_inside_left_shift
1586 if node.left_type.has_flag(.shared_f) {
1587 g.write('->val')
1588 }
1589 if left.typ.is_ptr() && right.typ.is_ptr() {
1590 g.write(', *(')
1591 } else {
1592 g.write(', (')
1593 }
1594 g.expr_with_cast(node.right, right.typ, resolved_left.unaliased.clear_flag(.shared_f))
1595 styp := g.styp(expected_push_many_atype)
1596 g.write('), ${tmp_var}, ${styp})')
1597 } else {
1598 // push a single element
1599 elem_type_str := g.styp(elem_type)
1600 elem_is_array_var := !elem_is_option && elem_sym.kind in [.array, .array_fixed]
1601 && node.right is ast.Ident
1602 g.write('builtin__array_push${noscan}((array*)')
1603 mut needs_addr := false
1604 if !left.typ.is_ptr()
1605 || (node.left_type.has_flag(.shared_f) && !node.left_type.deref().is_ptr()) {
1606 if node.left is ast.CallExpr {
1607 g.write('ADDR(${g.styp(node.left_type)}, ')
1608 needs_addr = true
1609 } else {
1610 g.write('&')
1611 }
1612 }
1613 old_inside_left_shift := g.inside_left_shift
1614 g.inside_left_shift = true
1615 g.expr(node.left)
1616 g.inside_left_shift = old_inside_left_shift
1617 if node.left_type.has_flag(.shared_f) {
1618 g.write('->val')
1619 }
1620 if needs_addr {
1621 g.write(')')
1622 }
1623 if elem_sym.kind == .function {
1624 g.write(', _MOV((voidptr[]){ ')
1625 } else if elem_is_array_var {
1626 addr := if elem_sym.kind == .array_fixed { '' } else { '&' }
1627 g.write(', ${addr}')
1628 } else {
1629 g.write(', _MOV((${elem_type_str}[]){ ')
1630 }
1631 if elem_type.has_flag(.option) {
1632 g.expr_with_opt(node.right, right.typ, elem_type)
1633 } else {
1634 // if g.autofree
1635 needs_clone := !g.is_builtin_mod && elem_type.idx() == ast.string_type_idx
1636 && elem_type.nr_muls() == 0
1637 && node.right !in [ast.StringLiteral, ast.StringInterLiteral, ast.CallExpr, ast.IndexExpr, ast.InfixExpr]
1638 if needs_clone {
1639 g.write('builtin__string_clone(')
1640 }
1641 if node.right is ast.CastExpr && node.right.expr is ast.ArrayInit
1642 && elem_sym.kind != .sum_type {
1643 g.expr(node.right.expr)
1644 } else if elem_sym.info is ast.ArrayFixed
1645 && node.right in [ast.CallExpr, ast.DumpExpr] {
1646 fixed_info := elem_sym.info as ast.ArrayFixed
1647 tmpvar := g.expr_with_var(node.right, elem_type, false)
1648 g.fixed_array_var_init(tmpvar, false, fixed_info.elem_type, fixed_info.size)
1649 } else {
1650 rhs_expr := g.expr_string_with_cast(node.right, right.typ, elem_type)
1651 // Don't dereference when the expression was cast via a
1652 // `_to_sumtype_` function, since that function already takes
1653 // a pointer parameter and returns a value (not a pointer).
1654 if needs_explicit_deref && !rhs_expr.trim_space().starts_with('*')
1655 && !rhs_expr.contains('_to_sumtype_')
1656 && !rhs_expr.contains('_to_Interface_') {
1657 g.write('*')
1658 }
1659 g.write(rhs_expr)
1660 }
1661 if needs_clone {
1662 g.write(')')
1663 }
1664 }
1665 if elem_is_array_var {
1666 g.write(')')
1667 } else {
1668 g.write(' }))')
1669 }
1670 }
1671 } else {
1672 g.gen_safe_shift_expr(node)
1673 }
1674}
1675
1676fn (mut g Gen) need_tmp_var_in_array_call(node ast.Expr) bool {
1677 match node {
1678 ast.CallExpr {
1679 if node.left_type != 0 && g.table.sym(node.left_type).kind == .array
1680 && node.name in ['all', 'any', 'filter', 'map', 'count'] {
1681 return true
1682 }
1683 }
1684 ast.IndexExpr {
1685 return g.need_tmp_var_in_array_call(node.left)
1686 }
1687 ast.InfixExpr {
1688 return g.need_tmp_var_in_array_call(node.left)
1689 || g.need_tmp_var_in_array_call(node.right)
1690 }
1691 ast.ParExpr {
1692 return g.need_tmp_var_in_array_call(node.expr)
1693 }
1694 ast.PostfixExpr {
1695 return g.need_tmp_var_in_array_call(node.expr)
1696 }
1697 ast.PrefixExpr {
1698 return g.need_tmp_var_in_array_call(node.right)
1699 }
1700 ast.RangeExpr {
1701 return g.need_tmp_var_in_array_call(node.low) || g.need_tmp_var_in_array_call(node.high)
1702 }
1703 ast.SelectorExpr {
1704 return g.need_tmp_var_in_array_call(node.expr)
1705 }
1706 else {}
1707 }
1708
1709 return false
1710}
1711
1712// infix_expr_and_or_op generates code for `&&` and `||`
1713fn (mut g Gen) infix_expr_and_or_op(node ast.InfixExpr) {
1714 if g.need_tmp_var_in_array_call(node.right) && g.inside_ternary == 0 {
1715 // `if a == 0 || arr.any(it.is_letter()) {...}`
1716 tmp := g.new_tmp_var()
1717 cur_line := g.go_before_last_stmt().trim_space()
1718 g.empty_line = true
1719 if g.infix_left_var_name.len > 0 {
1720 g.write('bool ${tmp} = ((${g.infix_left_var_name}) && ')
1721 } else {
1722 g.write('bool ${tmp} = (')
1723 }
1724 g.expr(node.left)
1725 g.writeln(');')
1726 g.set_current_pos_as_last_stmt_pos()
1727 g.write('${cur_line} ${tmp} ${node.op.str()} ')
1728 g.infix_left_var_name = if node.op == .and { tmp } else { '!${tmp}' }
1729 g.expr(node.right)
1730 g.infix_left_var_name = ''
1731 } else if g.need_tmp_var_in_expr(node.right) && g.inside_ternary == 0 {
1732 prev_inside_ternary := g.inside_ternary
1733 g.inside_ternary = 0
1734 tmp := g.new_tmp_var()
1735 cur_line := g.go_before_last_stmt().trim_space()
1736 g.empty_line = true
1737 if g.infix_left_var_name.len > 0 {
1738 g.write('bool ${tmp} = ((${g.infix_left_var_name}) && ')
1739 } else {
1740 g.write('bool ${tmp} = (')
1741 }
1742 g.expr(node.left)
1743 g.writeln(');')
1744 // Evaluate the right side with short-circuit: only evaluate if
1745 // left side is true (for &&) or false (for ||).
1746 // Use an if-block to prevent nested go_before_last_stmt() calls
1747 // from grabbing `cur_line` content during right-side evaluation.
1748 cond := if node.op == .and { tmp } else { '!${tmp}' }
1749 g.writeln('if (${cond}) {')
1750 g.indent++
1751 g.set_current_pos_as_last_stmt_pos()
1752 g.infix_left_var_name = ''
1753 g.write('${tmp} = ')
1754 g.expr(node.right)
1755 g.writeln(';')
1756 g.indent--
1757 g.writeln('}')
1758 g.set_current_pos_as_last_stmt_pos()
1759 g.write('${cur_line} ${tmp}')
1760 g.inside_ternary = prev_inside_ternary
1761 } else {
1762 g.gen_plain_infix_expr(node)
1763 }
1764}
1765
1766fn (mut g Gen) gen_is_none_check(node ast.InfixExpr) {
1767 if node.left in [ast.Ident, ast.SelectorExpr, ast.IndexExpr, ast.CallExpr, ast.CTempVar, ast.CastExpr] {
1768 // When a sumtype variable has been comptime-smartcast to an option variant
1769 // (e.g. `$if t is ?string { if t == none { ... } }`), we need to access the
1770 // sumtype's variant field directly rather than using .data on the sumtype.
1771 if node.left is ast.Ident && node.left.obj is ast.Var
1772 && node.left.obj.ct_type_var == .smartcast {
1773 obj_sym := g.table.sym(g.unwrap_generic(node.left.obj.typ))
1774 if obj_sym.kind == .sum_type {
1775 ctyp := g.unwrap_generic(g.type_resolver.get_type(node.left))
1776 cur_variant_sym := g.table.sym(ctyp)
1777 variant_name := g.get_sumtype_variant_name(ctyp, cur_variant_sym)
1778 dot := if node.left.obj.orig_type.is_ptr() { '->' } else { '.' }
1779 // Sumtype stores option variants as pointers, so use -> to access state
1780 g.write('${node.left.name}${dot}_${variant_name}->state')
1781 g.write(' ${node.op.str()} 2') // none state
1782 return
1783 }
1784 }
1785 // For Ident nodes that have been unwrapped by a smartcast (e.g. from a
1786 // none-guard fallthrough), write the variable name directly to avoid
1787 // g.expr() generating the unwrapped data access instead of the option wrapper.
1788 if node.left is ast.Ident && node.left.obj is ast.Var && node.left.obj.is_unwrapped {
1789 name := c_name(node.left.name)
1790 if node.left.is_auto_heap() {
1791 g.write('(*${name})')
1792 } else {
1793 g.write(name)
1794 }
1795 } else {
1796 old_inside_opt_or_res := g.inside_opt_or_res
1797 g.inside_opt_or_res = true
1798 g.write('(')
1799 g.expr(node.left)
1800 g.write(')')
1801 g.inside_opt_or_res = old_inside_opt_or_res
1802 }
1803 dot_or_ptr := if !node.left_type.has_flag(.option_mut_param_t) { '.' } else { '->' }
1804 g.write('${dot_or_ptr}state')
1805 } else {
1806 stmt_str := g.go_before_last_stmt().trim_space()
1807 g.empty_line = true
1808 left_var := g.expr_with_opt(node.left, node.left_type, node.left_type)
1809 g.writeln(';')
1810 g.write2(stmt_str, ' ')
1811 dot_or_ptr := if !node.left_type.has_flag(.option_mut_param_t) { '.' } else { '->' }
1812 g.write('${left_var}${dot_or_ptr}state')
1813 }
1814 g.write(' ${node.op.str()} 2') // none state
1815}
1816
1817struct VSafeArithmeticOp {
1818 typ ast.Type
1819 op token.Kind
1820}
1821
1822fn (mut g Gen) normalized_power_result_type(result_type ast.Type, left_type ast.Type, right_type ast.Type) ast.Type {
1823 mut typ :=
1824 g.unwrap_generic(g.recheck_concrete_type(result_type)).clear_flag(.shared_f).clear_flag(.atomic_f)
1825 if typ == 0 || typ == ast.void_type {
1826 typ = g.unwrap_generic(g.type_resolver.promote_type(g.unwrap_generic(left_type),
1827 g.unwrap_generic(right_type))).clear_flag(.shared_f).clear_flag(.atomic_f)
1828 }
1829 if typ == ast.int_literal_type {
1830 if left_type !in [ast.int_literal_type, ast.float_literal_type] {
1831 typ = g.unwrap_generic(left_type)
1832 } else if right_type !in [ast.int_literal_type, ast.float_literal_type] {
1833 typ = g.unwrap_generic(right_type)
1834 } else {
1835 typ = ast.int_type
1836 }
1837 } else if typ == ast.float_literal_type {
1838 if left_type !in [ast.int_literal_type, ast.float_literal_type] {
1839 typ = g.unwrap_generic(left_type)
1840 } else if right_type !in [ast.int_literal_type, ast.float_literal_type] {
1841 typ = g.unwrap_generic(right_type)
1842 } else {
1843 typ = ast.f64_type
1844 }
1845 }
1846 return typ.clear_flag(.shared_f).clear_flag(.atomic_f)
1847}
1848
1849fn (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) {
1850 power_result_type := g.normalized_power_result_type(result_type, left_type, right_type)
1851 builtin_power_type := g.table.unalias_num_type(power_result_type)
1852 result_styp := g.styp(power_result_type)
1853 g.uses_power = true
1854 if builtin_power_type == ast.f32_type {
1855 g.write('(${result_styp})powf(')
1856 g.expr_with_cast(left, left_type, ast.f32_type)
1857 g.write(', ')
1858 g.expr_with_cast(right, right_type, ast.f32_type)
1859 g.write(')')
1860 return
1861 }
1862 if builtin_power_type.is_float() {
1863 g.write('(${result_styp})pow(')
1864 g.expr_with_cast(left, left_type, ast.f64_type)
1865 g.write(', ')
1866 g.expr_with_cast(right, right_type, ast.f64_type)
1867 g.write(')')
1868 return
1869 }
1870 if builtin_power_type.is_unsigned() {
1871 g.uses_power_u64 = true
1872 g.write('(${result_styp})__v_pow_u64(')
1873 g.expr_with_cast(left, left_type, ast.u64_type)
1874 g.write(', ')
1875 g.expr_with_cast(right, right_type, ast.i64_type)
1876 g.write(')')
1877 return
1878 }
1879 g.write('(${result_styp})__v_pow_i64(')
1880 g.expr_with_cast(left, left_type, ast.i64_type)
1881 g.write(', ')
1882 g.expr_with_cast(right, right_type, ast.i64_type)
1883 g.write(')')
1884}
1885
1886// gen_plain_infix_expr generates basic code for infix expressions,
1887// without any overloading of any kind
1888// i.e. v`a + 1` => c`a + 1`
1889// It handles auto dereferencing of variables, as well as automatic casting
1890// (see Gen.expr_with_cast for more details)
1891fn (mut g Gen) gen_plain_infix_expr(node ast.InfixExpr) {
1892 mut resolved_left_type := g.resolved_expr_type(node.left, node.left_type)
1893 mut resolved_right_type := g.resolved_expr_type(node.right, node.right_type)
1894 // Don't override smartcasted concrete types with sumtype/interface from
1895 // resolved_expr_type (which doesn't check scope smartcasts).
1896 if resolved_left_type != 0 {
1897 resolved_sym := g.table.sym(resolved_left_type)
1898 left_sym := g.table.sym(node.left_type)
1899 is_sumtype_override := resolved_sym.kind in [.sum_type, .interface]
1900 && left_sym.kind !in [.sum_type, .interface]
1901 is_option_introduction := resolved_left_type.has_flag(.option)
1902 && !node.left_type.has_flag(.option)
1903 if is_sumtype_override || is_option_introduction {
1904 resolved_left_type = node.left_type
1905 }
1906 }
1907 if resolved_right_type != 0 {
1908 resolved_sym := g.table.sym(resolved_right_type)
1909 right_sym := g.table.sym(node.right_type)
1910 is_sumtype_override := resolved_sym.kind in [.sum_type, .interface]
1911 && right_sym.kind !in [.sum_type, .interface]
1912 is_option_introduction := resolved_right_type.has_flag(.option)
1913 && !node.right_type.has_flag(.option)
1914 if is_sumtype_override || is_option_introduction {
1915 resolved_right_type = node.right_type
1916 }
1917 }
1918 if node.op == .power {
1919 power_left_type := if resolved_left_type != 0 { resolved_left_type } else { node.left_type }
1920 power_right_type := if resolved_right_type != 0 {
1921 resolved_right_type
1922 } else {
1923 node.right_type
1924 }
1925 g.gen_power_expr_from_types(node.left, power_left_type, node.right, power_right_type,
1926 node.promoted_type)
1927 return
1928 }
1929 $if trace_ci_fixes ? {
1930 if g.file.path.contains('binary_search_tree.v') && node.right is ast.SelectorExpr {
1931 if node.right.expr is ast.Ident && node.right.expr.name == 'tree' {
1932 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}')
1933 }
1934 }
1935 }
1936 mut needs_cast := resolved_left_type.is_number() && resolved_right_type.is_number()
1937 && node.op in [.plus, .minus, .mul, .div, .mod] && !(g.pref.translated
1938 || g.file.is_translated)
1939 mut typ := node.promoted_type
1940 mut typ_str := g.styp(typ)
1941 if needs_cast {
1942 typ = if node.left_ct_expr {
1943 g.type_resolver.get_type_or_default(node.left, node.left_type)
1944 } else if node.left !in [ast.Ident, ast.CastExpr] && node.right_ct_expr {
1945 g.type_resolver.get_type_or_default(node.right, node.promoted_type)
1946 } else {
1947 node.promoted_type
1948 }
1949 // In generic contexts, the promoted type may be stale from a previous
1950 // instantiation. Recompute from the resolved operand types.
1951 if g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0 {
1952 resolved_promoted := g.type_resolver.promote_type(g.unwrap_generic(resolved_left_type),
1953 g.unwrap_generic(resolved_right_type))
1954 if resolved_promoted != ast.void_type {
1955 typ = resolved_promoted
1956 }
1957 }
1958 typ_str = g.styp(typ)
1959 // Skip redundant cast when operands already have the same type,
1960 // but keep it for types smaller than int (u8, i8, u16, i16) because
1961 // C integer promotion rules would widen them to int.
1962 if resolved_left_type == resolved_right_type && resolved_left_type == typ {
1963 typ_kind := g.table.type_kind(typ)
1964 if typ_kind !in [.u8, .i8, .u16, .i16] {
1965 needs_cast = false
1966 }
1967 }
1968 if needs_cast {
1969 g.write('(${typ_str})(')
1970 }
1971 }
1972 // do not use promoted_type for overflow detect
1973 left_type := g.unwrap_generic(resolved_left_type)
1974 checkoverflow_op := g.do_int_overflow_checks && left_type.is_int()
1975 is_safe_add := checkoverflow_op && node.op == .plus
1976 is_safe_sub := checkoverflow_op && node.op == .minus
1977 is_safe_mul := checkoverflow_op && node.op == .mul
1978 is_integer_div_mod := g.table.final_sym(g.unwrap_generic(typ)).is_int()
1979 is_safe_div := node.op == .div && is_integer_div_mod
1980 is_safe_mod := node.op == .mod && is_integer_div_mod
1981 if resolved_left_type.is_ptr() && node.left.is_auto_deref_var()
1982 && !resolved_right_type.is_pointer() {
1983 g.write('*')
1984 } else if !g.inside_interface_deref && node.left is ast.Ident
1985 && g.table.is_interface_var(node.left.obj) {
1986 inside_interface_deref_old := g.inside_interface_deref
1987 g.inside_interface_deref = true
1988 defer(fn) {
1989 g.inside_interface_deref = inside_interface_deref_old
1990 }
1991 }
1992 is_ctemp_fixed_ret := node.op in [.eq, .ne] && node.left is ast.CTempVar
1993 && node.left.is_fixed_ret
1994 if is_ctemp_fixed_ret {
1995 if node.op == .eq {
1996 g.write('!')
1997 }
1998 g.write('memcmp(')
1999 }
2000 mut opstr := node.op.str()
2001 if is_safe_add || is_safe_sub || is_safe_mul || is_safe_div || is_safe_mod {
2002 overflow_styp := g.styp(get_overflow_fn_type(left_type))
2003 vsafe_fn_name := match true {
2004 is_safe_add { 'builtin__overflow__add_${overflow_styp}' }
2005 is_safe_sub { 'builtin__overflow__sub_${overflow_styp}' }
2006 is_safe_mul { 'builtin__overflow__mul_${overflow_styp}' }
2007 is_safe_div { 'VSAFE_DIV_${typ_str}' }
2008 is_safe_mod { 'VSAFE_MOD_${typ_str}' }
2009 else { '' }
2010 }
2011
2012 g.write(vsafe_fn_name)
2013 g.write('(')
2014 if is_safe_div || is_safe_mod {
2015 g.vsafe_arithmetic_ops[vsafe_fn_name] = VSafeArithmeticOp{
2016 typ: typ
2017 op: node.op
2018 }
2019 }
2020 opstr = ','
2021 }
2022 g.expr(node.left)
2023 if !is_ctemp_fixed_ret {
2024 g.write(' ')
2025 g.write(opstr)
2026 g.write(' ')
2027 } else {
2028 g.write(', ')
2029 }
2030
2031 if is_ctemp_fixed_ret {
2032 g.write('(${g.styp(resolved_right_type)})')
2033 }
2034 if resolved_right_type.is_ptr() && node.right.is_auto_deref_var()
2035 && !resolved_left_type.is_pointer() {
2036 g.write('*')
2037 g.expr(node.right)
2038 } else {
2039 g.expr_with_cast(node.right, resolved_right_type, resolved_left_type)
2040 }
2041 if is_ctemp_fixed_ret {
2042 g.write(', sizeof(${g.styp(resolved_right_type)}))')
2043 }
2044 if is_safe_add || is_safe_sub || is_safe_mul || is_safe_div || is_safe_mod {
2045 g.write(')')
2046 }
2047 if needs_cast {
2048 g.write(')')
2049 }
2050}
2051
2052fn (mut g Gen) op_arg(expr ast.Expr, expected ast.Type, got ast.Type) {
2053 mut needs_closing := false
2054 mut nr_muls := got.nr_muls()
2055 if expected.is_ptr() {
2056 if nr_muls > 0 {
2057 nr_muls--
2058 } else {
2059 if expr.is_lvalue() {
2060 g.write('&')
2061 } else {
2062 styp := g.styp(got.set_nr_muls(0))
2063 g.write('ADDR(${styp}, ')
2064 needs_closing = true
2065 }
2066 }
2067 }
2068 g.write('*'.repeat(nr_muls))
2069 g.expr(expr)
2070 if needs_closing {
2071 g.write(')')
2072 }
2073}
2074
2075struct GenSafeIntegerCfg {
2076 op token.Kind
2077 reverse bool
2078 unsigned_type ast.Type
2079 unsigned_expr ast.Expr
2080 signed_type ast.Type
2081 signed_expr ast.Expr
2082}
2083
2084// gen_safe_integer_infix_expr generates code for comparison of
2085// unsigned and signed integers
2086fn (mut g Gen) gen_safe_integer_infix_expr(cfg GenSafeIntegerCfg) {
2087 bitsize := if cfg.unsigned_type.idx() == ast.u32_type_idx
2088 && cfg.signed_type.idx() != ast.i64_type_idx {
2089 32
2090 } else {
2091 64
2092 }
2093 op_idx := int(cfg.op) - int(token.Kind.eq)
2094 op_str := if cfg.reverse { cmp_rev[op_idx] } else { cmp_str[op_idx] }
2095 g.write('_us${bitsize}_${op_str}(')
2096 g.expr(cfg.unsigned_expr)
2097 g.write(',')
2098 g.expr(cfg.signed_expr)
2099 g.write(')')
2100}
2101