vxx2 / vlib / v / gen / c / cgen.v
14994 lines · 14553 sloc · 503.04 KB · 1782559716
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 term
8import strings
9import hash.fnv1a
10import v.ast
11import v.pref
12import v.token
13import v.util
14import v.util.version
15import v.depgraph
16import v.type_resolver
17import sync.pool
18
19// Note: some of the words in c_reserved, are not reserved in C, but are
20// in C++, or have special meaning in V
21const c_reserved = ['asm', 'array', 'auto', 'bool', 'break', 'calloc', 'case', 'char', 'class',
22 'complex', 'const', 'continue', 'default', 'delete', 'do', 'double', 'else', 'enum', 'error',
23 'exit', 'export', 'extern', 'false', 'float', 'for', 'free', 'goto', 'if', 'inline', 'int',
24 'long', 'malloc', 'namespace', 'new', 'nil', 'panic', 'register', 'restrict', 'return', 'short',
25 'signed', 'sizeof', 'static', 'string', 'struct', 'switch', 'typedef', 'typename', 'typeof',
26 'union', 'unix', 'unsigned', 'void', 'volatile', 'while', 'template', 'true', 'stdout', 'stdin',
27 'stderr', 'errno', 'environ', 'requires']
28const c_reserved_chk = token.new_keywords_matcher_from_array_trie(c_reserved)
29// same order as in token.Kind
30const cmp_str = ['eq', 'ne', 'gt', 'lt', 'ge', 'le']
31// when operands are switched
32const cmp_rev = ['eq', 'ne', 'lt', 'gt', 'le', 'ge']
33const result_name = ast.result_name
34const option_name = ast.option_name
35const max_c_string_literal_segment_len = 12000
36
37struct ScopeGcPin {
38 post_stmt string
39}
40
41struct ClosureCleanupKeep {
42 cname string
43 loop_pos int
44}
45
46pub struct Gen {
47 pref &pref.Preferences = unsafe { nil }
48 field_data_type ast.Type // cache her to avoid map lookups
49 enum_data_type ast.Type // cache her to avoid map lookups
50 variant_data_type ast.Type // cache her to avoid map lookups
51 module_built string
52 timers_should_print bool
53mut:
54 out strings.Builder
55 extern_out strings.Builder // extern declarations for -parallel-cc
56 // line_nr int
57 cheaders strings.Builder
58 preincludes strings.Builder // allows includes to go before `definitions`
59 postincludes strings.Builder // allows includes to go after all the rest of the code generation
60 includes strings.Builder // all C #includes required by V modules
61 typedefs strings.Builder
62 enum_typedefs strings.Builder // enum types
63 definitions strings.Builder // typedefs, defines etc (everything that goes to the top of the file)
64 type_definitions strings.Builder // typedefs, defines etc (everything that goes to the top of the file)
65 sort_fn_definitions strings.Builder // sort fns
66 alias_definitions strings.Builder // alias fixed array of non-builtin
67 hotcode_definitions strings.Builder // -live declarations & functions
68 channel_definitions strings.Builder // channel related code
69 thread_definitions strings.Builder // thread defines
70 comptime_definitions strings.Builder // custom defines, given by -d/-define flags on the CLI
71 type_default_vars strings.Builder // type_default() var declarations
72 cleanup strings.Builder
73 cleanups map[string]strings.Builder // contents of `void _vcleanup(){}`
74 gowrappers strings.Builder // all go callsite wrappers
75 waiter_fn_definitions strings.Builder // waiter fns definitions
76 auto_str_funcs strings.Builder // function bodies of all auto generated _str funcs
77 dump_funcs strings.Builder // function bodies of all auto generated _str funcs
78 pcs_declarations strings.Builder // -prof profile counter declarations for each function
79 cov_declarations strings.Builder // -cov coverage
80 embedded_data strings.Builder // data to embed in the executable/binary
81 interface_index_definitions strings.Builder // real interface index symbols for -parallel-cc helpers
82 shared_types strings.Builder // shared/lock types
83 shared_functions strings.Builder // shared constructors
84 out_options_forward strings.Builder // forward `option_xxxx` types
85 out_options strings.Builder // `option_xxxx` types
86 out_results_forward strings.Builder // forward`result_xxxx` types
87 out_results strings.Builder // `result_xxxx` types
88 json_forward_decls strings.Builder // json type forward decls
89 sql_buf strings.Builder // for writing exprs to args via `sqlite3_bind_int()` etc
90 global_const_defs map[string]GlobalConstDef
91 vsafe_arithmetic_ops map[string]VSafeArithmeticOp // 'VSAFE_DIV_u8' -> {11, /}, 'VSAFE_MOD_u8' -> {11,%}, 'VSAFE_MOD_i64' -> the same but with 9
92 sorted_global_const_names []string
93 file &ast.File = unsafe { nil }
94 table &ast.Table = unsafe { nil }
95 styp_cache map[ast.Type]string
96 no_eq_method_types map[ast.Type]bool // types that does not need to call its auto eq methods for optimization
97 generic_parts_cache []i8 // type idx -> 0 unknown, 1 false, 2 true
98 unwrap_generic_cache map[u64]ast.Type
99 resolved_scope_var_type_cache map[u64]ast.Type
100 unique_file_path_hash u64 // a hash of file.path, used for making auxiliary fn generation unique (like `compare_xyz`)
101 fn_decl &ast.FnDecl = unsafe { nil } // pointer to the FnDecl we are currently inside otherwise 0
102 last_fn_c_name string
103 tmp_count int // counter for unique tmp vars (_tmp1, _tmp2 etc); resets at the start of each fn.
104 tmp_count_af int // a separate tmp var counter for autofree fn calls
105 tmp_count_declarations int // counter for unique tmp names (_d1, _d2 etc); does NOT reset, used for C declarations
106 global_tmp_count int // like tmp_count but global and not reset in each function
107 discard_or_result bool // do not safe last ExprStmt of `or` block in tmp variable to defer ongoing expr usage
108 is_direct_array_access bool // inside a `[direct_array_access fn a() {}` function
109 is_assign_lhs bool // inside left part of assign expr (for array_set(), etc)
110 is_void_expr_stmt bool // ExprStmt whose result is discarded
111 is_arraymap_set bool // map or array set value state
112 is_amp bool // for `&Foo{}` to merge PrefixExpr `&` and StructInit `Foo{}`; also for `&u8(unsafe { nil })` etc
113 is_sql bool // Inside `sql db{}` statement, generating sql instead of C (e.g. `and` instead of `&&` etc)
114 is_shared bool // for initialization of hidden mutex in `[rw]shared` literals
115 is_vlines_enabled bool // is it safe to generate #line directives when -g is passed
116 is_autofree bool // false, inside the bodies of fns marked with [manualfree], otherwise === g.pref.autofree
117 is_autofree_tmp bool // when generating autofree temporary variables
118 is_builtin_mod bool
119 is_json_fn bool // inside json.encode()
120 is_js_call bool // for handling a special type arg #1 `json.decode(User, ...)`
121 is_fn_index_call bool
122 is_cc_msvc bool // g.pref.ccompiler == 'msvc'
123 is_option_auto_heap bool
124 vlines_path string // set to the proper path for generating #line directives
125 options_pos_forward int // insertion point to forward
126 options_forward []string // to forward
127 options map[string]string // to avoid duplicates
128 emitted_extern_sig_typedefs map[int]bool // type idx → already-emitted forward typedef (for C extern decls)
129 results_forward []string // to forward
130 results map[string]string // to avoid duplicates
131 done_options shared []string // to avoid duplicates
132 done_results shared []string // to avoid duplicates
133 array_typedefs []string // to avoid duplicate array typedefs
134 written_array_typedefs int
135 done_typedef_phase bool // set after write_typedef_types() completes
136 late_chan_types shared []string // concrete channel cnames discovered during file generation
137 emitted_chan_types map[string]bool // concrete channel typedefs/helpers already emitted
138 chan_pop_options map[string]string // types for `x := <-ch or {...}`
139 chan_push_options map[string]string // types for `ch <- x or {...}`
140 mtxs string // array of mutexes if the `lock` has multiple variables
141 tmp_var_ptr map[string]bool // indicates if the tmp var passed to or_block() is a ptr
142 labeled_loops map[string]&ast.Stmt
143 contains_ptr_cache map[ast.Type]bool
144 boehm_keep_gen shared map[string]bool
145 inner_loop &ast.Stmt = unsafe { nil }
146 cur_indexexpr []int // list of nested indexexpr which generates array_set/map_set
147 shareds map[int]string // types with hidden mutex for which decl has been emitted
148 coverage_files map[u64]&CoverageInfo
149 inside_smartcast bool
150 inside_ternary int // ?: comma separated statements on a single line
151 inside_map_postfix bool // inside map++/-- postfix expr
152 inside_map_infix bool // inside map<</+=/-= infix expr
153 wrote_windows_tcc_atomic_include bool
154 inside_left_shift bool // generating the left operand of `<<`
155 inside_assign bool
156 inside_map_index bool
157 inside_array_index bool
158 inside_array_fixed_struct bool
159 inside_opt_or_res bool
160 inside_opt_data bool
161 inside_if_option bool
162 inside_if_result bool
163 inside_match_option bool
164 inside_match_result bool
165 inside_veb_tmpl bool
166 inside_return bool
167 inside_return_expr bool
168 inside_return_tmpl bool
169 inside_struct_init bool
170 inside_or_block bool
171 inside_call bool
172 inside_curry_call bool // inside foo()()!, foo()()?, foo()()
173 inside_dump_fn bool
174 inside_c_extern bool // inside `@[c_extern] fn C.somename(param1 int, param2 voidptr, param3 &char) &char`
175 active_call_generic_names []string
176 active_call_concrete_types []ast.Type
177 expected_fixed_arr bool
178 inside_for_c_stmt bool
179 inside_cast_in_heap int // inside cast to interface type in heap (resolve recursive calls)
180 inside_cast bool
181 inside_interface_cast bool
182 inside_sumtype_cast bool
183 inside_selector bool
184 inside_selector_lhs bool
185 inside_selector_deref bool // indicates if the inside selector was already dereferenced
186 inside_memset bool
187 inside_const bool
188 inside_array_item bool
189 inside_const_opt_or_res bool
190 inside_lambda bool
191 inside_cinit bool
192 inside_global_decl bool
193 inside_interface_deref bool
194 inside_assign_fn_var bool
195 inside_lambda_autofree_tmp bool
196 track_lambda_autofree_tmp_arg_vars bool
197 outer_tmp_var string // tmp var from outer context (e.g. from stmts_with_tmp_var) to be used by nested if/match expressions
198 if_match_tmp_is_fn_ret_arr ?bool // set by if/match expr gen: authoritatively whether the shared tmp var is a fn-returned fixed array (wrapper struct accessed via `.ret_arr`); `none` means use the per-branch heuristic
199 last_tmp_call_var []string
200 last_if_option_type ast.Type // stores the expected if type on nested if expr
201 loop_depth int
202 unsafe_level int
203 ternary_names map[string]string
204 ternary_level_names map[string][]string
205 arraymap_set_pos int // map or array set value position
206 stmt_path_pos []int // positions of each statement start, for inserting C statements before the current statement
207 skip_stmt_pos bool // for handling if expressions + autofree (since both prepend C statements)
208 left_is_opt bool // left hand side on assignment is an option
209 right_is_opt bool // right hand side on assignment is an option
210 assign_ct_type map[int]ast.Type // left hand side resolved comptime type
211 expected_rhs_type_by_pos map[int]ast.Type // expected value type for local RHS expressions
212 closure_frame_arg_tmps map[int]string
213 indent int
214 empty_line bool
215 assign_op token.Kind // *=, =, etc (for array_set)
216 defer_stmts []ast.DeferStmt
217 defer_ifdef string
218 defer_profile_code string
219 inside_defer_generation bool
220 defer_vars []string
221 closure_structs []string
222 closure_cleanup_keep_vars []ClosureCleanupKeep
223 closure_cleanup_ignore_keep bool
224 closure_cleanup_target_loop_pos int
225 str_types []StrType // types that need automatic str() generation
226 generated_str_fns []StrType // types that already have a str() function
227 str_fn_names shared []string // remove duplicate function names
228 threaded_fns shared []string // for generating unique wrapper types and fns for `go xxx()`
229 waiter_fns shared []string // functions that wait for `go xxx()` to finish
230 needed_equality_fns []ast.Type
231 generated_eq_fns []ast.Type
232 needed_map_key_fns []ast.Type
233 generated_map_key_fns map[ast.Type]bool
234 generated_array_interface_cast_fns shared map[string]bool
235 generated_array_interface_repeat_fns shared map[string]bool
236 array_sort_fn shared []string
237 array_sort_wrappers shared []string
238 array_contains_types []ast.Type
239 array_index_types []ast.Type
240 array_last_index_types []ast.Type
241 array_get_types []ast.Type
242 auto_fn_definitions []string // auto generated functions definition list
243 sumtype_casting_fns []SumtypeCastingFn
244 anon_fn_definitions []string // anon generated functions definition list
245 anon_fns shared []string // remove duplicate anon generated functions
246 sumtype_definitions map[string]bool // `_TypeA_to_sumtype_TypeB()` fns that have been generated
247 trace_fn_definitions []string
248 json_types []ast.Type // to avoid json gen duplicates
249 json_types_pos map[ast.Type]token.Pos
250 json_gen_pos token.Pos
251 pcs []ProfileCounterMeta // -prof profile counter fn_names => fn counter name
252 hotcode_fn_names []string
253 hotcode_fpaths []string
254 embedded_files []ast.EmbeddedFile
255 sql_i int
256 sql_stmt_name string
257 sql_bind_name string
258 sql_idents []string
259 sql_idents_types []ast.Type
260 sql_left_type ast.Type
261 sql_table_name string
262 sql_table_typ ast.Type // the table type, used for generic types lookup
263 sql_fkey string
264 sql_parent_id string
265 sql_side SqlExprSide // left or right, to distinguish idents in `name == name`
266 sql_last_stmt_out_len int
267 strs_to_free0 []string // strings.Builder
268 lambda_autofree_tmp_arg_vars []string
269 for_c_init_autofree_keep_vars []string
270 for_c_init_autofree_cleanup_vars []ast.Var
271 skip_scope_cleanup_start_pos []int
272 // strs_to_free []string // strings.Builder
273 // tmp_arg_vars_to_free []string
274 // autofree_pregen map[string]string
275 // autofree_pregen_buf strings.Builder
276 // autofree_tmp_vars []string // to avoid redefining the same tmp vars in a single function
277 // nr_vars_to_free int
278 // doing_autofree_tmp bool
279 type_resolver type_resolver.TypeResolver
280 comptime &type_resolver.ResolverInfo = unsafe { nil }
281 prevent_sum_type_unwrapping_once bool // needed for assign new values to sum type
282 // used in match multi branch
283 // TypeOne, TypeTwo {}
284 // where an aggregate (at least two types) is generated
285 // sum type deref needs to know which index to deref because unions take care of the correct field
286 aggregate_type_idx int
287 arg_no_auto_deref bool // smartcast must not be dereferenced
288 branch_parent_pos int // used in BranchStmt (continue/break) for autofree stop position
289 returned_var_names map[string]bool // to detect that vars doesn't need to be freed since it's being returned
290 infix_left_var_name string // a && if expr
291 curr_var_name []string // curr var name on assignment
292 called_fn_name string
293 timers &util.Timers = util.get_timers()
294 force_main_console bool // true when @[console] used on fn main()
295 uses_power bool
296 uses_power_u64 bool
297 as_cast_type_names map[string]string // table for type name lookup in runtime (for __as_cast)
298 obf_table map[string]string
299 referenced_fns shared map[string]bool // functions that have been referenced
300 nr_closures int
301 expected_cast_type ast.Type // for match expr of sumtypes
302 expected_arg_mut bool // generating a mutable fn parameter
303 or_expr_return_type ast.Type // or { 0, 1 } return type
304 anon_fn &ast.AnonFn
305 tests_inited bool
306 has_main bool
307 // main_fn_decl_node ast.FnDecl
308 cur_mod ast.Module
309 cur_concrete_types []ast.Type // do not use table.cur_concrete_types because table is global, so should not be accessed by different threads
310 cur_fn &ast.FnDecl = unsafe { nil } // same here
311 cur_lock ast.LockExpr
312 cur_struct_init_typ ast.Type
313 zero_struct_init_stack []ast.Type
314 autofree_methods map[ast.Type]string
315 generated_free_methods map[ast.Type]bool
316 generated_free_fn_names map[string]bool
317 autofree_scope_stmts []string
318 use_segfault_handler bool = true
319 test_function_names []string
320 /////////
321 // out_parallel []strings.Builder
322 // out_idx int
323 out_fn_start_pos []int // for generating multiple .c files, stores locations of all fn positions in `out` string builder
324 static_modifier string // for parallel_cc
325 static_non_parallel string // for non -parallel_cc
326 has_reflection bool // v.reflection has been imported
327 has_debugger bool // $dbg has been used in the code
328 reflection_strings &map[string]int
329 defer_return_tmp_var string
330 veb_filter_fn_name string // veb__filter, used by $veb.html() for escaping strings in templates
331 export_funcs []string // for .dll export function names
332 //
333 type_default_impl_level int
334 preinclude_nodes []&ast.HashStmtNode // allows hash stmts to go before `includes`
335 include_nodes []&ast.HashStmtNode // all hash stmts to go `includes`
336 definition_nodes []&ast.HashStmtNode // allows hash stmts to go `definitions`
337 postinclude_nodes []&ast.HashStmtNode // allows hash stmts to go after all the rest of the code generation
338 curr_comptime_node ast.Expr = ast.empty_expr // current `$if` expr
339 is_builtin_overflow_mod bool
340 do_int_overflow_checks bool // outside a `@[ignore_overflow] fn abc() {}` or a function in `builtin.overflow`
341 //
342 tid string // the thread id of the file processor in the thread pool (log it to debug issues in parallel cgen)
343 fid int // the index of ast.File that is currently processed (log it to debug issues in parallel cgen)
344 mods_with_c_libs map[string]bool
345 mods_with_c_includes map[string]bool
346}
347
348@[heap]
349pub struct GenOutput {
350pub:
351 header string // produced output for out.h (-parallel-cc)
352 res_builder strings.Builder // produced output (complete)
353 out_str string // produced output from g.out
354 out0_str string // helpers output (auto fns, dump fns) for out_0.c (-parallel-cc)
355 extern_str string // extern chunk for (-parallel-cc)
356 out_fn_start_pos []int // fn decl positions
357}
358
359pub fn gen(files []&ast.File, mut table ast.Table, pref_ &pref.Preferences) GenOutput {
360 mut module_built := ''
361 if pref_.build_mode == .build_module || pref_.is_o {
362 for file in files {
363 if pref_.build_mode == .build_module && file.path.contains(pref_.path)
364 && file.mod.short_name == pref_.path.all_after_last(os.path_separator).trim_right(os.path_separator) {
365 module_built = file.mod.name
366 break
367 } else if pref_.is_o && file.path.contains(pref_.path) {
368 module_built = file.mod.name
369 break
370 }
371 }
372 }
373 mut timers_should_print := false
374 $if time_cgening ? {
375 timers_should_print = true
376 }
377 mut reflection_strings := map[string]int{}
378 mut global_g := Gen{
379 fid: -1
380 tid: v_gettid().hex()
381 file: unsafe { nil }
382 out: strings.new_builder(512000)
383 cheaders: strings.new_builder(15000)
384 includes: strings.new_builder(100)
385 preincludes: strings.new_builder(100)
386 postincludes: strings.new_builder(100)
387 typedefs: strings.new_builder(100)
388 enum_typedefs: strings.new_builder(100)
389 type_definitions: strings.new_builder(100)
390 sort_fn_definitions: strings.new_builder(100)
391 alias_definitions: strings.new_builder(100)
392 hotcode_definitions: strings.new_builder(100)
393 channel_definitions: strings.new_builder(100)
394 thread_definitions: strings.new_builder(100)
395 comptime_definitions: strings.new_builder(100)
396 definitions: strings.new_builder(100)
397 gowrappers: strings.new_builder(100)
398 auto_str_funcs: strings.new_builder(100)
399 dump_funcs: strings.new_builder(100)
400 pcs_declarations: strings.new_builder(100)
401 cov_declarations: strings.new_builder(100)
402 embedded_data: strings.new_builder(1000)
403 interface_index_definitions: strings.new_builder(100)
404 out_options_forward: strings.new_builder(100)
405 out_options: strings.new_builder(100)
406 out_results_forward: strings.new_builder(100)
407 out_results: strings.new_builder(100)
408 shared_types: strings.new_builder(100)
409 shared_functions: strings.new_builder(100)
410 json_forward_decls: strings.new_builder(100)
411 sql_buf: strings.new_builder(100)
412 table: table
413 pref: pref_
414 fn_decl: unsafe { nil }
415 anon_fn: unsafe { nil }
416 is_autofree: pref_.autofree
417 indent: -1
418 module_built: module_built
419 timers_should_print: timers_should_print
420 timers: util.new_timers(
421 should_print: timers_should_print
422 label: 'global_cgen'
423 )
424 inner_loop: unsafe { &ast.empty_stmt }
425 field_data_type: table.find_type('FieldData')
426 enum_data_type: table.find_type('EnumData')
427 variant_data_type: table.find_type('VariantData')
428 is_cc_msvc: pref_.ccompiler == 'msvc'
429 use_segfault_handler: pref_.should_use_segfault_handler()
430 static_modifier: if pref_.parallel_cc || pref_.is_o { 'static ' } else { '' }
431 static_non_parallel: if !pref_.parallel_cc { 'static ' } else { '' }
432 has_reflection: 'v.reflection' in table.modules
433 has_debugger: 'v.debug' in table.modules
434 reflection_strings: &reflection_strings
435 generated_map_key_fns: map[ast.Type]bool{}
436 boehm_keep_gen: map[string]bool{}
437 closure_frame_arg_tmps: map[int]string{}
438 generic_parts_cache: []i8{len: table.type_symbols.len}
439 unwrap_generic_cache: map[u64]ast.Type{}
440 resolved_scope_var_type_cache: map[u64]ast.Type{}
441 }
442
443 global_g.type_resolver = type_resolver.TypeResolver.new(table, global_g)
444 global_g.comptime = &global_g.type_resolver.info
445 // anon fn may include assert and thus this needs
446 // to be included before any test contents are written
447 if pref_.is_test {
448 global_g.write_tests_definitions()
449 }
450
451 util.timing_start('cgen init')
452 for mod in global_g.table.modules {
453 global_g.cleanups[mod] = strings.new_builder(100)
454 }
455 global_g.init()
456 for file in files {
457 if file.path.starts_with(pref_.vlib) {
458 continue
459 }
460 for stmt in file.stmts {
461 if stmt is ast.HashStmt {
462 if stmt.kind == 'flag' && stmt.main.contains('-l') {
463 global_g.mods_with_c_libs[file.mod.name] = true
464 }
465 if stmt.kind in ['include', 'preinclude'] {
466 global_g.mods_with_c_includes[file.mod.name] = true
467 }
468 }
469 }
470 }
471 util.timing_measure('cgen init')
472 global_g.tests_inited = false
473 global_g.file = files.last()
474 if !pref_.no_parallel {
475 util.timing_start('cgen parallel processing')
476 mut pp := pool.new_pool_processor(callback: cgen_process_one_file_cb)
477 pp.set_shared_context(global_g) // TODO: make global_g shared
478 pp.work_on_items(files)
479 util.timing_measure('cgen parallel processing')
480
481 util.timing_start('cgen unification')
482 for g in pp.get_results_ref[Gen]() {
483 global_g.embedded_files << g.embedded_files
484 global_g.out << g.out
485 global_g.cheaders << g.cheaders
486 global_g.preincludes << g.preincludes
487 global_g.postincludes << g.postincludes
488 global_g.includes << g.includes
489 global_g.typedefs << g.typedefs
490 global_g.type_definitions << g.type_definitions
491 global_g.sort_fn_definitions << g.sort_fn_definitions
492 global_g.alias_definitions << g.alias_definitions
493 global_g.definitions << g.definitions
494 global_g.gowrappers << g.gowrappers
495 global_g.waiter_fn_definitions << g.waiter_fn_definitions
496 global_g.auto_str_funcs << g.auto_str_funcs
497 global_g.dump_funcs << g.auto_str_funcs
498 global_g.comptime_definitions << g.comptime_definitions
499 global_g.pcs_declarations << g.pcs_declarations
500 global_g.cov_declarations << g.cov_declarations
501 global_g.hotcode_definitions << g.hotcode_definitions
502 global_g.embedded_data << g.embedded_data
503 global_g.shared_types << g.shared_types
504 global_g.shared_functions << g.shared_functions
505 global_g.export_funcs << g.export_funcs
506
507 global_g.force_main_console = global_g.force_main_console || g.force_main_console
508 global_g.uses_power = global_g.uses_power || g.uses_power
509 global_g.uses_power_u64 = global_g.uses_power_u64 || g.uses_power_u64
510
511 // merge maps
512 for k, v in g.vsafe_arithmetic_ops {
513 global_g.vsafe_arithmetic_ops[k] = v
514 }
515 for k, v in g.global_const_defs {
516 global_g.global_const_defs[k] = v
517 }
518 for k, v in g.shareds {
519 global_g.shareds[k] = v
520 }
521 for k, v in g.chan_pop_options {
522 global_g.chan_pop_options[k] = v
523 }
524 for k, v in g.chan_push_options {
525 global_g.chan_push_options[k] = v
526 }
527 for k, v in g.options {
528 global_g.options[k] = v
529 }
530 for k, v in g.results {
531 global_g.results[k] = v
532 }
533 for k, v in g.as_cast_type_names {
534 global_g.as_cast_type_names[k] = v
535 }
536 for k, v in g.sumtype_definitions {
537 global_g.sumtype_definitions[k] = v
538 }
539 for k, v in g.coverage_files {
540 global_g.coverage_files[k] = v
541 }
542 global_g.json_forward_decls << g.json_forward_decls
543 global_g.enum_typedefs << g.enum_typedefs
544 global_g.channel_definitions << g.channel_definitions
545 global_g.thread_definitions << g.thread_definitions
546 global_g.sql_buf << g.sql_buf
547 global_g.cleanups[g.file.mod.name] << g.cleanup
548
549 for str_type in g.str_types {
550 global_g.str_types << str_type
551 }
552 for scf in g.sumtype_casting_fns {
553 mut already_exists := false
554 for existing in global_g.sumtype_casting_fns {
555 if existing.fn_name == scf.fn_name {
556 already_exists = true
557 break
558 }
559 }
560 if !already_exists {
561 global_g.sumtype_casting_fns << scf
562 }
563 }
564
565 for at in g.array_typedefs {
566 if at !in global_g.array_typedefs {
567 global_g.array_typedefs << at
568 }
569 }
570 global_g.nr_closures += g.nr_closures
571 global_g.has_main = global_g.has_main || g.has_main
572
573 global_g.auto_fn_definitions << g.auto_fn_definitions
574 global_g.anon_fn_definitions << g.anon_fn_definitions
575 global_g.needed_equality_fns << g.needed_equality_fns // duplicates are resolved later in gen_equality_fns
576 global_g.needed_map_key_fns << g.needed_map_key_fns
577 global_g.array_contains_types << g.array_contains_types
578 global_g.array_index_types << g.array_index_types
579 global_g.array_last_index_types << g.array_last_index_types
580 global_g.array_get_types << g.array_get_types
581 global_g.pcs << g.pcs
582 global_g.json_types << g.json_types
583 for k, v in g.json_types_pos {
584 if k !in global_g.json_types_pos || global_g.json_types_pos[k] == token.Pos{} {
585 global_g.json_types_pos[k] = v
586 }
587 }
588 global_g.hotcode_fn_names << g.hotcode_fn_names
589 global_g.hotcode_fpaths << g.hotcode_fpaths
590 global_g.test_function_names << g.test_function_names
591 for k, v in g.autofree_methods {
592 global_g.autofree_methods[k] = v
593 }
594 for k, v in g.no_eq_method_types {
595 global_g.no_eq_method_types[k] = v
596 }
597 unsafe { g.free_builders() } // free at the very end
598 }
599 } else {
600 util.timing_start('cgen serial processing')
601 for fid, file in files {
602 global_g.fid = fid
603 global_g.file = file
604 global_g.gen_file()
605 global_g.cleanups[file.mod.name].drain_builder(mut global_g.cleanup, 100)
606 global_g.global_tmp_count = 0
607 global_g.tmp_count = 0
608 }
609 util.timing_measure('cgen serial processing')
610 global_g.fid = -1
611
612 util.timing_start('cgen unification')
613 }
614
615 if !global_g.pref.new_generic_solver {
616 global_g.post_process_generic_fns_for_files(files)
617 }
618 global_g.gen_jsons()
619 global_g.dump_expr_definitions() // this uses global_g.get_str_fn, so it has to go before the below for loop
620 // Pre-register auto-str types for interface implementing types that need str()
621 // but don't have an explicit str() method. This must happen before final_gen_str
622 // processing so all transitive str types are included.
623 global_g.register_auto_str_for_interfaces()
624 for i := 0; i < global_g.str_types.len; i++ {
625 global_g.final_gen_str(global_g.str_types[i])
626 }
627 for sumtype_casting_fn in global_g.sumtype_casting_fns {
628 global_g.write_sumtype_casting_fn(sumtype_casting_fn)
629 }
630 global_g.write_shareds()
631 global_g.emit_late_chan_type_definitions()
632 global_g.write_chan_pop_option_fns()
633 global_g.write_chan_push_option_fns()
634 global_g.gen_array_contains_methods()
635 global_g.gen_array_index_methods(false) // .index()
636 global_g.gen_array_index_methods(true) // .last_index()
637 global_g.gen_array_get_methods()
638 global_g.gen_equality_fns()
639 global_g.gen_map_key_fns()
640 global_g.gen_free_methods()
641 global_g.register_iface_return_types()
642 global_g.write_results()
643 global_g.write_options()
644 global_g.write_late_array_typedefs()
645 global_g.sort_globals_consts()
646 util.timing_measure('cgen unification')
647
648 mut g := global_g
649 util.timing_start('cgen common')
650
651 // to make sure type idx's are the same in cached mods
652 // Some distinct type symbols can still share a C name, for example repeated
653 // C struct declarations. Emit one cache helper per cname to avoid duplicate
654 // definitions when `-usecache` builds the main module.
655 mut emitted_cache_type_idx_cnames := map[string]bool{}
656 if g.pref.build_mode == .build_module {
657 is_toml := g.pref.path.contains('/toml')
658 for idx, sym in g.table.type_symbols {
659 if idx in [0, 31] {
660 continue
661 }
662 if is_toml && sym.cname.contains('map[string]') {
663 // Temporary hack to make toml work with -usecache TODO remove
664 continue
665 }
666 if sym.cname in emitted_cache_type_idx_cnames {
667 continue
668 }
669 emitted_cache_type_idx_cnames[sym.cname] = true
670 g.definitions.writeln('u32 _v_type_idx_${sym.cname}(); // 1build module ${g.pref.path}')
671 }
672 } else if g.pref.use_cache {
673 is_toml := g.pref.path.contains('/toml')
674 for idx, sym in g.table.type_symbols {
675 if idx in [0, 31] {
676 continue
677 }
678 if is_toml && sym.cname.contains('map[string]') {
679 continue
680 }
681 if sym.cname in emitted_cache_type_idx_cnames {
682 continue
683 }
684 emitted_cache_type_idx_cnames[sym.cname] = true
685 g.definitions.writeln('u32 _v_type_idx_${sym.cname}() { return ${idx}; }; //lol ${g.pref.path}')
686 }
687 }
688
689 // v files are finished, what remains is pure C code
690 g.gen_vlines_reset()
691 if g.pref.build_mode != .build_module {
692 // no init in builtin.o
693 g.write_init_function()
694 }
695
696 if g.pref.is_coverage {
697 mut total_code_points := 0
698 for k, cov in g.coverage_files {
699 g.cheaders.writeln('#define _v_cov_file_offset_${k} ${total_code_points}')
700 total_code_points += cov.points.len
701 }
702 g.cheaders.writeln('long int _v_cov[${total_code_points}] = {0};')
703 }
704
705 // insert for options forward
706 if g.out_options_forward.len > 0 || g.out_results_forward.len > 0 {
707 tail := g.type_definitions.cut_to(g.options_pos_forward)
708 if g.out_options_forward.len > 0 {
709 g.type_definitions.writeln('// #start V forward option_xxx definitions:')
710 g.type_definitions.writeln(g.out_options_forward.str())
711 g.type_definitions.writeln('// #end V forward option_xxx definitions\n')
712 }
713 if g.out_results_forward.len > 0 {
714 g.type_definitions.writeln('// #start V forward result_xxx definitions:')
715 g.type_definitions.writeln(g.out_results_forward.str())
716 g.type_definitions.writeln('// #end V forward result_xxx definitions\n')
717 }
718 g.type_definitions.writeln(tail)
719 }
720
721 g.finish()
722
723 mut b := strings.new_builder(g.out.len + 600_000)
724 b.write_string(g.hashes())
725 if g.use_segfault_handler || g.pref.is_prof {
726 b.writeln('\n#define V_USE_SIGNAL_H')
727 }
728 b.write_string2('\n// V comptime_definitions:\n', g.comptime_definitions.str())
729 b.write_string2('\n// V typedefs:\n', g.typedefs.str())
730 b.write_string2('\n // V preincludes:\n', g.preincludes.str())
731 b.write_string2('\n// V cheaders:\n', g.cheaders.str())
732 if g.pcs_declarations.len > 0 {
733 g.pcs_declarations.writeln('// V profile thread local:')
734 g.pcs_declarations.writeln('#if defined(__cplusplus) && __cplusplus >= 201103L')
735 g.pcs_declarations.writeln('\t#define PROF_THREAD_LOCAL thread_local')
736 g.pcs_declarations.writeln('#elif defined(__GNUC__) && __GNUC__ < 5')
737 g.pcs_declarations.writeln('\t#define PROF_THREAD_LOCAL __thread')
738 g.pcs_declarations.writeln('#elif defined(_MSC_VER)')
739 g.pcs_declarations.writeln('\t#define PROF_THREAD_LOCAL __declspec(thread)')
740 g.pcs_declarations.writeln('#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__)')
741 g.pcs_declarations.writeln('\t#define PROF_THREAD_LOCAL _Thread_local')
742 g.pcs_declarations.writeln('#endif')
743 g.pcs_declarations.writeln('#ifndef PROF_THREAD_LOCAL')
744 g.pcs_declarations.writeln('\t#if defined(__GNUC__)')
745 g.pcs_declarations.writeln('\t\t#define PROF_THREAD_LOCAL __thread')
746 g.pcs_declarations.writeln('\t#endif')
747 g.pcs_declarations.writeln('#endif')
748 g.pcs_declarations.writeln('#ifdef PROF_THREAD_LOCAL')
749 g.pcs_declarations.writeln('\tstatic PROF_THREAD_LOCAL double prof_measured_time = 0.0;')
750 g.pcs_declarations.writeln('#else')
751 g.pcs_declarations.writeln('\tdouble prof_measured_time = 0.0; // multithreaded: wrong values for func times without its children')
752 g.pcs_declarations.writeln('#endif')
753 b.write_string2('\n// V profile counters:\n', g.pcs_declarations.str())
754 }
755 b.write_string2('\n// V includes:\n', g.includes.str())
756 // Restore V's bool definition: system headers (e.g. macOS mach/vm_statistics.h)
757 // may include <stdbool.h> which redefines bool to _Bool via a macro,
758 // overriding V's `typedef u8 bool;` and causing type mismatches in clang.
759 // In C23, bool is a keyword, so we must not redefine it.
760 b.writeln('#if !defined(__cplusplus) && !defined(CUSTOM_DEFINE_no_bool)')
761 b.writeln('#ifdef bool')
762 b.writeln('#undef bool')
763 b.writeln('#endif')
764 b.writeln('#if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 202311L')
765 b.writeln('#ifdef CUSTOM_DEFINE_4bytebool')
766 b.writeln('typedef int bool;')
767 b.writeln('#else')
768 b.writeln('typedef u8 bool;')
769 b.writeln('#endif')
770 b.writeln('#endif')
771 b.writeln('#endif')
772 b.writeln('\n// V global/const #define ... :')
773 for var_name in g.sorted_global_const_names {
774 if var := g.global_const_defs[var_name] {
775 if var.def.starts_with('#define') {
776 b.writeln(var.def)
777 }
778 }
779 }
780 if g.enum_typedefs.len > 0 {
781 b.write_string2('\n// Enum definitions:\n', g.enum_typedefs.str())
782 }
783 if g.thread_definitions.len > 0 {
784 b.write_string2('\n// Thread definitions:\n', g.thread_definitions.str())
785 }
786 if g.type_definitions.len > 0 {
787 b.write_string2('\n// V type definitions:\n', g.type_definitions.str())
788 }
789 if g.alias_definitions.len > 0 {
790 b.write_string2('\n// V alias definitions:\n', g.alias_definitions.str())
791 }
792 if g.shared_types.len > 0 {
793 b.write_string2('\n// V shared types:\n', g.shared_types.str())
794 }
795 if g.out_options.len > 0 {
796 b.write_string2('\n// V Option_xxx definitions:\n', g.out_options.str())
797 }
798 if g.out_results.len > 0 {
799 b.write_string2('\n// V result_xxx definitions:\n', g.out_results.str())
800 }
801 b.write_string2('\n// V definitions:\n', g.definitions.str())
802 if !pref_.parallel_cc {
803 b.writeln('\n// V global/const non-precomputed definitions:')
804 for var_name in g.sorted_global_const_names {
805 if var := g.global_const_defs[var_name] {
806 if !var.def.starts_with('#define') {
807 b.writeln(var.def)
808 }
809 }
810 }
811 }
812 interface_table := g.interface_table()
813 if interface_table.len > 0 {
814 b.write_string2('\n// V interface table:\n', interface_table)
815 }
816 if g.sort_fn_definitions.len > 0 {
817 b.write_string2('\n// V sort fn definitions:\n', g.sort_fn_definitions.str())
818 }
819 if g.hotcode_definitions.len > 0 {
820 b.write_string2('\n// V hotcode definitions:\n', g.hotcode_definitions.str())
821 }
822 if g.shared_functions.len > 0 {
823 b.writeln('\n// V shared type functions:\n')
824 b.write_string2(g.shared_functions.str(), c_concurrency_helpers)
825 }
826 if g.channel_definitions.len > 0 {
827 b.write_string2('\n// V channel code:\n', g.channel_definitions.str())
828 }
829 if g.vsafe_arithmetic_ops.len > 0 {
830 for vsafe_fn_name, val in g.vsafe_arithmetic_ops {
831 styp := g.styp(val.typ)
832 if val.op == .div {
833 if g.pref.no_builtin || g.pref.div_by_zero_is_zero {
834 b.writeln('static inline ${styp} ${vsafe_fn_name}(${styp} x, ${styp} y) { if (_unlikely_(0 == y)) { return 0; } else { return x / y; } }')
835 } else {
836 b.writeln('static inline ${styp} ${vsafe_fn_name}(${styp} x, ${styp} y) { if (_unlikely_(0 == y)) { builtin___v_panic(_S("division by zero")); } return x / y; }')
837 }
838 } else {
839 if g.pref.no_builtin || g.pref.div_by_zero_is_zero {
840 b.writeln('static inline ${styp} ${vsafe_fn_name}(${styp} x, ${styp} y) { if (_unlikely_(0 == y)) { return x; } else { return x % y; } }')
841 } else {
842 b.writeln('static inline ${styp} ${vsafe_fn_name}(${styp} x, ${styp} y) { if (_unlikely_(0 == y)) { builtin___v_panic(_S("modulo by zero")); } return x % y; }')
843 }
844 }
845 }
846 }
847 if g.uses_power {
848 b.writeln('#include <math.h>')
849 b.writeln('static inline i64 __v_pow_i64(i64 base, i64 exponent) {')
850 b.writeln('\tif (exponent < 0) {')
851 b.writeln('\t\tif (base == 0) {')
852 b.writeln('\t\t\treturn -1;')
853 b.writeln('\t\t}')
854 b.writeln('\t\tif (base != 1 && base != -1) {')
855 b.writeln('\t\t\treturn 0;')
856 b.writeln('\t\t}')
857 b.writeln('\t\treturn (exponent & 1) != 0 ? base : 1;')
858 b.writeln('\t}')
859 b.writeln('\ti64 value = 1;')
860 b.writeln('\ti64 power = base;')
861 b.writeln('\tfor (; exponent > 0; exponent >>= 1) {')
862 b.writeln('\t\tif ((exponent & 1) != 0) {')
863 b.writeln('\t\t\tvalue *= power;')
864 b.writeln('\t\t}')
865 b.writeln('\t\tpower *= power;')
866 b.writeln('\t}')
867 b.writeln('\treturn value;')
868 b.writeln('}')
869 }
870 if g.uses_power_u64 {
871 b.writeln('static inline u64 __v_pow_u64(u64 base, i64 exponent) {')
872 b.writeln('\tif (exponent < 0) {')
873 b.writeln('\t\tif (base == 0) {')
874 b.writeln('\t\t\treturn ((u64)-1);')
875 b.writeln('\t\t}')
876 b.writeln('\t\treturn base == 1 ? 1 : 0;')
877 b.writeln('\t}')
878 b.writeln('\tu64 value = 1;')
879 b.writeln('\tu64 power = base;')
880 b.writeln('\tfor (; exponent > 0; exponent >>= 1) {')
881 b.writeln('\t\tif ((exponent & 1) != 0) {')
882 b.writeln('\t\t\tvalue *= power;')
883 b.writeln('\t\t}')
884 b.writeln('\t\tpower *= power;')
885 b.writeln('\t}')
886 b.writeln('\treturn value;')
887 b.writeln('}')
888 }
889 if g.pref.is_coverage {
890 b.write_string2('\n// V coverage:\n', g.cov_declarations.str())
891 }
892 b.writeln('\n// end of V out (header)')
893 mut header := b.last_n(b.len)
894 header = '#ifndef V_HEADER_FILE\n#define V_HEADER_FILE' + header
895 header += '\n#endif\n'
896
897 mut helpers := strings.new_builder(300_000)
898 // Code added here (after the header) goes to out_0.c in parallel cc mode
899 // Previously it went to the header which resulted in duplicated code and more code
900 // to compile for the C compiler
901 if g.embedded_data.len > 0 {
902 helpers.write_string2('\n// V embedded data:\n', g.embedded_data.str())
903 }
904 if g.interface_index_definitions.len > 0 {
905 helpers.write_string2('\n// V interface index definitions:\n',
906 g.interface_index_definitions.str())
907 }
908 if g.pref.parallel_cc {
909 helpers.writeln('\n// V global/const non-precomputed definitions:')
910 for var_name in g.sorted_global_const_names {
911 if var := g.global_const_defs[var_name] {
912 if !var.def.starts_with('#define') {
913 helpers.writeln(var.def)
914 if var.def.starts_with('/*') || var.def.starts_with('extern ') {
915 // skip C globals (comment-only placeholders) and
916 // already extern declarations (e.g. extern C globals)
917 g.extern_out.writeln(var.def)
918 } else if var.def.contains(' = ') {
919 g.extern_out.writeln('extern ${var.def.all_before(' = ')};')
920 } else {
921 g.extern_out.writeln('extern ${var.def}')
922 }
923 }
924 }
925 }
926 }
927 if g.waiter_fn_definitions.len > 0 {
928 if g.pref.parallel_cc {
929 g.extern_out.write_string2('\n// V gowrappers waiter fns:\n',
930 g.waiter_fn_definitions.bytestr())
931 }
932 helpers.write_string2('\n// V gowrappers waiter fns:\n', g.waiter_fn_definitions.str())
933 }
934 if g.auto_str_funcs.len > 0 {
935 helpers.write_string2('\n// V auto str functions:\n', g.auto_str_funcs.str())
936 }
937 if g.auto_fn_definitions.len > 0 {
938 helpers.writeln('\n// V auto functions:')
939 for fn_def in g.auto_fn_definitions {
940 helpers.writeln(fn_def)
941 }
942 }
943 if g.json_forward_decls.len > 0 {
944 helpers.write_string2('\n// V json forward decls:\n', g.json_forward_decls.bytestr())
945 if g.pref.parallel_cc {
946 g.extern_out.write_string2('\n// V json forward decls:\n', g.json_forward_decls.str())
947 }
948 }
949 if g.gowrappers.len > 0 {
950 helpers.write_string2('\n// V gowrappers:\n', g.gowrappers.str())
951 }
952 if g.dump_funcs.len > 0 {
953 helpers.write_string2('\n// V dump functions:\n', g.dump_funcs.str())
954 }
955 if g.anon_fn_definitions.len > 0 {
956 helpers.writeln('\n// V anon functions:')
957 for fn_def in g.anon_fn_definitions {
958 helpers.writeln(fn_def)
959 }
960 }
961
962 if g.pref.is_shared && g.pref.os == .windows && g.export_funcs.len > 0 {
963 // generate a .def for export function names, avoid function name mangle
964 mut def_name := ''
965 mut dll_name := ''
966 if g.pref.out_name.ends_with('.dll') {
967 def_name = g.pref.out_name[0..g.pref.out_name.len - 4] + '.def'
968 dll_name = g.pref.out_name.all_after_last('\\')
969 } else {
970 def_name = g.pref.out_name + '.def'
971 dll_name = g.pref.out_name.all_after_last('\\') + '.dll'
972 }
973 file_content := 'LIBRARY ${dll_name}\n\nEXPORTS\n' + g.export_funcs.join('\n')
974 os.write_file('${def_name}', file_content) or { panic(err) }
975 }
976
977 // End of out_0.c
978
979 shelpers := helpers.str()
980
981 if !g.pref.parallel_cc {
982 b.write_string(shelpers)
983 }
984
985 // The rest of the output
986 out_str := g.out.str()
987 extern_out_str := g.extern_out.str()
988 b.write_string(out_str)
989 b.writeln('// THE END.')
990
991 postincludes_str := g.postincludes.str()
992 if postincludes_str != '' {
993 b.write_string2('\n // V postincludes:\n', postincludes_str)
994 }
995
996 util.timing_measure('cgen common')
997 $if trace_all_generic_fn_keys ? {
998 gkeys := g.table.fn_generic_types.keys()
999 for gkey in gkeys {
1000 eprintln('>> g.table.fn_generic_types key: ${gkey}')
1001 }
1002 }
1003 out_fn_start_pos := g.out_fn_start_pos.clone()
1004 unsafe { helpers.free() }
1005 unsafe { g.free_builders() }
1006
1007 return GenOutput{
1008 header: header
1009 res_builder: b
1010 out_str: out_str
1011 out0_str: shelpers
1012 extern_str: extern_out_str
1013 out_fn_start_pos: out_fn_start_pos
1014 }
1015}
1016
1017fn cgen_process_one_file_cb(mut p pool.PoolProcessor, idx int, wid int) voidptr {
1018 file := p.get_item[&ast.File](idx)
1019 timing_label_for_thread := 'C GEN thread ${wid}'
1020 util.timing_start(timing_label_for_thread)
1021 defer {
1022 util.timing_measure_cumulative(timing_label_for_thread)
1023 }
1024 mut global_g := unsafe { &Gen(p.get_shared_context()) }
1025 mut g := &Gen{
1026 fid: idx
1027 tid: v_gettid().hex()
1028 file: file
1029 out: strings.new_builder(512000)
1030 cheaders: strings.new_builder(15000)
1031 includes: strings.new_builder(100)
1032 typedefs: strings.new_builder(100)
1033 type_definitions: strings.new_builder(100)
1034 sort_fn_definitions: strings.new_builder(100)
1035 alias_definitions: strings.new_builder(100)
1036 definitions: strings.new_builder(100)
1037 gowrappers: strings.new_builder(100)
1038 waiter_fn_definitions: strings.new_builder(100)
1039 auto_str_funcs: strings.new_builder(100)
1040 comptime_definitions: strings.new_builder(100)
1041 pcs_declarations: strings.new_builder(100)
1042 cov_declarations: strings.new_builder(100)
1043 hotcode_definitions: strings.new_builder(100)
1044 embedded_data: strings.new_builder(1000)
1045 interface_index_definitions: strings.new_builder(100)
1046 out_options_forward: strings.new_builder(100)
1047 out_options: strings.new_builder(100)
1048 out_results_forward: strings.new_builder(100)
1049 out_results: strings.new_builder(100)
1050 shared_types: strings.new_builder(100)
1051 shared_functions: strings.new_builder(100)
1052 channel_definitions: strings.new_builder(100)
1053 thread_definitions: strings.new_builder(100)
1054 json_forward_decls: strings.new_builder(100)
1055 enum_typedefs: strings.new_builder(100)
1056 sql_buf: strings.new_builder(100)
1057 cleanup: strings.new_builder(100)
1058 table: global_g.table
1059 pref: global_g.pref
1060 fn_decl: unsafe { nil }
1061 anon_fn: unsafe { nil }
1062 indent: -1
1063 module_built: global_g.module_built
1064 static_non_parallel: global_g.static_non_parallel
1065 static_modifier: global_g.static_modifier
1066 timers: util.new_timers(
1067 should_print: global_g.timers_should_print
1068 label: 'cgen_process_one_file_cb idx: ${idx}, wid: ${wid}'
1069 )
1070 inner_loop: &ast.empty_stmt
1071 field_data_type: global_g.table.find_type('FieldData')
1072 enum_data_type: global_g.table.find_type('EnumData')
1073 variant_data_type: global_g.table.find_type('VariantData')
1074 array_sort_fn: global_g.array_sort_fn
1075 array_sort_wrappers: global_g.array_sort_wrappers
1076 generated_array_interface_cast_fns: global_g.generated_array_interface_cast_fns
1077 waiter_fns: global_g.waiter_fns
1078 threaded_fns: global_g.threaded_fns
1079 str_fn_names: global_g.str_fn_names
1080 anon_fns: global_g.anon_fns
1081 options_forward: global_g.options_forward
1082 results_forward: global_g.results_forward
1083 done_options: global_g.done_options
1084 done_results: global_g.done_results
1085 late_chan_types: global_g.late_chan_types
1086 is_autofree: global_g.pref.autofree
1087 obf_table: global_g.obf_table
1088 referenced_fns: global_g.referenced_fns
1089 is_cc_msvc: global_g.is_cc_msvc
1090 use_segfault_handler: global_g.use_segfault_handler
1091 done_typedef_phase: global_g.done_typedef_phase
1092 array_typedefs: global_g.array_typedefs.clone()
1093 written_array_typedefs: global_g.written_array_typedefs
1094 has_reflection: 'v.reflection' in global_g.table.modules
1095 has_debugger: 'v.debug' in global_g.table.modules
1096 reflection_strings: global_g.reflection_strings
1097 generated_map_key_fns: map[ast.Type]bool{}
1098 boehm_keep_gen: global_g.boehm_keep_gen
1099 closure_frame_arg_tmps: map[int]string{}
1100 generic_parts_cache: []i8{len: global_g.table.type_symbols.len}
1101 unwrap_generic_cache: map[u64]ast.Type{}
1102 resolved_scope_var_type_cache: map[u64]ast.Type{}
1103 }
1104 g.type_resolver = type_resolver.TypeResolver.new(global_g.table, g)
1105 g.comptime = &g.type_resolver.info
1106 g.gen_file()
1107 return voidptr(g)
1108}
1109
1110// free_builders should be called only when a Gen would NOT be used anymore
1111// it frees the bulk of the memory that is private to the Gen instance
1112// (the various string builders)
1113@[unsafe]
1114pub fn (mut g Gen) free_builders() {
1115 unsafe {
1116 g.out.free()
1117 g.cheaders.free()
1118 g.includes.free()
1119 g.typedefs.free()
1120 g.type_definitions.free()
1121 g.sort_fn_definitions.free()
1122 g.alias_definitions.free()
1123 g.definitions.free()
1124 g.cleanup.free()
1125 g.gowrappers.free()
1126 g.waiter_fn_definitions.free()
1127 g.auto_str_funcs.free()
1128 g.dump_funcs.free()
1129 g.comptime_definitions.free()
1130 g.pcs_declarations.free()
1131 g.cov_declarations.free()
1132 g.hotcode_definitions.free()
1133 g.embedded_data.free()
1134 g.interface_index_definitions.free()
1135 g.shared_types.free()
1136 g.shared_functions.free()
1137 g.channel_definitions.free()
1138 g.thread_definitions.free()
1139 g.out_options_forward.free()
1140 g.out_options.free()
1141 g.out_results_forward.free()
1142 g.out_results.free()
1143 g.json_forward_decls.free()
1144 g.enum_typedefs.free()
1145 g.sql_buf.free()
1146 for _, mut v in g.cleanups {
1147 v.free()
1148 }
1149 }
1150}
1151
1152pub fn (mut g Gen) gen_file() {
1153 $if trace_cgen ? {
1154 eprintln('> ${@FILE}:${@LINE} | g.file.path: ${g.file.path} | g.tid: ${g.tid} | g.fid: ${g.fid:3}')
1155 }
1156 g.timers.start('cgen_file ${g.file.path}')
1157 g.unique_file_path_hash = fnv1a.sum64_string(g.file.path)
1158 if g.pref.is_vlines {
1159 g.vlines_path = util.vlines_escape_path(g.file.path, g.pref.ccompiler)
1160 g.is_vlines_enabled = true
1161 g.inside_ternary = 0
1162 }
1163 g.stmts(g.file.stmts)
1164
1165 // after all other stmts executed, we got info about hash stmts in top,
1166 // write them to corresponding sections
1167 g.gen_hash_stmts_in_top()
1168
1169 // Transfer embedded files
1170 for path in g.file.embedded_files {
1171 if path !in g.embedded_files {
1172 g.embedded_files << path
1173 }
1174 }
1175 g.timers.show('cgen_file ${g.file.path}')
1176}
1177
1178pub fn (g &Gen) hashes() string {
1179 return c_commit_hash_default.replace('@@@', version.vhash())
1180}
1181
1182pub fn (mut g Gen) init() {
1183 if g.pref.custom_prelude != '' {
1184 g.cheaders.writeln(g.pref.custom_prelude)
1185 } else if !g.pref.no_preludes {
1186 g.cheaders.writeln('// Generated by the V compiler')
1187 if g.pref.relaxed_gcc14 {
1188 // See https://gcc.gnu.org/gcc-14/porting_to.html#c-code-generators:
1189 g.cheaders.writeln('
1190#if defined __GNUC__ && __GNUC__ >= 14
1191#pragma GCC diagnostic warning "-Wimplicit-function-declaration"
1192#pragma GCC diagnostic warning "-Wincompatible-pointer-types"
1193#pragma GCC diagnostic warning "-Wint-conversion"
1194#pragma GCC diagnostic warning "-Wreturn-mismatch"
1195#endif
1196')
1197 }
1198 if g.pref.os == .linux {
1199 // For gettid() declaration (and other GNU-specific bits).
1200 // Must come before any C declarations that use GNU extensions.
1201 g.preincludes.writeln('#define _GNU_SOURCE')
1202 }
1203 if g.pref.os == .wasm32 {
1204 g.cheaders.writeln('#define VWASM 1')
1205 // Include <stdint.h> instead of <inttypes.h> for WASM target
1206 g.cheaders.writeln('#include <stdint.h>')
1207 g.cheaders.writeln('#include <stddef.h>')
1208 } else {
1209 tcc_undef_has_include := '
1210#if defined(__TINYC__) && defined(__has_include) // tcc does not support has_include properly yet, turn it off completely
1211#undef __has_include
1212#endif'
1213 g.preincludes.writeln(tcc_undef_has_include)
1214 g.cheaders.writeln(tcc_undef_has_include)
1215 g.includes.writeln(tcc_undef_has_include)
1216 // Android/Termux bionic <math.h> expands NAN/INFINITY/HUGE_VAL* via
1217 // __builtin_nanf/__builtin_inf*/__builtin_huge_val*. The AArch64 tcc
1218 // port does not lower these intrinsics, so binaries fail at runtime
1219 // with unresolved __builtin_nanf etc. symbols (vlang/v#27207).
1220 // Emit the macro shim into preincludes/cheaders/includes so it is
1221 // in scope before *any* bionic header is reached, including those
1222 // pulled in via `#preinclude <math.h>`.
1223 tcc_bionic_math_shim := '
1224#if defined(__TINYC__) && defined(__BIONIC__)
1225 #define __builtin_nanf(ignored_string) (0.0F / 0.0F)
1226 #define __builtin_nan(ignored_string) (0.0 / 0.0)
1227 #define __builtin_nanl(ignored_string) (0.0L / 0.0L)
1228 #define __builtin_inff() (1.0F / 0.0F)
1229 #define __builtin_inf() (1.0 / 0.0)
1230 #define __builtin_infl() (1.0L / 0.0L)
1231 #define __builtin_huge_valf() (1.0F / 0.0F)
1232 #define __builtin_huge_val() (1.0 / 0.0)
1233 #define __builtin_huge_vall() (1.0L / 0.0L)
1234#endif'
1235 g.preincludes.writeln(tcc_bionic_math_shim)
1236 g.cheaders.writeln(tcc_bionic_math_shim)
1237 g.includes.writeln(tcc_bionic_math_shim)
1238 if g.pref.os == .freebsd {
1239 g.cheaders.writeln('#include <inttypes.h>')
1240 g.cheaders.writeln('#include <stddef.h>')
1241 } else {
1242 install_compiler_msg := ' Please install the package `build-essential`.'
1243 // int64_t etc; allow a fallback to <stdint.h> for toolchains that do not ship <inttypes.h>.
1244 g.cheaders.writeln(get_inttypes_or_stdint_include_text('The C compiler can not find <stdint.h>.${install_compiler_msg}'))
1245 if g.pref.os == .ios {
1246 g.cheaders.writeln(get_guarded_include_text('<stdbool.h>',
1247 'The C compiler can not find <stdbool.h>.${install_compiler_msg}')) // bool, true, false
1248 }
1249 g.cheaders.writeln(get_guarded_include_text('<stddef.h>',
1250 'The C compiler can not find <stddef.h>.${install_compiler_msg}')) // size_t, ptrdiff_t
1251 }
1252 }
1253 if g.pref.nofloat {
1254 g.cheaders.writeln('#define VNOFLOAT 1')
1255 }
1256 g.cheaders.writeln(c_builtin_types)
1257 g.cheaders.writeln(c_shift_helpers)
1258 if !g.pref.skip_unused || g.table.used_features.used_maps > 0 {
1259 g.cheaders.writeln(c_mapfn_callback_types)
1260 }
1261 if g.pref.is_bare {
1262 g.cheaders.writeln(c_bare_headers)
1263 } else {
1264 g.cheaders.writeln(c_headers)
1265 }
1266 g.cheaders.writeln(c_float_to_unsigned_conversion_functions)
1267 if !g.pref.skip_unused || g.table.used_features.safe_int {
1268 g.cheaders.writeln(c_unsigned_comparison_functions)
1269 }
1270 if !g.pref.skip_unused || g.table.used_features.used_attr_weak {
1271 g.cheaders.writeln(c_common_weak_attr)
1272 }
1273 if !g.pref.skip_unused || g.table.used_features.used_attr_hidden {
1274 g.cheaders.writeln(c_common_hidden_attr)
1275 }
1276 if !g.pref.skip_unused || g.table.used_features.used_attr_noreturn {
1277 g.cheaders.writeln(c_common_noreturn_attr)
1278 g.cheaders.writeln(c_common_unreachable_attr)
1279 }
1280 if !g.pref.skip_unused || g.table.used_features.used_maps > 0 {
1281 g.cheaders.writeln(c_wyhash_headers)
1282 }
1283 }
1284 if g.pref.os == .ios {
1285 g.cheaders.writeln('#define __TARGET_IOS__ 1')
1286 g.cheaders.writeln('#include <spawn.h>')
1287 }
1288 if g.pref.os == .linux {
1289 g.cheaders.writeln('#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 30')
1290 g.cheaders.writeln('#include <sys/syscall.h>')
1291 g.cheaders.writeln('#define gettid() syscall(SYS_gettid)')
1292 g.cheaders.writeln('#endif')
1293 }
1294 g.write_builtin_types()
1295 g.options_pos_forward = g.type_definitions.len
1296 g.write_typedef_types()
1297 g.written_array_typedefs = g.array_typedefs.len
1298 g.done_typedef_phase = true
1299 g.write_typeof_functions()
1300 g.write_sorted_types()
1301 g.write_array_fixed_return_types()
1302 g.write_multi_return_types()
1303 g.definitions.writeln('// end of definitions #endif')
1304 if g.pref.compile_defines_all.len > 0 {
1305 g.comptime_definitions.writeln('// V compile time defines by -d or -define flags:')
1306 g.comptime_definitions.writeln('// All custom defines : ' +
1307 g.pref.compile_defines_all.join(','))
1308 g.comptime_definitions.writeln('// Turned ON custom defines: ' +
1309 g.pref.compile_defines.join(','))
1310 for cdefine in g.pref.compile_defines {
1311 g.comptime_definitions.writeln('#define CUSTOM_DEFINE_${cdefine}')
1312 }
1313 g.comptime_definitions.writeln('')
1314 }
1315 if g.table.gostmts > 0 {
1316 g.comptime_definitions.writeln('#define __VTHREADS__ (1)')
1317 }
1318 if g.pref.os in [.macos, .freebsd, .openbsd, .netbsd, .dragonfly] {
1319 // `#include bsd <...>` and any other code path that emits `defined(__BSD__)`
1320 // needs this; individual BSD/Darwin compilers do not predefine __BSD__.
1321 g.comptime_definitions.writeln('#define __BSD__ (1)')
1322 }
1323 if g.pref.gc_mode in [.boehm_full, .boehm_incr, .boehm_full_opt, .boehm_incr_opt, .boehm_leak] {
1324 g.comptime_definitions.writeln('#define _VGCBOEHM (1)')
1325 }
1326 if g.pref.is_debug {
1327 g.comptime_definitions.writeln('#define _VDEBUG (1)')
1328 }
1329 if g.pref.is_prod {
1330 g.comptime_definitions.writeln('#define _VPROD (1)')
1331 }
1332 if g.pref.is_test {
1333 g.comptime_definitions.writeln('#define _VTEST (1)')
1334 }
1335 if g.pref.is_prof {
1336 g.comptime_definitions.writeln('#define _VPROFILE (1)')
1337 }
1338 if g.pref.autofree {
1339 g.comptime_definitions.writeln('#define _VAUTOFREE (1)')
1340 }
1341 if g.pref.parallel_cc {
1342 g.comptime_definitions.writeln('#define _VPARALLELCC (1)')
1343 }
1344 if g.pref.prealloc {
1345 g.comptime_definitions.writeln('#define _VPREALLOC (1)')
1346 }
1347 if g.pref.use_cache {
1348 g.comptime_definitions.writeln('#define _VUSECACHE (1)')
1349 }
1350 if g.pref.build_mode == .build_module {
1351 g.comptime_definitions.writeln('#define _VBUILDMODULE (1)')
1352 }
1353 if g.pref.is_o {
1354 g.comptime_definitions.writeln('#define _VOBJECTFILE (1)')
1355 }
1356 if g.pref.is_livemain || g.pref.is_liveshared {
1357 g.generate_hotcode_reloading_declarations()
1358 }
1359 // we know that this is being called before the multi-threading starts
1360 // and this is being called in the main thread, so we can mutate the table
1361 mut muttable := unsafe { &ast.Table(g.table) }
1362 if g.use_segfault_handler {
1363 muttable.used_features.used_fns['v_segmentation_fault_handler'] = true
1364 }
1365 // muttable.used_features.used_fns['eprintln'] = true
1366 // muttable.used_features.used_fns['print_backtrace'] = true
1367 // muttable.used_features.used_fns['exit'] = true
1368}
1369
1370fn (g &Gen) should_use_object_local_linkage(mod string) bool {
1371 return g.pref.is_o && g.module_built != '' && mod != g.module_built
1372}
1373
1374pub fn (mut g Gen) finish() {
1375 if g.pref.is_prof && g.pref.build_mode != .build_module {
1376 g.gen_vprint_profile_stats()
1377 }
1378 if g.pref.is_livemain || g.pref.is_liveshared {
1379 g.generate_hotcode_reloader_code()
1380 }
1381 g.handle_embedded_files_finish()
1382 if g.pref.is_test {
1383 g.gen_c_main_for_tests()
1384 } else if (g.pref.is_shared || g.pref.is_liveshared) && g.pref.os == .windows
1385 && !g.has_user_defined_windows_dll_main() {
1386 // create DllMain() for windows .dll
1387 g.gen_dll_main()
1388 } else {
1389 g.gen_c_main()
1390 }
1391}
1392
1393// get_sumtype_variant_type_name returns the variant type name according to its type
1394@[inline]
1395pub fn (mut g Gen) get_sumtype_variant_type_name(typ ast.Type, sym ast.TypeSymbol) string {
1396 return if typ.has_flag(.option) {
1397 '_option_${sym.cname}'
1398 } else if sym.is_c_struct() {
1399 g.cc_type(typ, true)
1400 } else {
1401 sym.cname
1402 }
1403}
1404
1405// get_sumtype_variant_name returns the variant name according to its type
1406@[inline]
1407pub fn (mut g Gen) get_sumtype_variant_name(typ ast.Type, sym ast.TypeSymbol) string {
1408 return if typ.has_flag(.option) { '_option_${sym.cname}' } else { sym.cname }
1409}
1410
1411@[inline]
1412fn (g &Gen) sumtype_runtime_variants(typ ast.Type) []ast.Type {
1413 return g.table.sumtype_matchable_variants(typ.idx_type())
1414}
1415
1416// get_sumtype_casting_variant_name returns a helper-safe variant name that
1417// keeps pointer-depth distinctions for sumtype cast wrappers.
1418@[inline]
1419pub fn (mut g Gen) get_sumtype_casting_variant_name(typ ast.Type, sym ast.TypeSymbol) string {
1420 mut variant_name := g.get_sumtype_variant_name(typ, sym)
1421 if typ.nr_muls() > 0 {
1422 variant_name += '__ptr__'.repeat(typ.nr_muls())
1423 }
1424 return variant_name
1425}
1426
1427pub fn (mut g Gen) write_typeof_functions() {
1428 g.writeln('')
1429 g.writeln('// >> typeof() support for sum types / interfaces')
1430 mut already_generated_ifaces := map[string]bool{}
1431 for ityp, sym in g.table.type_symbols {
1432 if sym.kind == .sum_type {
1433 if g.pref.skip_unused && sym.idx !in g.table.used_features.used_syms {
1434 continue
1435 }
1436 static_prefix := if g.pref.build_mode == .build_module || g.pref.is_o {
1437 'static '
1438 } else {
1439 ''
1440 }
1441 sum_info := sym.info as ast.SumType
1442 if sum_info.is_generic {
1443 continue
1444 }
1445 g.writeln('${static_prefix}char * v_typeof_sumtype_${sym.cname}(u32 sidx) {')
1446 g.definitions.writeln('${static_prefix}char * v_typeof_sumtype_${sym.cname}(u32);')
1447 runtime_variants := g.sumtype_runtime_variants(ast.idx_to_type(ityp))
1448 if g.pref.build_mode == .build_module {
1449 g.writeln('\t\tif( sidx == _v_type_idx_${sym.cname}() ) return "${util.strip_main_name(sym.name)}";')
1450 for v in runtime_variants {
1451 subtype := g.table.sym(v)
1452 g.writeln('\tif( sidx == _v_type_idx_${g.get_sumtype_variant_name(v, subtype)}() ) return "${util.strip_main_name(subtype.name)}";')
1453 }
1454 g.writeln('\treturn "unknown ${util.strip_main_name(sym.name)}";')
1455 } else {
1456 tidx := g.table.find_type_idx(sym.name)
1457 g.writeln('\tswitch(sidx) {')
1458 g.writeln('\t\tcase ${tidx}: return "${util.strip_main_name(sym.name)}";')
1459 mut idxs := []ast.Type{}
1460 for v in runtime_variants {
1461 if v in idxs {
1462 continue
1463 }
1464 subtype := g.table.sym(v)
1465 g.writeln('\t\tcase ${u32(v)}: return "${util.strip_main_name(subtype.name)}";')
1466 idxs << v
1467 }
1468 g.writeln('\t\tdefault: return "unknown ${util.strip_main_name(sym.name)}";')
1469 g.writeln('\t}')
1470 }
1471 g.writeln2('}', '')
1472 g.writeln('${static_prefix}u32 v_typeof_sumtype_idx_${sym.cname}(u32 sidx) {')
1473 if g.pref.build_mode == .build_module {
1474 g.writeln('\t\tif( sidx == _v_type_idx_${sym.cname}() ) return ${u32(ityp)};')
1475 for v in runtime_variants {
1476 subtype := g.table.sym(v)
1477 g.writeln('\tif( sidx == _v_type_idx_${subtype.cname}() ) return ${u32(v)};')
1478 }
1479 g.writeln('\treturn ${u32(ityp)};')
1480 } else {
1481 tidx := g.table.find_type_idx(sym.name)
1482 g.writeln2('\tswitch(sidx) {', '\t\tcase ${tidx}: return ${u32(ityp)};')
1483 mut idxs := []ast.Type{}
1484 for v in runtime_variants {
1485 if v in idxs {
1486 continue
1487 }
1488 g.writeln('\t\tcase ${u32(v)}: return ${u32(v)};')
1489 idxs << v
1490 }
1491 g.writeln2('\t\tdefault: return ${u32(ityp)};', '\t}')
1492 }
1493 g.writeln('}')
1494 } else if sym.kind == .interface {
1495 if sym.info !is ast.Interface {
1496 continue
1497 }
1498 inter_info := sym.info as ast.Interface
1499 if inter_info.is_generic {
1500 continue
1501 }
1502 if g.pref.skip_unused && sym.idx !in g.table.used_features.used_syms {
1503 continue
1504 }
1505 if sym.cname in already_generated_ifaces {
1506 continue
1507 }
1508 already_generated_ifaces[sym.cname] = true
1509 impl_types := g.runtime_interface_variants(inter_info)
1510 g.definitions.writeln('${g.static_non_parallel}char * v_typeof_interface_${sym.cname}(u32 sidx);')
1511 if g.pref.parallel_cc {
1512 g.extern_out.writeln('extern char * v_typeof_interface_${sym.cname}(u32 sidx);')
1513 }
1514 g.writeln('${g.static_non_parallel}char * v_typeof_interface_${sym.cname}(u32 sidx) {')
1515 for t in impl_types {
1516 sub_sym := g.table.sym(ast.mktyp(t))
1517 if sub_sym.kind == .interface {
1518 continue
1519 }
1520 if sub_sym.info is ast.Struct && sub_sym.info.is_unresolved_generic() {
1521 continue
1522 }
1523 if g.pref.skip_unused && sub_sym.kind == .struct
1524 && sub_sym.idx !in g.table.used_features.used_syms {
1525 continue
1526 }
1527 g.writeln('\tif (sidx == _${sym.cname}_${sub_sym.cname}_index) return "${util.strip_main_name(sub_sym.name)}";')
1528 }
1529 g.writeln2('\treturn "unknown ${util.strip_main_name(sym.name)}";', '}')
1530 // Avoid duplicate symbol '_v_typeof_interface_idx_IError' when using -usecache
1531 if g.pref.build_mode != .build_module {
1532 interface_idx_static_prefix := if g.pref.is_o { 'static ' } else { '' }
1533 g.definitions.writeln('${interface_idx_static_prefix}u32 v_typeof_interface_idx_${sym.cname}(u32 sidx);')
1534 g.writeln2('',
1535 '${interface_idx_static_prefix}u32 v_typeof_interface_idx_${sym.cname}(u32 sidx) {')
1536 if g.pref.parallel_cc && interface_idx_static_prefix == '' {
1537 g.extern_out.writeln('extern u32 v_typeof_interface_idx_${sym.cname}(u32 sidx);')
1538 }
1539 for t in impl_types {
1540 sub_sym := g.table.sym(ast.mktyp(t))
1541 if sub_sym.kind == .interface {
1542 continue
1543 }
1544 if sub_sym.info is ast.Struct && sub_sym.info.is_unresolved_generic() {
1545 continue
1546 }
1547 if g.pref.skip_unused && sub_sym.kind == .struct
1548 && sub_sym.idx !in g.table.used_features.used_syms {
1549 continue
1550 }
1551 g.writeln('\tif (sidx == _${sym.cname}_${sub_sym.cname}_index) return ${u32(t.set_nr_muls(0))};')
1552 }
1553 g.writeln2('\treturn ${u32(ityp)};', '}')
1554 }
1555 }
1556 }
1557 g.writeln2('// << typeof() support for sum types', '')
1558}
1559
1560// V type to C typecc
1561@[inline]
1562fn (mut g Gen) styp(t ast.Type) string {
1563 if !t.has_option_or_result() {
1564 return g.base_type(t)
1565 } else if t.has_flag(.result) {
1566 return g.register_result(t)
1567 } else if t.has_flag(.option) {
1568 // Register an optional if it's not registered yet
1569 return g.register_option(t)
1570 } else {
1571 return g.base_type(t)
1572 }
1573}
1574
1575fn (mut g Gen) base_type(_t ast.Type) string {
1576 mut t := g.unwrap_generic(_t)
1577 // Safeguard: if unwrap_generic didn't convert because the generic flag wasn't set,
1578 // but the symbol name is a generic name, force the conversion
1579 if g.cur_fn != unsafe { nil } && g.cur_fn.generic_names.len > 0 && g.cur_concrete_types.len > 0 {
1580 sym := g.table.sym(t)
1581 if sym.name in g.cur_fn.generic_names {
1582 if utyp := g.table.convert_generic_type(t.set_flag(.generic), g.cur_fn.generic_names,
1583 g.cur_concrete_types)
1584 {
1585 t = utyp
1586 }
1587 }
1588 }
1589 if styp := g.styp_cache[t] {
1590 // Ensure late array typedefs are emitted even for cached types
1591 if g.done_typedef_phase {
1592 g.ensure_array_typedef(styp.trim_right('*'))
1593 }
1594 return styp
1595 }
1596 if g.pref.nofloat {
1597 // TODO: compile time if for perf?
1598 if t == ast.f32_type {
1599 return 'u32'
1600 } else if t == ast.f64_type {
1601 return 'u64'
1602 }
1603 }
1604 share := t.share()
1605 mut styp := if share == .atomic_t { t.atomic_typename() } else { g.cc_type(t, true) }
1606 if t.has_flag(.shared_f) {
1607 styp = g.find_or_register_shared(t, styp)
1608 }
1609 // During generic codegen, array types resolved from generic parameters
1610 // (e.g. []?T → []?i8) may not have been registered in the type table
1611 // early enough for write_typedef_types(). Emit a late typedef if needed.
1612 if g.done_typedef_phase {
1613 g.ensure_array_typedef(styp)
1614 }
1615 nr_muls := t.nr_muls()
1616 if nr_muls > 0 {
1617 styp += strings.repeat(`*`, nr_muls)
1618 }
1619 g.styp_cache[t] = styp
1620 return styp
1621}
1622
1623fn (mut g Gen) can_write_interface_typesymbol_declaration(sym ast.TypeSymbol) bool {
1624 if sym.info !is ast.Interface {
1625 return false
1626 }
1627 info := sym.info as ast.Interface
1628 if !info.is_generic {
1629 return true
1630 }
1631 return !info.fields.any(it.typ.has_flag(.generic) || g.type_has_unresolved_generic_parts(it.typ))
1632}
1633
1634fn (mut g Gen) generic_fn_name(types []ast.Type, before string) string {
1635 if types.len == 0 {
1636 return before
1637 }
1638 // Using _T_ to differentiate between get[string] and get_string
1639 // `foo[int]()` => `foo_T_int()`
1640 mut name := before + '_T'
1641 for typ in types {
1642 normalized_typ := ast.mktyp(typ)
1643 mut typ_name := g.styp(normalized_typ.set_nr_muls(0))
1644 // Normalize named function types to their anonymous form for consistent naming.
1645 // E.g. `neg` (a specific fn) should produce the same name as `fn(int) int`.
1646 sym := g.table.sym(normalized_typ)
1647 if sym.info is ast.FnType && sym.kind == .function {
1648 mut anon_cname := 'anon_fn_${g.table.fn_type_signature(sym.info.func)}'
1649 if normalized_typ.has_flag(.option) {
1650 anon_cname = '${option_name}_${anon_cname}'
1651 } else if normalized_typ.has_flag(.result) {
1652 anon_cname = '${result_name}_${anon_cname}'
1653 }
1654 if normalized_typ.has_flag(.shared_f) {
1655 anon_cname = '__shared__${anon_cname}'
1656 } else if normalized_typ.has_flag(.atomic_f) {
1657 anon_cname = 'atomic_${anon_cname}'
1658 }
1659 if anon_cname != typ_name {
1660 typ_name = anon_cname
1661 }
1662 }
1663 name += '_' + strings.repeat_string('__ptr__', normalized_typ.nr_muls()) +
1664 typ_name.replace(' ', '_')
1665 }
1666 return name
1667}
1668
1669fn (mut g Gen) expr_string(expr ast.Expr) string {
1670 pos := g.out.len
1671 // pos2 := g.out_parallel[g.out_idx].len
1672 g.expr(expr)
1673 // g.out_parallel[g.out_idx].cut_to(pos2)
1674 return g.out.cut_to(pos).trim_space()
1675}
1676
1677fn (mut g Gen) expr_string_opt(typ ast.Type, expr ast.Expr) string {
1678 expr_str := g.expr_string(expr)
1679 if expr is ast.None {
1680 return '(${g.styp(typ)}){.state=2, .err=${expr_str}, .data={E_STRUCT}}'
1681 }
1682 return expr_str
1683}
1684
1685fn (mut g Gen) expr_string_with_cast(expr ast.Expr, typ ast.Type, exp ast.Type) string {
1686 pos := g.out.len
1687 // pos2 := g.out_parallel[g.out_idx].len
1688 g.expr_with_cast(expr, typ, exp)
1689 // g.out_parallel[g.out_idx].cut_to(pos2)
1690 return g.out.cut_to(pos).trim_space()
1691}
1692
1693// Surround a potentially multi-statement expression safely with `prepend` and `append`.
1694// (and create a statement)
1695fn (mut g Gen) expr_string_surround(prepend string, expr ast.Expr, append string) string {
1696 pos := g.out.len
1697 // pos2 := g.out_parallel[g.out_idx].len
1698 g.stmt_path_pos << pos
1699 defer {
1700 g.stmt_path_pos.delete_last()
1701 }
1702 g.write(prepend)
1703 g.expr(expr)
1704 g.write(append)
1705 // g.out_parallel[g.out_idx].cut_to(pos2)
1706 return g.out.cut_to(pos)
1707}
1708
1709// TODO: this really shouldn't be separate from typ
1710// but I(emily) would rather have this generation
1711// all unified in one place so that it doesn't break
1712// if one location changes
1713fn (mut g Gen) option_type_name(t ast.Type) (string, string) {
1714 option_typ := if t.has_flag(.option_mut_param_t) && t.is_ptr() {
1715 t.deref().clear_flag(.option_mut_param_t)
1716 } else {
1717 t
1718 }
1719 mut base := g.base_type(option_typ)
1720 mut styp := ''
1721 sym := g.table.sym(option_typ)
1722 // If this is a type alias to an option type, use the parent type's option name
1723 // This ensures that ?int and MaybeInt (where MaybeInt = ?int) generate the same C type
1724 if sym.kind == .alias && sym.info is ast.Alias && sym.info.parent_type.has_flag(.option) {
1725 return g.option_type_name(sym.info.parent_type)
1726 }
1727 if sym.info is ast.FnType {
1728 base = 'anon_fn_${g.table.fn_type_signature(sym.info.func)}'
1729 }
1730 if sym.language == .c && sym.kind == .struct {
1731 styp = '${option_name}_${base.replace(' ', '_')}'
1732 } else {
1733 styp = '${option_name}_${base}'
1734 }
1735 if option_typ.has_flag(.generic) || option_typ.is_ptr() {
1736 styp = styp.replace('*', '_ptr')
1737 }
1738 return styp, base
1739}
1740
1741fn (mut g Gen) type_has_unresolved_generic_parts(typ ast.Type) bool {
1742 if typ == 0 {
1743 return false
1744 }
1745 if typ.has_flag(.generic) {
1746 return true
1747 }
1748 idx := typ.idx()
1749 if idx <= ast.nil_type_idx {
1750 return false
1751 }
1752 if idx < g.generic_parts_cache.len {
1753 cached := g.generic_parts_cache[idx]
1754 if cached != 0 {
1755 return cached == 2
1756 }
1757 } else if idx < g.table.type_symbols.len {
1758 g.generic_parts_cache << []i8{len: idx - g.generic_parts_cache.len + 1}
1759 }
1760 resolved := g.type_has_unresolved_generic_parts_uncached(typ)
1761 if idx < g.generic_parts_cache.len {
1762 g.generic_parts_cache[idx] = if resolved { i8(2) } else { i8(1) }
1763 }
1764 return resolved
1765}
1766
1767fn (mut g Gen) type_has_unresolved_generic_parts_uncached(typ ast.Type) bool {
1768 sym := g.table.sym(typ)
1769 if sym.kind == .placeholder || (sym.kind == .any && !sym.is_builtin()) {
1770 return true
1771 }
1772 match sym.info {
1773 ast.Array {
1774 return g.type_has_unresolved_generic_parts(sym.info.elem_type)
1775 }
1776 ast.ArrayFixed {
1777 return g.type_has_unresolved_generic_parts(sym.info.elem_type)
1778 }
1779 ast.Chan {
1780 return g.type_has_unresolved_generic_parts(sym.info.elem_type)
1781 }
1782 ast.Thread {
1783 return g.type_has_unresolved_generic_parts(sym.info.return_type)
1784 }
1785 ast.Map {
1786 return g.type_has_unresolved_generic_parts(sym.info.key_type)
1787 || g.type_has_unresolved_generic_parts(sym.info.value_type)
1788 }
1789 ast.FnType {
1790 return g.type_has_unresolved_generic_parts(sym.info.func.return_type)
1791 || sym.info.func.params.any(g.type_has_unresolved_generic_parts(it.typ))
1792 }
1793 ast.MultiReturn {
1794 return sym.info.types.any(g.type_has_unresolved_generic_parts(it))
1795 }
1796 ast.Struct {
1797 if sym.info.concrete_types.len > 0 {
1798 return sym.info.concrete_types.any(g.type_has_unresolved_generic_parts(it))
1799 }
1800 if sym.generic_types.len == sym.info.generic_types.len
1801 && sym.generic_types != sym.info.generic_types {
1802 return sym.generic_types.any(g.type_has_unresolved_generic_parts(it))
1803 }
1804 return sym.info.generic_types.len > 0
1805 }
1806 ast.Interface {
1807 if sym.info.concrete_types.len > 0 {
1808 return sym.info.concrete_types.any(g.type_has_unresolved_generic_parts(it))
1809 }
1810 if sym.generic_types.len == sym.info.generic_types.len
1811 && sym.generic_types != sym.info.generic_types {
1812 return sym.generic_types.any(g.type_has_unresolved_generic_parts(it))
1813 }
1814 return sym.info.is_generic
1815 }
1816 ast.SumType {
1817 if sym.info.concrete_types.len > 0 {
1818 return sym.info.concrete_types.any(g.type_has_unresolved_generic_parts(it))
1819 }
1820 if sym.generic_types.len == sym.info.generic_types.len
1821 && sym.generic_types != sym.info.generic_types {
1822 return sym.generic_types.any(g.type_has_unresolved_generic_parts(it))
1823 }
1824 return sym.info.is_generic
1825 }
1826 ast.GenericInst {
1827 return sym.info.concrete_types.any(g.type_has_unresolved_generic_parts(it))
1828 }
1829 ast.UnknownTypeInfo {
1830 if sym.name.contains('[') && sym.name.contains(']') {
1831 args := sym.name.all_after('[').trim_right(']').split(',').map(it.trim_space())
1832 return args.any(util.is_generic_type_name(it))
1833 }
1834 return false
1835 }
1836 else {
1837 return false
1838 }
1839 }
1840}
1841
1842fn (mut g Gen) typedef_type_has_unresolved_generic_placeholder_parts(typ ast.Type) bool {
1843 if typ == 0 {
1844 return false
1845 }
1846 if typ.has_flag(.generic) {
1847 return true
1848 }
1849 idx := typ.idx()
1850 if idx <= ast.nil_type_idx {
1851 return false
1852 }
1853 sym := g.table.sym(typ)
1854 if sym.kind == .placeholder || (sym.kind == .any && !sym.is_builtin()) {
1855 return true
1856 }
1857 match sym.info {
1858 ast.Array {
1859 return g.typedef_type_has_unresolved_generic_placeholder_parts(sym.info.elem_type)
1860 }
1861 ast.ArrayFixed {
1862 return g.typedef_type_has_unresolved_generic_placeholder_parts(sym.info.elem_type)
1863 }
1864 ast.Chan {
1865 return g.typedef_type_has_unresolved_generic_placeholder_parts(sym.info.elem_type)
1866 }
1867 ast.Thread {
1868 return g.typedef_type_has_unresolved_generic_placeholder_parts(sym.info.return_type)
1869 }
1870 ast.Map {
1871 if g.typedef_type_has_unresolved_generic_placeholder_parts(sym.info.key_type) {
1872 return true
1873 }
1874 return g.typedef_type_has_unresolved_generic_placeholder_parts(sym.info.value_type)
1875 }
1876 ast.FnType {
1877 if g.typedef_type_has_unresolved_generic_placeholder_parts(sym.info.func.return_type) {
1878 return true
1879 }
1880 for param in sym.info.func.params {
1881 if g.typedef_type_has_unresolved_generic_placeholder_parts(param.typ) {
1882 return true
1883 }
1884 }
1885 return false
1886 }
1887 ast.MultiReturn {
1888 for typ_ in sym.info.types {
1889 if g.typedef_type_has_unresolved_generic_placeholder_parts(typ_) {
1890 return true
1891 }
1892 }
1893 return false
1894 }
1895 ast.Struct {
1896 for concrete_type in sym.info.concrete_types {
1897 if g.typedef_type_has_unresolved_generic_placeholder_parts(concrete_type) {
1898 return true
1899 }
1900 }
1901 return false
1902 }
1903 ast.Interface {
1904 for concrete_type in sym.info.concrete_types {
1905 if g.typedef_type_has_unresolved_generic_placeholder_parts(concrete_type) {
1906 return true
1907 }
1908 }
1909 return false
1910 }
1911 ast.SumType {
1912 for concrete_type in sym.info.concrete_types {
1913 if g.typedef_type_has_unresolved_generic_placeholder_parts(concrete_type) {
1914 return true
1915 }
1916 }
1917 return false
1918 }
1919 ast.GenericInst {
1920 for concrete_type in sym.info.concrete_types {
1921 if g.typedef_type_has_unresolved_generic_placeholder_parts(concrete_type) {
1922 return true
1923 }
1924 }
1925 return false
1926 }
1927 ast.UnknownTypeInfo {
1928 if util.is_generic_type_name(sym.name) {
1929 return true
1930 }
1931 if sym.name.contains('[') && sym.name.contains(']') {
1932 args := sym.name.all_after('[').trim_right(']').split(',')
1933 for arg in args {
1934 if util.is_generic_type_name(arg.trim_space()) {
1935 return true
1936 }
1937 }
1938 }
1939 return false
1940 }
1941 else {
1942 return false
1943 }
1944 }
1945}
1946
1947fn (mut g Gen) result_type_name(t ast.Type) (string, string) {
1948 mut base := g.base_type(t)
1949 mut styp := ''
1950 sym := g.table.sym(t)
1951 // If this is a type alias to a result type, use the parent type's result name
1952 if sym.kind == .alias && sym.info is ast.Alias && sym.info.parent_type.has_flag(.result) {
1953 return g.result_type_name(sym.info.parent_type)
1954 }
1955 if t.has_flag(.option) {
1956 g.register_option(t)
1957 base = '_option_' + base
1958 }
1959 if sym.info is ast.FnType {
1960 base = 'anon_fn_${g.table.fn_type_signature(sym.info.func)}'
1961 }
1962 if sym.language == .c && sym.kind == .struct {
1963 styp = '${result_name}_${base.replace(' ', '_')}'
1964 } else {
1965 styp = '${result_name}_${base}'
1966 }
1967 if t.has_flag(.generic) || t.is_ptr() {
1968 styp = styp.replace('*', '_ptr')
1969 }
1970 return styp, base
1971}
1972
1973fn (g &Gen) option_type_text(styp string, base string) string {
1974 // replace void with something else
1975 size := if base == 'void' {
1976 'u8'
1977 } else if base == 'int' {
1978 ast.int_type_name
1979 } else if base.starts_with('anon_fn') {
1980 'void*'
1981 } else if base.starts_with('_option_') {
1982 base.replace('*', '')
1983 } else {
1984 if base.starts_with('struct ') && !base.ends_with('*') { '${base}*' } else { base }
1985 }
1986 ret := 'struct ${styp} {
1987 byte state;
1988 IError err;
1989 byte data[sizeof(${size}) > 1 ? sizeof(${size}) : 1];
1990}'
1991 return ret
1992}
1993
1994fn (g &Gen) result_type_text(styp string, base string) string {
1995 // replace void with something else
1996 size := if base == 'void' {
1997 'u8'
1998 } else if base == 'int' {
1999 ast.int_type_name
2000 } else if base.starts_with('anon_fn') {
2001 'void*'
2002 } else if base.starts_with('_option_') {
2003 base.replace('*', '')
2004 } else {
2005 if base.starts_with('struct ') && !base.ends_with('*') { '${base}*' } else { base }
2006 }
2007 ret := 'struct ${styp} {
2008 bool is_error;
2009 IError err;
2010 byte data[sizeof(${size}) > 1 ? sizeof(${size}) : 1];
2011}'
2012 return ret
2013}
2014
2015fn (mut g Gen) register_option(t ast.Type) string {
2016 resolved_t := g.unwrap_generic(t)
2017 styp, base := g.option_type_name(resolved_t)
2018 // Only skip registration if the computed base is still an unresolved generic type name
2019 // (base_type should have resolved generics, but check to be safe)
2020 is_unresolved_generic := (g.cur_fn != unsafe { nil } && base in g.cur_fn.generic_names)
2021 || g.type_has_unresolved_generic_parts(resolved_t)
2022 if !is_unresolved_generic {
2023 g.options[base] = styp
2024 }
2025 return if !t.has_flag(.option_mut_param_t) { styp } else { '${styp}*' }
2026}
2027
2028fn (mut g Gen) register_result(t ast.Type) string {
2029 resolved_t := g.unwrap_generic(t)
2030 styp, base := g.result_type_name(resolved_t)
2031 // Only skip registration if the computed base is still an unresolved generic type name
2032 is_unresolved_generic := (g.cur_fn != unsafe { nil } && base in g.cur_fn.generic_names)
2033 || g.type_has_unresolved_generic_parts(resolved_t)
2034 if !is_unresolved_generic {
2035 g.results[base] = styp
2036 }
2037 return styp
2038}
2039
2040fn (mut g Gen) write_options() {
2041 mut done := []string{}
2042 rlock g.done_options {
2043 done = g.done_options.clone()
2044 }
2045 for base, styp in g.options {
2046 if base in done {
2047 continue
2048 }
2049 done << base
2050 g.typedefs.writeln('typedef struct ${styp} ${styp};')
2051 g.ensure_array_typedef(base)
2052 if base in g.options_forward {
2053 g.out_options_forward.write_string(g.option_type_text(styp, base) + ';\n\n')
2054 } else {
2055 g.out_options.write_string(g.option_type_text(styp, base) + ';\n\n')
2056 }
2057 }
2058}
2059
2060fn (mut g Gen) write_results() {
2061 mut done := []string{}
2062 rlock g.done_results {
2063 done = g.done_results.clone()
2064 }
2065 for base, styp in g.results {
2066 if base in done {
2067 continue
2068 }
2069 done << base
2070 g.typedefs.writeln('typedef struct ${styp} ${styp};')
2071 g.ensure_array_typedef(base)
2072 if base in g.results_forward {
2073 g.out_results_forward.write_string(g.result_type_text(styp, base) + ';\n\n')
2074 } else {
2075 g.out_results.write_string(g.result_type_text(styp, base) + ';\n\n')
2076 }
2077 }
2078 for k, _ in g.table.anon_struct_names {
2079 ck := c_name(k)
2080 g.typedefs.writeln('typedef struct ${ck} ${ck};')
2081 }
2082 for k, _ in g.table.anon_union_names {
2083 ck := c_name(k)
2084 g.typedefs.writeln('typedef union ${ck} ${ck};')
2085 }
2086}
2087
2088fn (mut g Gen) find_or_register_shared(t ast.Type, base string) string {
2089 g.shareds[t.idx()] = base
2090 return '__shared__${base}'
2091}
2092
2093fn (mut g Gen) write_shareds() {
2094 mut done_types := []int{}
2095 for typ, base in g.shareds {
2096 if typ in done_types {
2097 continue
2098 }
2099 done_types << typ
2100 sh_typ := '__shared__${base}'
2101 mtx_typ := 'sync__RwMutex'
2102 g.shared_types.writeln('struct ${sh_typ} {')
2103 g.shared_types.writeln('\t${mtx_typ} mtx;')
2104 g.shared_types.writeln('\t${base} val;')
2105 g.shared_types.writeln('};')
2106 g.shared_functions.writeln('static inline voidptr __dup${sh_typ}(voidptr src, ${ast.int_type_name} sz) {')
2107 g.shared_functions.writeln('\t${sh_typ}* dest = builtin__memdup(src, sz);')
2108 g.shared_functions.writeln('\tsync__RwMutex_init(&dest->mtx);')
2109 g.shared_functions.writeln('\treturn dest;')
2110 g.shared_functions.writeln('}')
2111 g.typedefs.writeln('typedef struct ${sh_typ} ${sh_typ};')
2112 }
2113}
2114
2115fn (mut g Gen) register_thread_void_wait_call() {
2116 lock g.waiter_fns {
2117 if '__v_thread_wait' in g.waiter_fns {
2118 return
2119 }
2120 g.waiter_fns << '__v_thread_wait'
2121 g.waiter_fn_definitions.writeln('void __v_thread_wait(__v_thread thread);')
2122 }
2123 g.gowrappers.writeln('void __v_thread_wait(__v_thread thread) {')
2124 if g.pref.os == .windows {
2125 g.gowrappers.writeln('\tu32 stat = WaitForSingleObject(thread, INFINITE);')
2126 g.gowrappers.writeln('\tif (stat != WAIT_OBJECT_0) { builtin___v_panic(_S("error waiting thread")); }')
2127 } else {
2128 g.gowrappers.writeln('\tint stat = pthread_join(thread, (void **)NULL);')
2129 g.gowrappers.writeln('\tif (stat != 0) { builtin___v_panic(_S("error waiting thread")); }')
2130 }
2131 g.gowrappers.writeln('}')
2132}
2133
2134fn (mut g Gen) register_thread_wait_call(eltyp string) {
2135 thread_typ := '__v_thread_${eltyp}'
2136 fn_name := '${thread_typ}_wait'
2137 lock g.waiter_fns {
2138 if fn_name in g.waiter_fns {
2139 return
2140 }
2141 g.waiter_fns << fn_name
2142 g.waiter_fn_definitions.writeln('${eltyp} ${fn_name}(${thread_typ} thread);')
2143 }
2144 g.gowrappers.writeln('${eltyp} ${fn_name}(${thread_typ} thread) {')
2145 g.gowrappers.writeln('\t${eltyp} res;')
2146 if g.pref.os == .windows {
2147 g.gowrappers.writeln('\tu32 stat = WaitForSingleObject(thread.handle, INFINITE);')
2148 g.gowrappers.writeln('\tif (stat != WAIT_OBJECT_0) { builtin___v_panic(_S("error waiting thread")); }')
2149 g.gowrappers.writeln('\tCloseHandle(thread.handle);')
2150 g.gowrappers.writeln('\tres = *(${eltyp}*)(thread.ret_ptr);')
2151 if g.pref.prealloc {
2152 g.gowrappers.writeln('\tfree(thread.ret_ptr);')
2153 } else {
2154 g.gowrappers.writeln('\tbuiltin___v_free(thread.ret_ptr);')
2155 }
2156 } else {
2157 g.gowrappers.writeln('\tvoid* ret_val;')
2158 g.gowrappers.writeln('\tint stat = pthread_join(thread, &ret_val);')
2159 g.gowrappers.writeln('\tif (stat != 0) { builtin___v_panic(_S("error waiting thread")); }')
2160 g.gowrappers.writeln('\tres = *(${eltyp}*)ret_val;')
2161 if g.pref.prealloc {
2162 g.gowrappers.writeln('\tfree(ret_val);')
2163 } else {
2164 g.gowrappers.writeln('\tbuiltin___v_free(ret_val);')
2165 }
2166 }
2167 g.gowrappers.writeln('\treturn res;')
2168 g.gowrappers.writeln('}')
2169}
2170
2171fn (mut g Gen) thread_array_wait_return_type(thread_ret_type ast.Type) ast.Type {
2172 payload_type := thread_ret_type.clear_option_and_result()
2173 if payload_type == ast.void_type {
2174 return thread_ret_type
2175 }
2176 mut return_type := ast.idx_to_type(g.table.find_or_register_array(payload_type))
2177 if thread_ret_type.has_flag(.option) {
2178 return_type = return_type.set_flag(.option)
2179 }
2180 if thread_ret_type.has_flag(.result) {
2181 return_type = return_type.set_flag(.result)
2182 }
2183 return return_type
2184}
2185
2186fn (mut g Gen) register_thread_array_wait_call(thread_ret_type ast.Type) string {
2187 payload_type := thread_ret_type.clear_option_and_result()
2188 is_plain_void := thread_ret_type == ast.void_type
2189 is_void_payload := payload_type == ast.void_type
2190 thread_typ := g.gen_gohandle_name(thread_ret_type)
2191 waiter_fn_name := '${thread_typ}_wait'
2192 ret_type := g.thread_array_wait_return_type(thread_ret_type)
2193 ret_styp := g.styp(ret_type)
2194 thread_arr_typ := 'Array_${thread_typ}'
2195 fn_name := '${thread_arr_typ}_wait'
2196 mut should_register := false
2197 lock g.waiter_fns {
2198 if fn_name !in g.waiter_fns {
2199 g.waiter_fns << fn_name
2200 should_register = true
2201 }
2202 }
2203 if should_register {
2204 thread_ret_styp := g.styp(thread_ret_type)
2205 g.create_waiter_handler(thread_ret_type, thread_ret_styp, thread_typ)
2206 g.waiter_fn_definitions.writeln('${ret_styp} ${fn_name}(${thread_arr_typ} a);')
2207 g.gowrappers.writeln('
2208${ret_styp} ${fn_name}(${thread_arr_typ} a) {')
2209 if !is_void_payload {
2210 payload_arr_styp := g.base_type(ret_type.clear_option_and_result())
2211 payload_styp := g.styp(payload_type)
2212 g.gowrappers.writeln('\t${payload_arr_styp} res = builtin____new_array_with_default(a.len, a.len, sizeof(${payload_styp}), 0);')
2213 }
2214 if thread_ret_type.has_option_or_result() {
2215 g.gowrappers.writeln('\tbool has_failure = false;')
2216 g.gowrappers.writeln('\t${thread_ret_styp} first_failure = {0};')
2217 }
2218 g.gowrappers.writeln('\tfor (${ast.int_type_name} i = 0; i < a.len; ++i) {')
2219 g.gowrappers.writeln('\t\t${thread_typ} t = ((${thread_typ}*)a.data)[i];')
2220 if g.pref.os == .windows {
2221 if is_plain_void {
2222 g.gowrappers.writeln('\t\tif (t == 0) continue;')
2223 } else {
2224 g.gowrappers.writeln('\t\tif (t.handle == 0) continue;')
2225 }
2226 } else {
2227 g.gowrappers.writeln('\t\tif (t == 0) continue;')
2228 }
2229 if thread_ret_type.has_flag(.option) {
2230 payload_arr_styp := if is_void_payload {
2231 ''
2232 } else {
2233 g.base_type(ret_type.clear_option_and_result())
2234 }
2235 payload_styp := if is_void_payload { '' } else { g.styp(payload_type) }
2236 g.gowrappers.writeln('\t\t${thread_ret_styp} waited = ${waiter_fn_name}(t);')
2237 g.gowrappers.writeln('\t\tif (waited.state != 0) {')
2238 g.gowrappers.writeln('\t\t\tif (!has_failure) {')
2239 g.gowrappers.writeln('\t\t\t\thas_failure = true;')
2240 g.gowrappers.writeln('\t\t\t\tfirst_failure = waited;')
2241 g.gowrappers.writeln('\t\t\t}')
2242 g.gowrappers.writeln('\t\t\tcontinue;')
2243 g.gowrappers.writeln('\t\t}')
2244 if !is_void_payload {
2245 g.gowrappers.writeln('\t\t((${payload_styp}*)res.data)[i] = *((${payload_styp}*)waited.data);')
2246 }
2247 g.gowrappers.writeln('\t}')
2248 g.gowrappers.writeln('\tif (has_failure) {')
2249 if !is_void_payload {
2250 g.gowrappers.writeln('\t\tbuiltin__array_free(&res);')
2251 }
2252 if is_void_payload {
2253 g.gowrappers.writeln('\t\treturn first_failure;')
2254 } else {
2255 g.gowrappers.writeln('\t\treturn (${ret_styp}){ .state = first_failure.state, .err = first_failure.err, .data = {E_STRUCT} };')
2256 }
2257 g.gowrappers.writeln('\t}')
2258 if is_void_payload {
2259 g.gowrappers.writeln('\treturn (${ret_styp}){0};')
2260 } else {
2261 g.gowrappers.writeln('\t${ret_styp} waited_all = {0};')
2262 g.gowrappers.writeln('\tbuiltin___option_ok(&(${payload_arr_styp}[]) { res }, (${option_name}*)(&waited_all), sizeof(${payload_arr_styp}));')
2263 g.gowrappers.writeln('\treturn waited_all;')
2264 }
2265 } else if thread_ret_type.has_flag(.result) {
2266 payload_arr_styp := if is_void_payload {
2267 ''
2268 } else {
2269 g.base_type(ret_type.clear_option_and_result())
2270 }
2271 payload_styp := if is_void_payload { '' } else { g.styp(payload_type) }
2272 g.gowrappers.writeln('\t\t${thread_ret_styp} waited = ${waiter_fn_name}(t);')
2273 g.gowrappers.writeln('\t\tif (waited.is_error) {')
2274 g.gowrappers.writeln('\t\t\tif (!has_failure) {')
2275 g.gowrappers.writeln('\t\t\t\thas_failure = true;')
2276 g.gowrappers.writeln('\t\t\t\tfirst_failure = waited;')
2277 g.gowrappers.writeln('\t\t\t}')
2278 g.gowrappers.writeln('\t\t\tcontinue;')
2279 g.gowrappers.writeln('\t\t}')
2280 if !is_void_payload {
2281 g.gowrappers.writeln('\t\t((${payload_styp}*)res.data)[i] = *((${payload_styp}*)waited.data);')
2282 }
2283 g.gowrappers.writeln('\t}')
2284 g.gowrappers.writeln('\tif (has_failure) {')
2285 if !is_void_payload {
2286 g.gowrappers.writeln('\t\tbuiltin__array_free(&res);')
2287 }
2288 if is_void_payload {
2289 g.gowrappers.writeln('\t\treturn first_failure;')
2290 } else {
2291 g.gowrappers.writeln('\t\treturn (${ret_styp}){ .is_error = true, .err = first_failure.err, .data = {E_STRUCT} };')
2292 }
2293 g.gowrappers.writeln('\t}')
2294 if is_void_payload {
2295 g.gowrappers.writeln('\treturn (${ret_styp}){0};')
2296 } else {
2297 g.gowrappers.writeln('\t${ret_styp} waited_all = {0};')
2298 g.gowrappers.writeln('\tbuiltin___result_ok(&(${payload_arr_styp}[]) { res }, (${result_name}*)(&waited_all), sizeof(${payload_arr_styp}));')
2299 g.gowrappers.writeln('\treturn waited_all;')
2300 }
2301 } else if is_void_payload {
2302 g.gowrappers.writeln('\t\t${waiter_fn_name}(t);')
2303 g.gowrappers.writeln('\t}')
2304 } else {
2305 payload_styp := g.styp(payload_type)
2306 g.gowrappers.writeln('\t\t((${payload_styp}*)res.data)[i] = ${waiter_fn_name}(t);')
2307 g.gowrappers.writeln('\t}')
2308 g.gowrappers.writeln('\treturn res;')
2309 }
2310 g.gowrappers.writeln('}')
2311 }
2312 return fn_name
2313}
2314
2315fn (mut g Gen) register_thread_fixed_array_wait_call(node ast.CallExpr, thread_ret_type ast.Type) string {
2316 payload_type := thread_ret_type.clear_option_and_result()
2317 is_plain_void := thread_ret_type == ast.void_type
2318 is_void_payload := payload_type == ast.void_type
2319 thread_typ := g.gen_gohandle_name(thread_ret_type)
2320 waiter_fn_name := '${thread_typ}_wait'
2321 ret_type := g.thread_array_wait_return_type(thread_ret_type)
2322 ret_styp := g.styp(ret_type)
2323 rec_sym := g.table.sym(node.receiver_type)
2324 len := (rec_sym.info as ast.ArrayFixed).size
2325 thread_arr_typ := rec_sym.cname
2326 fn_name := '${thread_arr_typ}_wait'
2327 mut should_register := false
2328 lock g.waiter_fns {
2329 if fn_name !in g.waiter_fns {
2330 g.waiter_fns << fn_name
2331 should_register = true
2332 }
2333 }
2334 if should_register {
2335 thread_ret_styp := g.styp(thread_ret_type)
2336 g.create_waiter_handler(thread_ret_type, thread_ret_styp, thread_typ)
2337 g.waiter_fn_definitions.writeln('${ret_styp} ${fn_name}(${thread_arr_typ} a);')
2338 g.gowrappers.writeln('
2339${ret_styp} ${fn_name}(${thread_arr_typ} a) {')
2340 if !is_void_payload {
2341 payload_arr_styp := g.base_type(ret_type.clear_option_and_result())
2342 payload_styp := g.styp(payload_type)
2343 g.gowrappers.writeln('\t${payload_arr_styp} res = builtin____new_array_with_default(${len}, ${len}, sizeof(${payload_styp}), 0);')
2344 }
2345 if thread_ret_type.has_option_or_result() {
2346 g.gowrappers.writeln('\tbool has_failure = false;')
2347 g.gowrappers.writeln('\t${thread_ret_styp} first_failure = {0};')
2348 }
2349 g.gowrappers.writeln('\tfor (${ast.int_type_name} i = 0; i < ${len}; ++i) {')
2350 g.gowrappers.writeln('\t\t${thread_typ} t = a[i];')
2351 if g.pref.os == .windows {
2352 if is_plain_void {
2353 g.gowrappers.writeln('\t\tif (t == 0) continue;')
2354 } else {
2355 g.gowrappers.writeln('\t\tif (t.handle == 0) continue;')
2356 }
2357 } else {
2358 g.gowrappers.writeln('\t\tif (t == 0) continue;')
2359 }
2360 if thread_ret_type.has_flag(.option) {
2361 payload_arr_styp := if is_void_payload {
2362 ''
2363 } else {
2364 g.base_type(ret_type.clear_option_and_result())
2365 }
2366 payload_styp := if is_void_payload { '' } else { g.styp(payload_type) }
2367 g.gowrappers.writeln('\t\t${thread_ret_styp} waited = ${waiter_fn_name}(t);')
2368 g.gowrappers.writeln('\t\tif (waited.state != 0) {')
2369 g.gowrappers.writeln('\t\t\tif (!has_failure) {')
2370 g.gowrappers.writeln('\t\t\t\thas_failure = true;')
2371 g.gowrappers.writeln('\t\t\t\tfirst_failure = waited;')
2372 g.gowrappers.writeln('\t\t\t}')
2373 g.gowrappers.writeln('\t\t\tcontinue;')
2374 g.gowrappers.writeln('\t\t}')
2375 if !is_void_payload {
2376 g.gowrappers.writeln('\t\t((${payload_styp}*)res.data)[i] = *((${payload_styp}*)waited.data);')
2377 }
2378 g.gowrappers.writeln('\t}')
2379 g.gowrappers.writeln('\tif (has_failure) {')
2380 if !is_void_payload {
2381 g.gowrappers.writeln('\t\tbuiltin__array_free(&res);')
2382 }
2383 if is_void_payload {
2384 g.gowrappers.writeln('\t\treturn first_failure;')
2385 } else {
2386 g.gowrappers.writeln('\t\treturn (${ret_styp}){ .state = first_failure.state, .err = first_failure.err, .data = {E_STRUCT} };')
2387 }
2388 g.gowrappers.writeln('\t}')
2389 if is_void_payload {
2390 g.gowrappers.writeln('\treturn (${ret_styp}){0};')
2391 } else {
2392 g.gowrappers.writeln('\t${ret_styp} waited_all = {0};')
2393 g.gowrappers.writeln('\tbuiltin___option_ok(&(${payload_arr_styp}[]) { res }, (${option_name}*)(&waited_all), sizeof(${payload_arr_styp}));')
2394 g.gowrappers.writeln('\treturn waited_all;')
2395 }
2396 } else if thread_ret_type.has_flag(.result) {
2397 payload_arr_styp := if is_void_payload {
2398 ''
2399 } else {
2400 g.base_type(ret_type.clear_option_and_result())
2401 }
2402 payload_styp := if is_void_payload { '' } else { g.styp(payload_type) }
2403 g.gowrappers.writeln('\t\t${thread_ret_styp} waited = ${waiter_fn_name}(t);')
2404 g.gowrappers.writeln('\t\tif (waited.is_error) {')
2405 g.gowrappers.writeln('\t\t\tif (!has_failure) {')
2406 g.gowrappers.writeln('\t\t\t\thas_failure = true;')
2407 g.gowrappers.writeln('\t\t\t\tfirst_failure = waited;')
2408 g.gowrappers.writeln('\t\t\t}')
2409 g.gowrappers.writeln('\t\t\tcontinue;')
2410 g.gowrappers.writeln('\t\t}')
2411 if !is_void_payload {
2412 g.gowrappers.writeln('\t\t((${payload_styp}*)res.data)[i] = *((${payload_styp}*)waited.data);')
2413 }
2414 g.gowrappers.writeln('\t}')
2415 g.gowrappers.writeln('\tif (has_failure) {')
2416 if !is_void_payload {
2417 g.gowrappers.writeln('\t\tbuiltin__array_free(&res);')
2418 }
2419 if is_void_payload {
2420 g.gowrappers.writeln('\t\treturn first_failure;')
2421 } else {
2422 g.gowrappers.writeln('\t\treturn (${ret_styp}){ .is_error = true, .err = first_failure.err, .data = {E_STRUCT} };')
2423 }
2424 g.gowrappers.writeln('\t}')
2425 if is_void_payload {
2426 g.gowrappers.writeln('\treturn (${ret_styp}){0};')
2427 } else {
2428 g.gowrappers.writeln('\t${ret_styp} waited_all = {0};')
2429 g.gowrappers.writeln('\tbuiltin___result_ok(&(${payload_arr_styp}[]) { res }, (${result_name}*)(&waited_all), sizeof(${payload_arr_styp}));')
2430 g.gowrappers.writeln('\treturn waited_all;')
2431 }
2432 } else if is_void_payload {
2433 g.gowrappers.writeln('\t\t${waiter_fn_name}(t);')
2434 g.gowrappers.writeln('\t}')
2435 } else {
2436 payload_styp := g.styp(payload_type)
2437 g.gowrappers.writeln('\t\t((${payload_styp}*)res.data)[i] = ${waiter_fn_name}(t);')
2438 g.gowrappers.writeln('\t}')
2439 g.gowrappers.writeln('\treturn res;')
2440 }
2441 g.gowrappers.writeln('}')
2442 }
2443 return fn_name
2444}
2445
2446fn (mut g Gen) register_chan_pop_option_call(opt_el_type string, styp string) {
2447 g.chan_pop_options[opt_el_type] = styp
2448}
2449
2450fn (mut g Gen) note_chan_type_definition(chan_typ ast.Type) {
2451 mut concrete_chan_typ := g.unwrap_generic(g.recheck_concrete_type(chan_typ))
2452 if concrete_chan_typ == 0 {
2453 return
2454 }
2455 sym := g.table.final_sym(concrete_chan_typ)
2456 if sym.kind != .chan || sym.name == 'chan' {
2457 return
2458 }
2459 lock g.late_chan_types {
2460 if sym.cname in g.late_chan_types {
2461 return
2462 }
2463 g.late_chan_types << sym.cname
2464 }
2465}
2466
2467fn (mut g Gen) ensure_chan_type_definition(chan_typ ast.Type) {
2468 mut concrete_chan_typ := g.unwrap_generic(g.recheck_concrete_type(chan_typ))
2469 if concrete_chan_typ == 0 {
2470 return
2471 }
2472 sym := g.table.final_sym(concrete_chan_typ)
2473 if sym.kind != .chan || sym.name == 'chan' {
2474 return
2475 }
2476 if sym.cname in g.emitted_chan_types {
2477 return
2478 }
2479 g.emitted_chan_types[sym.cname] = true
2480 chan_inf := sym.chan_info()
2481 chan_elem_type := g.unwrap_generic(g.recheck_concrete_type(chan_inf.elem_type))
2482 esym := g.table.sym(chan_elem_type)
2483 g.type_definitions.writeln('typedef chan ${sym.cname};')
2484 is_fixed_arr := esym.kind == .array_fixed
2485 if !chan_elem_type.has_flag(.generic) {
2486 el_stype := if is_fixed_arr { '_v_' } else { '' } + g.styp(chan_elem_type)
2487 val_arg_pop := if is_fixed_arr { '&val.ret_arr' } else { '&val' }
2488 val_arg_push := if is_fixed_arr { 'val' } else { '&val' }
2489 push_arg := el_stype + if is_fixed_arr { '*' } else { '' }
2490 g.channel_definitions.writeln('
2491static inline ${el_stype} __${sym.cname}_popval(${sym.cname} ch) {
2492 ${el_stype} val = {0};
2493 sync__Channel_try_pop_priv(ch, ${val_arg_pop}, false);
2494 return val;
2495}')
2496 g.channel_definitions.writeln('
2497static inline void __${sym.cname}_pushval(${sym.cname} ch, ${push_arg} val) {
2498 sync__Channel_try_push_priv(ch, ${val_arg_push}, false);
2499}')
2500 }
2501}
2502
2503fn (mut g Gen) emit_late_chan_type_definitions() {
2504 mut late_chan_cnames := []string{}
2505 lock g.late_chan_types {
2506 late_chan_cnames = g.late_chan_types.clone()
2507 }
2508 for cname in late_chan_cnames {
2509 for idx, sym in g.table.type_symbols {
2510 if sym.cname == cname {
2511 g.ensure_chan_type_definition(ast.new_type(idx))
2512 break
2513 }
2514 }
2515 }
2516}
2517
2518fn (mut g Gen) write_chan_pop_option_fns() {
2519 mut done := []string{}
2520 for opt_el_type, styp in g.chan_pop_options {
2521 if opt_el_type in done {
2522 continue
2523 }
2524 if g.pref.skip_unused {
2525 if sym := g.table.find_sym(opt_el_type) {
2526 if sym.idx !in g.table.used_features.used_syms {
2527 continue
2528 }
2529 }
2530 }
2531 done << opt_el_type
2532 // Ensure the option type is registered for typedef generation
2533 // Only add if this styp is not already registered (to avoid duplicates with different bases)
2534 option_prefix := option_name + '_'
2535 if opt_el_type.starts_with(option_prefix) {
2536 already_registered := opt_el_type in g.options.values()
2537 if !already_registered {
2538 base := opt_el_type[option_prefix.len..]
2539 g.options[base] = opt_el_type
2540 }
2541 }
2542 g.channel_definitions.writeln('
2543static inline ${opt_el_type} __Option_${styp}_popval(${styp} ch) {
2544 ${opt_el_type} _tmp = {0};
2545 if (sync__Channel_try_pop_priv(ch, _tmp.data, false)) {
2546 return (${opt_el_type}){ .state = 2, .err = sync__Channel_closed_error(ch), .data = {E_STRUCT} };
2547 }
2548 return _tmp;
2549}')
2550 }
2551}
2552
2553fn (mut g Gen) register_chan_push_option_fn(el_type string, styp string) {
2554 g.chan_push_options[styp] = el_type
2555}
2556
2557fn (mut g Gen) write_chan_push_option_fns() {
2558 mut done := []string{}
2559 for styp, el_type in g.chan_push_options {
2560 if styp in done {
2561 continue
2562 }
2563 if g.pref.skip_unused {
2564 if sym := g.table.find_sym(el_type) {
2565 if sym.idx !in g.table.used_features.used_syms {
2566 continue
2567 }
2568 }
2569 }
2570 done << styp
2571 g.register_option(ast.void_type.set_flag(.option))
2572 g.channel_definitions.writeln('
2573static inline ${option_name}_void __Option_${styp}_pushval(${styp} ch, ${el_type} e) {
2574 if (sync__Channel_try_push_priv(ch, &e, false)) {
2575 return (${option_name}_void){ .state = 2, .err = builtin___v_error(_S("channel closed")), .data = {E_STRUCT} };
2576 }
2577 return (${option_name}_void){0};
2578}')
2579 }
2580}
2581
2582// cc_type whether to prefix 'struct' or not (C__Foo -> struct Foo)
2583fn (mut g Gen) cc_type(typ ast.Type, is_prefix_struct bool) string {
2584 mut resolved_typ := g.unwrap_generic(typ)
2585 // For generic_inst types with aliased concrete types, resolve to the unaliased
2586 // version so that e.g. MiddlewareOptions[AliasContext] uses the same C type as
2587 // MiddlewareOptions[Context].
2588 rsym := g.table.sym(resolved_typ)
2589 if rsym.kind == .generic_inst {
2590 info := rsym.info as ast.GenericInst
2591 mut has_alias := false
2592 mut unaliased_cts := info.concrete_types.clone()
2593 for i, ct in info.concrete_types {
2594 ua := g.table.unaliased_type(ct)
2595 if ua != ct {
2596 unaliased_cts[i] = ua.derive_add_muls(ct)
2597 has_alias = true
2598 }
2599 }
2600 if has_alias {
2601 // see comment at top of vlib/v/gen/c/utils.v
2602 mut muttable := unsafe { &ast.Table(g.table) }
2603 idx := muttable.find_or_register_generic_inst(ast.new_type(info.parent_idx),
2604 unaliased_cts)
2605 if idx > 0 {
2606 resolved_typ = ast.new_type(idx).derive_add_muls(resolved_typ)
2607 }
2608 }
2609 }
2610 sym := g.table.sym(resolved_typ)
2611 if g.pref.no_preludes {
2612 match sym.kind {
2613 .voidptr {
2614 return 'void*'
2615 }
2616 .charptr {
2617 return 'char*'
2618 }
2619 .byteptr {
2620 return 'unsigned char*'
2621 }
2622 else {}
2623 }
2624 }
2625 mut styp := sym.scoped_cname()
2626 // TODO: this needs to be removed; cgen shouldn't resolve generic types (job of checker)
2627 match sym.info {
2628 ast.Struct, ast.Interface, ast.SumType {
2629 if sym.info.is_generic && sym.generic_types.len == 0 && sym.kind != .interface {
2630 mut sgtyps := ''
2631 for gt in sym.info.generic_types {
2632 gts := g.table.sym(g.unwrap_generic(gt))
2633 sgtyps += '_T_${gts.scoped_cname()}'
2634 }
2635 styp += sgtyps
2636 }
2637 }
2638 else {}
2639 }
2640
2641 if is_prefix_struct && sym.language == .c {
2642 styp = styp[3..]
2643 if sym.kind == .struct {
2644 info := sym.info as ast.Struct
2645 if info.is_anon {
2646 styp = 'C__' + styp
2647 } else if !info.is_typedef {
2648 tag := if info.is_union { 'union' } else { 'struct' }
2649 styp = '${tag} ${styp}'
2650 }
2651 }
2652 }
2653 return styp
2654}
2655
2656@[inline]
2657fn (g &Gen) type_sidx(t ast.Type) string {
2658 if g.pref.build_mode == .build_module {
2659 sym := g.table.sym(t)
2660 return '_v_type_idx_${sym.cname}()'
2661 }
2662 return u32(t).str()
2663}
2664
2665fn (g &Gen) unalias_type_keep_muls(typ ast.Type) ast.Type {
2666 mut resolved := typ
2667 for {
2668 sym := g.table.sym(resolved)
2669 if sym.info is ast.Alias {
2670 resolved = sym.info.parent_type.derive_add_muls(resolved)
2671 continue
2672 }
2673 if sym.info !is ast.Alias {
2674 break
2675 }
2676 }
2677 return resolved
2678}
2679
2680fn (mut g Gen) type_resolves_to_shared(typ ast.Type) bool {
2681 if typ.has_flag(.shared_f) {
2682 return true
2683 }
2684 return g.alias_shared_parent_type(typ) != ast.void_type
2685}
2686
2687fn (mut g Gen) alias_shared_parent_type(typ ast.Type) ast.Type {
2688 mut current_type := g.unwrap_generic(typ)
2689 for _ in 0 .. 64 {
2690 if current_type.nr_muls() > 0 {
2691 return ast.void_type
2692 }
2693 sym := g.table.sym(current_type)
2694 if sym.info is ast.Alias {
2695 parent_type := sym.info.parent_type
2696 if parent_type.has_flag(.shared_f) {
2697 return parent_type
2698 }
2699 current_type = parent_type
2700 continue
2701 }
2702 return ast.void_type
2703 }
2704 return ast.void_type
2705}
2706
2707fn (mut g Gen) exposed_smartcast_type(orig_type ast.Type, smartcast_type ast.Type, is_mut bool) ast.Type {
2708 if is_mut || orig_type == 0 || smartcast_type == 0 || orig_type.is_ptr() {
2709 return smartcast_type
2710 }
2711 orig_sym := g.table.final_sym(orig_type)
2712 resolved_smartcast_type := g.unwrap_generic(smartcast_type)
2713 smartcast_sym := g.table.final_sym(resolved_smartcast_type)
2714 if orig_sym.kind == .interface && smartcast_sym.kind != .interface {
2715 if smartcast_sym.kind in [.struct, .aggregate] && !smartcast_sym.is_builtin() {
2716 return if smartcast_type.is_ptr() { smartcast_type } else { smartcast_type.ref() }
2717 }
2718 if smartcast_type.is_ptr() {
2719 return smartcast_type.deref()
2720 }
2721 }
2722 return smartcast_type
2723}
2724
2725fn (mut g Gen) concrete_interface_cast_type(typ ast.Type) ast.Type {
2726 unwrapped_typ := g.unwrap_generic(typ)
2727 sym := g.table.final_sym(unwrapped_typ)
2728 if sym.kind != .aggregate {
2729 return typ
2730 }
2731 variant_typ := sym.aggregate_variant_type(g.aggregate_type_idx)
2732 if variant_typ == 0 {
2733 return typ
2734 }
2735 return variant_typ.derive_add_muls(typ)
2736}
2737
2738fn (g &Gen) runtime_interface_variants(info ast.Interface) []ast.Type {
2739 mut variants := []ast.Type{cap: info.types.len + info.mut_types.len}
2740 for typ in info.implementor_types(true) {
2741 if g.table.sym(typ).kind == .aggregate {
2742 continue
2743 }
2744 variants << typ
2745 }
2746 return variants
2747}
2748
2749// ensure_array_typedef emits a `typedef array <cname>;` if the base type
2750// name looks like an array type that might not have been typedef'd yet.
2751// This is needed because option/result wrapper structs reference the base
2752// type in sizeof(), but the array typedef might have been skipped
2753// (e.g. for builtin nested array types like Array_Array_string).
2754fn (mut g Gen) ensure_array_typedef(base_ string) {
2755 base := base_.trim_right('*')
2756 if base.starts_with('Array_') && !base.starts_with('Array_fixed_') {
2757 if base !in g.array_typedefs {
2758 // Only track if write_typedef_types() already emitted at least
2759 // one array typedef, which means the `array` struct is defined.
2760 if g.written_array_typedefs > 0 || !g.done_typedef_phase {
2761 g.array_typedefs << base
2762 }
2763 }
2764 }
2765}
2766
2767fn (mut g Gen) write_late_array_typedefs() {
2768 for i := g.written_array_typedefs; i < g.array_typedefs.len; i++ {
2769 g.type_definitions.writeln('typedef array ${g.array_typedefs[i]};')
2770 }
2771}
2772
2773fn (g &Gen) fixed_array_base_elem_sym(typ ast.Type) &ast.TypeSymbol {
2774 mut sym := g.table.final_sym(typ)
2775 for {
2776 match sym.info {
2777 ast.ArrayFixed {
2778 sym = g.table.final_sym(sym.info.elem_type)
2779 }
2780 else {
2781 return sym
2782 }
2783 }
2784 }
2785 return sym
2786}
2787
2788pub fn (mut g Gen) write_typedef_types() {
2789 type_symbols := g.table.type_symbols.filter(!it.is_builtin
2790 && it.kind in [.array, .array_fixed, .chan, .map])
2791 for sym in type_symbols {
2792 if g.pref.skip_unused && sym.idx !in g.table.used_features.used_syms {
2793 continue
2794 }
2795 match sym.kind {
2796 .array {
2797 info := if sym.info is ast.Array {
2798 sym.info
2799 } else {
2800 continue
2801 }
2802 if g.typedef_type_has_unresolved_generic_placeholder_parts(info.elem_type) {
2803 continue
2804 }
2805 elem_sym := g.table.sym(info.elem_type)
2806 if !g.pref.no_preludes && elem_sym.kind != .placeholder
2807 && !info.elem_type.has_flag(.generic) {
2808 g.type_definitions.writeln('typedef array ${sym.cname};')
2809 g.array_typedefs << sym.cname
2810 }
2811 }
2812 .array_fixed {
2813 info := if sym.info is ast.ArrayFixed {
2814 sym.info
2815 } else {
2816 continue
2817 }
2818 if g.typedef_type_has_unresolved_generic_placeholder_parts(info.elem_type) {
2819 continue
2820 }
2821 base_elem_sym := g.fixed_array_base_elem_sym(info.elem_type)
2822 if base_elem_sym.kind != .struct && base_elem_sym.is_builtin() {
2823 styp := sym.cname
2824 len := info.size
2825 if len > 0 {
2826 fixed_elem_type := g.unalias_type_keep_muls(info.elem_type)
2827 resolved_elem_sym := g.table.sym(fixed_elem_type)
2828 mut fixed := g.styp(fixed_elem_type)
2829 if resolved_elem_sym.info is ast.FnType {
2830 pos := g.out.len
2831 // pos2:=g.out_parallel[g.out_idx].len
2832 g.write_fn_ptr_decl(&resolved_elem_sym.info, '')
2833 fixed = g.out.cut_to(pos)
2834 // g.out_parallel[g.out_idx].cut_to(pos2)
2835 mut def_str := 'typedef ${fixed};'
2836 def_str = def_str.replace_once('(*)', '(*${styp}[${len}])')
2837 g.type_definitions.writeln(def_str)
2838 } else if !info.is_fn_ret {
2839 base := g.styp(info.elem_type.clear_option_and_result())
2840 if info.elem_type.has_flag(.option) && base !in g.options_forward {
2841 styp_elem, elem_base := g.option_type_name(info.elem_type)
2842 lock g.done_options {
2843 if base !in g.done_options {
2844 g.done_options << elem_base
2845 g.typedefs.writeln('typedef struct ${styp_elem} ${styp_elem};')
2846 g.type_definitions.writeln('${g.option_type_text(styp_elem,
2847 elem_base)};')
2848 }
2849 if styp !in g.done_options {
2850 g.type_definitions.writeln('typedef ${fixed} ${styp} [${len}];')
2851 g.done_options << styp
2852 }
2853 }
2854 } else {
2855 g.type_definitions.writeln('typedef ${fixed} ${styp} [${len}];')
2856 if info.elem_type.has_flag(.result) && base !in g.results_forward {
2857 g.results_forward << base
2858 }
2859 }
2860 }
2861 }
2862 }
2863 }
2864 .chan {
2865 info := if sym.info is ast.Chan {
2866 sym.info
2867 } else {
2868 continue
2869 }
2870 if g.typedef_type_has_unresolved_generic_placeholder_parts(info.elem_type) {
2871 continue
2872 }
2873 g.ensure_chan_type_definition(ast.new_type(sym.idx))
2874 }
2875 .map {
2876 info := if sym.info is ast.Map {
2877 sym.info
2878 } else {
2879 continue
2880 }
2881 if g.typedef_type_has_unresolved_generic_placeholder_parts(info.key_type)
2882 || g.typedef_type_has_unresolved_generic_placeholder_parts(info.value_type) {
2883 continue
2884 }
2885 g.type_definitions.writeln('typedef map ${sym.cname};')
2886 }
2887 else {
2888 continue
2889 }
2890 }
2891 }
2892 // Emit aliases parent-first so that chained aliases (`type B = A` where
2893 // `A` is itself an alias, #27055) typedef in dependency order; otherwise C
2894 // hits the unknown parent typedef and silently falls back to implicit int.
2895 mut emitted_alias := map[int]bool{}
2896 for sym in g.table.type_symbols {
2897 if sym.kind == .alias && !sym.is_builtin && sym.name !in ['byte', 'i32'] {
2898 if g.pref.skip_unused && sym.idx !in g.table.used_features.used_syms {
2899 continue
2900 }
2901 g.emit_alias_typedef_chain(sym, mut emitted_alias)
2902 }
2903 }
2904 g.write_sorted_fn_typesymbol_declaration()
2905 // Generating interfaces after all the common types have been defined
2906 // to prevent generating interface struct before definition of field types
2907
2908 interfaces := g.table.type_symbols.filter(it.info is ast.Interface && !it.is_builtin)
2909 mut interface_declaration_syms := []&ast.TypeSymbol{cap: interfaces.len}
2910 for sym in interfaces {
2911 g.write_interface_typedef(sym)
2912 if g.can_write_interface_typesymbol_declaration(sym) {
2913 interface_declaration_syms << sym
2914 }
2915 }
2916 mut already_generated_ifaces := map[string]bool{}
2917 for sym in interface_declaration_syms {
2918 if g.pref.skip_unused && sym.idx !in g.table.used_features.used_syms {
2919 continue
2920 }
2921 if sym.cname !in already_generated_ifaces {
2922 g.write_interface_typesymbol_declaration(sym)
2923 already_generated_ifaces[sym.cname] = true
2924 }
2925 }
2926}
2927
2928// emit_alias_typedef_chain emits parent alias typedefs before child ones so a
2929// chain like `type Main = A; type A = B; type B = string` ends up as
2930// `typedef string B; typedef B A; typedef A Main;` in declaration order.
2931fn (mut g Gen) emit_alias_typedef_chain(sym ast.TypeSymbol, mut emitted map[int]bool) {
2932 if emitted[sym.idx] {
2933 return
2934 }
2935 emitted[sym.idx] = true
2936 if sym.info is ast.Alias {
2937 parent_idx := sym.info.parent_type.idx()
2938 if parent_idx > 0 && parent_idx < g.table.type_symbols.len {
2939 parent_sym := g.table.type_symbols[parent_idx]
2940 if parent_sym.kind == .alias && !parent_sym.is_builtin
2941 && parent_sym.name !in ['byte', 'i32'] {
2942 if !(g.pref.skip_unused && parent_sym.idx !in g.table.used_features.used_syms) {
2943 g.emit_alias_typedef_chain(parent_sym, mut emitted)
2944 }
2945 }
2946 }
2947 }
2948 g.write_alias_typesymbol_declaration(sym)
2949}
2950
2951pub fn (mut g Gen) write_alias_typesymbol_declaration(sym ast.TypeSymbol) {
2952 mut levels := 0
2953 parent := g.table.type_symbols[sym.parent_idx]
2954 is_c_parent := parent.name.len > 2 && parent.name[0] == `C` && parent.name[1] == `.`
2955 mut is_fixed_array_of_non_builtin := false
2956 mut parent_styp := parent.cname
2957 if is_c_parent {
2958 if sym.info is ast.Alias {
2959 parent_styp = g.styp(sym.info.parent_type)
2960 }
2961 } else {
2962 if sym.info is ast.Alias {
2963 parent_styp = g.styp(sym.info.parent_type)
2964 // Chained aliases (#27055): if the parent is itself an alias whose
2965 // ultimate base is a fixed array of non-builtin elements, the parent
2966 // typedef has been deferred to `alias_definitions` so this child
2967 // must also be deferred — otherwise `typedef Parent Child;` lands
2968 // in `type_definitions` before `Parent` exists.
2969 mut walk_typ := sym.info.parent_type
2970 for {
2971 walk_sym := g.table.sym(walk_typ)
2972 if walk_sym.info is ast.Alias {
2973 walk_typ = walk_sym.info.parent_type
2974 continue
2975 }
2976 if walk_sym.info is ast.ArrayFixed {
2977 base_elem_sym := g.fixed_array_base_elem_sym(walk_sym.info.elem_type)
2978 if !base_elem_sym.is_builtin() {
2979 is_fixed_array_of_non_builtin = true
2980 }
2981 }
2982 break
2983 }
2984 parent_sym := g.table.sym(sym.info.parent_type)
2985 if parent_sym.info is ast.ArrayFixed {
2986 mut elem_sym := g.table.sym(parent_sym.info.elem_type)
2987 base_elem_sym := g.fixed_array_base_elem_sym(parent_sym.info.elem_type)
2988
2989 mut parent_elem_info := parent_sym.info as ast.ArrayFixed
2990 mut parent_elem_styp := g.styp(sym.info.parent_type)
2991 mut out := strings.new_builder(50)
2992 for {
2993 if mut elem_sym.info is ast.ArrayFixed {
2994 old := out.str()
2995 out.clear()
2996 out.writeln('typedef ${elem_sym.cname} ${parent_elem_styp} [${parent_elem_info.size}];')
2997 out.writeln(old)
2998 parent_elem_styp = elem_sym.cname
2999 parent_elem_info = elem_sym.info as ast.ArrayFixed
3000 elem_sym = g.table.sym(elem_sym.info.elem_type)
3001 levels++
3002 } else {
3003 break
3004 }
3005 }
3006 // Determine based on the base element type (after walking
3007 // through all nesting levels) whether the fixed array
3008 // contains non-builtin types. For non-builtins (structs,
3009 // enums), write_sorted_types handles the typedefs after
3010 // struct definitions, so skip emitting them here.
3011 if !base_elem_sym.is_builtin() {
3012 is_fixed_array_of_non_builtin = true
3013 out.clear()
3014 }
3015 if out.len != 0 {
3016 g.type_definitions.writeln(out.str())
3017 }
3018 }
3019 }
3020 }
3021 if parent_styp == 'byte' && sym.cname == 'u8' {
3022 // TODO: remove this check; it is here just to fix V rebuilding in -cstrict mode with clang-12
3023 return
3024 }
3025 if sym.name.starts_with('C.') {
3026 // `pub type C.HINSTANCE = voidptr` means that `HINSTANCE` should be treated as a voidptr by V.
3027 // The C type itself however already exists on the C side, so just treat C__HINSTANCE as a macro for it:
3028 g.type_definitions.writeln('#define ${sym.cname} ${sym.cname#[3..]}')
3029 return
3030 }
3031 if is_fixed_array_of_non_builtin {
3032 g.alias_definitions.writeln('typedef ${parent_styp} ${sym.cname};')
3033 } else {
3034 g.type_definitions.writeln('typedef ${parent_styp} ${sym.cname};')
3035 }
3036}
3037
3038pub fn (mut g Gen) write_interface_typedef(sym ast.TypeSymbol) {
3039 struct_name := c_name(sym.cname)
3040 g.typedefs.writeln('typedef struct ${struct_name} ${struct_name};')
3041}
3042
3043pub fn (mut g Gen) write_interface_typesymbol_declaration(sym ast.TypeSymbol) {
3044 info := sym.info as ast.Interface
3045 struct_name := c_name(sym.cname)
3046 impl_types := info.implementor_types(true)
3047 g.type_definitions.writeln('struct ${struct_name} {')
3048 g.type_definitions.writeln('\tunion {')
3049 g.type_definitions.writeln('\t\tvoid* _object;')
3050 for variant in impl_types {
3051 mk_typ := ast.mktyp(variant)
3052 if mk_typ != variant && mk_typ in impl_types {
3053 continue
3054 }
3055 vsym := g.table.sym(mk_typ)
3056 if vsym.kind == .aggregate {
3057 continue
3058 }
3059 if g.pref.skip_unused && vsym.idx !in g.table.used_features.used_syms {
3060 continue
3061 }
3062 vcname := vsym.cname
3063 if vsym.info is ast.FnType {
3064 g.type_definitions.writeln('\t\t${g.fn_ptr_decl_str(vsym.info as ast.FnType,
3065 '_${vcname}')};')
3066 } else {
3067 g.type_definitions.writeln('\t\t${vcname}* _${vcname};')
3068 }
3069 }
3070 g.type_definitions.writeln('\t};')
3071 g.type_definitions.writeln('\tu32 _typ;')
3072 g.type_definitions.writeln('\tvoid* _methods;')
3073 for field in info.fields {
3074 styp := g.styp(field.typ)
3075 cname := c_name(field.name)
3076 g.type_definitions.writeln('\t${styp}* ${cname};')
3077 }
3078 g.type_definitions.writeln('};')
3079}
3080
3081fn (mut g Gen) interface_methods_struct_name(typ ast.Type) string {
3082 interface_sym := g.table.final_sym(g.table.unaliased_type(g.unwrap_generic(typ)))
3083 return 'struct _${interface_sym.cname}_interface_methods'
3084}
3085
3086fn (mut g Gen) shared_interface_mtx_helper_name(typ ast.Type) string {
3087 interface_typ := g.table.unaliased_type(g.unwrap_generic(typ.clear_flags(.shared_f, .atomic_f)))
3088 interface_sym := g.table.final_sym(interface_typ)
3089 return '__shared__${interface_sym.cname}_mtx'
3090}
3091
3092pub fn (mut g Gen) write_fn_typesymbol_declaration(sym ast.TypeSymbol) {
3093 info := sym.info as ast.FnType
3094 func := info.func
3095 is_fn_sig := func.name == ''
3096 not_anon := !info.is_anon
3097 mut has_generic_arg := false
3098
3099 if g.pref.skip_unused && sym.idx !in g.table.used_features.used_syms {
3100 return
3101 }
3102
3103 for param in func.params {
3104 if g.type_has_unresolved_generic_parts(param.typ) {
3105 has_generic_arg = true
3106 break
3107 }
3108 }
3109 if !info.has_decl && (not_anon || is_fn_sig)
3110 && !g.type_has_unresolved_generic_parts(func.return_type) && !has_generic_arg {
3111 fn_name := sym.cname
3112 mut call_conv_attr := ''
3113 for attr in func.attrs {
3114 match attr.name {
3115 'callconv' {
3116 call_conv_attr = call_convention_attribute(attr.arg)
3117 }
3118 else {}
3119 }
3120 }
3121 ret_typ :=
3122 if !func.return_type.has_flag(.option) && !func.return_type.has_flag(.result) && g.table.sym(func.return_type).kind == .array_fixed { '_v_' } else { '' } +
3123 g.styp(func.return_type)
3124 g.type_definitions.write_string('typedef ${ret_typ} (${call_conv_attr}*${fn_name})(')
3125 for i, param in func.params {
3126 const_prefix := if param.typ.is_any_kind_of_pointer() && !param.is_mut
3127 && param.name.starts_with('const_') {
3128 'const '
3129 } else {
3130 ''
3131 }
3132 if const_prefix != '' {
3133 styp := g.styp(param.typ)
3134 g.type_definitions.write_string(const_prefix +
3135 g.const_pointer_param_type_name(param.typ, styp))
3136 } else {
3137 g.type_definitions.write_string(g.fn_param_type_decl(param.typ, ''))
3138 }
3139 if i < func.params.len - 1 {
3140 g.type_definitions.write_string(',')
3141 }
3142 }
3143 g.type_definitions.writeln(');')
3144 }
3145}
3146
3147// fn_param_type_decl emits fixed-array parameters as direct C array declarators so
3148// function type definitions do not depend on later fixed-array typedef ordering.
3149fn (mut g Gen) fn_param_type_decl(typ ast.Type, name string) string {
3150 styp := g.styp(typ)
3151 if typ.is_ptr() || typ.has_flag(.option) || typ.has_flag(.result) {
3152 return if name == '' { styp } else { '${styp} ${name}' }
3153 }
3154 mut current_typ := g.table.unaliased_type(typ)
3155 mut current_sym := g.table.sym(current_typ)
3156 if current_sym.kind != .array_fixed {
3157 return if name == '' { styp } else { '${styp} ${name}' }
3158 }
3159 mut dims := []string{}
3160 for current_sym.kind == .array_fixed {
3161 info := current_sym.info as ast.ArrayFixed
3162 dims << '[${info.size}]'
3163 if info.elem_type.is_ptr() || info.elem_type.has_flag(.option)
3164 || info.elem_type.has_flag(.result) {
3165 base_styp := g.styp(info.elem_type)
3166 decl := if name == '' { dims.join('') } else { '${name}${dims.join('')}' }
3167 return '${base_styp} ${decl}'
3168 }
3169 current_typ = g.table.unaliased_type(info.elem_type)
3170 current_sym = g.table.sym(current_typ)
3171 }
3172 if current_sym.info is ast.FnType {
3173 return if name == '' { styp } else { '${styp} ${name}' }
3174 }
3175 decl := if name == '' { dims.join('') } else { '${name}${dims.join('')}' }
3176 return '${g.styp(current_typ)} ${decl}'
3177}
3178
3179pub fn (mut g Gen) write_array_fixed_return_types() {
3180 fixed_arr_wrappers := g.table.type_symbols.filter(it.info is ast.ArrayFixed
3181 && !it.info.elem_type.has_flag(.generic))
3182 if fixed_arr_wrappers.len == 0 {
3183 return
3184 }
3185
3186 g.typedefs.writeln('\n// BEGIN_array_fixed_return_typedefs')
3187 g.type_definitions.writeln('\n// BEGIN_array_fixed_return_structs')
3188 mut generated_wrappers := map[string]bool{}
3189
3190 for sym in fixed_arr_wrappers {
3191 if g.pref.skip_unused && sym.idx !in g.table.used_features.used_syms {
3192 continue
3193 }
3194 info := sym.info as ast.ArrayFixed
3195 if info.size <= 0 {
3196 // unresolved sizes e.g. [unknown_const]int
3197 continue
3198 }
3199 // Skip if element type is a non-builtin struct that isn't marked as used
3200 // This prevents generating wrapper structs that reference undefined types
3201 resolved_elem_type := g.unalias_type_keep_muls(info.elem_type)
3202 resolved_elem_sym := g.table.sym(resolved_elem_type)
3203 if g.pref.skip_unused && resolved_elem_sym.kind == .struct
3204 && !resolved_elem_sym.is_builtin()
3205 && resolved_elem_sym.idx !in g.table.used_features.used_syms {
3206 continue
3207 }
3208 wrapper_name := '_v_${sym.cname}'
3209 if wrapper_name in generated_wrappers {
3210 continue
3211 }
3212 generated_wrappers[wrapper_name] = true
3213 g.typedefs.writeln('typedef struct ${wrapper_name} ${wrapper_name};')
3214 g.type_definitions.writeln('struct ${wrapper_name} {')
3215 if resolved_elem_sym.info is ast.FnType {
3216 pos := g.out.len
3217 g.write_fn_ptr_decl(&resolved_elem_sym.info, 'ret_arr[${info.size}]')
3218 field_decl := g.out.cut_to(pos)
3219 g.type_definitions.writeln('\t${field_decl};')
3220 } else {
3221 mut fixed_elem_name := g.styp(resolved_elem_type.set_nr_muls(0))
3222 if resolved_elem_type.is_ptr() {
3223 fixed_elem_name += '*'.repeat(resolved_elem_type.nr_muls())
3224 }
3225 g.type_definitions.writeln('\t${fixed_elem_name} ret_arr[${info.size}];')
3226 }
3227 g.type_definitions.writeln('};')
3228 }
3229
3230 g.typedefs.writeln('// END_array_fixed_return_typedefs\n')
3231 g.type_definitions.writeln('// END_array_fixed_return_structs\n')
3232}
3233
3234pub fn (mut g Gen) write_multi_return_types() {
3235 start_pos := g.type_definitions.len
3236 g.typedefs.writeln('\n// BEGIN_multi_return_typedefs')
3237 g.type_definitions.writeln('\n// BEGIN_multi_return_structs')
3238 multi_rets := g.table.type_symbols.filter(it.kind == .multi_return && !it.cname.contains('['))
3239 for sym in multi_rets {
3240 info := sym.mr_info()
3241 if info.types.any(it.has_flag(.generic)) {
3242 continue
3243 }
3244 if g.pref.skip_unused && sym.idx !in g.table.used_features.used_syms {
3245 continue
3246 }
3247 g.typedefs.writeln('typedef struct ${sym.cname} ${sym.cname};')
3248 g.type_definitions.writeln('struct ${sym.cname} {')
3249 for i, mr_typ in info.types {
3250 type_name := g.styp(mr_typ)
3251 if mr_typ.has_flag(.option) {
3252 // option in multi_return
3253 // Dont use g.styp() here because it will register
3254 // option and we dont want that
3255 styp, base := g.option_type_name(mr_typ)
3256 lock g.done_options {
3257 if base !in g.done_options {
3258 g.done_options << base
3259 last_text := g.type_definitions.after(start_pos).clone()
3260 g.type_definitions.go_back_to(start_pos)
3261 g.typedefs.writeln('typedef struct ${styp} ${styp};')
3262 g.type_definitions.writeln('${g.option_type_text(styp, base)};')
3263 g.type_definitions.write_string(last_text)
3264 }
3265 }
3266 }
3267 if mr_typ.has_flag(.result) {
3268 // result in multi_return
3269 // Dont use g.styp() here because it will register
3270 // result and we dont want that
3271 styp, base := g.result_type_name(mr_typ)
3272 lock g.done_results {
3273 if base !in g.done_results {
3274 g.done_results << base
3275 last_text := g.type_definitions.after(start_pos).clone()
3276 g.type_definitions.go_back_to(start_pos)
3277 g.typedefs.writeln('typedef struct ${styp} ${styp};')
3278 g.type_definitions.writeln('${g.result_type_text(styp, base)};')
3279 g.type_definitions.write_string(last_text)
3280 }
3281 }
3282 }
3283 g.type_definitions.writeln('\t${type_name} arg${i};')
3284 }
3285 g.type_definitions.writeln('};\n')
3286 }
3287 g.typedefs.writeln('// END_multi_return_typedefs\n')
3288 g.type_definitions.writeln('// END_multi_return_structs\n')
3289}
3290
3291@[inline]
3292fn prefix_with_counter(prefix string, counter int) string {
3293 mut sb := strings.new_builder(prefix.len + 5)
3294 sb.write_string(prefix)
3295 sb.write_decimal(counter)
3296 return sb.str()
3297}
3298
3299pub fn (mut g Gen) new_tmp_var() string {
3300 g.tmp_count++
3301 return prefix_with_counter('_t', g.tmp_count)
3302}
3303
3304pub fn (mut g Gen) new_global_tmp_var() string {
3305 g.global_tmp_count++
3306 return prefix_with_counter('_t', g.global_tmp_count)
3307}
3308
3309pub fn (mut g Gen) current_tmp_var() string {
3310 return prefix_with_counter('_t', g.tmp_count)
3311}
3312
3313pub fn (mut g Gen) reset_tmp_count() int {
3314 old := g.tmp_count
3315 g.tmp_count = 0
3316 return old
3317}
3318
3319fn (mut g Gen) decrement_inside_ternary() {
3320 key := g.inside_ternary.str()
3321 for name in g.ternary_level_names[key] {
3322 g.ternary_names.delete(name)
3323 }
3324 g.ternary_level_names.delete(key)
3325 g.inside_ternary--
3326}
3327
3328fn (mut g Gen) stmts(stmts []ast.Stmt) {
3329 g.stmts_with_tmp_var(stmts, '')
3330}
3331
3332fn (mut g Gen) push_skip_scope_cleanup(scope &ast.Scope) int {
3333 start := g.skip_scope_cleanup_start_pos.len
3334 if scope != unsafe { nil } {
3335 g.skip_scope_cleanup_start_pos << scope.start_pos
3336 }
3337 return start
3338}
3339
3340fn (mut g Gen) pop_skip_scope_cleanup(start int) {
3341 if g.skip_scope_cleanup_start_pos.len > start {
3342 g.skip_scope_cleanup_start_pos = g.skip_scope_cleanup_start_pos[..start].clone()
3343 }
3344}
3345
3346fn (g &Gen) should_skip_scope_cleanup(scope &ast.Scope) bool {
3347 return scope != unsafe { nil } && scope.start_pos in g.skip_scope_cleanup_start_pos
3348}
3349
3350fn is_noreturn_callexpr(expr ast.Expr) bool {
3351 if expr is ast.CallExpr {
3352 return expr.is_noreturn
3353 }
3354 return false
3355}
3356
3357fn (g &Gen) local_closure_var_has_tracked_context(var ast.Var) bool {
3358 if var.name == '_' || var.is_arg || var.is_tmp || var.is_inherited || var.typ == 0 {
3359 return false
3360 }
3361 if g.table.final_sym(var.typ).kind != .function {
3362 return false
3363</