v2 / vlib / v / gen / c / if.v
771 lines · 760 sloc · 23.86 KB · 45545c2fda3dfafa31fb7341b31b786ad143e67d
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
7
8fn (mut g Gen) if_guard_var_needs_gc_pin(scope &ast.Scope, name string) bool {
9 if g.pref.gc_mode !in [.boehm_full, .boehm_incr, .boehm_full_opt, .boehm_incr_opt] {
10 return false
11 }
12 if name == '_' {
13 return false
14 }
15 if v := scope.find_var(name) {
16 return v.is_auto_heap || v.typ.is_any_kind_of_pointer() || g.contains_ptr(v.typ)
17 }
18 return false
19}
20
21fn (g &Gen) if_guard_else_uses_err(node ast.IfExpr, branch_idx int) bool {
22 if !node.has_else || branch_idx != node.branches.len - 2 {
23 return false
24 }
25 else_branch := node.branches[branch_idx + 1]
26 if err_var := else_branch.scope.find_var('err') {
27 return err_var.is_used
28 }
29 return false
30}
31
32fn (mut g Gen) write_if_guard_gc_pin(scope &ast.Scope, name string, cvar_name string) {
33 if g.inside_veb_tmpl {
34 return
35 }
36 if g.if_guard_var_needs_gc_pin(scope, name) {
37 g.writeln('\tGC_reachable_here(&${cvar_name});')
38 }
39}
40
41fn (mut g Gen) if_guard_error_cleanup(cvar_name string, expr_type ast.Type) {
42 if expr_type.has_flag(.option) {
43 g.writeln('\tif (${cvar_name}.state == 2 && ${cvar_name}.err._object != _const_none__._object) { builtin___v_free(${cvar_name}.err._object); }')
44 } else if expr_type.has_flag(.result) {
45 g.writeln('\tif (${cvar_name}.is_error && ${cvar_name}.err._object != _const_none__._object) { builtin___v_free(${cvar_name}.err._object); }')
46 }
47}
48
49fn (mut g Gen) need_tmp_var_in_if(node ast.IfExpr) bool {
50 if node.is_expr && (g.inside_ternary == 0 || g.is_assign_lhs) {
51 if g.is_autofree || node.typ.has_option_or_result() || node.is_comptime || g.is_assign_lhs {
52 return true
53 }
54 for branch in node.branches {
55 if branch.stmts.len > 1 {
56 return true
57 }
58 if g.need_tmp_var_in_expr(branch.cond) {
59 return true
60 }
61 if branch.stmts.len == 1 {
62 if branch.stmts[0] is ast.ExprStmt {
63 stmt := branch.stmts[0] as ast.ExprStmt
64 if stmt.expr is ast.ArrayInit && stmt.expr.is_fixed {
65 return true
66 }
67 if g.need_tmp_var_in_expr(stmt.expr) {
68 return true
69 }
70 } else if branch.stmts[0] is ast.Return {
71 return true
72 } else if branch.stmts[0] is ast.BranchStmt {
73 return true
74 }
75 }
76 }
77 }
78 return false
79}
80
81fn (mut g Gen) need_tmp_var_in_expr(expr ast.Expr) bool {
82 if is_noreturn_callexpr(expr) {
83 return true
84 }
85 match expr {
86 ast.ArrayInit {
87 elem_type := g.unwrap_generic(expr.elem_type)
88 elem_kind := if elem_type != 0 {
89 g.table.final_sym(elem_type).kind
90 } else {
91 ast.Kind.placeholder
92 }
93 if expr.has_index || (expr.has_len && (g.struct_has_array_or_map_field(elem_type)
94 || (elem_kind in [.array, .map] && !expr.has_init))) {
95 return true
96 }
97 if g.need_tmp_var_in_expr(expr.len_expr) {
98 return true
99 }
100 if g.need_tmp_var_in_expr(expr.cap_expr) {
101 return true
102 }
103 if g.need_tmp_var_in_expr(expr.init_expr) {
104 return true
105 }
106 for elem_expr in expr.exprs {
107 if g.need_tmp_var_in_expr(elem_expr) {
108 return true
109 }
110 }
111 }
112 ast.CallExpr {
113 if expr.is_method {
114 left_sym := g.table.sym(expr.receiver_type)
115 if left_sym.kind in [.array, .array_fixed, .map] {
116 return expr.name !in ['contains', 'exists', 'len', 'cap', 'first', 'last',
117 'index', 'last_index', 'get']
118 }
119 }
120 if expr.or_block.kind != .absent {
121 return true
122 }
123 if g.need_tmp_var_in_expr(expr.left) {
124 return true
125 }
126 for arg in expr.args {
127 if arg.expr is ast.ArrayDecompose {
128 return true
129 }
130 if g.need_tmp_var_in_expr(arg.expr) {
131 return true
132 }
133 }
134 return expr.expected_arg_types.any(it.has_flag(.option))
135 }
136 ast.CastExpr {
137 return g.need_tmp_var_in_expr(expr.expr)
138 }
139 ast.ConcatExpr {
140 for val in expr.vals {
141 if val is ast.CallExpr {
142 if val.return_type.has_option_or_result() {
143 return true
144 }
145 }
146 }
147 }
148 ast.Ident {
149 return expr.or_expr.kind != .absent
150 }
151 ast.IfExpr {
152 if g.need_tmp_var_in_if(expr) {
153 return true
154 }
155 }
156 ast.IfGuardExpr {
157 return true
158 }
159 ast.IndexExpr {
160 if expr.or_expr.kind != .absent {
161 return true
162 }
163 if g.need_tmp_var_in_expr(expr.left) {
164 return true
165 }
166 if g.need_tmp_var_in_expr(expr.index) {
167 return true
168 }
169 }
170 ast.InfixExpr {
171 if g.need_tmp_var_in_expr(expr.left) {
172 return true
173 }
174 if g.need_tmp_var_in_expr(expr.right) {
175 return true
176 }
177 // struct pointer equality comparisons may hoist temp vars
178 // (via gen_struct_pointer_eq_op) which breaks short-circuit
179 // evaluation when used on the right side of `&&` after an
180 // `is` check. Detect this so that infix_expr_and_or_op uses
181 // its safe short-circuit pattern instead.
182 if expr.op in [.eq, .ne] {
183 if (expr.left_type.is_ptr() && !expr.left.is_lvalue())
184 || (expr.right_type.is_ptr() && !expr.right.is_lvalue()) {
185 left_sym := g.table.sym(expr.left_type)
186 right_sym := g.table.sym(expr.right_type)
187 if left_sym.kind == .struct || right_sym.kind == .struct {
188 return true
189 }
190 }
191 }
192 }
193 ast.MapInit {
194 for key in expr.keys {
195 if g.need_tmp_var_in_expr(key) {
196 return true
197 }
198 }
199 for val in expr.vals {
200 if g.need_tmp_var_in_expr(val) {
201 return true
202 }
203 }
204 }
205 ast.MatchExpr {
206 return true
207 }
208 ast.ParExpr {
209 return g.need_tmp_var_in_expr(expr.expr)
210 }
211 ast.PrefixExpr {
212 return g.need_tmp_var_in_expr(expr.right)
213 }
214 ast.SelectorExpr {
215 if g.need_tmp_var_in_expr(expr.expr) {
216 return true
217 }
218 return expr.or_block.kind != .absent
219 }
220 ast.StringInterLiteral {
221 for e in expr.exprs {
222 if g.need_tmp_var_in_expr(e) {
223 return true
224 }
225 }
226 }
227 ast.StructInit {
228 if g.need_tmp_var_in_expr(expr.update_expr) {
229 return true
230 }
231 for init_field in expr.init_fields {
232 if g.need_tmp_var_in_expr(init_field.expr) {
233 return true
234 }
235 }
236 sym := g.table.sym(expr.typ)
237 return sym.info is ast.Struct && sym.info.has_option
238 }
239 ast.SqlExpr {
240 return true
241 }
242 else {}
243 }
244
245 return false
246}
247
248fn (mut g Gen) needs_conds_order(node ast.IfExpr) bool {
249 if node.branches.len > 1 {
250 for branch in node.branches {
251 if g.need_tmp_var_in_expr(branch.cond) {
252 return true
253 }
254 }
255 }
256 return false
257}
258
259fn (mut g Gen) if_expr(node ast.IfExpr) {
260 use_outer_tmp := g.outer_tmp_var != ''
261 saved_outer_tmp_var := g.outer_tmp_var
262 if use_outer_tmp {
263 g.outer_tmp_var = ''
264 }
265 resolved_node_typ := g.infer_if_expr_type(node)
266
267 // For simple if expressions we can use C's `?:`
268 // `if x > 0 { 1 } else { 2 }` => `(x > 0)? (1) : (2)`
269 // For if expressions with multiple statements or another if expression inside, it's much
270 // easier to use a temp var, than do C tricks with commas, introduce special vars etc
271 // (as it used to be done).
272 // Always use this in -autofree, since ?: can have tmp expressions that have to be freed.
273 needs_tmp_var := g.inside_if_option || g.need_tmp_var_in_if(node) || use_outer_tmp
274 needs_conds_order := g.needs_conds_order(node)
275 tmp := if use_outer_tmp {
276 // Use the tmp var from outer context (e.g. from stmts_with_tmp_var)
277 saved_outer_tmp_var
278 } else if g.inside_if_option || (resolved_node_typ != ast.void_type && needs_tmp_var) {
279 g.new_tmp_var()
280 } else {
281 ''
282 }
283 mut cur_line := ''
284 mut raw_state := false
285 tmp_if_option_type := g.last_if_option_type
286 mut exit_label := ''
287 if needs_tmp_var {
288 exit_label = g.new_tmp_var()
289 node_typ := if g.inside_or_block {
290 resolved_node_typ.clear_option_and_result()
291 } else {
292 resolved_node_typ
293 }
294 // For generic functions, if the if-expression's type was set to a concrete type
295 // by the checker but we're generating a different generic instance, we need to
296 // use the correct concrete type from cur_concrete_types
297 resolved_typ := if g.cur_fn != unsafe { nil } && g.cur_fn.generic_names.len > 0
298 && g.cur_concrete_types.len > 0 {
299 // Try to unwrap generic, and if that doesn't work, check if we should use
300 // the function's return type
301 unwrapped := g.unwrap_generic(node_typ)
302 if g.inside_return_expr && !g.inside_struct_init && unwrapped == node_typ
303 && g.cur_fn.return_type.has_flag(.generic) {
304 // The node type didn't unwrap, but the function return type is generic
305 // Get the unwrapped function return type for this instance
306 mut fn_ret_typ := g.unwrap_generic(g.cur_fn.return_type)
307 if g.inside_or_block {
308 fn_ret_typ = fn_ret_typ.clear_option_and_result()
309 }
310 // Check if the function return type directly matches one of the concrete types
311 // If it does, the if expression type should also match that concrete type
312 fn_ret_is_direct_generic := g.cur_concrete_types.any(it == fn_ret_typ)
313 if fn_ret_is_direct_generic && node_typ != fn_ret_typ {
314 // The function returns T directly, and node_typ doesn't match the current T
315 // This means node_typ is stale from another instance
316 fn_ret_typ
317 } else {
318 // Either the function return type is wrapped (like !T or []T),
319 // or node_typ is correct for this instance
320 unwrapped
321 }
322 } else {
323 unwrapped
324 }
325 } else {
326 g.unwrap_generic(node_typ)
327 }
328 resolved_sym := g.table.final_sym(resolved_typ)
329 mut styp := g.styp(resolved_typ)
330 if (g.inside_if_option || node_typ.has_flag(.option)) && !g.inside_or_block {
331 raw_state = g.inside_if_option
332 if resolved_node_typ != ast.void_type {
333 g.last_if_option_type = resolved_node_typ
334 defer(fn) {
335 g.last_if_option_type = tmp_if_option_type
336 }
337 }
338 defer(fn) {
339 g.inside_if_option = raw_state
340 }
341 g.inside_if_option = true
342 styp = styp.replace('*', '_ptr')
343 } else if node_typ.has_flag(.result) && !g.inside_or_block {
344 raw_state = g.inside_if_result
345 defer(fn) {
346 g.inside_if_result = raw_state
347 }
348 g.inside_if_result = true
349 styp = styp.replace('*', '_ptr')
350 } else {
351 g.last_if_option_type = node_typ
352 defer(fn) {
353 g.last_if_option_type = tmp_if_option_type
354 }
355 }
356 cur_line = g.go_before_last_stmt()
357 g.empty_line = true
358 if tmp != '' && !use_outer_tmp {
359 // Only declare the tmp var if it's not from outer context
360 mut declared_tmp := false
361 if resolved_node_typ == ast.void_type && g.last_if_option_type != 0 {
362 // nested if on return stmt
363 g.write2(g.styp(g.unwrap_generic(g.last_if_option_type)), ' ')
364 } else if resolved_sym.kind == .function && resolved_sym.info is ast.FnType {
365 param_types := resolved_sym.info.func.params.map(it.typ)
366 g.writeln('${g.fn_var_signature(resolved_typ, resolved_sym.info.func.return_type,
367 param_types, tmp)}; /* if prepend */')
368 declared_tmp = true
369 } else {
370 g.write('${styp} ')
371 }
372 if !declared_tmp {
373 g.writeln('${tmp}; /* if prepend */')
374 }
375 g.set_current_pos_as_last_stmt_pos()
376 }
377 if g.infix_left_var_name.len > 0 {
378 g.writeln('if (${g.infix_left_var_name}) {')
379 g.set_current_pos_as_last_stmt_pos()
380 g.indent++
381 }
382 } else if node.is_expr || g.inside_ternary != 0 {
383 g.inside_ternary++
384 g.write('(')
385 for i, branch in node.branches {
386 if i > 0 {
387 g.write(' : ')
388 }
389 if i < node.branches.len - 1 || !node.has_else {
390 g.expr(branch.cond)
391 g.write(' ? ')
392 }
393 prev_expected_cast_type := g.expected_cast_type
394 if node.is_expr && (g.table.sym(resolved_node_typ).kind == .sum_type
395 || resolved_node_typ.has_flag(.shared_f)) {
396 g.expected_cast_type = resolved_node_typ
397 }
398 g.stmts(branch.stmts)
399 g.expected_cast_type = prev_expected_cast_type
400 }
401 if node.branches.len == 1 && !node.is_expr {
402 g.write(': 0')
403 }
404 g.write(')')
405 g.decrement_inside_ternary()
406 return
407 }
408 mut is_guard := false
409 mut guard_idx := 0
410 mut guard_vars := []string{}
411 mut guard_expr_types := []ast.Type{len: node.branches.len}
412 mut guard_else_uses_err := []bool{len: node.branches.len}
413 mut guard_owns_error := []bool{len: node.branches.len}
414 for i, branch in node.branches {
415 cond := branch.cond
416 if cond is ast.IfGuardExpr {
417 if !is_guard {
418 is_guard = true
419 guard_vars = []string{len: node.branches.len}
420 }
421 guard_idx = i // saves the last if guard index
422 guard_else_uses_err[i] = g.if_guard_else_uses_err(node, i)
423 guard_owns_error[i] = cond.expr is ast.IndexExpr
424 if cond.expr !in [ast.IndexExpr, ast.PrefixExpr] {
425 var_name := g.new_tmp_var()
426 guard_vars[i] = var_name
427 cond_expr_type := if g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0
428 && cond.expr is ast.CallExpr {
429 resolved := g.resolve_return_type(cond.expr)
430 if resolved != ast.void_type && !resolved.has_flag(.generic) {
431 if cond.expr_type.has_flag(.option) && !resolved.has_flag(.option) {
432 resolved.set_flag(.option)
433 } else if cond.expr_type.has_flag(.result) && !resolved.has_flag(.result) {
434 resolved.set_flag(.result)
435 } else {
436 resolved
437 }
438 } else {
439 cond.expr_type
440 }
441 } else {
442 cond.expr_type
443 }
444 g.writeln('${g.styp(g.unwrap_generic(cond_expr_type))} ${var_name} = {0};')
445 } else if cond.expr is ast.IndexExpr {
446 value_type := g.table.value_type(g.unwrap_generic(cond.expr.left_type))
447 if value_type.has_flag(.option) {
448 var_name := g.new_tmp_var()
449 guard_vars[i] = var_name
450 g.writeln('${g.styp(value_type)} ${var_name} = {0};')
451 } else {
452 guard_vars[i] = ''
453 }
454 } else {
455 guard_vars[i] = ''
456 }
457 }
458 }
459 mut branch_cond_var_names := []string{}
460 mut tmp_var_scope_count := 0
461 for i, branch in node.branches {
462 is_else := i == node.branches.len - 1 && node.has_else
463 if i > 0 {
464 if needs_tmp_var {
465 g.writeln('};')
466 // Open a new scope so that any variables generated by the next
467 // branch's condition evaluation (e.g. from `.any()` or `.all()`)
468 // are inside a block that the previous branch's `goto` skips over
469 // entirely, preventing gcc's -Wjump-misses-init with -cstrict.
470 if !is_else {
471 g.writeln('{')
472 tmp_var_scope_count++
473 }
474 g.set_current_pos_as_last_stmt_pos()
475 } else {
476 g.write('} else ')
477 }
478 }
479 // if last branch is `else {`
480 if is_else {
481 g.writeln('{')
482 // define `err` for the last branch after a `if val := opt {...}' guard
483 if is_guard && guard_idx == i - 1 {
484 cvar_name := guard_vars[guard_idx]
485 if guard_else_uses_err[guard_idx] {
486 if err_var := branch.scope.find_var('err') {
487 if err_var.is_used {
488 g.writeln('\tIError err = ${cvar_name}.err;')
489 }
490 }
491 } else if guard_owns_error[guard_idx] && guard_expr_types[guard_idx] != 0 {
492 g.if_guard_error_cleanup(cvar_name, guard_expr_types[guard_idx])
493 }
494 }
495 } else if branch.cond is ast.IfGuardExpr {
496 mut var_name := guard_vars[i]
497 mut short_opt := false
498 g.left_is_opt = true
499 // Resolve the expression type in generic context
500 mut guard_expr_type := branch.cond.expr_type
501 if g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0 {
502 if branch.cond.expr is ast.CallExpr {
503 resolved := g.resolve_return_type(branch.cond.expr)
504 if resolved != ast.void_type && !resolved.has_flag(.generic) {
505 guard_expr_type = if branch.cond.expr_type.has_flag(.option)
506 && !resolved.has_flag(.option) {
507 resolved.set_flag(.option)
508 } else if branch.cond.expr_type.has_flag(.result)
509 && !resolved.has_flag(.result) {
510 resolved.set_flag(.result)
511 } else {
512 resolved
513 }
514 }
515 } else {
516 resolved := g.unwrap_generic(g.recheck_concrete_type(guard_expr_type))
517 if resolved != ast.void_type {
518 guard_expr_type = resolved
519 }
520 }
521 }
522 guard_expr_types[i] = guard_expr_type
523 if var_name == '' {
524 short_opt = true // we don't need a further tmp, so use the one we'll get later
525 var_name = g.new_tmp_var()
526 guard_vars[i] = var_name // for `else`
527 g.tmp_count--
528 if guard_expr_type.has_flag(.option) {
529 g.writeln('if (${var_name}.state == 0) {')
530 } else if guard_expr_type.has_flag(.result) {
531 g.writeln('if (!${var_name}.is_error) {')
532 }
533 } else {
534 g.write('if (${var_name} = ')
535 g.expr(branch.cond.expr)
536 if guard_expr_type.has_flag(.option) {
537 dot_or_ptr := if !guard_expr_type.has_flag(.option_mut_param_t) {
538 '.'
539 } else {
540 '-> '
541 }
542 g.writeln(', ${var_name}${dot_or_ptr}state == 0) {')
543 } else if guard_expr_type.has_flag(.result) {
544 g.writeln(', !${var_name}.is_error) {')
545 }
546 }
547 if short_opt || branch.cond.vars.len > 1 || branch.cond.vars[0].name != '_' {
548 base_type := g.base_type(guard_expr_type)
549 if short_opt {
550 cond_var_name := if branch.cond.vars[0].name == '_' {
551 '_dummy_${g.tmp_count + 1}'
552 } else {
553 branch.cond.vars[0].name
554 }
555 mut short_opt_is_auto_heap := false
556 if branch.stmts.len > 0 {
557 scope := g.file.scope.innermost(ast.Node(branch.stmts.last()).pos().pos)
558 if v := scope.find_var(branch.cond.vars[0].name) {
559 short_opt_is_auto_heap = v.is_auto_heap
560 }
561 }
562 if g.table.sym(branch.cond.expr_type).kind == .array_fixed {
563 g.writeln('\t${base_type} ${cond_var_name} = {0};')
564 g.write('\tmemcpy((${base_type}*)${cond_var_name}, &')
565 g.expr(branch.cond.expr)
566 g.writeln(', sizeof(${base_type}));')
567 g.write_if_guard_gc_pin(branch.scope, branch.cond.vars[0].name,
568 cond_var_name)
569 } else if short_opt_is_auto_heap {
570 g.write('\t${base_type}* ${cond_var_name} = HEAP(${base_type}, ')
571 g.expr(branch.cond.expr)
572 g.writeln(');')
573 g.write_if_guard_gc_pin(branch.scope, branch.cond.vars[0].name,
574 cond_var_name)
575 } else {
576 g.write('\t${base_type} ${cond_var_name} = ')
577 g.expr(branch.cond.expr)
578 g.writeln(';')
579 g.write_if_guard_gc_pin(branch.scope, branch.cond.vars[0].name,
580 cond_var_name)
581 }
582 } else {
583 mut is_auto_heap := false
584 if branch.stmts.len > 0 {
585 scope := g.file.scope.innermost(ast.Node(branch.stmts.last()).pos().pos)
586 if v := scope.find_var(branch.cond.vars[0].name) {
587 is_auto_heap = v.is_auto_heap
588 }
589 }
590 if branch.cond.vars.len == 1 {
591 left_var_name := c_name(branch.cond.vars[0].name)
592 dot_or_ptr := if !branch.cond.expr_type.has_flag(.option_mut_param_t) {
593 '.'
594 } else {
595 '-> '
596 }
597 guard_typ :=
598 g.unwrap_generic(branch.cond.expr_type.clear_option_and_result())
599 guard_is_heap_obj := g.table.final_sym(guard_typ).is_heap()
600 && !guard_typ.is_ptr()
601 if guard_is_heap_obj {
602 g.writeln('\t${base_type}* ${left_var_name} = (${base_type}*)${var_name}${dot_or_ptr}data;')
603 g.write_if_guard_gc_pin(branch.scope, branch.cond.vars[0].name,
604 left_var_name)
605 } else if is_auto_heap {
606 // Non-heap structs still need a dedicated heap copy when the guard value escapes.
607 // `@[heap]` structs already live behind the option data pointer and must not be copied.
608 g.writeln('\t${base_type}* ${left_var_name} = HEAP(${base_type}, *(${base_type}*)${var_name}${dot_or_ptr}data);')
609 g.write_if_guard_gc_pin(branch.scope, branch.cond.vars[0].name,
610 left_var_name)
611 } else if base_type.starts_with('Array_fixed') {
612 g.writeln('\t${base_type} ${left_var_name} = {0};')
613 g.writeln('memcpy(${left_var_name}, (${base_type}*)${var_name}.data, sizeof(${base_type}));')
614 g.write_if_guard_gc_pin(branch.scope, branch.cond.vars[0].name,
615 left_var_name)
616 } else {
617 expr_sym := g.table.sym(branch.cond.expr_type)
618 if expr_sym.info is ast.FnType {
619 g.write_fntype_decl(left_var_name, expr_sym.info,
620 guard_expr_type.nr_muls())
621 if guard_expr_type.nr_muls() == 0 {
622 g.writeln(' = *(${base_type}*)${var_name}${dot_or_ptr}data;')
623 } else {
624 g.writeln(' = (${base_type}*)${var_name}${dot_or_ptr}data;')
625 }
626 g.write_if_guard_gc_pin(branch.scope, branch.cond.vars[0].name,
627 left_var_name)
628 } else {
629 g.write('\t${base_type} ${left_var_name}')
630 g.writeln(' = *(${base_type}*)${var_name}${dot_or_ptr}data;')
631 g.write_if_guard_gc_pin(branch.scope, branch.cond.vars[0].name,
632 left_var_name)
633 }
634 }
635 } else if branch.cond.vars.len > 1 {
636 sym := g.table.sym(guard_expr_type)
637 if sym.info is ast.MultiReturn {
638 if sym.info.types.len == branch.cond.vars.len {
639 for vi, var in branch.cond.vars {
640 if var.name == '_' {
641 continue
642 }
643 var_typ := g.styp(sym.info.types[vi])
644 left_var_name := c_name(var.name)
645 if is_auto_heap {
646 g.writeln('\t${var_typ}* ${left_var_name} = (HEAP(${base_type}, *(${base_type}*)${var_name}.data).arg${vi});')
647 g.write_if_guard_gc_pin(branch.scope, var.name,
648 left_var_name)
649 } else {
650 g.writeln('\t${var_typ} ${left_var_name} = (*(${base_type}*)${var_name}.data).arg${vi};')
651 g.write_if_guard_gc_pin(branch.scope, var.name,
652 left_var_name)
653 }
654 }
655 }
656 }
657 }
658 }
659 }
660 } else {
661 if i == 0 && node.branches.len > 1 && !needs_tmp_var && needs_conds_order {
662 cond_var_name := g.new_tmp_var()
663 line := g.go_before_last_stmt().trim_space()
664 g.empty_line = true
665 g.write('bool ${cond_var_name} = ')
666 g.expr(branch.cond)
667 g.writeln(';')
668 branch_cond_var_names << cond_var_name
669 g.set_current_pos_as_last_stmt_pos()
670 g.writeln2(line, 'if (${cond_var_name}) {')
671 } else if i > 0 && branch_cond_var_names.len > 0 && !needs_tmp_var && needs_conds_order {
672 cond_var_name := g.new_tmp_var()
673 line := g.go_before_last_stmt()
674 g.empty_line = true
675 g.writeln('bool ${cond_var_name};')
676 branch_cond := branch_cond_var_names.join(' || ')
677 g.writeln('if (!(${branch_cond})) {')
678 g.set_current_pos_as_last_stmt_pos()
679 g.indent++
680 g.write('${cond_var_name} = ')
681 prev_is_autofree := g.is_autofree
682 g.is_autofree = false
683 g.expr(branch.cond)
684 g.is_autofree = prev_is_autofree
685 g.writeln(';')
686 g.indent--
687 g.writeln('}')
688 branch_cond_var_names << cond_var_name
689 g.set_current_pos_as_last_stmt_pos()
690 g.write(line)
691 g.writeln('if (${cond_var_name}) {')
692 } else {
693 mut no_needs_par := false
694 if branch.cond is ast.InfixExpr {
695 if branch.cond.op == .key_in && branch.cond.left !is ast.InfixExpr
696 && branch.cond.right is ast.ArrayInit {
697 no_needs_par = true
698 }
699 }
700 inside_interface_deref_old := g.inside_interface_deref
701 if !g.inside_interface_deref && branch.cond is ast.Ident
702 && g.table.is_interface_var(branch.cond.obj) {
703 g.inside_interface_deref = true
704 }
705 if no_needs_par {
706 g.write('if ')
707 } else {
708 g.write('if (')
709 }
710 g.expr(branch.cond)
711 if no_needs_par {
712 g.writeln(' {')
713 } else {
714 g.writeln(') {')
715 }
716 g.inside_interface_deref = inside_interface_deref_old
717 }
718 }
719 if needs_tmp_var {
720 prev_expected_cast_type := g.expected_cast_type
721 if node.is_expr && (g.table.sym(resolved_node_typ).kind == .sum_type
722 || resolved_node_typ.has_flag(.shared_f)) {
723 g.expected_cast_type = resolved_node_typ
724 }
725 g.stmts_with_tmp_var(branch.stmts, tmp)
726 g.write_defer_stmts(branch.scope, false, node.pos)
727 g.expected_cast_type = prev_expected_cast_type
728 if !is_else
729 && (branch.stmts.len > 0 && branch.stmts.last() !in [ast.Return, ast.BranchStmt]) {
730 g.writeln('\tgoto ${exit_label};')
731 }
732 } else {
733 // restore if_expr stmt header pos
734 stmt_pos := g.nth_stmt_pos(0)
735 g.stmts(branch.stmts)
736 g.write_defer_stmts(branch.scope, false, node.pos)
737 g.stmt_path_pos << stmt_pos
738 }
739 }
740 if node.branches.len > 0 {
741 g.writeln('}')
742 if !needs_tmp_var {
743 g.set_current_pos_as_last_stmt_pos()
744 }
745 }
746 for i, var_name in guard_vars {
747 if var_name == '' || guard_expr_types[i] == 0 || guard_else_uses_err[i]
748 || !guard_owns_error[i] {
749 continue
750 }
751 if node.has_else && i == node.branches.len - 2 {
752 continue
753 }
754 g.if_guard_error_cleanup(var_name, guard_expr_types[i])
755 }
756 if needs_tmp_var {
757 // Close the extra scopes opened between branches to isolate
758 // condition-evaluation variables from earlier branches' gotos.
759 for _ in 0 .. tmp_var_scope_count {
760 g.writeln('}')
761 }
762 if g.infix_left_var_name.len > 0 {
763 g.indent--
764 g.writeln('}')
765 }
766 g.empty_line = false
767 g.writeln('\t${exit_label}: {};')
768 g.set_current_pos_as_last_stmt_pos()
769 g.write('${cur_line}${tmp}')
770 }
771}
772