v / vlib / v2 / gen / cleanc / if.v
613 lines · 592 sloc · 15.18 KB · b831b0eec9b5b2756784b5dabf3808d47d6a39ae
Raw
1// Copyright (c) 2026 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.
4
5module cleanc
6
7import v2.ast
8
9fn const_bool_value(expr ast.Expr) ?bool {
10 match expr {
11 ast.BasicLiteral {
12 if expr.kind == .key_true {
13 return true
14 }
15 if expr.kind == .key_false {
16 return false
17 }
18 if expr.value == 'true' {
19 return true
20 }
21 if expr.value == 'false' {
22 return false
23 }
24 }
25 ast.Ident {
26 if expr.name == 'true' {
27 return true
28 }
29 if expr.name == 'false' {
30 return false
31 }
32 }
33 ast.Keyword {
34 if expr.tok == .key_true {
35 return true
36 }
37 if expr.tok == .key_false {
38 return false
39 }
40 }
41 ast.ParenExpr {
42 return const_bool_value(expr.expr)
43 }
44 ast.ModifierExpr {
45 return const_bool_value(expr.expr)
46 }
47 ast.CastExpr {
48 return const_bool_value(expr.expr)
49 }
50 ast.ComptimeExpr {
51 return const_bool_value(expr.expr)
52 }
53 ast.UnsafeExpr {
54 if expr.stmts.len == 1 && expr.stmts[0] is ast.ExprStmt {
55 stmt := expr.stmts[0] as ast.ExprStmt
56 return const_bool_value(stmt.expr)
57 }
58 }
59 ast.PrefixExpr {
60 if expr.op == .not {
61 if v := const_bool_value(expr.expr) {
62 return !v
63 }
64 }
65 }
66 else {}
67 }
68
69 return none
70}
71
72fn (mut g Gen) gen_if_expr_assign_value(name string, value_type string, expr ast.Expr) {
73 g.write_indent()
74 if value_type.starts_with('Array_fixed_') && !value_type.ends_with('*') {
75 if expr is ast.CallExpr {
76 if call_ret := g.get_call_return_type(expr.lhs, expr.args) {
77 if call_ret == value_type {
78 wrapper_type := g.c_fn_return_type_from_v(value_type)
79 g.sb.write_string('{ ${wrapper_type} _tmp = ')
80 g.expr(expr)
81 g.sb.writeln('; memcpy(${name}, _tmp.ret_arr, sizeof(${value_type})); }')
82 return
83 }
84 }
85 }
86 g.sb.write_string('memcpy(${name}, ')
87 if expr is ast.ArrayInitExpr {
88 g.sb.write_string('((${value_type})')
89 g.expr(expr)
90 g.sb.write_string(')')
91 } else {
92 g.expr(expr)
93 }
94 g.sb.writeln(', sizeof(${value_type}));')
95 return
96 }
97 g.sb.write_string('${name} = ')
98 if g.get_sum_type_variants_for(value_type).len > 0 {
99 g.gen_type_cast_expr(value_type, expr)
100 } else {
101 g.expr(expr)
102 }
103 g.sb.writeln(';')
104}
105
106fn (mut g Gen) gen_decl_if_expr_branch(name string, value_type string, stmts []ast.Stmt) {
107 last_idx := last_non_empty_stmt_index(stmts)
108 if last_idx < 0 {
109 return
110 }
111 for i, stmt in stmts {
112 if stmt is ast.EmptyStmt {
113 continue
114 }
115 if i == last_idx && stmt is ast.ExprStmt {
116 if stmt.expr is ast.IfExpr {
117 nested_if := stmt.expr as ast.IfExpr
118 if !g.if_expr_can_be_ternary(&nested_if) && nested_if.else_expr !is ast.EmptyExpr {
119 g.gen_decl_if_expr(name, value_type, &nested_if)
120 continue
121 }
122 if nested_if.else_expr is ast.EmptyExpr {
123 if payload_expr := g.unsafe_expr_or_payload_value(stmts) {
124 g.gen_stmt(stmt)
125 g.gen_if_expr_assign_value(name, value_type, payload_expr)
126 continue
127 }
128 }
129 }
130 // When the last expression is a void call (e.g. array__push from <<),
131 // emit it as a standalone statement without assigning to the temp var.
132 if g.expr_is_void_call(stmt.expr) {
133 g.write_indent()
134 g.expr(stmt.expr)
135 g.sb.writeln(';')
136 } else {
137 g.gen_if_expr_assign_value(name, value_type, stmt.expr)
138 }
139 } else {
140 g.gen_stmt(stmt)
141 }
142 }
143}
144
145fn (mut g Gen) gen_scoped_decl_if_expr_branch(name string, value_type string, stmts []ast.Stmt) {
146 saved_runtime_local_types := g.runtime_local_types.clone()
147 saved_runtime_decl_types := g.runtime_decl_types.clone()
148 saved_not_local_var_cache := g.not_local_var_cache.clone()
149 g.gen_decl_if_expr_branch(name, value_type, stmts)
150 g.runtime_local_types = saved_runtime_local_types.clone()
151 g.runtime_decl_types = saved_runtime_decl_types.clone()
152 g.not_local_var_cache = saved_not_local_var_cache.clone()
153}
154
155// expr_is_void_call checks if the expression is a function call that returns void.
156// Used to prevent assigning void results to temp variables in if-expression branches.
157fn (mut g Gen) expr_is_void_call(expr ast.Expr) bool {
158 if expr is ast.CallExpr {
159 if ret := g.get_call_return_type(expr.lhs, expr.args) {
160 return ret == 'void'
161 }
162 // Check resolved name
163 c_name := g.resolve_call_name(expr.lhs, expr.args.len)
164 if c_name != '' {
165 if ret := g.fn_return_types[c_name] {
166 return ret == 'void'
167 }
168 }
169 // panic/v_panic always returns void (noreturn)
170 if expr.lhs is ast.Ident {
171 if expr.lhs.name in ['panic', 'v_panic'] {
172 return true
173 }
174 }
175 }
176 return false
177}
178
179fn (mut g Gen) gen_decl_if_expr(name string, value_type string, if_expr &ast.IfExpr) {
180 if if_expr.cond is ast.EmptyExpr {
181 g.gen_scoped_decl_if_expr_branch(name, value_type, if_expr.stmts)
182 return
183 }
184 g.write_indent()
185 g.sb.write_string('if (')
186 g.expr(if_expr.cond)
187 g.sb.writeln(') {')
188 g.indent++
189 g.gen_scoped_decl_if_expr_branch(name, value_type, if_expr.stmts)
190 g.indent--
191 g.write_indent()
192 g.sb.write_string('}')
193 if if_expr.else_expr is ast.IfExpr {
194 else_if := if_expr.else_expr as ast.IfExpr
195 if else_if.cond is ast.EmptyExpr {
196 g.sb.writeln(' else {')
197 g.indent++
198 g.gen_scoped_decl_if_expr_branch(name, value_type, else_if.stmts)
199 g.indent--
200 g.write_indent()
201 g.sb.writeln('}')
202 } else {
203 g.sb.writeln(' else {')
204 g.indent++
205 g.gen_decl_if_expr(name, value_type, &else_if)
206 g.indent--
207 g.write_indent()
208 g.sb.writeln('}')
209 }
210 } else if if_expr.else_expr !is ast.EmptyExpr {
211 g.sb.writeln(' else {')
212 g.indent++
213 g.gen_if_expr_assign_value(name, value_type, if_expr.else_expr)
214 g.indent--
215 g.write_indent()
216 g.sb.writeln('}')
217 } else {
218 g.sb.writeln('')
219 }
220}
221
222fn (mut g Gen) gen_return_if_branch(stmts []ast.Stmt) {
223 if stmts.len == 0 {
224 return
225 }
226 for i, stmt in stmts {
227 if i == stmts.len - 1 && stmt is ast.ExprStmt {
228 if stmt.expr is ast.IfExpr {
229 nested_if := stmt.expr as ast.IfExpr
230 if !g.if_expr_can_be_ternary(&nested_if) && nested_if.else_expr !is ast.EmptyExpr {
231 g.gen_return_if_expr(nested_if, true)
232 continue
233 }
234 }
235 mut ret_exprs := []ast.Expr{cap: 1}
236 ret_exprs << stmt.expr
237 ret_stmt := ast.ReturnStmt{
238 exprs: ret_exprs
239 }
240 prev_suppress := g.suppress_return_drop_emit
241 g.suppress_return_drop_emit = true
242 g.gen_stmt(ret_stmt)
243 g.suppress_return_drop_emit = prev_suppress
244 } else {
245 g.gen_stmt(stmt)
246 }
247 }
248}
249
250fn (mut g Gen) gen_return_if_expr(if_expr ast.IfExpr, emit_indent bool) {
251 if if_expr.cond is ast.EmptyExpr {
252 g.gen_return_if_branch(if_expr.stmts)
253 return
254 }
255 if emit_indent {
256 g.write_indent()
257 }
258 g.sb.write_string('if (')
259 g.expr(if_expr.cond)
260 g.sb.writeln(') {')
261 g.indent++
262 g.gen_return_if_branch(if_expr.stmts)
263 g.indent--
264 g.write_indent()
265 g.sb.write_string('}')
266 if if_expr.else_expr is ast.IfExpr {
267 else_if := if_expr.else_expr as ast.IfExpr
268 if else_if.cond is ast.EmptyExpr {
269 g.sb.writeln(' else {')
270 g.indent++
271 g.gen_return_if_branch(else_if.stmts)
272 g.indent--
273 g.write_indent()
274 g.sb.writeln('}')
275 } else {
276 g.sb.writeln(' else {')
277 g.indent++
278 g.gen_return_if_expr(else_if, true)
279 g.indent--
280 g.write_indent()
281 g.sb.writeln('}')
282 }
283 } else if if_expr.else_expr !is ast.EmptyExpr {
284 g.sb.writeln(' else {')
285 g.indent++
286 mut else_exprs := []ast.Expr{cap: 1}
287 else_exprs << if_expr.else_expr
288 else_stmt := ast.ReturnStmt{
289 exprs: else_exprs
290 }
291 prev_suppress := g.suppress_return_drop_emit
292 g.suppress_return_drop_emit = true
293 g.gen_stmt(else_stmt)
294 g.suppress_return_drop_emit = prev_suppress
295 g.indent--
296 g.write_indent()
297 g.sb.writeln('}')
298 } else {
299 g.sb.writeln('')
300 }
301}
302
303fn (g &Gen) if_expr_can_be_ternary(node &ast.IfExpr) bool {
304 if node.cond !is ast.EmptyExpr {
305 if node.stmts.len != 1 || node.stmts[0] !is ast.ExprStmt {
306 return false
307 }
308 if node.else_expr is ast.EmptyExpr {
309 return false
310 }
311 }
312 if node.cond is ast.EmptyExpr {
313 return node.stmts.len == 1 && node.stmts[0] is ast.ExprStmt
314 }
315 if node.else_expr is ast.IfExpr {
316 else_if := node.else_expr as ast.IfExpr
317 return g.if_expr_can_be_ternary(&else_if)
318 }
319 return true
320}
321
322fn (g &Gen) extract_if_expr(expr ast.Expr) ?ast.IfExpr {
323 match expr {
324 ast.IfExpr {
325 return expr
326 }
327 ast.ParenExpr {
328 return g.extract_if_expr(expr.expr)
329 }
330 ast.ModifierExpr {
331 return g.extract_if_expr(expr.expr)
332 }
333 ast.UnsafeExpr {
334 if expr.stmts.len == 1 {
335 stmt0 := expr.stmts[0]
336 if stmt0 is ast.ExprStmt {
337 expr_stmt := stmt0 as ast.ExprStmt
338 return g.extract_if_expr(expr_stmt.expr)
339 }
340 }
341 return none
342 }
343 else {
344 return none
345 }
346 }
347}
348
349fn last_non_empty_stmt_index(stmts []ast.Stmt) int {
350 mut idx := stmts.len - 1
351 for idx >= 0 && stmts[idx] is ast.EmptyStmt {
352 idx--
353 }
354 return idx
355}
356
357fn (mut g Gen) branch_result_type(stmts []ast.Stmt) string {
358 last_idx := last_non_empty_stmt_index(stmts)
359 if last_idx < 0 {
360 return ''
361 }
362 last := stmts[last_idx]
363 match last {
364 ast.BlockStmt {
365 return g.branch_result_type(last.stmts)
366 }
367 ast.ExprStmt {
368 if nested := g.extract_if_expr(last.expr) {
369 return g.get_if_expr_type(&nested)
370 }
371 mut typ := g.get_expr_type(last.expr)
372 if (typ == '' || typ == 'int' || typ == 'void*' || typ == 'voidptr')
373 && last.expr is ast.Ident {
374 if local_typ := g.branch_declared_ident_type(stmts, last_idx, last.expr.name) {
375 typ = local_typ
376 }
377 }
378 if typ == '' || typ == 'int' || typ == 'void*' || typ == 'voidptr' {
379 if raw := g.get_raw_type(last.expr) {
380 raw_type := g.types_type_to_c(raw)
381 if raw_type != '' && raw_type != 'void*' && raw_type != 'voidptr' {
382 typ = raw_type
383 }
384 }
385 }
386 return typ
387 }
388 else {
389 return ''
390 }
391 }
392}
393
394fn (mut g Gen) branch_declared_ident_type(stmts []ast.Stmt, before_idx int, name string) ?string {
395 if name == '' {
396 return none
397 }
398 mut i := before_idx - 1
399 for i >= 0 {
400 stmt := stmts[i]
401 if stmt is ast.AssignStmt {
402 if stmt.op == .decl_assign && stmt.lhs.len == stmt.rhs.len {
403 for j, lhs in stmt.lhs {
404 if lhs is ast.Ident && lhs.name == name {
405 mut typ := g.get_expr_type(stmt.rhs[j])
406 if typ == '' || typ == 'int' || typ == 'void*' || typ == 'voidptr' {
407 if raw := g.get_raw_type(stmt.rhs[j]) {
408 raw_type := g.types_type_to_c(raw)
409 if raw_type != '' && raw_type != 'void*' && raw_type != 'voidptr' {
410 typ = raw_type
411 }
412 }
413 }
414 if typ != '' && typ != 'int' && typ != 'void*' && typ != 'voidptr' {
415 return typ
416 }
417 }
418 }
419 }
420 }
421 i--
422 }
423 return none
424}
425
426fn (mut g Gen) gen_if_expr_stmt(node &ast.IfExpr) {
427 // Skip empty conditions (pure else blocks shouldn't appear at top level)
428 if node.cond is ast.EmptyExpr {
429 return
430 }
431 if cond_value := const_bool_value(node.cond) {
432 if cond_value {
433 g.gen_scoped_stmts(node.stmts)
434 return
435 }
436 if node.else_expr is ast.IfExpr {
437 else_if := node.else_expr as ast.IfExpr
438 if else_if.cond is ast.EmptyExpr {
439 g.gen_scoped_stmts(else_if.stmts)
440 } else {
441 g.gen_if_expr_stmt(&else_if)
442 }
443 } else if node.else_expr !is ast.EmptyExpr {
444 g.gen_scoped_expr_stmts(node.else_expr)
445 }
446 return
447 }
448 g.sb.write_string('if (')
449 g.expr(node.cond)
450 g.sb.writeln(') {')
451 g.indent++
452 g.gen_scoped_stmts(node.stmts)
453 g.indent--
454 g.write_indent()
455 g.sb.write_string('}')
456 // Handle else / else-if
457 if node.else_expr !is ast.EmptyExpr {
458 if node.else_expr is ast.IfExpr {
459 else_if := node.else_expr as ast.IfExpr
460 if else_if.cond is ast.EmptyExpr {
461 g.sb.writeln(' else {')
462 g.indent++
463 g.gen_scoped_stmts(else_if.stmts)
464 g.indent--
465 g.write_indent()
466 g.sb.write_string('}')
467 } else {
468 g.sb.write_string(' else ')
469 g.gen_if_expr_stmt(&else_if)
470 }
471 } else {
472 g.sb.writeln(' else {')
473 g.indent++
474 g.gen_scoped_expr_stmts(node.else_expr)
475 g.indent--
476 g.write_indent()
477 g.sb.write_string('}')
478 }
479 }
480 g.sb.writeln('')
481}
482
483fn (mut g Gen) gen_if_expr_ternary(node &ast.IfExpr) {
484 if node.cond is ast.EmptyExpr {
485 if node.stmts.len == 1 && node.stmts[0] is ast.ExprStmt {
486 stmt := node.stmts[0] as ast.ExprStmt
487 g.expr(stmt.expr)
488 } else {
489 g.sb.write_string('0')
490 }
491 return
492 }
493 g.sb.write_string('(')
494 g.expr(node.cond)
495 g.sb.write_string(' ? ')
496 if node.stmts.len == 1 && node.stmts[0] is ast.ExprStmt {
497 stmt := node.stmts[0] as ast.ExprStmt
498 if nested := g.extract_if_expr(stmt.expr) {
499 g.gen_if_expr_value(&nested)
500 } else {
501 g.expr(stmt.expr)
502 }
503 } else {
504 g.sb.write_string('0')
505 }
506 g.sb.write_string(' : ')
507 if node.else_expr is ast.IfExpr {
508 else_if := node.else_expr as ast.IfExpr
509 g.gen_if_expr_value(&else_if)
510 } else if node.else_expr is ast.EmptyExpr {
511 g.sb.write_string('0')
512 } else {
513 if nested := g.extract_if_expr(node.else_expr) {
514 g.gen_if_expr_value(&nested)
515 } else {
516 g.expr(node.else_expr)
517 }
518 }
519 g.sb.write_string(')')
520}
521
522fn (mut g Gen) gen_if_expr_value(node &ast.IfExpr) {
523 mut value_type := g.get_if_expr_type(node)
524 if value_type == '' || value_type == 'void' {
525 g.sb.write_string('({ ')
526 g.gen_if_expr_stmt(node)
527 g.sb.write_string('; 0; })')
528 return
529 }
530 if value_type == 'int_literal' {
531 value_type = 'int'
532 }
533 tmp_name := '_if_expr_t${g.tmp_counter}'
534 g.tmp_counter++
535 g.sb.write_string('({ ${value_type} ${tmp_name} = ${zero_value_for_type(value_type)}; ')
536 g.gen_decl_if_expr(tmp_name, value_type, node)
537 if value_type.starts_with('Array_fixed_') && !value_type.ends_with('*') {
538 _, fixed_len := parse_fixed_array_elem_type(value_type)
539 if fixed_len > 0 {
540 g.sb.write_string(' (${value_type}){')
541 for i in 0 .. fixed_len {
542 if i > 0 {
543 g.sb.write_string(', ')
544 }
545 g.sb.write_string('${tmp_name}[${i}]')
546 }
547 g.sb.write_string('}; })')
548 return
549 }
550 }
551 g.sb.write_string(' ${tmp_name}; })')
552}
553
554fn (mut g Gen) get_if_expr_type(node &ast.IfExpr) string {
555 mut env_type := ''
556 if t := g.get_expr_type_from_env(ast.Expr(*node)) {
557 if t != '' {
558 env_type = t
559 }
560 }
561 mut branch_type := g.branch_result_type(node.stmts)
562 if branch_type == 'int_literal' {
563 branch_type = 'int'
564 }
565 mut else_type := ''
566 if node.else_expr is ast.IfExpr {
567 else_if := node.else_expr as ast.IfExpr
568 else_type = g.get_if_expr_type(&else_if)
569 } else if node.else_expr !is ast.EmptyExpr {
570 if nested := g.extract_if_expr(node.else_expr) {
571 else_type = g.get_if_expr_type(&nested)
572 } else {
573 else_type = g.get_expr_type(node.else_expr)
574 if else_type == '' || else_type == 'int' || else_type == 'void*'
575 || else_type == 'voidptr' {
576 if raw := g.get_raw_type(node.else_expr) {
577 raw_type := g.types_type_to_c(raw)
578 if raw_type != '' && raw_type != 'void*' && raw_type != 'voidptr' {
579 else_type = raw_type
580 }
581 }
582 }
583 }
584 }
585 if else_type == 'int_literal' {
586 else_type = 'int'
587 }
588 if branch_type != '' && else_type != '' && branch_type == else_type {
589 if branch_type != 'int' || env_type == '' || env_type == 'int' || env_type == 'void*'
590 || env_type == 'voidptr' {
591 return branch_type
592 }
593 }
594 if branch_type != '' && branch_type != 'int' {
595 if env_type == '' || env_type == 'int' || branch_type.starts_with('${env_type}_T_') {
596 return branch_type
597 }
598 }
599 if env_type != '' {
600 if g.cur_fn_ret_type.starts_with('${env_type}_T_') {
601 return g.cur_fn_ret_type
602 }
603 return env_type
604 }
605 if else_type != '' {
606 return else_type
607 }
608 return 'int'
609}
610
611// expr_produces_pointer checks if an expression produces a pointer value in C.
612// More comprehensive than expr_is_pointer - handles casts, pointer arithmetic, etc.
613// Used in gen_call_arg to avoid wrapping pointer expressions in compound literals.
614