v2 / vlib / v / gen / c / comptime.v
1776 lines · 1716 sloc · 59.03 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 os
7import v.ast
8import v.util
9import v.type_resolver
10
11fn (g &Gen) veb_context_html_arg() string {
12 if g.fn_decl.params.len < 2 {
13 return 'ctx'
14 }
15 ctx_param := g.fn_decl.params[1]
16 ctx_sym := g.table.final_sym(ctx_param.typ)
17 if ctx_sym.name == 'veb.Context' {
18 return ctx_param.name
19 }
20 if ctx_sym.info is ast.Struct {
21 for embed in ctx_sym.info.embeds {
22 embed_sym := g.table.sym(embed)
23 if embed_sym.name == 'veb.Context' {
24 dot := if ctx_param.typ.is_ptr() { '->' } else { '.' }
25 return '&${ctx_param.name}${dot}${embed_sym.embed_name()}'
26 }
27 }
28 }
29 return ctx_param.name
30}
31
32fn (mut g Gen) is_string_array_type(typ ast.Type) bool {
33 final_typ := g.table.unaliased_type(g.unwrap_generic(typ))
34 sym := g.table.final_sym(final_typ)
35 if sym.info is ast.Array {
36 return g.table.unaliased_type(sym.info.elem_type) == ast.string_type
37 }
38 return false
39}
40
41fn (mut g Gen) comptime_call_expands_string_args(m &ast.Fn, node ast.ComptimeCall) bool {
42 if node.args.len == 0 || node.args.last().expr !is ast.ArrayDecompose {
43 return false
44 }
45 array_decompose := node.args.last().expr as ast.ArrayDecompose
46 mut array_type := g.resolved_expr_type(array_decompose.expr, array_decompose.expr_type)
47 if array_type == ast.void_type {
48 array_type = array_decompose.expr_type
49 }
50 if !g.is_string_array_type(array_type) || m.params.len - 1 < node.args.len {
51 return false
52 }
53 return !g.is_string_array_type(m.params[node.args.len].typ)
54}
55
56fn (mut g Gen) comptime_zero_value(typ ast.Type) string {
57 resolved_type := g.unwrap_generic(g.recheck_concrete_type(typ))
58 styp := g.styp(resolved_type)
59 mut default_value := g.type_default(resolved_type)
60 if default_value.len > 0 && default_value[0] == `{` {
61 default_value = '(${styp})${default_value}'
62 }
63 return default_value
64}
65
66fn (mut g Gen) comptime_type_expr_type(expr ast.Expr, fallback_type ast.Type) ast.Type {
67 match expr {
68 ast.ParExpr {
69 return g.comptime_type_expr_type(expr.expr, fallback_type)
70 }
71 ast.TypeNode {
72 return g.unwrap_generic(g.recheck_concrete_type(expr.typ))
73 }
74 ast.TypeOf {
75 if expr.is_type {
76 return g.unwrap_generic(g.recheck_concrete_type(expr.typ))
77 }
78 mut default_type := g.get_type(expr.typ)
79 default_type = g.resolve_typeof_expr_type(expr.expr, default_type)
80 if expr.expr is ast.Ident && expr.expr.obj is ast.Var {
81 resolved := g.comptime_typeof_generic_ident_type(expr.expr)
82 if resolved != 0 {
83 return resolved
84 }
85 }
86 if g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0 {
87 resolved := g.resolve_typeof_in_generic(expr)
88 if resolved != 0 {
89 default_type = resolved
90 }
91 }
92 return g.resolved_typeof_name_type(expr, default_type)
93 }
94 ast.ArrayInit {
95 if expr.elem_type_expr !is ast.EmptyExpr {
96 elem_type := g.comptime_type_expr_type(expr.elem_type_expr, expr.elem_type)
97 if elem_type != 0 && elem_type != ast.void_type && elem_type != ast.no_type {
98 if expr.is_fixed {
99 sym := g.table.final_sym(expr.typ)
100 if sym.info is ast.ArrayFixed {
101 return ast.new_type(g.table.find_or_register_array_fixed(elem_type,
102 sym.info.size, sym.info.size_expr, sym.info.is_fn_ret))
103 }
104 }
105 return ast.new_type(g.table.find_or_register_array(elem_type))
106 }
107 }
108 return g.unwrap_generic(g.recheck_concrete_type(expr.typ))
109 }
110 ast.SelectorExpr {
111 return g.comptime_selector_type_expr_type(expr, fallback_type)
112 }
113 ast.Ident {
114 if g.comptime.inside_comptime_for && expr.obj is ast.Var
115 && expr.obj.ct_type_var != .no_comptime {
116 typ := g.type_resolver.get_type_from_comptime_var(expr)
117 if typ != 0 && typ != ast.void_type {
118 return g.unwrap_generic(g.recheck_concrete_type(typ))
119 }
120 }
121 resolved_generic_type := g.comptime_generic_type_expr_ident_type(expr.name)
122 if resolved_generic_type != 0 {
123 return resolved_generic_type
124 }
125 if expr.name in g.type_resolver.type_map {
126 return g.unwrap_generic(g.recheck_concrete_type(g.type_resolver.get_ct_type_or_default(expr.name,
127 fallback_type)))
128 }
129 if util.is_generic_type_name(expr.name) && g.cur_fn != unsafe { nil } {
130 return g.unwrap_generic(g.recheck_concrete_type(g.table.find_type(expr.name).set_flag(.generic)))
131 }
132 return g.resolved_expr_type(expr, fallback_type)
133 }
134 else {
135 return g.resolved_expr_type(expr, fallback_type)
136 }
137 }
138}
139
140fn (mut g Gen) comptime_generic_type_expr_ident_type(name string) ast.Type {
141 generic_names, concrete_types := g.comptime_current_generic_context()
142 if generic_names.len > 0 && generic_names.len == concrete_types.len {
143 idx := generic_names.index(name)
144 if idx >= 0 && idx < concrete_types.len {
145 return g.unwrap_generic(g.recheck_concrete_type(concrete_types[idx]))
146 }
147 }
148 if g.has_active_call_generic_context() {
149 idx := g.active_call_generic_names.index(name)
150 if idx >= 0 && idx < g.active_call_concrete_types.len {
151 return g.unwrap_generic(g.recheck_concrete_type(g.active_call_concrete_types[idx]))
152 }
153 }
154 return 0
155}
156
157fn (mut g Gen) comptime_current_generic_context() ([]string, []ast.Type) {
158 if g.cur_fn == unsafe { nil } || g.cur_concrete_types.len == 0 {
159 return []string{}, []ast.Type{}
160 }
161 mut generic_names := g.current_fn_generic_names()
162 mut concrete_types := g.cur_concrete_types.clone()
163 if generic_names.len == 0 || generic_names.len != concrete_types.len {
164 recovered_generic_names, recovered_concrete_types :=
165 g.recover_specialized_generic_context_for(g.cur_fn.name)
166 if recovered_generic_names.len > 0
167 && recovered_generic_names.len == recovered_concrete_types.len {
168 generic_names = recovered_generic_names.clone()
169 concrete_types = recovered_concrete_types.clone()
170 }
171 }
172 if generic_names.len == 0 || generic_names.len != concrete_types.len {
173 return []string{}, []ast.Type{}
174 }
175 return generic_names, concrete_types
176}
177
178fn (mut g Gen) comptime_typeof_generic_ident_type(ident ast.Ident) ast.Type {
179 if ident.obj !is ast.Var {
180 return 0
181 }
182 var := ident.obj as ast.Var
183 if var.generic_typ == 0 {
184 return 0
185 }
186 generic_names, concrete_types := g.comptime_current_generic_context()
187 if generic_names.len == 0 || generic_names.len != concrete_types.len {
188 return 0
189 }
190 mut muttable := unsafe { &ast.Table(g.table) }
191 if resolved := muttable.convert_generic_type(var.generic_typ, generic_names, concrete_types) {
192 return g.unwrap_generic(g.recheck_concrete_type(resolved))
193 }
194 unwrapped :=
195 muttable.unwrap_generic_type_ex(var.generic_typ, generic_names, concrete_types, true)
196 if unwrapped != var.generic_typ {
197 return g.unwrap_generic(g.recheck_concrete_type(unwrapped))
198 }
199 return 0
200}
201
202fn (mut g Gen) comptime_selector_type_expr_type(expr ast.SelectorExpr, fallback_type ast.Type) ast.Type {
203 if expr.expr is ast.Ident && g.comptime.inside_comptime_for
204 && expr.field_name in ['typ', 'unaliased_typ', 'indirections', 'pointee_type', 'payload_type', 'variant_types'] {
205 ident := expr.expr as ast.Ident
206 if ident.name == g.comptime.comptime_for_field_var
207 || ident.name == g.comptime.comptime_for_variant_var
208 || ident.name == g.comptime.comptime_for_method_param_var {
209 typ := g.type_resolver.get_type_from_comptime_var(ident)
210 if expr.field_name == 'unaliased_typ' {
211 return g.table.unaliased_type(typ)
212 }
213 if expr.field_name in ['pointee_type', 'payload_type', 'variant_types'] {
214 resolved := g.type_resolver.typeof_field_type(typ, expr.field_name)
215 if resolved != ast.no_type {
216 return g.unwrap_generic(g.recheck_concrete_type(resolved))
217 }
218 }
219 return g.unwrap_generic(g.recheck_concrete_type(typ))
220 }
221 }
222 if expr.field_name in ['typ', 'unaliased_typ', 'key_type', 'value_type', 'element_type',
223 'pointee_type', 'payload_type', 'variant_types'] {
224 mut base_type := g.comptime_type_expr_type(expr.expr, expr.name_type)
225 if (base_type == 0 || base_type == ast.void_type || base_type == ast.no_type)
226 && expr.name_type != 0 {
227 base_type = expr.name_type
228 }
229 if expr.field_name == 'unaliased_typ' {
230 return g.table.unaliased_type(g.unwrap_generic(base_type))
231 }
232 resolved := g.type_resolver.typeof_field_type(base_type, expr.field_name)
233 if resolved != ast.no_type {
234 return g.unwrap_generic(g.recheck_concrete_type(resolved))
235 }
236 }
237 return g.resolved_expr_type(expr, fallback_type)
238}
239
240fn (mut g Gen) comptime_zero_new_result_type(node ast.ComptimeCall, fallback_type ast.Type) ast.Type {
241 if node.kind !in [.zero, .new] || node.args.len == 0 {
242 return fallback_type
243 }
244 arg_fallback_type := if node.kind == .new && fallback_type.is_ptr() {
245 fallback_type.deref()
246 } else {
247 fallback_type
248 }
249 mut resolved_type := g.comptime_type_expr_type(node.args[0].expr, arg_fallback_type)
250 if resolved_type == 0 || resolved_type == ast.void_type || resolved_type == ast.no_type {
251 resolved_type = arg_fallback_type
252 }
253 resolved_type = g.unwrap_generic(g.recheck_concrete_type(resolved_type))
254 return if node.kind == .new { resolved_type.ref() } else { resolved_type }
255}
256
257fn (mut g Gen) comptime_selector(node ast.ComptimeSelector) {
258 left_type := g.resolved_expr_type(node.left, node.left_type)
259 if node.is_method && g.comptime.comptime_for_method != unsafe { nil } {
260 g.selector_expr(ast.SelectorExpr{
261 pos: node.pos
262 expr: node.left
263 expr_type: left_type
264 typ: g.type_resolver.get_type(node)
265 field_name: g.comptime.comptime_for_method.name
266 has_hidden_receiver: true
267 })
268 return
269 }
270 is_interface_field := g.table.sym(left_type).kind == .interface
271 if is_interface_field {
272 g.write('*(')
273 }
274 g.expr(node.left)
275 is_auto_heap_ident := node.left is ast.Ident && g.resolved_ident_is_auto_heap(node.left)
276 // When `g.expr` writes an auto-heap ident, it emits `(*(name))` by default,
277 // but skips the deref when it's an LHS / inside a selector LHS / assign-fn-var.
278 // In the deref-skipped case the C result is a pointer, so we need `->`.
279 auto_heap_no_deref := is_auto_heap_ident
280 && (g.is_assign_lhs || g.inside_selector_lhs || g.inside_assign_fn_var)
281 if g.unwrap_generic(left_type).is_ptr() || auto_heap_no_deref {
282 g.write('->')
283 } else {
284 g.write('.')
285 }
286 // check for field.name
287 if node.is_name && node.field_expr is ast.SelectorExpr {
288 if node.field_expr.expr is ast.Ident {
289 if node.field_expr.expr.name == g.comptime.comptime_for_field_var {
290 _, field_name := g.resolve_comptime_selector_field(node, left_type)
291 g.write(c_name(field_name))
292 if is_interface_field {
293 g.write(')')
294 }
295 return
296 }
297 }
298 }
299 g.expr(node.field_expr)
300 if is_interface_field {
301 g.write(')')
302 }
303}
304
305fn (mut g Gen) gen_comptime_selector(expr ast.ComptimeSelector) string {
306 left_type := g.resolved_expr_type(expr.left, expr.left_type)
307 is_auto_heap_ident := expr.left is ast.Ident && g.resolved_ident_is_auto_heap(expr.left)
308 auto_heap_no_deref := is_auto_heap_ident
309 && (g.is_assign_lhs || g.inside_selector_lhs || g.inside_assign_fn_var)
310 arrow_or_dot := if left_type.is_ptr() || auto_heap_no_deref { '->' } else { '.' }
311 mut field_name := if expr.typ_key.contains('|') {
312 expr.typ_key.all_after('|')
313 } else {
314 g.comptime.comptime_for_field_value.name
315 }
316 if expr.field_expr is ast.SelectorExpr {
317 if expr.field_expr.expr is ast.Ident
318 && expr.field_expr.expr.name == g.comptime.comptime_for_field_var {
319 field_name = g.comptime.comptime_for_field_value.name
320 }
321 }
322 return '${expr.left.str()}${arrow_or_dot}${field_name}'
323}
324
325fn (mut g Gen) resolve_comptime_selector_field(node ast.ComptimeSelector, left_type ast.Type) (ast.StructField, string) {
326 mut field_name := if node.typ_key.contains('|') {
327 node.typ_key.all_after('|')
328 } else {
329 g.comptime.comptime_for_field_value.name
330 }
331 if node.field_expr is ast.SelectorExpr && g.comptime.comptime_for_field_var != ''
332 && g.comptime.comptime_for_method_var == '' && node.field_expr.field_name == 'name' {
333 if node.field_expr.expr is ast.Ident
334 && node.field_expr.expr.name == g.comptime.comptime_for_field_var {
335 field_name = g.comptime.comptime_for_field_value.name
336 }
337 }
338 resolved_left_type := g.unwrap_generic(g.recheck_concrete_type(left_type))
339 left_sym := g.table.sym(resolved_left_type)
340 field := g.table.find_field_with_embeds(left_sym, field_name) or {
341 g.error('`${node.left}` has no field named `${field_name}`', node.left.pos())
342 }
343 return field, field_name
344}
345
346fn (mut g Gen) comptime_call(mut node ast.ComptimeCall) {
347 if node.kind == .compile_error || node.kind == .compile_warn {
348 // handled by checker, this branch was not taken
349 return
350 }
351 if node.kind == .embed_file {
352 // $embed_file('/path/to/file')
353 g.gen_embed_file_init(mut node)
354 return
355 }
356 if node.kind == .env {
357 // $env('ENV_VAR_NAME')
358 // TODO: deprecate after support for $d() is stable
359 val := util.cescaped_path(os.getenv(node.args_var))
360 g.write('_S("${val}")')
361 return
362 }
363 if node.kind == .d {
364 // $d('some_string',<default value>), affected by `-d some_string=actual_value`
365 val := util.cescaped_path(node.compile_value)
366 if node.result_type == ast.string_type {
367 g.write('_S("${val}")')
368 } else if node.result_type == ast.char_type {
369 g.write("'${val}'")
370 } else {
371 g.write('${val}')
372 }
373 return
374 }
375 if node.kind in [.zero, .new] {
376 result_type := g.comptime_zero_new_result_type(node, node.result_type)
377 resolved_type := if node.kind == .new { result_type.deref() } else { result_type }
378 default_value := g.comptime_zero_value(resolved_type)
379 if node.kind == .new {
380 g.write('HEAP(${g.styp(resolved_type)}, (${default_value}))')
381 } else {
382 g.write(default_value)
383 }
384 return
385 }
386 if node.kind == .res {
387 if node.args_var != '' {
388 g.write('${g.defer_return_tmp_var}.arg${node.args_var}')
389 return
390 }
391
392 g.write('${g.defer_return_tmp_var}')
393 return
394 }
395 if node.is_template {
396 is_html := node.kind == .html
397 mut cur_line := ''
398
399 if !is_html {
400 cur_line = g.go_before_last_stmt()
401 }
402
403 fn_name := g.fn_decl.name.replace('.', '__').to_lower() + node.pos.pos.str()
404
405 for stmt in node.veb_tmpl.stmts {
406 if stmt is ast.FnDecl {
407 if stmt.name.starts_with('main.veb_tmpl') {
408 prev_inside_veb_tmpl := g.inside_veb_tmpl
409 prev_veb_filter_fn_name := g.veb_filter_fn_name
410 if is_html {
411 g.inside_veb_tmpl = true
412 g.veb_filter_fn_name = 'veb__filter'
413 }
414 // insert stmts from veb_tmpl fn
415 g.stmts(stmt.stmts.filter(it !is ast.Return))
416 //
417 if is_html {
418 g.inside_veb_tmpl = prev_inside_veb_tmpl
419 g.veb_filter_fn_name = prev_veb_filter_fn_name
420 }
421 break
422 }
423 }
424 }
425
426 if is_html {
427 g.writeln('veb__Context_html(${g.veb_context_html_arg()}, _tmpl_res_${fn_name});')
428 g.writeln('strings__Builder_free(&sb_${fn_name});')
429 g.writeln('builtin__string_free(&_tmpl_res_${fn_name});')
430 } else {
431 // return $tmpl string
432 if g.inside_return_tmpl {
433 g.writeln('return _tmpl_res_${fn_name};')
434 } else {
435 g.write(cur_line)
436 g.write('_tmpl_res_${fn_name}')
437 }
438 }
439 return
440 }
441 mut left_type := g.resolved_expr_type(node.left, node.left_type)
442 if left_type == 0 {
443 left_type = node.left_type
444 }
445 left_type = g.unwrap_generic(g.recheck_concrete_type(left_type))
446 sym := g.table.sym(left_type)
447 g.trace_autofree('// \$method call. sym="${sym.name}"')
448 if node.method_name == 'method' {
449 // `app.$method()`
450 m := g.table.find_method(sym, g.comptime.comptime_for_method.name) or { return }
451 /*
452 vals := m.attrs[0].split('/')
453 args := vals.filter(it.starts_with(':')).map(it[1..])
454 println(vals)
455 for val in vals {
456 }
457 */
458 if g.inside_call && m.return_type == ast.void_type {
459 g.error('method `${m.name}()` (no value) used as value', node.pos)
460 }
461 expand_strs := g.comptime_call_expands_string_args(m, node)
462 mut has_decompose := !m.is_variadic && node.args.any(it.expr is ast.ArrayDecompose)
463 && !expand_strs
464 // check argument length and types
465 if m.params.len - 1 != node.args.len && !expand_strs {
466 if g.inside_call {
467 g.error('expected ${m.params.len - 1} arguments to method ${sym.name}.${m.name}, but got ${node.args.len}',
468 node.pos)
469 } else {
470 if !has_decompose {
471 // do not generate anything if the argument lengths don't match
472 g.writeln('/* skipping ${sym.name}.${m.name} due to mismatched arguments list: node.args=${node.args.len} m.params=${m.params.len} */')
473 // Adding a println(_S(...)) like this breaks options
474 return
475 }
476 }
477 }
478
479 mut has_unwrap := false
480 if !g.inside_call && node.or_block.kind != .block && m.return_type.has_option_or_result() {
481 if !(g.assign_ct_type[node.pos.pos] != 0
482 && g.assign_ct_type[node.pos.pos].has_option_or_result()) {
483 g.write('(*(${g.base_type(m.return_type)}*)')
484 has_unwrap = true
485 }
486 }
487 // TODO: check argument types
488 // Use the declaring receiver type for the symbol prefix, so alias
489 // comptime calls can dispatch to inherited methods.
490 method_receiver_type := g.unwrap_generic(m.params[0].typ)
491 g.write('${g.cc_type(method_receiver_type, false)}_${g.comptime.comptime_for_method.name}(')
492
493 // try to see if we need to pass a pointer
494 if mut node.left is ast.Ident {
495 if mut node.left.obj is ast.Var {
496 if m.params[0].typ.is_ptr() && !node.left.obj.typ.is_ptr()
497 && !node.left.obj.is_auto_deref {
498 g.write('&')
499 } else if !m.params[0].typ.is_ptr() && node.left.obj.typ.is_ptr() {
500 g.write('*'.repeat(node.left.obj.typ.nr_muls()))
501 } else if !m.params[0].typ.is_ptr() && node.left.obj.is_auto_deref {
502 g.write('*')
503 }
504 }
505 }
506 g.expr(node.left)
507 if m.params.len > 1 {
508 g.write(', ')
509 }
510 for i in 1 .. m.params.len {
511 if mut node.left is ast.Ident {
512 if m.params[i].name == node.left.name {
513 continue
514 }
515 }
516 if i - 1 <= node.args.len - 1 && has_decompose
517 && node.args[i - 1].expr is ast.ArrayDecompose {
518 mut d_count := 0
519 for d_i in i .. m.params.len {
520 g.write('*(${g.styp(m.params[d_i].typ)}*)builtin__array_get(')
521 g.expr(ast.Expr(node.args[i - 1].expr))
522 g.write(', ${d_count})')
523
524 if d_i < m.params.len - 1 {
525 g.write(', ')
526 }
527 d_count++
528 }
529 break
530 } else if i - 1 < node.args.len - 1 {
531 g.expr(node.args[i - 1].expr)
532 if i < m.params.len - 1 {
533 g.write(', ')
534 }
535 } else if !expand_strs && i == node.args.len {
536 g.expr(node.args[i - 1].expr)
537 break
538 } else {
539 // last argument; try to expand if it's []string
540 idx := i - node.args.len
541 last_arg := g.expr_string(node.args.last().expr)
542 // t := g.table.sym(m.params[i].typ)
543 // g.write('/*nr_args=${node.args.len} m.params.len=${m.params.len} i=${i} t=${t.name} ${m.params}*/')
544 if m.params[i].typ.is_int() || m.params[i].typ.is_float()
545 || m.params[i].typ.idx() == ast.bool_type_idx {
546 // Gets the type name and cast the string to the type with the string_<type> function
547 type_name := g.table.type_symbols[int(m.params[i].typ)].str()
548 g.write('builtin__string_${type_name}(((string*)${last_arg}.data) [${idx}])')
549 } else {
550 g.write('((string*)${last_arg}.data) [${idx}] ')
551 }
552 if i < m.params.len - 1 {
553 g.write(', ')
554 }
555 }
556 }
557 g.write(')')
558 if has_unwrap {
559 g.write('.data)')
560 }
561 if node.or_block.kind != .absent && m.return_type.has_option_or_result() {
562 if !g.inside_assign {
563 cur_line := g.go_before_last_stmt()
564 tmp_var := g.new_tmp_var()
565 g.write2('${g.styp(m.return_type)} ${tmp_var} = ', cur_line)
566 g.or_block(tmp_var, node.or_block, m.return_type)
567 }
568 }
569 return
570 }
571 mut j := 0
572 for method in sym.methods {
573 // if method.return_type != ast.void_type {
574 if method.return_type != node.result_type {
575 continue
576 }
577 if method.params.len != 1 {
578 continue
579 }
580 // receiver := method.args[0]
581 // if !p.expr_var.ptr {
582 // p.error('`${p.expr_var.name}` needs to be a reference')
583 // }
584 amp := '' // if receiver.is_mut && !p.expr_var.ptr { '&' } else { '' }
585 if node.is_template {
586 if j > 0 {
587 g.write(' else ')
588 }
589 g.write('if (builtin__string__eq(${node.method_name}, _S("${method.name}"))) ')
590 }
591 g.write('${g.cc_type(left_type, false)}_${method.name}(${amp} ')
592 g.expr(node.left)
593 g.writeln(');')
594 j++
595 }
596}
597
598fn cgen_attrs(attrs []ast.Attr) []string {
599 mut res := []string{cap: attrs.len}
600 for attr in attrs {
601 mut s := attr.name
602 if attr.has_arg {
603 mut arg := attr.arg
604 if attr.kind == .string {
605 quote := if attr.quote == `"` { '"' } else { "'" }
606 arg = '${quote}${arg}${quote}'
607 }
608 s += ': ${arg}'
609 }
610 res << '_S("${cescape_nonascii(util.smart_quote(s, false))}")'
611 }
612 return res
613}
614
615fn cgen_vattrs(attrs []ast.Attr) []string {
616 mut res := []string{cap: attrs.len}
617 for attr in attrs {
618 name := cescape_nonascii(util.smart_quote(attr.name, false))
619 arg := cescape_nonascii(util.smart_quote(attr.arg, false))
620 res << '((VAttribute){.name=_S("${name}"),.has_arg=${attr.has_arg},.arg=_S("${arg}"),.kind=AttributeKind__${attr.kind}})'
621 }
622 return res
623}
624
625fn (mut g Gen) comptime_at(node ast.AtExpr) {
626 if node.kind == .vmod_file {
627 val := cescape_nonascii(util.smart_quote(node.val, false))
628 g.write('_S("${val}")')
629 } else {
630 val := node.val.replace('\\', '\\\\')
631 g.write('_S("${val}")')
632 }
633}
634
635// gen_branch_context_string generate current branches context string.
636// context include generic types, `$for`.
637fn (mut g Gen) recover_specialized_generic_context_for(fn_name string) ([]string, []ast.Type) {
638 if fn_name == '' {
639 return []string{}, []ast.Type{}
640 }
641 if t_idx := fn_name.index('_T_') {
642 if t_idx <= 0 {
643 return []string{}, []ast.Type{}
644 }
645 } else {
646 return []string{}, []ast.Type{}
647 }
648 for generic_fn in g.file.generic_fns {
649 if generic_fn.generic_names.len == 0 {
650 // Methods on generic structs may have no explicit generic_names
651 // but still have receiver generics.
652 if !(generic_fn.is_method && generic_fn.receiver.typ.has_flag(.generic)) {
653 continue
654 }
655 }
656 for base_name in [generic_fn.name, generic_fn.fkey()] {
657 if !fn_name.starts_with(base_name + '_T_') {
658 continue
659 }
660 mut generic_names := generic_fn.generic_names.clone()
661 fkey := generic_fn.fkey()
662 for concrete_types in g.table.fn_generic_types[fkey] {
663 if concrete_types.any(it.has_flag(.generic)) {
664 continue
665 }
666 if g.generic_fn_name(concrete_types, base_name) != fn_name {
667 if !generic_fn.is_method {
668 continue
669 }
670 receiver_generic_names := g.table.generic_type_names(generic_fn.receiver.typ)
671 if receiver_generic_names.len == 0
672 || concrete_types.len <= receiver_generic_names.len {
673 continue
674 }
675 method_only_concrete_types := concrete_types[receiver_generic_names.len..]
676 if g.generic_fn_name(method_only_concrete_types, base_name) != fn_name {
677 continue
678 }
679 }
680 if generic_fn.is_method && concrete_types.len > generic_names.len {
681 receiver_generic_names := g.table.generic_type_names(generic_fn.receiver.typ)
682 if receiver_generic_names.len > 0 {
683 mut effective_generic_names := []string{cap: receiver_generic_names.len +
684 generic_names.len}
685 for name in receiver_generic_names {
686 if name !in effective_generic_names {
687 effective_generic_names << name
688 }
689 }
690 for name in generic_names {
691 if name !in effective_generic_names {
692 effective_generic_names << name
693 }
694 }
695 if effective_generic_names.len == concrete_types.len {
696 generic_names = effective_generic_names.clone()
697 }
698 }
699 }
700 if generic_names.len == concrete_types.len {
701 return generic_names, concrete_types
702 }
703 return []string{}, concrete_types
704 }
705 }
706 }
707 return []string{}, []ast.Type{}
708}
709
710fn (mut g Gen) gen_branch_context_string() string {
711 mut arr := []string{}
712
713 // gen `T=int,X=string`
714 mut generic_names := if g.cur_fn != unsafe { nil } {
715 g.cur_fn.generic_names.clone()
716 } else {
717 []string{}
718 }
719 // For methods on generic structs with no explicit generic params,
720 // extract generic names from the receiver type (mirrors checker's effective_fn_generic_names).
721 if generic_names.len == 0 && g.cur_fn != unsafe { nil } && g.cur_fn.is_method
722 && g.cur_fn.receiver.typ.has_flag(.generic) {
723 generic_names = g.table.generic_type_names(g.cur_fn.receiver.typ)
724 }
725 mut concrete_types := g.cur_concrete_types.clone()
726 if generic_names.len == 0 || generic_names.len != concrete_types.len {
727 recovered_generic_names, recovered_concrete_types := g.recover_specialized_generic_context_for(if g.cur_fn != unsafe { nil } {
728 g.cur_fn.name
729 } else {
730 ''
731 })
732 if recovered_generic_names.len > 0
733 && recovered_generic_names.len == recovered_concrete_types.len {
734 generic_names = recovered_generic_names.clone()
735 concrete_types = recovered_concrete_types.clone()
736 }
737 }
738 if generic_names.len > 0 && generic_names.len == concrete_types.len {
739 for i in 0 .. generic_names.len {
740 arr << generic_names[i] + '=' +
741 util.strip_main_name(g.table.type_to_str(concrete_types[i]))
742 }
743 }
744
745 // gen comptime `$for`
746 if g.comptime.inside_comptime_for {
747 // variants
748 if g.comptime.comptime_for_variant_var.len > 0 {
749 variant := g.table.type_to_str(g.type_resolver.get_ct_type_or_default('${g.comptime.comptime_for_variant_var}.typ',
750 ast.no_type))
751 arr << g.comptime.comptime_for_variant_var + '.typ=' + variant
752 }
753 // fields
754 if g.comptime.comptime_for_field_var.len > 0 {
755 arr << g.comptime.comptime_for_field_var + '.name=' +
756 g.comptime.comptime_for_field_value.name
757 }
758 // values
759 if g.comptime.comptime_for_enum_var.len > 0 {
760 enum_var := g.table.type_to_str(g.type_resolver.get_ct_type_or_default('${g.comptime.comptime_for_enum_var}.typ',
761 ast.void_type))
762 arr << g.comptime.comptime_for_enum_var + '.typ=' + enum_var
763 }
764 // attributes
765 if g.comptime.comptime_for_attr_var.len > 0 {
766 arr << g.comptime.comptime_for_attr_var + '.name=' +
767 g.comptime.comptime_for_attr_value.name
768 }
769 // methods
770 if g.comptime.comptime_for_method_var.len > 0 {
771 arr << g.comptime.comptime_for_method_var + '.name=' +
772 g.comptime.comptime_for_method.name
773 }
774 // args
775 if g.comptime.comptime_for_method_param_var.len > 0 {
776 arg_var := g.table.type_to_str(g.type_resolver.get_ct_type_or_default('${g.comptime.comptime_for_method_param_var}.typ',
777 ast.void_type))
778 arr << g.comptime.comptime_for_method_param_var + '.typ=' + arg_var
779 }
780 }
781 return arr.join(',')
782}
783
784fn (g &Gen) can_preserve_comptime_if_condition_in_c(cond ast.Expr) bool {
785 match cond {
786 ast.BoolLiteral {
787 return true
788 }
789 ast.Ident {
790 // CPU architecture and compiler conditions are safe to preserve as C
791 // preprocessor checks because they are mutually exclusive categories
792 // derived from real compiler intrinsics (see cheaders.v).
793 // This is important for Android builds where one C file is compiled
794 // by the NDK for multiple architectures (arm64, armv7a, x86, x86_64).
795 return cond.name in ast.valid_comptime_if_platforms
796 || cond.name in ast.valid_comptime_if_compilers
797 }
798 ast.ParExpr {
799 return g.can_preserve_comptime_if_condition_in_c(cond.expr)
800 }
801 ast.PrefixExpr {
802 return cond.op == .not && g.can_preserve_comptime_if_condition_in_c(cond.right)
803 }
804 ast.InfixExpr {
805 return cond.op in [.and, .logical_or]
806 && g.can_preserve_comptime_if_condition_in_c(cond.left)
807 && g.can_preserve_comptime_if_condition_in_c(cond.right)
808 }
809 ast.PostfixExpr {
810 return cond.op == .question && cond.expr is ast.Ident
811 }
812 else {
813 return false
814 }
815 }
816}
817
818fn (g &Gen) comptime_if_condition_for_c(cond ast.Expr, result ast.ComptTimeCondResult) string {
819 if g.pref.output_cross_c || g.can_preserve_comptime_if_condition_in_c(cond) {
820 return result.c_str
821 }
822 // For normal C generation, honor the branch result that V already resolved.
823 // Re-evaluating built-in comptime conditions in the C preprocessor can pick
824 // a different branch than the V checker, e.g. `termux` vs `linux`.
825 return if result.val { '1' } else { '0' }
826}
827
828fn (mut g Gen) comptime_if(node ast.IfExpr) {
829 tmp_var := g.new_tmp_var()
830 mut inferred_typ := node.typ
831 if node.is_expr && node.typ == ast.void_type && node.branches.len > 0 {
832 for branch in node.branches {
833 if branch.stmts.len > 0 {
834 last_stmt := branch.stmts.last()
835 if last_stmt is ast.ExprStmt {
836 expr_typ := g.type_resolver.get_type_or_default(last_stmt.expr, last_stmt.typ)
837 if expr_typ != ast.void_type && !expr_typ.has_flag(.generic) {
838 inferred_typ = expr_typ
839 break
840 }
841 }
842 }
843 }
844 }
845 is_opt_or_result := inferred_typ.has_option_or_result()
846 is_array_fixed := g.table.final_sym(inferred_typ).kind == .array_fixed
847 line := if node.is_expr && inferred_typ != ast.void_type {
848 stmt_str := g.go_before_last_stmt()
849 g.write(util.tabs(g.indent))
850 styp := g.styp(inferred_typ)
851 g.writeln('${styp} ${tmp_var};')
852 stmt_str
853 } else {
854 ''
855 }
856
857 // save node for processing hash stmts
858 // we only save the first node, when there is embedded $if
859 old_curr_comptime_node := g.curr_comptime_node
860 if !g.comptime.inside_comptime_if {
861 g.curr_comptime_node = ast.Expr(node)
862 }
863 defer {
864 g.curr_comptime_node = old_curr_comptime_node
865 }
866 mut comptime_branch_context_str := g.gen_branch_context_string()
867 mut is_true := ast.ComptTimeCondResult{}
868 for i, branch in node.branches {
869 g.push_new_comptime_info()
870 g.comptime.inside_comptime_if = true
871 start_pos := g.out.len
872 // `idx_str` is composed of two parts:
873 // The first part represents the current context of the branch statement, `comptime_branch_context_str`, formatted like `T=int,X=string,method.name=json`
874 // The second part is the branch's id.
875 // This format must match what is in `checker`.
876 mut idx_str := comptime_branch_context_str + '|id=${branch.id}|'
877 if g.comptime.inside_comptime_for && g.comptime.comptime_for_field_var != '' {
878 idx_str += '|field_type=${g.comptime.comptime_for_field_type}|'
879 }
880 if comptime_is_true := g.table.comptime_is_true[idx_str] {
881 // `g.table.comptime_is_true` are the branch condition results set by `checker`
882 is_true = comptime_is_true
883 } else {
884 // No checker data found for this key. This can happen when:
885 // 1. The markused walker spuriously registers generic instantiations
886 // that the checker never evaluated (e.g. from dead comptime branches).
887 // 2. Type alias resolution causes key mismatches.
888 // Default all branches to false (#if 0) since the function body
889 // is either dead code or will be generated correctly by another
890 // instantiation with matching types.
891 is_true = ast.ComptTimeCondResult{}
892 }
893 if !node.has_else || i < node.branches.len - 1 {
894 if i == 0 {
895 g.write('#if ')
896 } else {
897 g.write('#elif ')
898 }
899 g.writeln(g.comptime_if_condition_for_c(branch.cond, is_true))
900 $if debug_comptime_branch_context ? {
901 g.writeln('/* ${node.branches[i].cond} | generic=[${comptime_branch_context_str}] */')
902 }
903 } else {
904 g.writeln('#else')
905 }
906 expr_str := g.out.last_n(g.out.len - start_pos).trim_space()
907 if expr_str != '' {
908 if g.defer_ifdef != '' {
909 g.defer_ifdef += '\n' + '\t'.repeat(g.indent + 1)
910 }
911 g.defer_ifdef += expr_str
912 }
913 if node.is_expr {
914 if is_true.val {
915 g.bind_comptime_if_generic_types(branch.cond)
916 len := branch.stmts.len
917 if len > 0 {
918 last := branch.stmts.last() as ast.ExprStmt
919 if len > 1 {
920 g.indent++
921 g.writeln('{')
922 g.stmts(branch.stmts[..len - 1])
923 g.set_current_pos_as_last_stmt_pos()
924 prev_skip_stmt_pos := g.skip_stmt_pos
925 g.skip_stmt_pos = true
926 if is_opt_or_result {
927 tmp_var2 := g.new_tmp_var()
928 g.write('{ ${g.base_type(inferred_typ)} ${tmp_var2} = ')
929 g.stmt(last)
930 g.writeln('builtin___result_ok(&(${g.base_type(inferred_typ)}[]) { ${tmp_var2} }, (_result*)(&${tmp_var}), sizeof(${g.base_type(inferred_typ)}));')
931 g.writeln('}')
932 } else {
933 g.write('\t${tmp_var} = ')
934 g.stmt(last)
935 }
936 g.skip_stmt_pos = prev_skip_stmt_pos
937 g.writeln2(';', '}')
938 g.indent--
939 g.write_defer_stmts(branch.scope, false, branch.pos)
940 } else {
941 g.indent++
942 g.set_current_pos_as_last_stmt_pos()
943 prev_skip_stmt_pos := g.skip_stmt_pos
944 g.skip_stmt_pos = true
945 if is_opt_or_result {
946 tmp_var2 := g.new_tmp_var()
947 base_styp := g.base_type(inferred_typ)
948 g.write('{ ${base_styp} ${tmp_var2} = ')
949 g.stmt(last)
950 g.writeln('builtin___result_ok(&(${base_styp}[]) { ${tmp_var2} }, (_result*)(&${tmp_var}), sizeof(${base_styp}));')
951 g.writeln('}')
952 } else if is_array_fixed {
953 tmp_var2 := g.new_tmp_var()
954 base_styp := g.base_type(inferred_typ)
955 g.write('{ ${base_styp} ${tmp_var2} = ')
956 g.stmt(last)
957 if g.out.last_n(2).contains(';') {
958 g.go_back(2)
959 }
960 g.writeln(';')
961 g.writeln2('memcpy(&${tmp_var}, &${tmp_var2}, sizeof(${base_styp}));',
962 '}')
963 } else {
964 g.write('${tmp_var} = ')
965 g.stmt(last)
966 }
967 g.skip_stmt_pos = prev_skip_stmt_pos
968 g.writeln(';')
969 g.indent--
970 g.write_defer_stmts(branch.scope, false, branch.pos)
971 }
972 }
973 }
974 } else {
975 // Only wrap the contents in {} if we're inside a function, not on the top level scope
976 should_create_scope := unsafe { g.fn_decl != 0 }
977 if should_create_scope {
978 g.writeln('{')
979 }
980 if is_true.val || g.pref.output_cross_c {
981 g.bind_comptime_if_generic_types(branch.cond)
982 g.stmts(branch.stmts)
983 }
984 if should_create_scope {
985 g.write_defer_stmts(branch.scope, false, branch.pos)
986 g.writeln('}')
987 }
988 }
989 g.pop_comptime_info()
990 }
991 g.defer_ifdef = ''
992 g.writeln('#endif')
993 if node.is_expr {
994 g.write('${line}${tmp_var}')
995 }
996}
997
998fn (mut g Gen) bind_comptime_if_generic_types(cond ast.Expr) {
999 match cond {
1000 ast.ParExpr {
1001 g.bind_comptime_if_generic_types(cond.expr)
1002 }
1003 ast.PrefixExpr {
1004 g.bind_comptime_if_generic_types(cond.right)
1005 }
1006 ast.Likely {
1007 g.bind_comptime_if_generic_types(cond.expr)
1008 }
1009 ast.InfixExpr {
1010 match cond.op {
1011 .and, .logical_or {
1012 g.bind_comptime_if_generic_types(cond.left)
1013 g.bind_comptime_if_generic_types(cond.right)
1014 }
1015 .key_is {
1016 if cond.right is ast.TypeNode {
1017 g.type_resolver.bind_matching_generic_type(g.get_expr_type(cond.left),
1018 cond.right.typ)
1019 }
1020 }
1021 .key_in {
1022 if cond.right is ast.ArrayInit {
1023 left_type := g.get_expr_type(cond.left)
1024 for expr in cond.right.exprs {
1025 if expr is ast.TypeNode
1026 && g.type_resolver.bind_matching_generic_type(left_type, expr.typ) {
1027 break
1028 }
1029 }
1030 }
1031 }
1032 else {}
1033 }
1034 }
1035 else {}
1036 }
1037}
1038
1039fn (mut g Gen) get_expr_type(cond ast.Expr) ast.Type {
1040 match cond {
1041 ast.Ident {
1042 if cond.name in g.type_resolver.type_map {
1043 return g.type_resolver.get_ct_type_or_default(cond.name, ast.void_type)
1044 }
1045 return g.unwrap_generic(g.type_resolver.get_type_or_default(cond, cond.obj.typ))
1046 }
1047 ast.TypeNode {
1048 return g.unwrap_generic(cond.typ)
1049 }
1050 ast.SelectorExpr {
1051 if cond.name_type != 0
1052 && cond.field_name in ['key_type', 'value_type', 'element_type', 'pointee_type', 'payload_type', 'variant_types'] {
1053 return g.type_resolver.typeof_field_type(cond.name_type, cond.field_name)
1054 }
1055 if cond.gkind_field == .typ {
1056 return g.unwrap_generic(cond.name_type)
1057 } else if cond.gkind_field == .unaliased_typ {
1058 return g.table.unaliased_type(g.unwrap_generic(cond.name_type))
1059 } else if cond.gkind_field == .indirections {
1060 return ast.int_type
1061 } else {
1062 if cond.expr is ast.TypeOf {
1063 return g.type_resolver.typeof_field_type(g.type_resolver.typeof_type(cond.expr.expr,
1064 cond.name_type), cond.field_name)
1065 }
1066 name := '${cond.expr}.${cond.field_name}'
1067 if name in g.type_resolver.type_map {
1068 return g.type_resolver.get_ct_type_or_default(name, ast.void_type)
1069 } else {
1070 return g.unwrap_generic(cond.typ)
1071 }
1072 }
1073 }
1074 ast.IntegerLiteral {
1075 return ast.int_type
1076 }
1077 ast.BoolLiteral {
1078 return ast.bool_type
1079 }
1080 ast.StringLiteral {
1081 return ast.string_type
1082 }
1083 ast.CharLiteral {
1084 return ast.char_type
1085 }
1086 ast.FloatLiteral {
1087 return ast.f64_type
1088 }
1089 else {
1090 return ast.void_type
1091 }
1092 }
1093}
1094
1095// push_new_comptime_info saves the current comptime information
1096fn (mut g Gen) push_new_comptime_info() {
1097 g.clear_type_resolution_caches()
1098 g.type_resolver.info_stack << type_resolver.ResolverInfo{
1099 saved_type_map: g.type_resolver.type_map.clone()
1100 inside_comptime_for: g.comptime.inside_comptime_for
1101 inside_comptime_if: g.comptime.inside_comptime_if
1102 comptime_for_variant_var: g.comptime.comptime_for_variant_var
1103 comptime_for_field_var: g.comptime.comptime_for_field_var
1104 comptime_for_field_type: g.comptime.comptime_for_field_type
1105 comptime_for_field_value: g.comptime.comptime_for_field_value
1106 comptime_for_enum_var: g.comptime.comptime_for_enum_var
1107 comptime_for_attr_var: g.comptime.comptime_for_attr_var
1108 comptime_for_attr_value: g.comptime.comptime_for_attr_value
1109 comptime_for_method_var: g.comptime.comptime_for_method_var
1110 comptime_for_method: g.comptime.comptime_for_method
1111 comptime_for_method_ret_type: g.comptime.comptime_for_method_ret_type
1112 comptime_loop_id: g.comptime.comptime_loop_id++
1113 }
1114}
1115
1116// pop_comptime_info pops the current comptime information frame
1117fn (mut g Gen) pop_comptime_info() {
1118 old := g.type_resolver.info_stack.pop()
1119 g.type_resolver.type_map = old.saved_type_map.clone()
1120 g.comptime.inside_comptime_for = old.inside_comptime_for
1121 g.comptime.inside_comptime_if = old.inside_comptime_if
1122 g.comptime.comptime_for_variant_var = old.comptime_for_variant_var
1123 g.comptime.comptime_for_field_var = old.comptime_for_field_var
1124 g.comptime.comptime_for_field_type = old.comptime_for_field_type
1125 g.comptime.comptime_for_field_value = old.comptime_for_field_value
1126 g.comptime.comptime_for_enum_var = old.comptime_for_enum_var
1127 g.comptime.comptime_for_attr_var = old.comptime_for_attr_var
1128 g.comptime.comptime_for_attr_value = old.comptime_for_attr_value
1129 g.comptime.comptime_for_method_var = old.comptime_for_method_var
1130 g.comptime.comptime_for_method = old.comptime_for_method
1131 g.comptime.comptime_for_method_ret_type = old.comptime_for_method_ret_type
1132 g.clear_type_resolution_caches()
1133}
1134
1135fn (mut g Gen) eval_comptime_for_if_cond(cond ast.Expr) ?bool {
1136 match cond {
1137 ast.ParExpr {
1138 return g.eval_comptime_for_if_cond(cond.expr)
1139 }
1140 ast.PrefixExpr {
1141 if cond.op == .not {
1142 return !(g.eval_comptime_for_if_cond(cond.right)?)
1143 }
1144 }
1145 ast.InfixExpr {
1146 match cond.op {
1147 .and, .logical_or {
1148 left := g.eval_comptime_for_if_cond(cond.left)?
1149 right := g.eval_comptime_for_if_cond(cond.right)?
1150 return if cond.op == .and { left && right } else { left || right }
1151 }
1152 .key_is, .not_is {
1153 if cond.left is ast.SelectorExpr && cond.right is ast.TypeNode {
1154 if cond.left.field_name == 'return_type' && cond.left.expr is ast.Ident
1155 && cond.left.expr.name == g.comptime.comptime_for_method_var {
1156 left_type :=
1157 g.table.unaliased_type(g.unwrap_generic(g.comptime.comptime_for_method_ret_type))
1158 right_type := g.table.unaliased_type(g.unwrap_generic(cond.right.typ))
1159 is_true := left_type == right_type
1160 return if cond.op == .key_is { is_true } else { !is_true }
1161 }
1162 }
1163 }
1164 .eq, .ne {
1165 if cond.left is ast.SelectorExpr && cond.right is ast.StringLiteral {
1166 if cond.left.field_name == 'name' && cond.left.expr is ast.Ident
1167 && cond.left.expr.name == g.comptime.comptime_for_method_var {
1168 is_true := g.comptime.comptime_for_method.name == cond.right.val
1169 return if cond.op == .eq { is_true } else { !is_true }
1170 }
1171 }
1172 }
1173 else {}
1174 }
1175 }
1176 else {}
1177 }
1178
1179 return none
1180}
1181
1182fn (mut g Gen) comptime_if_has_live_branch(node ast.IfExpr) bool {
1183 if g.pref.output_cross_c || node.has_else {
1184 return true
1185 }
1186 comptime_branch_context_str := g.gen_branch_context_string()
1187 for branch in node.branches {
1188 if evaluated := g.eval_comptime_for_if_cond(branch.cond) {
1189 if evaluated {
1190 return true
1191 }
1192 continue
1193 }
1194 mut idx_str := comptime_branch_context_str + '|id=${branch.id}|'
1195 if g.comptime.inside_comptime_for && g.comptime.comptime_for_field_var != '' {
1196 idx_str += '|field_type=${g.comptime.comptime_for_field_type}|'
1197 }
1198 if is_true := g.table.comptime_is_true[idx_str] {
1199 if is_true.val {
1200 return true
1201 }
1202 } else {
1203 return true
1204 }
1205 }
1206 return false
1207}
1208
1209fn (mut g Gen) comptime_for_iteration_has_live_stmts(stmts []ast.Stmt) bool {
1210 for stmt in stmts {
1211 match stmt {
1212 ast.EmptyStmt, ast.SemicolonStmt {}
1213 ast.ExprStmt {
1214 if stmt.expr is ast.IfExpr && stmt.expr.is_comptime {
1215 if g.comptime_if_has_live_branch(stmt.expr) {
1216 return true
1217 }
1218 } else {
1219 return true
1220 }
1221 }
1222 else {
1223 return true
1224 }
1225 }
1226 }
1227 return false
1228}
1229
1230fn (mut g Gen) comptime_for(node ast.ComptimeFor) {
1231 resolved_typ := if node.expr !is ast.EmptyExpr {
1232 mut expr_typ := g.unwrap_generic(g.recheck_concrete_type(g.resolved_expr_type(node.expr,
1233 node.typ)))
1234 resolved_ct_typ := g.type_resolver.get_type(node.expr)
1235 if resolved_ct_typ != ast.void_type {
1236 expr_typ = g.unwrap_generic(g.recheck_concrete_type(resolved_ct_typ))
1237 }
1238 expr_typ
1239 } else if node.typ != g.field_data_type {
1240 g.unwrap_generic(node.typ)
1241 } else {
1242 g.comptime.comptime_for_field_type
1243 }
1244 // When the resolved type is FieldData, the expression refers to a comptime
1245 // field variable (e.g. `$for f3 in f.fields` where `f` comes from an outer
1246 // `$for f in T.fields`). In that case use the actual field type from the
1247 // outer comptime loop instead of the FieldData descriptor type.
1248 for_typ := if resolved_typ == g.field_data_type {
1249 g.comptime.comptime_for_field_type
1250 } else {
1251 resolved_typ
1252 }
1253 sym := g.table.final_sym(for_typ)
1254 iter_sym_name := if node.kind == .methods { g.table.sym(for_typ).name } else { sym.name }
1255 g.writeln('/* \$for ${node.val_var} in ${iter_sym_name}.${node.kind.str()} */ {')
1256 g.indent++
1257 mut i := 0
1258 old_defer_stmts := g.defer_stmts
1259 if node.kind == .methods {
1260 methods := g.table.get_type_methods(for_typ)
1261 if methods.len > 0 {
1262 g.writeln('FunctionData ${node.val_var} = {0};')
1263 }
1264 typ_veb_result := g.table.find_type('veb.Result')
1265 for method in methods {
1266 g.defer_stmts = old_defer_stmts
1267 g.push_new_comptime_info()
1268 g.comptime.inside_comptime_for = true
1269 // filter veb route methods (non-generic method)
1270 if method.receiver_type != 0 && method.return_type == typ_veb_result {
1271 rec_sym := g.table.sym(method.receiver_type)
1272 if rec_sym.kind == .struct {
1273 if _ := g.table.find_field_with_embeds(rec_sym, 'Context') {
1274 if method.generic_names.len > 0
1275 || (method.params.len > 1 && method.attrs.len == 0) {
1276 g.pop_comptime_info()
1277 continue
1278 }
1279 }
1280 }
1281 }
1282 g.comptime.comptime_for_method = unsafe { &method }
1283 g.comptime.comptime_for_method_var = node.val_var
1284 g.comptime.comptime_for_method_ret_type = method.return_type
1285 if !g.comptime_for_iteration_has_live_stmts(node.stmts) {
1286 g.pop_comptime_info()
1287 continue
1288 }
1289 g.writeln('/* method ${i} : ${method.name} */ {')
1290 g.writeln('\t${node.val_var}.name = _S("${method.name}");')
1291 mlocation := util.cescaped_path(util.path_styled_for_error_messages(method.file))
1292 g.writeln('\t${node.val_var}.location = _S("${mlocation}:${method.name_pos.line_nr + 1}:${method.name_pos.col}");')
1293 if method.attrs.len == 0 {
1294 g.writeln('\t${node.val_var}.attrs = builtin____new_array_with_default(0, 0, sizeof(string), 0);')
1295 g.writeln('\t${node.val_var}.attributes = builtin____new_array_with_default(0, 0, sizeof(VAttribute), 0);')
1296 } else {
1297 attrs := cgen_attrs(method.attrs)
1298 vattrs := cgen_vattrs(method.attrs)
1299 g.writeln(
1300 '\t${node.val_var}.attrs = builtin__new_array_from_c_array(${attrs.len}, ${attrs.len}, sizeof(string), _MOV((string[${attrs.len}]){' +
1301 attrs.join(', ') + '}));\n')
1302 g.writeln(
1303 '\t${node.val_var}.attributes = builtin__new_array_from_c_array(${vattrs.len}, ${vattrs.len}, sizeof(VAttribute), _MOV((VAttribute[${vattrs.len}]){' +
1304 vattrs.join(', ') + '}));\n')
1305 }
1306 if method.params.len < 2 {
1307 // 0 or 1 (the receiver) args
1308 g.writeln('\t${node.val_var}.args = builtin____new_array_with_default(0, 0, sizeof(FunctionParam), 0);')
1309 } else {
1310 len := method.params.len - 1
1311 g.write('\t${node.val_var}.args = builtin__new_array_from_c_array(${len}, ${len}, sizeof(FunctionParam), _MOV((FunctionParam[${len}]){')
1312 // Skip receiver arg
1313 for j, arg in method.params[1..] {
1314 typ := arg.typ.idx()
1315 g.write('{${typ.str()}, _S("${arg.name}")}')
1316 if j < len - 1 {
1317 g.write(', ')
1318 }
1319 g.type_resolver.update_ct_type('${node.val_var}.args[${j}].typ', typ)
1320 }
1321 g.writeln('}));\n')
1322 }
1323 mut sig := 'fn ('
1324 // skip the first (receiver) arg
1325 for j, arg in method.params[1..] {
1326 // TODO: ignore mut/pts in sig for now
1327 typ := arg.typ.set_nr_muls(0)
1328 sig += g.table.sym(typ).name
1329 if j < method.params.len - 2 {
1330 sig += ', '
1331 }
1332 }
1333 sig += ')'
1334 ret_type := g.table.sym(method.return_type).name
1335 if ret_type != 'void' {
1336 sig += ' ${ret_type}'
1337 }
1338 typ := g.table.find_type(sig)
1339
1340 // TODO: type aliases
1341 ret_typ := method.return_type
1342 g.writeln('\t${node.val_var}.typ = ${int(typ)};')
1343 g.writeln('\t${node.val_var}.return_type = ${int(ret_typ.idx())};')
1344
1345 g.type_resolver.update_ct_type('${node.val_var}.return_type', ret_typ)
1346 g.type_resolver.update_ct_type('${node.val_var}.typ', typ)
1347 g.stmts(node.stmts)
1348 g.write_defer_stmts(node.scope, false, node.pos)
1349 i++
1350 g.writeln('}')
1351 g.pop_comptime_info()
1352 }
1353 } else if node.kind == .fields {
1354 if sym.kind in [.struct, .interface] {
1355 fields := match sym.info {
1356 ast.Struct {
1357 sym.info.fields
1358 }
1359 ast.Interface {
1360 sym.info.fields
1361 }
1362 else {
1363 g.error('comptime field lookup is supported only for structs and interfaces, and ${sym.name} is neither',
1364 node.pos)
1365 []ast.StructField{}
1366 }
1367 }
1368
1369 if fields.len > 0 {
1370 g.writeln('\tFieldData ${node.val_var} = {0};')
1371 }
1372 for field in fields {
1373 g.defer_stmts = old_defer_stmts
1374 g.push_new_comptime_info()
1375 g.comptime.inside_comptime_for = true
1376 g.comptime.comptime_for_field_var = node.val_var
1377 g.comptime.comptime_for_field_value = field
1378 resolved_field_typ := g.unwrap_generic(field.typ)
1379 g.comptime.comptime_for_field_type = resolved_field_typ
1380 g.writeln('/* field ${i} : ${field.name} */ {')
1381 g.writeln('\t${node.val_var}.name = _S("${field.name}");')
1382 if field.attrs.len == 0 {
1383 g.writeln('\t${node.val_var}.attrs = builtin____new_array_with_default(0, 0, sizeof(string), 0);')
1384 } else {
1385 attrs := cgen_attrs(field.attrs)
1386 g.writeln(
1387 '\t${node.val_var}.attrs = builtin__new_array_from_c_array(${attrs.len}, ${attrs.len}, sizeof(string), _MOV((string[${attrs.len}]){' +
1388 attrs.join(', ') + '}));\n')
1389 }
1390 field_sym := g.table.sym(resolved_field_typ)
1391 styp := resolved_field_typ
1392 unaliased_styp := g.table.unaliased_type(styp)
1393
1394 g.writeln('\t${node.val_var}.typ = ${int(styp.idx())};\t// ${g.table.type_to_str(styp)}')
1395 g.writeln('\t${node.val_var}.unaliased_typ = ${int(unaliased_styp.idx())};\t// ${g.table.type_to_str(unaliased_styp)}')
1396 g.writeln('\t${node.val_var}.is_pub = ${field.is_pub};')
1397 g.writeln('\t${node.val_var}.is_mut = ${field.is_mut};')
1398 g.writeln('\t${node.val_var}.is_embed = ${field.is_embed};')
1399
1400 g.writeln('\t${node.val_var}.is_shared = ${resolved_field_typ.has_flag(.shared_f)};')
1401 g.writeln('\t${node.val_var}.is_atomic = ${resolved_field_typ.has_flag(.atomic_f)};')
1402 g.writeln('\t${node.val_var}.is_option = ${resolved_field_typ.has_flag(.option)};')
1403
1404 g.writeln('\t${node.val_var}.is_array = ${field_sym.kind in [.array, .array_fixed]};')
1405 g.writeln('\t${node.val_var}.is_map = ${field_sym.kind == .map};')
1406 g.writeln('\t${node.val_var}.is_chan = ${field_sym.kind == .chan};')
1407 g.writeln('\t${node.val_var}.is_struct = ${field_sym.kind == .struct};')
1408 g.writeln('\t${node.val_var}.is_alias = ${field_sym.kind == .alias};')
1409 g.writeln('\t${node.val_var}.is_enum = ${field_sym.kind == .enum};')
1410
1411 g.writeln('\t${node.val_var}.indirections = ${resolved_field_typ.nr_muls()};')
1412
1413 g.type_resolver.update_ct_type('${node.val_var}.typ', resolved_field_typ)
1414 g.type_resolver.update_ct_type('${node.val_var}.unaliased_typ', unaliased_styp)
1415 g.stmts(node.stmts)
1416 g.write_defer_stmts(node.scope, false, node.pos)
1417 i++
1418 g.writeln('}')
1419 g.pop_comptime_info()
1420 }
1421 }
1422 } else if node.kind == .values {
1423 if sym.kind == .enum {
1424 if sym.info is ast.Enum {
1425 if sym.info.vals.len > 0 {
1426 g.writeln('\tEnumData ${node.val_var} = {0};')
1427 }
1428 for val in sym.info.vals {
1429 g.defer_stmts = old_defer_stmts
1430 g.push_new_comptime_info()
1431 g.comptime.inside_comptime_for = true
1432 g.comptime.comptime_for_enum_var = node.val_var
1433 g.type_resolver.update_ct_type('${node.val_var}.typ', node.typ)
1434
1435 g.writeln('/* enum vals ${i} */ {')
1436 g.writeln('\t${node.val_var}.name = _S("${val}");')
1437 g.write('\t${node.val_var}.value = ')
1438 if g.pref.translated && node.typ.is_number() {
1439 g.writeln('_const_main__${val};')
1440 } else {
1441 node_sym := g.table.sym(g.unwrap_generic(node.typ))
1442 if node_sym.info is ast.Alias {
1443 g.writeln('${g.styp(node_sym.info.parent_type)}__${val};')
1444 } else {
1445 g.writeln('${g.styp(node.typ)}__${val};')
1446 }
1447 }
1448 enum_attrs := sym.info.attrs[val]
1449 if enum_attrs.len == 0 {
1450 g.writeln('\t${node.val_var}.attrs = builtin____new_array_with_default(0, 0, sizeof(string), 0);')
1451 } else {
1452 attrs := cgen_attrs(enum_attrs)
1453 g.writeln(
1454 '\t${node.val_var}.attrs = builtin__new_array_from_c_array(${attrs.len}, ${attrs.len}, sizeof(string), _MOV((string[${attrs.len}]){' +
1455 attrs.join(', ') + '}));\n')
1456 }
1457 g.stmts(node.stmts)
1458 g.write_defer_stmts(node.scope, false, node.pos)
1459 g.writeln('}')
1460 i++
1461 g.pop_comptime_info()
1462 }
1463 }
1464 }
1465 } else if node.kind == .attributes {
1466 attrs := g.table.get_attrs(sym)
1467 if attrs.len > 0 {
1468 g.writeln('\tVAttribute ${node.val_var} = {0};')
1469
1470 for attr in attrs {
1471 g.defer_stmts = old_defer_stmts
1472 g.push_new_comptime_info()
1473 g.comptime.inside_comptime_for = true
1474 g.comptime.comptime_for_attr_var = node.val_var
1475 g.comptime.comptime_for_attr_value = attr
1476 g.writeln('/* attribute ${i} : ${attr.name} */ {')
1477 g.writeln('\t${node.val_var}.name = _S("${attr.name}");')
1478 g.writeln('\t${node.val_var}.has_arg = ${attr.has_arg};')
1479 g.writeln('\t${node.val_var}.arg = _S("${util.smart_quote(attr.arg, false)}");')
1480 g.writeln('\t${node.val_var}.kind = AttributeKind__${attr.kind};')
1481 g.stmts(node.stmts)
1482 g.write_defer_stmts(node.scope, false, node.pos)
1483 g.writeln('}')
1484 i++
1485 g.pop_comptime_info()
1486 }
1487 }
1488 } else if node.kind == .variants {
1489 if sym.info is ast.SumType {
1490 if sym.info.variants.len > 0 {
1491 g.writeln('\tVariantData ${node.val_var} = {0};')
1492 }
1493 g.comptime.inside_comptime_for = true
1494 for variant in sym.info.variants {
1495 g.defer_stmts = old_defer_stmts
1496 g.push_new_comptime_info()
1497 g.comptime.inside_comptime_for = true
1498 g.comptime.comptime_for_variant_var = node.val_var
1499 g.type_resolver.update_ct_type('${node.val_var}.typ', variant)
1500
1501 g.writeln('/* variant ${i} : ${g.table.type_to_str(variant)} */ {')
1502 g.writeln('\t${node.val_var}.typ = ${int(variant)};\t// ')
1503 g.stmts(node.stmts)
1504 g.write_defer_stmts(node.scope, false, node.pos)
1505 g.writeln('}')
1506 i++
1507 g.pop_comptime_info()
1508 }
1509 }
1510 } else if node.kind == .params {
1511 func := if sym.info is ast.FnType { &sym.info.func } else { g.comptime.comptime_for_method }
1512 if func.params.len > 0 {
1513 g.writeln('\tFunctionParam ${node.val_var} = {0};')
1514 }
1515 params := if func.is_method { func.params[1..] } else { func.params }
1516 for param in params {
1517 g.defer_stmts = old_defer_stmts
1518 g.push_new_comptime_info()
1519 g.comptime.inside_comptime_for = true
1520 g.comptime.comptime_for_method_param_var = node.val_var
1521 g.type_resolver.update_ct_type('${node.val_var}.typ', param.typ)
1522
1523 g.writeln('/* method param ${i} : ${param.name} */ {')
1524 g.writeln('\t${node.val_var}.typ = ${int(param.typ)};\t// ${g.table.type_to_str(param.typ)}')
1525 g.writeln('\t${node.val_var}.name = _S("${param.name}");')
1526 g.stmts(node.stmts)
1527 g.write_defer_stmts(node.scope, false, node.pos)
1528 g.writeln('}')
1529 i++
1530 g.pop_comptime_info()
1531 }
1532 }
1533 g.defer_stmts = old_defer_stmts
1534 g.indent--
1535 g.writeln('}// \$for')
1536}
1537
1538// comptime_selector_type computes the selector type from an comptime var
1539fn (mut g Gen) comptime_selector_type(node ast.SelectorExpr) ast.Type {
1540 if !(node.expr is ast.Ident && node.expr.ct_expr) {
1541 return node.expr_type
1542 }
1543 prevent_sum_type_unwrapping_once := g.prevent_sum_type_unwrapping_once
1544 g.prevent_sum_type_unwrapping_once = false
1545
1546 mut typ := g.type_resolver.get_type(node.expr)
1547 if node.expr is ast.Ident && node.expr.obj is ast.Var {
1548 ct_var := node.expr.obj.ct_type_var
1549 if ct_var == .generic_param || ct_var == .generic_var {
1550 if scope_var := node.expr.scope.find_var(node.expr.name) {
1551 if scope_var.ct_type_var == ct_var && g.cur_fn != unsafe { nil }
1552 && g.cur_fn.generic_names.len > 0 {
1553 // For generic_param/generic_var, node.obj.typ is a stale
1554 // copy from checker time. Use the refreshed scope var.
1555 typ = scope_var.typ
1556 }
1557 }
1558 }
1559 }
1560 if node.expr.is_auto_deref_var() {
1561 if node.expr is ast.Ident {
1562 if node.expr.obj is ast.Var {
1563 typ = node.expr.obj.typ
1564 }
1565 }
1566 }
1567 if g.comptime.inside_comptime_for && typ == g.enum_data_type && node.field_name == 'value' {
1568 // for comp-time enum.values
1569 return g.type_resolver.get_ct_type_or_default('${g.comptime.comptime_for_enum_var}.typ',
1570 ast.void_type)
1571 }
1572 field_name := node.field_name
1573 sym := g.table.sym(typ)
1574 final_sym := g.table.final_sym(typ)
1575 if (typ.has_flag(.variadic) || final_sym.kind == .array_fixed) && field_name == 'len' {
1576 return ast.int_type
1577 }
1578 if sym.kind == .chan {
1579 if field_name == 'closed' {
1580 return ast.bool_type
1581 } else if field_name in ['len', 'cap'] {
1582 return ast.u32_type
1583 }
1584 }
1585 mut has_field := false
1586 mut field := ast.StructField{}
1587 if field_name.len > 0 && field_name[0].is_capital() && sym.language == .v {
1588 if sym.info is ast.Struct {
1589 // x.Foo.y => access the embedded struct
1590 for embed in sym.info.embeds {
1591 embed_sym := g.table.sym(embed)
1592 if embed_sym.embed_name() == field_name {
1593 return embed
1594 }
1595 }
1596 }
1597 } else {
1598 if f := g.table.find_field(sym, field_name) {
1599 has_field = true
1600 field = f
1601 } else {
1602 // look for embedded field
1603 has_field = true
1604 g.table.find_field_from_embeds(sym, field_name) or { has_field = false }
1605 }
1606 if typ.has_flag(.generic) && !has_field {
1607 gs := g.table.sym(g.unwrap_generic(typ))
1608 if f := g.table.find_field(gs, field_name) {
1609 has_field = true
1610 field = f
1611 } else {
1612 // look for embedded field
1613 has_field = true
1614 g.table.find_field_from_embeds(gs, field_name) or { has_field = false }
1615 }
1616 }
1617 }
1618
1619 if has_field {
1620 field_sym := g.table.sym(field.typ)
1621 if field_sym.kind in [.sum_type, .interface] {
1622 if !prevent_sum_type_unwrapping_once {
1623 scope_field := node.scope.find_struct_field(node.expr.str(), typ, field_name)
1624 if scope_field != unsafe { nil } {
1625 return scope_field.smartcasts.last()
1626 }
1627 }
1628 }
1629 return field.typ
1630 }
1631 if mut method := g.table.sym(g.unwrap_generic(typ)).find_method_with_generic_parent(field_name) {
1632 method.params = method.params[1..]
1633 method.name = ''
1634 fn_type := ast.new_type(g.table.find_or_register_fn_type(method, false, true))
1635 return fn_type
1636 }
1637 if sym.kind !in [.struct, .aggregate, .interface, .sum_type] {
1638 if sym.kind != .placeholder {
1639 unwrapped_sym := g.table.sym(g.unwrap_generic(typ))
1640 if unwrapped_sym.kind == .array_fixed && node.field_name == 'len' {
1641 return ast.int_type
1642 }
1643 }
1644 }
1645 return node.expr_type
1646}
1647
1648fn (mut g Gen) comptime_match(node ast.MatchExpr) {
1649 tmp_var := g.new_tmp_var()
1650 mut inferred_typ := node.return_type
1651 if node.is_expr && (node.return_type == ast.void_type || node.return_type.idx() == 0)
1652 && node.branches.len > 0 {
1653 for branch in node.branches {
1654 if branch.stmts.len > 0 {
1655 last_stmt := branch.stmts.last()
1656 if last_stmt is ast.ExprStmt {
1657 expr_typ := g.type_resolver.get_type_or_default(last_stmt.expr, last_stmt.typ)
1658 if expr_typ != ast.void_type && expr_typ.idx() != 0
1659 && !expr_typ.has_flag(.generic) {
1660 inferred_typ = expr_typ
1661 break
1662 }
1663 }
1664 }
1665 }
1666 }
1667 is_opt_or_result := inferred_typ.has_option_or_result()
1668 line := if node.is_expr && inferred_typ != ast.void_type && inferred_typ.idx() != 0 {
1669 stmt_str := g.go_before_last_stmt()
1670 g.write(util.tabs(g.indent))
1671 styp := g.styp(inferred_typ)
1672 g.writeln('${styp} ${tmp_var};')
1673 stmt_str
1674 } else {
1675 ''
1676 }
1677
1678 mut comptime_branch_context_str := g.gen_branch_context_string()
1679 mut is_true := ast.ComptTimeCondResult{}
1680 for i, branch in node.branches {
1681 // `idx_str` is composed of two parts:
1682 // The first part represents the current context of the branch statement, `comptime_branch_context_str`, formatted like `T=int,X=string,method.name=json`
1683 // The second part is the branch's id.
1684 // This format must match what is in `checker`.
1685 mut idx_str := comptime_branch_context_str + '|id=${branch.id}|'
1686 if g.comptime.inside_comptime_for && g.comptime.comptime_for_field_var != '' {
1687 idx_str += '|field_type=${g.comptime.comptime_for_field_type}|'
1688 }
1689 if comptime_is_true := g.table.comptime_is_true[idx_str] {
1690 // `g.table.comptime_is_true` are the branch condition results set by `checker`
1691 is_true = comptime_is_true
1692 } else {
1693 // No checker data - spurious instantiation or alias mismatch.
1694 // Default all branches to false (#if 0).
1695 is_true = ast.ComptTimeCondResult{}
1696 }
1697 if !branch.is_else {
1698 if i == 0 {
1699 g.write('#if ')
1700 } else {
1701 g.write('#elif ')
1702 }
1703 // directly use `checker` evaluate results
1704 g.writeln('${is_true.val}')
1705 $if debug_comptime_branch_context ? {
1706 g.writeln('/* | generic=[${comptime_branch_context_str}] */')
1707 }
1708 } else {
1709 g.writeln('#else')
1710 }
1711 if node.is_expr && !branch.is_comptime_err {
1712 len := branch.stmts.len
1713 if len > 0 {
1714 last := branch.stmts.last()
1715 if last is ast.ExprStmt {
1716 if len > 1 {
1717 g.indent++
1718 g.writeln('{')
1719 g.stmts(branch.stmts[..len - 1])
1720 g.set_current_pos_as_last_stmt_pos()
1721 prev_skip_stmt_pos := g.skip_stmt_pos
1722 g.skip_stmt_pos = true
1723 if is_opt_or_result {
1724 tmp_var2 := g.new_tmp_var()
1725 g.write('{ ${g.base_type(inferred_typ)} ${tmp_var2} = ')
1726 g.stmt(last)
1727 g.writeln('builtin___result_ok(&(${g.base_type(inferred_typ)}[]) { ${tmp_var2} }, (_result*)(&${tmp_var}), sizeof(${g.base_type(inferred_typ)}));')
1728 g.writeln('}')
1729 } else {
1730 g.write('\t${tmp_var} = ')
1731 g.stmt(last)
1732 }
1733 g.skip_stmt_pos = prev_skip_stmt_pos
1734 g.writeln(';')
1735 g.write_defer_stmts(branch.scope, false, branch.pos)
1736 g.writeln('}')
1737 g.indent--
1738 } else {
1739 g.indent++
1740 g.set_current_pos_as_last_stmt_pos()
1741 prev_skip_stmt_pos := g.skip_stmt_pos
1742 g.skip_stmt_pos = true
1743 if is_opt_or_result {
1744 tmp_var2 := g.new_tmp_var()
1745 g.write('{ ${g.base_type(inferred_typ)} ${tmp_var2} = ')
1746 g.stmt(last)
1747 g.writeln('builtin___result_ok(&(${g.base_type(inferred_typ)}[]) { ${tmp_var2} }, (_result*)(&${tmp_var}), sizeof(${g.base_type(inferred_typ)}));')
1748 g.writeln('}')
1749 } else {
1750 g.write('${tmp_var} = ')
1751 g.stmt(last)
1752 }
1753 g.skip_stmt_pos = prev_skip_stmt_pos
1754 g.writeln(';')
1755 g.write_defer_stmts(branch.scope, false, branch.pos)
1756 g.indent--
1757 }
1758 } else if last is ast.Return {
1759 if last.exprs.len > 0 {
1760 g.write('${tmp_var} = ')
1761 g.expr(last.exprs[0])
1762 g.writeln(';')
1763 g.write_defer_stmts(branch.scope, false, branch.pos)
1764 }
1765 }
1766 }
1767 } else if is_true.val || g.pref.output_cross_c {
1768 g.stmts(branch.stmts)
1769 g.write_defer_stmts(branch.scope, false, branch.pos)
1770 }
1771 }
1772 g.writeln('#endif')
1773 if node.is_expr {
1774 g.write('${line}${tmp_var}')
1775 }
1776}
1777