v4 / vlib / v / gen / c / match.v
698 lines · 683 sloc · 20.96 KB · 1803bc00358d1e9c1935e46437a27df1819b0b3c
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
9fn (g &Gen) match_cond_can_use_directly(cond ast.Expr) bool {
10 return (cond in [ast.Ident, ast.IntegerLiteral, ast.StringLiteral, ast.FloatLiteral]
11 && (cond !is ast.Ident || (cond is ast.Ident && cond.or_expr.kind == .absent)))
12 || (cond is ast.SelectorExpr && cond.or_block.kind == .absent && (cond.expr !is ast.CallExpr
13 || (cond.expr as ast.CallExpr).or_block.kind == .absent))
14}
15
16fn (mut g Gen) need_tmp_var_in_match(node ast.MatchExpr) bool {
17 resolved_return_type := g.infer_match_expr_type(node)
18 if node.is_expr && resolved_return_type != ast.void_type && resolved_return_type != 0 {
19 if g.inside_struct_init {
20 return true
21 }
22 if g.table.sym(resolved_return_type).kind in [.sum_type, .interface, .multi_return]
23 || resolved_return_type.has_option_or_result() {
24 return true
25 }
26 if g.table.final_sym(node.cond_type).kind == .enum && node.branches.len > 5 {
27 return true
28 }
29 if !g.match_cond_can_use_directly(node.cond) {
30 return true
31 }
32 if g.need_tmp_var_in_expr(node.cond) {
33 return true
34 }
35 for branch in node.branches {
36 if branch.stmts.len > 1 {
37 return true
38 }
39 if branch.stmts.len == 1 {
40 if branch.stmts[0] is ast.ExprStmt {
41 stmt := branch.stmts[0] as ast.ExprStmt
42 if stmt.expr is ast.ArrayInit && stmt.expr.is_fixed {
43 return true
44 }
45 if g.need_tmp_var_in_expr(stmt.expr) {
46 return true
47 }
48 } else if branch.stmts[0] is ast.Return {
49 return true
50 } else if branch.stmts[0] is ast.BranchStmt {
51 return true
52 }
53 }
54 }
55 }
56 return false
57}
58
59fn (mut g Gen) match_expr(node ast.MatchExpr) {
60 if node.cond_type == 0 {
61 g.writeln('// match 0')
62 return
63 }
64 resolved_return_type := g.infer_match_expr_type(node)
65 need_tmp_var := g.need_tmp_var_in_match(node)
66 is_expr := (node.is_expr && resolved_return_type != ast.void_type) || g.inside_ternary > 0
67
68 // When the tmp var is a fn-returned fixed array, it is a wrapper struct whose data
69 // lives in a `.ret_arr` member. Flag it so every branch writes through `.ret_arr`,
70 // even ones whose own expr type lacks the `is_fn_ret` flag (e.g. a fixed array
71 // literal mixed with a function call returning the same fixed array type).
72 prev_if_match_tmp_is_fn_ret_arr := g.if_match_tmp_is_fn_ret_arr
73 // Mirror the wrapper-struct check used when emitting the tmp var below, so every
74 // branch agrees with how the result is finally read.
75 ret_arr_sym := g.table.sym(g.unwrap_generic(g.recheck_concrete_type(resolved_return_type)))
76 g.if_match_tmp_is_fn_ret_arr = need_tmp_var && ret_arr_sym.info is ast.ArrayFixed
77 && ret_arr_sym.info.is_fn_ret
78 defer {
79 g.if_match_tmp_is_fn_ret_arr = prev_if_match_tmp_is_fn_ret_arr
80 }
81
82 mut cond_var := ''
83 mut tmp_var := ''
84 mut cur_line := ''
85 if is_expr && !need_tmp_var {
86 g.inside_ternary++
87 }
88 if is_expr {
89 if resolved_return_type.has_flag(.option) {
90 old := g.inside_match_option
91 defer(fn) {
92 g.inside_match_option = old
93 }
94 g.inside_match_option = true
95 } else if resolved_return_type.has_flag(.result) {
96 old := g.inside_match_result
97 defer(fn) {
98 g.inside_match_result = old
99 }
100 g.inside_match_result = true
101 }
102 }
103 if g.match_cond_can_use_directly(node.cond) {
104 cond_var = g.expr_string(node.cond)
105 } else {
106 line := if is_expr {
107 g.empty_line = true
108 g.go_before_last_stmt().trim_left('\t')
109 } else {
110 ''
111 }
112 cond_var = g.new_tmp_var()
113 g.write('${g.styp(node.cond_type)} ${cond_var} = ')
114 g.expr(node.cond)
115 g.writeln(';')
116 g.set_current_pos_as_last_stmt_pos()
117 g.write(line)
118 }
119 if need_tmp_var {
120 g.empty_line = true
121 cur_line = g.go_before_last_stmt().trim_left(' \t')
122 tmp_var = g.new_tmp_var()
123 mut func_decl := ''
124 ret_final_sym := g.table.final_sym(resolved_return_type)
125 if !resolved_return_type.has_option_or_result() && ret_final_sym.kind == .function {
126 if ret_final_sym.info is ast.FnType {
127 def := g.fn_var_signature(ast.void_type, ret_final_sym.info.func.return_type,
128 ret_final_sym.info.func.params.map(it.typ), tmp_var)
129 func_decl = '${def} = &${g.styp(resolved_return_type)};'
130 }
131 }
132 if func_decl != '' {
133 g.writeln(func_decl) // func, anon func declaration
134 } else {
135 g.writeln('${g.styp(resolved_return_type)} ${tmp_var} = ${g.type_default(resolved_return_type)};')
136 }
137 g.empty_line = true
138 if g.infix_left_var_name.len > 0 {
139 g.writeln('if (${g.infix_left_var_name}) {')
140 g.indent++
141 }
142 }
143
144 if is_expr && !need_tmp_var {
145 // brackets needed otherwise '?' will apply to everything on the left
146 g.write('(')
147 }
148 if node.is_sum_type {
149 g.match_expr_sumtype(node, is_expr, cond_var, tmp_var, resolved_return_type)
150 } else {
151 cond_fsym := g.table.final_sym(node.cond_type)
152 enum_is_multi_allowed := cond_fsym.info is ast.Enum && cond_fsym.info.is_multi_allowed
153 mut can_be_a_switch := true
154 all_branches: for branch in node.branches {
155 for expr in branch.exprs {
156 match expr {
157 ast.BoolLiteral, ast.IntegerLiteral, ast.CharLiteral, ast.EnumVal {
158 continue
159 }
160 else {
161 // ast.StringLiteral, ast.Ident, ast.RangeExpr can not used in switch cases in C
162 // eprintln('>>>> node.cond: ${node.cond} | branch expr: ${typeof(expr)} | expr: ${expr}')
163 can_be_a_switch = false
164 break all_branches
165 }
166 }
167 }
168 }
169 // eprintln('> can_be_a_switch: ${can_be_a_switch}')
170 if can_be_a_switch && !is_expr && g.loop_depth == 0 && g.fn_decl != unsafe { nil }
171 && cond_fsym.is_int() && !enum_is_multi_allowed {
172 g.match_expr_switch(node, is_expr, cond_var, tmp_var, cond_fsym, resolved_return_type)
173 } else if cond_fsym.kind == .enum && g.loop_depth == 0 && node.branches.len > 5
174 && g.fn_decl != unsafe { nil } && !enum_is_multi_allowed {
175 // do not optimize while in top-level
176 g.match_expr_switch(node, is_expr, cond_var, tmp_var, cond_fsym, resolved_return_type)
177 } else {
178 g.match_expr_classic(node, is_expr, cond_var, tmp_var, resolved_return_type)
179 }
180 }
181 g.set_current_pos_as_last_stmt_pos()
182 if need_tmp_var {
183 if g.infix_left_var_name.len > 0 {
184 g.writeln('')
185 g.indent--
186 g.writeln('}')
187 g.set_current_pos_as_last_stmt_pos()
188 }
189 }
190 g.write(cur_line)
191 if need_tmp_var {
192 resolved_sym := g.table.sym(g.unwrap_generic(g.recheck_concrete_type(resolved_return_type)))
193 tmp_is_return_fixed_array := resolved_sym.info is ast.ArrayFixed
194 && resolved_sym.info.is_fn_ret
195 if tmp_is_return_fixed_array {
196 g.write('${tmp_var}.ret_arr')
197 } else {
198 g.write(tmp_var)
199 }
200 }
201 if is_expr && !need_tmp_var {
202 g.write(')')
203 g.decrement_inside_ternary()
204 }
205}
206
207fn (mut g Gen) match_expr_sumtype(node ast.MatchExpr, is_expr bool, cond_var string, tmp_var string, resolved_return_type ast.Type) {
208 dot_or_ptr := g.dot_or_ptr(node.cond_type)
209 use_ternary := is_expr && tmp_var == ''
210 cond_sym := g.table.final_sym(node.cond_type)
211 // Tracks the runtime type tags already handled by earlier arms of this match.
212 // With alias/runtime-tag matching, a variant and an alias of it (e.g. `u8` and
213 // `type EmptyExpr = u8`, both variants of the same sumtype) expand to the same
214 // set of tags, so distinct arms can produce identical `_typ == ...` conditions.
215 // Emitting both yields a duplicated `else if` condition (a -Wduplicated-cond
216 // error under -cstrict) and the later arm is unreachable anyway, so drop the
217 // already-covered tags and skip an arm once nothing fresh remains.
218 mut seen_tag_idxs := map[string]bool{}
219 for j, branch in node.branches {
220 mut sumtype_index := 0
221 // iterates through all types in sumtype branches
222 for {
223 g.aggregate_type_idx = sumtype_index
224 is_last := j == node.branches.len - 1 && sumtype_index == branch.exprs.len - 1
225 mut has_branch_type := false
226 mut had_old_branch_type := false
227 mut old_branch_type := ast.Type(0)
228 mut branch_type := ast.Type(0)
229 if cond_sym.kind == .sum_type && sumtype_index < branch.exprs.len {
230 branch_expr := unsafe { &branch.exprs[sumtype_index] }
231 if branch_expr is ast.TypeNode {
232 branch_type = branch_expr.typ
233 }
234 }
235 mut sumtype_fresh_idx_exprs := []string{}
236 if !branch.is_else && cond_sym.kind == .sum_type && sumtype_index < branch.exprs.len {
237 ce := unsafe { &branch.exprs[sumtype_index] }
238 if ce is ast.TypeNode {
239 variant_type := g.unwrap_generic(g.recheck_concrete_type(ce.typ))
240 all_idx_exprs := g.matching_sumtype_variant_type_idx_exprs(node.cond_type,
241 variant_type)
242 if use_ternary {
243 // Ternary `?:` chains cannot drop an arm, so keep every tag.
244 sumtype_fresh_idx_exprs = all_idx_exprs.clone()
245 } else {
246 for idx_expr in all_idx_exprs {
247 if idx_expr !in seen_tag_idxs {
248 sumtype_fresh_idx_exprs << idx_expr
249 }
250 }
251 if sumtype_fresh_idx_exprs.len == 0 {
252 // every tag of this arm is already handled above: unreachable, skip it
253 sumtype_index++
254 if branch.exprs.len == 0 || sumtype_index == branch.exprs.len {
255 break
256 }
257 continue
258 }
259 for idx_expr in sumtype_fresh_idx_exprs {
260 seen_tag_idxs[idx_expr] = true
261 }
262 }
263 }
264 }
265 if branch.is_else || (use_ternary && is_last) {
266 if use_ternary {
267 // TODO: too many branches. maybe separate ?: matches
268 g.write(' : ')
269 } else {
270 g.writeln('')
271 g.write_v_source_line_info(branch)
272 g.writeln('else {')
273 }
274 } else {
275 if j > 0 || sumtype_index > 0 {
276 if use_ternary {
277 g.write(' : ')
278 } else {
279 g.write_v_source_line_info(branch)
280 g.write('else ')
281 }
282 }
283 if use_ternary {
284 g.write('(')
285 } else {
286 if j == 0 && sumtype_index == 0 {
287 g.empty_line = true
288 }
289 g.write_v_source_line_info(branch)
290 g.write('if (')
291 }
292 cur_expr := unsafe { &branch.exprs[sumtype_index] }
293 cond_expr := if node.cond_type.nr_muls() > 1 {
294 '(${'*'.repeat(node.cond_type.nr_muls() - 1)}${cond_var})'
295 } else {
296 cond_var
297 }
298 tag_expr := '${cond_expr}${dot_or_ptr}_typ'
299 if cond_sym.kind == .sum_type {
300 if cur_expr is ast.None {
301 g.write('${tag_expr} == ${ast.none_type.idx()} /* none */')
302 } else if cur_expr is ast.TypeNode {
303 g.write_type_tag_condition(tag_expr, '==', sumtype_fresh_idx_exprs)
304 } else {
305 g.write('${tag_expr} == ')
306 g.expr(cur_expr)
307 }
308 } else if cond_sym.kind == .interface {
309 if cur_expr is ast.TypeNode {
310 g.write_type_tag_condition(tag_expr, '==', g.matching_interface_variant_index_exprs(cond_sym,
311 cur_expr.typ))
312 } else if cur_expr is ast.None && cond_sym.idx == ast.error_type_idx {
313 g.write('${tag_expr} == _IError_None___index')
314 }
315 }
316 if use_ternary {
317 g.write(')? ')
318 } else {
319 g.writeln(') {')
320 }
321 }
322 if branch_type != 0 {
323 has_branch_type = true
324 if old_type := g.type_resolver.type_map[cond_var] {
325 had_old_branch_type = true
326 old_branch_type = old_type
327 }
328 g.type_resolver.update_ct_type(cond_var, branch_type)
329 g.clear_type_resolution_caches()
330 }
331 if is_expr && tmp_var.len > 0
332 && g.table.sym(resolved_return_type).kind in [.sum_type, .interface] {
333 g.expected_cast_type = resolved_return_type
334 }
335 inside_interface_deref_old := g.inside_interface_deref
336 if is_expr && branch.stmts.len > 0 {
337 mut stmt := branch.stmts.last()
338 if mut stmt is ast.ExprStmt {
339 if mut stmt.expr is ast.Ident && stmt.expr.obj is ast.Var
340 && g.table.is_interface_var(stmt.expr.obj) {
341 g.inside_interface_deref = true
342 } else if mut stmt.expr is ast.PrefixExpr && stmt.expr.right is ast.Ident {
343 ident := stmt.expr.right as ast.Ident
344 if ident.obj is ast.Var && g.table.is_interface_var(ident.obj) {
345 g.inside_interface_deref = true
346 }
347 }
348 }
349 }
350 g.stmts_with_tmp_var(branch.stmts, tmp_var)
351 g.write_defer_stmts(branch.scope, false, node.pos)
352 g.inside_interface_deref = inside_interface_deref_old
353 g.expected_cast_type = 0
354 if has_branch_type {
355 if had_old_branch_type {
356 g.type_resolver.update_ct_type(cond_var, old_branch_type)
357 } else {
358 g.type_resolver.type_map.delete(cond_var)
359 }
360 g.clear_type_resolution_caches()
361 }
362 if g.inside_ternary == 0 {
363 g.writeln('}')
364 g.set_current_pos_as_last_stmt_pos()
365 }
366 sumtype_index++
367 if branch.exprs.len == 0 || sumtype_index == branch.exprs.len {
368 break
369 }
370 }
371 // reset global field for next use
372 g.aggregate_type_idx = 0
373 }
374}
375
376fn (mut g Gen) match_expr_switch(node ast.MatchExpr, is_expr bool, cond_var string, tmp_var string, cond_fsym ast.TypeSymbol, resolved_return_type ast.Type) {
377 node_cond_type_unsigned := node.cond_type in [ast.u16_type, ast.u32_type, ast.u64_type]
378
379 covered_enum_cap := if cond_fsym.info is ast.Enum { cond_fsym.info.vals.len } else { 0 }
380 mut covered_enum := []string{cap: covered_enum_cap} // collects missing enum variant branches to avoid cstrict errors
381
382 // A branch that has a RangeExpr condition, cannot be emitted as a switch case branch;
383 // we will store each of them in range_branches, and then will handle them all in the default branch with if conditions:
384 mut range_branches := []ast.MatchBranch{cap: node.branches.len}
385 mut default_generated := false
386
387 g.empty_line = true
388 g.writeln('switch (${cond_var}) {')
389 g.indent++
390 for branch in node.branches {
391 if branch.is_else {
392 if cond_fsym.info is ast.Enum {
393 cname := '${cond_fsym.cname}__'
394 for val in cond_fsym.info.vals {
395 if val !in covered_enum {
396 g.writeln('case ${cname}${val}:')
397 }
398 }
399 }
400 g.writeln('default: {')
401 default_generated = true
402 g.indent++
403 if range_branches.len > 0 {
404 for range_branch in range_branches {
405 g.write('if (')
406 for i, expr in range_branch.exprs {
407 if i > 0 {
408 g.write(' || ')
409 }
410 if expr is ast.RangeExpr {
411 g.write('(')
412 if g.should_check_low_bound_in_range_expr(expr, node_cond_type_unsigned) {
413 g.write('${cond_var} >= ')
414 g.expr(expr.low)
415 g.write(' && ')
416 }
417 g.write('${cond_var} <= ')
418 g.expr(expr.high)
419 g.write(')')
420 } else {
421 g.write('${cond_var} == (')
422 g.expr(expr)
423 g.write(')')
424 }
425 }
426 g.writeln(') {')
427 ends_with_return := g.stmts_with_tmp_var(range_branch.stmts, tmp_var)
428 if !ends_with_return {
429 g.writeln('\tbreak;')
430 }
431 g.writeln('}')
432 }
433 }
434 } else {
435 if branch.exprs.any(it is ast.RangeExpr) {
436 range_branches << branch
437 continue
438 }
439 for expr in branch.exprs {
440 if expr is ast.EnumVal {
441 covered_enum << expr.val
442 }
443 g.write('case ')
444 g.expr(expr)
445 g.write(': ')
446 }
447 }
448 g.writeln('{')
449 if is_expr && tmp_var.len > 0
450 && g.table.sym(resolved_return_type).kind in [.sum_type, .interface] {
451 g.expected_cast_type = resolved_return_type
452 }
453 ends_with_return := g.stmts_with_tmp_var(branch.stmts, tmp_var)
454 g.expected_cast_type = 0
455 g.write_defer_stmts(branch.scope, false, node.pos)
456 if !ends_with_return {
457 g.writeln('\tbreak;')
458 }
459 g.writeln('}')
460 }
461 if range_branches.len > 0 && !default_generated {
462 g.writeln('default: {')
463 g.indent++
464 default_generated = true
465 for range_branch in range_branches {
466 g.write('if (')
467 for i, expr in range_branch.exprs {
468 if i > 0 {
469 g.write(' || ')
470 }
471 if expr is ast.RangeExpr {
472 g.write('(')
473 if g.should_check_low_bound_in_range_expr(expr, node_cond_type_unsigned) {
474 g.write('${cond_var} >= ')
475 g.expr(expr.low)
476 g.write(' && ')
477 }
478 g.write('${cond_var} <= ')
479 g.expr(expr.high)
480 g.write(')')
481 } else {
482 g.write('${cond_var} == (')
483 g.expr(expr)
484 g.write(')')
485 }
486 }
487 g.writeln(') {')
488 ends_with_return := g.stmts_with_tmp_var(range_branch.stmts, tmp_var)
489 if !ends_with_return {
490 g.writeln('\tbreak;')
491 }
492 g.write_defer_stmts(range_branch.scope, false, node.pos)
493 g.writeln('}')
494 }
495 }
496 if default_generated {
497 g.indent--
498 g.writeln('}')
499 }
500 g.indent--
501 g.writeln('}')
502}
503
504fn (mut g Gen) should_check_low_bound_in_range_expr(expr ast.RangeExpr, node_cond_type_unsigned bool) bool {
505 // if the type is unsigned, and the low bound of the range expression is 0,
506 // checking it at runtime is not needed:
507 mut should_check_low_bound := true
508 if node_cond_type_unsigned {
509 if expr.low is ast.IntegerLiteral {
510 if expr.low.val == '0' {
511 should_check_low_bound = false
512 }
513 } else if expr.low is ast.Ident {
514 mut elow := unsafe { expr.low }
515 if mut obj := g.table.global_scope.find_const(elow.full_name()) {
516 if mut obj.expr is ast.IntegerLiteral {
517 if obj.expr.val == '0' {
518 should_check_low_bound = false
519 }
520 }
521 }
522 }
523 }
524 return should_check_low_bound
525}
526
527fn (mut g Gen) match_expr_classic(node ast.MatchExpr, is_expr bool, cond_var string, tmp_var string, resolved_return_type ast.Type) {
528 node_cond_type_unsigned := node.cond_type in [ast.u16_type, ast.u32_type, ast.u64_type]
529 type_sym := g.table.final_sym(node.cond_type)
530 use_ternary := is_expr && tmp_var == ''
531 mut reset_if := node.branches.any(it.exprs.any(g.match_must_reset_if(it)))
532 mut has_goto := false
533 for j, branch in node.branches {
534 is_last := j == node.branches.len - 1
535 if reset_if {
536 g.writeln('')
537 g.set_current_pos_as_last_stmt_pos()
538 }
539 if branch.is_else || (use_ternary && is_last) {
540 if node.branches.len > 1 {
541 if use_ternary {
542 // TODO: too many branches. maybe separate ?: matches
543 g.write(' : ')
544 } else {
545 g.writeln('')
546 g.write_v_source_line_info(branch)
547 g.writeln('else {')
548 }
549 }
550 } else {
551 if j > 0 {
552 if use_ternary {
553 g.write(' : ')
554 } else {
555 g.writeln('')
556 g.write_v_source_line_info(branch)
557 if !reset_if {
558 g.write('else ')
559 }
560 }
561 }
562 if use_ternary {
563 g.write('(')
564 } else {
565 if j == 0 {
566 g.writeln('')
567 }
568 g.write_v_source_line_info(branch)
569 g.write('if (')
570 }
571 for i, expr in branch.exprs {
572 if i > 0 {
573 g.write(' || ')
574 }
575 if expr is ast.None {
576 old_left_is_opt := g.left_is_opt
577 g.left_is_opt = true
578 g.expr(node.cond)
579 g.left_is_opt = old_left_is_opt
580 g.write('.state == 2')
581 continue
582 }
583 match type_sym.kind {
584 .array {
585 ptr_typ := g.equality_fn(node.cond_type)
586 g.write('${ptr_typ}_arr_eq(${cond_var}, ')
587 g.expr(expr)
588 g.write(')')
589 }
590 .array_fixed {
591 ptr_typ := g.equality_fn(node.cond_type)
592 g.write('${ptr_typ}_arr_eq(${cond_var}, ')
593 if expr is ast.ArrayInit {
594 g.write('(${g.styp(node.cond_type)})')
595 }
596 g.expr(expr)
597 g.write(')')
598 }
599 .map {
600 ptr_typ := g.equality_fn(node.cond_type)
601 g.write('${ptr_typ}_map_eq(${cond_var}, ')
602 g.expr(expr)
603 g.write(')')
604 }
605 .string {
606 if expr is ast.StringLiteral {
607 slit := cescape_nonascii(util.smart_quote(expr.val, expr.is_raw))
608 if node.cond_type.is_ptr() {
609 g.write('_SLIT_EQ(${cond_var}->str, ${cond_var}->len, "${slit}")')
610 } else {
611 g.write('_SLIT_EQ(${cond_var}.str, ${cond_var}.len, "${slit}")')
612 }
613 } else {
614 deref := if node.cond_type.is_ptr() { '*' } else { '' }
615 g.write('builtin__fast_string_eq(${deref}${cond_var}, ')
616 g.expr(expr)
617 g.write(')')
618 }
619 }
620 .struct {
621 derefs_expr := '*'.repeat(g.get_expr_type(expr).nr_muls())
622 derefs_ctype := '*'.repeat(node.cond_type.nr_muls())
623 ptr_typ := g.equality_fn(node.cond_type)
624 g.write('${ptr_typ}_struct_eq(${derefs_ctype}${cond_var}, ${derefs_expr}')
625 g.expr(expr)
626 g.write(')')
627 }
628 else {
629 if expr is ast.RangeExpr {
630 g.write('(')
631 if g.should_check_low_bound_in_range_expr(expr, node_cond_type_unsigned) {
632 g.write('${cond_var} >= ')
633 g.expr(expr.low)
634 g.write(' && ')
635 }
636 g.write('${cond_var} <= ')
637 g.expr(expr.high)
638 g.write(')')
639 } else if expr is ast.None {
640 old_left_is_opt := g.left_is_opt
641 g.left_is_opt = true
642 g.expr(node.cond)
643 g.left_is_opt = old_left_is_opt
644 g.write('.state == 2')
645 } else {
646 g.write('${cond_var} == (')
647 g.expr(expr)
648 g.write(')')
649 }
650 }
651 }
652 }
653 if use_ternary {
654 g.write(')? ')
655 } else {
656 g.writeln(') {')
657 }
658 }
659 if is_expr && tmp_var.len > 0
660 && g.table.sym(resolved_return_type).kind in [.sum_type, .interface] {
661 g.expected_cast_type = resolved_return_type
662 }
663 g.stmts_with_tmp_var(branch.stmts, tmp_var)
664 g.write_defer_stmts(branch.scope, false, node.pos)
665 g.expected_cast_type = 0
666 if g.inside_ternary == 0 && node.branches.len >= 1 {
667 if reset_if {
668 has_goto = true
669 g.writeln2('\tgoto end_block_${node.pos.line_nr};', '}')
670 g.set_current_pos_as_last_stmt_pos()
671 } else {
672 g.write('}')
673 }
674 }
675 }
676 if has_goto {
677 g.writeln('end_block_${node.pos.line_nr}: {}')
678 g.set_current_pos_as_last_stmt_pos()
679 }
680}
681
682// match_must_reset_if checks if codegen must break the if-elseif sequence in another if expr
683fn (mut g Gen) match_must_reset_if(node ast.Expr) bool {
684 return match node {
685 ast.CallExpr {
686 node.or_block.kind != .absent
687 }
688 ast.CastExpr {
689 node.typ.has_flag(.option)
690 }
691 ast.InfixExpr {
692 g.match_must_reset_if(node.left) || g.match_must_reset_if(node.right)
693 }
694 else {
695 false
696 }
697 }
698}
699