@@ -99,7 +99,11 @@ fn C.fputs(msg &char, fstream &C.FILE) i32
9999fn C.fflush(fstream &C.FILE) i32
100100
101101// TODO: define args in these functions
102-fn C.fseek(stream &C.FILE, offset i32, whence i32) i32
102+$if windows {
103+ fn C.fseek(stream &C.FILE, offset i32, whence i32) i32
104+} $else {
105+ fn C.fseek(stream &C.FILE, offset isize, whence i32) i32
106+}
103107
104108fn C.fopen(filename &char, mode &char) &C.FILE
105109
@@ -45,13 +45,14 @@ fn fixed_array_index_info(t types.Type) (bool, bool, types.ArrayFixed) {
4545
4646// gen_array_literal_value emits array literal value output for c.
4747fn (mut g FlatGen) gen_array_literal_value(node flat.Node, elem_type types.Type) {
48- c_elem := g.tc.c_type(elem_type)
48+ c_elem := g.value_c_type(elem_type)
49+ sizeof_elem := g.value_sizeof_target(elem_type)
4950 count := node.children_count
5051 if count == 0 {
51- g.write('array_new(sizeof(${c_elem}), 0, 0)')
52+ g.write('array_new(sizeof(${sizeof_elem}), 0, 0)')
5253 return
5354 }
54- g.write('new_array_from_c_array(${count}, ${count}, sizeof(${c_elem}), (${c_elem}[]){')
55+ g.write('new_array_from_c_array(${count}, ${count}, sizeof(${sizeof_elem}), (${c_elem}[]){')
5556 for i in 0 .. count {
5657 if i > 0 {
5758 g.write(', ')
@@ -67,7 +68,7 @@ fn (mut g FlatGen) gen_array_literal_value(node flat.Node, elem_type types.Type)
6768}
6869
6970fn (mut g FlatGen) gen_array_literal_ptr_arg(node flat.Node, elem_type types.Type) {
70- c_elem := g.tc.c_type(elem_type)
71+ c_elem := g.value_c_type(elem_type)
7172 g.write('(${c_elem}[]){')
7273 for i in 0 .. node.children_count {
7374 if i > 0 {
@@ -96,7 +97,7 @@ fn (mut g FlatGen) gen_pointer_arg_from_array_literal(node flat.Node, expected t
9697fn (mut g FlatGen) gen_fixed_array_data_arg(id flat.NodeId, arr types.ArrayFixed) {
9798 node := g.a.nodes[int(id)]
9899 if node.kind == .array_literal {
99- c_elem := g.tc.c_type(arr.elem_type)
100+ c_elem := g.value_c_type(arr.elem_type)
100101 g.write('(${c_elem}[]){')
101102 for i in 0 .. node.children_count {
102103 if i > 0 {
@@ -107,6 +108,10 @@ fn (mut g FlatGen) gen_fixed_array_data_arg(id flat.NodeId, arr types.ArrayFixed
107108 g.write('}')
108109 return
109110 }
111+ if node.kind == .paren && node.children_count > 0 {
112+ g.gen_fixed_array_data_arg(g.a.child(&node, 0), arr)
113+ return
114+ }
110115 if node.kind == .postfix && node.children_count > 0 {
111116 child_id := g.a.child(&node, 0)
112117 child := g.a.nodes[int(child_id)]
@@ -219,22 +224,30 @@ fn (mut g FlatGen) gen_array_method_call(node flat.Node, fn_node &flat.Node, arr
219224 g.write(', 0)')
220225 }
221226 'delete_last' {
227+ g.write('array__delete_last(')
228+ if !is_ptr {
229+ g.write('&')
230+ }
222231 g.gen_expr(base_id)
223- g.write('${dot}len--')
232+ g.write(')')
224233 }
225234 'pop' {
226- g.write('({ ${c_elem} _pop${g.tmp_count} = *(${c_elem}*)array_get(')
227- g.gen_expr(base_id)
228- g.write(', ')
235+ amp := if is_ptr { '' } else { '&' }
236+ g.write('*(${c_elem}*)array__pop(${amp}')
229237 g.gen_expr(base_id)
230- g.write('${dot}len - 1); ')
238+ g.write(')')
239+ }
240+ 'pop_left' {
241+ amp := if is_ptr { '' } else { '&' }
242+ g.write('*(${c_elem}*)array__pop_left(${amp}')
231243 g.gen_expr(base_id)
232- g.write('${dot}len--; _pop${g.tmp_count}; })')
233- g.tmp_count++
244+ g.write(')')
234245 }
235246 'clear' {
247+ amp := if is_ptr { '' } else { '&' }
248+ g.write('array__clear(${amp}')
236249 g.gen_expr(base_id)
237- g.write('${dot}len = 0')
250+ g.write(')')
238251 }
239252 'push_many' {
240253 amp := if is_ptr { '' } else { '&' }
@@ -263,9 +276,12 @@ fn (mut g FlatGen) gen_array_method_call(node flat.Node, fn_node &flat.Node, arr
263276 g.write(')')
264277 }
265278 'trim' {
279+ amp := if is_ptr { '' } else { '&' }
280+ g.write('array__trim(${amp}')
266281 g.gen_expr(base_id)
267- g.write('${dot}len = ')
282+ g.write(', ')
268283 g.gen_expr(g.a.child(&node, 1))
284+ g.write(')')
269285 }
270286 'ensure_cap' {
271287 amp := if is_ptr { '' } else { '&' }
@@ -338,6 +354,33 @@ fn (mut g FlatGen) gen_array_method_call(node flat.Node, fn_node &flat.Node, arr
338354 g.gen_expr(g.a.child(&node, 1))
339355 g.write(')')
340356 }
357+ 'last_index' {
358+ if suffix := array_last_index_suffix(arr.elem_type) {
359+ index_fn := 'array_last_index_${suffix}'
360+ g.write('${index_fn}(')
361+ g.gen_expr(base_id)
362+ g.write(', ')
363+ g.gen_expr(g.a.child(&node, 1))
364+ g.write(')')
365+ } else {
366+ g.write('array_last_index_raw(')
367+ g.gen_expr(base_id)
368+ g.write(', &(${c_elem}[]){')
369+ g.gen_expr_with_expected_type(g.a.child(&node, 1), arr.elem_type)
370+ g.write('})')
371+ }
372+ }
373+ 'hex' {
374+ g.write('Array_u8__hex(')
375+ if base_node.kind == .array_literal {
376+ g.gen_expr_with_expected_type(base_id, types.Type(types.Array{
377+ elem_type: types.Type(types.u8_)
378+ }))
379+ } else {
380+ g.gen_expr(base_id)
381+ }
382+ g.write(')')
383+ }
341384 'wait' {
342385 // Only a thread array supports `.wait()` (joining every spawned thread and,
343386 // for non-void payloads, collecting their return values into a fresh `[]T`).
@@ -353,11 +396,11 @@ fn (mut g FlatGen) gen_array_method_call(node flat.Node, fn_node &flat.Node, arr
353396 if is_thread {
354397 g.gen_thread_array_wait(base_id, is_ptr, arr.elem_type)
355398 } else {
356- g.gen_array_method_call_fallback(node, fn_node.value, base_id, is_ptr)
399+ g.gen_array_method_call_fallback(node, fn_node.value, base_id, is_ptr, arr)
357400 }
358401 }
359402 else {
360- g.gen_array_method_call_fallback(node, fn_node.value, base_id, is_ptr)
403+ g.gen_array_method_call_fallback(node, fn_node.value, base_id, is_ptr, arr)
361404 }
362405 }
363406}
@@ -367,8 +410,8 @@ fn (mut g FlatGen) gen_array_method_call(node flat.Node, fn_node &flat.Node, arr
367410// emits the selector itself as a direct call. Shared by the catch-all `else` arm and by
368411// `.wait()` on non-thread arrays (which is unsupported and falls through here rather than
369412// joining elements as thread handles).
370-fn (mut g FlatGen) gen_array_method_call_fallback(node flat.Node, mname string, base_id flat.NodeId, is_ptr bool) {
371- best_mname := g.array_method_fallback(mname)
413+fn (mut g FlatGen) gen_array_method_call_fallback(node flat.Node, mname string, base_id flat.NodeId, is_ptr bool, arr types.Array) {
414+ best_mname := g.array_method_fallback_for_receiver(mname, arr)
372415 if best_mname.len > 0 {
373416 g.write(c_name(best_mname))
374417 g.write('(')
@@ -503,6 +546,16 @@ fn array_lookup_suffix(elem_type types.Type) string {
503546 return 'int'
504547}
505548
549+fn array_last_index_suffix(elem_type types.Type) ?string {
550+ elem_name := elem_type.name()
551+ return match elem_name {
552+ 'string' { 'string' }
553+ 'u8', 'byte' { 'u8' }
554+ 'int' { 'int' }
555+ else { none }
556+ }
557+}
558+
506559// array_method_fallback supports array method fallback handling for FlatGen.
507560fn (mut g FlatGen) array_method_fallback(method string) string {
508561 if method in g.array_method_cache {
@@ -521,6 +574,38 @@ fn (mut g FlatGen) array_method_fallback(method string) string {
521574 return best_mname
522575}
523576
577+fn (mut g FlatGen) array_method_fallback_for_receiver(method string, arr types.Array) string {
578+ key := '${arr.elem_type.name()}.${method}'
579+ if key in g.array_method_cache {
580+ return g.array_method_cache[key]
581+ }
582+ suffix := '.${method}'
583+ mut best_mname := ''
584+ for mname, ptypes in g.tc.fn_param_types {
585+ if !mname.ends_with(suffix) || ptypes.len == 0 {
586+ continue
587+ }
588+ recv := types.unwrap_pointer(ptypes[0])
589+ if recv is types.Array && g.array_elem_type_matches(recv.elem_type, arr.elem_type) {
590+ if best_mname.len == 0 || mname.len > best_mname.len {
591+ best_mname = mname
592+ }
593+ }
594+ }
595+ if best_mname.len == 0 {
596+ best_mname = g.array_method_fallback(method)
597+ }
598+ g.array_method_cache[key] = best_mname
599+ return best_mname
600+}
601+
602+fn (g &FlatGen) array_elem_type_matches(expected types.Type, actual types.Type) bool {
603+ if expected.name() == actual.name() {
604+ return true
605+ }
606+ return g.tc.c_type(expected) == g.tc.c_type(actual)
607+}
608+
524609// gen_map_ref_arg emits map ref arg output for c.
525610fn (mut g FlatGen) gen_map_ref_arg(base_id flat.NodeId, base_type types.Type) {
526611 if base_type is types.Pointer {
@@ -584,18 +669,55 @@ fn (mut g FlatGen) gen_index_assign(node flat.Node) {
584669 }
585670 if is_array_base {
586671 c_elem := g.value_c_type(arr_type.elem_type)
587- g.write('array_set(')
672+ tmp := g.tmp_count
673+ g.tmp_count++
674+ g.write('{ array* _a${tmp} = ')
588675 if base_type is types.Pointer {
589- g.write('*')
676+ g.gen_expr(base_id)
677+ } else {
678+ g.write('&')
679+ g.gen_expr(base_id)
590680 }
591- g.gen_expr(base_id)
592- g.write(', ')
681+ g.write('; int _i${tmp} = ')
593682 g.gen_expr(g.a.child(&lhs, 1))
594- g.write(', &(${c_elem}[]){')
595- g.gen_expr_with_expected_type(g.a.child(&node, 1), arr_type.elem_type)
596- g.writeln('});')
683+ g.write('; array__set(_a${tmp}, _i${tmp}, &(${c_elem}[]){')
684+ if op := compound_assign_to_infix_op(node.op) {
685+ if arr_type.elem_type is types.String && op == .plus {
686+ g.write('string__plus(')
687+ g.write('*(string*)array_get(*_a${tmp}, _i${tmp})')
688+ g.write(', ')
689+ g.gen_expr_as_string(g.a.child(&node, 1))
690+ g.write(')')
691+ } else {
692+ g.write('(')
693+ g.write('*(${c_elem}*)array_get(*_a${tmp}, _i${tmp})')
694+ g.write(' ${g.op_str(op)} ')
695+ g.gen_expr_with_expected_type(g.a.child(&node, 1), arr_type.elem_type)
696+ g.write(')')
697+ }
698+ } else {
699+ g.gen_expr_with_expected_type(g.a.child(&node, 1), arr_type.elem_type)
700+ }
701+ g.writeln('}); }')
597702 return
598703 }
599704 }
600705 g.gen_assign(node)
601706}
707+
708+fn compound_assign_to_infix_op(op flat.Op) ?flat.Op {
709+ match op {
710+ .plus_assign { return flat.Op.plus }
711+ .minus_assign { return flat.Op.minus }
712+ .mul_assign { return flat.Op.mul }
713+ .div_assign { return flat.Op.div }
714+ .mod_assign { return flat.Op.mod }
715+ .amp_assign { return flat.Op.amp }
716+ .pipe_assign { return flat.Op.pipe }
717+ .xor_assign { return flat.Op.xor }
718+ .left_shift_assign { return flat.Op.left_shift }
719+ .right_shift_assign { return flat.Op.right_shift }
720+ .right_shift_unsigned_assign { return flat.Op.right_shift_unsigned }
721+ else { return none }
722+ }
723+}
@@ -54,13 +54,17 @@ mut:
5454 fn_decl_ret_types map[string]types.Type // fn decl name (and qualified variants) -> return type
5555 struct_decl_infos map[string]StructDeclInfo
5656 struct_decl_short_infos map[string]StructDeclInfo
57+ const_runtime_inits []string
58+ const_runtime_init_modules []string
5759 runtime_inits []string
60+ runtime_init_modules []string
5861 compiler_vroot string
5962 c99_mode bool
6063 cur_fn_name string
6164 cur_param_names []string
6265 cur_param_type_values []types.Type
6366 cur_param_types map[string]types.Type
67+ cur_mut_params map[string]bool
6468 cur_fn_ret types.Type = types.Type(types.void_)
6569 cur_fn_ret_is_optional bool
6670 cur_fn_ret_base types.Type = types.Type(types.void_)
@@ -154,6 +158,7 @@ pub fn FlatGen.new() FlatGen {
154158 cur_param_names: []string{}
155159 cur_param_type_values: []types.Type{}
156160 cur_param_types: map[string]types.Type{}
161+ cur_mut_params: map[string]bool{}
157162 needed_optional_types: map[string]string{}
158163 emitted_optional_types: map[string]bool{}
159164 emitted_fns: map[string]bool{}
@@ -171,7 +176,10 @@ pub fn FlatGen.new() FlatGen {
171176 fn_defer_counts: map[int]string{}
172177 defer_capture_names: []string{}
173178 defer_capture_types: map[string]types.Type{}
179+ const_runtime_inits: []string{}
180+ const_runtime_init_modules: []string{}
174181 runtime_inits: []string{}
182+ runtime_init_modules: []string{}
175183 compiler_vroot: ''
176184 line_start: true
177185 }
@@ -207,7 +215,10 @@ pub fn (mut g FlatGen) gen_with_used_options(a &flat.FlatAst, used_fns map[strin
207215 g.fn_defer_counts = map[int]string{}
208216 g.defer_capture_names = []string{}
209217 g.defer_capture_types = map[string]types.Type{}
218+ g.const_runtime_inits = []string{}
219+ g.const_runtime_init_modules = []string{}
210220 g.runtime_inits = []string{}
221+ g.runtime_init_modules = []string{}
211222 g.compiler_vroot = ''
212223 g.str_lit_ids = map[string]int{}
213224 g.global_types = map[string]types.Type{}
@@ -241,6 +252,7 @@ pub fn (mut g FlatGen) gen_with_used_options(a &flat.FlatAst, used_fns map[strin
241252 g.cur_param_names = []string{}
242253 g.cur_param_type_values = []types.Type{}
243254 g.cur_param_types = map[string]types.Type{}
255+ g.cur_mut_params = map[string]bool{}
244256 g.needed_optional_types = map[string]string{}
245257 g.emitted_optional_types = map[string]bool{}
246258 g.emitted_fns = map[string]bool{}
@@ -312,15 +324,19 @@ pub fn (mut g FlatGen) gen_with_used_options(a &flat.FlatAst, used_fns map[strin
312324 g.sb.write_string(const_code)
313325 // The final builder now owns a copy of the const code.
314326 unsafe { const_code.free() }
315- if g.runtime_inits.len > 0 || g.module_init_fns.len > 0 || g.global_inits.len > 0 {
327+ if g.const_runtime_inits.len > 0 || g.runtime_inits.len > 0 || g.module_init_fns.len > 0
328+ || g.global_inits.len > 0 {
316329 g.writeln('void _vinit() {')
317- for ri in g.runtime_inits {
318- g.writeln(ri)
319- }
320- // Module-level init() functions run after const/global initialization.
321- for init_fn in g.ordered_module_init_fns() {
322- g.writeln('\t${init_fn}();')
330+ mut emitted_const := []bool{len: g.const_runtime_inits.len}
331+ mut emitted_runtime := []bool{len: g.runtime_inits.len}
332+ init_fns := g.module_init_fn_map()
333+ for mod in g.ordered_startup_modules(init_fns) {
334+ g.emit_runtime_inits_for_module(mod, mut emitted_const, mut emitted_runtime)
335+ if init_fn := init_fns[mod] {
336+ g.writeln('\t${init_fn}();')
337+ }
323338 }
339+ g.emit_remaining_runtime_inits(mut emitted_const, mut emitted_runtime)
324340 g.writeln('}')
325341 g.writeln('')
326342 }
@@ -345,7 +361,7 @@ fn node_kind_id(node flat.Node) int {
345361// collect_gen_info updates collect gen info state for c.
346362fn (mut g FlatGen) collect_gen_info() {
347363 g.collect_c_flags_from_directives()
348- mut cur_module := ''
364+ mut cur_module := 'main'
349365 mut cur_file := ''
350366 mut seen_import_in_file := false
351367 for node_idx in 0 .. g.a.nodes.len {
@@ -354,9 +370,9 @@ fn (mut g FlatGen) collect_gen_info() {
354370 if kind_id == 77 {
355371 cur_file = node.value
356372 g.note_compiler_source_file(node.value)
357- g.tc.cur_file = cur_file
358- cur_module = ''
373+ cur_module = 'main'
359374 g.tc.cur_module = cur_module
375+ g.tc.cur_file = cur_file
360376 seen_import_in_file = false
361377 continue
362378 }
@@ -1317,7 +1333,7 @@ fn (mut g FlatGen) collect_const_init_order_from_files() {
13171333 if node_kind_id(node) != 77 || node.children_count == 0 {
13181334 continue
13191335 }
1320- mut cur_module := ''
1336+ mut cur_module := 'main'
13211337 for i in 0 .. node.children_count {
13221338 child := g.a.child_node(&node, i)
13231339 kind_id := node_kind_id(child)
@@ -1345,21 +1361,128 @@ fn (mut g FlatGen) collect_const_init_order_from_files() {
13451361
13461362// ordered_module_init_fns supports ordered module init fns handling for FlatGen.
13471363fn (g &FlatGen) ordered_module_init_fns() []string {
1364+ module_to_init := g.module_init_fn_map()
1365+ mut result := []string{}
1366+ mut visiting := map[string]bool{}
1367+ mut visited := map[string]bool{}
1368+ for init_fn in g.module_init_fns {
1369+ mod := g.module_init_fn_modules[init_fn] or { '' }
1370+ g.visit_module_init(mod, module_to_init, mut visiting, mut visited, mut result)
1371+ }
1372+ return result
1373+}
1374+
1375+fn (g &FlatGen) module_init_fn_map() map[string]string {
13481376 mut module_to_init := map[string]string{}
13491377 for init_fn in g.module_init_fns {
13501378 mod := g.module_init_fn_modules[init_fn] or { '' }
13511379 module_to_init[mod] = init_fn
13521380 }
1381+ return module_to_init
1382+}
1383+
1384+fn (g &FlatGen) ordered_startup_modules(module_to_init map[string]string) []string {
1385+ mut module_order := []string{}
1386+ for init_fn in g.module_init_fns {
1387+ mod := g.module_init_fn_modules[init_fn] or { '' }
1388+ if mod !in module_order {
1389+ module_order << mod
1390+ }
1391+ }
1392+ for mod in g.const_runtime_init_modules {
1393+ if mod !in module_order {
1394+ module_order << mod
1395+ }
1396+ }
1397+ for mod in g.runtime_init_modules {
1398+ if mod !in module_order {
1399+ module_order << mod
1400+ }
1401+ }
1402+ mut startup_modules := map[string]bool{}
1403+ for mod in module_order {
1404+ startup_modules[mod] = true
1405+ }
1406+ for mod, _ in module_to_init {
1407+ startup_modules[mod] = true
1408+ }
13531409 mut result := []string{}
13541410 mut visiting := map[string]bool{}
13551411 mut visited := map[string]bool{}
1356- for init_fn in g.module_init_fns {
1357- mod := g.module_init_fn_modules[init_fn] or { '' }
1358- g.visit_module_init(mod, module_to_init, mut visiting, mut visited, mut result)
1412+ for mod in module_order {
1413+ g.visit_startup_module(mod, startup_modules, mut visiting, mut visited, mut result)
13591414 }
13601415 return result
13611416}
13621417
1418+fn (g &FlatGen) visit_startup_module(mod string, startup_modules map[string]bool, mut visiting map[string]bool, mut visited map[string]bool, mut result []string) {
1419+ if mod in visited || mod in visiting {
1420+ return
1421+ }
1422+ visiting[mod] = true
1423+ for dep in g.module_imports[mod] or { []string{} } {
1424+ dep_module := g.startup_dependency_module(dep, startup_modules)
1425+ g.visit_startup_module(dep_module, startup_modules, mut visiting, mut visited, mut result)
1426+ }
1427+ visiting.delete(mod)
1428+ visited[mod] = true
1429+ if mod in startup_modules {
1430+ result << mod
1431+ }
1432+}
1433+
1434+fn (g &FlatGen) startup_dependency_module(dep string, startup_modules map[string]bool) string {
1435+ if dep in startup_modules || dep in g.module_imports {
1436+ return dep
1437+ }
1438+ short := startup_module_key(dep)
1439+ if short in startup_modules || short in g.module_imports {
1440+ return short
1441+ }
1442+ return dep
1443+}
1444+
1445+fn (mut g FlatGen) emit_runtime_inits_for_module(mod string, mut emitted_const []bool, mut emitted_runtime []bool) {
1446+ for i, ri in g.const_runtime_inits {
1447+ if !emitted_const[i] && i < g.const_runtime_init_modules.len
1448+ && g.const_runtime_init_modules[i] == mod {
1449+ g.writeln(ri)
1450+ emitted_const[i] = true
1451+ }
1452+ }
1453+ for i, ri in g.runtime_inits {
1454+ if !emitted_runtime[i] && i < g.runtime_init_modules.len && g.runtime_init_modules[i] == mod {
1455+ g.writeln(ri)
1456+ emitted_runtime[i] = true
1457+ }
1458+ }
1459+}
1460+
1461+fn (mut g FlatGen) emit_remaining_runtime_inits(mut emitted_const []bool, mut emitted_runtime []bool) {
1462+ for i, ri in g.const_runtime_inits {
1463+ if !emitted_const[i] {
1464+ g.writeln(ri)
1465+ emitted_const[i] = true
1466+ }
1467+ }
1468+ for i, ri in g.runtime_inits {
1469+ if !emitted_runtime[i] {
1470+ g.writeln(ri)
1471+ emitted_runtime[i] = true
1472+ }
1473+ }
1474+}
1475+
1476+fn (mut g FlatGen) queue_const_runtime_init(line string) {
1477+ g.const_runtime_inits << line
1478+ g.const_runtime_init_modules << g.tc.cur_module
1479+}
1480+
1481+fn (mut g FlatGen) queue_runtime_init(line string) {
1482+ g.runtime_inits << line
1483+ g.runtime_init_modules << g.tc.cur_module
1484+}
1485+
13631486// visit_module_init updates visit module init state for FlatGen.
13641487fn (g &FlatGen) visit_module_init(mod string, module_to_init map[string]string, mut visiting map[string]bool, mut visited map[string]bool, mut result []string) {
13651488 if mod in visited || mod in visiting {
@@ -1367,7 +1490,8 @@ fn (g &FlatGen) visit_module_init(mod string, module_to_init map[string]string,
13671490 }
13681491 visiting[mod] = true
13691492 for dep in g.module_imports[mod] or { []string{} } {
1370- g.visit_module_init(dep, module_to_init, mut visiting, mut visited, mut result)
1493+ dep_module := startup_module_key(dep)
1494+ g.visit_module_init(dep_module, module_to_init, mut visiting, mut visited, mut result)
13711495 }
13721496 visiting.delete(mod)
13731497 visited[mod] = true
@@ -1997,6 +2121,140 @@ fn (mut g FlatGen) gen_amp_c_string_literal(id flat.NodeId, node flat.Node) bool
19972121 return false
19982122}
19992123
2124+fn (mut g FlatGen) gen_expr_as_string(id flat.NodeId) {
2125+ typ := g.usable_expr_type(id)
2126+ if g.gen_map_str_expr(id, typ) {
2127+ return
2128+ }
2129+ g.gen_expr(id)
2130+}
2131+
2132+fn (mut g FlatGen) gen_map_str_expr(id flat.NodeId, typ types.Type) bool {
2133+ clean := map_str_clean_type(typ)
2134+ if clean !is types.Map {
2135+ return false
2136+ }
2137+ node := g.a.nodes[int(id)]
2138+ if node.kind == .map_init && typ !is types.Pointer {
2139+ tmp := '__map_str_tmp_${g.tmp_count}'
2140+ g.tmp_count++
2141+ g.write('({ map ${tmp} = ')
2142+ g.gen_expr_with_expected_type(id, clean)
2143+ g.write(';')
2144+ key_kind := map_str_kind(g.tc, clean.key_type)
2145+ val_kind := map_str_kind(g.tc, clean.value_type)
2146+ fixed_len := map_str_fixed_len(clean.value_type)
2147+ g.write(' v3_map_str(${tmp}, ${key_kind}, ${val_kind}, ${fixed_len}); })')
2148+ return true
2149+ }
2150+ g.write('v3_map_str(')
2151+ if typ is types.Pointer {
2152+ needs_paren := g.a.nodes[int(id)].kind !in [.ident, .selector, .call]
2153+ g.write('*')
2154+ if needs_paren {
2155+ g.write('(')
2156+ }
2157+ g.gen_expr(id)
2158+ if needs_paren {
2159+ g.write(')')
2160+ }
2161+ } else {
2162+ g.gen_expr(id)
2163+ }
2164+ key_kind := map_str_kind(g.tc, clean.key_type)
2165+ val_kind := map_str_kind(g.tc, clean.value_type)
2166+ fixed_len := map_str_fixed_len(clean.value_type)
2167+ g.write(', ${key_kind}, ${val_kind}, ${fixed_len})')
2168+ return true
2169+}
2170+
2171+fn map_str_clean_type(typ types.Type) types.Type {
2172+ clean := types.unwrap_pointer(typ)
2173+ if clean is types.Alias {
2174+ return clean.base_type
2175+ }
2176+ return clean
2177+}
2178+
2179+fn map_str_kind(tc &types.TypeChecker, typ types.Type) int {
2180+ clean := if typ is types.Alias { typ.base_type } else { typ }
2181+ if clean is types.String {
2182+ return 1
2183+ }
2184+ if clean is types.Rune {
2185+ return 4
2186+ }
2187+ if clean is types.ISize || clean is types.Char {
2188+ return 2
2189+ }
2190+ if clean is types.USize {
2191+ return 3
2192+ }
2193+ if clean is types.Primitive {
2194+ if clean.props.has(.float) {
2195+ return if tc.c_type(types.Type(clean)) == 'float' { 8 } else { 5 }
2196+ }
2197+ name := types.Type(clean).name()
2198+ if name in ['i8', 'i16', 'i32', 'i64', 'int'] {
2199+ return 2
2200+ }
2201+ if name in ['u8', 'byte'] {
2202+ return 3
2203+ }
2204+ if name in ['u16', 'u32', 'u64'] {
2205+ return 3
2206+ }
2207+ if name == 'bool' {
2208+ return 7
2209+ }
2210+ }
2211+ if fixed := array_fixed_type(clean) {
2212+ elem := if fixed.elem_type is types.Alias {
2213+ fixed.elem_type.base_type
2214+ } else {
2215+ fixed.elem_type
2216+ }
2217+ if elem is types.Primitive && elem.props.has(.float) {
2218+ return if tc.c_type(types.Type(elem)) == 'float' { 9 } else { 6 }
2219+ }
2220+ }
2221+ return 0
2222+}
2223+
2224+fn map_str_fixed_len(typ types.Type) int {
2225+ if fixed := array_fixed_type(typ) {
2226+ if fixed.len > 0 {
2227+ return fixed.len
2228+ }
2229+ if fixed.len_expr.len > 0 && fixed.len_expr.int().str() == fixed.len_expr {
2230+ return fixed.len_expr.int()
2231+ }
2232+ }
2233+ return 0
2234+}
2235+
2236+// gen_cast_from_mut_param_address emits pointer casts for `¶m` where `param`
2237+// is a mutable V parameter already represented as a C pointer.
2238+fn (mut g FlatGen) gen_cast_from_mut_param_address(id flat.NodeId, ct string) bool {
2239+ node := g.a.nodes[int(id)]
2240+ if node.kind != .prefix || node.op != .amp || node.children_count != 1 {
2241+ return false
2242+ }
2243+ child_id := g.a.child(&node, 0)
2244+ child := g.a.nodes[int(child_id)]
2245+ if child.kind != .ident || !g.current_param_is_mut(child.value) {
2246+ return false
2247+ }
2248+ param_type := g.current_param_type(child.value) or { return false }
2249+ if param_type !is types.Pointer {
2250+ return false
2251+ }
2252+ g.write('(${ct})(')
2253+ g.gen_expr(child_id)
2254+ g.write(')')
2255+ return true
2256+}
2257+
20002258// gen_expr_with_expected_type emits expr with expected type output for c.
20012259fn (mut g FlatGen) gen_expr_with_expected_type(id flat.NodeId, expected types.Type) {
20022260 old_expected := g.expected_expr_type
@@ -2022,6 +2280,11 @@ fn (mut g FlatGen) gen_expr_with_expected_type(id flat.NodeId, expected types.Ty
20222280 actual = param_type
20232281 }
20242282 }
2283+ if expected is types.String && g.gen_map_str_expr(id, actual) {
2284+ g.expected_expr_type = old_expected
2285+ g.expected_enum = old_expected_enum
2286+ return
2287+ }
20252288 if _ := fn_type_from(expected) {
20262289 if g.gen_callback_fn_value_for_expected_type(id, expected) {
20272290 g.expected_expr_type = old_expected
@@ -2036,17 +2299,13 @@ fn (mut g FlatGen) gen_expr_with_expected_type(id flat.NodeId, expected types.Ty
20362299 }
20372300 }
20382301 if expected is types.Array && node.kind == .array_literal {
2039- elem_type := if node.children_count > 0 {
2040- g.tc.resolve_type(g.a.child(&node, 0))
2041- } else {
2042- expected.elem_type
2043- }
2044- g.gen_array_literal_value(node, elem_type)
2302+ g.gen_array_literal_value(node, expected.elem_type)
20452303 g.expected_expr_type = old_expected
20462304 g.expected_enum = old_expected_enum
20472305 return
20482306 }
2049- if expected !is types.Pointer && expected !is types.Void && actual is types.Pointer
2307+ if expected !is types.Pointer && expected !is types.Void && expected !is types.OptionType
2308+ && expected !is types.ResultType && actual is types.Pointer
20502309 && g.type_names_match(actual.base_type, expected) {
20512310 needs_paren := node.kind !in [.ident, .selector, .call, .index]
20522311 g.write('*')
@@ -2290,11 +2549,20 @@ fn (mut g FlatGen) type_name_c_type(type_name string) string {
22902549 if _ := g.tc.cur_scope.lookup(type_name) {
22912550 return c_name(type_name)
22922551 }
2552+ if type_name.starts_with('fn_ptr:') {
2553+ return g.resolve_fn_ptr_type(type_name)
2554+ }
22932555 t := g.tc.parse_type(type_name)
22942556 return g.tc.c_type(t)
22952557}
22962558
22972559fn (mut g FlatGen) sizeof_target(value string) string {
2560+ if value.starts_with('fn_ptr:') {
2561+ return g.resolve_fn_ptr_type(value)
2562+ }
2563+ if fixed_target := c_fixed_array_typedef_sizeof_target(value) {
2564+ return fixed_target
2565+ }
22982566 if value.contains('.') {
22992567 parts := value.split('.')
23002568 if parts.len > 1 {
@@ -2306,9 +2574,29 @@ fn (mut g FlatGen) sizeof_target(value string) string {
23062574 }
23072575 }
23082576 }
2577+ if fixed := array_fixed_type(g.tc.parse_type(value)) {
2578+ c_elem, dims := g.fixed_array_decl_parts(fixed)
2579+ return '${c_elem}${dims}'
2580+ }
23092581 return g.type_name_c_type(value)
23102582}
23112583
2584+fn c_fixed_array_typedef_sizeof_target(value string) ?string {
2585+ if !value.starts_with('Array_fixed_') {
2586+ return none
2587+ }
2588+ payload := value['Array_fixed_'.len..]
2589+ if !payload.contains('_') {
2590+ return none
2591+ }
2592+ elem := payload.all_before_last('_')
2593+ len := payload.all_after_last('_')
2594+ if elem.len == 0 || len.len == 0 {
2595+ return none
2596+ }
2597+ return '${elem}[${len}]'
2598+}
2599+
23122600fn sizeof_selector_target(base string, fields []string) string {
23132601 mut expr := c_name(base)
23142602 for field in fields {
@@ -2858,7 +3146,12 @@ fn (mut g FlatGen) gen_expr(id flat.NodeId) {
28583146 } else if v.starts_with('\\') {
28593147 g.write("'${v}'")
28603148 } else {
2861- g.write(v)
3149+ runes := v.runes()
3150+ if runes.len == 0 {
3151+ g.write('0')
3152+ } else {
3153+ g.write(int(runes[0]).str())
3154+ }
28623155 }
28633156 }
28643157 .string_literal {
@@ -2975,6 +3268,10 @@ fn (mut g FlatGen) gen_expr(id flat.NodeId) {
29753268 g.expected_enum = old_expected_enum
29763269 return
29773270 }
3271+ if g.gen_array_infix_eq(node, lhs_id, rhs_id, lhs_type, rhs_type) {
3272+ g.expected_enum = old_expected_enum
3273+ return
3274+ }
29783275 if lhs_type is types.String || rhs_type is types.String {
29793276 if g.gen_string_infix_fallback(node, lhs_id, rhs_id) {
29803277 g.expected_enum = old_expected_enum
@@ -3239,6 +3536,23 @@ fn (mut g FlatGen) gen_expr(id flat.NodeId) {
32393536 } else {
32403537 g.write('0')
32413538 }
3539+ } else if base_type0 is types.String && node.value == 'len' {
3540+ g.gen_expr(base_id)
3541+ g.write('.len')
3542+ } else if types.unwrap_pointer(base_type0) is types.Array && node.value == 'len' {
3543+ needs_paren := base.kind !in [.ident, .selector, .call]
3544+ if needs_paren {
3545+ g.write('(')
3546+ }
3547+ g.gen_expr(base_id)
3548+ if needs_paren {
3549+ g.write(')')
3550+ }
3551+ if base_type0 is types.Pointer {
3552+ g.write('->len')
3553+ } else {
3554+ g.write('.len')
3555+ }
32423556 } else if base.kind == .call && base.children_count == 2
32433557 && g.c_typedef_cast_call_name(base).len > 0 {
32443558 cast_name := g.c_typedef_cast_call_name(base)
@@ -3476,7 +3790,7 @@ fn (mut g FlatGen) gen_expr(id flat.NodeId) {
34763790 ct := g.tc.c_type(raw_init_type)
34773791 g.write('(${ct}){0}')
34783792 } else {
3479- c_elem := g.tc.c_type(init_type)
3793+ c_elem := g.sizeof_target(node.value)
34803794 g.write('array_new(sizeof(${c_elem}), 0, 0)')
34813795 }
34823796 }
@@ -3496,6 +3810,9 @@ fn (mut g FlatGen) gen_expr(id flat.NodeId) {
34963810 g.write('(${ct}){0}')
34973811 } else if target_type is types.SumType {
34983812 g.gen_sum_cast_expr(target_type, g.a.child(&node, 0))
3813+ } else if target_type is types.Pointer
3814+ && g.gen_cast_from_mut_param_address(g.a.child(&node, 0), ct) {
3815+ return
34993816 } else {
35003817 g.write('(${ct})(')
35013818 g.gen_expr(g.a.child(&node, 0))
@@ -3623,51 +3940,51 @@ fn (mut g FlatGen) gen_string_infix_fallback(node flat.Node, lhs_id flat.NodeId,
36233940 match node.op {
36243941 .plus {
36253942 g.write('string__plus(')
3626- g.gen_expr(lhs_id)
3943+ g.gen_expr_as_string(lhs_id)
36273944 g.write(', ')
3628- g.gen_expr(rhs_id)
3945+ g.gen_expr_as_string(rhs_id)
36293946 g.write(')')
36303947 }
36313948 .eq {
36323949 g.write('string__eq(')
3633- g.gen_expr(lhs_id)
3950+ g.gen_expr_as_string(lhs_id)
36343951 g.write(', ')
3635- g.gen_expr(rhs_id)
3952+ g.gen_expr_as_string(rhs_id)
36363953 g.write(')')
36373954 }
36383955 .ne {
36393956 g.write('!string__eq(')
3640- g.gen_expr(lhs_id)
3957+ g.gen_expr_as_string(lhs_id)
36413958 g.write(', ')
3642- g.gen_expr(rhs_id)
3959+ g.gen_expr_as_string(rhs_id)
36433960 g.write(')')
36443961 }
36453962 .lt {
36463963 g.write('string__lt(')
3647- g.gen_expr(lhs_id)
3964+ g.gen_expr_as_string(lhs_id)
36483965 g.write(', ')
3649- g.gen_expr(rhs_id)
3966+ g.gen_expr_as_string(rhs_id)
36503967 g.write(')')
36513968 }
36523969 .gt {
36533970 g.write('string__lt(')
3654- g.gen_expr(rhs_id)
3971+ g.gen_expr_as_string(rhs_id)
36553972 g.write(', ')
3656- g.gen_expr(lhs_id)
3973+ g.gen_expr_as_string(lhs_id)
36573974 g.write(')')
36583975 }
36593976 .le {
36603977 g.write('!string__lt(')
3661- g.gen_expr(rhs_id)
3978+ g.gen_expr_as_string(rhs_id)
36623979 g.write(', ')
3663- g.gen_expr(lhs_id)
3980+ g.gen_expr_as_string(lhs_id)
36643981 g.write(')')
36653982 }
36663983 .ge {
36673984 g.write('!string__lt(')
3668- g.gen_expr(lhs_id)
3985+ g.gen_expr_as_string(lhs_id)
36693986 g.write(', ')
3670- g.gen_expr(rhs_id)
3987+ g.gen_expr_as_string(rhs_id)
36713988 g.write(')')
36723989 }
36733990 else {
@@ -3678,6 +3995,45 @@ fn (mut g FlatGen) gen_string_infix_fallback(node flat.Node, lhs_id flat.NodeId,
36783995 return true
36793996}
36803997
3998+fn (mut g FlatGen) gen_array_infix_eq(node flat.Node, lhs_id flat.NodeId, rhs_id flat.NodeId, lhs_type types.Type, rhs_type types.Type) bool {
3999+ if node.op !in [.eq, .ne] {
4000+ return false
4001+ }
4002+ if lhs_type is types.Pointer || rhs_type is types.Pointer {
4003+ return false
4004+ }
4005+ lhs_arr := array_like_type(types.unwrap_pointer(lhs_type)) or { return false }
4006+ rhs_arr := array_like_type(types.unwrap_pointer(rhs_type)) or { return false }
4007+ elem_type := if lhs_arr.elem_type.name() != 'unknown' {
4008+ lhs_arr.elem_type
4009+ } else {
4010+ rhs_arr.elem_type
4011+ }
4012+ if node.op == .ne {
4013+ g.write('!')
4014+ }
4015+ if elem_type is types.String {
4016+ g.write('array_eq_string(')
4017+ } else {
4018+ g.write('array_eq_raw(')
4019+ }
4020+ g.gen_array_value_arg(lhs_id, lhs_type)
4021+ g.write(', ')
4022+ g.gen_array_value_arg(rhs_id, rhs_type)
4023+ if elem_type !is types.String {
4024+ g.write(', sizeof(${g.tc.c_type(elem_type)})')
4025+ }
4026+ g.write(')')
4027+ return true
4028+}
4029+
4030+fn (mut g FlatGen) gen_array_value_arg(id flat.NodeId, typ types.Type) {
4031+ if typ is types.Pointer {
4032+ g.write('*')
4033+ }
4034+ g.gen_expr(id)
4035+}
4036+
36814037fn array_membership_fn_name(elem_type types.Type, fixed bool) string {
36824038 prefix := if fixed { 'fixed_array_contains_' } else { 'array_contains_' }
36834039 elem_name := elem_type.name()
@@ -3787,6 +4143,7 @@ fn (mut g FlatGen) c99_feature_test_macros() {
37874143}
37884144
37894145fn (mut g FlatGen) headerless_libc_preamble() {
4146+ g.collect_preserved_c_fns(c_headerless_libc_declared_fns)
37904147 g.writeln('#ifndef NULL')
37914148 g.writeln('#define NULL ((void*)0)')
37924149 g.writeln('#endif')
@@ -3979,7 +4336,7 @@ fn (mut g FlatGen) headerless_libc_preamble() {
39794336 g.writeln('typedef struct SRWLOCK { void* Ptr; } SRWLOCK;')
39804337 g.writeln('typedef struct CONDITION_VARIABLE { void* Ptr; } CONDITION_VARIABLE;')
39814338 g.writeln('typedef void* atomic_uintptr_t;')
3982- g.writeln('#if !defined(__cplusplus) && !defined(_WCHAR_T) && !defined(_WCHAR_T_DEFINED) && !defined(__WCHAR_T) && !defined(__wchar_t_defined) && !defined(_BSD_WCHAR_T_DEFINED_) && !defined(_WCHAR_T_DECLARED)')
4339+ g.writeln('#if !defined(__TINYC__) && !defined(__cplusplus) && !defined(_WCHAR_T) && !defined(_WCHAR_T_DEFINED) && !defined(__WCHAR_T) && !defined(__wchar_t_defined) && !defined(_BSD_WCHAR_T_DEFINED_) && !defined(_WCHAR_T_DECLARED)')
39834340 g.writeln('#ifdef _WIN32')
39844341 g.writeln('typedef unsigned short wchar_t;')
39854342 g.writeln('#else')
@@ -4010,6 +4367,56 @@ fn (mut g FlatGen) headerless_libc_preamble() {
40104367 g.headerless_platform_constants()
40114368}
40124369
4370+const c_headerless_libc_declared_fns = [
4371+ 'getenv',
4372+ 'setenv',
4373+ 'abort',
4374+ 'memset',
4375+ 'memcpy',
4376+ 'memmove',
4377+ 'memcmp',
4378+ 'strlen',
4379+ 'strcmp',
4380+ 'strncmp',
4381+ 'strncpy',
4382+ 'floor',
4383+ 'ceil',
4384+ 'floorf',
4385+ 'ceilf',
4386+ 'sqrt',
4387+ 'pow',
4388+ 'ldexp',
4389+ 'fmod',
4390+ 'cos',
4391+ 'acos',
4392+ 'fabs',
4393+ 'open',
4394+ 'read',
4395+ 'fork',
4396+ 'dup2',
4397+ 'execlp',
4398+ 'execvp',
4399+ '_exit',
4400+ 'access',
4401+ 'realpath',
4402+ 'strrchr',
4403+ 'strstr',
4404+ 'snprintf',
4405+ 'fcntl',
4406+ 'pipe',
4407+ 'close',
4408+ 'signal',
4409+ 'atexit',
4410+ '__error',
4411+ '__errno',
4412+ '__errno_location',
4413+ '_errno',
4414+ 'pthread_mutex_init',
4415+ 'pthread_mutex_lock',
4416+ 'pthread_mutex_unlock',
4417+ 'pthread_mutex_destroy',
4418+]
4419+
40134420fn (mut g FlatGen) headerless_windows_sdk_types() {
40144421 g.writeln('#ifndef WINAPI')
40154422 g.writeln('#if defined(_WIN32) && (defined(__i386__) || defined(_M_IX86))')
@@ -5527,17 +5934,7 @@ fn (mut g FlatGen) builtin_abi_decls() {
55275934 g.writeln('#ifndef __linux__')
55285935 g.writeln('#define pthread_rwlockattr_setkind_np(attr, kind) 0')
55295936 g.writeln('#endif')
5530- g.writeln('#ifndef V_OS_FILELOCK_HELPERS_H')
5531- g.writeln('#ifdef _WIN32')
5532- g.writeln('BOOL LockFileEx(HANDLE handle, DWORD flags, DWORD reserved, DWORD low, DWORD high, OVERLAPPED* overlap);')
5533- g.writeln('BOOL UnlockFileEx(HANDLE handle, DWORD reserved, DWORD low, DWORD high, OVERLAPPED* overlap);')
5534- g.writeln('static inline int v_filelock_lock(HANDLE handle, int exclusive, int immediate, u64 start, u64 len) { OVERLAPPED overlap; memset(&overlap, 0, sizeof(overlap)); overlap.Offset = (DWORD)(start & 0xffffffffULL); overlap.OffsetHigh = (DWORD)(start >> 32); DWORD flags = immediate ? LOCKFILE_FAIL_IMMEDIATELY : 0; if (exclusive) { flags |= LOCKFILE_EXCLUSIVE_LOCK; } DWORD low = len == 0 ? MAXDWORD : (DWORD)(len & 0xffffffffULL); DWORD high = len == 0 ? MAXDWORD : (DWORD)(len >> 32); return LockFileEx(handle, flags, 0, low, high, &overlap) ? 0 : -1; }')
5535- g.writeln('static inline int v_filelock_unlock(HANDLE handle, u64 start, u64 len) { OVERLAPPED overlap; memset(&overlap, 0, sizeof(overlap)); overlap.Offset = (DWORD)(start & 0xffffffffULL); overlap.OffsetHigh = (DWORD)(start >> 32); DWORD low = len == 0 ? MAXDWORD : (DWORD)(len & 0xffffffffULL); DWORD high = len == 0 ? MAXDWORD : (DWORD)(len >> 32); return UnlockFileEx(handle, 0, low, high, &overlap) ? 0 : -1; }')
5536- g.writeln('#else')
5537- g.writeln('static inline int v_filelock_lock(int fd, int exclusive, int immediate, u64 start, u64 len) { struct flock fl; memset(&fl, 0, sizeof(fl)); fl.l_type = exclusive ? F_WRLCK : F_RDLCK; fl.l_whence = SEEK_SET; fl.l_start = (off_t)start; fl.l_len = len == 0 ? 0 : (off_t)len; return fcntl(fd, immediate ? F_SETLK : F_SETLKW, &fl); }')
5538- g.writeln('static inline int v_filelock_unlock(int fd, u64 start, u64 len) { struct flock fl; memset(&fl, 0, sizeof(fl)); fl.l_type = F_UNLCK; fl.l_whence = SEEK_SET; fl.l_start = (off_t)start; fl.l_len = len == 0 ? 0 : (off_t)len; return fcntl(fd, F_SETLK, &fl); }')
5539- g.writeln('#endif')
5540- g.writeln('#endif')
5937+ g.filelock_compat_decls()
55415938 g.writeln('#define array_new(elem_size, len, cap) __new_array((len), (cap), (elem_size))')
55425939 g.writeln('#define array_push array__push')
55435940 g.writeln('void array__push_many(array* a, void* val, int size);')
@@ -5577,14 +5974,65 @@ fn (mut g FlatGen) builtin_abi_decls() {
55775974 g.writeln('#define v_signal_with_handler_cast(sig, handler) signal((sig), ((void (*)(int))(handler)))')
55785975 g.writeln('string string__clone(string a);')
55795976 g.writeln('void string__free(string* s);')
5977+ g.writeln('string string__plus(string s, string a);')
5978+ g.writeln('string int__str(int n);')
5979+ g.writeln('string i64__str(i64 n);')
5980+ g.writeln('string u64__str(u64 nn);')
5981+ g.writeln('string f64__str(double x);')
5982+ g.writeln('string rune__str(i32 c);')
5983+ g.writeln('u8* malloc_noscan(ptrdiff_t n);')
5984+ g.writeln('static inline string v3_c_lit(const char* s, int len) { return (string){.str = (u8*)s, .len = len, .is_lit = 1}; }')
5985+ g.writeln('static inline string v3_char_string(int c) { return rune__str((i32)c); }')
5986+ g.writeln('static inline string v3_f64_fixed(double x, int precision) { char tmp[128]; int n = snprintf(tmp, sizeof(tmp), "%.*f", precision, x); if (n < 0) return v3_c_lit("", 0); if (n < (int)sizeof(tmp)) { u8* out = malloc_noscan(n + 1); memcpy(out, tmp, n + 1); return (string){.str = out, .len = n, .is_lit = 0}; } u8* out = malloc_noscan(n + 1); snprintf((char*)out, (size_t)n + 1, "%.*f", precision, x); return (string){.str = out, .len = n, .is_lit = 0}; }')
5987+ g.writeln('static inline string v3_int_zpad(int n, int width) { string s = int__str(n); if (n < 0) return s; while (s.len < width) s = string__plus(v3_c_lit("0", 1), s); return s; }')
5988+ g.writeln('static inline string v3_i64_zpad(i64 n, int width) { string s = i64__str(n); if (n < 0) return s; while (s.len < width) s = string__plus(v3_c_lit("0", 1), s); return s; }')
5989+ g.writeln('static inline string v3_u64_zpad(u64 n, int width) { string s = u64__str(n); while (s.len < width) s = string__plus(v3_c_lit("0", 1), s); return s; }')
5990+ g.writeln('static inline i64 v3_map_signed(void* p, int bytes) { if (bytes == 1) return *(signed char*)p; if (bytes == 2) return *(short*)p; if (bytes == 8) return *(long long*)p; return *(int*)p; }')
5991+ g.writeln('static inline u64 v3_map_unsigned(void* p, int bytes) { if (bytes == 1) return *(unsigned char*)p; if (bytes == 2) return *(unsigned short*)p; if (bytes == 8) return *(unsigned long long*)p; return *(unsigned int*)p; }')
5992+ g.writeln('static inline string v3_f32_array_str(float* vals, int n) { string out = v3_c_lit("[", 1); for (int i = 0; i < n; ++i) { if (i > 0) out = string__plus(out, v3_c_lit(", ", 2)); out = string__plus(out, f64__str((double)vals[i])); } return string__plus(out, v3_c_lit("]", 1)); }')
5993+ g.writeln('static inline string v3_f64_array_str(double* vals, int n) { string out = v3_c_lit("[", 1); for (int i = 0; i < n; ++i) { if (i > 0) out = string__plus(out, v3_c_lit(", ", 2)); out = string__plus(out, f64__str(vals[i])); } return string__plus(out, v3_c_lit("]", 1)); }')
5994+ g.writeln('static inline string v3_map_str_piece(void* p, int kind, int bytes, int fixed_len) {')
5995+ g.writeln('\tif (kind == 1) { return string__plus(string__plus(v3_c_lit("\'", 1), *(string*)p), v3_c_lit("\'", 1)); }')
5996+ g.writeln('\tif (kind == 2) { return v3_i64_zpad(v3_map_signed(p, bytes), 0); }')
5997+ g.writeln('\tif (kind == 3) { return u64__str(v3_map_unsigned(p, bytes)); }')
5998+ g.writeln('\tif (kind == 4) { i32 r = bytes == 1 ? (i32)(*(u8*)p) : *(i32*)p; return string__plus(string__plus(v3_c_lit("`", 1), rune__str(r)), v3_c_lit("`", 1)); }')
5999+ g.writeln('\tif (kind == 5) { if (bytes == (int)sizeof(float)) return f64__str((double)*(float*)p); return f64__str(*(double*)p); }')
6000+ g.writeln('\tif (kind == 6) { if (fixed_len == 0 && bytes == (int)sizeof(Array)) { Array a = *(Array*)p; if (a.element_size == (int)sizeof(float)) return v3_f32_array_str((float*)a.data, a.len); if (a.element_size == (int)sizeof(double)) return v3_f64_array_str((double*)a.data, a.len); } if (fixed_len > 0 && bytes == fixed_len * (int)sizeof(float)) return v3_f32_array_str((float*)p, fixed_len); int n = fixed_len > 0 ? fixed_len : bytes / (int)sizeof(double); return v3_f64_array_str((double*)p, n); }')
6001+ g.writeln('\tif (kind == 8) { return f64__str((double)*(float*)p); }')
6002+ g.writeln('\tif (kind == 9) { int n = fixed_len > 0 ? fixed_len : bytes / (int)sizeof(float); return v3_f32_array_str((float*)p, n); }')
6003+ g.writeln('\tif (kind == 7) { return *(bool*)p ? v3_c_lit("true", 4) : v3_c_lit("false", 5); }')
6004+ g.writeln('\treturn v3_c_lit("<map value>", 11);')
6005+ g.writeln('}')
6006+ g.writeln('static inline string v3_map_str(map m, int key_kind, int val_kind, int val_fixed_len) {')
6007+ g.writeln('\tstring out = v3_c_lit("{", 1); bool first = true;')
6008+ g.writeln('\tfor (int i = 0; i < m.key_values.len; ++i) {')
6009+ g.writeln('\t\tif (m.key_values.deletes != 0 && m.key_values.all_deleted != 0 && m.key_values.all_deleted[i] != 0) continue;')
6010+ g.writeln('\t\tif (!first) out = string__plus(out, v3_c_lit(", ", 2));')
6011+ g.writeln('\t\tvoid* key = (void*)(m.key_values.keys + i * m.key_values.key_bytes);')
6012+ g.writeln('\t\tvoid* val = (void*)(m.key_values.values + i * m.key_values.value_bytes);')
6013+ g.writeln('\t\tout = string__plus(out, v3_map_str_piece(key, key_kind, m.key_values.key_bytes, 0));')
6014+ g.writeln('\t\tout = string__plus(out, v3_c_lit(": ", 2));')
6015+ g.writeln('\t\tout = string__plus(out, v3_map_str_piece(val, val_kind, m.value_bytes, val_fixed_len));')
6016+ g.writeln('\t\tfirst = false;')
6017+ g.writeln('\t}')
6018+ g.writeln('\treturn string__plus(out, v3_c_lit("}", 1));')
6019+ g.writeln('}')
55806020 g.writeln('static inline int array_index_int(Array a, int val) { for (int i = 0; i < a.len; i++) if (((int*)a.data)[i] == val) return i; return -1; }')
6021+ g.writeln('static inline int array_last_index_int(Array a, int val) { for (int i = a.len - 1; i >= 0; i--) if (((int*)a.data)[i] == val) return i; return -1; }')
55816022 g.writeln('static inline bool array_contains_int(Array a, int val) { return array_index_int(a, val) >= 0; }')
55826023 g.writeln('static inline int array_index_u8(Array a, u8 val) { for (int i = 0; i < a.len; i++) if (((u8*)a.data)[i] == val) return i; return -1; }')
6024+ g.writeln('static inline int array_last_index_u8(Array a, u8 val) { for (int i = a.len - 1; i >= 0; i--) if (((u8*)a.data)[i] == val) return i; return -1; }')
55836025 g.writeln('static inline bool array_contains_u8(Array a, u8 val) { return array_index_u8(a, val) >= 0; }')
55846026 g.writeln('static inline int array_index_string(Array a, string val) { string* data = (string*)a.data; for (int i = 0; i < a.len; i++) if (data[i].len == val.len && memcmp(data[i].str, val.str, val.len) == 0) return i; return -1; }')
6027+ g.writeln('static inline int array_last_index_string(Array a, string val) { string* data = (string*)a.data; for (int i = a.len - 1; i >= 0; i--) if (data[i].len == val.len && memcmp(data[i].str, val.str, val.len) == 0) return i; return -1; }')
55856028 g.writeln('static inline bool array_contains_string(Array a, string val) { return array_index_string(a, val) >= 0; }')
6029+ g.writeln('static inline int array_last_index_raw(Array a, const void* val) { for (int i = a.len - 1; i >= 0; i--) if (memcmp((u8*)a.data + (size_t)i * (size_t)a.element_size, val, (size_t)a.element_size) == 0) return i; return -1; }')
55866030 g.writeln('static inline bool array_eq_raw(Array a, Array b, int elem_size) { return a.len == b.len && (a.len == 0 || memcmp(a.data, b.data, (size_t)a.len * elem_size) == 0); }')
55876031 g.writeln('static inline bool array_eq_string(Array a, Array b) { if (a.len != b.len) return false; string* ad = (string*)a.data; string* bd = (string*)b.data; for (int i = 0; i < a.len; i++) if (ad[i].len != bd[i].len || memcmp(ad[i].str, bd[i].str, ad[i].len) != 0) return false; return true; }')
6032+ g.writeln('static inline bool array_eq_array(Array a, Array b, int depth) { if (a.len != b.len || a.element_size != b.element_size) return false; if (depth <= 1 || a.element_size != sizeof(Array)) { if (a.element_size == sizeof(string)) return array_eq_string(a, b); return array_eq_raw(a, b, a.element_size); } Array* ad = (Array*)a.data; Array* bd = (Array*)b.data; for (int i = 0; i < a.len; i++) { if (!array_eq_array(ad[i], bd[i], depth - 1)) return false; } return true; }')
6033+ g.writeln('static inline bool v3_map_map_eq(map a, map b);')
6034+ g.writeln('static inline bool v3_map_value_eq(void* a, void* b, int value_bytes) { if (value_bytes == sizeof(string)) { string sa = *(string*)a; string sb = *(string*)b; return sa.len == sb.len && (sa.len == 0 || memcmp(sa.str, sb.str, sa.len) == 0); } if (value_bytes == sizeof(map)) { return v3_map_map_eq(*(map*)a, *(map*)b); } if (value_bytes == sizeof(string) + sizeof(map)) { string sa = *(string*)a; string sb = *(string*)b; if (!(sa.len == sb.len && (sa.len == 0 || memcmp(sa.str, sb.str, sa.len) == 0))) return false; map ma = *(map*)((u8*)a + sizeof(string)); map mb = *(map*)((u8*)b + sizeof(string)); return v3_map_map_eq(ma, mb); } if (value_bytes == sizeof(Array)) { Array aa = *(Array*)a; Array bb = *(Array*)b; if (aa.element_size != bb.element_size) return false; if (aa.element_size == sizeof(string)) return array_eq_string(aa, bb); if (aa.element_size == sizeof(Array)) return array_eq_array(aa, bb, 8); return array_eq_raw(aa, bb, aa.element_size); } return memcmp(a, b, value_bytes) == 0; }')
6035+ g.writeln('static inline bool v3_map_map_eq(map a, map b) { if (a.len != b.len) return false; for (int i = 0; i < a.key_values.len; ++i) { if (a.key_values.deletes != 0 && a.key_values.all_deleted != 0 && a.key_values.all_deleted[i] != 0) continue; void* ak = (void*)(a.key_values.keys + i * a.key_values.key_bytes); void* av = (void*)(a.key_values.values + i * a.key_values.value_bytes); bool found = false; for (int j = 0; j < b.key_values.len; ++j) { if (b.key_values.deletes != 0 && b.key_values.all_deleted != 0 && b.key_values.all_deleted[j] != 0) continue; void* bk = (void*)(b.key_values.keys + j * b.key_values.key_bytes); if (a.key_eq_fn(ak, bk)) { void* bv = (void*)(b.key_values.values + j * b.key_values.value_bytes); if (!v3_map_value_eq(av, bv, a.value_bytes)) return false; found = true; break; } } if (!found) return false; } return true; }')
55886036 g.writeln('static inline bool fixed_array_contains_string(const string* a, int len, string val) { for (int i = 0; i < len; i++) if (a[i].len == val.len && memcmp(a[i].str, val.str, val.len) == 0) return true; return false; }')
55896037 g.writeln('static inline bool fixed_array_contains_u8(const u8* a, int len, u8 val) { for (int i = 0; i < len; i++) if (a[i] == val) return true; return false; }')
55906038 g.writeln('static inline bool fixed_array_contains_int(const int* a, int len, int val) { for (int i = 0; i < len; i++) if (a[i] == val) return true; return false; }')
@@ -5598,6 +6046,23 @@ fn (mut g FlatGen) builtin_abi_decls() {
55986046 g.writeln('')
55996047}
56006048
6049+fn (mut g FlatGen) filelock_compat_decls() {
6050+ if !g.libc_compat_fns['filelock'] {
6051+ return
6052+ }
6053+ g.writeln('#ifndef V_OS_FILELOCK_HELPERS_H')
6054+ g.writeln('#ifdef _WIN32')
6055+ g.writeln('BOOL LockFileEx(HANDLE handle, DWORD flags, DWORD reserved, DWORD low, DWORD high, OVERLAPPED* overlap);')
6056+ g.writeln('BOOL UnlockFileEx(HANDLE handle, DWORD reserved, DWORD low, DWORD high, OVERLAPPED* overlap);')
6057+ g.writeln('static inline int v_filelock_lock(HANDLE handle, int exclusive, int immediate, u64 start, u64 len) { OVERLAPPED overlap; memset(&overlap, 0, sizeof(overlap)); overlap.Offset = (DWORD)(start & 0xffffffffULL); overlap.OffsetHigh = (DWORD)(start >> 32); DWORD flags = immediate ? LOCKFILE_FAIL_IMMEDIATELY : 0; if (exclusive) { flags |= LOCKFILE_EXCLUSIVE_LOCK; } DWORD low = len == 0 ? MAXDWORD : (DWORD)(len & 0xffffffffULL); DWORD high = len == 0 ? MAXDWORD : (DWORD)(len >> 32); return LockFileEx(handle, flags, 0, low, high, &overlap) ? 0 : -1; }')
6058+ g.writeln('static inline int v_filelock_unlock(HANDLE handle, u64 start, u64 len) { OVERLAPPED overlap; memset(&overlap, 0, sizeof(overlap)); overlap.Offset = (DWORD)(start & 0xffffffffULL); overlap.OffsetHigh = (DWORD)(start >> 32); DWORD low = len == 0 ? MAXDWORD : (DWORD)(len & 0xffffffffULL); DWORD high = len == 0 ? MAXDWORD : (DWORD)(len >> 32); return UnlockFileEx(handle, 0, low, high, &overlap) ? 0 : -1; }')
6059+ g.writeln('#else')
6060+ g.writeln('static inline int v_filelock_lock(int fd, int exclusive, int immediate, u64 start, u64 len) { struct flock fl; memset(&fl, 0, sizeof(fl)); fl.l_type = exclusive ? F_WRLCK : F_RDLCK; fl.l_whence = SEEK_SET; fl.l_start = (off_t)start; fl.l_len = len == 0 ? 0 : (off_t)len; return fcntl(fd, immediate ? F_SETLK : F_SETLKW, &fl); }')
6061+ g.writeln('static inline int v_filelock_unlock(int fd, u64 start, u64 len) { struct flock fl; memset(&fl, 0, sizeof(fl)); fl.l_type = F_UNLCK; fl.l_whence = SEEK_SET; fl.l_start = (off_t)start; fl.l_len = len == 0 ? 0 : (off_t)len; return fcntl(fd, F_SETLK, &fl); }')
6062+ g.writeln('#endif')
6063+ g.writeln('#endif')
6064+}
6065+
56016066fn (mut g FlatGen) collect_fixed_array_typedefs_needed() map[string]FixedArrayTypedefInfo {
56026067 mut needed := map[string]FixedArrayTypedefInfo{}
56036068 old_module := g.tc.cur_module
@@ -5636,6 +6101,16 @@ fn (mut g FlatGen) collect_fixed_array_typedefs_needed() map[string]FixedArrayTy
56366101 g.collect_fixed_array_typedef(typ, mut needed)
56376102 }
56386103 g.tc.cur_module = old_module
6104+ for node in g.a.nodes {
6105+ g.collect_fixed_array_typedef_text(node.typ, mut needed)
6106+ match node.kind {
6107+ .array_init, .array_literal, .cast_expr, .sizeof_expr, .typeof_expr {
6108+ g.collect_fixed_array_typedef_text(node.value, mut needed)
6109+ }
6110+ else {}
6111+ }
6112+ }
6113+ g.tc.cur_module = old_module
56396114 return needed
56406115}
56416116
@@ -5927,6 +6402,62 @@ fn (mut g FlatGen) collect_fixed_array_typedef(typ types.Type, mut needed map[st
59276402 }
59286403}
59296404
6405+fn (mut g FlatGen) collect_fixed_array_typedef_text(type_text string, mut needed map[string]FixedArrayTypedefInfo) {
6406+ clean := type_text.trim_space()
6407+ if clean.len == 0 {
6408+ return
6409+ }
6410+ typ := g.tc.parse_type(clean)
6411+ if fixed_array_typedef_has_non_decimal_len(typ) {
6412+ return
6413+ }
6414+ g.collect_fixed_array_typedef(typ, mut needed)
6415+}
6416+
6417+fn fixed_array_typedef_has_non_decimal_len(typ types.Type) bool {
6418+ if typ is types.ArrayFixed {
6419+ if typ.len_expr.len > 0 {
6420+ return true
6421+ }
6422+ return fixed_array_typedef_has_non_decimal_len(typ.elem_type)
6423+ }
6424+ if typ is types.Pointer {
6425+ return fixed_array_typedef_has_non_decimal_len(typ.base_type)
6426+ }
6427+ if typ is types.Alias {
6428+ return fixed_array_typedef_has_non_decimal_len(typ.base_type)
6429+ }
6430+ if typ is types.OptionType {
6431+ return fixed_array_typedef_has_non_decimal_len(typ.base_type)
6432+ }
6433+ if typ is types.ResultType {
6434+ return fixed_array_typedef_has_non_decimal_len(typ.base_type)
6435+ }
6436+ if typ is types.Array {
6437+ return fixed_array_typedef_has_non_decimal_len(typ.elem_type)
6438+ }
6439+ if typ is types.Map {
6440+ return fixed_array_typedef_has_non_decimal_len(typ.key_type)
6441+ || fixed_array_typedef_has_non_decimal_len(typ.value_type)
6442+ }
6443+ if typ is types.FnType {
6444+ for param in typ.params {
6445+ if fixed_array_typedef_has_non_decimal_len(param) {
6446+ return true
6447+ }
6448+ }
6449+ return fixed_array_typedef_has_non_decimal_len(typ.return_type)
6450+ }
6451+ if typ is types.MultiReturn {
6452+ for item in typ.types {
6453+ if fixed_array_typedef_has_non_decimal_len(item) {
6454+ return true
6455+ }
6456+ }
6457+ }
6458+ return false
6459+}
6460+
59306461fn (mut g FlatGen) emit_fixed_array_typedef(name string, info FixedArrayTypedefInfo, needed map[string]FixedArrayTypedefInfo, mut emitted map[string]bool) {
59316462 if emitted[name] {
59326463 return
@@ -5961,10 +6492,13 @@ fn (mut g FlatGen) global_decls() {
59616492 g.writeln('${c_elem} ${c_name(name)}${dims}${init};')
59626493 continue
59636494 }
5964- ct := g.tc.c_type(typ)
6495+ mut ct := g.tc.c_type(typ)
59656496 if ct == 'void' {
59666497 continue
59676498 }
6499+ if ct.starts_with('fn_ptr:') {
6500+ ct = g.resolve_fn_ptr_type(ct)
6501+ }
59686502 if name.starts_with('C.') {
59696503 continue
59706504 }
@@ -6031,7 +6565,7 @@ fn (mut g FlatGen) emit_global_inits() {
60316565 continue
60326566 }
60336567 target := c_name(qname)
6034- g.runtime_inits << '\t${target} = ${expr_str};'
6568+ g.queue_runtime_init('\t${target} = ${expr_str};')
60356569 if typ := g.global_types[qname] {
60366570 if typ is types.Map {
60376571 g.queue_map_literal_sets(target, val_id, typ)
@@ -6120,7 +6654,16 @@ fn (mut g FlatGen) emit_const(name string, val_id flat.NodeId) {
61206654 g.tc.cur_module = old_module
61216655 return
61226656 }
6123- expr_str := if g.is_const_expr(val_id) {
6657+ v_type := if val_node.kind == .offsetof_expr {
6658+ types.Type(types.usize_)
6659+ } else {
6660+ g.tc.resolve_type(val_id)
6661+ }
6662+ ct := g.tc.c_type(v_type)
6663+ qname := g.const_ident_c_name(name)
6664+ expr_str := if v_type is types.Array && val_node.kind == .array_literal {
6665+ g.expr_to_string_with_expected_type(val_id, v_type)
6666+ } else if g.is_const_expr(val_id) {
61246667 g.const_expr_to_string(val_id, []string{})
61256668 } else {
61266669 g.expr_to_string(val_id)
@@ -6129,14 +6672,10 @@ fn (mut g FlatGen) emit_const(name string, val_id flat.NodeId) {
61296672 g.tc.cur_module = old_module
61306673 return
61316674 }
6132- v_type := if val_node.kind == .offsetof_expr {
6133- types.Type(types.usize_)
6134- } else {
6135- g.tc.resolve_type(val_id)
6136- }
6137- ct := g.tc.c_type(v_type)
6138- qname := g.const_ident_c_name(name)
61396675 mut is_static_const := g.is_const_expr(val_id) && !g.const_expr_needs_runtime_storage(expr_str)
6676+ if v_type is types.Array {
6677+ is_static_const = false
6678+ }
61406679 if v_type is types.ArrayFixed && v_type.elem_type is types.ArrayFixed {
61416680 is_static_const = false
61426681 }
@@ -6144,15 +6683,15 @@ fn (mut g FlatGen) emit_const(name string, val_id flat.NodeId) {
61446683 if v_type is types.ArrayFixed {
61456684 c_elem, dims := g.fixed_array_decl_parts(v_type)
61466685 g.writeln('${c_elem} ${qname}${dims};')
6147- g.queue_fixed_array_runtime_init(qname, val_id, v_type)
6686+ g.queue_const_fixed_array_runtime_init(qname, val_id, v_type)
61486687 } else if ct != 'void' {
61496688 g.writeln('${ct} ${qname};')
61506689 // The initializer is not a compile-time constant (e.g. `os.args =
61516690 // arguments()`), so it cannot be a C static initializer. Run it at startup
61526691 // in _vinit; otherwise the const stays zero/empty and first use is wrong.
6153- g.runtime_inits << '\t${qname} = ${expr_str};'
6692+ g.queue_const_runtime_init('\t${qname} = ${expr_str};')
61546693 if v_type is types.Map {
6155- g.queue_map_literal_sets(qname, val_id, v_type)
6694+ g.queue_const_map_literal_sets(qname, val_id, v_type)
61566695 }
61576696 }
61586697 g.tc.cur_module = old_module
@@ -6215,7 +6754,24 @@ fn (mut g FlatGen) queue_map_literal_sets(target string, val_id flat.NodeId, map
62156754 for i := 0; i + 1 < node.children_count; i += 2 {
62166755 key := g.expr_to_string_with_expected_type(g.a.child(&node, i), map_type.key_type)
62176756 val := g.expr_to_string_with_expected_type(g.a.child(&node, i + 1), map_type.value_type)
6218- g.runtime_inits << '\tmap__set(&${target}, &(${c_key}[]){${key}}, &(${c_val}[]){${val}});'
6757+ g.queue_runtime_init('\tmap__set(&${target}, &(${c_key}[]){${key}}, &(${c_val}[]){${val}});')
6758+ }
6759+}
6760+
6761+fn (mut g FlatGen) queue_const_map_literal_sets(target string, val_id flat.NodeId, map_type types.Map) {
6762+ if int(val_id) < 0 || int(val_id) >= g.a.nodes.len {
6763+ return
6764+ }
6765+ node := g.a.nodes[int(val_id)]
6766+ if node.kind != .map_init {
6767+ return
6768+ }
6769+ c_key := g.tc.c_type(map_type.key_type)
6770+ c_val := g.tc.c_type(map_type.value_type)
6771+ for i := 0; i + 1 < node.children_count; i += 2 {
6772+ key := g.expr_to_string_with_expected_type(g.a.child(&node, i), map_type.key_type)
6773+ val := g.expr_to_string_with_expected_type(g.a.child(&node, i + 1), map_type.value_type)
6774+ g.queue_const_runtime_init('\tmap__set(&${target}, &(${c_key}[]){${key}}, &(${c_val}[]){${val}});')
62196775 }
62206776}
62216777
@@ -6224,7 +6780,16 @@ fn (mut g FlatGen) queue_fixed_array_runtime_init(target string, val_id flat.Nod
62246780 if expr.trim_space().len == 0 {
62256781 return false
62266782 }
6227- g.runtime_inits << '\tmemmove(${target}, ${expr}, sizeof(${target}));'
6783+ g.queue_runtime_init('\tmemmove(${target}, ${expr}, sizeof(${target}));')
6784+ return true
6785+}
6786+
6787+fn (mut g FlatGen) queue_const_fixed_array_runtime_init(target string, val_id flat.NodeId, fixed types.ArrayFixed) bool {
6788+ expr := g.fixed_array_compound_literal_expr(val_id, fixed)
6789+ if expr.trim_space().len == 0 {
6790+ return false
6791+ }
6792+ g.queue_const_runtime_init('\tmemmove(${target}, ${expr}, sizeof(${target}));')
62286793 return true
62296794}
62306795
@@ -6295,6 +6860,7 @@ fn (mut g FlatGen) precompute_consts() string {
62956860 }
62966861 names << name
62976862 }
6863+ names = g.ordered_const_init_names(names)
62986864 for name in names {
62996865 val_id := g.const_vals[name] or { continue }
63006866 if g.is_const_alias_name(name) {
@@ -6361,6 +6927,52 @@ fn (mut g FlatGen) precompute_consts() string {
63616927 return result
63626928}
63636929
6930+fn (g &FlatGen) ordered_const_init_names(names []string) []string {
6931+ mut names_by_module := map[string][]string{}
6932+ mut module_order := []string{}
6933+ for name in names {
6934+ mod := g.const_modules[name] or { '' }
6935+ if mod !in names_by_module {
6936+ names_by_module[mod] = []string{}
6937+ module_order << mod
6938+ }
6939+ names_by_module[mod] << name
6940+ }
6941+ mut result := []string{}
6942+ mut visiting := map[string]bool{}
6943+ mut visited := map[string]bool{}
6944+ for mod in module_order {
6945+ g.visit_const_init_module(mod, names_by_module, mut visiting, mut visited, mut result)
6946+ }
6947+ return result
6948+}
6949+
6950+fn (g &FlatGen) visit_const_init_module(mod string, names_by_module map[string][]string, mut visiting map[string]bool, mut visited map[string]bool, mut result []string) {
6951+ if mod in visited || mod in visiting {
6952+ return
6953+ }
6954+ visiting[mod] = true
6955+ for dep in g.module_imports[mod] or { []string{} } {
6956+ dep_module := startup_module_key(dep)
6957+ if dep_module in names_by_module {
6958+ g.visit_const_init_module(dep_module, names_by_module, mut visiting, mut visited, mut
6959+ result)
6960+ }
6961+ }
6962+ visiting.delete(mod)
6963+ visited[mod] = true
6964+ if module_names := names_by_module[mod] {
6965+ result << module_names
6966+ }
6967+}
6968+
6969+fn startup_module_key(mod string) string {
6970+ if mod.contains('.') {
6971+ return mod.all_after_last('.')
6972+ }
6973+ return mod
6974+}
6975+
63646976fn (g &FlatGen) is_const_expr(id flat.NodeId) bool {
63656977 if int(id) < 0 || int(id) >= g.a.nodes.len {
63666978 return false
@@ -26,6 +26,7 @@ struct TopLevelStmt {
2626
2727// gen_fns emits fns output for c.
2828fn (mut g FlatGen) gen_fns() {
29+ preferred_fns := g.preferred_c_backend_fn_nodes()
2930 mut cur_module := ''
3031 mut cur_file := ''
3132 for i in 0 .. g.a.nodes.len {
@@ -46,10 +47,15 @@ fn (mut g FlatGen) gen_fns() {
4647 }
4748
4849 if kind_id == 61 {
49- if !g.should_emit_fn_node_in_module(node, i, cur_module) {
50+ if !g.should_emit_fn_node_in_module(node, i, cur_module, cur_file) {
5051 continue
5152 }
5253 qfn := g.fn_c_name_in_module(cur_module, node.value)
54+ if preferred_idx := preferred_fns[qfn] {
55+ if preferred_idx != i {
56+ continue
57+ }
58+ }
5359 if g.emitted_fn_contains(qfn) {
5460 continue
5561 }
@@ -61,6 +67,43 @@ fn (mut g FlatGen) gen_fns() {
6167 }
6268}
6369
70+fn (g &FlatGen) preferred_c_backend_fn_nodes() map[string]int {
71+ mut preferred := map[string]int{}
72+ mut ranks := map[string]int{}
73+ mut cur_module := ''
74+ mut cur_file := ''
75+ for i in 0 .. g.a.nodes.len {
76+ node := g.a.nodes[i]
77+ kind_id := node_kind_id(node)
78+ if kind_id == 77 {
79+ cur_file = node.value
80+ cur_module = ''
81+ continue
82+ }
83+ if kind_id == 73 {
84+ cur_module = node.value
85+ continue
86+ }
87+ if kind_id != 61 {
88+ continue
89+ }
90+ qfn := qualified_fn_name_in_module(cur_module, node.value)
91+ rank := c_backend_fn_file_rank(cur_file)
92+ if qfn !in preferred || rank > ranks[qfn] {
93+ preferred[qfn] = i
94+ ranks[qfn] = rank
95+ }
96+ }
97+ return preferred
98+}
99+
100+fn c_backend_fn_file_rank(file string) int {
101+ if file.ends_with('.c.v') {
102+ return 1
103+ }
104+ return 0
105+}
106+
64107fn (mut g FlatGen) gen_synthetic_main_after_fns() {
65108 if g.test_files.len > 0 {
66109 g.gen_test_main()
@@ -162,11 +205,11 @@ fn (g &FlatGen) cgen_is_top_level_stmt(id flat.NodeId) bool {
162205
163206// should_emit_fn_node reports whether should emit fn node applies in c.
164207fn (mut g FlatGen) should_emit_fn_node(node flat.Node, node_index int) bool {
165- return g.should_emit_fn_node_in_module(node, node_index, g.tc.cur_module)
208+ return g.should_emit_fn_node_in_module(node, node_index, g.tc.cur_module, g.tc.cur_file)
166209}
167210
168211// should_emit_fn_node_in_module reports whether should emit fn node in module applies in c.
169-fn (mut g FlatGen) should_emit_fn_node_in_module(node flat.Node, node_index int, module_name string) bool {
212+fn (mut g FlatGen) should_emit_fn_node_in_module(node flat.Node, node_index int, module_name string, file_name string) bool {
170213 _ = node_index
171214 qfn := qualified_fn_name_in_module(module_name, node.value)
172215 if g.should_rename_user_main_for_tests(module_name, node.value) {
@@ -183,12 +226,24 @@ fn (mut g FlatGen) should_emit_fn_node_in_module(node flat.Node, node_index int,
183226 if node.value.starts_with('__anon_fn_') || qfn.contains('__anon_fn_') {
184227 return true
185228 }
229+ if node.generic_params.len == 0 && cgen_is_operator_overload_fn(node.value)
230+ && g.test_files[file_name] {
231+ return true
232+ }
186233 if g.has_used_fn_filter() && !g.used_fn_contains_in_module(node.value, module_name) {
187234 return false
188235 }
189236 return true
190237}
191238
239+fn cgen_is_operator_overload_fn(name string) bool {
240+ if !name.contains('.') {
241+ return false
242+ }
243+ method := name.all_after_last('.')
244+ return method in ['+', '-', '*', '/', '%', '==', '!=', '<', '>', '<=', '>=']
245+}
246+
192247fn is_generated_fn_after_markused(name string) bool {
193248 return name.starts_with('__anon_fn_')
194249}
@@ -217,6 +272,9 @@ fn (g &FlatGen) used_fn_contains_in_module(name string, module_name string) bool
217272 if g.used_fn_contains(dfn) || g.used_fn_contains(qfn) {
218273 return true
219274 }
275+ if g.used_fn_contains(c_name(dfn)) || g.used_fn_contains(c_name(qfn)) {
276+ return true
277+ }
220278 if module_name.len == 0 || module_name == 'main' || module_name == 'builtin' {
221279 cfn := c_name(name)
222280 return g.used_fn_contains(name) || g.used_fn_contains(cfn)
@@ -339,6 +397,12 @@ fn (mut g FlatGen) direct_call_name(name string) string {
339397 if name == 'bool_str' {
340398 return 'bool__str'
341399 }
400+ if name == 'char.vstring' {
401+ return 'charptr__vstring'
402+ }
403+ if name == 'char.vstring_with_len' {
404+ return 'charptr__vstring_with_len'
405+ }
342406 return c_name(name)
343407}
344408
@@ -351,6 +415,9 @@ fn (mut g FlatGen) direct_call_name_for_call(id flat.NodeId, name string) string
351415 }
352416 return c_name(name)
353417 }
418+ if specialized := g.specialized_generic_method_name_for_call_with_arg_count(id, name, -1) {
419+ return c_name(specialized)
420+ }
354421 return g.direct_call_name(name)
355422}
356423
@@ -449,11 +516,75 @@ fn (mut g FlatGen) gen_fn(node flat.Node) {
449516}
450517
451518fn (mut g FlatGen) write_method_c_name(id flat.NodeId, node flat.Node, method_name string) {
452- _ = id
453- _ = node
519+ if specialized := g.specialized_generic_method_name_for_call_with_arg_count(id, method_name,
520+ int(node.children_count))
521+ {
522+ g.write(c_name(specialized))
523+ return
524+ }
454525 g.write(c_name(method_name))
455526}
456527
528+fn (g &FlatGen) specialized_generic_method_name_for_call_with_arg_count(id flat.NodeId, method_name string, arg_count int) ?string {
529+ if !method_name.contains('.') {
530+ return none
531+ }
532+ receiver := method_name.all_before_last('.')
533+ method := method_name.all_after_last('.')
534+ if receiver.len == 0 || method.len == 0 {
535+ return none
536+ }
537+ call_ret := g.call_default_return_type(id)
538+ if type_arg := generic_method_type_arg_from_return(call_ret) {
539+ for arg in [type_arg, type_arg.all_after_last('.')] {
540+ for candidate in ['${receiver}[${arg}].${method}',
541+ '${receiver.all_after_last('.')}[${arg}].${method}'] {
542+ if candidate in g.tc.fn_param_types || candidate in g.tc.fn_ret_types {
543+ return candidate
544+ }
545+ }
546+ }
547+ }
548+ for allow_short_receiver in [false, true] {
549+ for candidate, ret in g.tc.fn_ret_types {
550+ if !candidate.contains('[') || candidate.all_after_last('.') != method {
551+ continue
552+ }
553+ candidate_receiver := candidate.all_before_last('.')
554+ base_receiver := candidate_receiver.all_before('[')
555+ receiver_matches := base_receiver == receiver || (allow_short_receiver
556+ && base_receiver.all_after_last('.') == receiver.all_after_last('.'))
557+ if !receiver_matches {
558+ continue
559+ }
560+ params := g.tc.fn_param_types[candidate] or { []types.Type{} }
561+ if arg_count >= 0 && params.len != arg_count {
562+ continue
563+ }
564+ if g.type_names_match(call_ret, ret) || call_ret.name() == ret.name() {
565+ return candidate
566+ }
567+ }
568+ }
569+ return none
570+}
571+
572+fn generic_method_type_arg_from_return(ret types.Type) ?string {
573+ if ret is types.ResultType {
574+ name := ret.base_type.name()
575+ if name.len > 0 && name != 'void' {
576+ return name
577+ }
578+ }
579+ if ret is types.OptionType {
580+ name := ret.base_type.name()
581+ if name.len > 0 && name != 'void' {
582+ return name
583+ }
584+ }
585+ return none
586+}
587+
457588// static_method_fn_name resolves `Type.method(...)` static calls where `Type` is a
458589// named type, struct, enum, sum type or type alias (e.g. `SimdFloat4.new` for
459590// `type SimdFloat4 = vec.Vec4[f32]`). Returns the fn key, or none if `type_ident`
@@ -510,6 +641,74 @@ fn c_string_literal_pointer_arg(arg_node flat.Node, expected types.Type) bool {
510641 return false
511642}
512643
644+fn (mut g FlatGen) gen_pointer_builtin_method_call(node flat.Node, fn_node &flat.Node, base_type types.Type) bool {
645+ receiver := pointer_builtin_receiver_name_for_c(base_type)
646+ if receiver.len == 0 {
647+ return false
648+ }
649+ method := fn_node.value
650+ if receiver in ['charptr', 'byteptr'] && method in ['vstring', 'vstring_with_len'] {
651+ g.write(c_name('${receiver}.${method}'))
652+ g.write('(')
653+ g.gen_expr(g.a.child(fn_node, 0))
654+ for i in 1 .. node.children_count {
655+ g.write(', ')
656+ g.gen_expr(g.a.child(&node, i))
657+ }
658+ g.write(')')
659+ return true
660+ }
661+ if receiver in ['byteptr', 'voidptr'] && method == 'vbytes' {
662+ g.write(c_name('${receiver}.${method}'))
663+ g.write('(')
664+ g.gen_expr(g.a.child(fn_node, 0))
665+ for i in 1 .. node.children_count {
666+ g.write(', ')
667+ g.gen_expr(g.a.child(&node, i))
668+ }
669+ g.write(')')
670+ return true
671+ }
672+ return false
673+}
674+
675+fn pointer_builtin_receiver_name_for_c(typ types.Type) string {
676+ if typ is types.Alias {
677+ if typ.name in ['charptr', 'byteptr', 'voidptr'] {
678+ return typ.name
679+ }
680+ return pointer_builtin_receiver_name_for_c(typ.base_type)
681+ }
682+ if typ is types.Pointer {
683+ base := typ.base_type
684+ if base is types.Alias {
685+ if base.name == 'byte' {
686+ return 'byteptr'
687+ }
688+ return pointer_builtin_receiver_name_for_c(base)
689+ }
690+ if base is types.Char {
691+ return 'charptr'
692+ }
693+ if base is types.Void {
694+ return 'voidptr'
695+ }
696+ if base is types.Primitive && base.name() == 'u8' {
697+ return 'byteptr'
698+ }
699+ }
700+ if typ is types.Char {
701+ return 'charptr'
702+ }
703+ if typ is types.Void {
704+ return 'voidptr'
705+ }
706+ if typ is types.Primitive && typ.name() == 'u8' {
707+ return 'byteptr'
708+ }
709+ return ''
710+}
711+
513712fn (mut g FlatGen) gen_special_c_callback_arg(fn_name string, arg_idx int, arg_id flat.NodeId, expected_param types.Type) bool {
514713 clean_name := fn_name.trim_string_left('C.').all_after_last('.')
515714 if clean_name == 'mbedtls_ssl_conf_sni' && arg_idx == 1 {
@@ -777,7 +976,7 @@ fn (mut g FlatGen) gen_spawn_expr(node flat.Node) {
777976 } else if fn_node.kind == .selector && fn_node.children_count > 0 {
778977 base_id := g.a.child(fn_node, 0)
779978 base_type := g.receiver_base_type(base_id)
780- clean_type := types.unwrap_pointer(base_type)
979+ clean_type := concrete_receiver_type(base_type)
781980 method_name := g.resolved_method_name_for_spawn(clean_type, fn_node.value)
782981 if method_name.len > 0 {
783982 param_types := g.param_types_for(method_name, fn_node.value)
@@ -954,9 +1153,11 @@ fn (mut g FlatGen) gen_fn_in_module(node flat.Node, module_name string) {
9541153 old_param_names := g.cur_param_names.clone()
9551154 old_param_type_values := g.cur_param_type_values.clone()
9561155 old_param_types := g.cur_param_types.clone()
1156+ old_mut_params := g.cur_mut_params.clone()
9571157 g.cur_param_names = []string{}
9581158 g.cur_param_type_values = []types.Type{}
9591159 g.cur_param_types = map[string]types.Type{}
1160+ g.cur_mut_params = map[string]bool{}
9601161 for i in 0 .. node.children_count {
9611162 param_id := g.a.child(&node, i)
9621163 p := g.a.node(param_id)
@@ -965,6 +1166,9 @@ fn (mut g FlatGen) gen_fn_in_module(node flat.Node, module_name string) {
9651166 g.cur_param_names << p.value
9661167 g.cur_param_type_values << param_type
9671168 g.cur_param_types[p.value] = param_type
1169+ if p.op == .amp {
1170+ g.cur_mut_params[p.value] = true
1171+ }
9681172 g.tc.cur_scope.insert(p.value, param_type)
9691173 }
9701174 }
@@ -979,7 +1183,8 @@ fn (mut g FlatGen) gen_fn_in_module(node flat.Node, module_name string) {
9791183 g.writeln('\tg_main_argv = argv;')
9801184 }
9811185 g.gen_compiler_vexe_env_setup()
982- if g.runtime_inits.len > 0 || g.module_init_fns.len > 0 || g.global_inits.len > 0 {
1186+ if g.const_runtime_inits.len > 0 || g.runtime_inits.len > 0 || g.module_init_fns.len > 0
1187+ || g.global_inits.len > 0 {
9831188 g.writeln('\t_vinit();')
9841189 }
9851190 } else {
@@ -1019,6 +1224,7 @@ fn (mut g FlatGen) gen_fn_in_module(node flat.Node, module_name string) {
10191224 g.cur_param_names = old_param_names.clone()
10201225 g.cur_param_type_values = old_param_type_values.clone()
10211226 g.cur_param_types = old_param_types.clone()
1227+ g.cur_mut_params = old_mut_params.clone()
10221228 g.tc.pop_scope()
10231229}
10241230
@@ -1089,9 +1295,11 @@ fn (mut g FlatGen) gen_top_level_main(stmts []TopLevelStmt) {
10891295 old_param_names := g.cur_param_names.clone()
10901296 old_param_type_values := g.cur_param_type_values.clone()
10911297 old_param_types := g.cur_param_types.clone()
1298+ old_mut_params := g.cur_mut_params.clone()
10921299 g.cur_param_names = []string{}
10931300 g.cur_param_type_values = []types.Type{}
10941301 g.cur_param_types = map[string]types.Type{}
1302+ g.cur_mut_params = map[string]bool{}
10951303 mut fn_defer_ids := []flat.NodeId{}
10961304 for stmt in stmts {
10971305 g.collect_function_defer_ids_from(stmt.id, mut fn_defer_ids)
@@ -1103,7 +1311,8 @@ fn (mut g FlatGen) gen_top_level_main(stmts []TopLevelStmt) {
11031311 g.writeln('\tg_main_argv = argv;')
11041312 }
11051313 g.gen_compiler_vexe_env_setup()
1106- if g.runtime_inits.len > 0 || g.module_init_fns.len > 0 || g.global_inits.len > 0 {
1314+ if g.const_runtime_inits.len > 0 || g.runtime_inits.len > 0 || g.module_init_fns.len > 0
1315+ || g.global_inits.len > 0 {
11071316 g.writeln('\t_vinit();')
11081317 }
11091318 g.indent++
@@ -1121,6 +1330,7 @@ fn (mut g FlatGen) gen_top_level_main(stmts []TopLevelStmt) {
11211330 g.cur_param_names = old_param_names.clone()
11221331 g.cur_param_type_values = old_param_type_values.clone()
11231332 g.cur_param_types = old_param_types.clone()
1333+ g.cur_mut_params = old_mut_params.clone()
11241334 g.cur_fn_name = old_fn_name
11251335 g.tc.cur_file = old_tc_file
11261336 g.tc.cur_module = old_tc_module
@@ -1153,7 +1363,8 @@ fn (mut g FlatGen) gen_test_main() {
11531363 g.writeln('\tg_main_argv = argv;')
11541364 }
11551365 g.gen_compiler_vexe_env_setup()
1156- if g.runtime_inits.len > 0 || g.module_init_fns.len > 0 || g.global_inits.len > 0 {
1366+ if g.const_runtime_inits.len > 0 || g.runtime_inits.len > 0 || g.module_init_fns.len > 0
1367+ || g.global_inits.len > 0 {
11571368 g.writeln('\t_vinit();')
11581369 }
11591370 g.indent++
@@ -1208,9 +1419,6 @@ fn (g &FlatGen) test_harness_fns() ([]TestHarnessFn, TestHarnessHooks) {
12081419 continue
12091420 }
12101421 module_name := g.test_file_module_name(file_node)
1211- if module_name.len > 0 && module_name != 'main' {
1212- continue
1213- }
12141422 mut decl_ids := []flat.NodeId{}
12151423 g.collect_test_harness_decl_ids(file_node, mut decl_ids)
12161424 for child_id in decl_ids {
@@ -1576,7 +1784,7 @@ fn (mut g FlatGen) gen_call(id flat.NodeId, node flat.Node) {
15761784 }
15771785 if fn_node.kind == .selector && fn_node.value == 'str' {
15781786 base_type := g.tc.resolve_type(g.a.child(fn_node, 0))
1579- clean_type := types.unwrap_pointer(base_type)
1787+ clean_type := concrete_receiver_type(base_type)
15801788 if clean_type is types.Enum {
15811789 if _ := g.enum_receiver_method_name(clean_type, fn_node.value) {
15821790 // Let normal method call generation handle custom enum str methods.
@@ -1594,6 +1802,40 @@ fn (mut g FlatGen) gen_call(id flat.NodeId, node flat.Node) {
15941802 }
15951803 }
15961804 match fn_name {
1805+ 'array_new' {
1806+ g.write('array_new(')
1807+ for i in 1 .. node.children_count {
1808+ if i > 1 {
1809+ g.write(', ')
1810+ }
1811+ arg_id := g.a.child(&node, i)
1812+ arg_node := g.a.nodes[int(arg_id)]
1813+ if i == 1 {
1814+ arg_expr := g.expr_to_string(arg_id)
1815+ if raw_sizeof := raw_sizeof_arg_value(arg_expr) {
1816+ if raw_sizeof_needs_normalization(raw_sizeof) {
1817+ g.write('sizeof(${g.sizeof_target(raw_sizeof)})')
1818+ } else {
1819+ g.write(arg_expr)
1820+ }
1821+ } else {
1822+ g.write(arg_expr)
1823+ }
1824+ } else if arg_node.kind == .sizeof_expr {
1825+ g.write('sizeof(${g.sizeof_target(arg_node.value)})')
1826+ } else if raw_sizeof := raw_sizeof_arg_value(arg_node.value) {
1827+ if raw_sizeof_needs_normalization(raw_sizeof) {
1828+ g.write('sizeof(${g.sizeof_target(raw_sizeof)})')
1829+ } else {
1830+ g.gen_expr(arg_id)
1831+ }
1832+ } else {
1833+ g.gen_expr(arg_id)
1834+ }
1835+ }
1836+ g.write(')')
1837+ return
1838+ }
15971839 'new_map' {
15981840 if node.typ.starts_with('map[') {
15991841 map_type := g.tc.parse_type(node.typ)
@@ -1718,7 +1960,7 @@ fn (mut g FlatGen) gen_call(id flat.NodeId, node flat.Node) {
17181960 }
17191961 {
17201962 base_type := g.tc.resolve_type(g.a.child(fn_node, 0))
1721- clean_type := types.unwrap_pointer(base_type)
1963+ clean_type := concrete_receiver_type(base_type)
17221964 if g.gen_interface_method_call(node, fn_node, base_type) {
17231965 return
17241966 }
@@ -1736,15 +1978,22 @@ fn (mut g FlatGen) gen_call(id flat.NodeId, node flat.Node) {
17361978 g.write(', ${len_expr})')
17371979 return
17381980 }
1739- if clean_type is types.Map && fn_node.value == 'clone' {
1740- g.write('map__clone(')
1741- g.gen_map_ref_arg(g.a.child(fn_node, 0), base_type)
1742- g.write(')')
1981+ if g.gen_pointer_builtin_method_call(node, fn_node, base_type) {
17431982 return
17441983 }
1745- if clean_type is types.Map
1746- && fn_node.value in ['delete', 'clear', 'free', 'move', 'reserve'] {
1747- panic('map method `${fn_node.value}` should be lowered by v3 transform')
1984+ if clean_type is types.Map {
1985+ if fn_node.value == 'str' {
1986+ g.gen_map_str_expr(g.a.child(fn_node, 0), base_type)
1987+ return
1988+ } else if fn_node.value == 'clone' {
1989+ g.write('map__clone(')
1990+ g.gen_map_ref_arg(g.a.child(fn_node, 0), base_type)
1991+ g.write(')')
1992+ return
1993+ } else if fn_node.value in ['keys', 'values', 'delete', 'clear', 'free',
1994+ 'move', 'reserve'] {
1995+ panic('map method `${fn_node.value}` should be lowered by v3 transform')
1996+ }
17481997 }
17491998 if clean_type is types.String {
17501999 method_name = 'string.${fn_node.value}'
@@ -1857,7 +2106,7 @@ fn (mut g FlatGen) gen_call(id flat.NodeId, node flat.Node) {
18572106 return
18582107 } else {
18592108 base_type := g.tc.resolve_type(g.a.child(fn_node, 0))
1860- clean_type := types.unwrap_pointer(base_type)
2109+ clean_type := concrete_receiver_type(base_type)
18612110 if g.gen_interface_method_call(node, fn_node, base_type) {
18622111 return
18632112 }
@@ -1875,15 +2124,22 @@ fn (mut g FlatGen) gen_call(id flat.NodeId, node flat.Node) {
18752124 g.write(', ${len_expr})')
18762125 return
18772126 }
1878- if clean_type is types.Map && fn_node.value == 'clone' {
1879- g.write('map__clone(')
1880- g.gen_map_ref_arg(g.a.child(fn_node, 0), base_type)
1881- g.write(')')
2127+ if g.gen_pointer_builtin_method_call(node, fn_node, base_type) {
18822128 return
18832129 }
1884- if clean_type is types.Map
1885- && fn_node.value in ['delete', 'clear', 'free', 'move', 'reserve'] {
1886- panic('map method `${fn_node.value}` should be lowered by v3 transform')
2130+ if clean_type is types.Map {
2131+ if fn_node.value == 'str' {
2132+ g.gen_map_str_expr(g.a.child(fn_node, 0), base_type)
2133+ return
2134+ } else if fn_node.value == 'clone' {
2135+ g.write('map__clone(')
2136+ g.gen_map_ref_arg(g.a.child(fn_node, 0), base_type)
2137+ g.write(')')
2138+ return
2139+ } else if fn_node.value in ['keys', 'values', 'delete', 'clear', 'free',
2140+ 'move', 'reserve'] {
2141+ panic('map method `${fn_node.value}` should be lowered by v3 transform')
2142+ }
18872143 }
18882144 if clean_type is types.String {
18892145 method_name = 'string.${fn_node.value}'
@@ -2072,6 +2328,10 @@ fn (mut g FlatGen) gen_call(id flat.NodeId, node flat.Node) {
20722328 g.write(c_name(fn_ident.value))
20732329 } else if call_key in g.tc.fn_ret_types || call_key in g.tc.fn_param_types {
20742330 g.write(g.direct_call_name_for_call(id, call_key))
2331+ } else if specialized := g.specialized_generic_method_name_for_call_with_arg_count(id,
2332+ fn_ident.value, -1)
2333+ {
2334+ g.write(c_name(specialized))
20752335 } else {
20762336 g.write(c_name(fn_ident.value))
20772337 }
@@ -2211,6 +2471,10 @@ fn (mut g FlatGen) gen_call(id flat.NodeId, node flat.Node) {
22112471 if variadic_idx >= 0 && arg_idx == variadic_idx {
22122472 variadic_type := param_types[variadic_idx]
22132473 if variadic_type is types.Array {
2474+ if spread_id := g.spread_arg_child(arg_node) {
2475+ g.gen_expr_with_expected_type(spread_id, variadic_type)
2476+ continue
2477+ }
22142478 if num_call_args > param_types.len {
22152479 c_elem := g.tc.c_type(variadic_type.elem_type)
22162480 count := num_call_args - variadic_idx
@@ -2266,7 +2530,14 @@ fn (mut g FlatGen) gen_call(id flat.NodeId, node flat.Node) {
22662530 }
22672531 is_rvalue := arg_node.kind == .call
22682532 || (arg_node.kind == .index && arg_node.value == 'range')
2269- if needs_addr && is_rvalue {
2533+ || g.arg_is_const_ident(arg_node)
2534+ if needs_addr && g.arg_is_const_ident(arg_node) {
2535+ pt := param_types[arg_idx]
2536+ ct := g.tc.c_type(types.unwrap_pointer(pt))
2537+ g.write('(${ct}[]){')
2538+ g.gen_expr_with_expected_type(arg_id, types.unwrap_pointer(pt))
2539+ g.write('}')
2540+ } else if needs_addr && is_rvalue {
22702541 pt := param_types[arg_idx]
22712542 ct := g.tc.c_type(types.unwrap_pointer(pt))
22722543 g.write('({${ct} _t${g.tmp_count} = ')
@@ -2345,6 +2616,14 @@ fn (g &FlatGen) receiver_base_type(base_id flat.NodeId) types.Type {
23452616 return g.tc.resolve_type(base_id)
23462617}
23472618
2619+fn concrete_receiver_type(base_type types.Type) types.Type {
2620+ clean_type := types.unwrap_pointer(base_type)
2621+ if clean_type is types.Alias {
2622+ return clean_type.base_type
2623+ }
2624+ return clean_type
2625+}
2626+
23482627fn (g &FlatGen) receiver_storage_type(base_id flat.NodeId) ?types.Type {
23492628 if int(base_id) < 0 || int(base_id) >= g.a.nodes.len {
23502629 return none
@@ -2725,6 +3004,11 @@ fn (g &FlatGen) current_param_type(name string) ?types.Type {
27253004 return none
27263005}
27273006
3007+// current_param_is_mut returns true when a current param originated from `mut name T`.
3008+fn (g &FlatGen) current_param_is_mut(name string) bool {
3009+ return g.cur_mut_params[name] or { false }
3010+}
3011+
27283012// gen_enum_str_call emits an explicit `enum_val.str()` (with no user-defined `str`)
27293013// by routing to the compiler-synthesized `<Enum>__autostr`, so it matches `${enum}`
27303014// interpolation exactly — including `[flag]` enums' `Enum{.a | .b}` form, which the
@@ -3466,6 +3750,15 @@ fn (mut g FlatGen) gen_call_args(fn_name string, node flat.Node, start int) {
34663750 if g.gen_special_c_callback_arg(fn_name, arg_idx, arg_id, cb_param) {
34673751 continue
34683752 }
3753+ if variadic_idx >= 0 && arg_idx == variadic_idx {
3754+ variadic_type := param_types[variadic_idx]
3755+ if variadic_type is types.Array {
3756+ if spread_id := g.spread_arg_child(arg_node) {
3757+ g.gen_expr_with_expected_type(spread_id, variadic_type)
3758+ continue
3759+ }
3760+ }
3761+ }
34693762 if is_variadic && arg_idx == variadic_idx {
34703763 variadic_type := param_types[variadic_idx]
34713764 if variadic_type is types.Array {
@@ -3506,7 +3799,14 @@ fn (mut g FlatGen) gen_call_args(fn_name string, node flat.Node, start int) {
35063799 }
35073800 is_rvalue := arg_node.kind == .call
35083801 || (arg_node.kind == .index && arg_node.value == 'range')
3509- if needs_addr && is_rvalue {
3802+ || g.arg_is_const_ident(arg_node)
3803+ if needs_addr && g.arg_is_const_ident(arg_node) {
3804+ pt := param_types[arg_idx]
3805+ ct := g.tc.c_type(types.unwrap_pointer(pt))
3806+ g.write('(${ct}[]){')
3807+ g.gen_expr_with_expected_type(arg_id, types.unwrap_pointer(pt))
3808+ g.write('}')
3809+ } else if needs_addr && is_rvalue {
35103810 pt := param_types[arg_idx]
35113811 ct := g.tc.c_type(types.unwrap_pointer(pt))
35123812 g.write('({${ct} _t${g.tmp_count} = ')
@@ -3564,6 +3864,18 @@ fn (mut g FlatGen) gen_call_args(fn_name string, node flat.Node, start int) {
35643864 }
35653865}
35663866
3867+fn raw_sizeof_arg_value(value string) ?string {
3868+ clean := value.trim_space()
3869+ if !clean.starts_with('sizeof(') || !clean.ends_with(')') {
3870+ return none
3871+ }
3872+ return clean['sizeof('.len..clean.len - 1].trim_space()
3873+}
3874+
3875+fn raw_sizeof_needs_normalization(value string) bool {
3876+ return value.starts_with('fn_ptr:') || value.starts_with('Array_fixed_')
3877+}
3878+
35673879// is_flag_enum_method reports whether is flag enum method applies in c.
35683880fn (g &FlatGen) is_flag_enum_method(fn_node &flat.Node) bool {
35693881 if fn_node.kind != .selector {
@@ -3671,6 +3983,24 @@ fn (g &FlatGen) addressed_rvalue_arg(arg_node flat.Node) ?flat.NodeId {
36713983 return none
36723984}
36733985
3986+fn (g &FlatGen) spread_arg_child(arg_node flat.Node) ?flat.NodeId {
3987+ if arg_node.kind == .prefix && arg_node.value == '...' && arg_node.children_count > 0 {
3988+ return g.a.child(&arg_node, 0)
3989+ }
3990+ return none
3991+}
3992+
3993+fn (g &FlatGen) arg_is_const_ident(arg_node flat.Node) bool {
3994+ if arg_node.kind != .ident || arg_node.value.len == 0 {
3995+ return false
3996+ }
3997+ looked_up := g.tc.cur_scope.lookup(arg_node.value) or { types.Type(types.void_) }
3998+ if looked_up !is types.Void {
3999+ return false
4000+ }
4001+ return g.const_ref_name(arg_node.value).len > 0
4002+}
4003+
36744004// find_prim_method resolves find prim method information for c.
36754005fn (g &FlatGen) find_prim_method(method string) string {
36764006 if 'u8.${method}' in g.tc.fn_param_types {
@@ -3766,6 +4096,7 @@ fn (mut g FlatGen) gen_sum_variant_arg(arg_id flat.NodeId, expected types.Type)
37664096
37674097// forward_decls supports forward decls handling for FlatGen.
37684098fn (mut g FlatGen) forward_decls() {
4099+ preferred_fns := g.preferred_c_backend_fn_nodes()
37694100 mut cur_module := ''
37704101 mut cur_file := ''
37714102 mut forwarded := map[string]bool{}
@@ -3788,10 +4119,15 @@ fn (mut g FlatGen) forward_decls() {
37884119
37894120 is_entry_main := is_main_fn_in_main_module(cur_module, node.value) && g.test_files.len == 0
37904121 if kind_id == 61 && !is_entry_main {
3791- if !g.should_emit_fn_node_in_module(node, i, cur_module) {
4122+ if !g.should_emit_fn_node_in_module(node, i, cur_module, cur_file) {
37924123 continue
37934124 }
37944125 qfn := g.fn_c_name_in_module(cur_module, node.value)
4126+ if preferred_idx := preferred_fns[qfn] {
4127+ if preferred_idx != i {
4128+ continue
4129+ }
4130+ }
37954131 if forwarded[qfn] {
37964132 continue
37974133 }
@@ -181,6 +181,7 @@ fn split_flat_cgen_items(items []FlatFnGenItem, n_jobs int) [][]FlatFnGenItem {
181181// collect_fn_gen_items updates collect fn gen items state for c.
182182fn (mut g FlatGen) collect_fn_gen_items() []FlatFnGenItem {
183183 mut items := []FlatFnGenItem{}
184+ preferred_fns := g.preferred_c_backend_fn_nodes()
184185 mut cur_module := ''
185186 mut cur_file := ''
186187 for i in 0 .. g.a.nodes.len {
@@ -203,10 +204,15 @@ fn (mut g FlatGen) collect_fn_gen_items() []FlatFnGenItem {
203204 if kind_id != 61 {
204205 continue
205206 }
206- if !g.should_emit_fn_node_in_module(node, i, cur_module) {
207+ if !g.should_emit_fn_node_in_module(node, i, cur_module, cur_file) {
207208 continue
208209 }
209210 qfn := qualified_fn_name_in_module(cur_module, node.value)
211+ if preferred_idx := preferred_fns[qfn] {
212+ if preferred_idx != i {
213+ continue
214+ }
215+ }
210216 if g.emitted_fn_contains(qfn) {
211217 continue
212218 }
@@ -389,11 +395,13 @@ fn (g &FlatGen) new_parallel_worker(worker_id int) &FlatGen {
389395 fn_decl_ret_types: g.fn_decl_ret_types
390396 struct_decl_infos: g.struct_decl_infos
391397 struct_decl_short_infos: g.struct_decl_short_infos
398+ const_runtime_inits: g.const_runtime_inits.clone()
392399 runtime_inits: g.runtime_inits.clone()
393400 compiler_vroot: g.compiler_vroot
394401 cur_param_names: g.cur_param_names.clone()
395402 cur_param_type_values: g.cur_param_type_values.clone()
396403 cur_param_types: g.cur_param_types.clone()
404+ cur_mut_params: g.cur_mut_params.clone()
397405 cur_fn_ret: g.cur_fn_ret
398406 cur_fn_ret_is_optional: g.cur_fn_ret_is_optional
399407 cur_fn_ret_base: g.cur_fn_ret_base
@@ -78,6 +78,7 @@ fn (mut g FlatGen) gen_for_in(node flat.Node) {
7878 var_name
7979 }
8080 clean_container_type := types.unwrap_pointer(container_type)
81+ mut map_snapshot_var := ''
8182 if clean_container_type is types.Map {
8283 c_key := g.value_c_type(clean_container_type.key_type)
8384 c_val := g.value_c_type(clean_container_type.value_type)
@@ -86,13 +87,42 @@ fn (mut g FlatGen) gen_for_in(node flat.Node) {
8687 g.tmp_count++
8788 key_var := if has_index { idx_var } else { '__mk_${g.tmp_count}' }
8889 val_var_ := if has_index { elem_var } else { var_name }
89- access := if container_type is types.Pointer { '->' } else { '.' }
90- key_values := '(${container_str})${access}key_values'
90+ use_snapshot := g.for_in_body_contains_delete_call(node, body_start)
91+ key_values := if use_snapshot {
92+ map_snapshot_var = '__for_map_${g.tmp_count}'
93+ g.tmp_count++
94+ if container_type is types.Pointer {
95+ g.writeln('map ${map_snapshot_var} = map__clone(${container_str});')
96+ } else {
97+ map_src := '__for_map_src_${g.tmp_count}'
98+ g.tmp_count++
99+ g.writeln('map ${map_src} = ${container_str};')
100+ g.writeln('map ${map_snapshot_var} = map__clone(&${map_src});')
101+ }
102+ '${map_snapshot_var}.key_values'
103+ } else {
104+ access := if container_type is types.Pointer { '->' } else { '.' }
105+ '(${container_str})${access}key_values'
106+ }
91107 g.writeln('for (int ${iter_var} = 0; ${iter_var} < ${key_values}.len; ${iter_var}++) {')
92108 g.indent++
93109 g.writeln('if (${key_values}.all_deleted && ${key_values}.all_deleted[${iter_var}]) continue;')
94- g.writeln('${c_key} ${key_var} = *(${c_key}*)(${key_values}.keys + ${iter_var} * ${key_values}.key_bytes);')
95- g.writeln('${c_val} ${val_var_} = *(${c_val}*)(${key_values}.values + ${iter_var} * ${key_values}.value_bytes);')
110+ key_slot := '${key_values}.keys + ${iter_var} * ${key_values}.key_bytes'
111+ if key_fixed := array_fixed_type(clean_container_type.key_type) {
112+ c_elem, dims := g.fixed_array_decl_parts(key_fixed)
113+ g.writeln('${c_elem} ${key_var}${dims};')
114+ g.writeln('memmove(${key_var}, ${key_slot}, sizeof(${key_var}));')
115+ } else {
116+ g.writeln('${c_key} ${key_var} = *(${c_key}*)(${key_slot});')
117+ }
118+ val_slot := '${key_values}.values + ${iter_var} * ${key_values}.value_bytes'
119+ if val_fixed := array_fixed_type(clean_container_type.value_type) {
120+ c_elem, dims := g.fixed_array_decl_parts(val_fixed)
121+ g.writeln('${c_elem} ${val_var_}${dims};')
122+ g.writeln('memmove(${val_var_}, ${val_slot}, sizeof(${val_var_}));')
123+ } else {
124+ g.writeln('${c_val} ${val_var_} = *(${c_val}*)(${val_slot});')
125+ }
96126 if has_index {
97127 g.tc.cur_scope.insert(key_var, clean_container_type.key_type)
98128 }
@@ -145,6 +175,9 @@ fn (mut g FlatGen) gen_for_in(node flat.Node) {
145175 }
146176 g.indent--
147177 g.writeln('}')
178+ if map_snapshot_var.len > 0 {
179+ g.writeln('map__free(&${map_snapshot_var});')
180+ }
148181 g.tc.pop_scope()
149182 return
150183 }
@@ -161,6 +194,37 @@ fn (mut g FlatGen) gen_for_in(node flat.Node) {
161194 g.tc.pop_scope()
162195}
163196
197+fn (g &FlatGen) for_in_body_contains_delete_call(node flat.Node, body_start int) bool {
198+ for i in body_start .. node.children_count {
199+ if g.node_contains_delete_call(g.a.child(&node, i)) {
200+ return true
201+ }
202+ }
203+ return false
204+}
205+
206+fn (g &FlatGen) node_contains_delete_call(id flat.NodeId) bool {
207+ if int(id) < 0 || int(id) >= g.a.nodes.len {
208+ return false
209+ }
210+ node := g.a.nodes[int(id)]
211+ if node.kind == .call && node.children_count > 0 {
212+ fn_node := g.a.child_node(&node, 0)
213+ if fn_node.kind == .selector && fn_node.value == 'delete' {
214+ return true
215+ }
216+ if fn_node.kind == .ident && fn_node.value in ['map.delete', 'map__delete'] {
217+ return true
218+ }
219+ }
220+ for i in 0 .. node.children_count {
221+ if g.node_contains_delete_call(g.a.child(&node, i)) {
222+ return true
223+ }
224+ }
225+ return false
226+}
227+
164228fn (g &FlatGen) c_loop_local_name(name string) string {
165229 if name.contains('.') {
166230 return c_name(name.all_after_last('.'))
@@ -292,10 +292,18 @@ fn (mut g FlatGen) gen_node(id flat.NodeId) {
292292 && !g.clone_call_matches_base(ret_node, base)
293293 && expr_value_type !is types.Primitive
294294 && expr_value_type !is types.Unknown {
295- g.writeln('return (${ct}){.ok = false};')
295+ if err_expr := g.optional_error_return_expr(ret_id,
296+ expr_value_type, ct)
297+ {
298+ g.writeln('return ${err_expr};')
299+ } else {
300+ g.writeln('return (${ct}){.ok = false};')
301+ }
296302 } else {
297303 g.write('return (${ct}){.ok = true, .value = ')
298- g.gen_expr_with_expected_type(ret_id, base)
304+ if !g.gen_heap_local_address_expr(ret_id, base) {
305+ g.gen_expr_with_expected_type(ret_id, base)
306+ }
299307 g.writeln('};')
300308 }
301309 }
@@ -349,7 +357,7 @@ fn (mut g FlatGen) gen_node(id flat.NodeId) {
349357 if !g.gen_interface_value_expr(ret_id, g.cur_fn_ret) {
350358 g.gen_expr(ret_id)
351359 }
352- } else {
360+ } else if !g.gen_heap_local_address_expr(ret_id, g.cur_fn_ret) {
353361 g.gen_expr(ret_id)
354362 }
355363 g.writeln(';')
@@ -517,6 +525,67 @@ fn (mut g FlatGen) return_c_type() string {
517525 return g.tc.c_type(g.cur_fn_ret)
518526}
519527
528+// local_ident_type returns the type of an identifier that is local to the
529+// currently emitted function body.
530+fn (g &FlatGen) local_ident_type(name string) ?types.Type {
531+ if typ := g.current_param_type(name) {
532+ return typ
533+ }
534+ if typ := g.cur_param_types[name] {
535+ return typ
536+ }
537+ if g.cur_scope_has_local_name(name) {
538+ if typ := g.tc.cur_scope.lookup(name) {
539+ if typ !is types.Void {
540+ return typ
541+ }
542+ }
543+ }
544+ return none
545+}
546+
547+// heap_local_address_expr returns a heap-copy expression for `&local` when the
548+// surrounding return type is a pointer. V permits local address escapes; C needs
549+// the local value copied out of the stack frame before returning.
550+fn (mut g FlatGen) heap_local_address_expr(ret_id flat.NodeId, expected types.Type) ?string {
551+ if int(ret_id) < 0 || int(ret_id) >= g.a.nodes.len {
552+ return none
553+ }
554+ node := g.a.nodes[int(ret_id)]
555+ if node.kind != .prefix || node.op != .amp || node.children_count == 0 {
556+ return none
557+ }
558+ if expected !is types.Pointer {
559+ return none
560+ }
561+ ptr := expected as types.Pointer
562+ child_id := g.a.child(&node, 0)
563+ child := g.a.nodes[int(child_id)]
564+ if child.kind != .ident || child.value.len == 0 {
565+ return none
566+ }