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('*', '')
3315 ret_styp := g.styp(ret_typ).replace('*', '')
3316 styp := g.base_type(ret_typ)
3317 opt_expr := g.expr_string(expr.right)
3318 g.writeln('${opt_styp}* ${opt_ptr_var} = &(${opt_expr});')
3319 g.writeln('if (${opt_ptr_var}->state != 0) {')
3320 g.writeln('\t${tmp_var} = (${ret_styp}){ .state = ${opt_ptr_var}->state, .err = ${opt_ptr_var}->err, .data = {E_STRUCT} };')
3321 g.writeln('} else {')
3322 g.writeln('\tbuiltin___option_ok(&(${styp}[]) { (${styp})${opt_ptr_var}->data }, (${option_name}*)(&${tmp_var}), sizeof(${styp}));')
3323 g.writeln('}')
3324 return true
3325}
3326
3327fn (mut g Gen) expr_with_tmp_var(expr ast.Expr, expr_typ ast.Type, ret_typ ast.Type, tmp_var string, use_at_once bool) {
3328 if !ret_typ.has_option_or_result() {
3329 panic('cgen: parameter `ret_typ` of function `expr_with_tmp_var()` must be an Option or Result')
3330 }
3331
3332 assign_op := g.assign_op
3333 defer {
3334 g.assign_op = assign_op
3335 }
3336 g.assign_op = .unknown
3337 stmt_str := if g.inside_ternary > 0 {
3338 g.go_before_ternary().trim_space()
3339 } else {
3340 g.go_before_last_stmt().trim_space()
3341 }
3342 // mut styp := g.base_type(ret_typ)
3343 unwrapped_ret_typ := g.unwrap_generic(ret_typ)
3344 mut styp := g.base_type(unwrapped_ret_typ)
3345 g.empty_line = true
3346 final_expr_sym := g.table.final_sym(g.unwrap_generic(expr_typ))
3347 mut expected_type := ret_typ
3348 if final_expr_sym.kind == .none {
3349 error_target_type := if unwrapped_ret_typ.has_flag(.option_mut_param_t) {
3350 unwrapped_ret_typ.clear_flag(.option_mut_param_t)
3351 } else {
3352 unwrapped_ret_typ
3353 }
3354 tmp_styp := g.styp(error_target_type)
3355 g.write('${tmp_styp} ${tmp_var} = ')
3356 g.gen_option_error(error_target_type, expr)
3357 g.writeln(';')
3358 } else if expr is ast.Ident && expr_typ == ast.error_type {
3359 g.writeln('${g.styp(unwrapped_ret_typ)} ${tmp_var} = {.state=2, .err=${expr.name}};')
3360 } else {
3361 mut simple_assign := false
3362 unwrapped_expr_typ := g.unwrap_generic(expr_typ)
3363 expr_typ_is_option := unwrapped_expr_typ.has_flag(.option)
3364 ret_typ_is_option := unwrapped_ret_typ.has_flag(.option)
3365 if ret_typ.has_flag(.generic) {
3366 if expr is ast.SelectorExpr && g.cur_concrete_types.len == 0 {
3367 // resolve generic struct on selectorExpr inside non-generic function
3368 if expr.expr is ast.Ident {
3369 if expr.expr.obj is ast.Var {
3370 if expr.expr.obj.expr is ast.StructInit {
3371 struct_info := g.table.sym(expr.expr.obj.typ).info
3372 if struct_info is ast.Struct && struct_info.concrete_types.len > 0 {
3373 save_cur_concrete_types := g.cur_concrete_types
3374 g.cur_concrete_types = struct_info.concrete_types
3375 g.clear_type_resolution_caches()
3376 defer {
3377 g.cur_concrete_types = save_cur_concrete_types
3378 g.clear_type_resolution_caches()
3379 }
3380 }
3381 }
3382 }
3383 }
3384 }
3385 ret_styp := g.styp(unwrapped_ret_typ).replace('*', '_ptr')
3386 g.writeln('${ret_styp} ${tmp_var};')
3387 } else {
3388 if unwrapped_ret_typ.has_flag(.option_mut_param_t) {
3389 ret_styp := g.styp(unwrapped_ret_typ).replace('*', '')
3390 g.writeln('${ret_styp} ${tmp_var};')
3391 } else {
3392 g.writeln('${g.styp(unwrapped_ret_typ)} ${tmp_var};')
3393 }
3394 }
3395 if ret_typ_is_option && expr is ast.PrefixExpr
3396 && g.gen_option_payload_ref(expr, unwrapped_ret_typ, tmp_var) {
3397 g.set_current_pos_as_last_stmt_pos()
3398 g.write2(stmt_str, ' ')
3399 if use_at_once {
3400 g.write(tmp_var)
3401 }
3402 return
3403 }
3404 mut expr_is_fixed_array_var := false
3405 mut fn_option_clone := false
3406 mut already_generated := false
3407 if ret_typ_is_option {
3408 if !expr_typ_is_option {
3409 expected_type = ret_typ.clear_flag(.option)
3410 }
3411 if expr_typ_is_option && expr in [ast.StructInit, ast.ArrayInit, ast.MapInit] {
3412 simple_assign = expr is ast.StructInit && !expr.typ.has_flag(.generic)
3413 if simple_assign {
3414 g.write('${tmp_var} = ')
3415 } else {
3416 if expr is ast.StructInit && expr.typ.has_flag(.generic) {
3417 // T{}
3418 g.write('builtin___option_ok(&(${styp}[]) { ')
3419 already_generated = true
3420 g.expr(ast.StructInit{
3421 ...expr
3422 typ: g.unwrap_generic(expr.typ).clear_flag(.option)
3423 })
3424 } else {
3425 g.write('builtin___option_none(&(${styp}[]) { ')
3426 }
3427 }
3428 } else {
3429 simple_assign =
3430 ((expr is ast.SelectorExpr || (expr is ast.Ident && !expr.is_auto_heap()))
3431 && ret_typ.is_ptr() && expr_typ.is_ptr() && expr_typ_is_option)
3432 || (expr_typ == ret_typ && !(expr_typ.has_option_or_result()
3433 && (expr_typ.is_ptr() || expr is ast.LambdaExpr)))
3434 // option ptr assignment simplification
3435 if simple_assign {
3436 g.write('${tmp_var} = ')
3437 } else if expr_typ_is_option && expr is ast.PrefixExpr
3438 && expr.right is ast.StructInit
3439 && (expr.right as ast.StructInit).init_fields.len == 0 {
3440 g.write('builtin___option_none(&(${styp}[]) { ')
3441 } else if final_expr_sym.kind == .array_fixed {
3442 expr_is_fixed_array_var = true
3443 info := final_expr_sym.array_fixed_info()
3444 mut no_cast := false
3445 if expr in [ast.CastExpr, ast.CallExpr, ast.Ident, ast.SelectorExpr] {
3446 no_cast = true
3447 }
3448 elem_sym := g.table.sym(info.elem_type)
3449 if elem_sym.kind == .struct {
3450 expr_is_fixed_array_var = false
3451 g.write('builtin___option_ok(&(${styp}[]) { ')
3452 } else if no_cast {
3453 g.write('builtin___option_ok(')
3454 } else {
3455 g.write('builtin___option_ok((${g.styp(final_expr_sym.idx)})')
3456 }
3457 } else {
3458 g.write('builtin___option_ok(&(${styp}[]) { ')
3459 if final_expr_sym.info is ast.FnType {
3460 final_ret_sym := g.table.final_sym(ret_typ)
3461 if final_ret_sym.info is ast.FnType {
3462 g.write('(')
3463 g.write_fn_ptr_decl(&final_ret_sym.info, '')
3464 g.write(')')
3465 fn_option_clone = expr is ast.SelectorExpr && expr_typ.has_flag(.option)
3466 }
3467 }
3468 }
3469 // For @[heap] variables, the C-level variable is already a pointer
3470 // even though the V-level type is a value type, so account for
3471 // that extra indirection when computing how many '&' to add.
3472 extra_muls := if expr is ast.Ident && g.resolved_ident_is_auto_heap(expr)
3473 && !expr_typ.is_ptr() {
3474 1
3475 } else {
3476 0
3477 }
3478 if !expr.is_literal() && expr_typ != ast.nil_type
3479 && ret_typ.nr_muls() > expr_typ.nr_muls() + extra_muls
3480 && !ret_typ.has_flag(.option_mut_param_t) {
3481 g.write('&'.repeat(ret_typ.nr_muls() - expr_typ.nr_muls() - extra_muls))
3482 } else if ret_typ.has_flag(.option_mut_param_t) {
3483 if expr_typ.is_ptr() {
3484 if ret_typ.nr_muls() < expr_typ.nr_muls() {
3485 g.write('*')
3486 }
3487 } else {
3488 if expr_typ.has_flag(.option) {
3489 fn_option_clone = true
3490 g.write('(${styp})')
3491 }
3492 g.write('&')
3493 }
3494 }
3495 }
3496 } else {
3497 if final_expr_sym.kind == .array_fixed {
3498 expr_is_fixed_array_var = true
3499 info := final_expr_sym.array_fixed_info()
3500 mut no_cast := false
3501 if expr in [ast.CastExpr, ast.CallExpr, ast.Ident, ast.SelectorExpr] {
3502 no_cast = true
3503 }
3504 elem_sym := g.table.sym(info.elem_type)
3505 if elem_sym.kind == .struct {
3506 expr_is_fixed_array_var = false
3507 g.write('builtin___result_ok(&(${styp}[]) { ')
3508 } else if no_cast {
3509 g.write('builtin___result_ok(')
3510 } else {
3511 g.write('builtin___result_ok((${g.styp(final_expr_sym.idx)})')
3512 }
3513 } else {
3514 g.write('builtin___result_ok(&(${styp}[]) { ')
3515 expected_type = ret_typ.clear_flag(.result)
3516 }
3517 }
3518 if !already_generated {
3519 g.expr_with_cast(expr, expr_typ, expected_type)
3520 }
3521
3522 if fn_option_clone {
3523 g.write('.data')
3524 }
3525 if ret_typ_is_option {
3526 if simple_assign {
3527 g.writeln(';')
3528 } else if expr_is_fixed_array_var {
3529 g.writeln(', (${option_name}*)(&${tmp_var}), sizeof(${styp}));')
3530 } else {
3531 g.writeln(' }, (${option_name}*)(&${tmp_var}), sizeof(${styp}));')
3532 }
3533 } else {
3534 if expr_is_fixed_array_var {
3535 g.writeln(', (${result_name}*)(&${tmp_var}), sizeof(${styp}));')
3536 } else {
3537 g.writeln(' }, (${result_name}*)(&${tmp_var}), sizeof(${styp}));')
3538 }
3539 }
3540 g.set_current_pos_as_last_stmt_pos()
3541 }
3542
3543 g.write2(stmt_str, ' ')
3544 if use_at_once {
3545 g.write(tmp_var)
3546 }
3547}
3548
3549@[inline]
3550fn (mut g Gen) write_v_source_line_info_pos(pos token.Pos) {
3551 if g.inside_ternary == 0 && g.pref.is_vlines && g.is_vlines_enabled {
3552 nline := pos.line_nr + 1
3553 lineinfo := '\n#line ${nline} "${g.vlines_path}"'
3554 $if trace_gen_source_line_info ? {
3555 eprintln('> lineinfo: ${lineinfo.replace('\n', '')}')
3556 }
3557 g.writeln(lineinfo)
3558 }
3559}
3560
3561@[inline]
3562fn (mut g Gen) write_v_source_line_info(node ast.Node) {
3563 g.write_v_source_line_info_pos(node.pos())
3564 if g.inside_ternary == 0 && g.pref.is_coverage
3565 && node !in [ast.MatchBranch, ast.IfBranch, ast.InfixExpr] {
3566 g.write_coverage_point(node.pos())
3567 }
3568}
3569
3570@[inline]
3571fn (mut g Gen) write_v_source_line_info_stmt(stmt ast.Stmt) {
3572 g.write_v_source_line_info_pos(stmt.pos)
3573 if g.inside_ternary == 0 && g.pref.is_coverage && !g.inside_for_c_stmt
3574 && stmt !in [ast.FnDecl, ast.ForCStmt, ast.ForInStmt, ast.ForStmt] {
3575 if stmt is ast.AssertStmt {
3576 if stmt.expr !in [ast.InfixExpr, ast.MatchExpr] {
3577 g.write_coverage_point(stmt.pos)
3578 }
3579 } else {
3580 g.write_coverage_point(stmt.pos)
3581 }
3582 }
3583}
3584
3585fn (g &Gen) should_autofree_discarded_call_result(typ ast.Type) bool {
3586 if typ == 0 || typ == ast.void_type || typ.has_option_or_result() {
3587 return false
3588 }
3589 sym := g.table.final_sym(typ)
3590 if sym.kind in [.array, .string] {
3591 return true
3592 }
3593 if sym.has_method('free') {
3594 return true
3595 }
3596 sym_name := sym.name.after('.')
3597 is_user_ref := typ.is_ptr() && sym_name.len > 0 && sym_name[0].is_capital()
3598 if is_user_ref && g.pref.experimental {
3599 return true
3600 }
3601 return false
3602}
3603
3604fn (mut g Gen) stmt(node ast.Stmt) {
3605 $if trace_cgen_stmt ? {
3606 ntype := typeof(node).replace('v.ast.', '')
3607 if g.file == unsafe { nil } {
3608 eprintln('cgen: <nil> | pos: ${node.pos.line_str():-39} | node: ${ntype} | ${node}')
3609 } else {
3610 eprintln('cgen: ${g.file.path:-30} | pos: ${node.pos.line_str():-39} | node: ${ntype} | ${node}')
3611 }
3612 }
3613 old_inside_call := g.inside_call
3614 g.inside_call = false
3615 defer {
3616 g.inside_call = old_inside_call
3617 }
3618 if !g.skip_stmt_pos {
3619 g.set_current_pos_as_last_stmt_pos()
3620 }
3621 match node {
3622 ast.FnDecl {
3623 g.fn_decl(node)
3624 }
3625 ast.Block {
3626 g.write_v_source_line_info_stmt(node)
3627 if node.is_unsafe {
3628 g.unsafe_level++
3629 }
3630
3631 if g.cur_fn != unsafe { nil } {
3632 if !node.is_unsafe {
3633 g.writeln('{')
3634 } else {
3635 if g.pref.is_prod {
3636 g.writeln('{')
3637 } else {
3638 g.writeln('{ // Unsafe block')
3639 }
3640 }
3641 }
3642 g.stmts(node.stmts)
3643 g.write_defer_stmts(node.scope, false, node.pos)
3644 if g.cur_fn != unsafe { nil } {
3645 g.writeln('}')
3646 }
3647 if node.is_unsafe {
3648 g.unsafe_level--
3649 }
3650 }
3651 ast.AssignStmt {
3652 g.write_v_source_line_info_stmt(node)
3653 g.assign_stmt(node)
3654 }
3655 ast.AssertStmt {
3656 g.write_v_source_line_info_stmt(node)
3657 g.assert_stmt(node)
3658 }
3659 ast.ConstDecl {
3660 g.write_v_source_line_info_stmt(node)
3661 g.const_decl(node)
3662 }
3663 ast.ComptimeFor {
3664 g.comptime_for(node)
3665 }
3666 ast.BranchStmt {
3667 g.write_v_source_line_info_stmt(node)
3668 g.branch_stmt(node)
3669 }
3670 ast.ExprStmt {
3671 g.write_v_source_line_info_stmt(node)
3672 // af := g.autofree && node.expr is ast.CallExpr && !g.is_builtin_mod
3673 // if af {
3674 // g.autofree_call_pregen(node.expr as ast.CallExpr)
3675 // }
3676 can_emit_standalone_expr_stmt := !g.skip_stmt_pos && g.inside_ternary == 0
3677 && !g.inside_if_option && !g.inside_match_option && !g.inside_if_result
3678 && !g.inside_match_result && !node.is_expr && node.expr !is ast.IfExpr
3679 mut needs_tmp_return_autofree := false
3680 if can_emit_standalone_expr_stmt && g.is_autofree && !g.is_builtin_mod
3681 && node.expr is ast.CallExpr && g.should_autofree_discarded_call_result(node.typ) {
3682 call_expr := node.expr as ast.CallExpr
3683 needs_tmp_return_autofree = call_expr.or_block.kind == .absent
3684 }
3685 scope_gc_pins := if can_emit_standalone_expr_stmt && !needs_tmp_return_autofree
3686 && node.expr is ast.CallExpr {
3687 g.scope_gc_pin_pregen(node.pos.pos)
3688 } else {
3689 []ScopeGcPin{}
3690 }
3691 mut discarded_ret_tmp_name := ''
3692 if needs_tmp_return_autofree {
3693 discarded_ret_tmp_name = g.new_tmp_var()
3694 g.write('${g.styp(node.typ)} ${discarded_ret_tmp_name} = ')
3695 }
3696 old_is_void_expr_stmt := g.is_void_expr_stmt
3697 g.is_void_expr_stmt = !node.is_expr && !needs_tmp_return_autofree
3698 if node.expr.is_auto_deref_var() {
3699 g.write('*')
3700 }
3701 if node.typ != ast.void_type && g.expected_cast_type != 0
3702 && node.expr !in [ast.IfExpr, ast.MatchExpr] {
3703 g.expr_with_cast(node.expr, node.typ, g.expected_cast_type)
3704 } else {
3705 g.expr(node.expr)
3706 }
3707 g.is_void_expr_stmt = old_is_void_expr_stmt
3708 if needs_tmp_return_autofree {
3709 g.writeln(';')
3710 // Discarded non-void call results should still be released under -autofree.
3711 g.autofree_variable(ast.Var{
3712 name: discarded_ret_tmp_name
3713 expr: node.expr
3714 typ: node.typ
3715 pos: node.pos
3716 })
3717 for g.autofree_scope_stmts.len > 0 {
3718 g.write(g.autofree_scope_stmts.pop())
3719 }
3720 } else if g.inside_ternary == 0 && !g.inside_if_option && !g.inside_match_option
3721 && !g.inside_if_result && !g.inside_match_result && !node.is_expr
3722 && node.expr !is ast.IfExpr {
3723 if node.expr is ast.MatchExpr {
3724 g.writeln('')
3725 } else {
3726 g.writeln(';')
3727 }
3728 }
3729 if can_emit_standalone_expr_stmt && !needs_tmp_return_autofree
3730 && node.expr is ast.CallExpr {
3731 g.write_scope_gc_pins(node.pos)
3732 }
3733 g.scope_gc_pin_postgen(scope_gc_pins)
3734 }
3735 ast.ForCStmt {
3736 prev_branch_parent_pos := g.branch_parent_pos
3737 g.branch_parent_pos = node.pos.pos
3738 save_inner_loop := g.inner_loop
3739 g.inner_loop = unsafe { &node }
3740 if node.label != '' {
3741 unsafe {
3742 g.labeled_loops[node.label] = &node
3743 }
3744 }
3745 g.write_v_source_line_info_stmt(node)
3746 g.for_c_stmt(node)
3747 g.branch_parent_pos = prev_branch_parent_pos
3748 g.labeled_loops.delete(node.label)
3749 g.inner_loop = save_inner_loop
3750 }
3751 ast.ForInStmt {
3752 prev_branch_parent_pos := g.branch_parent_pos
3753 g.branch_parent_pos = node.pos.pos
3754 save_inner_loop := g.inner_loop
3755 g.inner_loop = unsafe { &node }
3756 if node.label != '' {
3757 unsafe {
3758 g.labeled_loops[node.label] = &node
3759 }
3760 }
3761 g.write_v_source_line_info_stmt(node)
3762 g.for_in_stmt(node)
3763 g.branch_parent_pos = prev_branch_parent_pos
3764 g.labeled_loops.delete(node.label)
3765 g.inner_loop = save_inner_loop
3766 }
3767 ast.ForStmt {
3768 prev_branch_parent_pos := g.branch_parent_pos
3769 g.branch_parent_pos = node.pos.pos
3770 save_inner_loop := g.inner_loop
3771 g.inner_loop = unsafe { &node }
3772 if node.label != '' {
3773 unsafe {
3774 g.labeled_loops[node.label] = &node
3775 }
3776 }
3777 g.write_v_source_line_info_stmt(node)
3778 g.for_stmt(node)
3779 g.branch_parent_pos = prev_branch_parent_pos
3780 g.labeled_loops.delete(node.label)
3781 g.inner_loop = save_inner_loop
3782 }
3783 ast.Return {
3784 g.return_stmt(node)
3785 }
3786 ast.DeferStmt {
3787 mut defer_stmt := node
3788 defer_stmt.ifdef = g.defer_ifdef
3789 if defer_stmt.mode == .function {
3790 g.writeln('${g.defer_flag_var(defer_stmt)} = true;')
3791 }
3792 g.defer_stmts << defer_stmt
3793 }
3794 ast.EnumDecl {
3795 g.enum_decl(node)
3796 }
3797 ast.GlobalDecl {
3798 g.global_decl(node)
3799 }
3800 ast.Import {}
3801 ast.InterfaceDecl {
3802 // definitions are sorted and added in write_types
3803 ts := g.table.sym(node.typ)
3804 if !(ts.info as ast.Interface).is_generic
3805 && (!g.pref.skip_unused || ts.idx in g.table.used_features.used_syms) {
3806 for method in node.methods {
3807 if method.return_type.has_flag(.option) {
3808 // Register an option if it's not registered yet
3809 g.register_option(method.return_type)
3810 } else if method.return_type.has_flag(.result) {
3811 if g.pref.skip_unused
3812 && g.table.sym(method.return_type).idx !in g.table.used_features.used_syms {
3813 continue
3814 }
3815 // Register a result if it's not registered yet
3816 g.register_result(method.return_type)
3817 }
3818 }
3819 }
3820 }
3821 ast.StructDecl {
3822 name := if node.language == .c {
3823 util.no_dots(node.name)
3824 } else if node.name in ['array', 'string'] {
3825 node.name
3826 } else if node.scoped_name != '' {
3827 c_name(node.scoped_name)
3828 } else {
3829 c_name(node.name)
3830 }
3831 // TODO: For some reason, build fails with autofree with this line
3832 // as it's only informative, comment it for now
3833 // g.gen_attrs(node.attrs)
3834 // g.writeln('typedef struct {')
3835 // for field in it.fields {
3836 // field_type_sym := g.table.sym(field.typ)
3837 // g.writeln('\t${field_type_sym.name} ${field.name};')
3838 // }
3839 // g.writeln('} ${name};')
3840 if node.language == .c {
3841 return
3842 }
3843 if g.pref.skip_unused && node.idx !in g.table.used_features.used_syms {
3844 return
3845 }
3846 if node.is_union {
3847 g.typedefs.writeln('typedef union ${name} ${name};')
3848 } else {
3849 g.typedefs.writeln('typedef struct ${name} ${name};')
3850 }
3851 }
3852 ast.GotoLabel {
3853 g.writeln('${c_name(node.name)}: {}')
3854 }
3855 ast.GotoStmt {
3856 g.write_v_source_line_info_stmt(node)
3857 g.writeln('goto ${c_name(node.name)};')
3858 }
3859 ast.AsmStmt {
3860 if g.is_cc_msvc && !g.pref.output_cross_c {
3861 g.error('msvc does not support inline assembly', node.pos)
3862 }
3863 if g.should_skip_linux_bare_startup_stub(node) {
3864 return
3865 }
3866 g.write_v_source_line_info_stmt(node)
3867 g.asm_stmt(node)
3868 }
3869 ast.HashStmt {
3870 g.hash_stmt(node)
3871 }
3872 ast.TypeDecl {
3873 if !g.pref.skip_unused {
3874 g.writeln('// TypeDecl')
3875 }
3876 }
3877 ast.SemicolonStmt {
3878 g.writeln(';')
3879 }
3880 ast.SqlStmt {
3881 g.sql_stmt(node)
3882 }
3883 ast.Module {
3884 g.is_builtin_mod = util.module_is_builtin(node.name)
3885 g.is_builtin_overflow_mod = node.name == 'builtin.overflow'
3886 g.cur_mod = node
3887 }
3888 ast.EmptyStmt {}
3889 ast.DebuggerStmt {
3890 g.debugger_stmt(node)
3891 }
3892 ast.NodeError {}
3893 }
3894
3895 if !g.skip_stmt_pos { // && g.stmt_path_pos.len > 0 {
3896 g.stmt_path_pos.delete_last()
3897 }
3898 // TODO: If we have temporary string exprs to free after this statement, do it. e.g.:
3899 // `foo('a' + 'b')` => `tmp := 'a' + 'b'; foo(tmp); string_free(&tmp);`
3900}
3901
3902struct SumtypeCastingFn {
3903 fn_name string
3904 got ast.Type
3905 exp ast.Type
3906}
3907
3908fn (mut g Gen) get_sumtype_casting_fn(got_ ast.Type, exp_ ast.Type) string {
3909 mut got, exp := got_, exp_.idx_type()
3910 i := '${u32(got_)}:${u32(exp_)}'
3911 exp_sym := g.table.sym(exp)
3912 mut got_sym := g.table.sym(got)
3913 same_parent_sumtype_alias := got_sym.kind == .alias && g.table.unaliased_type(got_) == exp
3914 cname := if exp == ast.int_type_idx {
3915 ast.int_type_name
3916 } else {
3917 g.get_sumtype_variant_name(exp_, exp_sym)
3918 }
3919 // fn_name := '${got_sym.cname}_to_sumtype_${exp_sym.cname}'
3920 sumtype_variant_name := g.get_sumtype_casting_variant_name(got_, got_sym)
3921 fn_name := '${sumtype_variant_name}_to_sumtype_${cname}'
3922 if g.pref.experimental && fn_name.contains('v__ast__Struct_to_sumtype_v__ast__TypeInfo') {
3923 print_backtrace()
3924 eprintln('======\n\n')
3925 // exit(0)
3926 }
3927 if got == exp || g.sumtype_definitions[i] {
3928 return fn_name
3929 }
3930 // Also check by fn_name to avoid duplicates when the same conversion
3931 // is requested with different type flags (e.g., mutability):
3932 for existing in g.sumtype_casting_fns {
3933 if existing.fn_name == fn_name {
3934 g.sumtype_definitions[i] = true
3935 return fn_name
3936 }
3937 }
3938 if !same_parent_sumtype_alias {
3939 for got_sym.parent_idx != 0 && got_sym.idx !in (exp_sym.info as ast.SumType).variants {
3940 got_sym = g.table.sym(ast.idx_to_type(got_sym.parent_idx))
3941 }
3942 }
3943 g.sumtype_definitions[i] = true
3944 g.sumtype_casting_fns << SumtypeCastingFn{
3945 fn_name: fn_name
3946 got: if same_parent_sumtype_alias {
3947 got_
3948 } else {
3949 ast.idx_to_type(got_sym.idx).derive(got_)
3950 }
3951 exp: if exp_.has_flag(.option) {
3952 new_exp := exp.set_flag(.option)
3953 new_exp
3954 } else {
3955 exp
3956 }
3957 }
3958 return fn_name
3959}
3960
3961fn (mut g Gen) write_sumtype_casting_fn(fun SumtypeCastingFn) {
3962 got, exp := fun.got, fun.exp
3963 got_sym, exp_sym := g.table.sym(got), g.table.sym(exp)
3964 mut got_cname, exp_cname := g.get_sumtype_variant_type_name(got, got_sym), exp_sym.cname
3965 mut type_idx := g.type_sidx(got)
3966 mut sb := strings.new_builder(128)
3967 mut variant_name := g.get_sumtype_variant_name(got, got_sym)
3968 if got_sym.kind == .alias && got_sym.info is ast.Alias
3969 && g.table.unaliased_type(got).idx() == exp.idx() {
3970 g.definitions.writeln('${exp_cname} ${fun.fn_name}(${got_cname}* x, bool is_mut);')
3971 sb.writeln('${exp_cname} ${fun.fn_name}(${got_cname}* x, bool is_mut) {')
3972 sb.writeln('\treturn *(${exp_cname}*)x;')
3973 sb.writeln('}\n')
3974 g.auto_fn_definitions << sb.str()
3975 return
3976 }
3977 if got_sym.info is ast.FnType {
3978 got_name := 'fn ${g.table.fn_type_source_signature(got_sym.info.func)}'
3979 got_cname = 'anon_fn_${g.table.fn_type_signature(got_sym.info.func)}'
3980 type_idx = g.type_sidx(ast.idx_to_type(g.table.type_idxs[got_name]))
3981 for variant in g.sumtype_runtime_variants(exp) {
3982 variant_sym := g.table.sym(variant)
3983 if variant_sym.info is ast.FnType {
3984 if g.table.fn_type_source_signature(variant_sym.info.func) == g.table.fn_type_source_signature(got_sym.info.func) {
3985 variant_name = g.get_sumtype_variant_name(variant, variant_sym)
3986 got_cname = g.get_sumtype_variant_type_name(variant, variant_sym)
3987 type_idx = g.type_sidx(variant)
3988 break
3989 }
3990 }
3991 }
3992 sb.writeln('static inline ${exp_cname} ${fun.fn_name}(${got_cname} x) {')
3993 sb.writeln('\t${got_cname} ptr = x;')
3994 } else {
3995 got_cname = g.styp(got)
3996 // g.definitions.writeln('${g.static_modifier} inline ${exp_cname} ${fun.fn_name}(${got_cname}* x);')
3997 // sb.writeln('${g.static_modifier} inline ${exp_cname} ${fun.fn_name}(${got_cname}* x) {')
3998 g.definitions.writeln('${exp_cname} ${fun.fn_name}(${got_cname}* x, bool is_mut);')
3999 sb.writeln('${exp_cname} ${fun.fn_name}(${got_cname}* x, bool is_mut) {')
4000 sb.writeln('\t${got_cname}* ptr = x;')
4001 sb.writeln('\tif (!is_mut) { ptr = builtin__memdup(x, sizeof(${got_cname})); }')
4002 }
4003 for embed_hierarchy in g.table.get_embeds(got_sym) {
4004 // last embed in the hierarchy
4005 mut embed_cname := ''
4006 mut embed_name := ''
4007 mut accessor := '&x->'
4008 for j, embed in embed_hierarchy {
4009 embed_sym := g.table.sym(embed)
4010 embed_cname = embed_sym.cname
4011 embed_name = embed_sym.embed_name()
4012 if j > 0 {
4013 accessor += '.'
4014 }
4015 accessor += embed_name
4016 }
4017 // if the variable is not used, the C compiler will optimize it away
4018 sb.writeln('\t${embed_cname}* ${embed_name}_ptr = builtin__memdup(${accessor}, sizeof(${embed_cname}));')
4019 }
4020 sb.write_string('\treturn (${exp_cname}){ ._${variant_name} = ptr, ._typ = ${type_idx}')
4021 for field in (exp_sym.info as ast.SumType).fields {
4022 mut ptr := 'ptr'
4023 mut type_cname := got_cname
4024 _, embed_types := g.table.find_field_from_embeds(got_sym, field.name) or {
4025 ast.StructField{}, []ast.Type{}
4026 }
4027 if embed_types.len > 0 {
4028 embed_sym := g.table.sym(embed_types.last())
4029 ptr = '${embed_sym.embed_name()}_ptr'
4030 type_cname = embed_sym.cname
4031 }
4032 field_styp := g.styp(field.typ)
4033 if got_sym.kind in [.sum_type, .interface] {
4034 // the field is already a wrapped pointer; we shouldn't wrap it once again
4035 sb.write_string(', .${c_name(field.name)} = ptr->${field.name}')
4036 } else {
4037 sb.write_string(', .${c_name(field.name)} = (${field_styp}*)((char*)${ptr} + __offsetof_ptr(${ptr}, ${type_cname}, ${c_name(field.name)}))')
4038 }
4039 }
4040 sb.writeln('};\n}')
4041 g.auto_fn_definitions << sb.str()
4042}
4043
4044@[inline]
4045fn (g &Gen) can_reuse_sumtype_variant_storage(exp ast.Type, got_is_ptr bool) bool {
4046 // `&SumType(value)` must copy value variants unless the cast is borrowing a mutable argument.
4047 // When expected_arg_mut is set, the caller wraps with ADDR() and passes back a pointer,
4048 // so the variant storage can be reused (no copy needed).
4049 return g.expected_arg_mut || (exp.is_ptr() && (got_is_ptr || g.expected_arg_mut))
4050}
4051
4052fn (g &Gen) ident_unwraps_option_or_result(expr ast.Ident) bool {
4053 if expr.obj is ast.Var && assign_expr_unwraps_option_or_result(expr.obj.expr) {
4054 return true
4055 }
4056 if expr.scope != unsafe { nil } {
4057 if v := expr.scope.find_var(expr.name) {
4058 return assign_expr_unwraps_option_or_result(v.expr)
4059 }
4060 }
4061 return false
4062}
4063
4064fn (g &Gen) expr_root_ident_unwraps_option_or_result(expr ast.Expr) bool {
4065 return match expr {
4066 ast.Ident {
4067 g.ident_unwraps_option_or_result(expr)
4068 }
4069 ast.SelectorExpr {
4070 g.expr_root_ident_unwraps_option_or_result(expr.expr)
4071 }
4072 ast.IndexExpr {
4073 g.expr_root_ident_unwraps_option_or_result(expr.left)
4074 }
4075 ast.ParExpr {
4076 g.expr_root_ident_unwraps_option_or_result(expr.expr)
4077 }
4078 ast.UnsafeExpr {
4079 g.expr_root_ident_unwraps_option_or_result(expr.expr)
4080 }
4081 else {
4082 false
4083 }
4084 }
4085}
4086
4087fn (mut g Gen) interface_expr_needs_heap(expr ast.Expr) bool {
4088 match expr {
4089 ast.Ident, ast.SelectorExpr, ast.IndexExpr {
4090 return !g.expr_has_stable_interface_cast_address(expr)
4091 }
4092 ast.ParExpr {
4093 return g.interface_expr_needs_heap(expr.expr)
4094 }
4095 ast.UnsafeExpr {
4096 return g.interface_expr_needs_heap(expr.expr)
4097 }
4098 else {
4099 return false
4100 }
4101 }
4102}
4103
4104fn (mut g Gen) expr_has_stable_interface_cast_address(expr ast.Expr) bool {
4105 match expr {
4106 ast.Ident {
4107 if expr.kind == .global {
4108 return true
4109 }
4110 return g.resolved_ident_is_auto_heap(expr) && !g.ident_unwraps_option_or_result(expr)
4111 }
4112 ast.SelectorExpr {
4113 if expr.expr_type.is_ptr() {
4114 return true
4115 }
4116 return g.expr_has_stable_interface_cast_address(expr.expr)
4117 }
4118 ast.IndexExpr {
4119 if expr.index is ast.RangeExpr {
4120 return false
4121 }
4122 if expr.left_type.is_ptr() {
4123 return true
4124 }
4125 return g.expr_has_stable_interface_cast_address(expr.left)
4126 }
4127 else {
4128 return false
4129 }
4130 }
4131}
4132
4133fn (mut g Gen) expr_needs_heap_promotion_for_interface_cast(expr ast.Expr) bool {
4134 match expr {
4135 ast.Ident, ast.SelectorExpr, ast.IndexExpr {
4136 return !g.expr_has_stable_interface_cast_address(expr)
4137 }
4138 else {
4139 return false
4140 }
4141 }
4142}
4143
4144fn (g &Gen) interface_cast_requires_field_aliasing(expected_type ast.Type, expr ast.Expr) bool {
4145 if !expr.is_lvalue() {
4146 return false
4147 }
4148 interface_type := if expected_type.is_ptr() {
4149 expected_type.deref()
4150 } else {
4151 expected_type
4152 }
4153 interface_sym := g.table.final_sym(interface_type)
4154 if interface_sym.kind != .interface {
4155 return false
4156 }
4157 return match interface_sym.info {
4158 ast.Interface { interface_sym.info.fields.any(it.is_mut) }
4159 else { false }
4160 }
4161}
4162
4163fn (mut g Gen) fn_ptr_cast_typ(func ast.FnType) string {
4164 ptr_name := '__v_fn_ptr'
4165 return g.fn_ptr_decl_str(func, ptr_name).replace_once(ptr_name, '')
4166}
4167
4168fn (mut g Gen) call_cfn_for_casting_expr(fname string, expr ast.Expr, exp ast.Type, got ast.Type, actual_got ast.Type, exp_styp string,
4169 got_is_ptr bool, got_is_fn bool, got_styp string) {
4170 mut rparen_n := 1
4171 mut mutable_is_mut_arg_pos := 0
4172 mut force_auto_heap_value_deref := false
4173
4174 is_not_ptr_and_fn := !got_is_ptr && !got_is_fn
4175 is_sumtype_cast := !got_is_fn && fname.contains('_to_sumtype_')
4176 is_interface_cast := !got_is_fn && fname.contains('_to_Interface_')
4177 interface_cast_source_expr := if is_interface_cast && expr is ast.CastExpr {
4178 expr.expr
4179 } else {
4180 expr
4181 }
4182 needs_got_cast := is_sumtype_cast && actual_got != 0
4183 && g.table.can_implicit_array_cast(actual_got, got)
4184 expr_is_lvalue := if needs_got_cast {
4185 false
4186 } else if is_interface_cast {
4187 interface_cast_source_expr.is_lvalue()
4188 } else {
4189 expr.is_lvalue()
4190 }
4191 is_comptime_variant := is_not_ptr_and_fn && expr is ast.Ident
4192 && g.comptime.is_comptime_variant_var(expr)
4193 mut is_stack_rooted_interface_expr := false
4194 if exp.is_ptr() {
4195 if (expr is ast.UnsafeExpr && expr.expr is ast.Nil) || got == ast.nil_type {
4196 g.write('(void*)0')
4197 return
4198 } else {
4199 g.write('HEAP(${exp_styp}, ${fname}(')
4200 rparen_n++
4201 }
4202 } else {
4203 g.write('${fname}(')
4204 }
4205 mutable_is_mut_arg_pos = rparen_n
4206 if is_not_ptr_and_fn {
4207 is_cast_fixed_array_init := expr is ast.CastExpr
4208 && (expr.expr is ast.ArrayInit && expr.expr.is_fixed)
4209
4210 is_primitive_to_interface := is_interface_cast && expr is ast.Ident
4211 && g.table.sym(got).kind in [.i8, .i16, .i32, .int, .i64, .isize, .u8, .u16, .u32, .u64, .usize, .f32, .f64, .bool, .rune, .string]
4212 preserve_interface_field_aliasing := is_interface_cast
4213 && g.interface_cast_requires_field_aliasing(exp, interface_cast_source_expr)
4214
4215 // Interface casts must not store pointers into stack-rooted lvalues such as
4216 // local variables or fields on by-value fn args (`p.email`).
4217 is_stack_rooted_interface_expr = is_interface_cast && !preserve_interface_field_aliasing
4218 && g.interface_expr_needs_heap(interface_cast_source_expr)
4219 // Interface casts must not store pointers into expressions without a
4220 // stable address, including stack-rooted lvalues and slice results.
4221 needs_interface_cast_promotion := is_interface_cast && !preserve_interface_field_aliasing
4222 && g.expr_needs_heap_promotion_for_interface_cast(interface_cast_source_expr)
4223 // Value-to-interface conversions should preserve value semantics by storing a detached copy.
4224 // The only exception is an explicit mutable borrow, which must keep aliasing the original.
4225 // Interfaces with mutable fields also need to alias the original lvalue so field reads/writes
4226 // stay connected to the concrete value instead of a detached clone.
4227 needs_interface_value_copy := is_interface_cast && !preserve_interface_field_aliasing
4228 && !g.expected_arg_mut && !got_is_ptr && expr_is_lvalue
4229 force_auto_heap_value_deref = needs_interface_value_copy && expr is ast.Ident
4230 && g.scope_ident_is_auto_heap(expr)
4231
4232 if !is_cast_fixed_array_init && (is_comptime_variant || !expr_is_lvalue
4233 || (expr is ast.Ident && (expr.obj.is_simple_define_const()
4234 || (expr.obj is ast.Var && expr.obj.is_index_var)))
4235 || is_primitive_to_interface || needs_interface_cast_promotion
4236 || needs_interface_value_copy) {
4237 // Note: the `_to_sumtype_` family of functions do call memdup internally, making
4238 // another duplicate with the HEAP macro is redundant, so use ADDR instead:
4239 if expr.is_as_cast() {
4240 if !got_is_ptr && expr is ast.SelectorExpr {
4241 // (var as Type).field_non_ptr
4242 g.write('&')
4243 }
4244 old_inside_smartcast := g.inside_smartcast
4245 g.inside_smartcast = true
4246 defer(fn) {
4247 g.inside_smartcast = old_inside_smartcast
4248 }
4249 } else {
4250 promotion_macro_name := if is_sumtype_cast { 'ADDR' } else { 'HEAP' }
4251 g.write('${promotion_macro_name}(${got_styp}, (')
4252 rparen_n += 2
4253 }
4254 } else {
4255 g.write('&')
4256 }
4257 }
4258 if got_styp == 'none' && !g.cur_fn.return_type.has_flag(.option) {
4259 g.write('(none){E_STRUCT}')
4260 } else if is_comptime_variant {
4261 ctyp := g.type_resolver.get_ct_type_or_default('${g.comptime.comptime_for_variant_var}.typ',
4262 ast.void_type)
4263 g.write(g.type_default(ctyp))
4264 } else {
4265 old_inside_sumtype_cast := g.inside_sumtype_cast
4266 g.inside_sumtype_cast = true
4267 old_left_is_opt := g.left_is_opt
4268 g.left_is_opt = !exp.has_flag(.option)
4269 old_inside_assign_fn_var := g.inside_assign_fn_var
4270 if force_auto_heap_value_deref {
4271 g.inside_assign_fn_var = false
4272 } else if is_stack_rooted_interface_expr && g.expr_root_ident_unwraps_option_or_result(expr) {
4273 g.inside_assign_fn_var = true
4274 }
4275 if needs_got_cast {
4276 g.expr_with_cast(expr, actual_got, got)
4277 } else {
4278 g.expr(expr)
4279 }
4280 g.inside_assign_fn_var = old_inside_assign_fn_var
4281 g.left_is_opt = old_left_is_opt
4282 g.inside_sumtype_cast = old_inside_sumtype_cast
4283 }
4284 if is_sumtype_cast {
4285 // the `_to_sumtype_` family of functions last `is_mut` param
4286 g.write(')'.repeat(rparen_n - mutable_is_mut_arg_pos))
4287 g.write(', ${g.can_reuse_sumtype_variant_storage(exp, got_is_ptr)}')
4288 g.write(')'.repeat(mutable_is_mut_arg_pos))
4289 } else {
4290 g.write(')'.repeat(rparen_n))
4291 }
4292}
4293
4294fn (g &Gen) single_pointer_sumtype_nil_variant(expected_type ast.Type, expr ast.Expr, got_type ast.Type) ast.Type {
4295 if !expr.is_nil() && got_type != ast.nil_type {
4296 return 0
4297 }
4298 expected_sym := g.table.final_sym(expected_type)
4299 if expected_sym.kind != .sum_type {
4300 return 0
4301 }
4302 mut variant := ast.Type(0)
4303 for sumtype_variant in g.sumtype_runtime_variants(expected_type) {
4304 if g.table.unaliased_type(sumtype_variant).is_any_kind_of_pointer() {
4305 if variant != 0 {
4306 return 0
4307 }
4308 variant = sumtype_variant
4309 }
4310 }
4311 return variant
4312}
4313
4314@[inline]
4315fn (g &Gen) is_exact_sumtype_variant_match(variant ast.Type, got ast.Type) bool {
4316 if variant.idx() != got.idx() || variant.has_flag(.option) != got.has_flag(.option)
4317 || variant.nr_muls() != got.nr_muls() {
4318 return false
4319 }
4320 variant_sym := g.table.sym(variant)
4321 got_sym := g.table.sym(got)
4322 if variant_sym.info is ast.FnType && got_sym.info is ast.FnType {
4323 return g.table.fn_type_source_signature(variant_sym.info.func) == g.table.fn_type_source_signature(got_sym.info.func)
4324 }
4325 return true
4326}
4327
4328fn (g &Gen) find_matching_sumtype_variant(expected_type ast.Type, got_type ast.Type) ast.Type {
4329 expected_sym := g.table.final_sym(expected_type)
4330 if expected_sym.kind != .sum_type {
4331 return got_type
4332 }
4333 variants := g.sumtype_runtime_variants(expected_type)
4334 for variant in variants {
4335 if g.is_exact_sumtype_variant_match(variant, got_type) {
4336 return variant
4337 }
4338 }
4339 for variant in variants {
4340 if g.table.can_implicit_array_cast(got_type, variant) {
4341 return variant
4342 }
4343 }
4344 if got_type.is_any_kind_of_pointer() {
4345 deref_got_type := got_type.deref()
4346 for variant in variants {
4347 if g.is_exact_sumtype_variant_match(variant, deref_got_type) {
4348 return variant
4349 }
4350 }
4351 }
4352 return got_type
4353}
4354
4355fn (mut g Gen) sumtype_ptr_cast_payload_field(sumtype_typ ast.Type, target_ptr_typ ast.Type) string {
4356 matched_variant := g.find_matching_sumtype_variant(sumtype_typ, target_ptr_typ.deref())
4357 if g.table.sumtype_has_variant(sumtype_typ, matched_variant, false) {
4358 matched_variant_sym := g.table.sym(matched_variant)
4359 return g.get_sumtype_variant_name(matched_variant, matched_variant_sym)
4360 }
4361 return ''
4362}
4363
4364fn (mut g Gen) write_payload_pointer_from_ptr_wrapper(expr ast.Expr, wrapper_typ ast.Type,
4365 target_ptr_typ ast.Type) {
4366 wrapper_sym := g.table.final_sym(wrapper_typ)
4367 match wrapper_sym.kind {
4368 .interface {
4369 g.write('((')
4370 g.expr(expr)
4371 g.write(')->_object')
4372 g.write(')')
4373 }
4374 .sum_type {
4375 field_name := g.sumtype_ptr_cast_payload_field(wrapper_typ, target_ptr_typ)
4376 if field_name == '' {
4377 g.write('((*((void**)(')
4378 g.expr(expr)
4379 g.write('))))')
4380 } else {
4381 g.write('((')
4382 g.expr(expr)
4383 g.write(')->_${field_name})')
4384 }
4385 }
4386 else {}
4387 }
4388}
4389
4390fn (mut g Gen) ptr_wrapper_expr_is_raw_alias(expr ast.Expr, wrapper_typ ast.Type) bool {
4391 expected_wrapper_typ := g.table.unaliased_type(g.unwrap_generic(wrapper_typ))
4392 return match expr {
4393 ast.UnsafeExpr {
4394 g.ptr_wrapper_expr_is_raw_alias(expr.expr, expected_wrapper_typ)
4395 }
4396 ast.ParExpr {
4397 g.ptr_wrapper_expr_is_raw_alias(expr.expr, expected_wrapper_typ)
4398 }
4399 ast.CastExpr {
4400 target_typ := g.unwrap_generic(expr.typ)
4401 target_wrapper_typ := if target_typ.is_ptr() {
4402 g.table.unaliased_type(g.unwrap_generic(target_typ.deref()))
4403 } else {
4404 ast.void_type
4405 }
4406 source_typ := g.unwrap_generic(expr.expr_type)
4407 if target_wrapper_typ != expected_wrapper_typ
4408 || (!source_typ.is_any_kind_of_pointer() && source_typ != ast.voidptr_type) {
4409 false
4410 } else if source_typ.is_ptr() && source_typ.nr_muls() == 1 {
4411 source_wrapper_typ := g.table.unaliased_type(g.unwrap_generic(source_typ.deref()))
4412 source_wrapper_typ != expected_wrapper_typ
4413 } else {
4414 true
4415 }
4416 }
4417 ast.Ident {
4418 if expr.obj is ast.Var && expr.obj.expr !is ast.EmptyExpr
4419 && !(expr.obj.expr is ast.Ident && expr.obj.expr.name == expr.name) {
4420 g.ptr_wrapper_expr_is_raw_alias(expr.obj.expr, expected_wrapper_typ)
4421 } else if expr.scope != unsafe { nil } {
4422 if v := expr.scope.find_var(expr.name) {
4423 if v.expr !is ast.EmptyExpr && !(v.expr is ast.Ident
4424 && v.expr.name == expr.name) {
4425 return g.ptr_wrapper_expr_is_raw_alias(v.expr, expected_wrapper_typ)
4426 }
4427 }
4428 false
4429 } else {
4430 false
4431 }
4432 }
4433 else {
4434 false
4435 }
4436 }
4437}
4438
4439// use instead of expr() when you need a var to use as reference
4440fn (mut g Gen) expr_with_var(expr ast.Expr, expected_type ast.Type, do_cast bool) string {
4441 stmt_str := g.go_before_last_stmt().trim_space()
4442 g.empty_line = true
4443 tmp_var := g.new_tmp_var()
4444 styp := g.styp(expected_type)
4445
4446 g.writeln('${styp} ${tmp_var};')
4447 g.write('memcpy(&${tmp_var}, ')
4448 if do_cast {
4449 g.write('(${styp})')
4450 }
4451 // Temporarily clear inside_struct_init and assign_op so that cast expressions
4452 // for fixed array aliases generate proper compound literal casts
4453 old_inside_struct_init := g.inside_struct_init
4454 old_assign_op := g.assign_op
4455 g.inside_struct_init = false
4456 g.assign_op = .unknown
4457 g.expr(expr)
4458 g.inside_struct_init = old_inside_struct_init
4459 g.assign_op = old_assign_op
4460 g.writeln(', sizeof(${styp}));')
4461 g.write(stmt_str)
4462 return tmp_var
4463}
4464
4465// expr_with_fixed_array generates code for fixed array initialization with expr which requires tmp var
4466fn (mut g Gen) expr_with_fixed_array(expr ast.Expr, got_type_raw ast.Type, expected_type ast.Type) string {
4467 stmt_str := g.go_before_last_stmt().trim_space()
4468 g.empty_line = true
4469 tmp_var := g.new_tmp_var()
4470 styp := g.styp(expected_type)
4471 if expr is ast.ArrayInit {
4472 g.writeln('${styp} ${tmp_var};')
4473 // [ foo(), foo() ]!
4474 val_typ := g.table.value_type(got_type_raw)
4475 val_styp := g.styp(val_typ)
4476 val_sym := g.table.final_sym(val_typ)
4477 prefix := if val_sym.kind !in [.array, .array_fixed] { '&' } else { '' }
4478 prepared_exprs := g.prepare_array_init_exprs(expr.exprs, expr.expr_types, val_typ)
4479 for i, item_expr in prepared_exprs {
4480 actual_item_expr := array_init_orig_expr(item_expr)
4481 g.write('memcpy(${prefix}${tmp_var}[${i}], ')
4482 needs_addr :=
4483 (actual_item_expr is ast.CallExpr && !actual_item_expr.return_type.is_ptr()
4484 && g.table.final_sym(actual_item_expr.return_type).kind !in [.array, .array_fixed])
4485 || (actual_item_expr is ast.InfixExpr && !actual_item_expr.promoted_type.is_ptr())
4486 || actual_item_expr is ast.StructInit
4487 if needs_addr {
4488 g.write('ADDR(${val_styp}, ')
4489 }
4490 g.expr(item_expr)
4491 if needs_addr {
4492 g.write(')')
4493 }
4494 g.writeln(', sizeof(${val_styp}));')
4495 }
4496 } else if expr is ast.CallExpr {
4497 // return var.call() where returns is option/result fixed array
4498 g.writeln('${styp} ${tmp_var} = {0};')
4499 g.write('memcpy(&${tmp_var}.data, ')
4500 g.expr(expr)
4501 g.writeln(', sizeof(${g.base_type(expected_type)}));')
4502 } else {
4503 g.expr(expr)
4504 }
4505 g.write(stmt_str)
4506 return tmp_var
4507}
4508
4509fn (mut g Gen) gen_interface_to_interface_conversion(expr ast.Expr, expr_type ast.Type, to_type ast.Type) {
4510 mut expr_type_sym := g.table.sym(expr_type)
4511 to_sym := g.table.sym(to_type)
4512 g.write('I_${expr_type_sym.cname}_as_I_${to_sym.cname}(')
4513 if expr_type.is_ptr() {
4514 g.write('*')
4515 }
4516 g.expr(expr)
4517 g.write(')')
4518
4519 mut info := expr_type_sym.info as ast.Interface
4520 right_info := to_sym.info as ast.Interface
4521 left_variants := g.runtime_interface_variants(info)
4522 right_variants := g.runtime_interface_variants(right_info)
4523 lock info.conversions {
4524 if to_type !in info.conversions {
4525 // For conversion into an empty interface, all of the left's variants
4526 // are valid (empty interfaces accept anything).
4527 if right_info.methods.len == 0 && right_info.fields.len == 0 {
4528 info.conversions[to_type] = left_variants.clone()
4529 } else {
4530 info.conversions[to_type] = g.interface_conversion_variants(left_variants,
4531 right_variants)
4532 }
4533 }
4534 }
4535 expr_type_sym.info = info
4536}
4537
4538fn (g &Gen) interface_conversion_variants(left_variants []ast.Type, right_variants []ast.Type) []ast.Type {
4539 mut variants := []ast.Type{cap: left_variants.len}
4540 for left_variant in left_variants {
4541 left_idx := left_variant.idx()
4542 for right_variant in right_variants {
4543 if right_variant.idx() == left_idx {
4544 variants << left_variant
4545 break
4546 }
4547 }
4548 }
4549 return variants
4550}
4551
4552// expr_with_array_element_upcast materializes an array whose elements are boxed into
4553// the expected interface or sum type element type.
4554fn (mut g Gen) expr_with_array_element_upcast(expr ast.Expr, got_type ast.Type, expected_type ast.Type) {
4555 got_sym := g.table.final_sym(got_type)
4556 expected_sym := g.table.final_sym(expected_type)
4557 got_info := got_sym.info as ast.Array
4558 expected_info := expected_sym.info as ast.Array
4559 got_elem_type := got_info.elem_type
4560 expected_elem_type := expected_info.elem_type
4561 got_styp := g.styp(got_type)
4562 expected_styp := g.styp(expected_type)
4563 got_elem_styp := g.styp(got_elem_type)
4564 expected_elem_styp := g.styp(expected_elem_type)
4565 expected_elem_sym := g.table.final_sym(expected_elem_type)
4566 noscan := g.check_noscan(expected_elem_type)
4567 stmt_str := g.go_before_last_stmt().trim_space()
4568 g.empty_line = true
4569 source_tmp := g.new_tmp_var()
4570 result_tmp := g.new_tmp_var()
4571 index_tmp := g.new_tmp_var()
4572 item_tmp := g.new_tmp_var()
4573 converted_tmp := g.new_tmp_var()
4574 g.write('${got_styp} ${source_tmp}_orig = ')
4575 g.expr(expr)
4576 g.writeln(';')
4577 g.writeln('${expected_styp} ${result_tmp} = builtin____new_array${noscan}(0, ${source_tmp}_orig.len, sizeof(${expected_elem_styp}));')
4578 g.writeln('for (${ast.int_type_name} ${index_tmp} = 0; ${index_tmp} < ${source_tmp}_orig.len; ++${index_tmp}) {')
4579 g.indent++
4580 if expected_elem_sym.kind == .interface {
4581 expected_interface_info := expected_elem_sym.info as ast.Interface
4582 concrete_got_elem_type := g.concrete_interface_cast_type(got_elem_type)
4583 concrete_got_elem_styp := g.styp(concrete_got_elem_type)
4584 mut fname := 'I_${g.cc_type(concrete_got_elem_type, true)}_to_Interface_${expected_elem_sym.cname}'
4585 lock g.referenced_fns {
4586 g.referenced_fns[fname] = true
4587 }
4588 if expected_interface_info.is_generic {
4589 fname = g.generic_fn_name(expected_interface_info.concrete_types, fname)
4590 }
4591 g.writeln('${expected_elem_styp} ${converted_tmp} = ${fname}(&(((${concrete_got_elem_styp}*)${source_tmp}_orig.data)[${index_tmp}]));')
4592 } else {
4593 g.write_prepared_var(item_tmp, got_elem_type, got_elem_styp, source_tmp, index_tmp, true,
4594 false)
4595 g.write('${expected_elem_styp} ${converted_tmp} = ')
4596 g.expr_with_cast(ast.Ident{
4597 name: item_tmp
4598 }, got_elem_type, expected_elem_type)
4599 g.writeln(';')
4600 }
4601 g.writeln('builtin__array_push${noscan}((array*)&${result_tmp}, &${converted_tmp});')
4602 g.indent--
4603 g.writeln('}')
4604 g.write(stmt_str)
4605 g.write(result_tmp)
4606}
4607
4608fn (mut g Gen) can_widen_small_int_infix_for_cast(expr ast.InfixExpr, got_type ast.Type, expected_type ast.Type) bool {
4609 if expr.op !in [.plus, .minus] {
4610 return false
4611 }
4612 got_base := g.table.unalias_num_type(g.unwrap_generic(got_type))
4613 expected_base := g.table.unalias_num_type(g.unwrap_generic(expected_type))
4614 if !expected_base.is_number() || got_base == expected_base
4615 || got_base !in ast.int_promoted_type_idxs {
4616 return false
4617 }
4618 left_default := if expr.left_type != 0 { expr.left_type } else { got_type }
4619 right_default := if expr.right_type != 0 { expr.right_type } else { got_type }
4620 mut left_type := g.resolved_expr_type(expr.left, left_default)
4621 mut right_type := g.resolved_expr_type(expr.right, right_default)
4622 if left_type == 0 {
4623 left_type = left_default
4624 }
4625 if right_type == 0 {
4626 right_type = right_default
4627 }
4628 left_base := g.table.unalias_num_type(g.unwrap_generic(left_type))
4629 right_base := g.table.unalias_num_type(g.unwrap_generic(right_type))
4630 return left_base == got_base && right_base == got_base
4631}
4632
4633fn (mut g Gen) widen_small_int_infix_for_cast(expr ast.InfixExpr, got_type ast.Type, expected_type ast.Type) {
4634 left_default := if expr.left_type != 0 { expr.left_type } else { got_type }
4635 right_default := if expr.right_type != 0 { expr.right_type } else { got_type }
4636 mut left_type := g.resolved_expr_type(expr.left, left_default)
4637 mut right_type := g.resolved_expr_type(expr.right, right_default)
4638 if left_type == 0 {
4639 left_type = left_default
4640 }
4641 if right_type == 0 {
4642 right_type = right_default
4643 }
4644 // Keep small integer arithmetic widened when the result is immediately
4645 // converted to a larger numeric type, e.g. sort comparator callbacks.
4646 g.write('(')
4647 g.expr_with_cast(expr.left, left_type, expected_type)
4648 g.write(' ${expr.op.str()} ')
4649 g.expr_with_cast(expr.right, right_type, expected_type)
4650 g.write(')')
4651}
4652
4653// use instead of expr() when you need to cast to a different type
4654fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_type ast.Type) {
4655 got_type := ast.mktyp(got_type_raw)
4656 exp_sym := g.table.final_sym(expected_type)
4657 got_sym := g.table.sym(got_type)
4658 expected_is_ptr := expected_type.is_ptr()
4659 got_is_ptr := got_type.is_ptr()
4660 unaliased_expected_type := g.table.unaliased_type(g.unwrap_generic(expected_type)).clear_flags()
4661 unaliased_got_type := g.table.unaliased_type(g.unwrap_generic(got_type)).clear_flags()
4662 if g.can_convert_array_to_interface_array(got_type, expected_type) {
4663 fn_name := g.register_array_interface_cast_fn(got_type, expected_type)
4664 g.write('${fn_name}(')
4665 g.expr(expr)
4666 g.write(')')
4667 return
4668 }
4669 if unaliased_expected_type == ast.string_type {
4670 match unaliased_got_type {
4671 ast.char_type {
4672 g.write('builtin__u8_ascii_str((u8)(')
4673 g.expr(expr)
4674 g.write('))')
4675 return
4676 }
4677 ast.rune_type {
4678 g.write('builtin__rune_str((rune)(')
4679 g.expr(expr)
4680 g.write('))')
4681 return
4682 }
4683 else {}
4684 }
4685 }
4686 // allow using the new Error struct as a string, to avoid a breaking change
4687 // TODO: temporary to allow people to migrate their code; remove soon
4688 if got_type == ast.error_type_idx && expected_type == ast.string_type_idx {
4689 g.write('(*(')
4690 g.expr(expr)
4691 g.write('.msg))')
4692 return
4693 }
4694 if got_sym.kind == .none && exp_sym.idx == ast.error_type_idx {
4695 g.expr(expr)
4696 return
4697 }
4698 if !got_is_ptr && !expected_is_ptr && !got_type_raw.has_flag(.shared_f)
4699 && !expected_type.has_flag(.shared_f)
4700 && g.table.can_implicit_array_cast(got_type, expected_type) {
4701 g.expr_with_array_element_upcast(expr, got_type, expected_type)
4702 return
4703 }
4704 if got_sym.info !is ast.Interface && exp_sym.info is ast.Interface
4705 && got_type.idx() != expected_type.idx() && !expected_type.has_flag(.result) {
4706 concrete_got_type := g.concrete_interface_cast_type(got_type)
4707 concrete_got_sym := g.table.final_sym(concrete_got_type)
4708 concrete_got_is_ptr := concrete_got_type.is_ptr()
4709 if expr is ast.StructInit && !got_type.is_ptr() {
4710 g.inside_cast_in_heap++
4711 got_styp := g.cc_type(concrete_got_type.ref(), true)
4712 // TODO: why does cc_type even add this in the first place?
4713 exp_styp := exp_sym.cname
4714 mut fname := 'I_${got_styp}_to_Interface_${exp_styp}'
4715 if exp_sym.info.is_generic {
4716 fname = g.generic_fn_name(exp_sym.info.concrete_types, fname)
4717 }
4718 g.call_cfn_for_casting_expr(fname, expr, expected_type, concrete_got_type,
4719 concrete_got_type, exp_styp, true, false, got_styp)
4720 g.inside_cast_in_heap--
4721 } else {
4722 got_styp := g.cc_type(concrete_got_type, true)
4723 got_is_fn := concrete_got_sym.info is ast.FnType
4724 got_is_shared := concrete_got_type.has_flag(.shared_f)
4725 exp_styp := if got_is_shared { '__shared__${exp_sym.cname}' } else { exp_sym.cname }
4726 // If it's shared, we need to use the other caster:
4727 mut fname := if got_is_shared {
4728 'I___shared__${got_styp}_to_shared_Interface_${exp_styp}'
4729 } else {
4730 'I_${got_styp}_to_Interface_${exp_styp}'
4731 }
4732 lock g.referenced_fns {
4733 g.referenced_fns[fname] = true
4734 }
4735 if exp_sym.info.is_generic {
4736 fname = g.generic_fn_name(exp_sym.info.concrete_types, fname)
4737 }
4738 // Do not allocate for `Interface(unsafe{nil})` casts
4739 {
4740 is_nil_cast := expr is ast.UnsafeExpr && expr.expr is ast.Nil
4741 if is_nil_cast {
4742 if expected_type.is_ptr() {
4743 g.write('((void*)0)')
4744 } else {
4745 // Non-pointer interface is a struct in C, use zero-init
4746 g.write('(${g.styp(expected_type)}){0}')
4747 }
4748 return
4749 }
4750 }
4751 // Auto-heap variables are already pointers in C. Treat them as
4752 // pointer-backed only when interface semantics require aliasing the
4753 // original storage. Plain value-to-interface conversions still need
4754 // a detached copy, even when the value itself lives in auto-heap storage.
4755 mut effective_got_is_ptr := concrete_got_is_ptr
4756 mut suppress_auto_heap_deref := false
4757 if !concrete_got_is_ptr && expr is ast.Ident && g.resolved_ident_is_auto_heap(expr)
4758 && (g.expected_arg_mut
4759 || g.interface_cast_requires_field_aliasing(expected_type, expr)) {
4760 effective_got_is_ptr = true
4761 suppress_auto_heap_deref = true
4762 }
4763 if suppress_auto_heap_deref {
4764 old_inside_assign_fn_var := g.inside_assign_fn_var
4765 g.inside_assign_fn_var = true
4766 g.call_cfn_for_casting_expr(fname, expr, expected_type, concrete_got_type,
4767 concrete_got_type, exp_styp, effective_got_is_ptr, got_is_fn, got_styp)
4768 g.inside_assign_fn_var = old_inside_assign_fn_var
4769 } else {
4770 g.call_cfn_for_casting_expr(fname, expr, expected_type, concrete_got_type,
4771 concrete_got_type, exp_styp, effective_got_is_ptr, got_is_fn, got_styp)
4772 }
4773 }
4774 return
4775 }
4776 if got_sym.info is ast.Interface && exp_sym.info is ast.Interface && expected_is_ptr
4777 && !got_is_ptr && !got_type.has_flag(.shared_f) && !expected_type.has_flag(.shared_f) {
4778 if expr is ast.UnsafeExpr && expr.expr is ast.Nil {
4779 g.write('((void*)0)')
4780 return
4781 }
4782 target_interface_type := expected_type.deref()
4783 g.write('HEAP(${exp_sym.cname}, ')
4784 if got_type.idx() != target_interface_type.idx() {
4785 g.gen_interface_to_interface_conversion(expr, got_type, target_interface_type)
4786 } else {
4787 g.expr(expr)
4788 }
4789 g.write(')')
4790 return
4791 }
4792 if got_sym.info is ast.Interface && exp_sym.info is ast.Interface
4793 && got_type.idx() != expected_type.idx() && !got_type.has_flag(.shared_f)
4794 && !expected_type.has_flag(.shared_f) {
4795 g.gen_interface_to_interface_conversion(expr, got_type, expected_type)
4796 return
4797 }
4798 // cast to sum type
4799 exp_styp := g.styp(expected_type)
4800 mut got_styp := g.styp(got_type)
4801 mut got_is_fn := false
4802 if got_sym.info is ast.FnType {
4803 if got_sym.info.is_anon {
4804 got_styp = 'anon_fn_${g.table.fn_type_signature(got_sym.info.func)}'
4805 }
4806 got_is_fn = true
4807 }
4808 if expected_type != ast.void_type {
4809 unwrapped_expected_type := g.unwrap_generic(expected_type)
4810 unwrapped_exp_sym := g.table.sym(unwrapped_expected_type)
4811 mut unwrapped_got_type := g.unwrap_generic(got_type)
4812 mut unwrapped_got_sym := g.table.sym(unwrapped_got_type)
4813 mut sumtype_got_type := unwrapped_got_type
4814 mut sumtype_got_sym := unwrapped_got_sym
4815 mut sumtype_got_styp := got_styp
4816 mut sumtype_got_is_fn := got_is_fn
4817 mut sumtype_got_is_ptr := got_is_ptr
4818 nil_sumtype_variant := g.single_pointer_sumtype_nil_variant(unwrapped_expected_type, expr,
4819 got_type)
4820 if nil_sumtype_variant != 0 {
4821 sumtype_got_type = nil_sumtype_variant
4822 sumtype_got_sym = g.table.sym(sumtype_got_type)
4823 sumtype_got_styp = g.styp(sumtype_got_type)
4824 sumtype_got_is_fn = sumtype_got_sym.kind == .function
4825 sumtype_got_is_ptr = sumtype_got_type.is_ptr()
4826 }
4827
4828 expected_deref_type := if expected_is_ptr {
4829 unwrapped_expected_type.deref()
4830 } else {
4831 unwrapped_expected_type
4832 }
4833 got_deref_type := if sumtype_got_is_ptr {
4834 sumtype_got_type.deref()
4835 } else {
4836 sumtype_got_type
4837 }
4838 if nil_sumtype_variant != 0
4839 || g.table.sumtype_has_variant(expected_deref_type, got_deref_type, false)
4840 || g.table.sumtype_has_variant_recursive(expected_deref_type, got_deref_type, false) {
4841 mut is_already_sum_type := false
4842 scope := g.file.scope.innermost(expr.pos().pos)
4843 if expr is ast.Ident {
4844 if v := scope.find_var(expr.name) {
4845 if v.smartcasts.len > 0
4846 && unwrapped_expected_type == g.unwrap_generic(v.orig_type) {
4847 is_already_sum_type = true
4848 }
4849 }
4850 } else if expr is ast.SelectorExpr {
4851 v := scope.find_struct_field(expr.expr.str(), expr.expr_type, expr.field_name)
4852 if v != unsafe { nil } {
4853 if v.smartcasts.len > 0
4854 && unwrapped_expected_type == g.unwrap_generic(v.orig_type) {
4855 is_already_sum_type = true
4856 }
4857 }
4858 }
4859 if is_already_sum_type {
4860 // Don't create a new sum type wrapper if there is already one
4861 g.prevent_sum_type_unwrapping_once = true
4862 g.expr(expr)
4863 } else {
4864 sumtype_got_sym_val := *sumtype_got_sym
4865 variant_typ := sumtype_got_sym_val.aggregate_variant_type(g.aggregate_type_idx)
4866 if variant_typ != 0 {
4867 sumtype_got_type = variant_typ
4868 sumtype_got_sym = g.table.sym(sumtype_got_type)
4869 sumtype_got_styp = g.styp(sumtype_got_type)
4870 sumtype_got_is_fn = sumtype_got_sym.kind == .function
4871 sumtype_got_is_ptr = sumtype_got_type.is_ptr()
4872 }
4873 actual_sumtype_got_type := sumtype_got_type
4874 sumtype_got_type = g.find_matching_sumtype_variant(unwrapped_expected_type,
4875 sumtype_got_type)
4876 sumtype_got_sym = g.table.sym(sumtype_got_type)
4877 sumtype_got_styp = g.styp(sumtype_got_type)
4878 sumtype_got_is_fn = sumtype_got_sym.kind == .function
4879 sumtype_got_is_ptr = sumtype_got_type.is_ptr()
4880
4881 fname := g.get_sumtype_casting_fn(sumtype_got_type, unwrapped_expected_type)
4882 sumtype_cast_got_is_ptr := if sumtype_got_type.is_any_kind_of_pointer()
4883 && g.is_exact_sumtype_variant_match(sumtype_got_type, actual_sumtype_got_type) {
4884 false
4885 } else {
4886 got_is_ptr
4887 }
4888 if expr is ast.ArrayInit && got_sym.kind == .array_fixed {
4889 stmt_str := g.go_before_last_stmt().trim_space()
4890 g.empty_line = true
4891 tmp_var := g.new_tmp_var()
4892 g.write('${got_styp} ${tmp_var} = ')
4893 g.expr(expr)
4894 g.writeln(';')
4895 g.write2(stmt_str, ' ')
4896 g.write('${fname}(&${tmp_var}, ${g.can_reuse_sumtype_variant_storage(unwrapped_expected_type,
4897 sumtype_cast_got_is_ptr)})')
4898 return
4899 } else {
4900 g.call_cfn_for_casting_expr(fname, expr, expected_type, sumtype_got_type,
4901 actual_sumtype_got_type, unwrapped_exp_sym.cname, sumtype_cast_got_is_ptr,
4902 sumtype_got_is_fn, sumtype_got_styp)
4903 }
4904 }
4905 return
4906 }
4907 }
4908 // Generic dereferencing logic
4909 neither_void := ast.voidptr_type !in [got_type.idx_type(), expected_type.idx_type()]
4910 && ast.nil_type !in [got_type.idx_type(), expected_type.idx_type()]
4911 if expected_type.has_flag(.shared_f) && !got_type_raw.has_flag(.shared_f)
4912 && !expected_type.has_option_or_result() {
4913 shared_styp := exp_styp[0..exp_styp.len - 1] // `shared` implies ptr, so eat one `*`
4914 if exp_sym.kind == .array {
4915 g.writeln('(${shared_styp}*)__dup_shared_array(&(${shared_styp}){.mtx = {0}, .val =')
4916 } else if exp_sym.kind == .map {
4917 g.writeln('(${shared_styp}*)__dup_shared_map(&(${shared_styp}){.mtx = {0}, .val =')
4918 } else {
4919 g.writeln('(${shared_styp}*)__dup${shared_styp}(&(${shared_styp}){.mtx = {0}, .val =')
4920 }
4921 old_is_shared := g.is_shared
4922 g.is_shared = false
4923 if got_type_raw.is_ptr() {
4924 g.write('*'.repeat(got_type_raw.nr_muls()))
4925 }
4926 g.expr(expr)
4927 g.is_shared = old_is_shared
4928 g.writeln('}, sizeof(${shared_styp}))')
4929 return
4930 } else if got_type_raw.has_flag(.shared_f) && !expected_type.has_flag(.shared_f) {
4931 if expected_is_ptr {
4932 g.write('&')
4933 }
4934 g.expr(expr)
4935 if expr !is ast.IndexExpr {
4936 g.write('->val')
4937 }
4938 return
4939 }
4940 if got_is_ptr && !expected_is_ptr && neither_void && exp_sym.kind != .placeholder
4941 && expr !is ast.InfixExpr {
4942 got_deref_type := got_type.deref()
4943 deref_sym := g.table.sym(got_deref_type)
4944 deref_will_match := expected_type in [got_type, got_deref_type, deref_sym.parent_idx]
4945 got_is_opt_or_res := got_type.has_option_or_result()
4946 if deref_will_match || got_is_opt_or_res || expr.is_auto_deref_var() {
4947 g.write('*')
4948 }
4949 }
4950 if expr is ast.Ident && g.resolved_ident_is_auto_heap(expr) && expected_is_ptr && !got_is_ptr
4951 && neither_void {
4952 old_inside_assign_fn_var := g.inside_assign_fn_var
4953 g.inside_assign_fn_var = true
4954 g.expr(expr)
4955 g.inside_assign_fn_var = old_inside_assign_fn_var
4956 return
4957 }
4958 if (g.inside_struct_init && expected_type == ast.voidptr_type && expected_type != got_type_raw
4959 && expr !is ast.StructInit) || (g.inside_call && expected_type == ast.voidptr_type
4960 && expr is ast.ArrayInit && (expr as ast.ArrayInit).is_fixed) {
4961 g.write('(voidptr)')
4962 }
4963 if expr is ast.InfixExpr && g.can_widen_small_int_infix_for_cast(expr, got_type, expected_type) {
4964 g.widen_small_int_infix_for_cast(expr, got_type, expected_type)
4965 return
4966 }
4967 // no cast
4968 g.expr(expr)
4969}
4970
4971fn write_octal_escape(mut b strings.Builder, c u8) {
4972 b << 92 // \
4973 b << 48 + (c >> 6) // oct digit 2
4974 b << 48 + (c >> 3) & 7 // oct digit 1
4975 b << 48 + c & 7 // oct digit 0
4976}
4977
4978fn cescape_nonascii(original string) string {
4979 mut b := strings.new_builder(original.len)
4980 for c in original {
4981 if c < 32 || c > 126 {
4982 // Encode with a 3 digit octal escape code, which has the
4983 // advantage to be limited/non dependent on what character
4984 // will follow next, unlike hex escapes:
4985 write_octal_escape(mut b, c)
4986 continue
4987 }
4988 b << c
4989 }
4990 res := b.str()
4991 return res
4992}
4993
4994// cestring returns a V string, properly escaped for embedding in a C string literal.
4995fn cestring(s string) string {
4996 return s.replace('\\', '\\\\').replace('"', "'")
4997}
4998
4999// ctoslit returns a '_S("${s}")' call, where s is properly escaped.
5000fn ctoslit(s string) string {
5001 return '_S(' + cescaped_string_literal(cescape_nonascii(cestring(s))) + ')'
5002}
5003
5004fn cescaped_string_literal(s string) string {
5005 if s.len <= max_c_string_literal_segment_len {
5006 return '"${s}"'
5007 }
5008 mut b := strings.new_builder(s.len + (s.len / max_c_string_literal_segment_len + 1) * 3)
5009 mut start := 0
5010 for start < s.len {
5011 end := cescaped_string_literal_segment_end(s, start, max_c_string_literal_segment_len)
5012 if start > 0 {
5013 b.write_u8(` `)
5014 }
5015 b.write_u8(`"`)
5016 b.write_string(s[start..end])
5017 b.write_u8(`"`)
5018 start = end
5019 }
5020 return b.str()
5021}
5022
5023fn cescaped_string_literal_segment_end(s string, start int, max_len int) int {
5024 limit := if start + max_len < s.len { start + max_len } else { s.len }
5025 mut i := start
5026 mut last_safe := start
5027 for i < limit {
5028 if s[i] == `\\` {
5029 escape_end := cescaped_string_escape_end(s, i)
5030 if escape_end > limit {
5031 break
5032 }
5033 i = escape_end
5034 last_safe = i
5035 continue
5036 }
5037 i++
5038 last_safe = i
5039 }
5040 if last_safe > start {
5041 return last_safe
5042 }
5043 return limit
5044}
5045
5046fn cescaped_string_escape_end(s string, start int) int {
5047 if start + 1 >= s.len {
5048 return s.len
5049 }
5050 next := s[start + 1]
5051 if next >= `0` && next <= `7` {
5052 mut end := start + 2
5053 for end < s.len && end < start + 4 && s[end] >= `0` && s[end] <= `7` {
5054 end++
5055 }
5056 return end
5057 }
5058 if next == `x` {
5059 mut end := start + 2
5060 for end < s.len && end < start + 4 && ((s[end] >= `0` && s[end] <= `9`)
5061 || (s[end] >= `a` && s[end] <= `f`) || (s[end] >= `A` && s[end] <= `F`)) {
5062 end++
5063 }
5064 return end
5065 }
5066 if next == `u` {
5067 return if start + 6 < s.len { start + 6 } else { s.len }
5068 }
5069 if next == `U` {
5070 return if start + 10 < s.len { start + 10 } else { s.len }
5071 }
5072 return start + 2
5073}
5074
5075fn (mut g Gen) gen_attrs(attrs []ast.Attr) {
5076 if g.pref.skip_unused {
5077 return
5078 }
5079 if g.inside_c_extern {
5080 return
5081 }
5082 for attr in attrs {
5083 g.writeln('// Attr: [${attr.name}]')
5084 }
5085}
5086
5087fn (g &Gen) should_skip_linux_bare_startup_stub(stmt ast.AsmStmt) bool {
5088 if !g.pref.is_bare || g.file.path == '' {
5089 return false
5090 }
5091 if !g.file.path.ends_with(os.join_path('vlib', 'builtin', 'linux_bare', 'linux_syscalls.v')) {
5092 return false
5093 }
5094 mut exports_start := false
5095 for template in stmt.templates {
5096 if !template.is_directive || template.name !in ['globl', 'global'] {
5097 continue
5098 }
5099 for arg in template.args {
5100 if arg is ast.AsmAlias && arg.name == '_start' {
5101 exports_start = true
5102 break
5103 }
5104 }
5105 if exports_start {
5106 break
5107 }
5108 }
5109 if !exports_start {
5110 return false
5111 }
5112 for _, f in g.table.fns {
5113 if f.attrs.any(it.name == 'export' && it.arg == '_start') {
5114 return true
5115 }
5116 }
5117 return false
5118}
5119
5120fn (mut g Gen) asm_stmt(stmt ast.AsmStmt) {
5121 if stmt.templates.len == 0 && stmt.input.len == 0 && stmt.output.len == 0
5122 && stmt.global_labels.len == 0 && !stmt.is_goto && !stmt.is_volatile {
5123 return
5124 }
5125 g.write('__asm__')
5126 if stmt.is_volatile {
5127 g.write(' volatile')
5128 }
5129 if stmt.is_goto {
5130 g.write(' goto')
5131 }
5132 g.writeln(' (')
5133 g.indent++
5134 if stmt.templates.len == 0 {
5135 g.writeln('""')
5136 }
5137 for template_tmp in stmt.templates {
5138 mut template := template_tmp
5139 g.write('"')
5140 if template.is_directive {
5141 g.write('.')
5142 }
5143 g.write(template.name)
5144 if template.is_label {
5145 g.write(':')
5146 } else {
5147 g.write(' ')
5148 }
5149 // swap destination and operands for att syntax, not for arm64
5150 if template.args.len != 0 && !template.is_directive
5151 && stmt.arch !in [.arm64, .s390x, .ppc64le, .loongarch64, .rv64, .rv32] {
5152 template.args.prepend(template.args.last())
5153 template.args.delete(template.args.len - 1)
5154 }
5155
5156 for i, arg in template.args {
5157 if stmt.arch == .amd64 && (template.name == 'call' || template.name[0] == `j`)
5158 && arg is ast.AsmRegister {
5159 g.write('*') // indirect branching
5160 }
5161
5162 g.asm_arg(arg, stmt)
5163 if i + 1 < template.args.len {
5164 g.write(', ')
5165 }
5166 }
5167
5168 if !template.is_label {
5169 g.write('\\n\\t')
5170 }
5171 g.writeln('"')
5172 }
5173
5174 if stmt.output.len != 0 || stmt.input.len != 0 || stmt.clobbered.len != 0 || stmt.is_goto {
5175 g.write(': ')
5176 }
5177 g.gen_asm_ios(stmt.output)
5178 if stmt.input.len != 0 || stmt.clobbered.len != 0 || stmt.is_goto {
5179 g.write(': ')
5180 }
5181 g.gen_asm_ios(stmt.input)
5182 if stmt.clobbered.len != 0 || stmt.is_goto {
5183 g.write(': ')
5184 }
5185 for i, clob in stmt.clobbered {
5186 g.write2('"', clob.reg.name)
5187 g.write('"')
5188 if i + 1 < stmt.clobbered.len {
5189 g.writeln(',')
5190 } else {
5191 g.writeln('')
5192 }
5193 }
5194 if stmt.is_goto {
5195 g.write(': ')
5196 }
5197 for i, label in stmt.global_labels {
5198 g.write(label)
5199 if i + 1 < stmt.clobbered.len {
5200 g.writeln(',')
5201 } else {
5202 g.writeln('')
5203 }
5204 }
5205 g.indent--
5206 g.writeln(');')
5207}
5208
5209fn (mut g Gen) asm_arg(arg ast.AsmArg, stmt ast.AsmStmt) {
5210 match arg {
5211 ast.AsmAlias {
5212 name := arg.name
5213 if name in stmt.local_labels || name in stmt.global_labels
5214 || name in g.file.global_labels || stmt.is_basic
5215 || (name !in stmt.input.map(it.alias) && name !in stmt.output.map(it.alias)) {
5216 asm_formatted_name := if name in stmt.global_labels { '%l[${name}]' } else { name }
5217 g.write(asm_formatted_name)
5218 } else {
5219 g.write('%[${name}]')
5220 }
5221 }
5222 ast.CharLiteral {
5223 g.write("'${arg.val}'")
5224 }
5225 ast.IntegerLiteral {
5226 if stmt.arch == .arm64 {
5227 g.write('#${arg.val}')
5228 } else if stmt.arch in [.s390x, .ppc64le, .loongarch64, .rv64, .rv32] {
5229 g.write('${arg.val}')
5230 } else {
5231 g.write('\$${arg.val}')
5232 }
5233 }
5234 ast.FloatLiteral {
5235 if g.pref.nofloat {
5236 g.write('\$${arg.val.int()}')
5237 } else {
5238 g.write('\$${arg.val}')
5239 }
5240 }
5241 ast.BoolLiteral {
5242 g.write('\$${arg.val.str()}')
5243 }
5244 ast.AsmRegister {
5245 if stmt.arch in [.rv64, .rv32] {
5246 g.write('${arg.name}')
5247 } else if stmt.arch == .loongarch64 {
5248 g.write('$${arg.name}')
5249 } else {
5250 if !stmt.is_basic {
5251 g.write('%') // escape percent with percent in extended assembly
5252 }
5253 g.write('%${arg.name}')
5254 }
5255 }
5256 ast.AsmAddressing {
5257 if arg.segment != '' {
5258 g.write('%%${arg.segment}:')
5259 }
5260 base := arg.base
5261 index := arg.index
5262 displacement := arg.displacement
5263 scale := arg.scale
5264 match arg.mode {
5265 .base {
5266 if stmt.arch == .arm64 {
5267 g.write('[')
5268 } else if stmt.arch == .loongarch64 {
5269 g.write('')
5270 } else {
5271 g.write('(')
5272 }
5273 g.asm_arg(base, stmt)
5274 if stmt.arch == .arm64 {
5275 g.write(']')
5276 } else if stmt.arch == .loongarch64 {
5277 g.write('')
5278 } else {
5279 g.write(')')
5280 }
5281 }
5282 .displacement {
5283 g.asm_arg(displacement, stmt)
5284 }
5285 .base_plus_displacement {
5286 g.asm_arg(displacement, stmt)
5287 g.write('(')
5288 g.asm_arg(base, stmt)
5289 g.write(')')
5290 }
5291 .index_times_scale_plus_displacement {
5292 if displacement is ast.AsmDisp {
5293 g.asm_arg(displacement, stmt)
5294 g.write('(, ')
5295 } else if displacement is ast.AsmRegister {
5296 g.write('(')
5297 g.asm_arg(displacement, stmt)
5298 g.write(',')
5299 } else {
5300 panic('unexpected ${displacement.type_name()}')
5301 }
5302 g.asm_arg(index, stmt)
5303 g.write(',${scale})')
5304 }
5305 .base_plus_index_plus_displacement {
5306 g.asm_arg(displacement, stmt)
5307 g.write('(')
5308 g.asm_arg(base, stmt)
5309 g.write(',')
5310 g.asm_arg(index, stmt)
5311 g.write(',1)')
5312 }
5313 .base_plus_index_times_scale_plus_displacement {
5314 g.asm_arg(displacement, stmt)
5315 g.write('(')
5316 g.asm_arg(base, stmt)
5317 g.write(',')
5318 g.asm_arg(index, stmt)
5319 g.write(',${scale})')
5320 }
5321 .rip_plus_displacement {
5322 g.asm_arg(displacement, stmt)
5323 g.write('(')
5324 g.asm_arg(base, stmt)
5325 g.write(')')
5326 }
5327 .invalid {
5328 g.error('invalid addressing mode', arg.pos)
5329 }
5330 }
5331 }
5332 ast.AsmDisp {
5333 g.write(arg.val)
5334 }
5335 string {
5336 g.write(arg)
5337 }
5338 }
5339}
5340
5341fn (mut g Gen) gen_asm_ios(ios []ast.AsmIO) {
5342 for i, io in ios {
5343 if io.alias != '' {
5344 g.write('[${io.alias}] ')
5345 }
5346 g.write('"${io.constraint}" (')
5347 g.expr(io.expr)
5348 g.write(')')
5349 if i + 1 < ios.len {
5350 g.writeln(',')
5351 } else {
5352 g.writeln('')
5353 }
5354 }
5355}
5356
5357fn cnewlines(s string) string {
5358 return s.replace('\n', r'\n')
5359}
5360
5361fn (mut g Gen) write_fn_ptr_decl(func &ast.FnType, ptr_name string) {
5362 ret_styp := g.styp(func.func.return_type)
5363 g.write('${ret_styp} (*${ptr_name}) (')
5364 arg_len := func.func.params.len
5365 for i, arg in func.func.params {
5366 g.write(g.fn_param_type_decl(arg.typ, arg.name))
5367 if i < arg_len - 1 {
5368 g.write(', ')
5369 }
5370 }
5371 g.write(')')
5372}
5373
5374fn (mut g Gen) fn_ptr_decl_str(func ast.FnType, ptr_name string) string {
5375 pos := g.out.len
5376 g.write_fn_ptr_decl(&func, ptr_name)
5377 return g.out.cut_to(pos)
5378}
5379
5380fn (mut g Gen) register_ternary_name(name string) {
5381 level_key := g.inside_ternary.str()
5382 if level_key !in g.ternary_level_names {
5383 g.ternary_level_names[level_key] = []string{}
5384 }
5385 new_name := g.new_tmp_var()
5386 g.ternary_names[name] = new_name
5387 g.ternary_level_names[level_key] << name
5388}
5389
5390fn (mut g Gen) get_ternary_name(name string) string {
5391 if g.inside_ternary == 0 {
5392 return name
5393 }
5394 if name !in g.ternary_names {
5395 return name
5396 }
5397 return g.ternary_names[name]
5398}
5399
5400fn (mut g Gen) gen_clone_assignment(var_type ast.Type, val ast.Expr, typ ast.Type, add_eq bool) bool {
5401 if val !in [ast.Ident, ast.SelectorExpr] {
5402 return false
5403 }
5404 right_sym := g.table.sym(typ)
5405 if g.is_autofree {
5406 if add_eq {
5407 g.write('=')
5408 }
5409 if right_sym.kind == .array {
5410 // `arr1 = arr2` => `arr1 = arr2.clone()`
5411 shared_styp := g.styp(typ.set_nr_muls(0))
5412 if typ.share() == .shared_t {
5413 g.write('(${shared_styp}*)__dup_shared_array(&(${shared_styp}){.mtx = {0}, .val =')
5414 }
5415 is_sumtype := g.table.type_kind(var_type) == .sum_type
5416 if is_sumtype {
5417 variant_typ := g.styp(typ).replace('*', '')
5418 fn_name := g.get_sumtype_casting_fn(typ, var_type)
5419 g.write('${fn_name}(ADDR(${variant_typ}, builtin__array_clone_static_to_depth(')
5420 if typ.is_ptr() {
5421 g.write('*')
5422 }
5423 } else {
5424 g.write(' builtin__array_clone_static_to_depth(')
5425 }
5426 g.expr(val)
5427 if typ.share() == .shared_t {
5428 g.write('->val')
5429 }
5430 elem_type := (right_sym.info as ast.Array).elem_type
5431 array_depth := g.get_array_depth(elem_type)
5432 g.write(', ${array_depth})')
5433 if typ.share() == .shared_t {
5434 g.write('}, sizeof(${shared_styp}))')
5435 }
5436 if is_sumtype {
5437 g.write('), false)')
5438 }
5439 } else if right_sym.kind == .string {
5440 // `str1 = str2` => `str1 = str2.clone()`
5441 if var_type.has_flag(.option) {
5442 g.write(' builtin__string_option_clone_static(')
5443 } else {
5444 g.write(' builtin__string_clone_static(')
5445 }
5446 g.expr(val)
5447 g.write(')')
5448 }
5449 }
5450 return true
5451}
5452
5453fn (mut g Gen) map_fn_ptrs(key_sym ast.TypeSymbol) (string, string, string, string) {
5454 mut hash_fn := ''
5455 mut key_eq_fn := ''
5456 mut clone_fn := ''
5457 mut free_fn := '&builtin__map_free_nop'
5458 match key_sym.kind {
5459 .alias {
5460 alias_key_type := (key_sym.info as ast.Alias).parent_type
5461 return g.map_fn_ptrs(g.table.sym(alias_key_type))
5462 }
5463 .u8, .i8, .char {
5464 hash_fn = '&builtin__map_hash_int_1'
5465 key_eq_fn = '&builtin__map_eq_int_1'
5466 clone_fn = '&builtin__map_clone_int_1'
5467 }
5468 .i16, .u16 {
5469 hash_fn = '&builtin__map_hash_int_2'
5470 key_eq_fn = '&builtin__map_eq_int_2'
5471 clone_fn = '&builtin__map_clone_int_2'
5472 }
5473 .enum {
5474 hash_fn = 'builtin__map_enum_fn(1,sizeof(${key_sym.cname}))'
5475 key_eq_fn = 'builtin__map_enum_fn(2,sizeof(${key_sym.cname}))'
5476 clone_fn = 'builtin__map_enum_fn(3,sizeof(${key_sym.cname}))'
5477 }
5478 .int {
5479 $if new_int ? && x64 {
5480 hash_fn = '&builtin__map_hash_int_8'
5481 key_eq_fn = '&builtin__map_eq_int_8'
5482 clone_fn = '&builtin__map_clone_int_8'
5483 } $else {
5484 hash_fn = '&builtin__map_hash_int_4'
5485 key_eq_fn = '&builtin__map_eq_int_4'
5486 clone_fn = '&builtin__map_clone_int_4'
5487 }
5488 }
5489 .i32, .u32, .rune, .f32 {
5490 hash_fn = '&builtin__map_hash_int_4'
5491 key_eq_fn = '&builtin__map_eq_int_4'
5492 clone_fn = '&builtin__map_clone_int_4'
5493 }
5494 .voidptr {
5495 ts := if g.pref.m64 {
5496 unsafe { g.table.sym_by_idx(ast.u64_type_idx) }
5497 } else {
5498 unsafe { g.table.sym_by_idx(ast.u32_type_idx) }
5499 }
5500 return g.map_fn_ptrs(ts)
5501 }
5502 .u64, .i64, .f64 {
5503 hash_fn = '&builtin__map_hash_int_8'
5504 key_eq_fn = '&builtin__map_eq_int_8'
5505 clone_fn = '&builtin__map_clone_int_8'
5506 }
5507 .string {
5508 hash_fn = '&builtin__map_hash_string'
5509 key_eq_fn = '&builtin__map_eq_string'
5510 clone_fn = '&builtin__map_clone_string'
5511 free_fn = '&builtin__map_free_string'
5512 }
5513 .array_fixed {
5514 key_type := ast.new_type(key_sym.idx)
5515 return g.fixed_array_map_fn_ptrs(key_type)
5516 }
5517 else {
5518 verror('map key type `${key_sym.name}` not supported')
5519 }
5520 }
5521
5522 return hash_fn, key_eq_fn, clone_fn, free_fn
5523}
5524
5525@[inline]
5526fn (mut g Gen) map_key_fn_key(typ ast.Type) ast.Type {
5527 return g.table.fully_unaliased_type(g.unwrap_generic(typ).set_nr_muls(0))
5528}
5529
5530@[inline]
5531fn (mut g Gen) fixed_array_map_fn_base(typ ast.Type) string {
5532 return '${g.styp(g.map_key_fn_key(typ))}_map_key'
5533}
5534
5535fn (mut g Gen) fixed_array_map_fn_ptrs(key_type ast.Type) (string, string, string, string) {
5536 key := g.map_key_fn_key(key_type)
5537 g.needed_map_key_fns << key
5538 base := g.fixed_array_map_fn_base(key)
5539 return '&${base}_hash', '&${base}_eq', '&${base}_clone', '&${base}_free'
5540}
5541
5542fn (mut g Gen) gen_map_key_fns() {
5543 for needed_typ in g.needed_map_key_fns {
5544 key := g.map_key_fn_key(needed_typ)
5545 if g.generated_map_key_fns[key] {
5546 continue
5547 }
5548 sym := g.table.sym(key)
5549 match sym.kind {
5550 .array_fixed {
5551 g.gen_fixed_array_map_key_fns(key)
5552 }
5553 else {
5554 verror('could not generate map key functions for type `${sym.name}`')
5555 }
5556 }
5557 }
5558}
5559
5560fn (mut g Gen) gen_fixed_array_map_key_fns(key_type ast.Type) {
5561 key := g.map_key_fn_key(key_type)
5562 if g.generated_map_key_fns[key] {
5563 return
5564 }
5565 g.generated_map_key_fns[key] = true
5566 key_sym := g.table.sym(key)
5567 if key_sym.kind != .array_fixed {
5568 verror('map key type `${key_sym.name}` is not a fixed array')
5569 }
5570 key_info := key_sym.info as ast.ArrayFixed
5571 key_styp := g.styp(key)
5572 base := g.fixed_array_map_fn_base(key)
5573 hash_fn := '${base}_hash'
5574 eq_fn := '${base}_eq'
5575 clone_fn := '${base}_clone'
5576 free_fn := '${base}_free'
5577 array_eq_fn := g.gen_fixed_array_equality_fn(key)
5578 g.definitions.writeln('${g.static_non_parallel}u64 ${hash_fn}(voidptr pkey);')
5579 g.definitions.writeln('${g.static_non_parallel}bool ${eq_fn}(voidptr a, voidptr b);')
5580 g.definitions.writeln('${g.static_non_parallel}void ${clone_fn}(voidptr dest, voidptr pkey);')
5581 g.definitions.writeln('${g.static_non_parallel}void ${free_fn}(voidptr pkey);')
5582
5583 mut hash_builder := strings.new_builder(256)
5584 hash_builder.writeln('${g.static_non_parallel}u64 ${hash_fn}(voidptr pkey) {')
5585 hash_builder.writeln('\t${key_styp}* key = (${key_styp}*)pkey;')
5586 hash_builder.writeln('\tu64 hash = 0;')
5587 hash_builder.writeln('\tfor (${ast.int_type_name} i = 0; i < ${key_info.size}; ++i) {')
5588 hash_builder.writeln('\t\thash = wyhash64(hash, ${g.fixed_array_map_key_hash_expr(key_info.elem_type,
5589 '(*key)[i]')});')
5590 hash_builder.writeln('\t}')
5591 hash_builder.writeln('\treturn hash;')
5592 hash_builder.writeln('}')
5593 g.auto_fn_definitions << hash_builder.str()
5594
5595 mut eq_builder := strings.new_builder(128)
5596 eq_builder.writeln('${g.static_non_parallel}bool ${eq_fn}(voidptr a, voidptr b) {')
5597 eq_builder.writeln('\treturn ${array_eq_fn}_arr_eq(*(${key_styp}*)a, *(${key_styp}*)b);')
5598 eq_builder.writeln('}')
5599 g.auto_fn_definitions << eq_builder.str()
5600
5601 mut clone_builder := strings.new_builder(256)
5602 clone_builder.writeln('${g.static_non_parallel}void ${clone_fn}(voidptr dest, voidptr pkey) {')
5603 clone_builder.writeln('\t${key_styp}* dest_key = (${key_styp}*)dest;')
5604 clone_builder.writeln('\t${key_styp}* src_key = (${key_styp}*)pkey;')
5605 clone_builder.writeln('\tfor (${ast.int_type_name} i = 0; i < ${key_info.size}; ++i) {')
5606 g.fixed_array_map_key_clone_stmt(mut clone_builder, key_info.elem_type, '(*dest_key)[i]',
5607 '(*src_key)[i]')
5608 clone_builder.writeln('\t}')
5609 clone_builder.writeln('}')
5610 g.auto_fn_definitions << clone_builder.str()
5611
5612 mut free_builder := strings.new_builder(256)
5613 free_builder.writeln('${g.static_non_parallel}void ${free_fn}(voidptr pkey) {')
5614 free_builder.writeln('\t${key_styp}* key = (${key_styp}*)pkey;')
5615 free_builder.writeln('\tfor (${ast.int_type_name} i = 0; i < ${key_info.size}; ++i) {')
5616 g.fixed_array_map_key_free_stmt(mut free_builder, key_info.elem_type, '(*key)[i]')
5617 free_builder.writeln('\t}')
5618 free_builder.writeln('}')
5619 g.auto_fn_definitions << free_builder.str()
5620}
5621
5622fn (mut g Gen) fixed_array_map_key_hash_expr(elem_type ast.Type, expr string) string {
5623 elem := g.map_key_fn_key(elem_type)
5624 elem_sym := g.table.final_sym(elem)
5625 match elem_sym.kind {
5626 .string {
5627 return 'builtin__map_hash_string(&${expr})'
5628 }
5629 .array_fixed {
5630 g.gen_fixed_array_map_key_fns(elem)
5631 return '${g.fixed_array_map_fn_base(elem)}_hash(&${expr})'
5632 }
5633 else {
5634 return 'wyhash((const void*)(&${expr}), sizeof(${g.styp(elem)}), 0, _wyp)'
5635 }
5636 }
5637}
5638
5639fn (mut g Gen) fixed_array_map_key_clone_stmt(mut sb strings.Builder, elem_type ast.Type, dest string,
5640 src string) {
5641 elem := g.map_key_fn_key(elem_type)
5642 elem_sym := g.table.final_sym(elem)
5643 match elem_sym.kind {
5644 .string {
5645 sb.writeln('\t\tbuiltin__map_clone_string(&${dest}, &${src});')
5646 }
5647 .array_fixed {
5648 g.gen_fixed_array_map_key_fns(elem)
5649 sb.writeln('\t\t${g.fixed_array_map_fn_base(elem)}_clone(&${dest}, &${src});')
5650 }
5651 else {
5652 sb.writeln('\t\tmemcpy(&${dest}, &${src}, sizeof(${g.styp(elem)}));')
5653 }
5654 }
5655}
5656
5657fn (mut g Gen) fixed_array_map_key_free_stmt(mut sb strings.Builder, elem_type ast.Type, expr string) {
5658 elem := g.map_key_fn_key(elem_type)
5659 elem_sym := g.table.final_sym(elem)
5660 match elem_sym.kind {
5661 .string {
5662 sb.writeln('\t\tbuiltin__map_free_string(&${expr});')
5663 }
5664 .array_fixed {
5665 g.gen_fixed_array_map_key_fns(elem)
5666 sb.writeln('\t\t${g.fixed_array_map_fn_base(elem)}_free(&${expr});')
5667 }
5668 else {}
5669 }
5670}
5671
5672fn (mut g Gen) write_map_key_arg(expr ast.Expr, key_type ast.Type) {
5673 key_styp := g.styp(key_type)
5674 if g.table.final_sym(key_type).kind == .array_fixed && expr is ast.Ident {
5675 g.expr(expr)
5676 return
5677 }
5678 g.write('&(${key_styp}[]){')
5679 g.expr(expr)
5680 g.write('}')
5681}
5682
5683fn (mut g Gen) write_atomic_postfix_target(node ast.PostfixExpr) {
5684 if node.expr.is_auto_deref_var() {
5685 g.write('*')
5686 }
5687 g.expr(node.expr)
5688 if node.typ.has_flag(.shared_f) {
5689 g.write('->val')
5690 }
5691}
5692
5693fn (g &Gen) is_windows_tcc() bool {
5694 return g.pref.os == .windows
5695 && (g.pref.ccompiler_type == .tinyc || pref.cc_from_string(g.pref.ccompiler) == .tinyc)
5696}
5697
5698fn (mut g Gen) write_atomic_postfix_expr(node ast.PostfixExpr) {
5699 atomic_value_styp := g.styp(node.typ.clear_flag(.atomic_f))
5700 if g.is_windows_tcc() {
5701 g.write_windows_tcc_atomic_postfix_expr(node, atomic_value_styp)
5702 return
5703 }
5704 atomic_op := if node.op == .inc {
5705 '__atomic_fetch_add'
5706 } else {
5707 '__atomic_fetch_sub'
5708 }
5709 g.write('${atomic_op}((${atomic_value_styp}*)&(')
5710 g.write_atomic_postfix_target(node)
5711 g.write('), 1, 5)')
5712}
5713
5714fn (mut g Gen) write_windows_tcc_atomic_postfix_expr(node ast.PostfixExpr, atomic_value_styp string) {
5715 if !g.wrote_windows_tcc_atomic_include {
5716 atomic_h_path :=
5717 os.join_path(@VEXEROOT, 'thirdparty', 'stdatomic', 'win', 'atomic.h').replace('\\', '/')
5718 g.includes.writeln('#include "${atomic_h_path}"')
5719 g.wrote_windows_tcc_atomic_include = true
5720 }
5721 base_typ := g.table.unaliased_type(g.unwrap_generic(node.typ.clear_flags(.shared_f, .atomic_f)))
5722 mut atomic_fn := 'InterlockedExchangeAdd'
5723 mut atomic_ptr_styp := 'LONG'
5724 match base_typ.idx() {
5725 ast.i8_type_idx, ast.u8_type_idx, ast.char_type_idx {
5726 atomic_fn = 'InterlockedExchangeAdd8'
5727 atomic_ptr_styp = 'char'
5728 }
5729 ast.i16_type_idx, ast.u16_type_idx {
5730 atomic_fn = 'InterlockedExchangeAdd16'
5731 atomic_ptr_styp = 'short'
5732 }
5733 ast.i64_type_idx, ast.u64_type_idx {
5734 atomic_fn = 'InterlockedExchangeAdd64'
5735 atomic_ptr_styp = 'LONG64'
5736 }
5737 ast.isize_type_idx, ast.usize_type_idx {
5738 if g.pref.arch != .i386 {
5739 atomic_fn = 'InterlockedExchangeAdd64'
5740 atomic_ptr_styp = 'LONG64'
5741 }
5742 }
5743 else {}
5744 }
5745
5746 delta := if node.op == .inc { '1' } else { '-1' }
5747 g.write('((${atomic_value_styp})${atomic_fn}((${atomic_ptr_styp} volatile*)&(')
5748 g.write_atomic_postfix_target(node)
5749 g.write('), ${delta}))')
5750}
5751
5752fn (mut g Gen) expr(node_ ast.Expr) {
5753 old_discard_or_result := g.discard_or_result
5754 old_is_void_expr_stmt := g.is_void_expr_stmt
5755 if g.is_void_expr_stmt {
5756 g.discard_or_result = true
5757 g.is_void_expr_stmt = false
5758 } else {
5759 g.discard_or_result = false
5760 }
5761 // Note: please keep the type names in the match here in alphabetical order:
5762 mut node := unsafe { node_ }
5763 match mut node {
5764 ast.ComptimeType {
5765 g.error('g.expr(): Unhandled ComptimeType', node.pos)
5766 }
5767 ast.EmptyExpr {
5768 g.error('g.expr(): unhandled EmptyExpr', token.Pos{})
5769 }
5770 ast.AnonFn {
5771 save_cur_concrete_types := g.cur_concrete_types
5772 call_concrete := g.active_call_generic_concrete_types(node.decl.generic_names)
5773 if call_concrete.len > 0 && !call_concrete.any(it.has_flag(.generic)
5774 || g.type_has_unresolved_generic_parts(it)) {
5775 g.cur_concrete_types = call_concrete
5776 g.clear_type_resolution_caches()
5777 }
5778 g.gen_anon_fn(mut node)
5779 g.cur_concrete_types = save_cur_concrete_types
5780 g.clear_type_resolution_caches()
5781 }
5782 ast.ArrayDecompose {
5783 g.expr(node.expr)
5784 }
5785 ast.ArrayInit {
5786 g.array_init(node, '')
5787 }
5788 ast.AsCast {
5789 g.as_cast(node)
5790 }
5791 ast.Assoc {
5792 g.assoc(node)
5793 }
5794 ast.AtExpr {
5795 g.comptime_at(node)
5796 }
5797 ast.BoolLiteral {
5798 g.write(node.val.str())
5799 }
5800 ast.CallExpr {
5801 ret_type := if node.or_block.kind == .absent {
5802 node.return_type
5803 } else {
5804 node.return_type.clear_option_and_result()
5805 }
5806 mut shared_styp := ''
5807 ret_typ_is_shared := ret_type.has_flag(.shared_f)
5808 if g.is_shared && !ret_typ_is_shared && !g.inside_or_block {
5809 ret_sym := g.table.sym(ret_type)
5810 shared_typ := if ret_type.is_ptr() {
5811 ret_type.deref().set_flag(.shared_f)
5812 } else {
5813 ret_type.set_flag(.shared_f)
5814 }
5815 shared_styp = g.styp(shared_typ)
5816 if ret_sym.kind == .array {
5817 g.writeln('(${shared_styp}*)__dup_shared_array(&(${shared_styp}){.mtx = {0}, .val =')
5818 } else if ret_sym.kind == .map {
5819 g.writeln('(${shared_styp}*)__dup_shared_map(&(${shared_styp}){.mtx = {0}, .val =')
5820 } else {
5821 g.writeln('(${shared_styp}*)__dup${shared_styp}(&(${shared_styp}){.mtx = {0}, .val =')
5822 }
5823 }
5824 stmt_before_call_expr_pos := if g.stmt_path_pos.len > 0 {
5825 g.stmt_path_pos.last()
5826 } else {
5827 0
5828 }
5829
5830 if g.is_shared && !ret_typ_is_shared && !g.inside_or_block && ret_type.is_ptr() {
5831 g.write('*'.repeat(ret_type.nr_muls()))
5832 }
5833 g.call_expr(node)
5834 if g.is_autofree && !g.is_builtin_mod && !g.is_js_call && g.strs_to_free0.len == 0
5835 && !g.is_autofree_tmp && !g.inside_lambda && g.inside_ternary == 0 {
5836 // if len != 0, that means we are handling call expr inside call expr (arg)
5837 // and it'll get messed up here, since it's handled recursively in autofree_call_pregen()
5838 // so just skip it
5839 g.autofree_call_pregen(node)
5840 if g.strs_to_free0.len > 0 {
5841 g.insert_at(stmt_before_call_expr_pos, g.strs_to_free0.join('\n'))
5842 }
5843 g.strs_to_free0 = []
5844 }
5845 if g.is_shared && !ret_typ_is_shared && !g.inside_or_block {
5846 g.writeln('}, sizeof(${shared_styp}))')
5847 }
5848 /*
5849 if g.autofree {
5850 s := g.autofree_pregen[node.pos.pos.str()]
5851 if s != '' {
5852 // g.insert_before_stmt('/*START2*/' + g.strs_to_free0.join('\n') + '/*END*/')
5853 // g.insert_before_stmt('/*START3*/' + node.autofree_pregen + '/*END*/')
5854 g.insert_before_stmt('/*START3*/' + s + '/*END*/')
5855 // for s in g.strs_to_free0 {
5856 }
5857 // //g.writeln(s)
5858 // }
5859 g.strs_to_free0 = []
5860 }
5861 */
5862 }
5863 ast.CastExpr {
5864 g.cast_expr(node)
5865 }
5866 ast.ChanInit {
5867 resolved_elem_type := g.unwrap_generic(g.recheck_concrete_type(node.elem_type))
5868 mut muttable := unsafe { &ast.Table(g.table) }
5869 chan_type := ast.new_type(muttable.find_or_register_chan(resolved_elem_type,
5870 resolved_elem_type.nr_muls() > 0))
5871 g.note_chan_type_definition(chan_type)
5872 elem_typ_str := g.styp(resolved_elem_type)
5873 noscan := g.check_noscan(resolved_elem_type)
5874 g.write('sync__new_channel_st${noscan}(')
5875 if node.has_cap {
5876 g.expr(node.cap_expr)
5877 } else {
5878 g.write('0')
5879 }
5880 g.write2(', sizeof(', elem_typ_str)
5881 g.write2(')>0 ? sizeof(', elem_typ_str)
5882 g.write(') : 1)')
5883 }
5884 ast.CharLiteral {
5885 g.char_literal(node)
5886 }
5887 ast.Comment {}
5888 ast.ComptimeCall {
5889 g.comptime_call(mut node)
5890 }
5891 ast.ComptimeSelector {
5892 g.comptime_selector(node)
5893 }
5894 ast.ConcatExpr {
5895 g.concat_expr(node)
5896 }
5897 ast.CTempVar {
5898 g.write(node.name)
5899 if node.is_fixed_ret {
5900 g.write('.ret_arr')
5901 }
5902 }
5903 ast.DumpExpr {
5904 g.dump_expr(node)
5905 }
5906 ast.EnumVal {
5907 g.enum_val(node)
5908 }
5909 ast.FloatLiteral {
5910 if g.pref.nofloat {
5911 g.write(node.val.int().str())
5912 } else {
5913 g.write(node.val)
5914 }
5915 }
5916 ast.SpawnExpr {
5917 old_is_arraymap_set := g.is_arraymap_set
5918 g.is_arraymap_set = false
5919 g.spawn_and_go_expr(node, .spawn_)
5920 g.is_arraymap_set = old_is_arraymap_set
5921 }
5922 ast.GoExpr {
5923 // XTODO this results in a cgen bug, order of fields is broken
5924 // g.spawn_and_go_expr(ast.SpawnExpr{node.pos, node.call_expr, node.is_expr},
5925 old_is_arraymap_set := g.is_arraymap_set
5926 g.is_arraymap_set = false
5927 g.spawn_and_go_expr(ast.SpawnExpr{
5928 pos: node.pos
5929 call_expr: node.call_expr
5930 is_expr: node.is_expr
5931 }, .go_)
5932 g.is_arraymap_set = old_is_arraymap_set
5933 }
5934 ast.Ident {
5935 g.ident(node)
5936 }
5937 ast.IfExpr {
5938 if !node.is_comptime {
5939 g.if_expr(node)
5940 } else {
5941 g.comptime_if(node)
5942 }
5943 }
5944 ast.IfGuardExpr {
5945 g.write('/* guard */')
5946 }
5947 ast.IndexExpr {
5948 g.index_expr(node)
5949 }
5950 ast.InfixExpr {
5951 if node.op !in [.left_shift, .plus_assign, .minus_assign] {
5952 g.infix_expr(node)
5953 } else {
5954 g.inside_map_infix = true
5955 g.infix_expr(node)
5956 g.inside_map_infix = false
5957 }
5958 }
5959 ast.IntegerLiteral {
5960 if node.val.starts_with('0o') {
5961 g.write2('0', node.val[2..])
5962 } else if node.val.starts_with('-0o') {
5963 g.write2('-0', node.val[3..])
5964 } else {
5965 g.write(node.val)
5966 val_type := determine_integer_literal_type(node)
5967 if val_type in [ast.u32_type, ast.u64_type] {
5968 g.write('U')
5969 }
5970 if val_type in [ast.i64_type, ast.u64_type] {
5971 g.write('LL')
5972 }
5973 }
5974 }
5975 ast.IsRefType {
5976 typ := g.type_resolver.typeof_type(node.expr, g.get_type(node.typ))
5977 node_typ := g.unwrap_generic(typ)
5978 sym := g.table.sym(node_typ)
5979 if sym.language == .v && sym.kind in [.placeholder, .any] {
5980 g.error('unknown type `${sym.name}`', node.pos)
5981 }
5982 is_ref_type := g.contains_ptr(node_typ)
5983 g.write('/*IsRefType*/ ${is_ref_type}')
5984 }
5985 ast.LambdaExpr {
5986 if node.call_ctx != unsafe { nil } {
5987 save_cur_concrete_types := g.cur_concrete_types
5988 call_concrete := g.active_call_lambda_concrete_types(node)
5989 // Only override cur_concrete_types if the call context has
5990 // fully resolved (non-generic) types. When inside a generic
5991 // function instantiation, g.cur_concrete_types already has
5992 // the correct concrete types from gen_fn_decl. The call_ctx
5993 // may still have unresolved generic placeholders from the
5994 // checker's first pass.
5995 if call_concrete.len > 0 && !call_concrete.any(it.has_flag(.generic)
5996 || g.type_has_unresolved_generic_parts(it)) {
5997 g.cur_concrete_types = call_concrete
5998 g.clear_type_resolution_caches()
5999 }
6000 g.gen_anon_fn(mut node.func)
6001 g.cur_concrete_types = save_cur_concrete_types
6002 g.clear_type_resolution_caches()
6003 } else {
6004 g.gen_anon_fn(mut node.func)
6005 }
6006 }
6007 ast.Likely {
6008 if node.is_likely {
6009 g.write('_likely_')
6010 } else {
6011 g.write('_unlikely_')
6012 }
6013 g.write('(')
6014 g.expr(node.expr)
6015 g.write(')')
6016 }
6017 ast.LockExpr {
6018 g.lock_expr(node)
6019 }
6020 ast.MapInit {
6021 g.map_init(node)
6022 }
6023 ast.MatchExpr {
6024 if node.is_comptime {
6025 g.comptime_match(node)
6026 } else {
6027 g.match_expr(node)
6028 }
6029 }
6030 ast.NodeError {}
6031 ast.Nil {
6032 g.write('((void*)0)')
6033 }
6034 ast.None {
6035 g.write('_const_none__')
6036 }
6037 ast.OffsetOf {
6038 styp := g.styp(node.struct_type)
6039 g.write('/*OffsetOf*/ (u32)(__offsetof(${util.no_dots(styp)}, ${node.field}))')
6040 }
6041 ast.OrExpr {
6042 // this should never appear here
6043 }
6044 ast.ParExpr {
6045 g.write('(')
6046 if node.expr is ast.ArrayInit && node.expr.is_fixed && !g.inside_assign {
6047 array_init := node.expr as ast.ArrayInit
6048 g.fixed_array_init_with_cast(array_init, array_init.typ)
6049 } else {
6050 g.expr(node.expr)
6051 }
6052 g.write(')')
6053 }
6054 ast.PostfixExpr {
6055 if node.auto_locked != '' {
6056 g.writeln('sync__RwMutex_lock(&${node.auto_locked}->mtx);')
6057 }
6058 is_safe_inc := g.do_int_overflow_checks && node.op == .inc
6059 is_safe_dec := g.do_int_overflow_checks && node.op == .dec
6060 is_atomic_postfix := node.typ.has_flag(.atomic_f) && node.op in [.inc, .dec]
6061 && g.pref.ccompiler_type != .msvc
6062 g.inside_map_postfix = true
6063 if is_atomic_postfix {
6064 g.write_atomic_postfix_expr(node)
6065 } else if node.is_c2v_prefix {
6066 g.write(node.op.str())
6067 }
6068 if is_atomic_postfix {
6069 // already emitted above
6070 } else if node.expr.is_auto_deref_var() {
6071 g.write('(*')
6072 g.expr(node.expr)
6073 g.write(')')
6074 } else if node.op == .question {
6075 mut expr_type := ast.void_type
6076 if mut node.expr is ast.ComptimeSelector {
6077 if node.expr.field_expr is ast.SelectorExpr
6078 && g.comptime.is_comptime_selector_field_name(node.expr.field_expr, 'name') {
6079 expr_type = g.unwrap_generic(g.comptime.comptime_for_field_type)
6080 }
6081 } else if mut node.expr is ast.Ident && node.expr.ct_expr {
6082 expr_type = g.get_comptime_for_var_type(node.expr, g.type_resolver.get_type_or_default(ast.Expr(node.expr),
6083 ast.void_type))
6084 }
6085 if expr_type != ast.void_type {
6086 if !expr_type.has_flag(.option) {
6087 g.error('cannot use `?` on non-option expression', node.pos)
6088 }
6089 }
6090 cur_line := g.go_before_last_stmt().trim_space()
6091 mut expr_str := ''
6092 mut is_unwrapped := true
6093 mut dot_or_ptr := '.'
6094 if mut node.expr is ast.ComptimeSelector && node.expr.left is ast.Ident {
6095 // val.$(field.name)?
6096 expr_str = g.gen_comptime_selector(node.expr)
6097 } else if mut node.expr is ast.Ident && node.expr.ct_expr {
6098 // val?
6099 expr_str = node.expr.name
6100 is_unwrapped = !g.inside_assign
6101 dot_or_ptr = if !(node.expr.obj is ast.Var && node.expr.obj.is_auto_deref) {
6102 '.'
6103 } else {
6104 '->'
6105 }
6106 }
6107 g.writeln('if (${expr_str}${dot_or_ptr}state != 0) {')
6108 g.writeln2('\tbuiltin__panic_option_not_set(_S("none"));', '}')
6109 g.write(cur_line)
6110 if is_unwrapped {
6111 typ := g.type_resolver.typeof_type(node.expr, node.typ)
6112 g.write('*(${g.base_type(typ)}*)&')
6113 g.expr(node.expr)
6114 g.write('.data')
6115 } else {
6116 g.expr(node.expr)
6117 }
6118 } else {
6119 g.expr(node.expr)
6120 if node.typ.has_flag(.shared_f) {
6121 g.write('->val')
6122 }
6123 }
6124 g.inside_map_postfix = false
6125 if is_atomic_postfix {
6126 // already emitted above
6127 } else if !node.is_c2v_prefix && node.op != .question && !is_safe_inc && !is_safe_dec {
6128 g.write(node.op.str())
6129 } else if is_safe_inc || is_safe_dec {
6130 overflow_styp := g.styp(get_overflow_fn_type(node.typ))
6131 vsafe_fn_name := if is_safe_inc {
6132 'builtin__overflow__add_${overflow_styp}'
6133 } else {
6134 'builtin__overflow__sub_${overflow_styp}'
6135 }
6136 g.write('=${vsafe_fn_name}(')
6137 g.expr(node.expr)
6138 if node.typ.has_flag(.shared_f) {
6139 g.write('->val')
6140 }
6141 g.write(',1)')
6142 }
6143 if node.auto_locked != '' {
6144 g.writeln(';')
6145 g.write('sync__RwMutex_unlock(&${node.auto_locked}->mtx)')
6146 }
6147 }
6148 ast.PrefixExpr {
6149 gen_or := node.op == .arrow && (node.or_block.kind != .absent || node.is_option)
6150 if node.op == .amp {
6151 g.is_amp = true
6152 }
6153 if node.op == .arrow {
6154 mut right_type := g.resolved_expr_type(node.right, node.right_type)
6155 if right_type == 0 {
6156 right_type = node.right_type
6157 }
6158 right_type = g.unwrap_generic(g.recheck_concrete_type(right_type))
6159 mut styp := g.styp(right_type)
6160 mut right_sym := g.table.sym(right_type)
6161 if right_sym.info !is ast.Chan {
6162 right_type = node.right_type
6163 styp = g.styp(right_type)
6164 right_sym = g.table.sym(right_type)
6165 }
6166 g.note_chan_type_definition(right_type)
6167 mut right_inf := right_sym.info as ast.Chan
6168 elem_type := g.unwrap_generic(g.recheck_concrete_type(right_inf.elem_type))
6169 if g.pref.skip_unused {
6170 mut muttable := unsafe { &ast.Table(g.table) }
6171 muttable.used_features.used_syms[right_type.idx()] = true
6172 muttable.used_features.used_syms[elem_type.idx()] = true
6173 }
6174 is_gen_or_and_assign_rhs := gen_or && !g.discard_or_result
6175 cur_line := if is_gen_or_and_assign_rhs {
6176 line := g.go_before_last_stmt()
6177 g.out.write_string(util.tabs(g.indent))
6178 line
6179 } else {
6180 ''
6181 }
6182 tmp_opt := if gen_or { g.new_tmp_var() } else { '' }
6183 if gen_or {
6184 opt_elem_type := g.styp(elem_type.set_flag(.option))
6185 g.register_chan_pop_option_call(opt_elem_type, styp)
6186 g.write('${opt_elem_type} ${tmp_opt} = __Option_${styp}_popval(')
6187 } else {
6188 g.write('__${styp}_popval(')
6189 }
6190 g.expr(node.right)
6191 g.write(')')
6192 if gen_or {
6193 if !node.is_option {
6194 g.or_block(tmp_opt, node.or_block, elem_type)
6195 }
6196 if is_gen_or_and_assign_rhs {
6197 elem_styp := g.styp(elem_type)
6198 g.write(';\n${cur_line}*(${elem_styp}*)${tmp_opt}.data')
6199 }
6200 }
6201 } else {
6202 if g.is_option_auto_heap {
6203 g.write('(${g.base_type(node.right_type)}*)')
6204 }
6205 mut tmp_var := ''
6206 if node.op == .amp {
6207 if node.right is ast.ParExpr && node.right.expr is ast.AsCast
6208 && (node.right.expr as ast.AsCast).expr is ast.CallExpr {
6209 str := g.go_before_last_stmt()
6210 g.empty_line = true
6211 typ := g.styp(((node.right as ast.ParExpr).expr as ast.AsCast).typ)
6212 tmp_var = g.new_tmp_var()
6213 g.writeln('${typ} ${tmp_var};')
6214 mut stmts := []ast.Stmt{cap: 1}
6215 stmts << ast.ExprStmt{
6216 pos: node.pos
6217 expr: node.right
6218 }
6219 g.stmts_with_tmp_var(stmts, tmp_var)
6220 g.set_current_pos_as_last_stmt_pos()
6221 g.write(str)
6222 }
6223 }
6224 mut has_slice_call := false
6225 // When taking the address of an auto-deref variable (e.g. `&receiver`
6226 // where the receiver is already a pointer in C), the `&` and the
6227 // implicit dereference cancel out, so emit neither.
6228 is_amp_auto_deref := node.op == .amp && node.right.is_auto_deref_var()
6229 if !g.is_option_auto_heap {
6230 has_slice_call = node.op == .amp && node.right is ast.IndexExpr
6231 && node.right.index is ast.RangeExpr
6232 if has_slice_call {
6233 g.write('ADDR(${g.styp(node.right_type)}, ')
6234 } else if !is_amp_auto_deref {
6235 g.write(node.op.str())
6236 }
6237 }
6238 // For `*val` where val is a mut generic param with pointer type
6239 // (e.g. `mut val T` where T=&int -> C: `int** val`), auto-deref needs
6240 // an extra `*` so `*val` in V becomes `**val` in C.
6241 if node.op == .mul && node.right is ast.Ident {
6242 ident_right := node.right as ast.Ident
6243 if ident_right.obj is ast.Var && ident_right.obj.is_auto_deref
6244 && ident_right.obj.is_arg && ident_right.obj.generic_typ != 0 {
6245 // generic_typ stores the mut-lowered surface type (`&T`) for mut
6246 // generic params, but we only need the underlying `T` here.
6247 resolved_generic := g.unwrap_generic(ident_right.obj.generic_typ.deref())
6248 if resolved_generic.is_ptr() {
6249 g.write('*')
6250 }
6251 }
6252 }
6253 if node.right.is_auto_deref_var() && node.op !in [.amp, .mul, .arrow] {
6254 g.write('*')
6255 }
6256 // Keep nested unary +/- parenthesized so C does not collapse them into ++/--
6257 // when the parser has preserved a nested prefix node or folded a signed literal.
6258 needs_nested_prefix_parens := prefix_expr_operand_needs_parens(node)
6259 if needs_nested_prefix_parens {
6260 g.write('(')
6261 }
6262 if tmp_var == '' {
6263 g.expr(node.right)
6264 } else {
6265 g.write(tmp_var)
6266 }
6267 if needs_nested_prefix_parens {
6268 g.write(')')
6269 }
6270 if has_slice_call {
6271 g.write(')')
6272 }
6273 if g.is_option_auto_heap {
6274 g.write('.data')
6275 }
6276 }
6277 g.is_amp = false
6278 }
6279 ast.RangeExpr {
6280 // Only used in IndexExpr and InfixExpr
6281 }
6282 ast.SelectExpr {
6283 g.select_expr(node)
6284 }
6285 ast.SelectorExpr {
6286 g.selector_expr(node)
6287 }
6288 ast.SizeOf {
6289 g.size_of(node)
6290 }
6291 ast.SqlExpr {
6292 if node.is_insert {
6293 g.sql_insert_expr(node)
6294 } else {
6295 g.sql_select_expr(node)
6296 }
6297 }
6298 ast.SqlQueryDataExpr {
6299 left := g.go_before_last_stmt()
6300 query_var := g.emit_sql_query_data(node, false)
6301 g.write2(left, query_var)
6302 }
6303 ast.StringLiteral {
6304 g.string_literal(node)
6305 }
6306 ast.StringInterLiteral {
6307 g.string_inter_literal(node)
6308 }
6309 ast.StructInit {
6310 if node.unresolved {
6311 g.expr(g.table.resolve_init(node, g.unwrap_generic(node.typ)))
6312 } else {
6313 // `user := User{name: 'Bob'}`
6314 g.inside_struct_init = true
6315 g.cur_struct_init_typ = node.typ
6316 g.struct_init(node)
6317 g.cur_struct_init_typ = 0
6318 g.inside_struct_init = false
6319 }
6320 }
6321 ast.TypeNode {
6322 // match sum Type
6323 typ := g.unwrap_generic(node.typ)
6324 sym := g.table.sym(typ)
6325 sidx := g.type_sidx(typ)
6326 g.write('${sidx}')
6327 if !g.pref.is_prod {
6328 g.write(' /* ${sym.name} */')
6329 }
6330 }
6331 ast.TypeOf {
6332 g.typeof_expr(node)
6333 }
6334 ast.UnsafeExpr {
6335 g.unsafe_level++
6336 g.expr(node.expr)
6337 g.unsafe_level--
6338 }
6339 }
6340
6341 g.discard_or_result = old_discard_or_result
6342 g.is_void_e