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

567+ local_type := g.local_ident_type(child.value) or { return none }

568+ base_type := ptr.base_type

569+ local_ct := g.tc.c_type(local_type)

570+ base_ct := g.tc.c_type(base_type)

571+ if base_ct.len == 0 || base_ct == 'void' {

572+ return none

573+ }

574+ if local_ct != base_ct && !g.type_names_match(local_type, base_type) {

575+ return none

576+ }

577+ local_expr := g.expr_to_string(child_id)

578+ return '(${base_ct}*)memdup(&${local_expr}, sizeof(${base_ct}))'

579+}

580+

581+fn (mut g FlatGen) gen_heap_local_address_expr(ret_id flat.NodeId, expected types.Type) bool {

582+ if expr := g.heap_local_address_expr(ret_id, expected) {

583+ g.write(expr)

584+ return true

585+ }

586+ return false

587+}

588+

520589// return_expr_string supports return expr string handling for FlatGen.

521590fn (mut g FlatGen) return_expr_string(node flat.Node, ret_id flat.NodeId, ret_node flat.Node, ct string) string {

522591 if ret_node.kind == .call {

@@ -589,9 +658,15 @@ fn (mut g FlatGen) return_expr_string(node flat.Node, ret_id flat.NodeId, ret_no

589658 && !g.types_numeric_compatible(expr_value_type, base)

590659 && !g.call_constructs_type(ret_id, base) && !g.clone_call_matches_base(ret_node, base)

591660 && expr_value_type !is types.Primitive && expr_value_type !is types.Unknown {

661+ if err_expr := g.optional_error_return_expr(ret_id, expr_value_type, ct) {

662+ return err_expr

663+ }

592664 return '(${ct}){.ok = false}'

593665 }

594- return '(${ct}){.ok = true, .value = ${g.expr_to_string_with_expected_type(ret_id, base)}}'

666+ value := g.heap_local_address_expr(ret_id, base) or {

667+ g.expr_to_string_with_expected_type(ret_id, base)

668+ }

669+ return '(${ct}){.ok = true, .value = ${value}}'

595670 }

596671 if g.cur_fn_ret is types.MultiReturn {

597672 if node.children_count > 1 {

@@ -612,7 +687,53 @@ fn (mut g FlatGen) return_expr_string(node flat.Node, ret_id flat.NodeId, ret_no

612687 // preserves `_typ`/`_object` instead of zeroing the interface.

613688 return g.interface_value_to_string(ret_id, g.cur_fn_ret)

614689 }

615- return g.expr_to_string(ret_id)

690+ return g.heap_local_address_expr(ret_id, g.cur_fn_ret) or { g.expr_to_string(ret_id) }

691+}

692+

693+fn (mut g FlatGen) optional_error_return_expr(ret_id flat.NodeId, expr_type types.Type, ct string) ?string {

694+ if !g.type_can_return_as_ierror(expr_type) {

695+ return none

696+ }

697+ err := g.interface_value_to_string(ret_id, types.Type(types.Interface{

698+ name: 'IError'

699+ }))

700+ return '(${ct}){.ok = false, .err = ${err}}'

701+}

702+

703+fn (g &FlatGen) type_can_return_as_ierror(typ types.Type) bool {

704+ clean := types.unwrap_pointer(typ)

705+ if clean is types.Alias {

706+ return g.type_can_return_as_ierror(clean.base_type)

707+ }

708+ if clean is types.Interface {

709+ return clean.name == 'IError' || clean.name.ends_with('.IError')

710+ }

711+ if clean is types.Struct {

712+ if clean.name == 'IError' || clean.name.ends_with('.IError') {

713+ return true

714+ }

715+ if g.tc.named_type_implements_interface(clean.name, 'IError') {

716+ return true

717+ }

718+ return g.struct_type_embeds_error(clean.name)

719+ }

720+ return false

721+}

722+

723+fn (g &FlatGen) struct_type_embeds_error(type_name string) bool {

724+ if type_name == 'Error' || type_name.ends_with('.Error') {

725+ return true

726+ }

727+ for field in g.struct_embedded_fields(type_name) {

728+ embedded_type_name := g.embedded_field_type_name(field)

729+ if embedded_type_name.len == 0 {

730+ continue

731+ }

732+ if g.struct_type_embeds_error(embedded_type_name) {

733+ return true

734+ }

735+ }

736+ return false

616737}

617738

618739fn (g &FlatGen) local_fn_call_return_type(call_id flat.NodeId, call_node flat.Node) types.Type {

@@ -921,10 +1042,14 @@ fn (g &FlatGen) clone_call_matches_base(call_node flat.Node, base types.Type) bo

9211042// option_c_name_for_base returns the C optional type name used for a `?base`/`!base`

9221043// value, mirroring optional_type_name without its side effects.

9231044fn (g &FlatGen) option_c_name_for_base(base types.Type) string {

924- if base is types.Void || base is types.Primitive || base is types.Enum {

1045+ if base is types.Void {

1046+ return 'Optional'

1047+ }

1048+ inner_ct := g.tc.c_type(base)

1049+ if inner_ct == 'int' {

9251050 return 'Optional'

9261051 }

927- return 'Optional_' + g.tc.c_type(base).replace('*', 'ptr').replace(' ', '_')

1052+ return 'Optional_' + inner_ct.replace('*', 'ptr').replace(' ', '_')

9281053}

9291054

9301055fn (g &FlatGen) expr_is_nil_pointer_payload(id flat.NodeId, base types.Type) bool {

@@ -1178,6 +1303,24 @@ fn (mut g FlatGen) gen_decl_assign(node flat.Node) {

11781303 rhs := g.a.nodes[int(rhs_id)]

11791304 lhs_is_defer_capture := lhs.kind == .ident && lhs.value in g.defer_capture_types

11801305 if rhs.kind == .array_literal {

1306+ rhs_v_type := if rhs.typ.len > 0 {

1307+ g.tc.parse_type(rhs.typ)

1308+ } else {

1309+ g.tc.resolve_type(rhs_id)

1310+ }

1311+ if fixed := array_fixed_type(rhs_v_type) {

1312+ lhs_str := g.decl_lhs_str(lhs_id)

1313+ if !lhs_is_defer_capture {

1314+ c_elem, dims := g.fixed_array_decl_parts(fixed)

1315+ g.writeln('${decl_prefix}${c_elem} ${lhs_str}${dims};')

1316+ }

1317+ g.gen_fixed_array_copy_from_node(lhs_str, rhs_id, fixed)

1318+ if lhs.kind == .ident {

1319+ g.tc.cur_scope.insert(lhs.value, rhs_v_type)

1320+ }

1321+ i += 2

1322+ continue

1323+ }

11811324 elem_type := if rhs.children_count > 0 {

11821325 g.tc.resolve_type(g.a.child(&rhs, 0))

11831326 } else {

@@ -1207,7 +1350,7 @@ fn (mut g FlatGen) gen_decl_assign(node flat.Node) {

12071350 g.tc.cur_scope.insert(lhs.value, raw_init_type)

12081351 }

12091352 } else {

1210- c_elem := g.tc.c_type(init_type)

1353+ c_elem := g.sizeof_target(rhs.value)

12111354 mut init_len := '0'

12121355 mut init_cap := '0'

12131356 mut init_val := ''

@@ -1272,13 +1415,33 @@ fn (mut g FlatGen) gen_decl_assign(node flat.Node) {

12721415 }

12731416 }

12741417 } else {

1275- v_type := if node.typ.len > 0 {

1276- g.tc.parse_type(node.typ)

1418+ mut v_type := if node.typ.len > 0 {

1419+ decl_type := g.tc.parse_type(node.typ)

1420+ if decl_type is types.Struct && decl_type.name == 'array' {

1421+ g.usable_expr_type(rhs_id)

1422+ } else if decl_annotation_is_unusable(decl_type, node.typ) {

1423+ rhs_type := g.decl_rhs_fallback_type(rhs_id, rhs)

1424+ if rhs_type is types.Unknown {

1425+ types.Type(decl_type)

1426+ } else {

1427+ rhs_type

1428+ }

1429+ } else {

1430+ decl_type

1431+ }

12771432 } else if rhs.kind == .if_expr {

12781433 g.if_expr_type(&rhs)

12791434 } else {

12801435 g.usable_expr_type(rhs_id)

12811436 }

1437+ if fixed := array_fixed_type(v_type) {

1438+ if g.fixed_array_decl_is_unusable(fixed) {

1439+ rhs_type := g.decl_rhs_fallback_type(rhs_id, rhs)

1440+ if rhs_type !is types.Unknown {

1441+ v_type = rhs_type

1442+ }

1443+ }

1444+ }

12821445 if fixed := array_fixed_type(v_type) {

12831446 lhs_str := g.decl_lhs_str(lhs_id)

12841447 if !lhs_is_defer_capture {

@@ -1320,6 +1483,55 @@ fn (mut g FlatGen) gen_decl_assign(node flat.Node) {

13201483 }

13211484}

13221485

1486+fn (g &FlatGen) decl_rhs_fallback_type(rhs_id flat.NodeId, rhs flat.Node) types.Type {

1487+ if rhs.kind == .index && rhs.value.len > 0 {

1488+ index_type := g.tc.parse_type(rhs.value)

1489+ if !decl_annotation_is_unusable(index_type, rhs.value) {

1490+ return index_type

1491+ }

1492+ }

1493+ if rhs.typ.len > 0 {

1494+ rhs_type := g.tc.parse_type(rhs.typ)

1495+ if !decl_annotation_is_unusable(rhs_type, rhs.typ) {

1496+ return rhs_type

1497+ }

1498+ }

1499+ if rhs.kind == .index && rhs.children_count > 0 {

1500+ base_type := types.unwrap_pointer(g.usable_expr_type(g.a.child(&rhs, 0)))

1501+ if base_type is types.Array {

1502+ return base_type.elem_type

1503+ }

1504+ if base_type is types.ArrayFixed {

1505+ return base_type.elem_type

1506+ }

1507+ if base_type is types.Map {

1508+ return base_type.value_type

1509+ }

1510+ }

1511+ return g.usable_expr_type(rhs_id)

1512+}

1513+

1514+fn (mut g FlatGen) fixed_array_decl_is_unusable(typ types.ArrayFixed) bool {

1515+ c_elem, _ := g.fixed_array_decl_parts(typ)

1516+ return c_elem == 'void'

1517+}

1518+

1519+fn decl_annotation_is_unusable(typ types.Type, raw string) bool {

1520+ if typ is types.ArrayFixed && fixed_array_contains_void(typ) {

1521+ return true

1522+ }

1523+ clean_raw := raw.replace(' ', '')

1524+ return clean_raw == 'void' || clean_raw.starts_with('void[') || clean_raw.ends_with(']void')

1525+ || clean_raw.contains(']void[')

1526+}

1527+

1528+fn fixed_array_contains_void(typ types.Type) bool {

1529+ if typ is types.ArrayFixed {

1530+ return fixed_array_contains_void(typ.elem_type)

1531+ }

1532+ return typ is types.Void

1533+}

1534+

13231535fn (mut g FlatGen) gen_fixed_array_zero_init_decl(lhs_id flat.NodeId, init_type types.ArrayFixed, decl_prefix string, lhs_is_defer_capture bool) {

13241536 c_elem, dims := g.fixed_array_decl_parts(init_type)

13251537 lhs_str := g.decl_lhs_str(lhs_id)

@@ -1486,7 +1698,8 @@ fn (mut g FlatGen) gen_assign(node flat.Node) {

14861698 g.write('(void)(')

14871699 g.gen_expr(g.a.child(&node, i + 1))

14881700 g.writeln(');')

1489- } else if node.op == .left_shift_assign && lhs.kind == .ident {

1701+ } else if node.op == .left_shift_assign && lhs.kind == .ident

1702+ && node.value in ['push', 'push_many'] {

14901703 if node.value == 'push_many' {

14911704 g.gen_array_push_many_stmt(g.a.child(&node, i), g.a.child(&node, i + 1))

14921705 } else if node.value == 'push' {

@@ -1735,6 +1948,10 @@ fn (mut g FlatGen) gen_multi_return_assign(node flat.Node) {

17351948fn (mut g FlatGen) gen_decl_lhs(id flat.NodeId) {

17361949 node := g.a.nodes[int(id)]

17371950 if node.kind == .ident {

1951+ if node.value == '_' {

1952+ g.write('__discard_${int(id)}')

1953+ return

1954+ }

17381955 g.write(c_name(node.value))

17391956 } else {

17401957 g.gen_expr(id)

@@ -1745,6 +1962,9 @@ fn (mut g FlatGen) gen_decl_lhs(id flat.NodeId) {

17451962fn (mut g FlatGen) decl_lhs_str(id flat.NodeId) string {

17461963 node := g.a.nodes[int(id)]

17471964 if node.kind == .ident {

1965+ if node.value == '_' {

1966+ return '__discard_${int(id)}'

1967+ }

17481968 return c_name(node.value)

17491969 }

17501970 return g.expr_to_string(id)

@@ -1755,8 +1975,9 @@ fn (mut g FlatGen) gen_assign_or_expr(node flat.Node, lhs_idx int, or_node flat.

17551975 expr_id := g.a.child(&or_node, 0)

17561976 or_body_id := g.a.child(&or_node, 1)

17571977 or_body := g.a.nodes[int(or_body_id)]

1978+ expr_node := g.a.nodes[int(expr_id)]

17581979 tmp := g.tmp_name()

1759- expr_type := g.tc.resolve_type(expr_id)

1980+ expr_type := g.or_expr_source_type(expr_id, expr_node)

17601981 opt_ct := g.optional_type_name(expr_type)

17611982 g.write('${opt_ct} ${tmp} = ')

17621983 g.gen_expr(expr_id)

@@ -1806,7 +2027,7 @@ fn (mut g FlatGen) gen_decl_or_expr(lhs flat.Node, or_node flat.Node) {

18062027 }

18072028 }

18082029 tmp := g.tmp_name()

1809- expr_type := g.tc.resolve_type(expr_id)

2030+ expr_type := g.or_expr_source_type(expr_id, expr_node)

18102031 opt_ct := g.optional_type_name(expr_type)

18112032 val_ct, val_type := g.optional_value_ct(expr_type)

18122033 g.tc.cur_scope.insert(lhs.value, val_type)

@@ -1951,7 +2172,7 @@ fn (mut g FlatGen) gen_or_expr(node flat.Node) {

19512172 }

19522173 }

19532174 tmp := g.tmp_name()

1954- expr_type := g.tc.resolve_type(expr_id)

2175+ expr_type := g.or_expr_source_type(expr_id, expr_node)

19552176 opt_ct := g.optional_type_name(expr_type)

19562177 val_ct, val_type := g.optional_value_ct(expr_type)

19572178 val := g.tmp_name()

@@ -1970,6 +2191,20 @@ fn (mut g FlatGen) gen_or_expr(node flat.Node) {

19702191 g.write(' } ${val};})')

19712192}

19722193

2194+fn (g &FlatGen) or_expr_source_type(expr_id flat.NodeId, expr_node flat.Node) types.Type {

2195+ if expr_node.kind == .call {

2196+ decl_type := g.declared_call_return_type(expr_id)

2197+ if decl_type is types.OptionType || decl_type is types.ResultType {

2198+ return decl_type

2199+ }

2200+ local_type := g.local_fn_call_return_type(expr_id, expr_node)

2201+ if local_type is types.OptionType || local_type is types.ResultType {

2202+ return local_type

2203+ }

2204+ }

2205+ return g.tc.resolve_type(expr_id)

2206+}

2207+

19732208// gen_or_body emits or body output for c.

19742209fn (mut g FlatGen) gen_or_body(or_body flat.Node) {

19752210 if or_body.children_count == 1 {

vlib/v3/gen/c/str_intp.v +2 -0

@@ -38,6 +38,8 @@ fn (mut g FlatGen) gen_string_interp(node flat.Node) {

3838 typ_name := types.Type(typ).name()

3939 if typ is types.String {

4040 g.gen_expr(child_id)

41+ } else if g.gen_map_str_expr(child_id, typ) {

42+ // emitted by gen_map_str_expr

4143 } else if typ_name == 'IError' || typ_name.ends_with('.IError') {

4244 // IError may resolve as Interface/Alias/Struct depending on context; match by

4345 // name and interpolate its `.message` (mirrors the transformer's IError path).

vlib/v3/gen/c/struct.v +6 -8

@@ -351,6 +351,10 @@ fn (mut g FlatGen) gen_fixed_array_copy_source(value_id flat.NodeId, field_type

351351 g.gen_expr(value_id)

352352 return

353353 }

354+ if val_node.kind == .paren && val_node.children_count > 0 {

355+ g.gen_fixed_array_copy_source(g.a.child(val_node, 0), field_type)

356+ return

357+ }

354358 val_type := types.unwrap_pointer(g.usable_expr_type(value_id))

355359 if val_type is types.Array {

356360 g.write('(')

@@ -1414,14 +1418,8 @@ fn (mut g FlatGen) gen_map_init(id flat.NodeId, node flat.Node) {

14141418

14151419// write_new_map writes new map output for c.

14161420fn (mut g FlatGen) write_new_map(key_type types.Type, value_type types.Type) {

1417- mut c_key := g.tc.c_type(key_type)

1418- mut c_val := g.tc.c_type(value_type)

1419- if c_key.starts_with('fn_ptr:') {

1420- c_key = g.resolve_fn_ptr_type(c_key)

1421- }

1422- if c_val.starts_with('fn_ptr:') {

1423- c_val = g.resolve_fn_ptr_type(c_val)

1424- }

1421+ c_key := g.value_sizeof_target(key_type)

1422+ c_val := g.value_sizeof_target(value_type)

14251423 hash_fn, eq_fn, clone_fn, free_fn := g.map_callback_names(key_type)

14261424 g.write('new_map(sizeof(${c_key}), sizeof(${c_val}), ${hash_fn}, ${eq_fn}, ${clone_fn}, ${free_fn})')

14271425}

vlib/v3/gen/c/types.v +89 -6

@@ -14,13 +14,16 @@ fn (mut g FlatGen) optional_type_name(t types.Type) string {

1414 return g.tc.c_type(t)

1515 }

1616

17- if base_type is types.Void || base_type is types.Primitive || base_type is types.Enum {

17+ if base_type is types.Void {

1818 return 'Optional'

1919 }

2020 mut inner_ct := g.tc.c_type(base_type)

2121 if inner_ct.starts_with('fn_ptr:') {

2222 inner_ct = g.resolve_fn_ptr_type(inner_ct)

2323 }

24+ if inner_ct == 'int' {

25+ return 'Optional'

26+ }

2427 safe_name := inner_ct.replace('*', 'ptr').replace(' ', '_')

2528 opt_name := 'Optional_${safe_name}'

2629 g.needed_optional_types[opt_name] = inner_ct

@@ -38,6 +41,14 @@ fn (mut g FlatGen) value_c_type(t types.Type) string {

3841 return ct

3942}

4043

44+fn (mut g FlatGen) value_sizeof_target(t types.Type) string {

45+ if fixed := array_fixed_type(t) {

46+ c_elem, dims := g.fixed_array_decl_parts(fixed)

47+ return '${c_elem}${dims}'

48+ }

49+ return g.value_c_type(t)

50+}

51+

4152fn (mut g FlatGen) cast_c_type(t types.Type) string {

4253 if t is types.Pointer {

4354 return '${g.value_c_type(t.base_type)}*'

@@ -63,11 +74,7 @@ fn (mut g FlatGen) optional_value_ct(t types.Type) (string, types.Type) {

6374

6475// optional_typedefs supports optional typedefs handling for FlatGen.

6576fn (mut g FlatGen) optional_typedefs() {

66- for _, ret in g.tc.fn_ret_types {

67- if ret is types.OptionType || ret is types.ResultType {

68- g.optional_type_name(ret)

69- }

70- }

77+ g.collect_optional_typedefs()

7178 mut wrote := false

7279 for opt_name, val_type in g.needed_optional_types {

7380 if g.emit_optional_typedef(opt_name, val_type) {

@@ -79,6 +86,82 @@ fn (mut g FlatGen) optional_typedefs() {

7986 }

8087}

8188

89+fn (mut g FlatGen) collect_optional_typedefs() {

90+ for _, ret in g.tc.fn_ret_types {

91+ g.collect_optional_typedef_type(ret)

92+ }

93+ for _, params in g.tc.fn_param_types {

94+ for param in params {

95+ g.collect_optional_typedef_type(param)

96+ }

97+ }

98+ for _, fields in g.tc.structs {

99+ for field in fields {

100+ g.collect_optional_typedef_type(field.typ)

101+ }

102+ }

103+ for _, fields in g.tc.interface_fields {

104+ for field in fields {

105+ g.collect_optional_typedef_type(field.typ)

106+ }

107+ }

108+ for _, typ in g.tc.c_globals {

109+ g.collect_optional_typedef_type(typ)

110+ }

111+ for _, typ in g.tc.const_types {

112+ g.collect_optional_typedef_type(typ)

113+ }

114+ for idx, _ in g.a.nodes {

115+ if typ := g.tc.expr_type(flat.NodeId(idx)) {

116+ g.collect_optional_typedef_type(typ)

117+ }

118+ }

119+}

120+

121+fn (mut g FlatGen) collect_optional_typedef_type(t types.Type) {

122+ match t {

123+ types.OptionType {

124+ g.optional_type_name(t)

125+ g.collect_optional_typedef_type(t.base_type)

126+ }

127+ types.ResultType {

128+ g.optional_type_name(t)

129+ g.collect_optional_typedef_type(t.base_type)

130+ }

131+ types.Array {

132+ g.collect_optional_typedef_type(t.elem_type)

133+ }

134+ types.ArrayFixed {

135+ g.collect_optional_typedef_type(t.elem_type)

136+ }

137+ types.Channel {

138+ g.collect_optional_typedef_type(t.elem_type)

139+ }

140+ types.Map {

141+ g.collect_optional_typedef_type(t.key_type)

142+ g.collect_optional_typedef_type(t.value_type)

143+ }

144+ types.Pointer {

145+ g.collect_optional_typedef_type(t.base_type)

146+ }

147+ types.FnType {

148+ for param in t.params {

149+ g.collect_optional_typedef_type(param)

150+ }

151+ g.collect_optional_typedef_type(t.return_type)

152+ }

153+ types.Alias {

154+ g.collect_optional_typedef_type(t.base_type)

155+ }

156+ types.MultiReturn {

157+ for typ in t.types {

158+ g.collect_optional_typedef_type(typ)

159+ }

160+ }

161+ else {}

162+ }

163+}

164+

82165// emit_optional_typedef emits emit optional typedef output for c.

83166fn (mut g FlatGen) emit_optional_typedef(opt_name string, val_type string) bool {

84167 if opt_name in g.emitted_optional_types {

vlib/v3/markused/markused.v +107 -11

@@ -144,16 +144,20 @@ fn mark_used_with_test_files(a &flat.FlatAst, tc &types.TypeChecker, test_files

144144 queue << 'c.gen_assign'

145145 used['c.gen_assign'] = true

146146 for seed in ['__new_array', 'new_array_from_c_array', 'array.get', 'array.set', 'array.push',

147- 'array.push_many', 'array.slice', 'array.clone', 'array.delete', 'array.ensure_cap',

147+ 'array.push_many', 'array.insert', 'array.insert_many', 'array.prepend', 'array.reverse',

148+ 'array.slice', 'array.pop_left', 'array.clone', 'array.delete', 'array.ensure_cap',

148149 'string.==', 'string.<', 'string.free', 'string.all_before', 'string.all_before_last',

149150 'string.all_after', 'string.all_after_last', 'string.substr', 'string__substr', 'u8.vstring',

150- '[]rune.string', 'map.set', 'map.exists', 'map.get', 'map.get_check', 'map.get_and_set',

151- 'map.delete', 'map.clone', 'map.clear', 'memdup', 'strings.Builder.write_ptr',

152- 'strings.Builder.write_runes', 'strings.Builder.free', 'strconv.format_int',

153- 'strconv.format_uint', 'bool.str', 'ptr_str', 'strconv__f32_to_str_l',

154- 'strconv__f64_to_str_l', 'sync.new_channel_st', 'sync.Channel.push', 'sync.Channel.pop',

155- 'sync.Channel.close', 'sync.Channel.len', 'sync.Channel.closed', 'new_channel_st',

156- 'Channel.push', 'Channel.pop', 'Channel.close', 'Channel.len', 'Channel.closed', 'panic',

151+ 'u8.vstring_with_len', 'charptr.vstring', 'charptr.vstring_with_len', 'byteptr.vstring',

152+ 'byteptr.vstring_with_len', 'byteptr.vbytes', 'voidptr.vbytes', '[]rune.string', 'map.set',

153+ 'map.exists', 'map.get', 'map.get_check', 'map.get_and_set', 'map.delete', 'map.clone',

154+ 'map.clear', 'map.keys', 'map.values', 'map.reserve', 'map_map_eq', 'memdup',

155+ 'strings.Builder.write_ptr', 'strings.Builder.write_runes', 'strings.Builder.free',

156+ 'strconv.format_int', 'strconv.format_uint', 'bool.str', 'int.str', 'u64.str', 'f64.str',

157+ 'rune.str', 'string.+', 'ptr_str', 'strconv__f32_to_str_l', 'strconv__f64_to_str_l',

158+ 'sync.new_channel_st', 'sync.Channel.push', 'sync.Channel.pop', 'sync.Channel.close',

159+ 'sync.Channel.len', 'sync.Channel.closed', 'new_channel_st', 'Channel.push', 'Channel.pop',

160+ 'Channel.close', 'Channel.len', 'Channel.closed', 'os.join_path_single', 'panic',

157161 'u8.is_letter', 'u8.is_capital', 'string.is_capital', 'string.to_lower_ascii',

158162 'rune.to_lower', 'Array_u8__bytestr', 'Array_u8__hex', 'data_to_hex_string',

159163 'map_hash_string', 'map_hash_int_1', 'map_hash_int_2', 'map_hash_int_4', 'map_hash_int_8',

@@ -164,6 +168,12 @@ fn mark_used_with_test_files(a &flat.FlatAst, tc &types.TypeChecker, test_files

164168 queue << seed

165169 used[seed] = true

166170 }

171+ queue << 'array.delete_last'

172+ used['array.delete_last'] = true

173+ for seed in ['i8.str', 'i16.str', 'i32.str', 'i64.str'] {

174+ queue << seed

175+ used[seed] = true

176+ }

167177

168178 if trace_markused {

169179 eprintln('markused: fn_count:')

@@ -739,9 +749,6 @@ fn enqueue_test_file_roots(a &flat.FlatAst, test_files map[string]bool, mut used

739749 continue

740750 }

741751 module_name := test_file_module_name(a, file_node)

742- if module_name.len > 0 && module_name != 'main' {

743- continue

744- }

745752 mut decl_ids := []flat.NodeId{}

746753 markused_collect_test_harness_decl_ids(a, file_node, mut decl_ids)

747754 for child_id in decl_ids {

@@ -816,6 +823,7 @@ fn enqueue_detected_runtime_helpers(a &flat.FlatAst, tc &types.TypeChecker, mut

816823 mut needs_string_plus_helper := false

817824 mut needs_string_membership_helpers := false

818825 mut needs_new_map := false

826+ mut needs_map_iteration_snapshot := false

819827 mut cur_module := ''

820828 mut imports := map[string]string{}

821829 for node in a.nodes {

@@ -872,6 +880,9 @@ fn enqueue_detected_runtime_helpers(a &flat.FlatAst, tc &types.TypeChecker, mut

872880 enqueue('join_path_single', mut used, mut queue)

873881 enqueue('os.join_path_single', mut used, mut queue)

874882 }

883+ if fn_node.kind == .selector && fn_node.value == 'runes_iterator' {

884+ enqueue('RunesIterator.next', mut used, mut queue)

885+ }

875886 if fn_node.kind == .ident

876887 && fn_node.value in ['print', 'println', 'eprint', 'eprintln']

877888 && node.children_count >= 2 {

@@ -913,6 +924,15 @@ fn enqueue_detected_runtime_helpers(a &flat.FlatAst, tc &types.TypeChecker, mut

913924 .map_init {

914925 needs_new_map = true

915926 }

927+ .for_in_stmt {

928+ if node.value.int() == 3 && node.children_count > 2 {

929+ container_id := a.child(&node, 2)

930+ container_type := tc.resolve_type(container_id)

931+ if types.unwrap_pointer(container_type) is types.Map {

932+ needs_map_iteration_snapshot = true

933+ }

934+ }

935+ }

916936 .enum_decl {

917937 // A `[flag]` enum's synthesized `<Enum>__autostr` helper (emitted

918938 // unconditionally in cgen) builds its `Enum{.a | .b}` string with

@@ -949,6 +969,11 @@ fn enqueue_detected_runtime_helpers(a &flat.FlatAst, tc &types.TypeChecker, mut

949969 if needs_new_map {

950970 enqueue('new_map', mut used, mut queue)

951971 }

972+ if needs_map_iteration_snapshot {

973+ for helper in ['map.clone', 'map__clone', 'map.free', 'map__free'] {

974+ enqueue(helper, mut used, mut queue)

975+ }

976+ }

952977}

953978

954979fn markused_call_lowers_to_join_path_single(a &flat.FlatAst, fn_node flat.Node, imports map[string]string) bool {

@@ -987,7 +1012,11 @@ fn enqueue_stringified_custom_str_method(expr_id flat.NodeId, cur_module string,

9871012 }

9881013 break

9891014 }

1015+ type_name := typ.name()

9901016 match typ {

1017+ types.Primitive, types.Rune, types.Char, types.ISize, types.USize, types.String {

1018+ enqueue_stringified_primitive_helpers(type_name, mut used, mut queue)

1019+ }

9911020 types.Enum {

9921021 enqueue_enum_str_method(typ.name, cur_module, tc, mut used, mut queue)

9931022 }

@@ -1001,6 +1030,40 @@ fn enqueue_stringified_custom_str_method(expr_id flat.NodeId, cur_module string,

10011030 }

10021031}

10031032

1033+fn enqueue_stringified_primitive_helpers(type_name string, mut used map[string]bool, mut queue []string) {

1034+ match type_name {

1035+ 'bool' {

1036+ enqueue('bool.str', mut used, mut queue)

1037+ }

1038+ 'rune', 'char' {

1039+ enqueue('rune.str', mut used, mut queue)

1040+ }

1041+ 'int', 'i8', 'i16', 'i32', 'i64' {

1042+ enqueue('${type_name}.str', mut used, mut queue)

1043+ enqueue(markused_c_name('${type_name}.str'), mut used, mut queue)

1044+ enqueue('strconv__format_int', mut used, mut queue)

1045+ }

1046+ 'isize' {

1047+ enqueue('strconv__format_int', mut used, mut queue)

1048+ }

1049+ 'u8', 'byte', 'u16', 'u32', 'usize' {

1050+ enqueue('strconv__format_uint', mut used, mut queue)

1051+ }

1052+ 'u64' {

1053+ enqueue('u64.str', mut used, mut queue)

1054+ enqueue(markused_c_name('u64.str'), mut used, mut queue)

1055+ enqueue('strconv__format_uint', mut used, mut queue)

1056+ }

1057+ 'f32' {

1058+ enqueue('strconv__f32_to_str_l', mut used, mut queue)

1059+ }

1060+ 'f64' {

1061+ enqueue('strconv__f64_to_str_l', mut used, mut queue)

1062+ }

1063+ else {}

1064+ }

1065+}

1066+

10041067// enqueue_enum_str_method supports enqueue enum str method handling for markused.

10051068fn enqueue_enum_str_method(type_name string, cur_module string, tc &types.TypeChecker, mut used map[string]bool, mut queue []string) {

10061069 for candidate in stringification_type_candidates(type_name, cur_module) {

@@ -1483,6 +1546,9 @@ fn (c &CallCollector) collect_calls(node &flat.Node, cur_module string, imports

14831546 .string_interp {

14841547 calls << 'string_plus_many'

14851548 }

1549+ .assign, .selector_assign, .index_assign {

1550+ c.collect_assign_operator_call(child, cur_module, mut calls)

1551+ }

14861552 .infix {

14871553 if child.op == .plus {

14881554 calls << 'string__plus'

@@ -1890,6 +1956,9 @@ fn (c &CallCollector) collect_top_level_expr_calls(id flat.NodeId, cur_module st

18901956 .string_interp {

18911957 calls << 'string_plus_many'

18921958 }

1959+ .assign, .selector_assign, .index_assign {

1960+ c.collect_assign_operator_call(child, cur_module, mut calls)

1961+ }

18931962 .infix {

18941963 if child.op == .plus {

18951964 calls << 'string__plus'

@@ -2157,6 +2226,19 @@ fn (c &CallCollector) collect_struct_operator_call(lhs_id flat.NodeId, op flat.O

21572226 c.add_operator_call_name(method_name, mut calls)

21582227}

21592228

2229+// collect_assign_operator_call adds operator overloads used through assignment operators.

2230+fn (c &CallCollector) collect_assign_operator_call(node &flat.Node, cur_module string, mut calls []string) {

2231+ op := markused_assign_operator_symbol(node.op) or { return }

2232+ mut i := 0

2233+ for i + 1 < node.children_count {

2234+ lhs_id := c.a.child(node, i)

2235+ if int(lhs_id) >= 0 {

2236+ c.collect_struct_operator_call(lhs_id, op, cur_module, mut calls)

2237+ }

2238+ i += 2

2239+ }

2240+}

2241+

21602242// struct_operator_call_name supports struct operator call name handling for CallCollector.

21612243fn (c &CallCollector) struct_operator_call_name(struct_type string, op flat.Op) ?string {

21622244 if op_name := markused_struct_operator_symbol(op) {

@@ -2181,6 +2263,20 @@ fn (c &CallCollector) struct_operator_call_name(struct_type string, op flat.Op)

21812263 return none

21822264}

21832265

2266+// markused_assign_operator_symbol maps assignment operators to their binary operator.

2267+fn markused_assign_operator_symbol(op flat.Op) ?flat.Op {

2268+ match op {

2269+ .plus_assign { return .plus }

2270+ .minus_assign { return .minus }

2271+ .mul_assign { return .mul }

2272+ .div_assign { return .div }

2273+ .mod_assign { return .mod }

2274+ else {}

2275+ }

2276+

2277+ return none

2278+}

2279+

21842280// markused_struct_operator_symbol supports markused struct operator symbol handling for markused.

21852281fn markused_struct_operator_symbol(op flat.Op) ?string {

21862282 match op {

vlib/v3/parser/parser.v +242 -21

@@ -625,6 +625,7 @@ fn (mut p Parser) fn_decl() flat.NodeId {

625625 mut name := ''

626626 mut receiver_name := ''

627627 mut receiver_type := ''

628+ mut receiver_is_mut := false

628629 mut is_method := false

629630

630631 // method receiver: fn (mut r Type) name()

@@ -634,6 +635,7 @@ fn (mut p Parser) fn_decl() flat.NodeId {

634635 mut is_mut := false

635636 if p.tok == .key_mut || p.tok == .key_shared {

636637 is_mut = p.tok == .key_mut

638+ receiver_is_mut = is_mut

637639 p.next()

638640 }

639641 receiver_name = p.expect_name()

@@ -648,7 +650,8 @@ fn (mut p Parser) fn_decl() flat.NodeId {

648650 if p.tok.is_overloadable() {

649651 op_name := overload_token_name(p.tok)

650652 p.next()

651- return p.fn_operator_overload(receiver_name, receiver_type, op_name)

653+ return p.fn_operator_overload(receiver_name, receiver_type, receiver_is_mut,

654+ op_name)

652655 }

653656 }

654657 }

@@ -669,7 +672,8 @@ fn (mut p Parser) fn_decl() flat.NodeId {

669672 clean_type := receiver_type.trim_left('&')

670673 name = '${clean_type}.${name}'

671674 }

672- return p.fn_decl_body(name, receiver_name, receiver_type, is_method, true)

675+ return p.fn_decl_body(name, receiver_name, receiver_type, receiver_is_mut,

676+ is_method, true)

673677 }

674678 // module.func or Type.static_method

675679 second := p.expect_name_or_keyword()

@@ -690,10 +694,10 @@ fn (mut p Parser) fn_decl() flat.NodeId {

690694 name = '${clean_type}.${name}'

691695 }

692696

693- return p.fn_decl_body(name, receiver_name, receiver_type, is_method, false)

697+ return p.fn_decl_body(name, receiver_name, receiver_type, receiver_is_mut, is_method, false)

694698}

695699

696-fn (mut p Parser) fn_operator_overload(receiver_name string, receiver_type string, op_name string) flat.NodeId {

700+fn (mut p Parser) fn_operator_overload(receiver_name string, receiver_type string, receiver_is_mut bool, op_name string) flat.NodeId {

697701 disable_body := p.disable_fn_body

698702 p.disable_fn_body = false

699703 // parse parameter

@@ -704,6 +708,7 @@ fn (mut p Parser) fn_operator_overload(receiver_name string, receiver_type strin

704708 kind: .param

705709 value: receiver_name

706710 typ: receiver_type

711+ op: if receiver_is_mut { .amp } else { .none }

707712 })

708713 // operator param

709714 if p.tok == .key_mut {

@@ -765,7 +770,7 @@ fn (mut p Parser) fn_operator_overload(receiver_name string, receiver_type strin

765770 return id

766771}

767772

768-fn (mut p Parser) fn_decl_body(name string, receiver_name string, receiver_type string, is_method bool, is_c_decl bool) flat.NodeId {

773+fn (mut p Parser) fn_decl_body(name string, receiver_name string, receiver_type string, receiver_is_mut bool, is_method bool, is_c_decl bool) flat.NodeId {

769774 // Capture & clear here so it applies only to this function (not nested closures

770775 // or a following declaration), and is cleared even on the extern/no-body path.

771776 disable_body := p.disable_fn_body

@@ -784,6 +789,7 @@ fn (mut p Parser) fn_decl_body(name string, receiver_name string, receiver_type

784789 kind: .param

785790 value: receiver_name

786791 typ: receiver_type

792+ op: if receiver_is_mut { .amp } else { .none }

787793 })

788794 }

789795 for p.tok != .rpar && p.tok != .eof {

@@ -908,6 +914,7 @@ fn (mut p Parser) parse_param_group(is_c_decl bool) []flat.NodeId {

908914 kind: .param

909915 value: ''

910916 typ: typ

917+ op: if is_mut { .amp } else { .none }

911918 })

912919 if p.tok == .comma {

913920 p.next()

@@ -923,6 +930,7 @@ fn (mut p Parser) parse_param_group(is_c_decl bool) []flat.NodeId {

923930 kind: .param

924931 value: ''

925932 typ: typ

933+ op: if is_mut { .amp } else { .none }

926934 })

927935 if p.tok == .comma {

928936 p.next()

@@ -950,6 +958,7 @@ fn (mut p Parser) parse_param_group(is_c_decl bool) []flat.NodeId {

950958 kind: .param

951959 value: name

952960 typ: typ

961+ op: if is_mut { .amp } else { .none }

953962 })

954963 }

955964 if p.tok == .comma {

@@ -1772,9 +1781,9 @@ fn (mut p Parser) parse_comptime_if() flat.NodeId {

17721781 p.next() // skip 'if'

17731782 cond := p.parse_comptime_cond()

17741783 if comptime_cond_has_type_test(cond) {

1775- p.skip_block()

1776- p.skip_comptime_else()

1777- return flat.empty_node

1784+ then_block := p.block_stmt()

1785+ else_block := p.parse_comptime_else()

1786+ return p.comptime_if_node(cond, then_block, else_block)

17781787 }

17791788 taken := eval_comptime_cond(p.prefs, cond)

17801789 if taken {

@@ -1810,9 +1819,9 @@ fn (mut p Parser) parse_top_level_comptime_if() flat.NodeId {

18101819 p.next() // skip 'if'

18111820 cond := p.parse_comptime_cond()

18121821 if comptime_cond_has_type_test(cond) {

1813- p.skip_block()

1814- p.skip_comptime_else()

1815- return flat.empty_node

1822+ then_block := p.top_level_block_stmt()

1823+ else_block := p.parse_top_level_comptime_else()

1824+ return p.comptime_if_node(cond, then_block, else_block)

18161825 }

18171826 taken := eval_comptime_cond(p.prefs, cond)

18181827 if taken {

@@ -1824,6 +1833,23 @@ fn (mut p Parser) parse_top_level_comptime_if() flat.NodeId {

18241833 return p.parse_top_level_comptime_else()

18251834}

18261835

1836+fn (mut p Parser) comptime_if_node(cond string, then_block flat.NodeId, else_block flat.NodeId) flat.NodeId {

1837+ mut ids := []flat.NodeId{cap: 2}

1838+ if int(then_block) >= 0 {

1839+ ids << then_block

1840+ }

1841+ if int(else_block) >= 0 {

1842+ ids << else_block

1843+ }

1844+ start := p.add_children(ids)

1845+ return p.a.add_node(flat.Node{

1846+ kind: .comptime_if

1847+ value: cond

1848+ children_start: start

1849+ children_count: flat.child_count(ids.len)

1850+ })

1851+}

1852+

18271853fn (mut p Parser) top_level_block_stmt() flat.NodeId {

18281854 ids := p.parse_top_level_block_body()

18291855 start := p.add_children(ids)

@@ -1853,21 +1879,20 @@ fn (mut p Parser) parse_top_level_block_body() []flat.NodeId {

18531879

18541880fn (mut p Parser) parse_comptime_cond() string {

18551881 mut cond := strings.new_builder(64)

1882+ mut prev_tok_str := ''

18561883 for p.tok != .lcbr && p.tok != .eof {

18571884 tok_str := p.comptime_cond_token_text()

1858- if cond.len > 0 && tok_str != '?' {

1885+ if cond.len > 0 && comptime_cond_needs_space(prev_tok_str, tok_str) {

18591886 cond.write_string(' ')

18601887 }

18611888 cond.write_string(tok_str)

1889+ prev_tok_str = tok_str

18621890 p.next()

18631891 }

18641892 return cond.str()

18651893}

18661894

18671895fn (p &Parser) comptime_cond_token_text() string {

1868- if p.lit.len > 0 {

1869- return p.lit

1870- }

18711896 if p.s.pos >= 0 && p.s.pos < p.s.src.len {

18721897 c := p.s.src[p.s.pos]

18731898 if c == `&` && p.s.pos + 1 < p.s.src.len && p.s.src[p.s.pos + 1] == `&` {

@@ -1876,6 +1901,10 @@ fn (p &Parser) comptime_cond_token_text() string {

18761901 if c == `|` && p.s.pos + 1 < p.s.src.len && p.s.src[p.s.pos + 1] == `|` {

18771902 return '||'

18781903 }

1904+ if c == `!` && p.s.pos + 2 < p.s.src.len && p.s.src[p.s.pos + 1] == `i`

1905+ && p.s.src[p.s.pos + 2] == `s` {

1906+ return '!is'

1907+ }

18791908 if c == `!` {

18801909 return '!'

18811910 }

@@ -1893,6 +1922,33 @@ fn (p &Parser) comptime_cond_token_text() string {

18931922 if tok == .not {

18941923 return '!'

18951924 }

1925+ if tok == .lpar {

1926+ return '('

1927+ }

1928+ if tok == .rpar {

1929+ return ')'

1930+ }

1931+ if tok == .key_is {

1932+ return 'is'

1933+ }

1934+ if tok == .not_is {

1935+ return '!is'

1936+ }

1937+ if tok == .dollar {

1938+ return '$'

1939+ }

1940+ if tok == .lsbr {

1941+ return '['

1942+ }

1943+ if tok == .rsbr {

1944+ return ']'

1945+ }

1946+ if tok == .dot {

1947+ return '.'

1948+ }

1949+ if tok == .comma {

1950+ return ','

1951+ }

18961952 if tok == .question {

18971953 return '?'

18981954 }

@@ -1902,9 +1958,28 @@ fn (p &Parser) comptime_cond_token_text() string {

19021958 if tok == .key_false {

19031959 return 'false'

19041960 }

1961+ if p.lit.len > 0 {

1962+ return p.lit

1963+ }

19051964 return ''

19061965}

19071966

1967+fn comptime_cond_needs_space(prev string, cur string) bool {

1968+ if prev == 'is' || prev == '!is' {

1969+ return true

1970+ }

1971+ if prev == '?' || prev == '!' {

1972+ return false

1973+ }

1974+ if cur == '?' || cur == ']' || cur == '[' || cur == '.' || cur == ',' {

1975+ return false

1976+ }

1977+ if prev == '$' || prev == '[' || prev == ']' || prev == '.' {

1978+ return false

1979+ }

1980+ return true

1981+}

1982+

19081983fn comptime_cond_has_type_test(cond string) bool {

19091984 return cond.contains(' is ') || cond.contains(' !is ')

19101985}

@@ -4542,14 +4617,28 @@ fn (mut p Parser) string_interp(first_part string, quote u8) flat.NodeId {

45424617 for int(p.tok) == 106 {

45434618 p.next() // skip $

45444619 p.check(.lcbr) // skip {

4545- ids << p.expr(.lowest)

4620+ expr_id := p.expr(.lowest)

4621+ mut part_id := expr_id

45464622 // format spec: :fmt

45474623 if p.tok == .colon {

45484624 p.next()

4625+ mut fmt := ''

45494626 for p.tok != .rcbr && p.tok != .eof {

4627+ fmt += if p.lit.len > 0 { p.lit } else { p.tok.str() }

45504628 p.next()

45514629 }

4630+ if fmt.len > 0 {

4631+ start := p.add_child(expr_id)

4632+ part_id = p.a.add_node(flat.Node{

4633+ kind: .directive

4634+ value: 'string_interp_format'

4635+ typ: fmt

4636+ children_start: start

4637+ children_count: 1

4638+ })

4639+ }

45524640 }

4641+ ids << part_id

45534642 p.check(.rcbr) // skip }

45544643 if int(p.tok) == 107 {

45554644 part := strip_interp_quotes(p.lit, quote)

@@ -4639,7 +4728,8 @@ fn (mut p Parser) array_literal() flat.NodeId {

46394728 if p.tok == .rsbr {

46404729 size_end := p.tok_pos

46414730 p.next()

4642- if p.tok == .name || p.tok == .amp || p.tok == .lsbr || p.tok == .question {

4731+ if p.tok == .name || p.tok == .amp || p.tok == .question

4732+ || (p.tok == .lsbr && p.current_lbr_starts_array_type()) {

46434733 // fixed array type: [N]Type. Use the literal node value for a plain integer

46444734 // size, but recover the full source text for a const expression (e.g.

46454735 // `[segs + 1]f32`) — an infix node has no `.value`, which would otherwise

@@ -4698,6 +4788,22 @@ fn (mut p Parser) array_literal() flat.NodeId {

46984788 })

46994789 }

47004790 // single-element array: [expr]

4791+ if p.tok == .not {

4792+ p.next()

4793+ start := p.add_child(ids[0])

4794+ lit := p.a.add_node(flat.Node{

4795+ kind: .array_literal

4796+ children_start: start

4797+ children_count: 1

4798+ })

4799+ pstart := p.add_child(lit)

4800+ return p.a.add_node(flat.Node{

4801+ kind: .postfix

4802+ op: .not

4803+ children_start: pstart

4804+ children_count: 1

4805+ })

4806+ }

47014807 start := p.add_children(ids)

47024808 return p.a.add_node(flat.Node{

47034809 kind: .array_literal

@@ -4715,15 +4821,27 @@ fn (mut p Parser) array_literal() flat.NodeId {

47154821 }

47164822 p.check(.rsbr)

47174823 // check for `!` (fixed array with values)

4824+ mut is_fixed_literal := false

47184825 if p.tok == .not {

47194826 p.next()

4827+ is_fixed_literal = true

47204828 }

47214829 start := p.add_children(ids)

4722- return p.a.add_node(flat.Node{

4830+ lit := p.a.add_node(flat.Node{

47234831 kind: .array_literal

47244832 children_start: start

47254833 children_count: flat.child_count(ids.len)

47264834 })

4835+ if is_fixed_literal {

4836+ pstart := p.add_child(lit)

4837+ return p.a.add_node(flat.Node{

4838+ kind: .postfix

4839+ op: .not

4840+ children_start: pstart

4841+ children_count: 1

4842+ })

4843+ }

4844+ return lit

47274845}

47284846

47294847// fn_literal supports fn literal handling for Parser.

@@ -4968,7 +5086,20 @@ fn (mut p Parser) peek_lbr_starts_array_type() bool {

49685086 if p.peek() != .lsbr {

49695087 return false

49705088 }

4971- mut idx := p.s.offset

5089+ return p.lbr_starts_array_type_from_offset(p.s.offset)

5090+}

5091+

5092+// current_lbr_starts_array_type reports whether the current `[` starts a nested

5093+// array type, not an index expression.

5094+fn (mut p Parser) current_lbr_starts_array_type() bool {

5095+ if p.tok != .lsbr {

5096+ return false

5097+ }

5098+ return p.lbr_starts_array_type_from_offset(p.s.offset)

5099+}

5100+

5101+fn (mut p Parser) lbr_starts_array_type_from_offset(offset int) bool {

5102+ mut idx := offset

49725103 for idx < p.s.src.len && (p.s.src[idx] == ` ` || p.s.src[idx] == `\t`

49735104 || p.s.src[idx] == `\n` || p.s.src[idx] == `\r`) {

49745105 idx++

@@ -4982,13 +5113,15 @@ fn (mut p Parser) peek_lbr_starts_array_type() bool {

49825113 depth--

49835114 if depth == 0 {

49845115 idx++

4985- for idx < p.s.src.len && (p.s.src[idx] == ` ` || p.s.src[idx] == `\t`

4986- || p.s.src[idx] == `\n` || p.s.src[idx] == `\r`) {

5116+ for idx < p.s.src.len && (p.s.src[idx] == ` ` || p.s.src[idx] == `\t`) {

49875117 idx++

49885118 }

49895119 if idx >= p.s.src.len {

49905120 return false

49915121 }

5122+ if p.s.src[idx] == `\n` || p.s.src[idx] == `\r` || p.s.src[idx] == `;` {

5123+ return false

5124+ }

49925125 next := p.s.src[idx]

49935126 return (next >= `a` && next <= `z`) || (next >= `A` && next <= `Z`)

49945127 || next == `_` || next == `&` || next == `?` || next == `!`

@@ -5279,6 +5412,44 @@ fn unescape_string(s string) string {

52795412 mut i := 0

52805413 for i < s.len {

52815414 if s[i] == `\\` && i + 1 < s.len {

5415+ if s[i + 1] == `\n` {

5416+ i += 2

5417+ for i < s.len && (s[i] == ` ` || s[i] == `\t` || s[i] == `\r`) {

5418+ i++

5419+ }

5420+ continue

5421+ }

5422+ if s[i + 1] == `\r` && i + 2 < s.len && s[i + 2] == `\n` {

5423+ i += 3

5424+ for i < s.len && (s[i] == ` ` || s[i] == `\t`) {

5425+ i++

5426+ }

5427+ continue

5428+ }

5429+ if s[i + 1] == `x` && i + 3 < s.len {

5430+ if code := parse_fixed_hex(s, i + 2, 2) {

5431+ unsafe {

5432+ buf[j] = u8(code)

5433+ }

5434+ j++

5435+ i += 4

5436+ continue

5437+ }

5438+ }

5439+ if s[i + 1] == `u` && i + 5 < s.len {

5440+ if code := parse_fixed_hex(s, i + 2, 4) {

5441+ j = write_utf8_codepoint(buf, j, u32(code))

5442+ i += 6

5443+ continue

5444+ }

5445+ }

5446+ if s[i + 1] == `U` && i + 9 < s.len {

5447+ if code := parse_fixed_hex(s, i + 2, 8) {

5448+ j = write_utf8_codepoint(buf, j, u32(code))

5449+ i += 10

5450+ continue

5451+ }

5452+ }

52825453 c := match s[i + 1] {

52835454 `n` { u8(`\n`) }

52845455 `t` { u8(`\t`) }

@@ -5322,6 +5493,56 @@ fn unescape_string(s string) string {

53225493 }

53235494}

53245495

5496+fn parse_fixed_hex(s string, start int, len int) ?u32 {

5497+ mut code := u32(0)

5498+ for i in 0 .. len {

5499+ if start + i >= s.len {

5500+ return none

5501+ }

5502+ n := hex_digit_value(s[start + i]) or { return none }

5503+ code = (code << 4) | u32(n)

5504+ }

5505+ return code

5506+}

5507+

5508+fn hex_digit_value(c u8) ?int {

5509+ if c >= `0` && c <= `9` {

5510+ return int(c - `0`)

5511+ }

5512+ if c >= `a` && c <= `f` {

5513+ return int(c - `a`) + 10

5514+ }

5515+ if c >= `A` && c <= `F` {

5516+ return int(c - `A`) + 10

5517+ }

5518+ return none

5519+}

5520+

5521+fn write_utf8_codepoint(buf &u8, j int, code u32) int {

5522+ unsafe {

5523+ if code <= 0x7f {

5524+ buf[j] = u8(code)

5525+ return j + 1

5526+ }

5527+ if code <= 0x7ff {

5528+ buf[j] = u8(0xc0 | (code >> 6))

5529+ buf[j + 1] = u8(0x80 | (code & 0x3f))

5530+ return j + 2

5531+ }

5532+ if code <= 0xffff {

5533+ buf[j] = u8(0xe0 | (code >> 12))

5534+ buf[j + 1] = u8(0x80 | ((code >> 6) & 0x3f))

5535+ buf[j + 2] = u8(0x80 | (code & 0x3f))

5536+ return j + 3

5537+ }

5538+ buf[j] = u8(0xf0 | (code >> 18))

5539+ buf[j + 1] = u8(0x80 | ((code >> 12) & 0x3f))

5540+ buf[j + 2] = u8(0x80 | ((code >> 6) & 0x3f))

5541+ buf[j + 3] = u8(0x80 | (code & 0x3f))

5542+ return j + 4

5543+ }

5544+}

5545+

53255546fn is_builtin_type(name string) bool {

53265547 return name in ['int', 'i8', 'i16', 'i32', 'i64', 'u8', 'u16', 'u32', 'u64', 'f32', 'f64',

53275548 'byte', 'bool', 'string', 'rune', 'char', 'voidptr', 'charptr', 'byteptr', 'usize', 'isize',

vlib/v3/pref/pref.v +38 -19

@@ -193,8 +193,10 @@ pub fn get_v_files_from_dir(dir string, user_defines []string, target_os string)

193193 return []string{}

194194 }

195195 all_files := os.ls(dir) or { return []string{} }

196+ mut sorted_files := all_files.clone()

197+ sorted_files.sort()

196198 mut has_os_specific := map[string]bool{}

197- for file in all_files {

199+ for file in sorted_files {

198200 if !file.ends_with('.v') || file.ends_with('.js.v') || file.contains('_test.') {

199201 continue

200202 }

@@ -206,30 +208,35 @@ pub fn get_v_files_from_dir(dir string, user_defines []string, target_os string)

206208 }

207209 }

208210 mut v_files := []string{}

209- for file in all_files {

210- if !file.ends_with('.v') || file.ends_with('.js.v') || file.contains('_test.') {

211- continue

212- }

213- if file_has_incompatible_os_suffix(file, target_os) {

214- continue

215- }

216- if base := default_file_base(file) {

217- if has_os_specific[base] {

211+ for backend_specific in [false, true] {

212+ for file in sorted_files {

213+ if file.ends_with('.c.v') != backend_specific {

218214 continue

219215 }

220- }

221- if file.contains('_notd_') {

222- feature := extract_define_feature(file, '_notd_')

223- if feature.len > 0 && feature in user_defines {

216+ if !file.ends_with('.v') || file.ends_with('.js.v') || file.contains('_test.') {

224217 continue

225218 }

226- } else if file.contains('_d_') {

227- feature := extract_define_feature(file, '_d_')

228- if feature.len == 0 || feature !in user_defines {

219+ if file_has_incompatible_os_suffix(file, target_os) {

229220 continue

230221 }

222+ if base := default_file_base(file) {

223+ if has_os_specific[base] {

224+ continue

225+ }

226+ }

227+ if file.contains('_notd_') {

228+ feature := extract_define_feature(file, '_notd_')

229+ if feature.len > 0 && feature in user_defines {

230+ continue

231+ }

232+ } else if file.contains('_d_') {

233+ feature := extract_define_feature(file, '_d_')

234+ if feature.len == 0 || feature !in user_defines {

235+ continue

236+ }

237+ }

238+ v_files << os.join_path_single(dir, file)

231239 }

232- v_files << os.join_path_single(dir, file)

233240 }

234241 return v_files

235242}

@@ -397,7 +404,19 @@ pub fn comptime_flag_value(p &Preferences, name string) bool {

397404 return tos == 'macos' || tos == 'freebsd' || tos == 'openbsd' || tos == 'netbsd'

398405 || tos == 'dragonfly'

399406 }

400- 'x64', 'amd64' {

407+ 'x64' {

408+ $if x64 {

409+ return true

410+ }

411+ return false

412+ }

413+ 'x32' {

414+ $if x32 {

415+ return true

416+ }

417+ return false

418+ }

419+ 'amd64' {

401420 $if amd64 {

402421 return true

403422 }

vlib/v3/test_all.vsh +28 -5

@@ -2,7 +2,21 @@

22

33import os

44

5-const total_steps = 6

5+const total_steps = 7

6+const temp_prefix = 'v3_test_all'

7+const requested_vlib_tests = [

8+ 'vlib/builtin/string_test.v',

9+ 'vlib/math/math_test.v',

10+ 'vlib/builtin/array_test.v',

11+ 'vlib/math/complex/complex_test.v',

12+ 'vlib/builtin/map_test.v',

13+ 'vlib/crypto/hmac/hmac_test.v',

14+ 'vlib/crypto/sha3/sha3_test.v',

15+ 'vlib/time/time_test.v',

16+ 'vlib/os/process_test.v',

17+ 'vlib/os/file_test.v',

18+ 'vlib/arrays/arrays_test.v',

19+]

620

721struct Config {

822 vexe string

@@ -51,13 +65,22 @@ fn main() {

5165 section(2, 'Build v3')

5266 run('${q(cfg.vexe)} -o ${q(v3_bin)} ${q(cfg.v3_src)}')

5367

54- section(3, 'C backend hello world')

68+ section(3, 'Requested vlib tests')

69+ for rel_path in requested_vlib_tests {

70+ test_path := os.join_path(cfg.repo_root, rel_path)

71+ test_bin := temp_path(rel_path.replace('/', '_').replace('.v', ''))

72+ run('${q(v3_bin)} ${q(test_path)} -o ${q(test_bin)}')

73+ run(q(test_bin))

74+ cleanup_files([test_bin, test_bin + '.c'])

75+ }

76+

77+ section(4, 'C backend hello world')

5578 hello_v := os.join_path(cfg.tests_dir, 'hello.v')

5679 run('${q(v3_bin)} ${cfg.c99_flag} ${q(hello_v)} -b c -o ${q(hello_c_bin)}')

5780 run(q(hello_c_bin))

5881 cleanup_files([hello_c_bin, hello_c_bin + '.c'])

5982

60- section(4, 'ARM64 self-host hello world')

83+ section(5, 'ARM64 self-host hello world')

6184 if cfg.c99 {

6285 println(' Skipping ARM64 self-host in C99 mode (-c99 applies to the C backend)')

6386 } else if cfg.host_backend == 'arm64' && cfg.host_os == 'macos' {

@@ -69,7 +92,7 @@ fn main() {

6992 println(' Skipping ARM64 self-host on ${cfg.host_os}/${cfg.host_backend} host (Mach-O only)')

7093 }

7194

72- section(5, 'Self-host chain (v3->v4->v5->v6)')

95+ section(6, 'Self-host chain (v3->v4->v5->v6)')

7396 println(' Building v4 from v3...')

7497 run('${q(v3_bin)} ${cfg.c99_flag} --no-parallel -selfhost -o ${q(v4_bin)} ${q(cfg.v3_src)}')

7598 println(' Building v5 from v4...')

@@ -81,7 +104,7 @@ fn main() {

81104 println(' v5.c=v6.c (${converged_size} bytes) - chain converged')

82105 cleanup_files([v4_bin, v4_bin + '.c', v5_bin, v5_bin + '.c', v6_bin, v6_bin + '.c'])

83106

84- section(6, 'Language feature parity')

107+ section(7, 'Language feature parity')

85108 lang_v := os.join_path(cfg.tests_dir, 'test_all_lang_features.v')

86109 lang_out := os.join_path(cfg.tests_dir, 'test_all_lang_features.out')

87110 run('${q(v3_bin)} ${cfg.c99_flag} ${q(lang_v)} -b c -o ${q(v3_lang_bin)}')

vlib/v3/tests/c_anonymous_param_codegen_test.v +2 -0

@@ -51,6 +51,7 @@ fn main() {

5151 _ = C.take_ptr(&native)

5252 _ = C.take_named(ptr)

5353 _ = C.take_void(voidptr(ptr))

54+ _ = C.take_void(voidptr(&ptr))

5455 _ = C.take_primitive(1, u64(2))

5556 _ = C.take_multi(pptr)

5657 _ = C.take_mixed(ptr, voidptr(ptr), 3, pptr)

@@ -76,6 +77,7 @@ fn main() {

7677 assert generated.contains('take_ptr(&native)'), generated

7778 assert generated.contains('take_named(ptr)'), generated

7879 assert generated.contains('take_void((void*)(ptr))'), generated

80+ assert generated.contains('take_void((void*)(&ptr))'), generated

7981 assert generated.contains('take_multi(pptr)'), generated

8082 assert generated.contains('take_mixed(ptr, (void*)(ptr), 3, pptr)'), generated

8183 fn_ptr_idx := generated.index('typedef int (*_fn_ptr_') or { -1 }

vlib/v3/tests/markused_test.v +25 -0

@@ -989,6 +989,31 @@ fn main() {

989989 assert compile.exit_code == 0, compile.output

990990}

991991

992+// test_print_signed_width_compile_keeps_str_runtime_helpers validates this v3 regression case.

993+fn test_print_signed_width_compile_keeps_str_runtime_helpers() {

994+ v3_bin := build_v3_bin('print_signed_width_test')

995+

996+ src := os.join_path(os.temp_dir(), 'v3_markused_print_signed_width_input.v')

997+ bin := os.join_path(os.temp_dir(), 'v3_markused_print_signed_width_input')

998+ os.write_file(src, "

999+fn main() {

1000+ println(i8(-5))

1001+ println(i16(-300))

1002+ println(i32(-70000))

1003+ println(i64(-5000000000))

1004+ println('\${i64(42)}')

1005+}

1006+") or {

1007+ panic(err)

1008+ }

1009+ compile := os.execute('${v3_bin} -o ${bin} ${src}')

1010+ assert compile.exit_code == 0, compile.output

1011+ assert !compile.output.contains('implicit declaration'), compile.output

1012+ run := os.execute(bin)

1013+ assert run.exit_code == 0, run.output

1014+ assert run.output.trim_space() == '-5\n-300\n-70000\n-5000000000\n42', run.output

1015+}

1016+

9921017// test_string_plus_compile_keeps_plus_runtime_helper validates this v3 regression case.

9931018fn test_string_plus_compile_keeps_plus_runtime_helper() {

9941019 v3_bin := build_v3_bin('string_plus_test')

vlib/v3/tests/option_arg_codegen_test.v +8 -0

@@ -43,6 +43,14 @@ fn test_optional_if_expr_codegen_initializes_optional_temp() {

4343 assert out == 'ok'

4444}

4545

46+// test_nested_local_optional_primitive_typedef validates this v3 regression case.

47+fn test_nested_local_optional_primitive_typedef() {

48+ v3_bin := build_v3()

49+ out := run_good(v3_bin, 'nested_local_optional_primitive_typedef_input',

50+ "fn main() {\n\txs := [?f32(1.5)]\n\tassert xs.len == 1\n\tprintln('ok')\n}\n")

51+ assert out == 'ok'

52+}

53+

4654// test_optional_clone_return_wraps_payload validates this v3 regression case.

4755fn test_optional_clone_return_wraps_payload() {

4856 v3_bin := build_v3()

vlib/v3/tests/post_merge_review_fixes_test.v +63 -6

@@ -100,17 +100,16 @@ fn test_filelock_helpers_are_inlined_in_generated_c() {

100100 c_source := gen_c(v3_bin, 'filelock_helpers_inline',

101101 'import os.filelock\n\nfn C.v_filelock_lock(i32, i32, i32, u64, u64) i32\nfn C.v_filelock_unlock(i32, u64, u64) i32\n\nfn main() {\n\t_ = filelock.LockMode.exclusive\n\t_ = C.v_filelock_lock(i32(-1), 1, 1, u64(0), u64(0))\n\t_ = C.v_filelock_unlock(i32(-1), u64(0), u64(0))\n}\n')

102102 assert !c_source.contains('filelock_helpers.h')

103- assert c_source.contains('static int v_filelock_lock(')

104- assert c_source.contains('static int v_filelock_unlock(')

103+ assert c_source.contains('static inline int v_filelock_lock(')

104+ assert c_source.contains('static inline int v_filelock_unlock(')

105105 assert c_source.contains('#ifndef V_OS_FILELOCK_HELPERS_H')

106106 assert !c_source.contains('v_filelock_status')

107107 status_source := gen_c(v3_bin, 'filelock_custom_prefix_decl',

108108 'import os.filelock\n\nfn C.v_filelock_lock(i32, i32, i32, u64, u64) i32\nfn C.v_filelock_unlock(i32, u64, u64) i32\nfn C.v_filelock_status() int\n\nfn main() {\n\t_ = filelock.LockMode.exclusive\n\t_ = C.v_filelock_lock(i32(-1), 1, 1, u64(0), u64(0))\n\t_ = C.v_filelock_status()\n}\n')

109109 assert status_source.contains('int v_filelock_status(')

110- user_name_source := gen_c(v3_bin, 'filelock_user_names_not_helpers',

110+ out := run_good(v3_bin, 'filelock_user_names_not_helpers',

111111 'fn v_filelock_lock() int {\n\treturn 3\n}\n\nfn v_filelock_unlock() int {\n\treturn 4\n}\n\nfn main() {\n\tprintln(int_str(v_filelock_lock() + v_filelock_unlock()))\n}\n')

112- assert !user_name_source.contains('static int v_filelock_lock(')

113- assert !user_name_source.contains('static int v_filelock_unlock(')

112+ assert out == '7'

114113}

115114

116115fn test_assoc_return_runs_defers() {

@@ -240,7 +239,7 @@ fn test_map_builtin_method_fallback_checks_arguments() {

240239 v3_bin := build_v3()

241240 run_bad(v3_bin, 'map_keys_rejects_extra_arg',

242241 'fn extra_arg() int {\n\treturn 1\n}\n\nfn main() {\n\tmut m := map[string]int{}\n\t_ := m.keys(extra_arg())\n}\n',

243- 'argument count mismatch for `m.keys`: expected 1, got 2')

242+ 'argument count mismatch for `m.keys`: expected 0, got 1')

244243 run_bad(v3_bin, 'map_delete_rejects_bad_key_type',

245244 'fn main() {\n\tmut m := map[string]int{}\n\tm.delete(123)\n}\n',

246245 'cannot use `int` as argument 2 to `m.delete`; expected `string`')

@@ -285,3 +284,61 @@ fn test_map_builtin_method_fallback_checks_arguments() {

285284 }, 'main.v')

286285 assert module_collection_out == '31\n42\n53'

287286}

287+

288+fn test_runtime_inits_run_before_module_init() {

289+ v3_bin := build_v3()

290+ out := run_good_project(v3_bin, 'runtime_inits_before_module_init', {

291+ 'main.v': 'module main\n\nimport moda\n\nfn main() {\n\tprintln(int_str(moda.const_seen()))\n\tprintln(int_str(moda.global_seen()))\n}\n'

292+ 'moda/moda.v': "module moda\n\nconst const_map = map[string]int{\n\t'const': 5\n}\n\n__global (\n\tglobal_map = map[string]int{\n\t\t'global': 7\n\t}\n\tseen_const int\n\tseen_global int\n)\n\nfn init() {\n\tseen_const = const_map['const']\n\tseen_global = global_map['global']\n}\n\npub fn const_seen() int {\n\treturn seen_const\n}\n\npub fn global_seen() int {\n\treturn seen_global\n}\n"

293+ }, 'main.v')

294+ assert out == '5\n7'

295+}

296+

297+fn test_string_index_type_is_u8() {

298+ v3_bin := build_v3()

299+ out := run_good(v3_bin, 'string_index_type_is_u8',

300+ "fn main() {\n\ts := 'ABC'\n\tprintln(typeof(s[0]).name)\n\tprintln('\${s[2]}')\n}\n")

301+ assert out == 'u8\n67'

302+}

303+

304+fn test_f32_map_and_fixed_array_stringification() {

305+ v3_bin := build_v3()

306+ out := run_good(v3_bin, 'f32_map_stringification',

307+ "fn main() {\n\tm := {\n\t\t'a': f32(1.5)\n\t}\n\tprintln(m)\n\tfixed := [f32(1.5), f32(2.25)]!\n\tmf := {\n\t\t'x': fixed\n\t}\n\tprintln(mf)\n}\n")

308+ assert out == "{'a': 1.5}\n{'x': [1.5, 2.25]}"

309+}

310+

311+fn test_u8_map_stringification_is_numeric() {

312+ v3_bin := build_v3()

313+ out := run_good(v3_bin, 'u8_map_stringification',

314+ "fn main() {\n\tkeys := {\n\t\tu8(23): 'x'\n\t}\n\tvals := {\n\t\t'x': u8(23)\n\t}\n\tboth := {\n\t\tu8(65): u8(10)\n\t}\n\tprintln(keys)\n\tprintln(vals)\n\tprintln(both)\n}\n")

315+ assert out == "{23: 'x'}\n{'x': 23}\n{65: 10}"

316+}

317+

318+fn test_map_equality_uses_semantic_value_comparison() {

319+ v3_bin := build_v3()

320+ out := run_good(v3_bin, 'map_semantic_value_equality',

321+ "struct Item {\n\tname string\n\tparts []string\n}\n\nfn join(a string, b string) string {\n\treturn a + b\n}\n\nfn main() {\n\tleft := {\n\t\t'x': Item{\n\t\t\tname: 'hello'.clone()\n\t\t\tparts: ['ab'.clone()]\n\t\t}\n\t}\n\tright := {\n\t\t'x': Item{\n\t\t\tname: join('he', 'llo')\n\t\t\tparts: [join('a', 'b')]\n\t\t}\n\t}\n\tarr_left := {\n\t\t'y': ['cd'.clone()]\n\t}\n\tarr_right := {\n\t\t'y': [join('c', 'd')]\n\t}\n\tprintln(left == right)\n\tprintln(left != right)\n\tprintln(arr_left == arr_right)\n}\n")

322+ assert out == 'true\nfalse\ntrue'

323+}

324+

325+fn test_array_equality_marks_struct_operator_used() {

326+ v3_bin := build_v3()

327+ out := run_good(v3_bin, 'array_eq_struct_operator_used',

328+ 'struct Item {\n\tvalue int\n}\n\nfn (a Item) == (b Item) bool {\n\treturn a.value % 10 == b.value % 10\n}\n\nfn main() {\n\tleft := [Item{value: 12}]\n\tright := [Item{value: 2}]\n\tprintln(left == right)\n}\n')

329+ assert out == 'true'

330+}

331+

332+fn test_zero_padded_interpolation_preserves_wide_integers() {

333+ v3_bin := build_v3()

334+ out := run_good(v3_bin, 'wide_zero_padded_interpolation',

335+ "fn main() {\n\tbig := i64(5000000000)\n\tubig := u64(18446744073709551615)\n\tsmall := u64(42)\n\tprintln('\${big:012d}')\n\tprintln('\${ubig:020d}')\n\tprintln('\${small:08d}')\n}\n")

336+ assert out == '005000000000\n18446744073709551615\n00000042'

337+}

338+

339+fn test_formatted_interpolation_rune_and_long_float() {

340+ v3_bin := build_v3()

341+ out := run_good(v3_bin, 'formatted_interpolation_rune_and_long_float',

342+ "fn main() {\n\tr := '\${rune(0x20ac):c}'\n\tprintln(int_str(r.len))\n\tprintln(int_str(int(r[0])) + ',' + int_str(int(r[1])) + ',' + int_str(int(r[2])))\n\tlong := '\${1.0:.200f}'\n\tprintln(int_str(long.len))\n\tprintln(int_str(int(long[0])) + ',' + int_str(int(long[1])) + ',' + int_str(int(long[2])) + ',' + int_str(int(long[long.len - 1])))\n}\n")

343+ assert out == '3\n226,130,172\n202\n49,46,48,48'

344+}

vlib/v3/tests/review_checker_regressions_test.v new file +164 -0

@@ -0,0 +1,164 @@

1+import os

2+

3+const vexe = @VEXE

4+const tests_dir = os.dir(@FILE)

5+const v3_dir = os.dir(tests_dir)

6+const vlib_dir = os.dir(v3_dir)

7+const v3_src = os.join_path(v3_dir, 'v3.v')

8+

9+fn build_v3_review_checker() string {

10+ v3_bin := os.join_path(os.temp_dir(), 'v3_review_checker_regressions_test')

11+ build := os.execute('${vexe} -path "${vlib_dir}|@vlib|@vmodules" -o ${v3_bin} ${v3_src}')

12+ assert build.exit_code == 0, build.output

13+ return v3_bin

14+}

15+

16+fn run_bad(v3_bin string, name string, src string, expected string) {

17+ bad_src := os.join_path(os.temp_dir(), 'v3_${name}.v')

18+ os.write_file(bad_src, src) or { panic(err) }

19+ bad_bin := os.join_path(os.temp_dir(), 'v3_${name}')

20+ result := os.execute('${v3_bin} ${bad_src} -b c -o ${bad_bin}')

21+ assert result.exit_code != 0, '${name}: expected failure, got success\n${result.output}'

22+ assert result.output.contains(expected), '${name}: expected `${expected}` in\n${result.output}'

23+ assert !result.output.contains('C compilation failed'), '${name}: reached C compilation\n${result.output}'

24+}

25+

26+fn run_good(v3_bin string, name string, src string) string {

27+ good_src := os.join_path(os.temp_dir(), 'v3_${name}.v')

28+ os.write_file(good_src, src) or { panic(err) }

29+ good_bin := os.join_path(os.temp_dir(), 'v3_${name}')

30+ compile := os.execute('${v3_bin} ${good_src} -b c -o ${good_bin}')

31+ assert compile.exit_code == 0, '${name}: compile failed\n${compile.output}'

32+ assert !compile.output.contains('C compilation failed'), '${name}: C compilation failed\n${compile.output}'

33+ run := os.execute(good_bin)

34+ assert run.exit_code == 0, '${name}: run failed\n${run.output}'

35+ return run.output.trim_space()

36+}

37+

38+fn test_reject_pointer_expressions_for_value_returns() {

39+ v3_bin := build_v3_review_checker()

40+ run_bad(v3_bin, 'bad_return_pointer_to_value',

41+ 'fn f() int {\n\tx := 1\n\treturn &x\n}\nfn main() {}\n', 'cannot return `&int` as `int`')

42+ run_bad(v3_bin, 'bad_result_return_pointer_to_value',

43+ 'fn f() !int {\n\tx := 1\n\treturn &x\n}\nfn main() {}\n', 'cannot return `&int` as `!int`')

44+}

45+

46+fn test_restrict_synthetic_hex_fallback_receivers() {

47+ v3_bin := build_v3_review_checker()

48+ run_bad(v3_bin, 'bad_struct_hex_method', 'struct S {}\nfn main() {\n\t_ := S{}.hex()\n}\n',

49+ 'unknown function')

50+ run_bad(v3_bin, 'bad_int_array_hex_method', 'fn main() {\n\t_ := [1, 2].hex()\n}\n',

51+ 'unknown function')

52+ run_bad(v3_bin, 'bad_map_hex_method',

53+ 'fn main() {\n\tm := map[string]int{}\n\t_ := m.hex()\n}\n', 'unknown function')

54+ run_bad(v3_bin, 'bad_float_hex_method', 'fn main() {\n\t_ := f32(1.5).hex()\n}\n',

55+ 'unknown function')

56+ run_bad(v3_bin, 'bad_pointer_hex_method',

57+ 'fn main() {\n\tx := 1\n\tp := &x\n\t_ := p.hex()\n}\n', 'unknown function')

58+ run_bad(v3_bin, 'bad_numeric_hex_arg',

59+ 'fn side_effect() int {\n\treturn 1\n}\n\nfn main() {\n\t_ := u8(1).hex(side_effect())\n}\n',

60+ 'argument count mismatch')

61+ out := run_good(v3_bin, 'supported_hex_methods',

62+ "fn main() {\n\tprintln(u8(15).hex())\n\tprintln(i64(255).hex())\n\tprintln([u8(1), 15, 255].hex())\n\tprintln(char(65).hex())\n\tprintln(`A`.hex())\n\tprintln('abc'.hex())\n}\n")

63+ assert out == '0f\nff\n010fff\n41\n41\n616263'

64+}

65+

66+fn test_auto_str_rejects_arguments() {

67+ v3_bin := build_v3_review_checker()

68+ run_bad(v3_bin, 'bad_auto_str_arg',

69+ 'struct S {\n\tx int\n}\n\nfn side_effect() int {\n\treturn 1\n}\n\nfn main() {\n\t_ := S{\n\t\tx: 1\n\t}.str(side_effect())\n}\n',

70+ 'argument count mismatch')

71+}

72+

73+fn test_pointer_hex_receiver_methods_are_allowed() {

74+ v3_bin := build_v3_review_checker()

75+ out := run_good(v3_bin, 'good_pointer_hex_receiver_method',

76+ "struct S {\n\tvalue int\n}\n\nfn (s &S) hex() string {\n\treturn 'ptr:' + int_str(s.value)\n}\n\nfn main() {\n\ts := S{\n\t\tvalue: 7\n\t}\n\tp := &s\n\tprintln(p.hex())\n}\n")

77+ assert out == 'ptr:7'

78+}

79+

80+fn test_map_keys_and_values_reject_arguments() {

81+ v3_bin := build_v3_review_checker()

82+ run_bad(v3_bin, 'bad_map_keys_arg',

83+ 'fn main() {\n\tm := map[string]int{}\n\t_ := m.keys(123)\n}\n', 'argument count mismatch')

84+ run_bad(v3_bin, 'bad_map_values_arg',

85+ "fn main() {\n\tm := map[string]int{}\n\t_ := m.values('x')\n}\n",

86+ 'argument count mismatch')

87+}

88+

89+fn test_array_insert_and_prepend_reject_wrong_arity() {

90+ v3_bin := build_v3_review_checker()

91+ run_bad(v3_bin, 'bad_array_prepend_missing_arg',

92+ 'fn main() {\n\tmut a := [1, 2]\n\ta.prepend()\n}\n', 'argument count mismatch')

93+ run_bad(v3_bin, 'bad_array_prepend_extra_arg',

94+ 'fn side_effect() int {\n\treturn 3\n}\nfn main() {\n\tmut a := [1, 2]\n\ta.prepend(0, side_effect())\n}\n',

95+ 'argument count mismatch')

96+ run_bad(v3_bin, 'bad_array_insert_missing_arg',

97+ 'fn main() {\n\tmut a := [1, 2]\n\ta.insert(0)\n}\n', 'argument count mismatch')

98+ run_bad(v3_bin, 'bad_array_insert_extra_arg',

99+ 'fn side_effect() int {\n\treturn 3\n}\nfn main() {\n\tmut a := [1, 2]\n\ta.insert(0, 1, side_effect())\n}\n',

100+ 'argument count mismatch')

101+ run_bad(v3_bin, 'bad_array_prepend_arg_type',

102+ "fn main() {\n\tmut a := [1, 2]\n\ta.prepend('x')\n}\n", 'cannot use')

103+ run_bad(v3_bin, 'bad_array_insert_index_type',

104+ "fn main() {\n\tmut a := [1, 2]\n\ta.insert('0', 3)\n}\n", 'cannot use')

105+ run_bad(v3_bin, 'bad_array_insert_value_type',

106+ "fn main() {\n\tmut a := [1, 2]\n\ta.insert(0, 'x')\n}\n", 'cannot use')

107+ run_bad(v3_bin, 'bad_array_prepend_many_arg_type',

108+ "fn main() {\n\tmut a := [1, 2]\n\ta.prepend(['x'])\n}\n", 'cannot use')

109+ run_bad(v3_bin, 'bad_array_insert_many_arg_type',

110+ "fn main() {\n\tmut a := [1, 2]\n\ta.insert(0, ['x'])\n}\n", 'cannot use')

111+}

112+

113+fn test_array_insert_and_prepend_accept_many_operands() {

114+ v3_bin := build_v3_review_checker()

115+ out := run_good(v3_bin, 'good_array_insert_prepend_many_operands',

116+ "type Strings = []string\n\nfn main() {\n\tmut a := [3, 4]\n\ta.insert(0, [1, 2])\n\tb := [5, 6]\n\ta.insert(1, b)\n\ta.prepend([0])\n\tfixed := [7, 8]!\n\ta.insert(a.len, fixed)\n\tassert a == [0, 1, 5, 6, 2, 3, 4, 7, 8]\n\tmut strs := Strings(['hi'])\n\tstrs.insert(0, ['there'])\n\tstrs.prepend(['hello'])\n\tassert strs == ['hello', 'there', 'hi']\n\tprintln('ok')\n}\n")

117+ assert out == 'ok'

118+}

119+

120+fn test_comptime_if_selected_bodies_are_checked() {

121+ v3_bin := build_v3_review_checker()

122+ run_bad(v3_bin, 'bad_concrete_comptime_if_selected_call',

123+ 'fn main() {\n\t$if int is int {\n\t\tmissing_selected_symbol()\n\t}\n}\n',

124+ 'unknown function `missing_selected_symbol`')

125+ out := run_good(v3_bin, 'good_generic_comptime_if_unselected_branch_is_not_checked',

126+ "fn ok() {}\n\nfn f[T]() {\n\t$if T is int {\n\t\tok()\n\t} $else {\n\t\tonly_for_other_t()\n\t}\n}\n\nfn main() {\n\tf[int]()\n\tprintln('ok')\n}\n")

127+ assert out == 'ok'

128+}

129+

130+fn test_explicit_generic_calls_use_all_type_arguments() {

131+ v3_bin := build_v3_review_checker()

132+ out := run_good(v3_bin, 'good_multi_explicit_generic_call',

133+ "struct Pair[A, B] {\n\tleft A\n\tright B\n}\n\nfn make_pair[A, B]() Pair[A, B] {\n\treturn Pair[A, B]{}\n}\n\nfn expect_pair(p Pair[int, string]) string {\n\treturn 'ok'\n}\n\nfn main() {\n\tp := make_pair[int, string]()\n\tprintln(expect_pair(p))\n}\n")

134+ assert out == 'ok'

135+ nested := run_good(v3_bin, 'good_nested_explicit_generic_call',

136+ "struct Pair[A, B] {\n\tleft A\n\tright B\n}\n\nstruct Box[T] {\n\tvalue T\n}\n\nfn wrap[T]() Box[T] {\n\treturn Box[T]{}\n}\n\nfn expect_box(b Box[Pair[int, string]]) string {\n\treturn 'ok'\n}\n\nfn main() {\n\tb := wrap[Pair[int, string]]()\n\tprintln(expect_box(b))\n}\n")

137+ assert nested == 'ok'

138+ run_bad(v3_bin, 'bad_explicit_generic_too_many_type_args',

139+ 'fn id[T](x T) T {\n\treturn x\n}\n\nfn main() {\n\t_ := id[int, string](1)\n}\n',

140+ 'generic argument count mismatch')

141+}

142+

143+fn test_reject_escaping_capturing_fn_literals() {

144+ v3_bin := build_v3_review_checker()

145+ run_bad(v3_bin, 'bad_return_capturing_fn_literal',

146+ 'fn make(x int) fn () int {\n\treturn fn [x] () int {\n\t\treturn x\n\t}\n}\nfn main() {}\n',

147+ 'capturing fn literal cannot be stored or returned')

148+ run_bad(v3_bin, 'bad_store_capturing_fn_literal_alias',

149+ 'fn main() {\n\tx := 1\n\tf := fn [x] () int {\n\t\treturn x\n\t}\n\tmut cbs := []fn () int{}\n\tcbs << f\n}\n',

150+ 'capturing fn literal cannot be stored or returned')

151+}

152+

153+fn test_generic_functions_report_missing_return() {

154+ v3_bin := build_v3_review_checker()

155+ run_bad(v3_bin, 'bad_generic_missing_return', 'fn f[T]() int {\n}\nfn main() {}\n',

156+ 'missing return at end of function `f`')

157+}

158+

159+fn test_local_identifiers_shadow_module_consts() {

160+ v3_bin := build_v3_review_checker()

161+ out := run_good(v3_bin, 'good_const_shadowed_by_param_and_local',

162+ "const shadowed_value = 'const'\n\nfn param_shadow(shadowed_value int) int {\n\treturn shadowed_value + 1\n}\n\nfn local_shadow() int {\n\tshadowed_value := 2\n\treturn shadowed_value + 1\n}\n\nfn main() {\n\tprintln(int_str(param_shadow(1)))\n\tprintln(int_str(local_shadow()))\n\tprintln(shadowed_value)\n}\n")

163+ assert out == '2\n3\nconst'

164+}

vlib/v3/tests/review_transform_regressions_test.v new file +297 -0

@@ -0,0 +1,297 @@

1+import os

2+

3+const vexe = @VEXE

4+const tests_dir = os.dir(@FILE)

5+const v3_dir = os.dir(tests_dir)

6+const vlib_dir = os.dir(v3_dir)

7+const v3_src = os.join_path(v3_dir, 'v3.v')

8+

9+fn build_v3_review_transform() string {

10+ v3_bin := os.join_path(os.temp_dir(), 'v3_review_transform_regressions_test')

11+ build := os.execute('${vexe} -path "${vlib_dir}|@vlib|@vmodules" -o ${v3_bin} ${v3_src}')

12+ assert build.exit_code == 0, build.output

13+ return v3_bin

14+}

15+

16+fn run_bad(v3_bin string, name string, src string, expected string) {

17+ bad_src := os.join_path(os.temp_dir(), 'v3_${name}.v')

18+ os.write_file(bad_src, src) or { panic(err) }

19+ bad_bin := os.join_path(os.temp_dir(), 'v3_${name}')

20+ result := os.execute('${v3_bin} ${bad_src} -b c -o ${bad_bin}')

21+ assert result.exit_code != 0, '${name}: expected failure, got success\n${result.output}'

22+ assert result.output.contains(expected), '${name}: expected `${expected}` in\n${result.output}'

23+ assert !result.output.contains('C compilation failed'), '${name}: reached C compilation\n${result.output}'

24+}

25+

26+fn run_good(v3_bin string, name string, src string) string {

27+ good_src := os.join_path(os.temp_dir(), 'v3_${name}.v')

28+ os.write_file(good_src, src) or { panic(err) }

29+ good_bin := os.join_path(os.temp_dir(), 'v3_${name}')

30+ compile := os.execute('${v3_bin} ${good_src} -b c -o ${good_bin}')

31+ assert compile.exit_code == 0, '${name}: compile failed\n${compile.output}'

32+ assert !compile.output.contains('C compilation failed'), '${name}: C compilation failed\n${compile.output}'

33+ run := os.execute(good_bin)

34+ assert run.exit_code == 0, '${name}: run failed\n${run.output}'

35+ return run.output.trim_space()

36+}

37+

38+fn write_project_file(root string, rel string, src string) {

39+ path := os.join_path(root, rel)

40+ os.mkdir_all(os.dir(path)) or { panic(err) }

41+ os.write_file(path, src) or { panic(err) }

42+}

43+

44+fn run_good_project(v3_bin string, name string, files map[string]string, input string) string {

45+ root := os.join_path(os.temp_dir(), 'v3_${name}_project')

46+ if os.exists(root) {

47+ os.rmdir_all(root) or { panic(err) }

48+ }

49+ os.mkdir_all(root) or { panic(err) }

50+ for rel, src in files {

51+ write_project_file(root, rel, src)

52+ }

53+ input_path := if input.len == 0 { root } else { os.join_path(root, input) }

54+ good_bin := os.join_path(os.temp_dir(), 'v3_${name}')

55+ compile := os.execute('${v3_bin} ${input_path} -b c -o ${good_bin}')

56+ assert compile.exit_code == 0, '${name}: compile failed\n${compile.output}'

57+ assert !compile.output.contains('C compilation failed'), '${name}: C compilation failed\n${compile.output}'

58+ run := os.execute(good_bin)

59+ assert run.exit_code == 0, '${name}: run failed\n${run.output}'

60+ return run.output.trim_space()

61+}

62+

63+fn test_reject_dynamic_arrays_for_fixed_array_expectations() {

64+ v3_bin := build_v3_review_transform()

65+ run_bad(v3_bin, 'bad_fixed_array_literal_len',

66+ 'fn take3(a [3]int) int {\n\treturn a[0]\n}\nfn main() {\n\t_ := take3([1, 2])\n}\n',

67+ 'cannot use')

68+ run_bad(v3_bin, 'bad_dynamic_array_for_fixed_array',

69+ 'fn take3(a [3]int) int {\n\treturn a[0]\n}\nfn main() {\n\txs := [1, 2, 3]\n\t_ := take3(xs)\n}\n',

70+ 'cannot use')

71+ out := run_good(v3_bin, 'good_exact_fixed_array_literal',

72+ 'fn take3(a [3]int) int {\n\treturn a[0] + a[1] + a[2]\n}\nfn main() {\n\tprintln(int_str(take3([1, 2, 3])))\n}\n')

73+ assert out == '6'

74+ indexed := run_good(v3_bin, 'good_fixed_array_init_index',

75+ 'fn main() {\n\ta := [4]int{init: index * index}\n\tprintln(int_str(a[0]) + "," + int_str(a[1]) + "," + int_str(a[2]) + "," + int_str(a[3]))\n}\n')

76+ assert indexed == '0,1,4,9'

77+ const_indexed := run_good(v3_bin, 'good_fixed_array_const_init_index',

78+ 'const n = 4\n\nfn main() {\n\ta := [n]int{init: index * index}\n\tprintln(int_str(a[0]) + "," + int_str(a[1]) + "," + int_str(a[2]) + "," + int_str(a[3]))\n}\n')

79+ assert const_indexed == '0,1,4,9'

80+}

81+

82+fn test_array_equality_uses_semantic_element_comparison() {

83+ v3_bin := build_v3_review_transform()

84+ out := run_good(v3_bin, 'semantic_array_equality',

85+ "struct Child {\n\tlabel string\n}\n\nstruct Item {\n\tname string\n\tparts []string\n\tnested [][]string\n\tchildren []Child\n}\n\nfn join(a string, b string) string {\n\treturn a + b\n}\n\nfn main() {\n\tleft := [Item{\n\t\tname: 'hi'.clone()\n\t\tparts: ['ab'.clone()]\n\t\tnested: [[join('n', 'est')]]\n\t\tchildren: [Child{\n\t\t\tlabel: 'kid'.clone()\n\t\t}]\n\t}]\n\tright := [Item{\n\t\tname: join('h', 'i')\n\t\tparts: [join('a', 'b')]\n\t\tnested: [['nest'.clone()]]\n\t\tchildren: [Child{\n\t\t\tlabel: join('k', 'id')\n\t\t}]\n\t}]\n\tmaps_left := [{\n\t\t'k': 'value'.clone()\n\t}]\n\tmaps_right := [{\n\t\t'k': join('val', 'ue')\n\t}]\n\tnested_left := [[join('y', 'o')]]\n\tnested_right := [['yo'.clone()]]\n\tchild_map_left := {\n\t\t'items': [Child{\n\t\t\tlabel: 'mapkid'.clone()\n\t\t}]\n\t}\n\tchild_map_right := {\n\t\t'items': [Child{\n\t\t\tlabel: join('map', 'kid')\n\t\t}]\n\t}\n\tneedle := Item{\n\t\tname: join('h', 'i')\n\t\tparts: [join('a', 'b')]\n\t\tnested: [['nest'.clone()]]\n\t\tchildren: [Child{\n\t\t\tlabel: join('k', 'id')\n\t\t}]\n\t}\n\tprintln(left == right)\n\tprintln(left.equals(right))\n\tprintln(maps_left == maps_right)\n\tprintln(nested_left == nested_right)\n\tprintln(child_map_left == child_map_right)\n\tprintln(needle in left)\n\tprintln(int_str(left.index(needle)))\n}\n")

86+ assert out == 'true\ntrue\ntrue\ntrue\ntrue\ntrue\n0'

87+}

88+

89+fn test_nested_map_equality_uses_declared_value_type() {

90+ v3_bin := build_v3_review_transform()

91+ out := run_good(v3_bin, 'nested_map_semantic_equality',

92+ "struct Item {\n\tname string\n\tparts []string\n}\n\nstruct Holder {\n\titems map[string][]Item\n}\n\nfn join(a string, b string) string {\n\treturn a + b\n}\n\nfn main() {\n\tmut left_map := map[string][]Item{}\n\tleft_map['items'] = [Item{\n\t\tname: 'ab'.clone()\n\t\tparts: ['xy'.clone()]\n\t}]\n\tmut right_map := map[string][]Item{}\n\tright_map['items'] = [Item{\n\t\tname: join('a', 'b')\n\t\tparts: [join('x', 'y')]\n\t}]\n\tleft_arr := [left_map]\n\tright_arr := [right_map]\n\tleft_holder := Holder{\n\t\titems: left_map\n\t}\n\tright_holder := Holder{\n\t\titems: right_map\n\t}\n\tprintln(left_arr == right_arr)\n\tprintln(left_holder == right_holder)\n}\n")

93+ assert out == 'true\ntrue'

94+}

95+

96+fn test_pointer_array_equality_uses_pointer_identity() {

97+ v3_bin := build_v3_review_transform()

98+ out := run_good(v3_bin, 'pointer_array_equality',

99+ 'struct Node {\n\tvalue int\n}\n\nfn main() {\n\tleft_node := Node{\n\t\tvalue: 5\n\t}\n\tright_node := Node{\n\t\tvalue: 5\n\t}\n\tleft_ptr := &left_node\n\tright_ptr := &right_node\n\tleft := [left_ptr]\n\tright := [right_ptr]\n\tsame := [left_ptr]\n\tprintln(left == right)\n\tprintln(left != right)\n\tprintln(left == same)\n\tprintln(right_ptr in left)\n\tprintln(int_str(left.index(right_ptr)))\n}\n')

100+ assert out == 'false\ntrue\ntrue\nfalse\n-1'

101+}

102+

103+fn test_array_pointer_equality_uses_pointer_identity() {

104+ v3_bin := build_v3_review_transform()

105+ out := run_good(v3_bin, 'array_pointer_equality',

106+ 'fn main() {\n\tleft := [1, 2]\n\tright := [1, 2]\n\tleft_ptr := &left\n\tright_ptr := &right\n\tsame_ptr := left_ptr\n\tprintln(left_ptr == right_ptr)\n\tprintln(left_ptr != right_ptr)\n\tprintln(left_ptr == same_ptr)\n\tprintln(*left_ptr == *right_ptr)\n}\n')

107+ assert out == 'false\ntrue\ntrue\ntrue'

108+}

109+

110+fn test_map_pointer_equality_uses_pointer_identity() {

111+ v3_bin := build_v3_review_transform()

112+ out := run_good(v3_bin, 'map_pointer_equality',

113+ "fn main() {\n\tleft := {\n\t\t'x': 1\n\t}\n\tright := {\n\t\t'x': 1\n\t}\n\tleft_ptr := &left\n\tright_ptr := &right\n\tsame_ptr := left_ptr\n\tprintln(left_ptr == right_ptr)\n\tprintln(left_ptr != right_ptr)\n\tprintln(left_ptr == same_ptr)\n\tprintln(*left_ptr == *right_ptr)\n}\n")

114+ assert out == 'false\ntrue\ntrue\ntrue'

115+}

116+

117+fn test_fixed_array_values_compare_semantically() {

118+ v3_bin := build_v3_review_transform()

119+ out := run_good(v3_bin, 'fixed_array_semantic_equality',

120+ "fn join(a string, b string) string {\n\treturn a + b\n}\n\nfn main() {\n\tleft := [[1]string{init: 'ab'.clone()}]\n\tright := [[1]string{init: join('a', 'b')}]\n\tmut map_left := map[string][1]string{}\n\tmap_left['k'] = [1]string{init: 'cd'.clone()}\n\tmut map_right := map[string][1]string{}\n\tmap_right['k'] = [1]string{init: join('c', 'd')}\n\tmut ints_left := map[string][2]i64{}\n\tints_left['k'] = [i64(1), i64(0)]!\n\tmut ints_right := map[string][2]i64{}\n\tints_right['k'] = [i64(2), i64(0)]!\n\tprintln(left == right)\n\tprintln(left.equals(right))\n\tprintln(map_left == map_right)\n\tprintln(ints_left == ints_right)\n}\n")

121+ assert out == 'true\ntrue\ntrue\nfalse'

122+}

123+

124+fn test_const_length_fixed_array_map_values_compare_semantically() {

125+ v3_bin := build_v3_review_transform()

126+ out := run_good_project(v3_bin, 'const_len_fixed_array_map_equality', {

127+ 'main.v': 'module main\n\nimport store\n\nfn main() {\n\tprintln(store.check())\n}\n'

128+ 'store/store.v': "module store\n\nconst n = 2\n\nfn join(a string, b string) string {\n\treturn a + b\n}\n\npub fn check() bool {\n\tmut left := map[string][n]string{}\n\tleft['k'] = [n]string{init: 'ab'.clone()}\n\tmut right := map[string][n]string{}\n\tright['k'] = [n]string{init: join('a', 'b')}\n\treturn left == right\n}\n"

129+ }, 'main.v')

130+ assert out == 'true'

131+}

132+

133+fn test_interface_array_repeat_evaluates_receiver_once() {

134+ v3_bin := build_v3_review_transform()

135+ out := run_good(v3_bin, 'interface_repeat_side_effects',

136+ 'interface Thing {\n\tvalue() int\n}\n\nstruct Item {\n\tn int\n}\n\nfn (i Item) value() int {\n\treturn i.n\n}\n\n__global calls int\n\nfn make_item() Thing {\n\tcalls++\n\treturn Item{\n\t\tn: calls\n\t}\n}\n\nfn main() {\n\titems := [make_item()].repeat(3)\n\tprintln(int_str(calls))\n\tprintln(int_str(items[0].value() + items[1].value() + items[2].value()))\n}\n')

137+ assert out == '1\n3'

138+}

139+

140+fn test_comptime_type_conditions_handle_logical_ops() {

141+ v3_bin := build_v3_review_transform()

142+ out := run_good(v3_bin, 'comptime_type_condition_logical_ops',

143+ "fn classify[T](x T) int {\n\t_ := x\n\t\$if T !is string && T !is \$int && T !is []u8 {\n\t\treturn 1\n\t} \$else {\n\t\treturn 2\n\t}\n\treturn 0\n}\n\nfn grouped[T](x T) int {\n\t_ := x\n\t\$if (T is int || T is string) && T is bool {\n\t\treturn 1\n\t} \$else {\n\t\treturn 2\n\t}\n\treturn 0\n}\n\nfn main() {\n\tprintln(int_str(classify('abc')))\n\tprintln(int_str(classify(3)))\n\tprintln(int_str(classify([u8(1)])))\n\tprintln(int_str(classify(1.5)))\n\tprintln(int_str(grouped(3)))\n\tprintln(int_str(grouped('abc')))\n}\n")

144+ assert out == '2\n2\n2\n1\n2\n2'

145+}

146+

147+fn test_comptime_type_conditions_keep_prefix_types_compact() {

148+ v3_bin := build_v3_review_transform()

149+ out := run_good(v3_bin, 'comptime_type_condition_prefix_types',

150+ 'struct Foo {}\n\nfn main() {\n\t\$if ?int is ?int {\n\t\tprintln("opt")\n\t} \$else {\n\t\tprintln("badopt")\n\t}\n\t\$if !Foo is !Foo {\n\t\tprintln("res")\n\t} \$else {\n\t\tprintln("badres")\n\t}\n}\n')

151+ assert out == 'opt\nres'

152+}

153+

154+fn test_comptime_type_conditions_qualify_module_aliases() {

155+ v3_bin := build_v3_review_transform()

156+ out := run_good_project(v3_bin, 'comptime_type_condition_module_alias', {

157+ 'main.v': 'module main\n\nimport foo\n\nfn main() {\n\tprintln(foo.check())\n}\n'

158+ 'foo/foo.v': "module foo\n\ntype ID = int\n\npub fn check() string {\n\t\$if ID is \$alias {\n\t\treturn 'alias'\n\t} \$else {\n\t\treturn 'not alias'\n\t}\n}\n"

159+ }, 'main.v')

160+ assert out == 'alias'

161+}

162+

163+fn test_struct_equality_compares_pointer_fields_as_pointers() {

164+ v3_bin := build_v3_review_transform()

165+ out := run_good(v3_bin, 'struct_eq_pointer_field',

166+ 'struct Node {\n\tvalue int\n\tnext &Node\n}\n\nfn main() {\n\tleft := Node{\n\t\tvalue: 7\n\t\tnext: unsafe { nil }\n\t}\n\tright := Node{\n\t\tvalue: 7\n\t\tnext: unsafe { nil }\n\t}\n\tprintln([left] == [right])\n}\n')

167+ assert out == 'true'

168+}

169+

170+fn test_single_module_test_file_skips_premodule_attributes() {

171+ v3_bin := build_v3_review_transform()

172+ root := os.join_path(os.temp_dir(), 'v3_premodule_attr_module_test')

173+ os.rmdir_all(root) or {}

174+ os.mkdir_all(os.join_path(root, 'tar')) or { panic(err) }

175+ os.write_file(os.join_path(root, 'v.mod'), 'Module { name: "premodule_attr_module_test" }\n') or {

176+ panic(err)

177+ }

178+ os.write_file(os.join_path(root, 'tar', 'reader.v'),

179+ 'module tar /* implementation module */\n\nfn reader_value() string {\n\treturn "reader"\n}\n') or {

180+ panic(err)

181+ }

182+ test_file := os.join_path(root, 'tar', 'reader_test.v')

183+ os.write_file(test_file,

184+ '@[has_globals]\n/* block comment before module */\nmodule tar // test module\n\nfn test_reader_value() {\n\tprintln(reader_value())\n}\n') or {

185+ panic(err)

186+ }

187+ bin_path := os.join_path(root, 'reader_test_bin')

188+ compile := os.execute('${v3_bin} ${test_file} -b c -o ${bin_path}')

189+ assert compile.exit_code == 0, compile.output

190+ run := os.execute(bin_path)

191+ assert run.exit_code == 0, run.output

192+ assert run.output.trim_space() == 'reader'

193+}

194+

195+fn test_delete_last_empty_array_panics_before_tail_clear() {

196+ v3_bin := build_v3_review_transform()

197+ src := 'fn main() {\n\tmut values := []int{}\n\tvalues.delete_last()\n\tprintln("after")\n}\n'

198+ good_src := os.join_path(os.temp_dir(), 'v3_delete_last_empty.v')

199+ os.write_file(good_src, src) or { panic(err) }

200+ good_bin := os.join_path(os.temp_dir(), 'v3_delete_last_empty')

201+ compile := os.execute('${v3_bin} ${good_src} -b c -o ${good_bin}')

202+ assert compile.exit_code == 0, compile.output

203+ run := os.execute(good_bin)

204+ assert run.exit_code != 0, run.output

205+ assert run.output.contains('array.delete_last: array is empty'), run.output

206+}

207+

208+fn test_delete_last_preserves_shared_slice_buffer() {

209+ v3_bin := build_v3_review_transform()

210+ out := run_good(v3_bin, 'delete_last_preserves_shared_slice_buffer',

211+ "fn main() {\n\tmut a := [1, 2, 3, 4]\n\tb := unsafe { a[..a.len] }\n\told_data := a.data\n\ta.delete_last()\n\tassert a == [1, 2, 3]\n\tassert b == [1, 2, 3, 4]\n\tassert a.data != old_data\n\tprintln('ok')\n}\n")

212+ assert out == 'ok'

213+}

214+

215+fn test_slice_element_assignment_writes_through() {

216+ v3_bin := build_v3_review_transform()

217+ out := run_good(v3_bin, 'slice_element_assignment_writes_through',

218+ "fn main() {\n\tmut a := [1, 2, 3, 4]\n\tmut s := unsafe { a[1..3] }\n\ts[0] = 42\n\ts[1] += 5\n\tassert a == [1, 42, 8, 4]\n\tassert s == [42, 8]\n\tprintln('ok')\n}\n")

219+ assert out == 'ok'

220+}

221+

222+fn test_string_pointer_comparisons_keep_pointer_semantics() {

223+ v3_bin := build_v3_review_transform()

224+ out := run_good(v3_bin, 'string_pointer_comparison',

225+ "fn main() {\n\tleft := 'same'.clone()\n\tright := 'same'.clone()\n\tpleft := &left\n\tpright := &right\n\tprintln(pleft == pright)\n\tprintln(pleft != pright)\n\tprintln(*pleft == *pright)\n}\n")

226+ assert out == 'false\ntrue\ntrue'

227+}

228+

229+fn test_map_keys_and_values_lower_to_runtime_methods() {

230+ v3_bin := build_v3_review_transform()

231+ out := run_good(v3_bin, 'map_keys_values_lowering',

232+ "fn make_lookup() map[string]int {\n\treturn {\n\t\t'one': 1\n\t\t'two': 2\n\t}\n}\n\nfn main() {\n\tlookup := make_lookup()\n\tkeys := lookup.keys()\n\tvalues := make_lookup().values()\n\tmut total := 0\n\tfor value in values {\n\t\ttotal += value\n\t}\n\tprintln(int_str(keys.len))\n\tprintln(int_str(values.len))\n\tprintln(int_str(total))\n}\n")

233+ assert out == '2\n2\n3'

234+}

235+

236+fn test_map_str_preserves_signed_wide_entries() {

237+ v3_bin := build_v3_review_transform()

238+ out := run_good(v3_bin, 'map_str_signed_wide_entries',

239+ "fn main() {\n\tvalue_map := {\n\t\t'x': i64(5000000000)\n\t}\n\tkey_map := {\n\t\ti64(-5000000000): 'x'\n\t}\n\tprintln(value_map.str())\n\tprintln(key_map.str())\n}\n")

240+ assert out == "{'x': 5000000000}\n{-5000000000: 'x'}"

241+}

242+

243+fn test_map_str_normalizes_alias_key_and_value_types() {

244+ v3_bin := build_v3_review_transform()

245+ out := run_good(v3_bin, 'map_str_alias_kinds',

246+ "type ID = int\n\ntype Amount = f64\n\nfn main() {\n\tids := {\n\t\tID(23): 'id'\n\t}\n\tamounts := {\n\t\t'price': Amount(1.25)\n\t}\n\tprintln('\${ids}')\n\tprintln('\${amounts}')\n}\n")

247+ assert out == "{23: 'id'}\n{'price': 1.25}"

248+}

249+

250+fn test_map_literal_stringification_evaluates_entries_once() {

251+ v3_bin := build_v3_review_transform()

252+ out := run_good(v3_bin, 'map_literal_str_side_effects',

253+ "__global key_calls int\n__global val_calls int\n\nfn next_key() string {\n\tkey_calls++\n\treturn 'k' + int_str(key_calls)\n}\n\nfn next_val() int {\n\tval_calls++\n\treturn val_calls * 10\n}\n\nfn main() {\n\tprintln({\n\t\tnext_key(): next_val()\n\t})\n\tprintln(int_str(key_calls) + ',' + int_str(val_calls))\n}\n")

254+ assert out == "{'k1': 10}\n1,1"

255+}

256+

257+fn test_shadowed_minmaxof_calls_are_not_rewritten() {

258+ v3_bin := build_v3_review_transform()

259+ out := run_good_project(v3_bin, 'shadowed_minmaxof_calls', {

260+ 'main.v': 'module main\n\nimport shadow { maxof, minof }\n\nfn main() {\n\tprintln(int_str(maxof[int]()))\n\tprintln(int_str(minof[int]()))\n}\n'

261+ 'shadow/shadow.v': 'module shadow\n\npub fn maxof[T]() int {\n\treturn 7\n}\n\npub fn minof[T]() int {\n\treturn -7\n}\n'

262+ }, 'main.v')

263+ assert out == '7\n-7'

264+}

265+

266+fn test_runes_iterator_index_is_loop_scoped() {

267+ v3_bin := build_v3_review_transform()

268+ out := run_good(v3_bin, 'runes_iterator_index_scope',

269+ "fn main() {\n\tfor i, r in 'ab'.runes_iterator() {\n\t\t_ := r\n\t\tprintln(int_str(i))\n\t}\n\ti := 9\n\tprintln(int_str(i))\n}\n")

270+ assert out == '0\n1\n9'

271+}

272+

273+fn test_array_last_index_uses_element_width() {

274+ v3_bin := build_v3_review_transform()

275+ out := run_good(v3_bin, 'array_last_index_element_width',

276+ 'fn main() {\n\twide := [i64(1), i64(5000000000), i64(2), i64(5000000000)]\n\tfloats := [1.25, 2.5, 1.25]\n\tflags := [true, false, true]\n\tprintln(int_str(wide.last_index(i64(5000000000))))\n\tprintln(int_str(floats.last_index(1.25)))\n\tprintln(int_str(flags.last_index(true)))\n}\n')

277+ assert out == '3\n2\n2'

278+}

279+

280+fn test_array_last_index_uses_semantic_element_comparison() {

281+ v3_bin := build_v3_review_transform()

282+ out := run_good(v3_bin, 'array_last_index_semantic_equality',

283+ "struct Item {\n\tname string\n\tparts []string\n}\n\nfn join(a string, b string) string {\n\treturn a + b\n}\n\nfn main() {\n\tnested := [['ab'.clone()], [join('x', 'y')], [join('a', 'b')]]\n\tnested_needle := ['ab'.clone()]\n\titems := [Item{\n\t\tname: 'ab'.clone()\n\t\tparts: ['xy'.clone()]\n\t}, Item{\n\t\tname: join('a', 'b')\n\t\tparts: [join('x', 'y')]\n\t}]\n\tneedle := Item{\n\t\tname: 'ab'.clone()\n\t\tparts: ['xy'.clone()]\n\t}\n\tprintln(int_str(nested.last_index(nested_needle)))\n\tprintln(int_str(items.last_index(needle)))\n}\n")

284+ assert out == '2\n1'

285+}

286+

287+fn test_hierarchical_import_runtime_inits_before_importer_init() {

288+ v3_bin := build_v3_review_transform()

289+ out := run_good_project(v3_bin, 'hierarchical_runtime_init_order', {

290+ 'main.v': 'module main\n\nimport foo.user\nimport bar as shortbar\n\nfn main() {\n\t_ := shortbar.value()\n\tprintln(int_str(user.value()))\n}\n'

291+ 'foo/user/user.v': 'module user\n\nimport foo.bar as foobar\n\n__global seen int\n\nfn init() {\n\tseen = foobar.value() + 1\n}\n\npub fn value() int {\n\treturn seen\n}\n'

292+ 'foo/bar/bar.v': 'module bar\n\n__global flag = make_flag()\n\nfn make_flag() int {\n\treturn 40\n}\n\npub fn value() int {\n\treturn flag\n}\n'

293+ 'bar/bar.v': 'module bar\n\n__global flag = make_flag()\n\nfn make_flag() int {\n\treturn 3\n}\n\npub fn value() int {\n\treturn flag\n}\n'

294+ 'unrelated/other.v': 'module other\n\npub fn value() int {\n\treturn 0\n}\n'

295+ }, 'main.v')

296+ assert out == '41'

297+}

vlib/v3/transform/array.v +498 -16

@@ -9,6 +9,123 @@ fn (mut t Transformer) make_array_new_call(elem_type string, len_expr flat.NodeI

99 '[]${elem_type}')

1010}

1111

12+fn (mut t Transformer) try_lower_array_repeat_call(_id flat.NodeId, node flat.Node) ?flat.NodeId {

13+ if node.children_count != 2 {

14+ return none

15+ }

16+ fn_id := t.a.child(&node, 0)

17+ fn_node := t.a.nodes[int(fn_id)]

18+ if fn_node.kind != .selector || fn_node.value != 'repeat' || fn_node.children_count == 0 {

19+ return none

20+ }

21+ base_id := t.a.child(&fn_node, 0)

22+ base_type := t.node_type(base_id)

23+ count_id := t.a.child(&node, 1)

24+ if expanded := t.try_expand_interface_array_literal_repeat(base_id, count_id, base_type) {

25+ return expanded

26+ }

27+ depth := array_repeat_clone_depth(base_type)

28+ if depth == 0 {

29+ return none

30+ }

31+ base := t.transform_expr(base_id)

32+ count := t.transform_expr(count_id)

33+ selector := t.make_selector(base, 'repeat_to_depth', '')

34+ return t.make_call_expr_typed(selector, arr2(count, t.make_int_literal(depth)), node.typ)

35+}

36+

37+fn (mut t Transformer) try_expand_interface_array_literal_repeat(base_id flat.NodeId, count_id flat.NodeId, base_type string) ?flat.NodeId {

38+ if !base_type.starts_with('[]') {

39+ return none

40+ }

41+ elem_type := base_type[2..]

42+ if elem_type !in t.tc.interface_names && t.tc.qualify_name(elem_type) !in t.tc.interface_names {

43+ return none

44+ }

45+ base := t.a.nodes[int(base_id)]

46+ count_node := t.a.nodes[int(count_id)]

47+ if base.kind != .array_literal || count_node.kind != .int_literal {

48+ return none

49+ }

50+ count := count_node.value.int()

51+ if count < 0 || count > 32 {

52+ return none

53+ }

54+ if !t.array_repeat_literal_can_duplicate(base) {

55+ return none

56+ }

57+ mut values := []flat.NodeId{cap: int(base.children_count) * count}

58+ for _ in 0 .. count {

59+ for i in 0 .. base.children_count {

60+ values << t.a.child(&base, i)

61+ }

62+ }

63+ lit := t.make_array_literal_typed(values, base_type)

64+ return t.transform_array_literal(lit, t.a.nodes[int(lit)])

65+}

66+

67+fn (t &Transformer) array_repeat_literal_can_duplicate(node flat.Node) bool {

68+ for i in 0 .. node.children_count {

69+ if !t.array_repeat_expr_can_duplicate(t.a.child(&node, i)) {

70+ return false

71+ }

72+ }

73+ return true

74+}

75+

76+fn (t &Transformer) array_repeat_expr_can_duplicate(id flat.NodeId) bool {

77+ node := t.a.nodes[int(id)]

78+ match node.kind {

79+ .int_literal, .float_literal, .bool_literal, .char_literal, .string_literal, .ident,

80+ .enum_val, .nil_literal, .none_expr {

81+ return true

82+ }

83+ .paren, .prefix, .postfix, .cast_expr, .as_expr, .field_init, .array_literal, .struct_init {

84+ for i in 0 .. node.children_count {

85+ if !t.array_repeat_expr_can_duplicate(t.a.child(&node, i)) {

86+ return false

87+ }

88+ }

89+ return true

90+ }

91+ else {

92+ return false

93+ }

94+ }

95+}

96+

97+fn array_repeat_clone_depth(typ string) int {

98+ mut clean := typ

99+ if clean.starts_with('&') {

100+ clean = clean[1..]

101+ }

102+ mut depth := 0

103+ for clean.starts_with('[]') {

104+ depth++

105+ clean = clean[2..]

106+ }

107+ if depth <= 1 {

108+ return 0

109+ }

110+ return depth - 1

111+}

112+

113+fn array_nested_eq_depth(typ string) int {

114+ mut clean := typ

115+ if clean.starts_with('&') {

116+ clean = clean[1..]

117+ }

118+ mut depth := 0

119+ for clean.starts_with('[]') {

120+ depth++

121+ clean = clean[2..]

122+ }

123+ if depth <= 1 {

124+ return 1

125+ }

126+ return depth

127+}

128+

12129fn (mut t Transformer) make_array_push_many_call(lhs_addr flat.NodeId, rhs flat.NodeId, rhs_type string) flat.NodeId {

13130 t.mark_fn_used('array__push_many')

14131 rhs_value := t.stable_transformed_expr_for_reuse(rhs, rhs_type, 'push_many')

@@ -16,10 +133,30 @@ fn (mut t Transformer) make_array_push_many_call(lhs_addr flat.NodeId, rhs flat.

16133 'voidptr'), t.make_selector(rhs_value, 'len', 'int')), 'void')

17134}

18135

136+fn (mut t Transformer) make_array_insert_many_call(lhs_addr flat.NodeId, index flat.NodeId, rhs flat.NodeId, rhs_type string) flat.NodeId {

137+ if t.is_fixed_array_type(rhs_type) {

138+ return t.make_call_typed('array__insert_many', arr4(lhs_addr, index, rhs,

139+ t.make_fixed_array_len_expr(rhs_type)), 'void')

140+ }

141+ rhs_value := t.stable_transformed_expr_for_reuse(rhs, rhs_type, 'insert_many')

142+ return t.make_call_typed('array__insert_many', arr4(lhs_addr, index, t.make_selector(rhs_value,

143+ 'data', 'voidptr'), t.make_selector(rhs_value, 'len', 'int')), 'void')

144+}

145+

19146fn (mut t Transformer) make_array_clone_call(base_id flat.NodeId, base_type string) flat.NodeId {

20147 t.mark_fn_used('array__clone')

21148 receiver := t.transform_expr(base_id)

22- return t.make_call_typed('array__clone', arr1(t.runtime_addr(receiver, base_type)), base_type)

149+ return t.make_array_clone_value(receiver, base_type)

150+}

151+

152+fn (mut t Transformer) make_array_clone_value(receiver flat.NodeId, base_type string) flat.NodeId {

153+ clean_type := if base_type.starts_with('&') { base_type[1..] } else { base_type }

154+ depth := array_repeat_clone_depth(clean_type)

155+ if depth > 0 {

156+ return t.make_call_typed('array__clone_to_depth', arr2(t.runtime_addr(receiver, base_type),

157+ t.make_int_literal(depth)), clean_type)

158+ }

159+ return t.make_call_typed('array__clone', arr1(t.runtime_addr(receiver, base_type)), clean_type)

23160}

24161

25162// lower_array_init_to_runtime converts lower array init to runtime data for transform.

@@ -46,7 +183,15 @@ fn (mut t Transformer) lower_array_init_to_runtime(id flat.NodeId, node flat.Nod

46183 }

47184 new_call := t.make_array_new_call(elem_type, len_expr, cap_expr)

48185 if int(init_expr) < 0 {

49- return new_call

186+ clean_elem_type := t.normalize_type_alias(elem_type)

187+ if clean_elem_type.starts_with('[]') {

188+ init_expr = t.make_array_new_call(clean_elem_type[2..], t.make_int_literal(0),

189+ t.make_int_literal(0))

190+ } else if default_value := t.make_struct_runtime_default_value(clean_elem_type) {

191+ init_expr = default_value

192+ } else {

193+ return new_call

194+ }

50195 }

51196 tmp_name := t.new_temp('arr_init')

52197 idx_name := t.new_temp('arr_idx')

@@ -56,18 +201,80 @@ fn (mut t Transformer) lower_array_init_to_runtime(id flat.NodeId, node flat.Nod

56201 'len', 'int'))

57202 post := t.make_expr_stmt(t.make_postfix(t.make_ident(idx_name), .inc))

58203 elem_lhs := t.make_index(t.make_ident(tmp_name), t.make_ident(idx_name), elem_type)

59- assign := t.make_assign(elem_lhs, init_expr)

60204 // `init:` expressions may reference the magic `index` variable, which V binds to

61205 // the current element index. Declare it inside the loop body so it resolves to the

62206 // generated loop counter instead of leaking to an external symbol (e.g. libc `index`).

63207 index_decl := t.make_decl_assign_typed('index', t.make_ident(idx_name), 'int')

64- t.pending_stmts << t.make_for_stmt(init_idx, cond, post, arr2(index_decl, assign), node)

65- return t.make_ident(tmp_name)

208+ mut loop_body := []flat.NodeId{}

209+ loop_body << index_decl

210+ mut assign_value := init_expr

211+ clean_elem_type := t.normalize_type_alias(elem_type)

212+ if clean_elem_type.starts_with('[]') {

213+ if !t.expr_can_take_address(assign_value) {

214+ value_name := t.new_temp('arr_init_val')

215+ loop_body << t.make_decl_assign_typed(value_name, assign_value, clean_elem_type)

216+ assign_value = t.make_ident(value_name)

217+ }

218+ assign_value = t.make_array_clone_value(assign_value, clean_elem_type)

219+ }

220+ assign := t.make_assign(elem_lhs, assign_value)

221+ loop_body << assign

222+ t.pending_stmts << t.make_for_stmt(init_idx, cond, post, loop_body, node)

223+ result := t.make_ident(tmp_name)

224+ t.a.nodes[int(result)].typ = '[]${elem_type}'

225+ return result

226+}

227+

228+fn (mut t Transformer) make_struct_runtime_default_value(struct_type string) ?flat.NodeId {

229+ info := t.lookup_struct_info(struct_type) or { return none }

230+ mut field_ids := []flat.NodeId{}

231+ for field in info.fields {

232+ field_type := if field.typ.len > 0 { field.typ } else { field.raw_typ }

233+ clean_type := t.normalize_type_alias(field_type)

234+ mut value := flat.empty_node

235+ if clean_type.starts_with('map[') || clean_type.starts_with('[]') {

236+ value = t.zero_value_for_type(clean_type)

237+ } else if nested := t.make_struct_runtime_default_value(clean_type) {

238+ value = nested

239+ }

240+ if int(value) < 0 {

241+ continue

242+ }

243+ start := t.a.children.len

244+ t.a.children << value

245+ field_ids << t.a.add_node(flat.Node{

246+ kind: .field_init

247+ children_start: start

248+ children_count: 1

249+ value: field.name

250+ typ: field_type

251+ })

252+ }

253+ if field_ids.len == 0 {

254+ return none

255+ }

256+ start := t.a.children.len

257+ for field_id in field_ids {

258+ t.a.children << field_id

259+ }

260+ return t.a.add_node(flat.Node{

261+ kind: .struct_init

262+ children_start: start

263+ children_count: flat.child_count(field_ids.len)

264+ value: struct_type

265+ typ: struct_type

266+ })

66267}

67268

68269// lower_array_literal_to_runtime converts lower array literal to runtime data for transform.

69270fn (mut t Transformer) lower_array_literal_to_runtime(id flat.NodeId, node flat.Node) flat.NodeId {

70- array_type := t.node_type(id)

271+ array_type := if checker_alias_type := t.array_literal_checker_alias_type(id) {

272+ checker_alias_type

273+ } else if alias_type := t.array_literal_alias_type(node) {

274+ alias_type

275+ } else {

276+ t.node_type(id)

277+ }

71278 if !array_type.starts_with('[]') {

72279 return id

73280 }

@@ -95,19 +302,67 @@ fn (mut t Transformer) lower_array_literal_to_runtime(id flat.NodeId, node flat.

95302 value := if elem_type in t.sum_types || t.resolve_sum_name(elem_type) in t.sum_types {

96303 t.wrap_sum_value(elem_id, elem_type)

97304 } else {

98- t.transform_expr(elem_id)

305+ t.transform_expr_for_type(elem_id, elem_type)

99306 }

100307 t.pending_stmts << t.make_decl_assign_typed(value_name, value, elem_type)

101308 call := t.make_call_typed('array_push', arr2(t.make_prefix(.amp, t.make_ident(tmp_name)), t.make_prefix(.amp,

102309 t.make_ident(value_name))), 'void')

103310 t.pending_stmts << t.make_expr_stmt(call)

104311 }

105- return t.make_ident(tmp_name)

312+ result := t.make_ident(tmp_name)

313+ t.a.nodes[int(result)].typ = array_type

314+ return result

315+}

316+

317+fn (t &Transformer) array_literal_checker_alias_type(id flat.NodeId) ?string {

318+ if isnil(t.tc) || int(id) < 0 {

319+ return none

320+ }

321+ typ := t.tc.expr_type(id) or { t.tc.resolve_type(id) }

322+ name := typ.name()

323+ if !name.starts_with('[]') {

324+ return none

325+ }

326+ elem := name[2..]

327+ if !t.generic_arg_is_alias_name(elem, t.cur_module) {

328+ return none

329+ }

330+ return '[]${elem.all_after_last('.')}'

331+}

332+

333+fn (t &Transformer) array_literal_alias_type(node flat.Node) ?string {

334+ if node.kind != .array_literal || node.children_count == 0 {

335+ return none

336+ }

337+ first_id := t.a.child(&node, 0)

338+ first := t.a.nodes[int(first_id)]

339+ mut alias_name := ''

340+ if first.kind in [.cast_expr, .as_expr] && first.value.len > 0 {

341+ alias_name = first.value

342+ } else if first.kind == .call && first.children_count > 0 {

343+ callee := t.a.child_node(&first, 0)

344+ if callee.kind == .ident {

345+ alias_name = callee.value

346+ }

347+ }

348+ if alias_name.len == 0 {

349+ return none

350+ }

351+ if !t.generic_arg_is_alias_name(alias_name, t.cur_module) {

352+ return none

353+ }

354+ return '[]${alias_name}'

106355}

107356

108357// transform_array_literal_for_type transforms transform array literal for type data for transform.

109-fn (mut t Transformer) transform_array_literal_for_type(_id flat.NodeId, node flat.Node, target_type string) ?flat.NodeId {

110- array_type := t.normalize_type_alias(target_type)

358+fn (mut t Transformer) transform_array_literal_for_type(id flat.NodeId, node flat.Node, target_type string) ?flat.NodeId {

359+ array_type := if checker_alias_type := t.array_literal_checker_alias_type(id) {

360+ checker_alias_type

361+ } else if alias_type := t.array_literal_alias_type(node) {

362+ alias_type

363+ } else {

364+ t.normalize_type_alias(target_type)

365+ }

111366 if !array_type.starts_with('[]') {

112367 return none

113368 }

@@ -136,7 +391,9 @@ fn (mut t Transformer) transform_array_literal_for_type(_id flat.NodeId, node fl

136391 t.make_ident(value_name))), 'void')

137392 t.pending_stmts << t.make_expr_stmt(call)

138393 }

139- return t.make_ident(tmp_name)

394+ result := t.make_ident(tmp_name)

395+ t.a.nodes[int(result)].typ = array_type

396+ return result

140397}

141398

142399fn (mut t Transformer) transform_fixed_array_literal_for_type(_id flat.NodeId, node flat.Node, target_type string) ?flat.NodeId {

@@ -153,6 +410,40 @@ fn (mut t Transformer) transform_fixed_array_literal_for_type(_id flat.NodeId, n

153410 return t.make_array_literal_typed(values, fixed_type)

154411}

155412

413+fn (mut t Transformer) transform_fixed_array_init_expr(node flat.Node) ?flat.NodeId {

414+ fixed_type := t.normalize_type_alias(if node.value.len > 0 { node.value } else { node.typ })

415+ if !t.is_fixed_array_type(fixed_type) || node.children_count == 0 {

416+ return none

417+ }

418+ len_text := fixed_array_len_text(fixed_type)

419+ len := if is_decimal_text(len_text) {

420+ len_text.int()

421+ } else if !isnil(t.tc) {

422+ t.tc.const_int_value_in_module(len_text, t.cur_module, []string{}) or { return none }

423+ } else {

424+ return none

425+ }

426+ mut init_id := flat.empty_node

427+ for i in 0 .. node.children_count {

428+ child_id := t.a.child(&node, i)

429+ child := t.a.nodes[int(child_id)]

430+ if child.kind == .field_init && child.value == 'init' && child.children_count > 0 {

431+ init_id = t.a.child(&child, 0)

432+ break

433+ }

434+ }

435+ if int(init_id) < 0 {

436+ return none

437+ }

438+ elem_type := fixed_array_elem_type(fixed_type)

439+ mut values := []flat.NodeId{cap: len}

440+ for i in 0 .. len {

441+ indexed_init := t.substitute_ident_expr(init_id, 'index', t.make_int_literal(i))

442+ values << t.transform_expr_for_type(indexed_init, elem_type)

443+ }

444+ return t.make_array_literal_typed(values, fixed_type)

445+}

446+

156447// transform_empty_array_init_for_type supports transform_empty_array_init_for_type handling.

157448fn (mut t Transformer) transform_empty_array_init_for_type(node flat.Node, target_type string) ?flat.NodeId {

158449 if node.value.len > 0 || node.children_count > 0 {

@@ -298,7 +589,24 @@ fn (mut t Transformer) lower_array_prepend_call(node flat.Node, fn_node flat.Nod

298589 }

299590 base_id := t.a.child(&fn_node, 0)

300591 value_id := t.a.child(&node, 1)

592+ mut rhs_type := t.normalize_type_alias(t.node_type(value_id))

593+ value_node := t.a.nodes[int(value_id)]

594+ mut prepend_many := t.array_append_rhs_is_push_many(base_id, value_id, rhs_type, elem_type)

595+ if value_node.kind == .array_literal && !elem_type.starts_with('[]')

596+ && !t.is_fixed_array_type(elem_type) {

597+ prepend_many = true

598+ t.a.nodes[int(value_id)].typ = base_type

599+ rhs_type = base_type

600+ } else if prepend_many && value_node.kind == .array_literal && !rhs_type.starts_with('[]') {

601+ t.a.nodes[int(value_id)].typ = base_type

602+ rhs_type = base_type

603+ }

301604 base := t.transform_lvalue(base_id)

605+ if prepend_many {

606+ value := t.transform_expr(value_id)

607+ return t.make_array_insert_many_call(t.runtime_addr(base, base_type),

608+ t.make_int_literal(0), value, rhs_type)

609+ }

302610 value := if elem_type in t.sum_types || t.resolve_sum_name(elem_type) in t.sum_types {

303611 t.wrap_sum_value(value_id, elem_type)

304612 } else {

@@ -321,8 +629,25 @@ fn (mut t Transformer) lower_array_insert_call(node flat.Node, fn_node flat.Node

321629 base_id := t.a.child(&fn_node, 0)

322630 index_id := t.a.child(&node, 1)

323631 value_id := t.a.child(&node, 2)

632+ mut rhs_type := t.normalize_type_alias(t.node_type(value_id))

633+ value_node := t.a.nodes[int(value_id)]

634+ mut insert_many := t.array_append_rhs_is_push_many(base_id, value_id, rhs_type, elem_type)

635+ if value_node.kind == .array_literal && !elem_type.starts_with('[]')

636+ && !t.is_fixed_array_type(elem_type) {

637+ insert_many = true

638+ t.a.nodes[int(value_id)].typ = base_type

639+ rhs_type = base_type

640+ } else if insert_many && value_node.kind == .array_literal && !rhs_type.starts_with('[]') {

641+ t.a.nodes[int(value_id)].typ = base_type

642+ rhs_type = base_type

643+ }

324644 base := t.transform_lvalue(base_id)

325645 index := t.transform_expr_for_type(index_id, 'int')

646+ if insert_many {

647+ value := t.transform_expr(value_id)

648+ return t.make_array_insert_many_call(t.runtime_addr(base, base_type), index, value,

649+ rhs_type)

650+ }

326651 value := if elem_type in t.sum_types || t.resolve_sum_name(elem_type) in t.sum_types {

327652 t.wrap_sum_value(value_id, elem_type)

328653 } else {

@@ -373,11 +698,17 @@ fn (t &Transformer) push_many_count_is_type_name(id flat.NodeId) bool {

373698// array_append_rhs_is_push_many supports array append rhs is push many handling for Transformer.

374699fn (t &Transformer) array_append_rhs_is_push_many(lhs_id flat.NodeId, rhs_id flat.NodeId, rhs_type string, elem_type string) bool {

375700 clean_rhs_type := rhs_type.trim_space()

701+ if clean_rhs_type.starts_with('...'