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