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