medvednikov

/

vxx Public
0 commits 0 issues 0 pull requests 0 contributors Discussions Projects CI

committed 56 years ago · View patch
35 files changed +6635 -495
vlib/builtin/cfns.c.v +5 -1

@@ -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

vlib/v3/gen/c/array.v +147 -25

@@ -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+}

vlib/v3/gen/c/cleanc.v +677 -65

@@ -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

vlib/v3/gen/c/fn.v +368 -32

@@ -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 }

vlib/v3/gen/c/fn_d_parallel.v +9 -1

@@ -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

vlib/v3/gen/c/for.v +68 -4

@@ -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('.'))

vlib/v3/gen/c/stmt.v +249 -14

@@ -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+ }