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