v2 / vlib / v / gen / c / assign.v
2769 lines · 2723 sloc · 95.85 KB · 142f665ddd1b56426ca611b16c8dc7088aa05190
Raw
1// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4module c
5
6import v.ast
7import v.util
8import v.token
9
10fn smartcast_selector_expr_str(expr ast.SelectorExpr) string {
11 mut expr_str := expr.expr.str()
12 if expr.expr is ast.ParExpr && expr.expr.expr is ast.AsCast {
13 expr_str = expr.expr.expr.expr.str()
14 }
15 return expr_str
16}
17
18fn (g &Gen) is_smartcast_assign_lhs(expr ast.Expr) bool {
19 match expr {
20 ast.Ident {
21 if expr.obj is ast.Var && expr.obj.smartcasts.len > 0 {
22 if expr.obj.is_mut && expr.obj.orig_type != 0 {
23 orig_sym := g.table.final_sym(expr.obj.orig_type)
24 if orig_sym.kind == .sum_type {
25 return false
26 }
27 }
28 return true
29 }
30 return false
31 }
32 ast.SelectorExpr {
33 if expr.expr_type == 0 {
34 return false
35 }
36 scope_field := expr.scope.find_struct_field(smartcast_selector_expr_str(expr),
37 expr.expr_type, expr.field_name)
38 if scope_field == unsafe { nil } || scope_field.smartcasts.len == 0 {
39 return false
40 }
41 // Option field smartcast on LHS: the assignment replaces the option
42 // as a whole (e.g. `s.x = 10` or `s.x = none` inside `if s.x != none`),
43 // so skip unwrap treatment that's only meant for sumtype reassignments.
44 if scope_field.orig_type.has_flag(.option) {
45 return false
46 }
47 return true
48 }
49 else {
50 return false
51 }
52 }
53}
54
55fn (mut g Gen) expr_in_value_context(expr ast.Expr, value_type ast.Type, expected_type ast.Type) {
56 mut expr_copy := expr
57 match mut expr_copy {
58 ast.IfExpr {
59 if !expr_copy.is_expr || expr_copy.typ == 0 || expr_copy.typ == ast.void_type {
60 expr_copy.is_expr = true
61 expr_copy.typ = if value_type != 0 && value_type != ast.void_type {
62 value_type
63 } else {
64 expected_type
65 }
66 }
67 }
68 ast.MatchExpr {
69 if !expr_copy.is_expr || expr_copy.return_type == 0
70 || expr_copy.return_type == ast.void_type {
71 resolved_value_type := if value_type != 0 && value_type != ast.void_type {
72 value_type
73 } else {
74 expected_type
75 }
76 expr_copy.is_expr = true
77 expr_copy.return_type = resolved_value_type
78 if expr_copy.expected_type in [0, ast.void_type, ast.none_type] {
79 expr_copy.expected_type = expected_type
80 }
81 }
82 }
83 else {}
84 }
85
86 if expected_type.has_flag(.shared_f) && !value_type.has_flag(.shared_f) && value_type.is_ptr()
87 && !expected_type.has_option_or_result() {
88 g.expr_with_cast(expr_copy, value_type, expected_type)
89 return
90 }
91 g.expr(expr_copy)
92}
93
94fn (mut g Gen) auto_heap_assignment_uses_existing_storage(expr ast.Expr, expr_type ast.Type) bool {
95 // Reusing container-backed storage avoids leaking a fresh HEAP() copy for
96 // `@[heap]` values like `x := arr[i]`. Keep the old path under autofree,
97 // because aliasing array storage there would hand ownership to the local.
98 if g.is_autofree {
99 return false
100 }
101 return match expr {
102 ast.IndexExpr { g.auto_heap_array_index_uses_existing_storage(expr, expr_type) }
103 else { false }
104 }
105}
106
107fn (mut g Gen) auto_heap_array_index_uses_existing_storage(node ast.IndexExpr, expr_type ast.Type) bool {
108 if node.index is ast.RangeExpr || node.or_expr.kind != .absent || node.is_option {
109 return false
110 }
111 mut resolved_expr_type := g.recheck_concrete_type(g.resolved_expr_type(ast.Expr(node),
112 expr_type))
113 if resolved_expr_type == 0 || resolved_expr_type == ast.void_type {
114 resolved_expr_type = g.recheck_concrete_type(expr_type)
115 }
116 if resolved_expr_type == 0 || resolved_expr_type == ast.void_type {
117 resolved_expr_type = g.recheck_concrete_type(node.typ)
118 }
119 elem_type := g.unwrap_generic(resolved_expr_type)
120 if elem_type == 0 || elem_type.is_ptr() || !g.table.final_sym(elem_type).is_heap() {
121 return false
122 }
123 mut resolved_left_type := g.recheck_concrete_type(g.resolved_expr_type(node.left,
124 node.left_type))
125 if resolved_left_type == 0 || resolved_left_type == ast.void_type {
126 resolved_left_type = g.recheck_concrete_type(node.left_type)
127 }
128 left_sym := g.table.final_sym(g.unwrap_generic(resolved_left_type))
129 return left_sym.kind == .array
130}
131
132fn (mut g Gen) write_auto_heap_assignment_expr(expr ast.Expr, expr_type ast.Type) bool {
133 return match expr {
134 ast.IndexExpr { g.write_auto_heap_array_index_expr(expr, expr_type) }
135 else { false }
136 }
137}
138
139fn (mut g Gen) write_auto_heap_array_index_expr(node ast.IndexExpr, expr_type ast.Type) bool {
140 if !g.auto_heap_array_index_uses_existing_storage(node, expr_type) {
141 return false
142 }
143 mut resolved_expr_type := g.recheck_concrete_type(g.resolved_expr_type(ast.Expr(node),
144 expr_type))
145 if resolved_expr_type == 0 || resolved_expr_type == ast.void_type {
146 resolved_expr_type = g.recheck_concrete_type(expr_type)
147 }
148 if resolved_expr_type == 0 || resolved_expr_type == ast.void_type {
149 resolved_expr_type = g.recheck_concrete_type(node.typ)
150 }
151 elem_type := g.unwrap_generic(resolved_expr_type)
152 elem_type_str := g.styp(elem_type)
153 mut resolved_left_type := g.recheck_concrete_type(g.resolved_expr_type(node.left,
154 node.left_type))
155 if resolved_left_type == 0 || resolved_left_type == ast.void_type {
156 resolved_left_type = g.recheck_concrete_type(node.left_type)
157 }
158 left_type := if resolved_left_type != 0 { resolved_left_type } else { node.left_type }
159 left_is_ptr := left_type.is_ptr() || node.left.is_auto_deref_var()
160 left_is_shared := left_type.has_flag(.shared_f)
161 array_get_fn := if node.is_gated { 'builtin__array_get_ni' } else { 'builtin__array_get' }
162 g.write('((${elem_type_str}*)${array_get_fn}(')
163 if left_is_ptr && !left_is_shared {
164 g.write('*')
165 }
166 if node.left is ast.IndexExpr {
167 g.inside_array_index = true
168 g.expr(ast.Expr(node.left))
169 g.inside_array_index = false
170 } else {
171 g.expr(ast.Expr(node.left))
172 }
173 if left_is_shared {
174 if left_is_ptr {
175 g.write('->val')
176 } else {
177 g.write('.val')
178 }
179 }
180 g.write(', ')
181 g.expr(node.index)
182 g.write('))')
183 return true
184}
185
186fn (mut g Gen) write_assign_target_expr(left ast.Expr, var_type ast.Type) {
187 if left.is_auto_deref_var() && !var_type.has_flag(.shared_f) {
188 g.write('*')
189 }
190 g.expr(left)
191 if var_type.has_flag(.shared_f) {
192 g.write('->val')
193 }
194}
195
196fn (mut g Gen) write_assign_value_expr(left ast.Expr, var_type ast.Type) {
197 old_is_assign_lhs := g.is_assign_lhs
198 g.is_assign_lhs = false
199 defer {
200 g.is_assign_lhs = old_is_assign_lhs
201 }
202 if left.is_auto_deref_var() && !var_type.has_flag(.shared_f) {
203 g.write('*')
204 }
205 g.expr(left)
206 if var_type.has_flag(.shared_f) {
207 g.write('->val')
208 }
209}
210
211fn (mut g Gen) gen_power_assign_expr(left ast.Expr, left_type ast.Type, right ast.Expr, right_type ast.Type) {
212 power_result_type := g.normalized_power_result_type(left_type.clear_flag(.shared_f).clear_flag(.atomic_f),
213 left_type.clear_flag(.shared_f).clear_flag(.atomic_f), right_type)
214 builtin_power_type := g.table.unalias_num_type(power_result_type)
215 result_styp := g.styp(power_result_type)
216 g.uses_power = true
217 if builtin_power_type == ast.f32_type {
218 g.write('(${result_styp})powf((${g.styp(ast.f32_type)})(')
219 g.write_assign_value_expr(left, left_type)
220 g.write('), ')
221 g.expr_with_cast(right, right_type, ast.f32_type)
222 g.write(')')
223 return
224 }
225 if builtin_power_type.is_float() {
226 g.write('(${result_styp})pow((${g.styp(ast.f64_type)})(')
227 g.write_assign_value_expr(left, left_type)
228 g.write('), ')
229 g.expr_with_cast(right, right_type, ast.f64_type)
230 g.write(')')
231 return
232 }
233 if builtin_power_type.is_unsigned() {
234 g.uses_power_u64 = true
235 g.write('(${result_styp})__v_pow_u64((${g.styp(ast.u64_type)})(')
236 g.write_assign_value_expr(left, left_type)
237 g.write('), ')
238 g.expr_with_cast(right, right_type, ast.i64_type)
239 g.write(')')
240 return
241 }
242 g.write('(${result_styp})__v_pow_i64((${g.styp(ast.i64_type)})(')
243 g.write_assign_value_expr(left, left_type)
244 g.write('), ')
245 g.expr_with_cast(right, right_type, ast.i64_type)
246 g.write(')')
247}
248
249fn assign_expr_unwraps_option_or_result(expr ast.Expr) bool {
250 return match expr {
251 ast.CallExpr { expr.or_block.kind != .absent }
252 ast.ComptimeCall { expr.or_block.kind != .absent }
253 ast.ComptimeSelector { expr.or_block.kind != .absent }
254 ast.Ident { expr.or_expr.kind != .absent }
255 ast.IndexExpr { expr.or_expr.kind != .absent && !expr.typ.has_option_or_result() }
256 ast.InfixExpr { expr.or_block.kind != .absent }
257 ast.PostfixExpr { expr.op == .question }
258 ast.PrefixExpr { expr.or_block.kind != .absent }
259 ast.SelectorExpr { expr.or_block.kind != .absent }
260 else { false }
261 }
262}
263
264fn (mut g Gen) decl_assign_struct_init_needs_tmp(expr ast.Expr) bool {
265 mut node := ast.StructInit{}
266 match expr {
267 ast.StructInit {
268 node = expr
269 }
270 ast.ParExpr {
271 if expr.expr !is ast.StructInit {
272 return false
273 }
274 node = expr.expr as ast.StructInit
275 }
276 else {
277 return false
278 }
279 }
280
281 sym := g.table.final_sym(g.unwrap_generic(g.recheck_concrete_type(node.typ)))
282 if sym.info !is ast.Struct {
283 return false
284 }
285 if g.styp(node.typ) in skip_struct_init {
286 return false
287 }
288 info := sym.info as ast.Struct
289 if node.no_keys {
290 return node.init_fields.len < info.fields.len
291 }
292 init_field_names := node.init_fields.map(it.name)
293 return info.fields.any(it.name !in init_field_names)
294}
295
296fn (mut g Gen) gen_self_recursing_anon_fn_capture_patch(left ast.Expr, anon_fn ast.AnonFn) {
297 if left !is ast.Ident || anon_fn.inherited_vars.len == 0 {
298 return
299 }
300 ident := left as ast.Ident
301 if ident.name !in anon_fn.inherited_vars.map(it.name) {
302 return
303 }
304 ctx_struct := g.closure_ctx(anon_fn.decl)
305 left_expr := g.expr_string(left)
306 g.writeln('((${ctx_struct}*)builtin__closure__closure_data(${left_expr}))->${c_name(ident.name)} = ${left_expr};')
307}
308
309fn (mut g Gen) expr_with_opt_or_block(expr ast.Expr, expr_typ ast.Type, var_expr ast.Expr, ret_typ ast.Type,
310 in_heap bool) {
311 gen_or := expr is ast.Ident && expr.or_expr.kind != .absent
312 if gen_or {
313 old_inside_opt_or_res := g.inside_opt_or_res
314 g.inside_opt_or_res = true
315 expr_var := if expr is ast.Ident && expr.kind == .constant {
316 g.c_const_name(expr.name)
317 } else if expr is ast.Ident && expr.is_auto_heap() {
318 '(*${expr.name})'
319 } else {
320 '${expr}'
321 }
322 dot_or_ptr := if !expr_typ.has_flag(.option_mut_param_t) { '.' } else { '-> ' }
323 mut heap_line := ''
324 if in_heap {
325 // When the variable needs heap allocation, the caller has already
326 // written the partial line `TYPE *var = HEAP(TYPE, (`.
327 // We must NOT access the option's .data inside the HEAP macro
328 // before checking the state, because the option may be `none`.
329 // Pull back the partial line, emit the state check first, then
330 // write the assignment with data access after the check.
331 heap_line = g.go_before_last_stmt()
332 g.empty_line = true
333 } else {
334 g.expr_with_cast(expr, expr_typ, ret_typ)
335 g.writeln(';')
336 }
337 g.writeln('if (${c_name(expr_var)}${dot_or_ptr}state != 0) { // assign')
338 if expr is ast.Ident && expr.or_expr.kind == .propagate_option {
339 g.writeln('\tbuiltin__panic_option_not_set(_S("none"));')
340 } else {
341 g.inside_or_block = true
342 defer {
343 g.inside_or_block = false
344 }
345 or_expr := (expr as ast.Ident).or_expr
346 stmts := or_expr.stmts
347 scope := or_expr.scope
348 last_stmt := stmts.last()
349 // handles stmt block which returns something
350 // e.g. { return none }
351 if stmts.len > 0 && last_stmt is ast.ExprStmt && last_stmt.typ != ast.void_type {
352 var_expr_name := c_name(var_expr.str())
353 if last_stmt.expr is ast.Ident && last_stmt.expr.or_expr.kind != .absent {
354 g.write('${var_expr_name} = ')
355 g.expr_with_opt_or_block(ast.Expr(last_stmt.expr), last_stmt.typ, var_expr,
356 ret_typ, in_heap)
357 } else {
358 g.gen_or_block_stmts(var_expr_name, '', stmts, ret_typ, false, scope,
359 expr.pos())
360 }
361 } else {
362 // handles stmt block which doesn't returns value
363 // e.g. { return }
364 g.stmts(stmts)
365 if stmts.len > 0 && last_stmt is ast.ExprStmt {
366 g.writeln(';')
367 }
368 g.write_defer_stmts(scope, false, expr.pos())
369 }
370 }
371 g.writeln('}')
372 if in_heap {
373 // Now that the state has been checked (and we haven't returned/panicked),
374 // it is safe to access .data and do the heap allocation.
375 g.write(heap_line)
376 g.expr_with_cast(expr, expr_typ, ret_typ)
377 g.writeln('));')
378 }
379 g.inside_opt_or_res = old_inside_opt_or_res
380 } else {
381 g.expr_with_opt(expr, expr_typ, ret_typ)
382 if in_heap {
383 g.write('))')
384 }
385 }
386}
387
388// expr_opt_with_alias handles conversion from different option alias type name
389fn (mut g Gen) expr_opt_with_alias(expr ast.Expr, expr_typ ast.Type, ret_typ ast.Type) string {
390 styp := g.base_type(ret_typ)
391
392 line := g.go_before_last_stmt().trim_space()
393 g.empty_line = true
394
395 ret_var := g.new_tmp_var()
396 ret_styp := g.styp(ret_typ).replace('*', '_ptr')
397 g.writeln('${ret_styp} ${ret_var} = {.state=2, .err=_const_none__, .data={E_STRUCT}};')
398
399 if expr !is ast.None {
400 is_option_expr := expr_typ.has_flag(.option)
401 if is_option_expr {
402 g.write('builtin___option_clone((${option_name}*)')
403 } else {
404 g.write('builtin___option_ok(&(${styp}[]){ ')
405 }
406 has_addr := is_option_expr && expr !in [ast.Ident, ast.SelectorExpr]
407 if has_addr {
408 expr_styp := g.styp(expr_typ).replace('*', '_ptr')
409 g.write('ADDR(${expr_styp}, ')
410 } else if is_option_expr {
411 g.write('&')
412 }
413 g.expr(expr)
414 if has_addr {
415 g.write(')')
416 }
417 if !is_option_expr {
418 g.write(' }')
419 }
420 g.writeln(', (${option_name}*)&${ret_var}, sizeof(${styp}));')
421 }
422 g.write(line)
423 if g.inside_return {
424 g.write(' ')
425 }
426 g.write(ret_var)
427 return ret_var
428}
429
430// expr_opt_with_cast is used in cast expr when converting compatible option types
431// e.g. ?int(?u8(0))
432fn (mut g Gen) expr_opt_with_cast(expr ast.Expr, expr_typ ast.Type, ret_typ ast.Type) string {
433 if !expr_typ.has_flag(.option) || !ret_typ.has_flag(.option) {
434 panic('cgen: expected expr_type and ret_typ to be options')
435 }
436
437 if expr_typ.idx() == ret_typ.idx() && g.table.sym(expr_typ).kind != .alias {
438 return g.expr_with_opt(expr, expr_typ, ret_typ)
439 } else {
440 if expr is ast.CallExpr && expr.return_type.has_flag(.option) {
441 return g.expr_opt_with_alias(expr, expr_typ, ret_typ)
442 } else {
443 past := g.past_tmp_var_new()
444 defer {
445 g.past_tmp_var_done(past)
446 }
447 unwrapped_ret := g.unwrap_generic(ret_typ)
448 // Unwrap type aliases to ensure sizeof uses the base type size, not the alias size
449 // This fixes the ASAN stack-buffer-overflow issue when using type aliases like MaybeInt = ?int
450 unaliased_ret := g.table.unaliased_type(unwrapped_ret)
451 styp := g.base_type(unaliased_ret)
452 decl_styp := g.styp(unwrapped_ret).replace('*', '_ptr')
453 g.writeln('${decl_styp} ${past.tmp_var};')
454 is_none := expr is ast.CastExpr && expr.expr is ast.None
455 if is_none {
456 g.write('builtin___option_none(&(${styp}[]) {')
457 } else {
458 g.write('builtin___option_ok(&(${styp}[]) {')
459 }
460 if expr is ast.CastExpr && expr_typ.has_flag(.option) {
461 ret_sym := g.table.sym(ret_typ)
462 if ret_sym.kind == .sum_type {
463 exp_sym := g.table.sym(expr_typ)
464 fname := g.get_sumtype_casting_fn(expr_typ, ret_typ)
465 g.call_cfn_for_casting_expr(fname, expr, ret_typ, expr_typ, expr_typ,
466 ret_sym.cname, expr_typ.is_ptr(), exp_sym.kind == .function,
467 g.styp(expr_typ))
468 } else {
469 g.write('*((${g.base_type(expr_typ)}*)')
470 g.expr(expr)
471 g.write('.data)')
472 }
473 } else {
474 old_inside_opt_or_res := g.inside_opt_or_res
475 g.inside_opt_or_res = false
476 g.expr_with_cast(expr, expr_typ, ret_typ)
477 g.inside_opt_or_res = old_inside_opt_or_res
478 }
479 g.writeln(' }, (${option_name}*)(&${past.tmp_var}), sizeof(${styp}));')
480 return past.tmp_var
481 }
482 }
483}
484
485// expr_with_opt is used in assigning an expression to an `option` variable
486// e.g. x = y (option lhs and rhs), mut x = ?int(123), y = none
487fn (mut g Gen) expr_with_opt(expr ast.Expr, expr_typ ast.Type, ret_typ ast.Type) string {
488 old_inside_opt_or_res := g.inside_opt_or_res
489 g.inside_opt_or_res = true
490 defer {
491 g.inside_opt_or_res = old_inside_opt_or_res
492 }
493 unwrapped_expr_typ := g.unwrap_generic(expr_typ)
494 unwrapped_ret_typ := g.unwrap_generic(ret_typ)
495 if unwrapped_expr_typ.has_flag(.option) && unwrapped_ret_typ.has_flag(.option)
496 && !g.is_arraymap_set
497 && expr in [ast.SelectorExpr, ast.DumpExpr, ast.Ident, ast.ComptimeSelector, ast.ComptimeCall, ast.AsCast, ast.CallExpr, ast.MatchExpr, ast.IfExpr, ast.IndexExpr, ast.UnsafeExpr, ast.CastExpr] {
498 if expr in [ast.Ident, ast.CastExpr] {
499 if unwrapped_expr_typ.idx() != unwrapped_ret_typ.idx()
500 && g.table.type_to_str(unwrapped_expr_typ) != g.table.type_to_str(unwrapped_ret_typ) {
501 return g.expr_opt_with_cast(expr, unwrapped_expr_typ, unwrapped_ret_typ)
502 }
503 }
504 g.expr(expr)
505 if expr is ast.ComptimeSelector {
506 return g.gen_comptime_selector(expr)
507 } else {
508 return expr.str()
509 }
510 } else {
511 tmp_out_var := g.new_tmp_var()
512 g.expr_with_tmp_var(expr, unwrapped_expr_typ, unwrapped_ret_typ, tmp_out_var, true)
513 return tmp_out_var
514 }
515 return ''
516}
517
518fn (g &Gen) static_init_guard_name(pos token.Pos) string {
519 return '_vstatic_init_${pos.pos}'
520}
521
522fn (mut g Gen) gen_static_decl_runtime_init(node ast.AssignStmt, left ast.Expr, left_type ast.Type, right ast.Expr, right_type ast.Type) bool {
523 if node.left.len != 1 || node.right.len != 1 || g.inside_ternary != 0 {
524 return false
525 }
526 if left !is ast.Ident {
527 return false
528 }
529 guard_name := g.static_init_guard_name(left.pos())
530 g.writeln(';')
531 g.writeln('static bool ${guard_name};')
532 g.writeln('if (!${guard_name}) {')
533 g.indent++
534 g.writeln('${guard_name} = true;')
535 old_is_assign_lhs := g.is_assign_lhs
536 g.is_assign_lhs = false
537 g.assign_stmt(ast.AssignStmt{
538 op: .assign
539 pos: node.pos
540 left: [left]
541 right: [right]
542 left_types: [left_type]
543 right_types: [right_type]
544 })
545 g.is_assign_lhs = old_is_assign_lhs
546 g.indent--
547 g.writeln('}')
548 return true
549}
550
551fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) {
552 mut node := unsafe { node_ }
553 if node.is_static {
554 is_defer_var := node.left[0] is ast.Ident && node.left[0].name in g.defer_vars
555 if is_defer_var && node.op == .decl_assign {
556 return
557 }
558 if !is_defer_var {
559 g.write('static ')
560 }
561 }
562 if node.is_volatile && node.left[0] is ast.Ident && node.left[0].name !in g.defer_vars {
563 g.write('volatile ')
564 }
565 mut return_type := ast.void_type
566 is_decl := node.op == .decl_assign
567 g.assign_op = node.op
568 g.inside_assign = true
569 g.arraymap_set_pos = 0
570 g.is_arraymap_set = false
571 g.is_assign_lhs = false
572 g.is_shared = false
573 defer {
574 g.assign_op = .unknown
575 g.inside_assign = false
576 g.assign_ct_type.clear()
577 g.expected_rhs_type_by_pos.clear()
578 g.arraymap_set_pos = 0
579 g.is_arraymap_set = false
580 g.is_assign_lhs = false
581 g.is_shared = false
582 }
583 op := if is_decl { token.Kind.assign } else { node.op }
584 right_expr := node.right[0]
585 match right_expr {
586 ast.CallExpr {
587 resolved_call_type := g.resolve_return_type(right_expr)
588 if resolved_call_type != ast.void_type {
589 return_type = resolved_call_type
590 } else {
591 return_type = right_expr.return_type
592 }
593 }
594 ast.LockExpr {
595 return_type = right_expr.typ
596 }
597 ast.MatchExpr {
598 return_type = right_expr.return_type
599 }
600 ast.IfExpr {
601 return_type = right_expr.typ
602 }
603 else {}
604 }
605
606 if node.right.len == 1 && node.left.len > 1 && node.left_types.len == node.left.len {
607 is_generic_context := g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0
608 concrete_left_types := if is_generic_context {
609 node.left_types.map(g.unwrap_generic(g.recheck_concrete_type(it)))
610 } else {
611 node.left_types
612 }
613 if concrete_left_types.all(it != 0 && !it.has_flag(.generic)
614 && !g.type_has_unresolved_generic_parts(it))
615 {
616 expected_multi_return := g.table.find_or_register_multi_return(concrete_left_types)
617 if return_type == ast.void_type || return_type == 0 {
618 return_type = expected_multi_return
619 } else if g.table.sym(return_type).kind == .multi_return
620 && return_type != expected_multi_return {
621 actual_return_types := if is_generic_context {
622 (g.table.sym(return_type).info as ast.MultiReturn).types.map(g.unwrap_generic(g.recheck_concrete_type(it)))
623 } else {
624 (g.table.sym(return_type).info as ast.MultiReturn).types
625 }
626 if actual_return_types.any(it == 0 || it.has_flag(.generic)
627 || g.type_has_unresolved_generic_parts(it))
628 {
629 return_type = expected_multi_return
630 }
631 }
632 }
633 }
634 // Free the old value assigned to this string var (only if it's `str = [new value]`
635 // or `x.str = [new value]` )
636 mut af := g.is_autofree && !g.is_builtin_mod && !g.is_autofree_tmp && node.left_types.len == 1
637 && node.left[0] in [ast.Ident, ast.SelectorExpr] && (node.op == .assign
638 || (node.op == .plus_assign && node.left_types[0] == ast.string_type))
639 if af && node.right.len == 1 && node.right[0] is ast.CallExpr {
640 call_expr := node.right[0] as ast.CallExpr
641 if call_expr.is_method && call_expr.left is ast.CallExpr {
642 af = false
643 }
644 }
645 mut sref_name := ''
646 mut type_to_free := ''
647 if af {
648 first_left_type := node.left_types[0]
649 first_left_sym := g.table.sym(node.left_types[0])
650 if first_left_type == ast.string_type
651 || (node.op == .assign && first_left_sym.kind == .array) {
652 type_to_free = if first_left_type == ast.string_type { 'string' } else { 'array' }
653 mut ok := true
654 left0 := node.left[0]
655 if left0 is ast.Ident {
656 if left0.name == '_' {
657 ok = false
658 }
659 }
660 if ok {
661 sref_name = '_sref${node.pos.pos}'
662 g.write('${type_to_free} ${sref_name} = (') // TODO: we are copying the entire string here, optimize
663 // we can't just do `.str` since we need the extra data from the string struct
664 // doing `&string` is also not an option since the stack memory with the data will be overwritten
665 if left0.is_auto_deref_var() && !first_left_type.has_flag(.shared_f) {
666 g.write('*')
667 }
668 g.expr(left0) // node.left[0])
669 if first_left_type.has_flag(.shared_f) {
670 g.write('->val')
671 }
672 g.writeln('); // free ${type_to_free} on re-assignment2')
673 defer(fn) {
674 if af {
675 g.writeln('builtin__${type_to_free}_free(&${sref_name});')
676 }
677 }
678 } else {
679 af = false
680 }
681 } else {
682 af = false
683 }
684 }
685 // TODO: g.gen_assign_vars_autofree(node)
686 // json_test failed w/o this check
687 if node.right.len == 1 && return_type != ast.void_type && return_type != 0 {
688 sym := g.table.sym(return_type)
689 if sym.kind == .multi_return {
690 g.gen_multi_return_assign(node, return_type, sym)
691 return
692 }
693 }
694 // TODO: non idents on left (exprs)
695 if node.has_cross_var {
696 g.gen_cross_var_assign(node)
697 }
698 // `a := 1` | `a,b := 1,2`
699 if node.right.len < node.left.len {
700 g.checker_bug('node.right.len < node.left.len', node.pos)
701 }
702 if node.right_types.len < node.left.len {
703 g.checker_bug('node.right_types.len < node.left.len', node.pos)
704 }
705 if node.left_types.len < node.left.len {
706 g.checker_bug('node.left_types.len < node.left.len', node.pos)
707 }
708
709 last_curr_var_name := g.curr_var_name.clone()
710 defer {
711 g.curr_var_name = last_curr_var_name
712 }
713 g.curr_var_name = []
714
715 for i, mut left in node.left {
716 mut is_auto_heap := false
717 mut is_fn_var := false
718 mut var_type := node.left_types[i]
719 mut val_type := node.right_types[i]
720 // Save original shared status of val_type before resolution blocks can overwrite it.
721 // This is needed because `val_type = var_type` in resolution blocks can lose the
722 // RHS shared flag (e.g., lock expr returning shared value to non-shared variable).
723 orig_val_shared := val_type.has_flag(.shared_f)
724 if g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0 {
725 resolved_left_type := g.recheck_concrete_type(var_type)
726 if resolved_left_type != 0 {
727 var_type = g.unwrap_generic(resolved_left_type)
728 }
729 resolved_right_type := g.recheck_concrete_type(val_type)
730 if resolved_right_type != 0 {
731 val_type = g.unwrap_generic(resolved_right_type)
732 }
733 }
734 mut val := node.right[i]
735 expected_lhs_type := var_type
736 if expected_lhs_type != 0 && expected_lhs_type != ast.void_type
737 && !expected_lhs_type.has_option_or_result() && val in [ast.IfExpr, ast.MatchExpr] {
738 g.expected_rhs_type_by_pos[val.pos().pos] = expected_lhs_type
739 }
740 mut str_add_rhs_tmp := ''
741 mut str_add_rhs_needs_free := false
742 mut skip_str_add_rhs_clone := false
743 if is_decl && g.cur_concrete_types.len > 0 && val is ast.CallExpr
744 && val.return_type_generic != 0 {
745 mut resolved_val_type := g.resolve_return_type(val).clear_option_and_result()
746 if resolved_val_type == ast.void_type || resolved_val_type.has_flag(.generic) {
747 resolved_val_type =
748 g.unwrap_generic(val.return_type_generic).clear_option_and_result()
749 }
750 // When unwrap_generic couldn't resolve (e.g. cur_fn.generic_names is empty
751 // for methods whose generics come from the receiver), try resolving using
752 // the receiver's generic type names from the current fn.
753 if (resolved_val_type == ast.void_type || resolved_val_type.has_flag(.generic)
754 || g.type_has_unresolved_generic_parts(resolved_val_type))
755 && g.cur_fn != unsafe { nil } && g.cur_fn.is_method
756 && g.cur_fn.receiver.typ.has_flag(.generic) {
757 receiver_generic_names := g.table.generic_type_names(g.cur_fn.receiver.typ)
758 if receiver_generic_names.len == g.cur_concrete_types.len {
759 if gen_type := g.table.convert_generic_type(val.return_type_generic,
760 receiver_generic_names, g.cur_concrete_types)
761 {
762 resolved_val_type = gen_type.clear_option_and_result()
763 }
764 }
765 }
766 if resolved_val_type != ast.void_type && !resolved_val_type.has_flag(.generic) {
767 var_type = ast.mktyp(resolved_val_type)
768 val_type = resolved_val_type
769 }
770 }
771 mut is_call := false
772 mut gen_or := false
773 mut blank_assign := false
774 mut is_va_list := false // C varargs
775 mut ident := ast.Ident{
776 scope: unsafe { nil }
777 }
778 if is_decl {
779 // Strip shared/atomic flags and pointer from the default type
780 // so that `resolved_expr_type` doesn't propagate LHS declaration
781 // properties to the resolved RHS type (e.g., for string/int literals
782 // that fall through to the default).
783 mut decl_default := var_type.clear_flag(.shared_f).clear_flag(.atomic_f)
784 if var_type.has_flag(.shared_f) && decl_default.nr_muls() > 0 {
785 decl_default = decl_default.set_nr_muls(decl_default.nr_muls() - 1)
786 }
787 // Check if this variable is auto-heap promoted early so we can
788 // skip resolved_expr_type updates that would add extra pointer
789 // levels (the HEAP macro handles the pointer promotion).
790 left_is_auto_heap := left is ast.Ident && g.resolved_ident_is_auto_heap_not_stack(left)
791 resolved_decl_type := g.resolved_expr_type(val, decl_default)
792 if resolved_decl_type != 0 && resolved_decl_type != ast.void_type && !left_is_auto_heap {
793 mut resolved_unwrapped :=
794 g.unwrap_generic(g.recheck_concrete_type(resolved_decl_type))
795 // Don't propagate pointer flag from auto-deref mut parameters.
796 // `mut e := expr` where expr is a mut param should create a value copy.
797 // Also handles the case where var_type is already a pointer (e.g.
798 // `c := head` where head is `mut &Client` → var_type is &Client
799 // but resolved_expr_type returns &&Client). Compare nr_muls to
800 // deref only when there's an extra level from auto-deref.
801 if resolved_unwrapped.is_ptr() && !var_type.is_ptr() {
802 resolved_unwrapped = resolved_unwrapped.deref()
803 } else if val is ast.Ident && val.is_auto_deref_var()
804 && resolved_unwrapped.nr_muls() > var_type.nr_muls() {
805 resolved_unwrapped = resolved_unwrapped.set_nr_muls(var_type.nr_muls())
806 }
807 // Don't propagate shared/atomic flags from the resolved RHS expression
808 // to a non-shared declaration. E.g., `sliced := shared_arr[..x]` inside
809 // a rlock block resolves to a shared type, but `sliced` is not shared.
810 if !var_type.has_flag(.shared_f) && resolved_unwrapped.has_flag(.shared_f) {
811 resolved_unwrapped = resolved_unwrapped.clear_flag(.shared_f)
812 if resolved_unwrapped.nr_muls() > 0 {
813 resolved_unwrapped =
814 resolved_unwrapped.set_nr_muls(resolved_unwrapped.nr_muls() - 1)
815 }
816 }
817 if !var_type.has_flag(.atomic_f) && resolved_unwrapped.has_flag(.atomic_f) {
818 resolved_unwrapped = resolved_unwrapped.clear_flag(.atomic_f)
819 }
820 // Skip when resolved type is a parent sumtype of a smartcast variant.
821 // This happens in match arms where the RHS uses a smartcast variable
822 // (e.g. `mut info := ts.info` inside `match ts.info { Struct { ... } }`).
823 // Resolve aggregate types (from multi-branch match arms)
824 // to the concrete variant type for the current iteration.
825 resolved_sym_ := g.table.sym(resolved_unwrapped)
826 if resolved_sym_.info is ast.Aggregate {
827 resolved_unwrapped = resolved_sym_.info.types[g.aggregate_type_idx]
828 }
829 resolved_sym := g.table.sym(resolved_unwrapped)
830 var_sym := g.table.sym(var_type)
831 is_sumtype_reversal := resolved_sym.kind == .sum_type
832 && resolved_unwrapped != var_type && var_sym.kind != .sum_type
833 // Don't downgrade a specific map/array type (e.g. map[int]SqlExpr)
834 // to the base map/array type. This happens when .clone() etc.
835 // return the base type but the checker already has a specific type.
836 is_base_container_downgrade := (resolved_unwrapped == ast.map_type
837 && var_sym.kind == .map && var_type != ast.map_type)
838 || (resolved_unwrapped == ast.array_type && var_sym.kind == .array
839 && var_type != ast.array_type)
840 // Don't introduce option/result flag when the checker already unwrapped it.
841 // E.g., `x := *var?` where var is `?&int`: checker says x is `int`,
842 // but resolved_expr_type may return `?int` because it sees the option var.
843 is_option_introduction := !var_type.has_option_or_result()
844 && resolved_unwrapped.has_option_or_result()
845 if !is_sumtype_reversal && !is_base_container_downgrade && !is_option_introduction {
846 var_type = resolved_unwrapped
847 val_type = var_type
848 node.left_types[i] = var_type
849 if i < node.right_types.len {
850 node.right_types[i] = val_type
851 }
852 if mut left is ast.Ident {
853 if mut left.obj is ast.Var {
854 left.obj.typ = var_type
855 if !var_type.has_option_or_result() {
856 left.obj.orig_type = ast.no_type
857 left.obj.smartcasts = []
858 left.obj.is_unwrapped = false
859 }
860 if left.obj.ct_type_var != .no_comptime {
861 g.type_resolver.update_ct_type(left.name, var_type)
862 }
863 }
864 if left.scope != unsafe { nil } {
865 if mut scope_var := left.scope.find_var(left.name) {
866 scope_var.typ = var_type
867 if !var_type.has_option_or_result() {
868 scope_var.orig_type = ast.no_type
869 scope_var.smartcasts = []
870 scope_var.is_unwrapped = false
871 }
872 }
873 }
874 }
875 }
876 }
877 }
878 mut cur_indexexpr := -1
879 consider_int_overflow := g.do_int_overflow_checks && g.unwrap_generic(var_type).is_int()
880 consider_int_div_mod := g.table.final_sym(g.unwrap_generic(var_type)).is_int()
881 is_safe_add_assign := node.op == .plus_assign && consider_int_overflow
882 is_safe_sub_assign := node.op == .minus_assign && consider_int_overflow
883 is_safe_mul_assign := node.op == .mult_assign && consider_int_overflow
884 is_safe_div_assign := node.op == .div_assign && consider_int_div_mod
885 is_safe_mod_assign := node.op == .mod_assign && consider_int_div_mod
886 initial_left_sym := g.table.sym(g.unwrap_generic(var_type))
887 is_va_list = initial_left_sym.language == .c && initial_left_sym.name == 'C.va_list'
888 if mut left is ast.Ident {
889 ident = left
890 g.curr_var_name << ident.name
891 // id_info := ident.var_info()
892 // var_type = id_info.typ
893 blank_assign = left.kind == .blank_ident
894 // TODO: temporary, remove this
895 left_info := left.info
896 if left_info is ast.IdentVar {
897 share := left_info.share
898 if share == .shared_t {
899 var_type = var_type.set_flag(.shared_f)
900 }
901 if share == .atomic_t {
902 var_type = var_type.set_flag(.atomic_f)
903 }
904 }
905 if mut left.obj is ast.Var {
906 if is_decl {
907 if val is ast.Ident && val.ct_expr {
908 ctyp := g.unwrap_generic(g.type_resolver.get_type(val))
909 if ctyp != ast.void_type {
910 var_type = ctyp
911 val_type = var_type
912 gen_or = val.or_expr.kind != .absent
913 if gen_or {
914 var_type = val_type.clear_flag(.option)
915 }
916 left.obj.typ = var_type
917 g.type_resolver.update_ct_type(left.name, var_type)
918 }
919 } else if val is ast.ComptimeSelector {
920 if val.typ_key != '' {
921 if is_decl {
922 var_type = g.type_resolver.get_ct_type_or_default(val.typ_key,
923 var_type)
924 val_type = var_type
925 left.obj.typ = var_type
926 g.type_resolver.update_ct_type(left.name, var_type)
927 } else {
928 val_type = g.type_resolver.get_ct_type_or_default(val.typ_key,
929 var_type)
930 }
931 }
932 } else if val is ast.ComptimeCall {
933 var_type = if val.kind in [.zero, .new] {
934 g.comptime_zero_new_result_type(val, var_type)
935 } else {
936 key_str := '${val.method_name}.return_type'
937 g.type_resolver.get_ct_type_or_default(key_str, var_type)
938 }
939 val_type = var_type
940 left.obj.typ = var_type
941 g.type_resolver.update_ct_type(left.name, var_type)
942 g.assign_ct_type[val.pos.pos] = var_type
943 } else if val is ast.Ident && val.info is ast.IdentVar {
944 val_info := val.info as ast.IdentVar
945 gen_or = val.or_expr.kind != .absent
946 if val_info.is_option && gen_or {
947 var_type = val_type.clear_flag(.option)
948 left.obj.typ = var_type
949 }
950 } else if val is ast.DumpExpr {
951 if val.expr is ast.ComptimeSelector {
952 if val.expr.typ_key != '' {
953 var_type = g.type_resolver.get_ct_type_or_default(val.expr.typ_key,
954 var_type)
955 val_type = var_type
956 left.obj.typ = var_type
957 g.type_resolver.update_ct_type(left.name, var_type)
958 }
959 }
960 } else if val is ast.IndexExpr && (val.left is ast.Ident && val.left.ct_expr) {
961 ctyp := g.unwrap_generic(g.type_resolver.get_type(val))
962 if ctyp != ast.void_type {
963 var_type = ctyp
964 val_type = var_type
965 left.obj.typ = var_type
966 g.type_resolver.update_ct_type(left.name, var_type)
967 }
968 } else if left.obj.ct_type_var == .generic_var && val is ast.CallExpr {
969 if val.return_type_generic != 0 {
970 mut fn_ret_type := g.resolve_return_type(val).clear_option_and_result()
971 if fn_ret_type == ast.void_type || fn_ret_type.has_flag(.generic) {
972 fn_ret_type =
973 g.unwrap_generic(val.return_type_generic).clear_option_and_result()
974 }
975 if fn_ret_type != ast.void_type {
976 var_type = fn_ret_type
977 val_type = var_type
978 left.obj.typ = var_type
979 g.assign_ct_type[val.pos.pos] = var_type
980 }
981 } else if val.is_static_method && val.left_type.has_flag(.generic) {
982 fn_ret_type := g.resolve_return_type(val)
983 var_type = fn_ret_type
984 val_type = var_type
985 left.obj.typ = var_type
986 g.assign_ct_type[val.pos.pos] = var_type
987 } else if val.left_type != 0 && g.table.type_kind(val.left_type) == .array
988 && val.name == 'map' && val.args.len > 0
989 && val.args[0].expr is ast.AsCast
990 && val.args[0].expr.typ.has_flag(.generic) {
991 var_type =
992 g.table.find_or_register_array(g.unwrap_generic((val.args[0].expr as ast.AsCast).typ))
993 val_type = var_type
994 left.obj.typ = var_type
995 g.assign_ct_type[val.pos.pos] = var_type
996 }
997 } else if val is ast.InfixExpr
998 && val.op in [.plus, .minus, .mul, .power, .div, .mod] && val.left_ct_expr {
999 left_ctyp := g.type_resolver.get_type_or_default(val.left, val.left_type)
1000 right_ctyp := g.type_resolver.get_type_or_default(val.right, val.right_type)
1001 ctyp := g.type_resolver.promote_type(g.unwrap_generic(left_ctyp),
1002 g.unwrap_generic(right_ctyp))
1003 if ctyp != ast.void_type {
1004 ct_type_var := g.comptime.get_ct_type_var(val.left)
1005 if ct_type_var in [.key_var, .value_var] {
1006 g.type_resolver.update_ct_type(left.name, g.unwrap_generic(ctyp))
1007 }
1008 var_type = ctyp
1009 val_type = var_type
1010 left.obj.typ = var_type
1011 }
1012 } else if val is ast.PostfixExpr && val.op == .question
1013 && (val.expr is ast.Ident && val.expr.ct_expr) {
1014 ctyp := g.unwrap_generic(g.type_resolver.get_type(val))
1015 if ctyp != ast.void_type {
1016 var_type = ctyp
1017 val_type = var_type
1018 left.obj.typ = var_type
1019
1020 ct_type_var := g.comptime.get_ct_type_var(val.expr)
1021 if ct_type_var == .field_var {
1022 g.type_resolver.update_ct_type(left.name, ctyp)
1023 }
1024 }
1025 } else if var_type.has_flag(.generic) && val is ast.StructInit
1026 && val_type.has_flag(.generic) {
1027 val_type = g.unwrap_generic(val_type)
1028 var_type = val_type
1029 }
1030 }
1031 is_auto_heap = g.resolved_ident_is_auto_heap(left)
1032 // In generic instantiations, is_auto_heap may be set by the
1033 // checker's post_process when concrete types resolve to @[heap]
1034 // structs. Reset when the variable is an option type.
1035 if is_auto_heap && g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0 {
1036 resolved_obj_typ := g.unwrap_generic(left.obj.typ)
1037 if resolved_obj_typ != 0 && resolved_obj_typ.has_flag(.option)
1038 && !resolved_obj_typ.is_ptr() {
1039 is_auto_heap = false
1040 }
1041 }
1042 if left.obj.typ != 0 && val is ast.PrefixExpr {
1043 is_fn_var = g.table.final_sym(left.obj.typ).kind == .function
1044 }
1045 }
1046 } else if mut left is ast.ComptimeSelector {
1047 if left.typ_key != '' {
1048 var_type = g.type_resolver.get_ct_type_or_default(left.typ_key, var_type)
1049 }
1050 if val is ast.ComptimeSelector {
1051 if val.typ_key != '' {
1052 val_type = g.type_resolver.get_ct_type_or_default(val.typ_key, var_type)
1053 }
1054 } else if val is ast.CallExpr && val.return_type_generic.has_flag(.generic) {
1055 g.assign_ct_type[val.pos.pos] = g.comptime.comptime_for_field_type
1056 val_type = var_type
1057 }
1058 } else if mut left is ast.IndexExpr && val is ast.ComptimeSelector {
1059 if val.typ_key != '' {
1060 val_type = g.type_resolver.get_ct_type_or_default(val.typ_key, var_type)
1061 }
1062 }
1063 if is_decl && val is ast.CallExpr && val.or_block.kind != .absent {
1064 mut resolved_call_type := g.resolve_return_type(val)
1065 if resolved_call_type == ast.void_type {
1066 resolved_call_type = val.return_type
1067 }
1068 if g.table.sym(resolved_call_type).kind == .alias {
1069 unaliased_call_type := g.table.unaliased_type(resolved_call_type)
1070 if unaliased_call_type.has_option_or_result() {
1071 resolved_call_type = g.unwrap_generic(unaliased_call_type)
1072 }
1073 }
1074 var_type = resolved_call_type.clear_option_and_result()
1075 val_type = var_type
1076 if mut left is ast.Ident && mut left.obj is ast.Var {
1077 left.obj.typ = var_type
1078 }
1079 }
1080 if is_decl && val is ast.PostfixExpr && val.op == .question {
1081 mut resolved_val_type := g.resolved_expr_type(val.expr, val_type)
1082 if g.table.sym(resolved_val_type).kind == .alias {
1083 unaliased_val_type := g.table.unaliased_type(resolved_val_type)
1084 if unaliased_val_type.has_option_or_result() {
1085 resolved_val_type = g.unwrap_generic(unaliased_val_type)
1086 }
1087 }
1088 resolved_val_type = resolved_val_type.clear_option_and_result()
1089 if resolved_val_type != 0 && resolved_val_type != ast.void_type {
1090 var_type = resolved_val_type
1091 val_type = resolved_val_type
1092 if mut left is ast.Ident && mut left.obj is ast.Var {
1093 left.obj.typ = var_type
1094 }
1095 }
1096 }
1097 if is_decl && val is ast.Ident && val.or_expr.kind != .absent && g.cur_fn != unsafe { nil }
1098 && g.cur_concrete_types.len > 0 && val.or_expr.stmts.len > 0 {
1099 last_or_stmt := val.or_expr.stmts.last()
1100 if last_or_stmt is ast.ExprStmt && last_or_stmt.typ != ast.void_type {
1101 resolved_or_type := g.resolved_expr_type(last_or_stmt.expr, last_or_stmt.typ)
1102 if resolved_or_type != 0 && resolved_or_type != ast.void_type {
1103 var_type = g.unwrap_generic(g.recheck_concrete_type(resolved_or_type))
1104 val_type = var_type
1105 if mut left is ast.Ident && mut left.obj is ast.Var {
1106 left.obj.typ = var_type
1107 }
1108 }
1109 }
1110 }
1111 if is_decl && assign_expr_unwraps_option_or_result(val) {
1112 var_type = var_type.clear_option_and_result()
1113 val_type = val_type.clear_option_and_result()
1114 if mut left is ast.Ident && mut left.obj is ast.Var {
1115 left.obj.typ = var_type
1116 }
1117 }
1118 if is_decl && var_type.has_option_or_result() {
1119 if (val is ast.CallExpr && val.or_block.kind != .absent)
1120 || (val is ast.PostfixExpr && val.op == .question) {
1121 var_type = var_type.clear_option_and_result()
1122 val_type = val_type.clear_option_and_result()
1123 if mut left is ast.Ident && mut left.obj is ast.Var {
1124 left.obj.typ = var_type
1125 }
1126 }
1127 }
1128 if is_decl && mut left is ast.Ident && mut left.obj is ast.Var {
1129 mut should_recompute_decl_type := false
1130 match val {
1131 ast.Ident {
1132 should_recompute_decl_type = val.ct_expr
1133 || (val.obj is ast.Var && val.obj.ct_type_var != .no_comptime)
1134 || (val.obj is ast.Var && (val.obj.typ.has_flag(.generic)
1135 || g.type_has_unresolved_generic_parts(val.obj.typ)))
1136 }
1137 ast.SelectorExpr {
1138 should_recompute_decl_type = val.typ.has_flag(.generic)
1139 || g.type_has_unresolved_generic_parts(val.typ)
1140 || val.expr_type.has_flag(.generic)
1141 || g.type_has_unresolved_generic_parts(val.expr_type)
1142 }
1143 ast.IndexExpr {
1144 should_recompute_decl_type = val.typ.has_flag(.generic)
1145 || g.type_has_unresolved_generic_parts(val.typ)
1146 || val.left_type.has_flag(.generic)
1147 || g.type_has_unresolved_generic_parts(val.left_type)
1148 || (val.left is ast.Ident && val.left.ct_expr)
1149 }
1150 ast.ComptimeSelector {
1151 should_recompute_decl_type = true
1152 }
1153 ast.ComptimeCall {
1154 should_recompute_decl_type = val.kind in [.zero, .new]
1155 }
1156 ast.CastExpr {
1157 should_recompute_decl_type = val.typ.has_flag(.generic)
1158 || g.type_has_unresolved_generic_parts(val.typ)
1159 }
1160 ast.PostfixExpr {
1161 should_recompute_decl_type = val.op == .question
1162 }
1163 ast.PrefixExpr {
1164 should_recompute_decl_type = val.op == .arrow
1165 }
1166 else {}
1167 }
1168
1169 if g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0 {
1170 should_recompute_decl_type = true
1171 }
1172 if val is ast.CallExpr {
1173 should_recompute_decl_type = val.return_type_generic != 0
1174 || val.is_static_method || val.concrete_types.len > 0
1175 || val.raw_concrete_types.len > 0
1176 || (g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0)
1177 || (val.is_method && (val.left_type.has_flag(.generic)
1178 || g.type_has_unresolved_generic_parts(val.left_type)
1179 || val.receiver_type.has_flag(.generic)
1180 || g.type_has_unresolved_generic_parts(val.receiver_type)))
1181 }
1182 mut resolved_val_type := if val is ast.ComptimeCall && val.kind in [.zero, .new] {
1183 g.comptime_zero_new_result_type(val, val_type)
1184 } else if val is ast.Ident && val.or_expr.kind != .absent && g.cur_fn != unsafe { nil }
1185 && g.cur_concrete_types.len > 0 {
1186 or_value_type := g.resolved_or_block_value_type(val.or_expr)
1187 if or_value_type != 0 {
1188 or_value_type
1189 } else {
1190 g.resolved_expr_type(val, val_type)
1191 }
1192 } else {
1193 g.resolved_expr_type(val, val_type)
1194 }
1195 if should_recompute_decl_type && resolved_val_type != 0
1196 && resolved_val_type != ast.void_type {
1197 if assign_expr_unwraps_option_or_result(val) {
1198 resolved_val_type = resolved_val_type.clear_option_and_result()
1199 }
1200 resolved_val_type = g.unwrap_generic(g.recheck_concrete_type(resolved_val_type))
1201 // Resolve aggregate types (from multi-branch match arms)
1202 // to the concrete variant type for the current iteration.
1203 resolved_val_sym2 := g.table.sym(resolved_val_type)
1204 if resolved_val_sym2.info is ast.Aggregate {
1205 resolved_val_type = resolved_val_sym2.info.types[g.aggregate_type_idx]
1206 }
1207 // For SelectorExpr with scope smartcast (e.g. `if w.check != none`),
1208 // the resolved field type has the option flag, but the smartcast
1209 // unwraps it. Clear the option flag in that case.
1210 if val is ast.SelectorExpr && resolved_val_type.has_flag(.option) {
1211 scope := g.file.scope.innermost(val.pos.pos)
1212 field := scope.find_struct_field(val.expr.str(), val.expr_type, val.field_name)
1213 if field != unsafe { nil } && field.smartcasts.len > 0 {
1214 resolved_val_type = resolved_val_type.clear_flag(.option)
1215 }
1216 }
1217 resolved_val_sym := g.table.final_sym(resolved_val_type)
1218 if resolved_val_sym.kind == .array && !resolved_val_type.is_ptr()
1219 && g.table.sym(resolved_val_type).kind != .alias {
1220 mut resolved_elem_type :=
1221 g.unwrap_generic(g.recheck_concrete_type(resolved_val_sym.array_info().elem_type))
1222 if resolved_elem_type == ast.int_literal_type {
1223 resolved_elem_type = ast.int_type
1224 } else if resolved_elem_type == ast.float_literal_type {
1225 resolved_elem_type = ast.f64_type
1226 }
1227 if resolved_elem_type != 0 && !resolved_elem_type.has_flag(.generic)
1228 && !g.type_has_unresolved_generic_parts(resolved_elem_type) {
1229 mut new_arr_type :=
1230 ast.idx_to_type(g.table.find_or_register_array(resolved_elem_type))
1231 // Preserve option/result flags from the original resolved type
1232 if resolved_val_type.has_flag(.option) {
1233 new_arr_type = new_arr_type.set_flag(.option)
1234 }
1235 if resolved_val_type.has_flag(.result) {
1236 new_arr_type = new_arr_type.set_flag(.result)
1237 }
1238 resolved_val_type = new_arr_type
1239 }
1240 }
1241 // When assigning from an auto-deref variable (e.g. mut ref param),
1242 // the resolved type from scope includes the extra pointer level.
1243 // Deref it to match the V value semantics.
1244 if val is ast.Ident && val.is_auto_deref_var() && resolved_val_type.is_ptr()
1245 && !g.auto_deref_source_type_is_pointer(val) {
1246 resolved_val_type = resolved_val_type.deref()
1247 }
1248 // Preserve shared/atomic flags from the original declaration.
1249 if var_type.has_flag(.shared_f) {
1250 resolved_val_type = resolved_val_type.set_flag(.shared_f)
1251 }
1252 if var_type.has_flag(.atomic_f) {
1253 resolved_val_type = resolved_val_type.set_flag(.atomic_f)
1254 }
1255 var_type = resolved_val_type
1256 // val_type represents the RHS expression type, which is NOT shared/atomic.
1257 // Only var_type (the LHS declaration type) should carry those flags.
1258 val_type = resolved_val_type.clear_flag(.shared_f).clear_flag(.atomic_f)
1259 left.obj.typ = var_type
1260 }
1261 }
1262 // Various resolution blocks above may overwrite var_type, stripping the
1263 // shared/atomic flags and nr_muls that the checker originally set.
1264 // Re-apply them based on the left-hand identifier's share attribute,
1265 // which is the authoritative source for whether the declaration is shared.
1266 if is_decl && mut left is ast.Ident {
1267 left_info := left.info
1268 if left_info is ast.IdentVar {
1269 if left_info.share == .shared_t && !var_type.has_flag(.shared_f) {
1270 var_type = var_type.set_flag(.shared_f)
1271 }
1272 if left_info.share == .atomic_t && !var_type.has_flag(.atomic_f) {
1273 var_type = var_type.set_flag(.atomic_f)
1274 }
1275 }
1276 }
1277 if is_decl && var_type.has_flag(.shared_f) && var_type.nr_muls() == 0 {
1278 var_type = var_type.set_nr_muls(1)
1279 }
1280 if is_decl && mut left is ast.Ident && mut left.obj is ast.Var {
1281 left.obj.typ = var_type
1282 if mut scope_var := left.scope.find_var(left.name) {
1283 scope_var.typ = var_type
1284 scope_var.orig_type = ast.no_type
1285 scope_var.smartcasts = []
1286 scope_var.is_unwrapped = false
1287 }
1288 }
1289 if is_decl && val is ast.CallExpr && val.kind == .clone && val.left is ast.IndexExpr {
1290 left_idx := val.left as ast.IndexExpr
1291 if left_idx.index is ast.RangeExpr && g.table.final_sym(var_type).kind == .array {
1292 is_auto_heap = false
1293 }
1294 }
1295 mut styp := g.styp(var_type)
1296 if is_decl && val is ast.CallExpr && val.kind == .clone && val.left is ast.IndexExpr {
1297 left_idx := val.left as ast.IndexExpr
1298 if left_idx.index is ast.RangeExpr && g.table.final_sym(val.return_type).kind == .array {
1299 styp = styp.trim('*')
1300 }
1301 }
1302 mut is_fixed_array_init := false
1303 mut has_val := false
1304 match val {
1305 ast.ArrayInit {
1306 is_fixed_array_init = val.is_fixed
1307 has_val = val.has_val
1308 }
1309 ast.ParExpr {
1310 if val.expr is ast.ArrayInit {
1311 array_init := val.expr as ast.ArrayInit
1312 is_fixed_array_init = array_init.is_fixed
1313 has_val = array_init.has_val
1314 }
1315 }
1316 ast.CallExpr {
1317 is_call = true
1318 if val.comptime_ret_val {
1319 return_type = g.comptime.comptime_for_field_type
1320 styp = g.styp(return_type)
1321 } else {
1322 return_type = val.return_type
1323 }
1324 }
1325 // TODO: no buffer fiddling
1326 ast.AnonFn {
1327 if !var_type.has_option_or_result() {
1328 if blank_assign {
1329 g.write('{')
1330 }
1331 // if it's a decl assign (`:=`) or a blank assignment `_ =`/`_ :=` then generate `void (*ident) (args) =`
1332 if (is_decl || blank_assign) && left is ast.Ident {
1333 sig := g.fn_var_signature(val.typ, val.decl.return_type,
1334 val.decl.params.map(it.typ), ident.name)
1335 g.write(sig + ' = ')
1336 } else {
1337 g.is_assign_lhs = true
1338 g.assign_op = node.op
1339 g.expr(left)
1340 g.is_assign_lhs = false
1341 g.is_arraymap_set = false
1342 if mut left is ast.IndexExpr {
1343 sym := g.table.final_sym(left.left_type)
1344 if sym.kind in [.map, .array] {
1345 g.expr(val)
1346 g.writeln('});')
1347 continue
1348 }
1349 }
1350 g.write(' = ')
1351 }
1352 g.expr(val)
1353 g.writeln(';')
1354 g.gen_self_recursing_anon_fn_capture_patch(left, val)
1355 if blank_assign {
1356 g.write('}')
1357 }
1358 continue
1359 }
1360 }
1361 else {}
1362 }
1363
1364 if g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0 {
1365 orig_var_option := var_type.has_flag(.option)
1366 resolved_left_type := g.resolved_expr_type(left, var_type)
1367 if resolved_left_type != 0 {
1368 var_type = g.unwrap_generic(g.recheck_concrete_type(resolved_left_type))
1369 }
1370 // Preserve the option flag when the variable was option-smartcasted.
1371 // `resolved_expr_type` returns the smartcasted (unwrapped) type for variables
1372 // inside `if x != none` blocks, but the C variable is still the option type
1373 // and needs option wrapping on assignment.
1374 if orig_var_option && !var_type.has_flag(.option) {
1375 var_type = var_type.set_flag(.option)
1376 }
1377 resolved_val_type := g.resolved_expr_type(val, val_type)
1378 if resolved_val_type != 0 {
1379 new_val_type := g.unwrap_generic(g.recheck_concrete_type(resolved_val_type))
1380 // Preserve option/result flag clearing from earlier unwrap
1381 if !val_type.has_flag(.option) && new_val_type.has_flag(.option) {
1382 val_type = new_val_type.clear_option_and_result()
1383 } else if !val_type.has_flag(.result) && new_val_type.has_flag(.result) {
1384 val_type = new_val_type.clear_option_and_result()
1385 } else {
1386 val_type = new_val_type
1387 }
1388 }
1389 }
1390 styp = g.styp(var_type)
1391 if is_decl && val is ast.CallExpr && val.kind == .clone && val.left is ast.IndexExpr {
1392 left_idx := val.left as ast.IndexExpr
1393 if left_idx.index is ast.RangeExpr && g.table.final_sym(val.return_type).kind == .array {
1394 styp = styp.trim('*')
1395 }
1396 }
1397 left_sym := g.table.sym(g.unwrap_generic(var_type))
1398 is_va_list = left_sym.language == .c && left_sym.name == 'C.va_list'
1399 unwrapped_val_type := g.unwrap_generic(val_type)
1400 right_sym := g.table.sym(unwrapped_val_type)
1401 unaliased_right_sym := g.table.final_sym(unwrapped_val_type)
1402 unaliased_left_sym := g.table.final_sym(g.unwrap_generic(var_type))
1403 is_fixed_array_var := unaliased_right_sym.kind == .array_fixed && val !is ast.ArrayInit
1404 && (val in [ast.Ident, ast.IndexExpr, ast.CallExpr, ast.SelectorExpr, ast.ComptimeSelector, ast.DumpExpr, ast.InfixExpr]
1405 || (val is ast.CastExpr && val.expr !is ast.ArrayInit)
1406 || (val is ast.PrefixExpr && val.op == .arrow)
1407 || (val is ast.UnsafeExpr && val.expr in [ast.SelectorExpr, ast.Ident, ast.CallExpr]))
1408 && !((g.pref.translated || g.file.is_translated)
1409 && unaliased_left_sym.kind != .array_fixed)
1410 g.is_assign_lhs = true
1411 g.assign_op = node.op
1412
1413 g.left_is_opt = var_type.has_option_or_result()
1414 g.right_is_opt = val_type.has_option_or_result()
1415 defer(fn) {
1416 g.left_is_opt = false
1417 g.right_is_opt = false
1418 }
1419 if !is_decl && node.op == .assign && var_type.has_flag(.option_mut_param_t) {
1420 // For `mut ?T` parameters / for-loop variables, the C type is
1421 // `_option_T*` (pointer to option). Write through the pointer
1422 // by copying the entire option struct (state + data).
1423 mut target_option_type := g.resolve_current_fn_generic_param_type(left.str())
1424 if target_option_type == 0 || !target_option_type.has_flag(.option) {
1425 target_option_type = var_type.clear_flag(.option_mut_param_t)
1426 }
1427 if target_option_type.has_flag(.option) {
1428 target_inner_type := target_option_type.clear_option_and_result()
1429 if target_inner_type.is_ptr() {
1430 target_option_type = target_inner_type.deref().set_flag(.option)
1431 }
1432 }
1433 target_option_type = g.unwrap_generic(g.recheck_concrete_type(target_option_type))
1434 tmp_var := g.new_tmp_var()
1435 g.expr_with_tmp_var(val, val_type, target_option_type, tmp_var, false)
1436 left_name := c_name(left.str())
1437 is_fn_param := left is ast.Ident && left.is_auto_deref_var()
1438 if is_fn_param {
1439 // Function params use _option_T* type, copy data through pointer
1440 val_base_type := g.base_type(val_type)
1441 g.writeln('${left_name}->state = ${tmp_var}.state;')
1442 g.writeln('memcpy(&${left_name}->data, ${tmp_var}.data, sizeof(${val_base_type}));')
1443 } else {
1444 // For-loop variables use _option_T* type, copy whole struct
1445 g.writeln('*${left_name} = ${tmp_var};')
1446 }
1447 continue
1448 }
1449
1450 if blank_assign {
1451 if val is ast.IndexExpr {
1452 g.assign_op = .decl_assign
1453 }
1454 g.is_assign_lhs = false
1455 if is_call {
1456 old_is_void_expr_stmt := g.is_void_expr_stmt
1457 g.is_void_expr_stmt = true
1458 g.expr(val)
1459 g.is_void_expr_stmt = old_is_void_expr_stmt
1460 } else if g.inside_for_c_stmt {
1461 g.expr(val)
1462 } else if var_type.has_flag(.option) {
1463 // Blank ident option types can be stale across generic instantiations.
1464 // Use the actual RHS option type when it is available so `_ = val`
1465 // does not rewrap the expression into a mismatched option payload.
1466 blank_option_type := if val_type.has_flag(.option) { val_type } else { var_type }
1467 g.expr_with_opt(val, val_type, blank_option_type)
1468 } else {
1469 if left_sym.kind == .function {
1470 g.write('{void* _ = ')
1471 } else {
1472 // For blank idents, use val_type to determine the C type
1473 // instead of var_type (styp), because in generic functions
1474 // the checker's left_types[i] for blank idents can be
1475 // overwritten by a later generic instantiation.
1476 mut blank_styp := g.styp(val_type)
1477 if val is ast.Ident && val.is_auto_deref_var()
1478 && !g.auto_deref_source_type_is_pointer(val) {
1479 blank_styp = '${blank_styp}*'
1480 }
1481 if blank_styp.ends_with('*') {
1482 blank_styp = 'void*'
1483 }
1484 g.write('{${blank_styp} _ = ')
1485 }
1486 if (val in [ast.MatchExpr, ast.IfExpr, ast.ComptimeSelector] || is_fixed_array_var)
1487 && unaliased_right_sym.info is ast.ArrayFixed {
1488 tmp_var := g.expr_with_var(val, var_type, false)
1489 // When the temp var is a return wrapper struct (_v_Array_fixed_...),
1490 // access .ret_arr to get the actual C array for subscripting.
1491 init_expr := if unaliased_right_sym.info.is_fn_ret {
1492 '${tmp_var}.ret_arr'
1493 } else {
1494 tmp_var
1495 }
1496 g.fixed_array_var_init(init_expr, false, unaliased_right_sym.info.elem_type,
1497 unaliased_right_sym.info.size)
1498 } else {
1499 g.expr(val)
1500 }
1501 g.writeln(';}')
1502 }
1503 } else if node.op == .assign && (is_fixed_array_init || is_fixed_array_var
1504 || (unaliased_right_sym.kind == .array_fixed && val is ast.CastExpr)) {
1505 // Fixed arrays
1506 if unaliased_left_sym.kind != .array_fixed && unaliased_right_sym.kind == .array_fixed
1507 && (g.pref.translated || g.file.is_translated) {
1508 // translated:
1509 // arr = [5]u8{}
1510 // ptr = arr => ptr = &arr[0]
1511 g.expr(left)
1512 g.write(' = ')
1513 g.expr(val)
1514 } else if is_fixed_array_init && var_type.has_flag(.option) {
1515 g.expr(left)
1516 g.write(' = ')
1517 g.expr_with_opt(val, val_type, var_type)
1518 } else if unaliased_right_sym.kind == .array_fixed && val is ast.CastExpr {
1519 if var_type.has_flag(.option) {
1520 g.expr(left)
1521 g.writeln('.state = 0;')
1522 g.write('memcpy(')
1523 g.expr(left)
1524 g.write('.data, ')
1525 g.expr(val)
1526 g.writeln(', sizeof(${g.styp(var_type.clear_flag(.option))}));')
1527 } else {
1528 g.write('memcpy(')
1529 g.expr(left)
1530 g.write(', ')
1531 g.expr(val)
1532 g.writeln(', sizeof(${g.styp(var_type)}));')
1533 }
1534 } else {
1535 arr_typ := styp.trim('*')
1536 old_is_assign_lhs := g.is_assign_lhs
1537 // For map IndexExpr LHS, keep is_assign_lhs = true so the index
1538 // generator emits `map_get_and_set` (which inserts missing keys)
1539 // instead of `map_get` (which returns a zero-default buffer).
1540 left_is_map_index := left is ast.IndexExpr
1541 && g.table.final_sym(left.left_type).kind == .map
1542 g.is_assign_lhs = left_is_map_index
1543 left_expr := g.expr_string(left)
1544 if !is_fixed_array_init && assign_expr_unwraps_option_or_result(val) {
1545 // val has an or-block — its code generator emits unwrap
1546 // statements via go_before_last_stmt(), so we must emit
1547 // memcpy() inline. Capturing val with expr_string() would
1548 // slurp those statements into the memcpy() argument list.
1549 g.write('memcpy(${left_expr}, ')
1550 g.expr(val)
1551 g.writeln(', sizeof(${arr_typ}));')
1552 } else {
1553 mut fixed_right_expr := ''
1554 if is_fixed_array_init {
1555 right := val as ast.ArrayInit
1556 right_var := g.new_tmp_var()
1557 g.write('${arr_typ} ${right_var} = ')
1558 g.expr(right)
1559 g.writeln(';')
1560 fixed_right_expr = right_var
1561 } else {
1562 fixed_right_expr = g.expr_string(val)
1563 }
1564 g.writeln('')
1565 g.writeln('memcpy(${left_expr}, ${fixed_right_expr}, sizeof(${arr_typ}));')
1566 }
1567 g.is_assign_lhs = old_is_assign_lhs
1568 g.is_assign_lhs = false
1569 }
1570 } else {
1571 is_inside_ternary := g.inside_ternary != 0
1572 cur_line := if is_inside_ternary && is_decl {
1573 g.register_ternary_name(ident.name)
1574 g.empty_line = false
1575 g.go_before_ternary()
1576 } else {
1577 ''
1578 }
1579 if mut left is ast.IndexExpr && left.is_index_operator {
1580 g.write(cur_line)
1581 if node.op == .assign {
1582 g.index_operator_call(left.left, left.left_type, left.index, left.index_type,
1583 '[]=', val, val_type)
1584 } else {
1585 infix_op := token.assign_op_to_infix_op(node.op)
1586 op_expr := ast.InfixExpr{
1587 left: ast.Expr(left)
1588 right: val
1589 op: infix_op
1590 pos: node.pos
1591 left_type: left.typ
1592 right_type: val_type
1593 promoted_type: g.type_resolver.promote_type(left.typ, val_type)
1594 }
1595 g.index_operator_call(left.left, left.left_type, left.index, left.index_type,
1596 '[]=', ast.Expr(op_expr), left.typ)
1597 }
1598 if !g.inside_for_c_stmt {
1599 g.writeln(';')
1600 }
1601 continue
1602 }
1603 mut str_add := false
1604 mut op_overloaded := false
1605 mut op_expected_left := ast.no_type
1606 mut op_expected_right := ast.no_type
1607 is_shared_re_assign := !is_decl && node.left_types[i].has_flag(.shared_f)
1608 && left is ast.Ident && left_sym.kind in [.array, .map, .struct]
1609 mut is_mut_arg_pointer_rebind := false
1610 // Keep pointer traversal assignments on auto-deref vars as local pointer rebinds,
1611 // but only for function arguments (is_arg), not for loop iteration variables.
1612 if !is_decl && !is_shared_re_assign && !var_type.has_flag(.option)
1613 && var_type.is_any_kind_of_pointer() && unwrapped_val_type.is_any_kind_of_pointer()
1614 && left.is_auto_deref_var() && !val.is_auto_deref_var() && node.op == .assign
1615 && left.is_auto_deref_arg() {
1616 is_mut_arg_pointer_rebind = true
1617 }
1618 if node.op == .plus_assign && g.is_string_type(var_type)
1619 && g.is_string_concat_type(val_type) {
1620 if g.is_autofree && !g.is_builtin_mod && !g.is_autofree_tmp
1621 && (!g.is_string_type(val_type)
1622 || val !in [ast.Ident, ast.StringLiteral, ast.SelectorExpr, ast.ComptimeSelector]) {
1623 str_add_rhs_tmp = '_str_add_rhs_${node.pos.pos}_${i}'
1624 if g.is_string_type(val_type) {
1625 g.writeln(g.autofree_tmp_arg_init_stmt('string ${str_add_rhs_tmp} = ', val))
1626 } else {
1627 g.writeln('string ${str_add_rhs_tmp} = ${g.expr_string_with_cast(val,
1628 val_type, ast.string_type)};')
1629 val_type = ast.string_type
1630 }
1631 val = ast.Expr(ast.Ident{
1632 mod: g.cur_mod.name
1633 name: str_add_rhs_tmp
1634 })
1635 str_add_rhs_needs_free = true
1636 skip_str_add_rhs_clone = true
1637 defer(fn) {
1638 if str_add_rhs_needs_free {
1639 g.writeln('builtin__string_free(&${str_add_rhs_tmp});')
1640 }
1641 }
1642 }
1643 if mut left is ast.IndexExpr {
1644 if g.table.sym(left.left_type).kind == .array_fixed {
1645 // strs[0] += str2 => `strs[0] = _string__plus(strs[0], str2)`
1646 g.expr(left)
1647 g.write(' = builtin__string__plus(')
1648 } else {
1649 // a[0] += str => `builtin__array_set(&a, 0, &(string[]) {_string__plus(...))})`
1650 g.expr(left)
1651 g.write('builtin__string__plus(')
1652 }
1653 } else {
1654 // allow literal values to auto deref var (e.g.`for mut v in values { v += 1.0 }`)
1655 if left.is_auto_deref_var() {
1656 g.write('*')
1657 }
1658 // str += str2 => `str = builtin__string__plus(str, str2)`
1659 g.expr(left)
1660 g.write(' = builtin__string__plus(')
1661 }
1662 g.is_assign_lhs = false
1663 str_add = true
1664 }
1665 // Assignment Operator Overloading
1666 has_power_assign_overload := node.op == .power_assign
1667 && left_sym.find_method_with_generic_parent('**') != none
1668 if (((left_sym.kind == .struct && right_sym.kind == .struct)
1669 || (left_sym.kind == .alias && right_sym.kind == .alias))
1670 && node.op in [.plus_assign, .minus_assign, .div_assign, .mult_assign, .power_assign, .mod_assign])
1671 || has_power_assign_overload {
1672 extracted_op := match node.op {
1673 .plus_assign { '+' }
1674 .minus_assign { '-' }
1675 .div_assign { '/' }
1676 .mod_assign { '%' }
1677 .mult_assign { '*' }
1678 .power_assign { '**' }
1679 else { 'unknown op' }
1680 }
1681
1682 pos := g.out.len
1683 g.expr(left)
1684 struct_info := g.table.final_sym(var_type)
1685 if left_sym.info is ast.Struct && left_sym.info.generic_types.len > 0 {
1686 concrete_types := left_sym.info.concrete_types
1687 mut method_name := left_sym.cname + '_' + util.replace_op(extracted_op)
1688 specialized_suffix := g.generic_fn_name(concrete_types, '')
1689 if specialized_suffix != '' && !method_name.ends_with(specialized_suffix) {
1690 method_name = g.generic_fn_name(concrete_types, method_name)
1691 }
1692 g.write(' = ${method_name}(')
1693 g.expr(left)
1694 g.write(', ')
1695 g.expr(val)
1696 g.writeln(');')
1697 return
1698 } else if left_sym.kind == .alias
1699 && g.table.final_sym(g.unwrap_generic(var_type)).is_number()
1700 && !left_sym.has_method(extracted_op) {
1701 g.write(' = ')
1702 if node.op == .power_assign {
1703 g.gen_power_assign_expr(left, var_type, val, val_type)
1704 } else {
1705 g.expr(left)
1706 g.write(' ${extracted_op} ')
1707 g.expr(val)
1708 }
1709 if !g.inside_for_c_stmt {
1710 g.write(';')
1711 }
1712 return
1713 } else if left_sym.kind == .alias && struct_info.kind == .struct
1714 && struct_info.info is ast.Struct && struct_info.info.generic_types.len > 0 {
1715 mut method_name := struct_info.cname + '_' + util.replace_op(extracted_op)
1716 specialized_suffix := g.generic_fn_name(struct_info.info.concrete_types, '')
1717 if specialized_suffix != '' && !method_name.ends_with(specialized_suffix) {
1718 method_name = g.generic_fn_name(struct_info.info.concrete_types,
1719 method_name)
1720 }
1721 g.write(' = ${method_name}(')
1722 g.expr(left)
1723 g.write(', ')
1724 g.expr(val)
1725 g.writeln(');')
1726 return
1727 } else {
1728 if g.table.final_sym(g.unwrap_generic(var_type)).kind == .array_fixed {
1729 g.go_back_to(pos)
1730 g.empty_line = true
1731 g.write('memcpy(')
1732 g.expr(left)
1733 g.write(', ${styp}_${util.replace_op(extracted_op)}(')
1734 } else {
1735 g.write(' = ${styp}_${util.replace_op(extracted_op)}(')
1736 }
1737 method := g.table.find_method(left_sym, extracted_op) or {
1738 // the checker will most likely have found this, already...
1739 g.error('assignment operator `${extracted_op}=` used but no `${extracted_op}` method defined',
1740 node.pos)
1741 ast.Fn{}
1742 }
1743 op_expected_left = method.params[0].typ
1744 op_expected_right = method.params[1].typ
1745 op_overloaded = true
1746 }
1747 }
1748 final_left_sym := g.table.final_sym(g.unwrap_generic(var_type))
1749 final_right_sym := g.table.final_sym(unwrapped_val_type)
1750 mut aligned := 0
1751 is_safe_shift_assign := !g.pref.translated && !g.file.is_translated
1752 && node.op in [.left_shift_assign, .right_shift_assign] && final_left_sym.is_int()
1753 safe_shift_fn_name := if is_safe_shift_assign {
1754 g.safe_shift_fn_name(var_type, token.assign_op_to_infix_op(node.op))
1755 } else {
1756 ''
1757 }
1758 if final_left_sym.info is ast.Struct {
1759 if attr := final_left_sym.info.attrs.find_first('aligned') {
1760 aligned = if attr.arg == '' { 0 } else { attr.arg.int() }
1761 }
1762 }
1763
1764 if final_left_sym.kind == .bool && final_right_sym.kind == .bool
1765 && node.op in [.boolean_or_assign, .boolean_and_assign] {
1766 extracted_op := match node.op {
1767 .boolean_or_assign {
1768 '||'
1769 }
1770 .boolean_and_assign {
1771 '&&'
1772 }
1773 else {
1774 'unknown op'
1775 }
1776 }
1777
1778 g.expr(left)
1779 g.write(' = ')
1780 g.expr(left)
1781 g.write(' ${extracted_op} ')
1782 g.expr(val)
1783 g.writeln(';')
1784 return
1785 }
1786 if node.op == .power_assign && !op_overloaded {
1787 g.write_assign_target_expr(left, var_type)
1788 g.write(' = ')
1789 g.gen_power_assign_expr(left, var_type, val, val_type)
1790 g.writeln(';')
1791 return
1792 }
1793 if right_sym.info is ast.FnType && is_decl {
1794 if is_inside_ternary {
1795 g.out.write_string(util.tabs(g.indent - g.inside_ternary))
1796 }
1797 fn_name := c_fn_name(g.get_ternary_name(ident.name))
1798
1799 if val_type.has_flag(.option) {
1800 ret_styp := g.styp(g.unwrap_generic(val_type))
1801 g.write('${ret_styp} ${fn_name}')
1802 } else {
1803 g.write_fntype_decl(fn_name, right_sym.info, var_type.nr_muls())
1804 }
1805 } else {
1806 if is_decl {
1807 if is_inside_ternary {
1808 g.out.write_string(util.tabs(g.indent - g.inside_ternary))
1809 }
1810 mut is_used_var_styp := false
1811 if ident.name !in g.defer_vars {
1812 val_sym := g.table.sym(val_type)
1813 if val_sym.info is ast.Struct && val_sym.info.generic_types.len > 0 {
1814 if val is ast.StructInit {
1815 var_styp := g.styp(val.typ)
1816 if var_type.has_flag(.shared_f) {
1817 g.write('__shared__${var_styp}* ')
1818 } else {
1819 g.write('${var_styp} ')
1820 }
1821 is_used_var_styp = true
1822 } else if val is ast.PrefixExpr {
1823 if val.op == .amp && val.right is ast.StructInit {
1824 var_styp := g.styp(val.right.typ.ref())
1825 if var_type.has_flag(.shared_f) {
1826 g.write('__shared__')
1827 }
1828 g.write('${var_styp} ')
1829 is_used_var_styp = true
1830 }
1831 }
1832 }
1833 if !is_used_var_styp {
1834 if !val_type.has_flag(.option) && left_sym.is_array_fixed() {
1835 if left_sym.info is ast.Alias {
1836 parent_sym := g.table.final_sym(left_sym.info.parent_type)
1837 styp = g.styp(left_sym.info.parent_type)
1838 if !parent_sym.is_array_fixed_ret() {
1839 g.write('${styp} ')
1840 } else {
1841 g.write('${styp[3..]} ')
1842 }
1843 } else {
1844 if !left_sym.is_array_fixed_ret() {
1845 g.write('${styp} ')
1846 } else {
1847 g.write('${styp[3..]} ')
1848 }
1849 }
1850 } else {
1851 g.write('${styp} ')
1852 }
1853 }
1854 if is_auto_heap && !(val_type.is_ptr() && val_type.has_flag(.option)) {
1855 g.write('*')
1856 }
1857 }
1858 }
1859 if left in [ast.Ident, ast.SelectorExpr] && !g.is_smartcast_assign_lhs(left) {
1860 g.prevent_sum_type_unwrapping_once = true
1861 }
1862 if !is_fixed_array_var || is_decl || is_shared_re_assign {
1863 if op_overloaded {
1864 g.op_arg(left, op_expected_left, var_type)
1865 } else {
1866 if !is_decl && !is_shared_re_assign
1867 && (left.is_auto_deref_var() || is_auto_heap)
1868 && !is_mut_arg_pointer_rebind
1869 && (!var_type.has_flag(.option) || is_auto_heap) {
1870 g.write('*')
1871 }
1872 if var_type.has_flag(.option_mut_param_t) {
1873 g.expr(left)
1874 g.write(' = ')
1875 } else {
1876 g.expr(left)
1877 }
1878 if !is_decl && var_type.has_flag(.shared_f) {
1879 g.write('->val') // don't reset the mutex, just change the value
1880 }
1881 }
1882 }
1883 }
1884 if is_inside_ternary && is_decl {
1885 g.write(';\n${cur_line}')
1886 g.out.write_string(util.tabs(g.indent))
1887 g.expr(left)
1888 }
1889 if is_decl && node.is_static
1890 && g.gen_static_decl_runtime_init(node, left, var_type, val, val_type) {
1891 continue
1892 }
1893 g.is_assign_lhs = false
1894 left_expr := left
1895 if left_expr is ast.IndexExpr && g.cur_indexexpr.len > 0 {
1896 cur_indexexpr = g.cur_indexexpr.len - 1
1897 }
1898 if is_fixed_array_var || is_va_list {
1899 if is_decl {
1900 g.writeln(';')
1901 if is_va_list {
1902 continue
1903 }
1904 }
1905 } else if !var_type.has_flag(.option_mut_param_t) && cur_indexexpr == -1 && !str_add
1906 && !op_overloaded && !is_safe_add_assign && !is_safe_sub_assign
1907 && !is_safe_mul_assign && !is_safe_div_assign && !is_safe_mod_assign
1908 && !is_safe_shift_assign {
1909 g.write(' ${op} ')
1910 } else if (str_add || op_overloaded) && !is_safe_add_assign && !is_safe_sub_assign
1911 && !is_safe_mul_assign && !is_safe_div_assign && !is_safe_mod_assign {
1912 g.write(', ')
1913 } else if is_safe_shift_assign {
1914 g.write(' = ${safe_shift_fn_name}(')
1915 g.expr(left)
1916 g.write(', (u64)')
1917 } else if is_safe_add_assign || is_safe_sub_assign || is_safe_mul_assign
1918 || is_safe_div_assign || is_safe_mod_assign {
1919 overflow_styp := g.styp(get_overflow_fn_type(var_type))
1920 div_mod_styp :=
1921 g.styp(g.unwrap_generic(var_type).clear_flag(.shared_f).clear_flag(.atomic_f))
1922 vsafe_fn_name := match true {
1923 is_safe_add_assign { 'builtin__overflow__add_${overflow_styp}' }
1924 is_safe_sub_assign { 'builtin__overflow__sub_${overflow_styp}' }
1925 is_safe_mul_assign { 'builtin__overflow__mul_${overflow_styp}' }
1926 is_safe_div_assign { 'VSAFE_DIV_${div_mod_styp}' }
1927 is_safe_mod_assign { 'VSAFE_MOD_${div_mod_styp}' }
1928 else { '' }
1929 }
1930
1931 if is_safe_div_assign || is_safe_mod_assign {
1932 g.vsafe_arithmetic_ops[vsafe_fn_name] = VSafeArithmeticOp{
1933 typ: g.unwrap_generic(var_type).clear_flag(.shared_f).clear_flag(.atomic_f)
1934 op: token.assign_op_to_infix_op(node.op)
1935 }
1936 }
1937 g.write(' = ${vsafe_fn_name}(')
1938 g.expr(left)
1939 g.write(',')
1940 }
1941 mut decl_tmp_var := ''
1942 mut decl_stmt_str := ''
1943 if is_decl && g.inside_ternary == 0 && !node.is_static && !var_type.has_flag(.shared_f)
1944 && g.decl_assign_struct_init_needs_tmp(val) {
1945 decl_stmt_str = g.go_before_last_stmt().trim_space()
1946 g.empty_line = true
1947 decl_tmp_var = g.new_tmp_var()
1948 g.write('${styp} ')
1949 if is_auto_heap && !(val_type.is_ptr() && val_type.has_flag(.option)) {
1950 g.write('*')
1951 }
1952 g.write('${decl_tmp_var} ${op} ')
1953 }
1954 mut cloned := false
1955 if g.is_autofree {
1956 if right_sym.kind in [.array, .string] && !unwrapped_val_type.has_flag(.shared_f) {
1957 if !skip_str_add_rhs_clone
1958 && g.gen_clone_assignment(var_type, val, unwrapped_val_type, false) {
1959 cloned = true
1960 }
1961 } else if right_sym.info is ast.Interface && var_type != ast.error_type {
1962 g.register_free_method(var_type)
1963 }
1964 }
1965 if !cloned {
1966 if g.comptime.comptime_for_field_var == ''
1967 && ((var_type.has_flag(.option) && !val_type.has_flag(.option))
1968 || (var_type.has_flag(.result) && !val_type.has_flag(.result))) {
1969 old_inside_opt_or_res := g.inside_opt_or_res
1970 defer(fn) {
1971 g.inside_opt_or_res = old_inside_opt_or_res
1972 }
1973 g.inside_opt_or_res = true
1974 if is_decl && is_auto_heap && var_type.has_flag(.option) {
1975 g.write('&')
1976 }
1977 tmp_var := g.new_tmp_var()
1978 g.expr_with_tmp_var(val, val_type, var_type, tmp_var, true)
1979 } else if is_fixed_array_var {
1980 // TODO: Instead of the translated check, check if it's a pointer already
1981 // and don't generate memcpy &
1982 typ_str := g.styp(val_type).trim('*')
1983 final_typ_str := if is_fixed_array_var { '' } else { '(${typ_str}*)' }
1984 final_ref_str := if is_fixed_array_var {
1985 ''
1986 } else if val_type.is_ptr() {
1987 '(byte*)'
1988 } else {
1989 '(byte*)&'
1990 }
1991 if val_type.has_flag(.option) {
1992 g.expr(left)
1993 g.write(' = ')
1994 g.expr(val)
1995 } else {
1996 if op_overloaded {
1997 g.expr(left)
1998 g.write(', ')
1999 g.expr(val)
2000 g.write(').ret_arr, sizeof(${typ_str})')
2001 } else {
2002 g.write('memcpy(${final_typ_str}')
2003 g.expr(left)
2004 g.write(', ${final_ref_str}')
2005 g.expr(val)
2006 g.write(', sizeof(${typ_str}))')
2007 }
2008 }
2009 } else if is_decl {
2010 g.is_shared = var_type.has_flag(.shared_f)
2011 if is_fixed_array_init && !has_val {
2012 if val is ast.ArrayInit {
2013 g.array_init(val, g.ident_cname(ident))
2014 } else {
2015 g.write('{0}')
2016 }
2017 } else {
2018 is_option_unwrapped := assign_expr_unwraps_option_or_result(val)
2019 is_option_auto_heap := is_auto_heap && is_option_unwrapped
2020 auto_heap_uses_existing_storage := is_auto_heap && !is_fn_var
2021 && !is_option_auto_heap
2022 && g.auto_heap_assignment_uses_existing_storage(val, val_type)
2023 // For large structs (with large fixed arrays), avoid stack-allocated
2024 // compound literals which can cause stack overflow. Use vcalloc directly.
2025 mut is_large_struct_heap := false
2026 if is_auto_heap && !is_fn_var && val is ast.StructInit
2027 && g.struct_has_large_fixed_array(val.typ) {
2028 is_large_struct_heap = true
2029 }
2030 if is_auto_heap && !is_fn_var && !is_large_struct_heap
2031 && !auto_heap_uses_existing_storage {
2032 if aligned != 0 {
2033 g.write('HEAP_align(${styp}, (')
2034 } else {
2035 ptrmap_o, _ := g.vgc_ptrmap(var_type.set_nr_muls(0))
2036 if ptrmap_o.len > 0 {
2037 g.write('HEAP_vgc(${styp}, (')
2038 } else {
2039 g.write('HEAP(${styp}, (')
2040 }
2041 }
2042 }
2043 if !is_fn_var && val.is_auto_deref_var() && !is_option_unwrapped
2044 && !g.auto_deref_source_type_is_pointer(val) {
2045 g.write('*')
2046 }
2047 if (var_type.has_flag(.option) && val !in [ast.Ident, ast.SelectorExpr])
2048 || gen_or {
2049 g.expr_with_opt_or_block(val, val_type, left, var_type,
2050 is_option_auto_heap)
2051 } else if val is ast.ArrayInit {
2052 cvar_name := g.ident_cname(ident)
2053 if val.is_fixed && ident.name in g.defer_vars {
2054 g.go_before_last_stmt()
2055 g.empty_line = true
2056 g.write('memcpy(${cvar_name}, ')
2057 g.write('(${styp})')
2058 g.array_init(val, cvar_name)
2059 g.write(', sizeof(${styp}))')
2060 } else {
2061 g.array_init(val, cvar_name)
2062 }
2063 } else if val is ast.ParExpr && val.expr is ast.ArrayInit {
2064 array_init := val.expr as ast.ArrayInit
2065 cvar_name := g.ident_cname(ident)
2066 if array_init.is_fixed && ident.name in g.defer_vars {
2067 g.go_before_last_stmt()
2068 g.empty_line = true
2069 g.write('memcpy(${cvar_name}, ')
2070 g.write('(${styp})')
2071 g.array_init(array_init, cvar_name)
2072 g.write(', sizeof(${styp}))')
2073 } else {
2074 g.array_init(array_init, cvar_name)
2075 }
2076 } else if var_type.has_flag(.shared_f) && !val_type.has_flag(.shared_f)
2077 && !val_type.is_ptr() {
2078 g.expr_with_cast(val, val_type, var_type)
2079 } else if val_type.has_flag(.shared_f) || orig_val_shared {
2080 g.expr_with_cast(val, val_type.set_flag(.shared_f), var_type)
2081 } else if val in [ast.MatchExpr, ast.IfExpr]
2082 && unaliased_right_sym.info is ast.ArrayFixed {
2083 tmp_var := g.expr_with_var(val, var_type, false)
2084 // When the temp var is a return wrapper struct (_v_Array_fixed_...),
2085 // access .ret_arr to get the actual C array for subscripting.
2086 init_expr := if unaliased_right_sym.info.is_fn_ret {
2087 '${tmp_var}.ret_arr'
2088 } else {
2089 tmp_var
2090 }
2091 g.fixed_array_var_init(init_expr, false,
2092 unaliased_right_sym.info.elem_type, unaliased_right_sym.info.size)
2093 } else if is_large_struct_heap && val is ast.StructInit {
2094 // For large structs, use vcalloc directly to avoid stack overflow
2095 // from compound literals on the stack
2096 tmp_var := g.new_tmp_var()
2097 stmt_str := g.go_before_last_stmt()
2098 g.empty_line = true
2099 g.writeln('${styp}* ${tmp_var} = (${styp}*)builtin__vcalloc(sizeof(${styp}));')
2100 // Initialize non-zero fields
2101 val_sym := g.table.final_sym(val.typ)
2102 if val_sym.info is ast.Struct {
2103 for init_field in val.init_fields {
2104 if init_field.typ == 0 {
2105 continue
2106 }
2107 field_name := c_name(init_field.name)
2108 g.write('${tmp_var}->${field_name} = ')
2109 g.expr(init_field.expr)
2110 g.writeln(';')
2111 }
2112 // Handle fields with default values
2113 for field in val_sym.info.fields {
2114 mut found := false
2115 for init_field in val.init_fields {
2116 if init_field.name == field.name {
2117 found = true
2118 break
2119 }
2120 }
2121 if !found && field.has_default_expr {
2122 field_name := c_name(field.name)
2123 g.write('${tmp_var}->${field_name} = ')
2124 g.expr(field.default_expr)
2125 g.writeln(';')
2126 }
2127 }
2128 }
2129 g.empty_line = false
2130 g.write2(stmt_str, tmp_var)
2131 } else {
2132 old_inside_assign_fn_var := g.inside_assign_fn_var
2133 g.inside_assign_fn_var = val is ast.PrefixExpr && val.op == .amp
2134 && is_fn_var
2135 mut nval := val
2136 if val is ast.PrefixExpr && val.right is ast.CallExpr {
2137 call_expr := val.right as ast.CallExpr
2138 if call_expr.name == 'new_array_from_c_array' {
2139 nval = call_expr
2140 if !var_type.has_flag(.shared_f) {
2141 g.write('HEAP(${g.styp(var_type.clear_ref())}, ')
2142 }
2143 g.expr(nval)
2144 if !var_type.has_flag(.shared_f) {
2145 g.write(')')
2146 }
2147 }
2148 }
2149 if nval == val {
2150 if auto_heap_uses_existing_storage {
2151 g.write_auto_heap_assignment_expr(nval, val_type)
2152 } else {
2153 g.expr_in_value_context(nval, val_type, var_type)
2154 }
2155 if !is_fn_var && is_auto_heap && is_option_auto_heap
2156 && !is_large_struct_heap {
2157 if aligned != 0 {
2158 g.write('), ${aligned})')
2159 } else {
2160 g.write('))')
2161 }
2162 }
2163 }
2164 g.inside_assign_fn_var = old_inside_assign_fn_var
2165 }
2166 if !is_fn_var && is_auto_heap && !is_option_auto_heap
2167 && !auto_heap_uses_existing_storage && !is_large_struct_heap {
2168 if aligned != 0 {
2169 g.write('), ${aligned})')
2170 } else {
2171 ptrmap, nptrs := g.vgc_ptrmap(var_type.set_nr_muls(0))
2172 if ptrmap.len > 0 {
2173 g.write('), ${ptrmap}, ${nptrs})')
2174 } else {
2175 g.write('))')
2176 }
2177 }
2178 }
2179 }
2180 } else {
2181 // var = &auto_heap_var
2182 old_is_auto_heap := g.is_option_auto_heap
2183 defer(fn) {
2184 g.is_option_auto_heap = old_is_auto_heap
2185 }
2186 if val is ast.Ident && val.is_mut() && var_type.is_ptr() {
2187 if var_type.nr_muls() < val_type.nr_muls() {
2188 g.write('*'.repeat(var_type.nr_muls()))
2189 }
2190 }
2191 g.is_option_auto_heap = val_type.has_flag(.option) && val is ast.PrefixExpr
2192 && val.right is ast.Ident && (val.right as ast.Ident).is_auto_heap()
2193 if var_type.has_flag(.option) || gen_or {
2194 g.expr_with_opt_or_block(val, val_type, left, var_type, false)
2195 } else if node.has_cross_var {
2196 g.gen_cross_tmp_variable(node.left, val)
2197 } else if val is ast.None {
2198 if var_type.has_flag(.generic)
2199 && g.unwrap_generic(var_type).has_flag(.option)
2200 && var_type.nr_muls() > 0 {
2201 g.gen_option_error(var_type.set_nr_muls(0), ast.None{})
2202 } else {
2203 g.gen_option_error(var_type, ast.None{})
2204 }
2205 } else {
2206 if op_overloaded {
2207 g.op_arg(val, op_expected_right, val_type)
2208 } else {
2209 exp_type := if is_mut_arg_pointer_rebind {
2210 var_type
2211 } else if var_type.is_ptr()
2212 && (left.is_auto_deref_var() || var_type.has_flag(.shared_f)) {
2213 var_type.deref()
2214 } else {
2215 var_type
2216 }.clear_flag(.shared_f) // don't reset the mutex, just change the value
2217 mut use_heap_pointed_ident := false
2218 mut use_raw_auto_heap_ident := false
2219 if val is ast.PrefixExpr && val.op == .amp && val.right is ast.Ident {
2220 right_ident := val.right as ast.Ident
2221 mut resolved_right_type := g.resolved_expr_type(ast.Expr(right_ident),
2222 val.right_type)
2223 if resolved_right_type == 0 {
2224 resolved_right_type = val.right_type
2225 }
2226 resolved_right_type =
2227 g.unwrap_generic(g.recheck_concrete_type(resolved_right_type))
2228 rhs_sym_ := g.table.final_sym(resolved_right_type)
2229 if rhs_sym_.kind != .function {
2230 right_type_for_compare :=
2231 resolved_right_type.clear_flag(.shared_f).clear_flag(.atomic_f)
2232 exp_type_for_compare :=
2233 exp_type.clear_flag(.shared_f).clear_flag(.atomic_f)
2234 right_points_to_heap := resolved_right_type.is_ptr()
2235 && g.table.final_sym(resolved_right_type.deref()).is_heap()
2236 use_heap_pointed_ident = resolved_right_type != 0
2237 && right_points_to_heap
2238 && right_type_for_compare == exp_type_for_compare
2239 right_is_auto_heap := right_ident.is_auto_heap()
2240 || g.resolved_ident_is_auto_heap(right_ident)
2241 use_raw_auto_heap_ident = exp_type.is_ptr()
2242 && right_is_auto_heap && !use_heap_pointed_ident
2243 && resolved_right_type != 0 && !resolved_right_type.is_ptr()
2244 }
2245 }
2246 if use_heap_pointed_ident {
2247 g.expr(ast.Expr((val as ast.PrefixExpr).right))
2248 } else if use_raw_auto_heap_ident {
2249 old_inside_assign_fn_var := g.inside_assign_fn_var
2250 g.inside_assign_fn_var = true
2251 g.expr(ast.Expr((val as ast.PrefixExpr).right))
2252 g.inside_assign_fn_var = old_inside_assign_fn_var
2253 } else {
2254 g.expr_with_cast(val, val_type, exp_type)
2255 }
2256 }
2257 }
2258 }
2259 }
2260 if str_add || op_overloaded || is_safe_add_assign || is_safe_sub_assign
2261 || is_safe_mul_assign || is_safe_div_assign || is_safe_mod_assign {
2262 g.write(')')
2263 } else if is_safe_shift_assign {
2264 g.write(')')
2265 }
2266 if node_.op == .assign && var_type.has_flag(.option_mut_param_t) {
2267 g.write('.data, sizeof(${g.base_type(val_type)}))')
2268 }
2269 if cur_indexexpr != -1 {
2270 g.cur_indexexpr.delete(cur_indexexpr)
2271 g.write(' })')
2272 g.is_arraymap_set = g.cur_indexexpr.len > 0
2273 }
2274 if decl_tmp_var != '' {
2275 g.writeln(';')
2276 g.write2(decl_stmt_str, ' ')
2277 g.write(decl_tmp_var)
2278 }
2279 g.is_shared = false
2280 }
2281 g.right_is_opt = false
2282 if g.inside_ternary == 0 && (node.left.len > 1 || !node.is_simple) {
2283 g.writeln(';')
2284 }
2285 }
2286}
2287
2288fn (mut g Gen) gen_multi_return_assign(node &ast.AssignStmt, return_type ast.Type, return_sym ast.TypeSymbol) {
2289 // multi return
2290 // TODO: Handle in if_expr
2291 mut ret_type := return_type
2292 mut ret_sym := return_sym
2293 mut suffix := ''
2294 if g.comptime.inside_comptime_for && node.right[0] is ast.CallExpr {
2295 call_expr := node.right[0] as ast.CallExpr
2296 if call_expr.concrete_types.len > 0 && return_sym.info is ast.MultiReturn
2297 && g.comptime.comptime_for_field_var != '' {
2298 field_type := g.comptime.comptime_for_field_type
2299 field_sym := g.table.sym(field_type)
2300 if field_sym.info is ast.Map {
2301 map_info := field_sym.info as ast.Map
2302 ret_type =
2303 g.table.find_or_register_multi_return([map_info.key_type, map_info.value_type])
2304 ret_sym = *g.table.sym(ret_type)
2305 }
2306 }
2307 if g.comptime.comptime_for_field_var != '' {
2308 suffix = '_${g.comptime.comptime_for_field_value.name}'
2309 }
2310 }
2311 if node.right[0] is ast.CallExpr {
2312 call_expr := node.right[0] as ast.CallExpr
2313 mut fn_var_type := ast.void_type
2314 lookup_name := if call_expr.left is ast.Ident {
2315 call_expr.left.name
2316 } else {
2317 call_expr.name
2318 }
2319 resolved_current_type := g.resolve_current_fn_generic_param_type(lookup_name)
2320 if resolved_current_type != 0
2321 && g.table.final_sym(g.unwrap_generic(resolved_current_type)).kind == .function {
2322 fn_var_type = g.unwrap_generic(g.recheck_concrete_type(resolved_current_type))
2323 } else if call_expr.is_fn_var {
2324 fn_var_type = g.unwrap_generic(g.recheck_concrete_type(call_expr.fn_var_type))
2325 }
2326 if fn_var_type == 0 {
2327 if obj := call_expr.scope.find_var(lookup_name) {
2328 if g.table.final_sym(g.unwrap_generic(obj.typ)).kind == .function {
2329 fn_var_type = g.unwrap_generic(g.recheck_concrete_type(obj.typ))
2330 }
2331 }
2332 }
2333 if fn_var_type != 0 {
2334 fn_sym := g.table.final_sym(fn_var_type)
2335 if fn_sym.info is ast.FnType {
2336 resolved_ret_type :=
2337 g.unwrap_generic(g.recheck_concrete_type(fn_sym.info.func.return_type))
2338 if resolved_ret_type != 0 && g.table.sym(resolved_ret_type).kind == .multi_return {
2339 ret_type = resolved_ret_type
2340 ret_sym = *g.table.sym(ret_type)
2341 }
2342 }
2343 }
2344 }
2345 mr_var_name := 'mr_${node.pos.pos}${suffix}'
2346 mut is_option := ret_type.has_flag(.option)
2347 mut mr_styp := g.styp(ret_type.clear_flag(.result))
2348 if node.right[0] is ast.CallExpr && node.right[0].or_block.kind != .absent {
2349 is_option = false
2350 mr_styp = g.styp(ret_type.clear_option_and_result())
2351 }
2352 g.write('${mr_styp} ${mr_var_name} = ')
2353 g.expr(node.right[0])
2354 g.writeln(';')
2355 raw_mr_types := (ret_sym.info as ast.MultiReturn).types
2356 is_generic_context := g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0
2357 mr_types := if is_generic_context {
2358 raw_mr_types.map(g.unwrap_generic(g.recheck_concrete_type(it)))
2359 } else if node.right_types.len == raw_mr_types.len {
2360 node.right_types.clone()
2361 } else {
2362 raw_mr_types.clone()
2363 }
2364 mut recompute_types := node.op == .decl_assign || ret_type != return_type
2365 if g.comptime.inside_comptime_for && node.right[0] is ast.CallExpr {
2366 call_expr := node.right[0] as ast.CallExpr
2367 if call_expr.concrete_types.len > 0 && g.comptime.comptime_for_field_var != ''
2368 && return_sym.info is ast.MultiReturn {
2369 recompute_types = true
2370 for i, mut lx in &node.left {
2371 if mut lx is ast.Ident && lx.kind != .blank_ident {
2372 if mut lx.obj is ast.Var {
2373 lx.obj.typ = mr_types[i]
2374 }
2375 }
2376 }
2377 }
2378 }
2379 if recompute_types {
2380 for i, mut lx in &node.left {
2381 if mut lx is ast.Ident && lx.kind != .blank_ident {
2382 if mut lx.obj is ast.Var && i < mr_types.len {
2383 lx.obj.typ = mr_types[i]
2384 if !mr_types[i].has_option_or_result() {
2385 lx.obj.orig_type = ast.no_type
2386 lx.obj.smartcasts = []
2387 lx.obj.is_unwrapped = false
2388 }
2389 if lx.obj.ct_type_var != .no_comptime {
2390 g.type_resolver.update_ct_type(lx.name, mr_types[i])
2391 }
2392 }
2393 if i < mr_types.len && lx.scope != unsafe { nil } {
2394 if mut scope_var := lx.scope.find_var(lx.name) {
2395 scope_var.typ = mr_types[i]
2396 if !mr_types[i].has_option_or_result() {
2397 scope_var.orig_type = ast.no_type
2398 scope_var.smartcasts = []
2399 scope_var.is_unwrapped = false
2400 }
2401 }
2402 }
2403 }
2404 }
2405 }
2406 for i, lx in node.left {
2407 mut cur_indexexpr := -1
2408 mut is_auto_heap := false
2409 mut ident := ast.Ident{
2410 scope: unsafe { nil }
2411 }
2412 if lx is ast.Ident {
2413 ident = lx
2414 if lx.kind == .blank_ident {
2415 continue
2416 }
2417 if lx.obj is ast.Var {
2418 is_auto_heap = lx.obj.is_auto_heap
2419 }
2420 }
2421 if lx is ast.IndexExpr && g.cur_indexexpr.len > 0 {
2422 cur_indexexpr = g.cur_indexexpr.index(lx.pos.pos)
2423 }
2424 left_type := if recompute_types { mr_types[i] } else { node.left_types[i] }
2425 styp := if ident.name in g.defer_vars { '' } else { g.styp(left_type) }
2426 needs_auto_heap_alloc := is_auto_heap && node.op == .decl_assign
2427 if node.op == .decl_assign {
2428 g.write('${styp} ')
2429 }
2430 if lx.is_auto_deref_var() {
2431 g.write('*')
2432 }
2433 noscan := if is_auto_heap { g.check_noscan(return_type) } else { '' }
2434 mut aligned := 0
2435 sym := g.table.final_sym(left_type)
2436 if sym.info is ast.Struct {
2437 if attr := sym.info.attrs.find_first('aligned') {
2438 aligned = if attr.arg == '' { 0 } else { attr.arg.int() }
2439 }
2440 }
2441 if left_type.has_flag(.option) {
2442 base_typ := g.base_type(left_type)
2443 tmp_var := if needs_auto_heap_alloc {
2444 if aligned != 0 {
2445 'HEAP_align(${styp}, ${mr_var_name}.arg${i}, ${aligned})'
2446 } else {
2447 'HEAP${noscan}(${styp}, ${mr_var_name}.arg${i})'
2448 }
2449 } else if is_option {
2450 '(*((${g.base_type(return_type)}*)${mr_var_name}.data)).arg${i}'
2451 } else {
2452 '${mr_var_name}.arg${i}'
2453 }
2454 if mr_types[i].has_flag(.option) {
2455 old_left_is_opt := g.left_is_opt
2456 g.left_is_opt = true
2457 g.expr(lx)
2458 g.writeln(' = ${tmp_var};')
2459 g.left_is_opt = old_left_is_opt
2460 } else {
2461 g.write('builtin___option_ok(&(${base_typ}[]) { ${tmp_var} }, (${option_name}*)(&')
2462 tmp_left_is_opt := g.left_is_opt
2463 g.left_is_opt = true
2464 g.expr(lx)
2465 g.left_is_opt = tmp_left_is_opt
2466 g.writeln('), sizeof(${base_typ}));')
2467 }
2468 } else {
2469 g.expr(lx)
2470 if sym.kind == .array_fixed {
2471 g.writeln2(';',
2472 'memcpy(&${g.expr_string(lx)}, &${mr_var_name}.arg${i}, sizeof(${styp}));')
2473 } else {
2474 if cur_indexexpr != -1 {
2475 if needs_auto_heap_alloc {
2476 if aligned != 0 {
2477 g.writeln('HEAP_align(${styp}, ${mr_var_name}.arg${i}, ${aligned}) });')
2478 } else {
2479 g.writeln('HEAP${noscan}(${styp}, ${mr_var_name}.arg${i}) });')
2480 }
2481 } else if is_option {
2482 g.writeln('(*((${g.base_type(return_type)}*)${mr_var_name}.data)).arg${i} });')
2483 } else {
2484 g.writeln('${mr_var_name}.arg${i} });')
2485 }
2486 g.cur_indexexpr.delete(cur_indexexpr)
2487 } else {
2488 if needs_auto_heap_alloc {
2489 if aligned != 0 {
2490 g.writeln(' = HEAP_align(${styp}, ${mr_var_name}.arg${i}, ${aligned});')
2491 } else {
2492 g.writeln(' = HEAP${noscan}(${styp}, ${mr_var_name}.arg${i});')
2493 }
2494 } else if is_option {
2495 g.writeln(' = (*((${g.base_type(return_type)}*)${mr_var_name}.data)).arg${i};')
2496 } else {
2497 g.writeln(' = ${mr_var_name}.arg${i};')
2498 }
2499 }
2500 }
2501 }
2502 }
2503 if g.is_arraymap_set {
2504 g.is_arraymap_set = false
2505 }
2506}
2507
2508fn (mut g Gen) gen_cross_var_assign(node &ast.AssignStmt) {
2509 for i, left in node.left {
2510 left_is_auto_deref_var := left.is_auto_deref_var()
2511 match left {
2512 ast.Ident {
2513 left_typ := node.left_types[i]
2514 left_sym := g.table.sym(left_typ)
2515 mut anon_ctx := ''
2516 if g.anon_fn != unsafe { nil } {
2517 if obj := left.scope.find_var(left.name) {
2518 if obj.is_inherited {
2519 anon_ctx = '${closure_ctx}->'
2520 }
2521 }
2522 }
2523 if left_sym.info is ast.FnType {
2524 g.write_fn_ptr_decl(&left_sym.info, '_var_${left.pos.pos}')
2525 g.writeln(' = ${anon_ctx}${g.ident_cname(left)};')
2526 } else if left_is_auto_deref_var {
2527 styp := g.styp(left_typ).trim('*')
2528 if left_sym.kind == .array {
2529 g.writeln('${styp} _var_${left.pos.pos} = builtin__array_clone(${anon_ctx}${g.ident_cname(left)});')
2530 } else {
2531 g.writeln('${styp} _var_${left.pos.pos} = *${anon_ctx}${g.ident_cname(left)};')
2532 }
2533 } else {
2534 styp := g.styp(left_typ)
2535 if left_sym.kind == .array {
2536 g.writeln('${styp} _var_${left.pos.pos} = builtin__array_clone(&${anon_ctx}${g.ident_cname(left)});')
2537 } else {
2538 g.writeln('${styp} _var_${left.pos.pos} = ${anon_ctx}${g.ident_cname(left)};')
2539 }
2540 }
2541 }
2542 ast.IndexExpr {
2543 if left.is_index_operator {
2544 styp := g.styp(node.left_types[i])
2545 g.write('${styp} _var_${left.pos.pos} = ')
2546 g.expr(ast.Expr(left))
2547 g.writeln(';')
2548 continue
2549 }
2550 mut container_type := left.left_type
2551 if g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0 {
2552 resolved_container_type := g.resolved_expr_type(left.left, left.left_type)
2553 if resolved_container_type != 0 {
2554 container_type =
2555 g.unwrap_generic(g.recheck_concrete_type(resolved_container_type))
2556 }
2557 }
2558 sym := g.table.sym(g.table.unaliased_type(container_type))
2559 if sym.kind == .array {
2560 info := sym.info as ast.Array
2561 elem_sym := g.table.sym(info.elem_type)
2562 needs_clone := info.elem_type == ast.string_type && g.is_autofree
2563
2564 if elem_sym.kind == .function {
2565 left_typ := node.left_types[i]
2566 left_sym := g.table.sym(left_typ)
2567 g.write_fn_ptr_decl(left_sym.info as ast.FnType, '_var_${left.pos.pos}')
2568 g.write(' = *(voidptr*)builtin__array_get(')
2569 } else {
2570 styp := g.styp(info.elem_type)
2571 string_clone := if needs_clone { 'builtin__string_clone(' } else { '' }
2572
2573 g.write('${styp} _var_${left.pos.pos} = ${string_clone}*(${styp}*)builtin__array_get(')
2574 }
2575
2576 if left.left_type.is_ptr() || left.left.is_auto_deref_var() {
2577 g.write('*')
2578 }
2579 g.expr(left.left)
2580 g.write(', ')
2581 g.expr(left.index)
2582 if needs_clone {
2583 g.write(')')
2584 }
2585 g.writeln(');')
2586 } else if sym.kind == .array_fixed {
2587 info := sym.info as ast.ArrayFixed
2588 elem_sym := g.table.sym(info.elem_type)
2589 if elem_sym.kind == .function {
2590 left_typ := node.left_types[i]
2591 left_sym := g.table.sym(left_typ)
2592 g.write_fn_ptr_decl(left_sym.info as ast.FnType, '_var_${left.pos.pos}')
2593 g.write(' = *(voidptr*)')
2594 } else {
2595 styp := g.styp(info.elem_type)
2596 g.write('${styp} _var_${left.pos.pos} = ')
2597 }
2598 if left.left_type.is_ptr() {
2599 g.write('*')
2600 }
2601 needs_clone := info.elem_type == ast.string_type && g.is_autofree
2602 if needs_clone {
2603 g.write('builtin__string_clone(')
2604 }
2605 g.expr(left)
2606 if needs_clone {
2607 g.write(')')
2608 }
2609 g.writeln(';')
2610 } else if sym.kind == .map {
2611 info := sym.info as ast.Map
2612 styp := g.styp(info.value_type)
2613 zero := g.type_default(info.value_type)
2614 val_sym := g.table.sym(info.value_type)
2615 if val_sym.kind == .function {
2616 left_type := node.left_types[i]
2617 left_sym := g.table.sym(left_type)
2618 g.write_fn_ptr_decl(left_sym.info as ast.FnType, '_var_${left.pos.pos}')
2619 g.write(' = *(voidptr*)builtin__map_get(')
2620 } else {
2621 g.write('${styp} _var_${left.pos.pos} = *(${styp}*)builtin__map_get(')
2622 }
2623 if !left.left_type.is_ptr() {
2624 g.write('ADDR(map, ')
2625 g.expr(left.left)
2626 g.write(')')
2627 } else {
2628 g.expr(left.left)
2629 }
2630 g.write(', ')
2631 g.write_map_key_arg(left.index, info.key_type)
2632 if val_sym.kind == .function {
2633 g.writeln(', &(voidptr[]){ ${zero} });')
2634 } else {
2635 g.writeln(', &(${styp}[]){ ${zero} });')
2636 }
2637 }
2638 }
2639 ast.SelectorExpr {
2640 styp := g.styp(left.typ)
2641 g.write('${styp} _var_${left.pos.pos} = ')
2642 g.expr(left.expr)
2643 sel := g.dot_or_ptr(left.expr_type)
2644 g.writeln('${sel}${left.field_name};')
2645 }
2646 else {}
2647 }
2648 }
2649}
2650
2651fn (mut g Gen) gen_cross_tmp_variable(left []ast.Expr, val ast.Expr) {
2652 val_ := val
2653 match val {
2654 ast.Ident {
2655 mut has_var := false
2656 for lx in left {
2657 if lx is ast.Ident {
2658 if val.name == lx.name {
2659 g.write2('_var_', lx.pos.pos.str())
2660 has_var = true
2661 break
2662 }
2663 }
2664 }
2665 if !has_var {
2666 g.expr(val_)
2667 }
2668 }
2669 ast.IndexExpr {
2670 mut has_var := false
2671 for lx in left {
2672 if val_.str() == lx.str() {
2673 g.write2('_var_', lx.pos().pos.str())
2674 has_var = true
2675 break
2676 }
2677 }
2678 if !has_var {
2679 g.expr(val_)
2680 }
2681 }
2682 ast.InfixExpr {
2683 sym := g.table.sym(val.left_type)
2684 svalop := val.op.str()
2685 if _ := g.table.find_method(sym, svalop) {
2686 left_styp := g.styp(val.left_type.set_nr_muls(0))
2687 g.write2(left_styp, '_')
2688 g.write2(util.replace_op(svalop), '(')
2689 g.gen_cross_tmp_variable(left, val.left)
2690 g.write(', ')
2691 g.gen_cross_tmp_variable(left, val.right)
2692 g.write(')')
2693 } else {
2694 g.gen_cross_tmp_variable(left, val.left)
2695 g.write(svalop)
2696 g.gen_cross_tmp_variable(left, val.right)
2697 }
2698 }
2699 ast.ParExpr {
2700 g.write('(')
2701 g.gen_cross_tmp_variable(left, val.expr)
2702 g.write(')')
2703 }
2704 ast.CallExpr {
2705 if val.is_method {
2706 unwrapped_rec_type, typ_sym := g.unwrap_receiver_type(val)
2707 left_type := g.unwrap_generic(val.left_type)
2708 left_sym := g.table.sym(left_type)
2709 final_left_sym := g.table.final_sym(left_type)
2710 rec_typ_name := g.resolve_receiver_name(val, unwrapped_rec_type, final_left_sym,
2711 left_sym, typ_sym)
2712 mut fn_name := util.no_dots('${rec_typ_name}_${val.name}')
2713 if resolved_sym := g.table.find_sym(rec_typ_name) {
2714 if resolved_sym.is_builtin() && !fn_name.starts_with('builtin__') {
2715 fn_name = 'builtin__${fn_name}'
2716 }
2717 } else if rec_typ_name in ['int_literal', 'float_literal', 'vint_t'] {
2718 fn_name = 'builtin__${fn_name}'
2719 }
2720 g.write('${fn_name}(&')
2721 g.gen_cross_tmp_variable(left, val.left)
2722 for i, arg in val.args {
2723 g.gen_cross_tmp_variable(left, arg.expr)
2724 if i != val.args.len - 1 {
2725 g.write(', ')
2726 }
2727 }
2728 g.write(')')
2729 } else {
2730 mut fn_name := val.name.replace('.', '__')
2731 if val.concrete_types.len > 0 {
2732 fn_name = g.generic_fn_name(val.concrete_types, fn_name)
2733 }
2734 g.write('${fn_name}(')
2735 for i, arg in val.args {
2736 g.gen_cross_tmp_variable(left, arg.expr)
2737 if i != val.args.len - 1 {
2738 g.write(', ')
2739 }
2740 }
2741 g.write(')')
2742 }
2743 }
2744 ast.PrefixExpr {
2745 g.write(val.op.str())
2746 g.gen_cross_tmp_variable(left, val.right)
2747 }
2748 ast.PostfixExpr {
2749 g.gen_cross_tmp_variable(left, val.expr)
2750 g.write(val.op.str())
2751 }
2752 ast.SelectorExpr {
2753 mut has_var := false
2754 for lx in left {
2755 if val_.str() == lx.str() {
2756 g.write2('_var_', lx.pos().pos.str())
2757 has_var = true
2758 break
2759 }
2760 }
2761 if !has_var {
2762 g.expr(val_)
2763 }
2764 }
2765 else {
2766 g.expr(val_)
2767 }
2768 }
2769}
2770