v4 / vlib / v / gen / c / for.v
1215 lines · 1180 sloc · 38.98 KB · 1a2d0e5b70d6dd1baaa60290f2446e9ece4324bf
Raw
1// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4module c
5
6import v.ast
7import v.token
8import v.util
9
10struct ForCOverflowGuard {
11 cname string
12 limit_expr string
13}
14
15fn for_in_val_type(base_type ast.Type, is_mut bool, is_ref bool) ast.Type {
16 if base_type == 0 {
17 return base_type
18 }
19 if is_mut || is_ref {
20 if base_type.has_flag(.option) {
21 return base_type.set_flag(.option_mut_param_t)
22 }
23 if !base_type.is_any_kind_of_pointer() {
24 return base_type.ref()
25 }
26 }
27 return base_type
28}
29
30fn (mut g Gen) write_for_in_array_value_decl(node ast.ForInStmt, styp string, val_sym_ ast.TypeSymbol) {
31 mut val_sym := val_sym_
32 if mut val_sym.info is ast.FnType {
33 g.writeln('${g.fn_ptr_decl_str(val_sym.info, c_name(node.val_var))};')
34 return
35 }
36 if !node.val_type.has_flag(.option) && val_sym.kind == .array_fixed && !node.val_is_mut {
37 g.writeln('${styp} ${c_name(node.val_var)};')
38 return
39 }
40 needs_memcpy := !node.val_type.is_ptr() && !node.val_type.has_flag(.option)
41 && g.table.final_sym(node.val_type).kind == .array_fixed
42 if needs_memcpy {
43 g.writeln('${styp} ${c_name(node.val_var)} = {0};')
44 } else {
45 g.writeln('${styp} ${c_name(node.val_var)};')
46 }
47}
48
49fn (mut g Gen) write_for_in_array_value_assign(node ast.ForInStmt, styp string, val_sym_ ast.TypeSymbol, cond_var string, op_field string, idx string, cond_is_option bool, opt_expr string) {
50 mut val_sym := val_sym_
51 if mut val_sym.info is ast.FnType {
52 g.writeln('\t${c_name(node.val_var)} = ((voidptr*)${cond_var}${op_field}data)[${idx}];')
53 return
54 }
55 if !node.val_type.has_flag(.option) && val_sym.kind == .array_fixed && !node.val_is_mut {
56 right := '((${styp}*)${cond_var}${op_field}data)[${idx}]'
57 g.writeln('\tmemcpy(*(${styp}*)${c_name(node.val_var)}, (byte*)${right}, sizeof(${styp}));')
58 return
59 }
60 needs_memcpy := !node.val_type.is_ptr() && !node.val_type.has_flag(.option)
61 && g.table.final_sym(node.val_type).kind == .array_fixed
62 right := if cond_is_option {
63 '((${styp}*)${opt_expr}${op_field}data)[${idx}]'
64 } else if node.val_is_mut || node.val_is_ref {
65 if g.table.value_type(node.cond_type).is_ptr() {
66 '((${styp}*)${cond_var}${op_field}data)[${idx}]'
67 } else {
68 '((${styp})${cond_var}${op_field}data) + ${idx}'
69 }
70 } else if val_sym.kind == .array_fixed {
71 '((${styp}*)${cond_var}${op_field}data)[${idx}]'
72 } else {
73 '((${styp}*)${cond_var}${op_field}data)[${idx}]'
74 }
75 if !needs_memcpy {
76 g.writeln('\t${c_name(node.val_var)} = ${right};')
77 } else {
78 g.writeln('\tmemcpy(${c_name(node.val_var)}, ${right}, sizeof(${styp}));')
79 }
80}
81
82// A labeled continue jumps back to this gate instead of forward over later
83// declarations in the loop body, which avoids gcc -Wjump-misses-init.
84fn (mut g Gen) write_labeled_continue_gate(label string, prefix string) {
85 if label.len == 0 {
86 return
87 }
88 continue_flag := labeled_continue_flag_name(label)
89 continue_entry_label := labeled_continue_entry_label_name(label)
90 g.writeln('${prefix}bool ${continue_flag} = false;')
91 g.writeln('${prefix}${continue_entry_label}: {}')
92 g.writeln('${prefix}if (${continue_flag}) goto ${label}__continue;')
93}
94
95fn for_c_ident_name(expr ast.Expr) string {
96 return match expr {
97 ast.Ident {
98 expr.name
99 }
100 ast.ParExpr {
101 for_c_ident_name(expr.expr)
102 }
103 else {
104 ''
105 }
106 }
107}
108
109fn (mut g Gen) for_c_unsigned_overflow_guard(node ast.ForCStmt) ?ForCOverflowGuard {
110 if node.is_multi || !node.has_cond || !node.has_inc {
111 return none
112 }
113 if node.cond !is ast.InfixExpr {
114 return none
115 }
116 if node.inc !is ast.ExprStmt {
117 return none
118 }
119 cond := node.cond as ast.InfixExpr
120 inc := node.inc as ast.ExprStmt
121 if inc.expr !is ast.PostfixExpr {
122 return none
123 }
124 postfix := inc.expr as ast.PostfixExpr
125 postfix_var_name := for_c_ident_name(postfix.expr)
126 if postfix_var_name == '' {
127 return none
128 }
129 unaliased_typ := g.table.unaliased_type(g.unwrap_generic(postfix.typ))
130 if !unaliased_typ.is_unsigned() {
131 return none
132 }
133 cond_matches := match postfix.op {
134 .inc {
135 (cond.op == .le && for_c_ident_name(cond.left) == postfix_var_name)
136 || (cond.op == .ge && for_c_ident_name(cond.right) == postfix_var_name)
137 }
138 .dec {
139 (cond.op == .ge && for_c_ident_name(cond.left) == postfix_var_name)
140 || (cond.op == .le && for_c_ident_name(cond.right) == postfix_var_name)
141 }
142 else {
143 false
144 }
145 }
146
147 if !cond_matches {
148 return none
149 }
150 limit_expr := match postfix.op {
151 .inc { '(${g.styp(unaliased_typ)})-1' }
152 .dec { '(${g.styp(unaliased_typ)})0' }
153 else { return none }
154 }
155
156 return ForCOverflowGuard{
157 cname: c_name(postfix_var_name)
158 limit_expr: limit_expr
159 }
160}
161
162fn (mut g Gen) write_for_c_inc_expr(node ast.ForCStmt) {
163 mut processed := false
164 if node.inc is ast.ExprStmt && node.inc.expr is ast.ConcatExpr {
165 for inc_expr_idx, inc_expr in node.inc.expr.vals {
166 g.expr(inc_expr)
167 if inc_expr_idx < node.inc.expr.vals.len - 1 {
168 g.write(', ')
169 }
170 }
171 processed = true
172 }
173 if !processed {
174 g.stmt(node.inc)
175 }
176}
177
178fn (g &Gen) for_c_init_local_closure_vars(node ast.ForCStmt) []ast.Var {
179 if !node.has_init || node.init !is ast.AssignStmt {
180 return []
181 }
182 init := node.init as ast.AssignStmt
183 if init.op != .decl_assign {
184 return []
185 }
186 mut vars := []ast.Var{}
187 for left in init.left {
188 if left is ast.Ident {
189 mut obj := if left.obj is ast.Var {
190 left.obj
191 } else {
192 ast.Var{}
193 }
194 if node.scope != unsafe { nil } {
195 if scope_var := node.scope.find_var(left.name) {
196 if scope_var.pos.pos == left.pos.pos {
197 obj = *scope_var
198 }
199 }
200 }
201 if g.local_closure_var_has_tracked_context(obj) {
202 vars << obj
203 }
204 }
205 }
206 return vars
207}
208
209fn (mut g Gen) local_var_needs_scope_autofree(var ast.Var) bool {
210 if !g.needs_scope_cleanup() || g.is_builtin_mod || var.name == '_' || var.is_arg || var.is_tmp
211 || var.is_inherited || var.typ == 0 {
212 return false
213 }
214 base_typ := var.typ.set_nr_muls(0).clear_option_and_result()
215 if g.type_has_unresolved_generic_parts(base_typ) {
216 return false
217 }
218 sym := g.table.sym(base_typ)
219 if sym.kind in [.array, .map, .string] || sym.has_method('free') || var.is_auto_heap {
220 return true
221 }
222 return var.typ.is_ptr() && sym.name.after('.').len > 0 && sym.name.after('.')[0].is_capital()
223 && g.pref.experimental
224}
225
226fn (mut g Gen) for_c_init_autofree_vars(node ast.ForCStmt) []ast.Var {
227 if !node.has_init || node.init !is ast.AssignStmt {
228 return []
229 }
230 init := node.init as ast.AssignStmt
231 if init.op != .decl_assign {
232 return []
233 }
234 mut vars := []ast.Var{}
235 for left in init.left {
236 if left is ast.Ident {
237 mut obj := if left.obj is ast.Var {
238 left.obj
239 } else {
240 ast.Var{}
241 }
242 if node.scope != unsafe { nil } {
243 if scope_var := node.scope.find_var(left.name) {
244 if scope_var.pos.pos == left.pos.pos {
245 obj = *scope_var
246 }
247 }
248 }
249 if g.local_var_needs_scope_autofree(obj) {
250 vars << obj
251 }
252 }
253 }
254 return vars
255}
256
257fn (mut g Gen) push_for_c_init_autofree_keep_vars(vars []ast.Var) int {
258 start := g.for_c_init_autofree_keep_vars.len
259 for var in vars {
260 g.for_c_init_autofree_keep_vars << var.name
261 g.for_c_init_autofree_cleanup_vars << var
262 }
263 return start
264}
265
266fn (mut g Gen) pop_for_c_init_autofree_keep_vars(start int) {
267 if g.for_c_init_autofree_keep_vars.len > start {
268 g.for_c_init_autofree_keep_vars = g.for_c_init_autofree_keep_vars[..start].clone()
269 }
270 if g.for_c_init_autofree_cleanup_vars.len > start {
271 g.for_c_init_autofree_cleanup_vars = g.for_c_init_autofree_cleanup_vars[..start].clone()
272 }
273}
274
275fn (mut g Gen) cleanup_for_c_init_autofree_vars(vars []ast.Var) {
276 for var in vars {
277 g.autofree_variable(var)
278 }
279 for g.autofree_scope_stmts.len > 0 {
280 g.write(g.autofree_scope_stmts.pop())
281 }
282}
283
284fn (mut g Gen) cleanup_for_c_init_autofree_vars_on_return(returned_names map[string]bool, selector_owner_names map[string]bool) {
285 for var in g.for_c_init_autofree_cleanup_vars {
286 if var.name in returned_names {
287 continue
288 }
289 if var.name in selector_owner_names {
290 continue
291 }
292 g.autofree_variable(var)
293 }
294 for g.autofree_scope_stmts.len > 0 {
295 g.write(g.autofree_scope_stmts.pop())
296 }
297}
298
299fn (mut g Gen) write_loop_scope_cleanup_after_defer(scope &ast.Scope, pos token.Pos, stmts []ast.Stmt,
300 ends_with_branch bool) {
301 if ends_with_branch || scope == unsafe { nil } {
302 return
303 }
304 if g.needs_scope_cleanup() && !g.is_builtin_mod {
305 g.autofree_scope_vars2(scope, scope.start_pos, scope.end_pos, pos.line_nr, false, -1)
306 }
307 if g.fn_decl != unsafe { nil } {
308 g.cleanup_local_closure_vars2(scope, scope.start_pos, scope.end_pos, pos.line_nr, false,
309 -1, stmts)
310 }
311}
312
313fn (mut g Gen) for_c_stmt(node ast.ForCStmt) {
314 g.loop_depth++
315 init_closure_vars := g.for_c_init_local_closure_vars(node)
316 init_autofree_vars := g.for_c_init_autofree_vars(node)
317 has_init_outer_cleanup := init_closure_vars.len > 0 || init_autofree_vars.len > 0
318 if node.is_multi {
319 g.is_vlines_enabled = false
320 g.inside_for_c_stmt = true
321 if node.label.len > 0 {
322 g.writeln('${node.label}:')
323 }
324 g.writeln('{')
325 g.indent++
326 if node.has_init {
327 g.stmt(node.init)
328 if node.init is ast.ExprStmt {
329 g.write('; ')
330 }
331 }
332 g.writeln('bool _is_first = true;')
333 g.writeln('while (true) {')
334 g.writeln('\tif (_is_first) {')
335 g.writeln('\t\t_is_first = false;')
336 g.writeln('\t} else {')
337 if node.has_inc {
338 g.indent++
339 g.stmt(node.inc)
340 g.writeln(';')
341 g.indent--
342 }
343 g.writeln('}')
344 if node.has_cond {
345 g.write('if (!(')
346 g.expr(node.cond)
347 g.writeln(')) break;')
348 }
349 g.is_vlines_enabled = true
350 g.inside_for_c_stmt = false
351 g.write_labeled_continue_gate(node.label, '')
352 if node.label.len > 0 {
353 g.writeln('{')
354 }
355 autofree_keep_start := g.push_for_c_init_autofree_keep_vars(init_autofree_vars)
356 preserve_start := g.push_local_closure_cleanup_preserve_vars(init_closure_vars,
357 node.pos.pos)
358 skip_cleanup_start := g.push_skip_scope_cleanup(node.scope)
359 ends_with_branch := g.stmts_with_tmp_var(node.stmts, '')
360 g.pop_skip_scope_cleanup(skip_cleanup_start)
361 if node.label.len > 0 {
362 g.write_defer_stmts(node.scope, false, node.pos)
363 g.write_loop_scope_cleanup_after_defer(node.scope, node.pos, node.stmts,
364 ends_with_branch)
365 g.writeln('}')
366 g.writeln('${node.label}__continue: {}')
367 } else {
368 g.write_defer_stmts(node.scope, false, node.pos)
369 g.write_loop_scope_cleanup_after_defer(node.scope, node.pos, node.stmts,
370 ends_with_branch)
371 }
372 g.pop_local_closure_cleanup_preserve_vars(preserve_start)
373 g.pop_for_c_init_autofree_keep_vars(autofree_keep_start)
374 g.writeln('}')
375 if has_init_outer_cleanup && node.label.len > 0 {
376 g.writeln('${node.label}__break: {}')
377 }
378 g.cleanup_for_c_init_local_closure_vars(node, init_closure_vars)
379 g.cleanup_for_c_init_autofree_vars(init_autofree_vars)
380 g.indent--
381 g.writeln('}')
382 if !has_init_outer_cleanup && node.label.len > 0 {
383 g.writeln('${node.label}__break: {}')
384 }
385 } else {
386 overflow_guard := g.for_c_unsigned_overflow_guard(node) or { ForCOverflowGuard{} }
387 has_overflow_guard := overflow_guard.cname.len > 0
388 overflow_guard_flag := if has_overflow_guard { g.new_tmp_var() } else { '' }
389 g.is_vlines_enabled = false
390 g.inside_for_c_stmt = true
391 needs_init_closure_scope := init_closure_vars.len > 0
392 needs_init_autofree_scope := init_autofree_vars.len > 0
393 has_outer_block := has_overflow_guard || needs_init_closure_scope
394 || needs_init_autofree_scope
395 if has_outer_block {
396 g.writeln('{')
397 g.indent++
398 }
399 if needs_init_closure_scope || needs_init_autofree_scope {
400 g.stmt(node.init)
401 }
402 if has_overflow_guard {
403 g.writeln('bool ${overflow_guard_flag} = false;')
404 }
405 if node.label.len > 0 {
406 g.writeln('${node.label}:')
407 }
408 g.set_current_pos_as_last_stmt_pos()
409 g.skip_stmt_pos = true
410 g.write('for (')
411 if !node.has_init || needs_init_closure_scope || needs_init_autofree_scope {
412 g.write('; ')
413 } else {
414 g.stmt(node.init)
415 if node.init is ast.ExprStmt {
416 g.write('; ')
417 }
418 // Remove excess return and add space
419 if g.out.last_n(1) == '\n' {
420 g.go_back(1)
421 g.empty_line = false
422 g.write(' ')
423 }
424 }
425 if node.has_cond {
426 if has_overflow_guard {
427 g.write('!${overflow_guard_flag} && (')
428 }
429 g.expr(node.cond)
430 if has_overflow_guard {
431 g.write(')')
432 }
433 }
434 g.write('; ')
435 if node.has_inc {
436 if has_overflow_guard {
437 g.write('(${overflow_guard.cname} == ${overflow_guard.limit_expr} ? (${overflow_guard_flag} = true, 0) : (')
438 g.write_for_c_inc_expr(node)
439 g.write('))')
440 } else {
441 g.write_for_c_inc_expr(node)
442 }
443 }
444 g.writeln(') {')
445 g.skip_stmt_pos = false
446 g.is_vlines_enabled = true
447 g.inside_for_c_stmt = false
448 g.write_labeled_continue_gate(node.label, '')
449 if node.label.len > 0 {
450 g.writeln('{')
451 }
452 autofree_keep_start := g.push_for_c_init_autofree_keep_vars(init_autofree_vars)
453 preserve_start := g.push_local_closure_cleanup_preserve_vars(init_closure_vars,
454 node.pos.pos)
455 skip_cleanup_start := g.push_skip_scope_cleanup(node.scope)
456 ends_with_branch := g.stmts_with_tmp_var(node.stmts, '')
457 g.pop_skip_scope_cleanup(skip_cleanup_start)
458 if node.label.len > 0 {
459 g.write_defer_stmts(node.scope, false, node.pos)
460 g.write_loop_scope_cleanup_after_defer(node.scope, node.pos, node.stmts,
461 ends_with_branch)
462 g.writeln('}')
463 g.writeln('${node.label}__continue: {}')
464 } else {
465 g.write_defer_stmts(node.scope, false, node.pos)
466 g.write_loop_scope_cleanup_after_defer(node.scope, node.pos, node.stmts,
467 ends_with_branch)
468 }
469 g.pop_local_closure_cleanup_preserve_vars(preserve_start)
470 g.pop_for_c_init_autofree_keep_vars(autofree_keep_start)
471 g.writeln('}')
472 if has_init_outer_cleanup && node.label.len > 0 {
473 g.writeln('${node.label}__break: {}')
474 }
475 g.cleanup_for_c_init_local_closure_vars(node, init_closure_vars)
476 g.cleanup_for_c_init_autofree_vars(init_autofree_vars)
477 if has_outer_block {
478 g.indent--
479 g.writeln('}')
480 }
481 if !has_init_outer_cleanup && node.label.len > 0 {
482 g.writeln('${node.label}__break: {}')
483 }
484 }
485 g.loop_depth--
486}
487
488fn (mut g Gen) for_stmt(node ast.ForStmt) {
489 g.loop_depth++
490 g.is_vlines_enabled = false
491 if node.label.len > 0 {
492 g.writeln('${node.label}:')
493 }
494 g.writeln('for (;;) {')
495 if !node.is_inf {
496 g.indent++
497 g.set_current_pos_as_last_stmt_pos()
498 g.write('if (!(')
499 g.expr(node.cond)
500 g.writeln(')) break;')
501 g.indent--
502 }
503 g.is_vlines_enabled = true
504 g.write_labeled_continue_gate(node.label, '\t')
505 if node.label.len > 0 {
506 g.writeln('\t{')
507 }
508 skip_cleanup_start := g.push_skip_scope_cleanup(node.scope)
509 ends_with_branch := g.stmts_with_tmp_var(node.stmts, '')
510 g.pop_skip_scope_cleanup(skip_cleanup_start)
511 if node.label.len > 0 {
512 g.write_defer_stmts(node.scope, false, node.pos)
513 g.write_loop_scope_cleanup_after_defer(node.scope, node.pos, node.stmts, ends_with_branch)
514 g.writeln('\t}')
515 g.writeln('\t${node.label}__continue: {}')
516 } else {
517 g.write_defer_stmts(node.scope, false, node.pos)
518 g.write_loop_scope_cleanup_after_defer(node.scope, node.pos, node.stmts, ends_with_branch)
519 }
520 g.writeln('}')
521 if node.label.len > 0 {
522 g.writeln('${node.label}__break: {}')
523 }
524 g.loop_depth--
525}
526
527fn (mut g Gen) for_in_stmt(node_ ast.ForInStmt) {
528 mut node := node_
529 mut is_comptime := false
530 mut param_key_type := ast.Type(0)
531 mut param_val_type := ast.Type(0)
532 mut scope_cond_type := ast.Type(0)
533 mut resolved_cond_expr := node.cond
534 if node.cond is ast.Ident {
535 mut cond_ident := node.cond as ast.Ident
536 if node.scope != unsafe { nil } {
537 cond_ident.scope = node.scope
538 } else if g.file.scope != unsafe { nil } {
539 cond_ident.scope = g.file.scope.innermost(node.pos.pos)
540 }
541 if cond_ident.scope != unsafe { nil } {
542 if scope_var := cond_ident.scope.find_var(cond_ident.name) {
543 cond_ident.obj = *scope_var
544 }
545 }
546 resolved_cond_expr = cond_ident
547 param_cond_type := g.resolve_current_fn_generic_param_type(cond_ident.name)
548 scope_cond_type = g.resolved_scope_var_type(cond_ident)
549 // Don't let an aggregate/sumtype scope type override a more specific
550 // cond_type (e.g., a concrete array type from the aggregate handler).
551 if scope_cond_type != 0 && node.cond_type != 0 && node.cond_type != scope_cond_type {
552 scope_sym := g.table.final_sym(scope_cond_type)
553 if scope_sym.kind == .aggregate || scope_sym.kind == .sum_type {
554 scope_cond_type = 0
555 }
556 }
557 if scope_cond_type != 0 {
558 node.cond_type = scope_cond_type
559 } else if param_cond_type != 0 {
560 node.cond_type = param_cond_type
561 }
562 param_key_type = g.resolve_current_fn_generic_param_key_type(cond_ident.name)
563 param_val_type = g.resolve_current_fn_generic_param_value_type(cond_ident.name)
564 }
565 resolved_cond_type := g.resolved_expr_type(resolved_cond_expr, node.cond_type)
566 if resolved_cond_type != 0 {
567 // Don't let an aggregate/sumtype resolved type override a more specific
568 // cond_type (e.g., a concrete array type from the aggregate handler).
569 resolved_sym := g.table.final_sym(resolved_cond_type)
570 if !(resolved_sym.kind in [.aggregate, .sum_type] && node.cond_type != 0
571 && node.cond_type != resolved_cond_type
572 && g.table.final_sym(node.cond_type).kind !in [.aggregate, .sum_type]) {
573 node.cond_type = resolved_cond_type
574 }
575 }
576 if scope_cond_type != 0 {
577 node.cond_type = scope_cond_type
578 }
579 node.cond_type = g.recheck_concrete_type(node.cond_type)
580 if scope_cond_type != 0 {
581 node.cond_type = scope_cond_type
582 }
583 if node.cond_type != 0 {
584 resolved_cond_sym := g.table.final_sym(g.unwrap_generic(node.cond_type))
585 if resolved_cond_sym.kind in [.array, .array_fixed, .map, .string, .aggregate, .alias] {
586 node.kind = resolved_cond_sym.kind
587 }
588 if node.kind in [.array, .array_fixed, .map, .string] {
589 unwrapped_cond_type := g.unwrap_generic(g.recheck_concrete_type(node.cond_type))
590 if node.key_var.len > 0 {
591 node.key_type = if param_key_type != 0 {
592 param_key_type
593 } else {
594 match resolved_cond_sym.kind {
595 .map { resolved_cond_sym.map_info().key_type }
596 else { ast.int_type }
597 }
598 }
599 node.scope.update_var_type(node.key_var, node.key_type)
600 }
601 base_val_type := if scope_cond_type != 0 {
602 g.recheck_concrete_type(g.table.value_type(g.unwrap_generic(scope_cond_type)))
603 } else if param_val_type != 0 {
604 param_val_type
605 } else {
606 g.recheck_concrete_type(g.table.value_type(unwrapped_cond_type))
607 }
608 node.val_type = for_in_val_type(base_val_type, node.val_is_mut, node.val_is_ref)
609 node.scope.update_var_type(node.val_var, node.val_type)
610 }
611 }
612
613 if (node.cond is ast.Ident && node.cond.ct_expr) || node.cond is ast.ComptimeSelector {
614 mut unwrapped_typ := g.unwrap_generic(g.recheck_concrete_type(node.cond_type))
615 ctyp := g.type_resolver.get_type(node.cond)
616 if ctyp != ast.void_type {
617 unwrapped_typ = g.unwrap_generic(g.recheck_concrete_type(ctyp))
618 is_comptime = true
619 }
620
621 mut unwrapped_sym := g.table.sym(unwrapped_typ)
622
623 node.cond_type = unwrapped_typ
624 base_val_type := g.recheck_concrete_type(g.table.value_type(unwrapped_typ))
625 node.val_type = for_in_val_type(base_val_type, node.val_is_mut, node.val_is_ref)
626 node.scope.update_var_type(node.val_var, node.val_type)
627 node.kind = unwrapped_sym.kind
628
629 if is_comptime {
630 g.type_resolver.update_ct_type(node.val_var, node.val_type)
631 node.scope.update_ct_var_kind(node.val_var, .value_var)
632
633 defer(fn) {
634 g.type_resolver.type_map.delete(node.val_var)
635 }
636 }
637
638 if node.key_var.len > 0 {
639 key_type := if param_key_type != 0 {
640 param_key_type
641 } else {
642 match unwrapped_sym.kind {
643 .map { unwrapped_sym.map_info().key_type }
644 else { ast.int_type }
645 }
646 }
647 node.key_type = key_type
648 node.scope.update_var_type(node.key_var, key_type)
649
650 if is_comptime {
651 g.type_resolver.update_ct_type(node.key_var, node.key_type)
652 node.scope.update_ct_var_kind(node.key_var, .key_var)
653
654 defer(fn) {
655 g.type_resolver.type_map.delete(node.key_var)
656 }
657 }
658 }
659 }
660
661 if node.kind == .any && !is_comptime {
662 mut unwrapped_typ := if scope_cond_type != 0 {
663 g.unwrap_generic(scope_cond_type)
664 } else {
665 g.unwrap_generic(g.recheck_concrete_type(node.cond_type))
666 }
667 mut unwrapped_sym := g.table.sym(unwrapped_typ)
668 node.kind = unwrapped_sym.kind
669 node.cond_type = unwrapped_typ
670 if node.key_var.len > 0 {
671 key_type := if param_key_type != 0 {
672 param_key_type
673 } else {
674 match unwrapped_sym.kind {
675 .map { unwrapped_sym.map_info().key_type }
676 else { ast.int_type }
677 }
678 }
679 node.key_type = key_type
680 node.scope.update_var_type(node.key_var, key_type)
681 }
682 base_val_type := g.recheck_concrete_type(g.table.value_type(unwrapped_typ))
683 node.val_type = for_in_val_type(base_val_type, node.val_is_mut, node.val_is_ref)
684 node.scope.update_var_type(node.val_var, node.val_type)
685 } else if node.kind == .alias {
686 mut unwrapped_typ := if scope_cond_type != 0 {
687 g.unwrap_generic(scope_cond_type)
688 } else {
689 g.unwrap_generic(g.recheck_concrete_type(node.cond_type))
690 }
691 mut unwrapped_sym := g.table.final_sym(unwrapped_typ)
692 node.kind = unwrapped_sym.kind
693 node.cond_type = unwrapped_typ
694 if node.key_var.len > 0 {
695 key_type := if param_key_type != 0 {
696 param_key_type
697 } else {
698 match unwrapped_sym.kind {
699 .map { unwrapped_sym.map_info().key_type }
700 else { ast.int_type }
701 }
702 }
703 node.key_type = key_type
704 node.scope.update_var_type(node.key_var, key_type)
705 }
706 base_val_type :=
707 g.recheck_concrete_type(g.table.value_type(g.table.unaliased_type(unwrapped_typ)))
708 node.val_type = for_in_val_type(base_val_type, node.val_is_mut, node.val_is_ref)
709 node.scope.update_var_type(node.val_var, node.val_type)
710 }
711 g.loop_depth++
712 mut array_debug_value_scope_opened := false
713 if node.label.len > 0 {
714 g.writeln('\t${node.label}: {}')
715 }
716 if node.is_range {
717 // `for x in 1..10 {`
718 i := if node.val_var == '_' { g.new_tmp_var() } else { c_name(node.val_var) }
719 plus_plus_i := if g.do_int_overflow_checks {
720 $if new_int ? && x64 {
721 '${i}=builtin__overflow__add_i64(${i},1)'
722 } $else {
723 '${i}=builtin__overflow__add_i32(${i},1)'
724 }
725 } else {
726 '++${i}'
727 }
728 val_typ := ast.mktyp(node.val_type)
729 g.write('for (${g.styp(val_typ)} ${i} = ')
730 g.expr(node.cond)
731 g.write('; ${i} < ')
732 g.expr(node.high)
733 g.writeln('; ${plus_plus_i}) {')
734 } else if node.kind == .array {
735 // `for num in nums {`
736 // g.writeln('// FOR IN array')
737 if node.cond_type != 0 {
738 // Use scope_cond_type only if it's a concrete container type.
739 // Skip if it's an aggregate/sumtype (e.g., from a match arm
740 // smartcast) as value_type would return void for those.
741 use_scope_cond := scope_cond_type != 0
742 && g.table.final_sym(scope_cond_type).kind !in [.aggregate, .sum_type]
743 resolved_val_type := if use_scope_cond {
744 g.recheck_concrete_type(g.table.value_type(g.unwrap_generic(scope_cond_type)))
745 } else if param_val_type != 0 {
746 param_val_type
747 } else {
748 g.recheck_concrete_type(g.table.value_type(g.unwrap_generic(g.recheck_concrete_type(node.cond_type))))
749 }
750 if resolved_val_type != 0 {
751 node.val_type = for_in_val_type(resolved_val_type, node.val_is_mut, node.val_is_ref)
752 node.scope.update_var_type(node.val_var, node.val_type)
753 }
754 }
755 $if trace_ci_fixes ? {
756 if g.cur_fn != unsafe { nil } && g.cur_fn.name in ['arrays.flatten', 'arrays.group_by'] {
757 trace_scope_cond_type := if node.cond is ast.Ident {
758 g.resolved_scope_var_type(node.cond as ast.Ident)
759 } else {
760 ast.no_type
761 }
762 eprintln('cgen for ${g.cur_fn.name} val=${node.val_var} val_type=${g.table.type_to_str(node.val_type)} cond_type=${g.table.type_to_str(node.cond_type)} scope_cond=${if trace_scope_cond_type != 0 {
763 g.table.type_to_str(trace_scope_cond_type)
764 } else {
765 '<none>'
766 }} cur=${g.cur_concrete_types.map(g.table.type_to_str(it))}')
767 }
768 }
769 mut styp := g.styp(node.val_type)
770 mut val_sym := g.table.sym(node.val_type)
771 op_field := if node.cond_type.has_flag(.shared_f) {
772 '->val.'
773 } else if node.cond_type.is_ptr() || resolved_cond_expr.is_auto_deref_var() {
774 '->'
775 } else {
776 g.dot_or_ptr(node.cond_type)
777 }
778
779 mut cond_var := ''
780 // Check if the cond has an or-block that unwraps the option
781 cond_has_or_block := (node.cond is ast.SelectorExpr && node.cond.or_block.kind != .absent)
782 || (node.cond is ast.CallExpr && node.cond.or_block.kind != .absent)
783 || (node.cond is ast.IndexExpr && node.cond.or_expr.kind != .absent)
784 if cond_has_or_block {
785 node.cond_type = node.cond_type.clear_flag(.option)
786 }
787 cond_is_option := node.cond_type.has_flag(.option)
788 if (node.cond is ast.Ident && !cond_is_option)
789 || (node.cond is ast.SelectorExpr && node.cond.or_block.kind == .absent) {
790 cond_var = g.expr_string(node.cond)
791 } else {
792 cond_var = g.new_tmp_var()
793 g.write2(g.styp(node.cond_type), ' ${cond_var} = ')
794 old_inside_opt_or_res := g.inside_opt_or_res
795 if cond_is_option {
796 g.inside_opt_or_res = true
797 }
798 g.expr(node.cond)
799 g.inside_opt_or_res = old_inside_opt_or_res
800 g.writeln(';')
801 }
802 i := if node.key_var in ['', '_'] { g.new_tmp_var() } else { node.key_var }
803 plus_plus_i := if g.do_int_overflow_checks {
804 $if new_int ? && x64 {
805 '${i}=builtin__overflow__add_i64(${i},1)'
806 } $else {
807 '${i}=builtin__overflow__add_i32(${i},1)'
808 }
809 } else {
810 '++${i}'
811 }
812 g.empty_line = true
813 opt_expr := '(*(${g.styp(node.cond_type.clear_flag(.option))}*)${cond_var}${op_field}data)'
814 cond_expr := if cond_is_option {
815 '${opt_expr}${op_field}len'
816 } else {
817 '${cond_var}${op_field}len'
818 }
819 if g.pref.is_debug && node.val_var != '_' {
820 // Keep the user-visible loop variable alive for the full loop scope so
821 // debuggers observe the current iteration value instead of the previous
822 // one.
823 g.writeln('{')
824 g.indent++
825 array_debug_value_scope_opened = true
826 g.write_for_in_array_value_decl(node, styp, val_sym)
827 }
828 g.writeln('for (${ast.int_type_name} ${i} = 0; ${i} < ${cond_expr}; ${plus_plus_i}) {')
829 if node.val_var != '_' {
830 if array_debug_value_scope_opened {
831 g.write_for_in_array_value_assign(node, styp, val_sym, cond_var, op_field, i,
832 cond_is_option, opt_expr)
833 } else if mut val_sym.info is ast.FnType {
834 g.write('\t')
835 tcc_bug := c_name(node.val_var)
836 g.write_fn_ptr_decl(&val_sym.info, tcc_bug)
837 g.writeln(' = ((voidptr*)${cond_var}${op_field}data)[${i}];')
838 } else if !node.val_type.has_flag(.option) && val_sym.kind == .array_fixed
839 && !node.val_is_mut {
840 right := '((${styp}*)${cond_var}${op_field}data)[${i}]'
841 g.writeln('\t${styp} ${c_name(node.val_var)};')
842 g.writeln('\tmemcpy(*(${styp}*)${c_name(node.val_var)}, (byte*)${right}, sizeof(${styp}));')
843 } else {
844 needs_memcpy := !node.val_type.is_ptr() && !node.val_type.has_flag(.option)
845 && g.table.final_sym(node.val_type).kind == .array_fixed
846 right := if cond_is_option {
847 '((${styp}*)${opt_expr}${op_field}data)[${i}]'
848 } else if node.val_is_mut || node.val_is_ref {
849 if g.table.value_type(node.cond_type).is_ptr() {
850 '((${styp}*)${cond_var}${op_field}data)[${i}]'
851 } else {
852 '((${styp})${cond_var}${op_field}data) + ${i}'
853 }
854 } else if val_sym.kind == .array_fixed {
855 '((${styp}*)${cond_var}${op_field}data)[${i}]'
856 } else {
857 '((${styp}*)${cond_var}${op_field}data)[${i}]'
858 }
859 if !needs_memcpy {
860 g.writeln('\t${styp} ${c_name(node.val_var)} = ${right};')
861 } else {
862 g.writeln('\t${styp} ${c_name(node.val_var)} = {0};')
863 g.writeln('\tmemcpy(${c_name(node.val_var)}, ${right}, sizeof(${styp}));')
864 }
865 }
866 }
867 } else if node.kind == .array_fixed {
868 mut cond_var := ''
869 cond_type_is_ptr := node.cond_type.is_ptr()
870 cond_is_literal := node.cond is ast.ArrayInit
871 if cond_is_literal {
872 cond_var = g.new_tmp_var()
873 g.write2(g.styp(node.cond_type), ' ${cond_var} = ')
874 g.expr(node.cond)
875 g.writeln(';')
876 } else if cond_type_is_ptr {
877 cond_var = g.new_tmp_var()
878 cond_var_type := g.styp(node.cond_type).trim('*')
879 if !node.cond.is_lvalue() {
880 g.write('${cond_var_type} *${cond_var} = ((${cond_var_type})')
881 } else {
882 g.write('${cond_var_type} *${cond_var} = (')
883 }
884 g.expr(node.cond)
885 g.writeln(');')
886 } else {
887 cond_var = g.expr_string(node.cond)
888 }
889 idx := if node.key_var in ['', '_'] { g.new_tmp_var() } else { node.key_var }
890 plus_plus_idx := if g.do_int_overflow_checks {
891 $if new_int ? && x64 {
892 '${idx}=builtin__overflow__add_i64(${idx},1)'
893 } $else {
894 '${idx}=builtin__overflow__add_i32(${idx},1)'
895 }
896 } else {
897 '++${idx}'
898 }
899 cond_sym := g.table.final_sym(node.cond_type)
900 info := cond_sym.info as ast.ArrayFixed
901 g.writeln('for (${ast.int_type_name} ${idx} = 0; ${idx} != ${info.size}; ${plus_plus_idx}) {')
902 if node.val_var != '_' {
903 val_sym := g.table.sym(node.val_type)
904 is_fixed_array := val_sym.kind == .array_fixed && !node.val_is_mut
905 && !node.val_type.has_flag(.option)
906 if val_sym.info is ast.FnType {
907 g.write('\t')
908 tcc_bug := c_name(node.val_var)
909 g.write_fn_ptr_decl(&val_sym.info, tcc_bug)
910 } else if is_fixed_array {
911 styp := g.styp(node.val_type)
912 g.writeln('\t${styp} ${c_name(node.val_var)};')
913 g.writeln('\tmemcpy(*(${styp}*)${c_name(node.val_var)}, (byte*)${cond_var}[${idx}], sizeof(${styp}));')
914 } else {
915 styp := g.styp(node.val_type)
916 g.write('\t${styp} ${c_name(node.val_var)}')
917 }
918 if !is_fixed_array {
919 addr := if node.val_is_mut { '&' } else { '' }
920 if cond_type_is_ptr {
921 g.writeln(' = ${addr}(*${cond_var})[${idx}];')
922 } else if cond_is_literal {
923 g.writeln(' = ${addr}${cond_var}[${idx}];')
924 } else {
925 g.write(' = ${addr}')
926 g.expr(node.cond)
927 if info.is_fn_ret {
928 g.write('.ret_arr')
929 }
930 g.writeln('[${idx}];')
931 }
932 }
933 }
934 } else if node.kind == .map {
935 // `for key, val in map {
936 // g.writeln('// FOR IN map')
937 mut cond_var := ''
938 if node.cond is ast.Ident {
939 cond_var = g.expr_string(node.cond)
940 } else {
941 cond_var = g.new_tmp_var()
942 g.write2(g.styp(node.cond_type), ' ${cond_var} = ')
943 g.expr(node.cond)
944 g.writeln(';')
945 }
946 dot_or_ptr := if node.cond_type.has_flag(.shared_f) {
947 '->val.'
948 } else if node.cond_type.is_ptr() || resolved_cond_expr.is_auto_deref_var() {
949 '->'
950 } else {
951 g.dot_or_ptr(node.cond_type)
952 }
953 idx := g.new_tmp_var()
954 plus_plus_idx := if g.do_int_overflow_checks {
955 $if new_int ? && x64 {
956 '${idx}=builtin__overflow__add_i64(${idx},1)'
957 } $else {
958 '${idx}=builtin__overflow__add_i32(${idx},1)'
959 }
960 } else {
961 '++${idx}'
962 }
963 map_len := g.new_tmp_var()
964 g.empty_line = true
965 g.writeln('${ast.int_type_name} ${map_len} = ${cond_var}${dot_or_ptr}key_values.len;')
966 g.writeln('for (${ast.int_type_name} ${idx} = 0; ${idx} < ${map_len}; ${plus_plus_idx} ) {')
967 // TODO: don't have this check when the map has no deleted elements
968 g.indent++
969 diff := g.new_tmp_var()
970 g.writeln('${ast.int_type_name} ${diff} = ${cond_var}${dot_or_ptr}key_values.len - ${map_len};')
971 g.writeln('${map_len} = ${cond_var}${dot_or_ptr}key_values.len;')
972 // TODO: optimize this
973 g.writeln('if (${diff} < 0) {')
974 g.writeln('\t${idx} = -1;')
975 g.writeln('\tcontinue;')
976 g.writeln('}')
977 g.writeln('if (!builtin__DenseArray_has_index(&${cond_var}${dot_or_ptr}key_values, ${idx})) {continue;}')
978 if node.cond is ast.Ident {
979 cond_ident := node.cond as ast.Ident
980 resolved_key_type := g.resolve_current_fn_generic_param_key_type(cond_ident.name)
981 if resolved_key_type != 0 {
982 node.key_type = resolved_key_type
983 if node.key_var.len > 0 {
984 node.scope.update_var_type(node.key_var, node.key_type)
985 }
986 }
987 resolved_val_type := g.resolve_current_fn_generic_param_value_type(cond_ident.name)
988 if resolved_val_type != 0 {
989 node.val_type = for_in_val_type(resolved_val_type, node.val_is_mut, node.val_is_ref)
990 if node.val_var.len > 0 {
991 node.scope.update_var_type(node.val_var, node.val_type)
992 }
993 }
994 }
995 if node.key_var != '_' {
996 key_styp := g.styp(node.key_type)
997 key := c_name(node.key_var)
998 if g.table.final_sym(node.key_type).kind == .array_fixed {
999 g.writeln('${key_styp} ${key};')
1000 g.writeln('memcpy(${key}, builtin__DenseArray_key(&${cond_var}${dot_or_ptr}key_values, ${idx}), sizeof(${key_styp}));')
1001 } else {
1002 g.writeln('${key_styp} ${key} = *(${key_styp}*)builtin__DenseArray_key(&${cond_var}${dot_or_ptr}key_values, ${idx});')
1003 }
1004 // TODO: analyze whether node.key_type has a .clone() method and call .clone() for all types:
1005 if node.key_type == ast.string_type {
1006 g.writeln('${key} = builtin__string_clone(${key});')
1007 }
1008 }
1009 if node.val_var != '_' {
1010 val_sym := g.table.sym(node.val_type)
1011 if val_sym.info is ast.FnType {
1012 tcc_bug := c_name(node.val_var)
1013 g.write_fn_ptr_decl(&val_sym.info, tcc_bug)
1014 g.write(' = (*(voidptr*)')
1015 g.writeln('builtin__DenseArray_value(&${cond_var}${dot_or_ptr}key_values, ${idx}));')
1016 } else if val_sym.kind == .array_fixed && !node.val_is_mut {
1017 val_styp := g.styp(node.val_type)
1018 g.writeln('${val_styp} ${c_name(node.val_var)};')
1019 g.writeln('memcpy(*(${val_styp}*)${c_name(node.val_var)}, (byte*)builtin__DenseArray_value(&${cond_var}${dot_or_ptr}key_values, ${idx}), sizeof(${val_styp}));')
1020 } else {
1021 val_styp := g.styp(node.val_type)
1022 if node.val_is_mut || node.val_is_ref {
1023 if g.table.value_type(node.cond_type).is_ptr() {
1024 g.write('${val_styp} ${c_name(node.val_var)} = (*(${val_styp}*)')
1025 } else {
1026 g.write('${val_styp} ${c_name(node.val_var)} = ((${val_styp})')
1027 }
1028 } else {
1029 g.write('${val_styp} ${c_name(node.val_var)} = (*(${val_styp}*)')
1030 }
1031 g.writeln('builtin__DenseArray_value(&${cond_var}${dot_or_ptr}key_values, ${idx}));')
1032 }
1033 }
1034 g.indent--
1035 } else if node.kind == .string {
1036 cond := if node.cond in [ast.StringLiteral, ast.StringInterLiteral] {
1037 ast.Expr(g.new_ctemp_var_then_gen(node.cond, ast.string_type))
1038 } else {
1039 node.cond
1040 }
1041 field_accessor := if node.cond_type.is_ptr() { '->' } else { '.' }
1042 i := if node.key_var in ['', '_'] { g.new_tmp_var() } else { node.key_var }
1043 plus_plus_i := if g.do_int_overflow_checks {
1044 $if new_int ? && x64 {
1045 '${i}=builtin__overflow__add_i64(${i},1)'
1046 } $else {
1047 '${i}=builtin__overflow__add_i32(${i},1)'
1048 }
1049 } else {
1050 '++${i}'
1051 }
1052 g.write('for (${ast.int_type_name} ${i} = 0; ${i} < ')
1053 g.expr(cond)
1054 g.writeln('${field_accessor}len; ${plus_plus_i}) {')
1055 if node.val_var != '_' {
1056 g.write('\tu8 ${c_name(node.val_var)} = ')
1057 g.expr(cond)
1058 g.writeln('${field_accessor}str[${i}];')
1059 }
1060 } else if node.kind in [.struct, .interface] {
1061 // In generic functions, `node.cond_type` may have been overwritten by the checker
1062 // for the last concrete specialization. Re-resolve from the function parameter's
1063 // declared type which still has the generic flag.
1064 mut unwrapped_cond_type := g.unwrap_generic(node.cond_type)
1065 if g.cur_concrete_types.len > 0 && g.cur_fn != unsafe { nil } && node.cond is ast.Ident {
1066 for param in g.cur_fn.params {
1067 if param.name == (node.cond as ast.Ident).name {
1068 resolved := g.unwrap_generic(param.typ)
1069 if resolved != unwrapped_cond_type {
1070 unwrapped_cond_type = resolved
1071 }
1072 break
1073 }
1074 }
1075 }
1076 cond_type_sym := g.table.sym(unwrapped_cond_type)
1077 mut next_fn := ast.Fn{}
1078 // use alias `next` method if exists else use parent type `next` method
1079 if cond_type_sym.kind == .alias {
1080 next_fn = cond_type_sym.find_method_with_generic_parent('next') or {
1081 g.table.final_sym(unwrapped_cond_type).find_method_with_generic_parent('next') or {
1082 verror('`next` method not found')
1083 return
1084 }
1085 }
1086 } else {
1087 next_fn = cond_type_sym.find_method_with_generic_parent('next') or {
1088 verror('`next` method not found')
1089 return
1090 }
1091 }
1092 ret_typ := g.unwrap_generic(next_fn.return_type)
1093 t_expr := g.new_tmp_var()
1094 g.write('${g.styp(unwrapped_cond_type)} ${t_expr} = ')
1095 g.expr(node.cond)
1096 g.writeln(';')
1097 i := node.key_var
1098 plus_plus_i := if g.do_int_overflow_checks {
1099 $if new_int ? && x64 {
1100 '${i}=builtin__overflow__add_i64(${i},1)'
1101 } $else {
1102 '${i}=builtin__overflow__add_i32(${i},1)'
1103 }
1104 } else {
1105 '++${i}'
1106 }
1107 if i in ['', '_'] {
1108 g.writeln('while (1) {')
1109 } else {
1110 g.writeln('for (size_t ${i} = 0;; ${plus_plus_i}) {')
1111 }
1112 t_var := g.new_tmp_var()
1113 receiver_typ := g.unwrap_generic(next_fn.params[0].typ)
1114 receiver_styp := g.cc_type(receiver_typ, false)
1115 mut fn_name := receiver_styp.replace_each(['*', '', '.', '__']) + '_next'
1116 receiver_sym := g.table.sym(receiver_typ)
1117 if receiver_sym.is_builtin() {
1118 fn_name = 'builtin__${fn_name}'
1119 } else if receiver_sym.info is ast.Interface {
1120 left_cc_type := g.cc_type(g.table.unaliased_type(unwrapped_cond_type), false)
1121 left_type_name := util.no_dots(left_cc_type)
1122 fn_name = '${c_name(left_type_name)}_name_table[${t_expr}._typ]._method_next'
1123 } else {
1124 fn_name = g.specialized_method_name_from_receiver(next_fn, unwrapped_cond_type, fn_name)
1125 }
1126 g.write('\t${g.styp(ret_typ)} ${t_var} = ${fn_name}(')
1127 if !node.cond_type.is_ptr() && receiver_typ.is_ptr() {
1128 g.write('&')
1129 }
1130 if node.kind == .interface {
1131 g.writeln('${t_expr}._object);')
1132 } else {
1133 g.writeln('${t_expr});')
1134 }
1135 g.writeln('\tif (${t_var}.state != 0) break;')
1136 val := if node.val_var in ['', '_'] { g.new_tmp_var() } else { node.val_var }
1137 val_styp := g.styp(ret_typ.clear_option_and_result())
1138 ret_sym := g.table.final_sym(ret_typ)
1139 if node.val_is_mut {
1140 if ret_typ.is_any_kind_of_pointer() {
1141 g.writeln('\t${val_styp} ${val} = *(${val_styp}*)${t_var}.data;')
1142 } else {
1143 g.writeln('\t${val_styp}* ${val} = (${val_styp}*)${t_var}.data;')
1144 }
1145 } else {
1146 ret_is_fixed_array := ret_sym.is_array_fixed()
1147 if ret_is_fixed_array {
1148 g.writeln('\t${val_styp} ${val} = {0};')
1149 g.write('\tmemcpy(${val}, ${t_var}.data, sizeof(${val_styp}));')
1150 } else {
1151 if ret_sym.info is ast.FnType {
1152 g.write_fntype_decl(val, ret_sym.info, 0)
1153 g.writeln(' = **(${val_styp}**)&${t_var}.data;')
1154 } else {
1155 g.writeln('\t${val_styp} ${val} = *(${val_styp}*)${t_var}.data;')
1156 }
1157 }
1158 }
1159 } else if node.kind == .aggregate {
1160 for_type := (g.table.sym(node.cond_type).info as ast.Aggregate).types[g.aggregate_type_idx]
1161 val_type := g.table.value_type(for_type)
1162 node.scope.update_var_type(node.val_var, val_type)
1163
1164 g.for_in_stmt(ast.ForInStmt{
1165 cond: node.cond
1166 cond_type: for_type
1167 kind: g.table.sym(for_type).kind
1168 stmts: node.stmts
1169 val_type: val_type
1170 val_var: node.val_var
1171 val_is_mut: node.val_is_mut
1172 val_is_ref: node.val_is_ref
1173 })
1174
1175 g.loop_depth--
1176 return
1177 } else {
1178 typ_str := g.table.type_to_str(node.cond_type)
1179 g.error('for in: unhandled symbol `${node.cond}` of type `${typ_str}`', node.pos)
1180 }
1181 g.write_labeled_continue_gate(node.label, '\t')
1182 if node.label.len > 0 {
1183 g.writeln('\t{')
1184 }
1185 skip_cleanup_start := g.push_skip_scope_cleanup(node.scope)
1186 ends_with_branch := g.stmts_with_tmp_var(node.stmts, '')
1187 g.pop_skip_scope_cleanup(skip_cleanup_start)
1188
1189 if node.kind == .map {
1190 // diff := g.new_tmp_var()
1191 // g.writeln('${ast.int_type_name} ${diff} = ${cond_var}${arw_or_pt}key_values.len - ${map_len};')
1192 // g.writeln('if (${diff} < 0) {')
1193 // g.writeln('\t${idx} = -1;')
1194 // g.writeln('\t${map_len} = ${cond_var}${arw_or_pt}key_values.len;')
1195 // g.writeln('}')
1196 }
1197 if node.label.len > 0 {
1198 g.write_defer_stmts(node.scope, false, node.pos)
1199 g.write_loop_scope_cleanup_after_defer(node.scope, node.pos, node.stmts, ends_with_branch)
1200 g.writeln('\t}')
1201 g.writeln('\t${node.label}__continue: {}')
1202 } else {
1203 g.write_defer_stmts(node.scope, false, node.pos)
1204 g.write_loop_scope_cleanup_after_defer(node.scope, node.pos, node.stmts, ends_with_branch)
1205 }
1206 g.writeln('}')
1207 if array_debug_value_scope_opened {
1208 g.indent--
1209 g.writeln('}')
1210 }
1211 if node.label.len > 0 {
1212 g.writeln('\t${node.label}__break: {}')
1213 }
1214 g.loop_depth--
1215}
1216