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

702+ return t.array_append_elem_types_match(clean_rhs_type[3..], elem_type)

703+ }

376704 if clean_rhs_type.starts_with('[]') {

377705 if t.array_append_elem_types_match(clean_rhs_type[2..], elem_type) {

378706 return true

379707 }

380708 if declared_rhs_type := t.array_append_ident_type(rhs_id) {

709+ if declared_rhs_type.starts_with('...') {

710+ return t.array_append_elem_types_match(declared_rhs_type[3..], elem_type)

711+ }

381712 if declared_rhs_type.starts_with('[]') {

382713 return t.array_append_elem_types_match(declared_rhs_type[2..], elem_type)

383714 }

@@ -501,6 +832,7 @@ fn (mut t Transformer) lower_array_filter_call(node flat.Node, fn_node flat.Node

501832 predicate_node := t.a.nodes[int(predicate_id)]

502833 mut predicate_expr_id := predicate_id

503834 mut lambda_param := ''

835+ mut predicate_fn_name := ''

504836 if predicate_node.kind == .lambda_expr && predicate_node.children_count > 0 {

505837 predicate_expr_id = t.a.child(&predicate_node, predicate_node.children_count - 1)

506838 if predicate_node.children_count > 1 {

@@ -509,6 +841,14 @@ fn (mut t Transformer) lower_array_filter_call(node flat.Node, fn_node flat.Node

509841 lambda_param = param.value

510842 }

511843 }

844+ } else if predicate_node.kind == .ident {

845+ if fn_name := t.resolve_fn_value_ident(predicate_node.value) {

846+ predicate_fn_name = fn_name

847+ } else if ret_type := t.fn_value_return_type_name(predicate_id) {

848+ if ret_type == 'bool' {

849+ predicate_fn_name = predicate_node.value

850+ }

851+ }

512852 }

513853 elem_name := if lambda_param.len > 0 { lambda_param } else { elem_name_default }

514854 elem_decl := t.make_decl_assign_typed(elem_name, elem_expr, elem_type)

@@ -519,7 +859,23 @@ fn (mut t Transformer) lower_array_filter_call(node flat.Node, fn_node flat.Node

519859 } else {

520860 t.substitute_ident(predicate_expr_id, 'it', elem_name)

521861 }

522- predicate := t.transform_expr(predicate_source)

862+ saved_pending := t.pending_stmts.clone()

863+ t.pending_stmts.clear()

864+ predicate := if predicate_fn_name.len > 0 {

865+ t.make_call_typed(predicate_fn_name, arr1(t.make_ident(elem_name)), 'bool')

866+ } else if predicate_node.kind == .fn_literal {

867+ fn_value := t.transform_expr(predicate_id)

868+ fn_value_node := t.a.nodes[int(fn_value)]

869+ if fn_value_node.kind == .ident {

870+ t.make_call_typed(fn_value_node.value, arr1(t.make_ident(elem_name)), 'bool')

871+ } else {

872+ fn_value

873+ }

874+ } else {

875+ t.transform_expr(predicate_source)

876+ }

877+ predicate_pending := t.pending_stmts.clone()

878+ t.pending_stmts = saved_pending

523879 if old_elem.len > 0 {

524880 t.set_var_type(elem_name, old_elem)

525881 } else {

@@ -527,7 +883,9 @@ fn (mut t Transformer) lower_array_filter_call(node flat.Node, fn_node flat.Node

527883 }

528884 mut loop_body := []flat.NodeId{}

529885 loop_body << elem_decl

530- t.drain_pending(mut loop_body)

886+ for stmt in predicate_pending {

887+ loop_body << stmt

888+ }

531889 push_call := t.make_call_typed('array_push', arr2(t.make_prefix(.amp, t.make_ident(out_name)), t.make_prefix(.amp,

532890 t.make_ident(elem_name))), 'void')

533891 then_block := t.make_block(arr1(t.make_expr_stmt(push_call)))

@@ -554,7 +912,16 @@ fn (mut t Transformer) lower_array_map_call(node flat.Node, fn_node flat.Node, b

554912 mapped_source := t.substitute_ident(map_expr_id, 'it', elem_name)

555913 mut result_elem_type := t.node_type(map_expr_id)

556914 mut direct_selector_type := ''

557- mapped_source_node := t.a.nodes[int(mapped_source)]

915+ mut mapped_source_node := t.a.nodes[int(mapped_source)]

916+ if mapped_source_node.kind == .map_init {

917+ inferred_map_type := t.infer_map_init_entry_type(mapped_source_node)

918+ if inferred_map_type.len > 0 {

919+ result_elem_type = inferred_map_type

920+ t.a.nodes[int(mapped_source)].value = inferred_map_type

921+ t.a.nodes[int(mapped_source)].typ = inferred_map_type

922+ mapped_source_node = t.a.nodes[int(mapped_source)]

923+ }

924+ }

558925 if mapped_source_node.kind == .selector {

559926 selector_type := t.resolve_selector_type(mapped_source_node)

560927 if selector_type.len > 0 {

@@ -572,6 +939,13 @@ fn (mut t Transformer) lower_array_map_call(node flat.Node, fn_node flat.Node, b

572939 result_elem_type = t.normalize_type_alias(ret_type.name())

573940 }

574941 }

942+ } else if ret_type := t.fn_value_return_type_name(map_expr_id) {

943+ map_fn_name = map_expr.value

944+ result_elem_type = ret_type

945+ }

946+ } else if map_expr.kind == .fn_literal || map_expr.kind == .lambda_expr {

947+ if ret_type := t.fn_value_return_type_name(map_expr_id) {

948+ result_elem_type = ret_type

575949 }

576950 }

577951 if result_elem_type.len == 0 {

@@ -581,6 +955,16 @@ fn (mut t Transformer) lower_array_map_call(node flat.Node, fn_node flat.Node, b

581955 t.pending_stmts.clear()

582956 mapped_expr := if map_fn_name.len > 0 {

583957 t.make_call_typed(map_fn_name, arr1(t.make_ident(elem_name)), result_elem_type)

958+ } else if map_expr.kind == .fn_literal || map_expr.kind == .lambda_expr {

959+ fn_value := t.transform_expr(map_expr_id)

960+ fn_value_node := t.a.nodes[int(fn_value)]

961+ if fn_value_node.kind == .ident && result_elem_type.len > 0 {

962+ t.make_call_typed(fn_value_node.value, arr1(t.make_ident(elem_name)), result_elem_type)

963+ } else {

964+ fn_value

965+ }

966+ } else if mapped_source_node.kind == .map_init && result_elem_type.starts_with('map[') {

967+ t.transform_expr_for_type(mapped_source, result_elem_type)

584968 } else {

585969 t.transform_expr(mapped_source)

586970 }

@@ -639,6 +1023,28 @@ fn (mut t Transformer) lower_array_map_call(node flat.Node, fn_node flat.Node, b

6391023 return result

6401024}

6411025

1026+fn (t &Transformer) fn_value_return_type_name(id flat.NodeId) ?string {

1027+ if int(id) < 0 {

1028+ return none

1029+ }

1030+ node := t.a.nodes[int(id)]

1031+ mut typ := node.typ

1032+ if node.kind == .ident {

1033+ typ = t.var_type(node.value)

1034+ }

1035+ if typ.len == 0 || isnil(t.tc) {

1036+ return none

1037+ }

1038+ parsed := t.tc.parse_type(typ)

1039+ if parsed is types.FnType {

1040+ name := parsed.return_type.name()

1041+ if name.len > 0 {

1042+ return t.normalize_type_alias(name)

1043+ }

1044+ }

1045+ return none

1046+}

1047+

6421048// selector_expr_node supports selector expr node handling for Transformer.

6431049fn (t &Transformer) selector_expr_node(id flat.NodeId) ?flat.Node {

6441050 if int(id) < 0 {

@@ -659,7 +1065,9 @@ fn (mut t Transformer) substitute_ident(id flat.NodeId, name string, replacement

6591065 node := t.a.nodes[int(id)]

6601066 if node.kind == .ident && node.value == name {

6611067 new_id := t.make_ident(replacement)

662- t.a.nodes[int(new_id)].typ = node.typ

1068+ if t.a.nodes[int(new_id)].typ.len == 0 {

1069+ t.a.nodes[int(new_id)].typ = node.typ

1070+ }

6631071 return new_id

6641072 }

6651073 if node.kind == .lambda_expr && node.children_count > 1 {

@@ -706,14 +1114,86 @@ fn (mut t Transformer) substitute_ident(id flat.NodeId, name string, replacement

7061114 return t.a.add_node(flat.Node{

7071115 kind: node.kind

7081116 op: node.op

1117+ kind_id: node.kind_id

7091118 children_start: start

7101119 children_count: flat.child_count(new_children.len)

7111120 pos: node.pos

7121121 value: node.value

7131122 typ: node.typ

1123+ generic_params: node.generic_params.clone()

7141124 })

7151125}

7161126

1127+fn (mut t Transformer) substitute_ident_expr(id flat.NodeId, name string, replacement flat.NodeId) flat.NodeId {

1128+ if int(id) < 0 || name.len == 0 || int(replacement) < 0 {

1129+ return id

1130+ }

1131+ node := t.a.nodes[int(id)]

1132+ if node.kind == .ident && node.value == name {

1133+ return replacement

1134+ }

1135+ if node.kind == .lambda_expr && node.children_count > 1 {

1136+ first := t.a.child_node(&node, 0)

1137+ if first.kind == .ident && first.value == name {

1138+ return id

1139+ }

1140+ }

1141+ if node.children_count == 0 {

1142+ return id

1143+ }

1144+ mut new_children := []flat.NodeId{cap: int(node.children_count)}

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

1146+ new_children << t.substitute_ident_expr(t.a.child(&node, i), name, replacement)

1147+ }

1148+ start := t.a.children.len

1149+ for child in new_children {

1150+ t.a.children << child

1151+ }

1152+ return t.a.add_node(flat.Node{

1153+ kind: node.kind

1154+ op: node.op

1155+ kind_id: node.kind_id

1156+ children_start: start

1157+ children_count: flat.child_count(new_children.len)

1158+ pos: node.pos

1159+ value: node.value

1160+ typ: node.typ

1161+ generic_params: node.generic_params.clone()

1162+ })

1163+}

1164+

1165+fn (mut t Transformer) infer_map_init_entry_type(node flat.Node) string {

1166+ if node.kind != .map_init || node.children_count < 2 {

1167+ return ''

1168+ }

1169+ key_type := t.node_type(t.a.child(&node, 0))

1170+ value_type := t.node_type(t.a.child(&node, 1))

1171+ if key_type.len == 0 || value_type.len == 0 {

1172+ return ''

1173+ }

1174+ return 'map[${key_type}]${value_type}'

1175+}

1176+

1177+fn (t &Transformer) is_array_transform_call(id flat.NodeId) bool {

1178+ if int(id) < 0 {

1179+ return false

1180+ }

1181+ node := t.a.nodes[int(id)]

1182+ if node.kind != .call || node.children_count == 0 {

1183+ return false

1184+ }

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

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

1187+ if fn_node.kind != .selector || fn_node.children_count == 0 {

1188+ return false

1189+ }

1190+ if fn_node.value !in ['filter', 'map', 'sorted'] {

1191+ return false

1192+ }

1193+ base_type := t.node_type(t.a.child(&fn_node, 0))

1194+ return base_type.starts_with('[]')

1195+}

1196+

7171197// lower_array_count_call builds lower array count call data for transform.

7181198fn (mut t Transformer) lower_array_count_call(node flat.Node, fn_node flat.Node, base_type string) ?flat.NodeId {

7191199 if node.children_count < 2 || !base_type.starts_with('[]') {

@@ -831,6 +1311,7 @@ fn (mut t Transformer) lower_array_sorted_call(node flat.Node, fn_node flat.Node

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

8321312 clone_name := t.new_temp('sorted')

8331313 clone_call := t.make_array_clone_call(base_id, base_type)

1314+ t.set_var_type(clone_name, base_type)

8341315 t.pending_stmts << t.make_decl_assign_typed(clone_name, clone_call, base_type)

8351316 cmp_id := if node.children_count > 1 { t.a.child(&node, 1) } else { flat.empty_node }

8361317 t.pending_stmts << t.make_array_default_sort_stmt(t.make_ident(clone_name), base_type[2..],

@@ -862,9 +1343,10 @@ fn (mut t Transformer) lower_array_sorted_with_compare_call(node flat.Node, fn_n

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

8631344 clone_name := t.new_temp('sorted')

8641345 clone_call := t.make_array_clone_call(base_id, base_type)

865- t.pending_stmts << t.make_decl_assign_typed(clone_name, clone_call, base_type)

8661346 elem_type := base_type[2..]

8671347 cmp := t.stable_array_compare_fn(t.a.child(&node, 1), elem_type)

1348+ t.set_var_type(clone_name, base_type)

1349+ t.pending_stmts << t.make_decl_assign_typed(clone_name, clone_call, base_type)

8681350 t.pending_stmts << t.make_array_compare_sort_stmt(t.make_ident(clone_name), elem_type, node, cmp)

8691351 return t.make_ident(clone_name)

8701352}

vlib/v3/transform/expr.v +613 -31

@@ -17,15 +17,31 @@ fn (mut t Transformer) transform_infix_string_ops(_id flat.NodeId, node flat.Nod

1717

1818 lhs_id := t.a.children[node.children_start]

1919 rhs_id := t.a.children[node.children_start + 1]

20+ lhs_raw_type := t.node_type(lhs_id)

21+ rhs_raw_type := t.node_type(rhs_id)

22+ lhs_clean_type := t.normalize_type_alias(lhs_raw_type)

23+ rhs_clean_type := t.normalize_type_alias(rhs_raw_type)

24+ lhs_is_string_ptr := t.equality_expr_is_string_pointer(lhs_id, lhs_clean_type)

25+ rhs_is_string_ptr := t.equality_expr_is_string_pointer(rhs_id, rhs_clean_type)

26+ if node.op in [.plus, .eq, .ne] && (lhs_is_string_ptr || rhs_is_string_ptr) {

27+ return none

28+ }

2029

21- is_string := t.is_string_type(lhs_id) || t.is_string_type(rhs_id)

30+ is_string := t.is_string_type(lhs_id) || t.is_string_type(rhs_id) || lhs_is_string_ptr

31+ || rhs_is_string_ptr

2232

2333 if !is_string {

2434 return none

2535 }

2636

27- new_lhs := t.transform_expr(lhs_id)

28- new_rhs := t.transform_expr(rhs_id)

37+ mut new_lhs := t.transform_expr(lhs_id)

38+ mut new_rhs := t.transform_expr(rhs_id)

39+ if lhs_is_string_ptr {

40+ new_lhs = t.make_prefix(.mul, new_lhs)

41+ }

42+ if rhs_is_string_ptr {

43+ new_rhs = t.make_prefix(.mul, new_rhs)

44+ }

2945

3046 match node.op {

3147 .plus {

@@ -94,8 +110,30 @@ fn (mut t Transformer) transform_infix_array_ops(_id flat.NodeId, node flat.Node

94110 }

95111 lhs_id := t.a.children[node.children_start]

96112 rhs_id := t.a.children[node.children_start + 1]

97- lhs_type := t.membership_container_type(t.node_type(lhs_id))

98- rhs_type := t.membership_container_type(t.node_type(rhs_id))

113+ lhs_raw_type := t.node_type(lhs_id)

114+ rhs_raw_type := t.node_type(rhs_id)

115+ lhs_is_array_ptr := t.equality_type_is_array_pointer(lhs_raw_type)

116+ rhs_is_array_ptr := t.equality_type_is_array_pointer(rhs_raw_type)

117+ if lhs_is_array_ptr && rhs_is_array_ptr {

118+ return none

119+ }

120+ mut lhs_type := t.membership_container_type(lhs_raw_type)

121+ mut rhs_type := t.membership_container_type(rhs_raw_type)

122+ lhs_is_fixed := t.is_fixed_array_type(lhs_type)

123+ rhs_is_fixed := t.is_fixed_array_type(rhs_type)

124+ if lhs_is_fixed || rhs_is_fixed {

125+ if !lhs_is_fixed || !rhs_is_fixed {

126+ return none

127+ }

128+ fixed_type := t.resolved_fixed_array_canonical_type(lhs_type)

129+ new_lhs := t.transform_expr_for_type(lhs_id, fixed_type)

130+ new_rhs := t.transform_expr_for_type(rhs_id, fixed_type)

131+ eq_call := t.make_membership_eq_expr(new_lhs, new_rhs, fixed_type)

132+ if node.op == .ne {

133+ return t.make_prefix(.not, eq_call)

134+ }

135+ return eq_call

136+ }

99137 if !(lhs_type.starts_with('[]') || lhs_type == 'array') || !(rhs_type.starts_with('[]')

100138 || rhs_type == 'array') {

101139 return none

@@ -109,9 +147,29 @@ fn (mut t Transformer) transform_infix_array_ops(_id flat.NodeId, node flat.Node

109147 if elem_type.len == 0 {

110148 elem_type = 'int'

111149 }

112- new_lhs := t.transform_expr(lhs_id)

113- new_rhs := t.transform_expr(rhs_id)

114- eq_call := if elem_type == 'string' {

150+ mut new_lhs := t.transform_expr(lhs_id)

151+ mut new_rhs := t.transform_expr(rhs_id)

152+ new_lhs_type := t.membership_container_type(t.node_type(new_lhs))

153+ new_rhs_type := t.membership_container_type(t.node_type(new_rhs))

154+ if new_lhs_type.starts_with('[]') {

155+ elem_type = new_lhs_type[2..]

156+ lhs_type = new_lhs_type

157+ } else if new_rhs_type.starts_with('[]') {

158+ elem_type = new_rhs_type[2..]

159+ rhs_type = new_rhs_type

160+ }

161+ if lhs_is_array_ptr {

162+ new_lhs = t.make_prefix(.mul, new_lhs)

163+ }

164+ if rhs_is_array_ptr {

165+ new_rhs = t.make_prefix(.mul, new_rhs)

166+ }

167+ eq_call := if t.array_elem_needs_element_eq(elem_type) {

168+ t.make_array_elementwise_eq_call(new_lhs, new_rhs, elem_type, lhs_type, rhs_type, node)

169+ } else if elem_type.starts_with('[]') {

170+ t.make_call_typed('array_eq_array', arr3(new_lhs, new_rhs,

171+ t.make_int_literal(array_nested_eq_depth(lhs_type))), 'bool')

172+ } else if elem_type == 'string' {

115173 t.make_call_typed('array_eq_string', arr2(new_lhs, new_rhs), 'bool')

116174 } else {

117175 t.make_call_typed('array_eq_raw', arr3(new_lhs, new_rhs, t.make_sizeof_type(elem_type)),

@@ -130,28 +188,79 @@ fn (mut t Transformer) transform_infix_map_ops(_id flat.NodeId, node flat.Node)

130188 }

131189 lhs_id := t.a.children[node.children_start]

132190 rhs_id := t.a.children[node.children_start + 1]

133- lhs_type := t.node_type(lhs_id)

134- rhs_type := t.node_type(rhs_id)

135- lhs_map_type := t.clean_map_type(lhs_type)

136- rhs_map_type := t.clean_map_type(rhs_type)

191+ lhs_type := t.map_comparison_expr_type(lhs_id)

192+ rhs_type := t.map_comparison_expr_type(rhs_id)

193+ if t.equality_type_is_map_pointer(lhs_type) || t.equality_type_is_map_pointer(rhs_type) {

194+ return none

195+ }

196+ mut lhs_map_type := t.clean_map_type(lhs_type)

197+ mut rhs_map_type := t.clean_map_type(rhs_type)

198+ if !lhs_map_type.starts_with('map[') && rhs_map_type.starts_with('map[')

199+ && t.is_empty_map_init(lhs_id) {

200+ lhs_map_type = rhs_map_type

201+ }

202+ if !rhs_map_type.starts_with('map[') && lhs_map_type.starts_with('map[')

203+ && t.is_empty_map_init(rhs_id) {

204+ rhs_map_type = lhs_map_type

205+ }

137206 if !lhs_map_type.starts_with('map[') || !rhs_map_type.starts_with('map[') {

138207 return none

139208 }

140- mut new_lhs := t.transform_expr(lhs_id)

141- mut new_rhs := t.transform_expr(rhs_id)

209+ map_type := lhs_map_type

210+ mut new_lhs := t.transform_expr_for_type(lhs_id, map_type)

211+ mut new_rhs := t.transform_expr_for_type(rhs_id, map_type)

142212 if lhs_type.starts_with('&') {

143213 new_lhs = t.make_prefix(.mul, new_lhs)

144214 }

145215 if rhs_type.starts_with('&') {

146216 new_rhs = t.make_prefix(.mul, new_rhs)

147217 }

148- eq_call := t.make_call_typed('map_map_eq', arr2(new_lhs, new_rhs), 'bool')

218+ _, value_type := t.map_type_parts(map_type)

219+ eq_call := if value_type.len > 0 && t.map_value_needs_element_eq(value_type) {

220+ t.make_map_elementwise_eq_call(new_lhs, new_rhs, map_type, node)

221+ } else {

222+ t.make_call_typed('v3_map_map_eq', arr2(new_lhs, new_rhs), 'bool')

223+ }

149224 if node.op == .ne {

150225 return t.make_prefix(.not, eq_call)

151226 }

152227 return eq_call

153228}

154229

230+fn (mut t Transformer) map_comparison_expr_type(id flat.NodeId) string {

231+ if int(id) < 0 {

232+ return ''

233+ }

234+ node := t.a.nodes[int(id)]

235+ if node.kind == .call {

236+ concrete := t.concrete_generic_call_return_type(id, node)

237+ if concrete.len > 0 && t.clean_map_type(concrete).starts_with('map[') {

238+ return concrete

239+ }

240+ }

241+ if !isnil(t.tc) {

242+ if typ := t.tc.expr_type(id) {

243+ name := typ.name()

244+ if name.len > 0 && t.clean_map_type(name).starts_with('map[') {

245+ return name

246+ }

247+ }

248+ }

249+ mut typ := t.node_type(id)

250+ if typ.len == 0 && node.kind == .map_init {

251+ typ = if node.value.len > 0 { node.value } else { node.typ }

252+ }

253+ return typ

254+}

255+

256+fn (t &Transformer) is_empty_map_init(id flat.NodeId) bool {

257+ if int(id) < 0 {

258+ return false

259+ }

260+ node := t.a.nodes[int(id)]

261+ return node.kind == .map_init && node.children_count == 0

262+}

263+

155264// transform_infix_struct_ops transforms transform infix struct ops data for transform.

156265fn (mut t Transformer) transform_infix_struct_ops(_id flat.NodeId, node flat.Node) ?flat.NodeId {

157266 if node.children_count < 2 {

@@ -180,13 +289,21 @@ fn (mut t Transformer) transform_infix_struct_ops(_id flat.NodeId, node flat.Nod

180289 // so the operator lowers to the monomorphized method (`vec__Vec4_f32__plus`).

181290 struct_type = t.generic_struct_instance_name(lhs_type)

182291 }

292+ mut is_alias_operator := false

293+ if struct_type.len == 0 {

294+ if _ := t.struct_operator_call_info(lhs_type, node.op) {

295+ struct_type = lhs_type

296+ is_alias_operator = true

297+ }

298+ }

183299 if struct_type.len == 0 {

184300 return none

185301 }

186302 // Skip the checker/transformer agreement guard for generic-struct instances:

187303 // they resolve reliably, and an alias name (`SimdFloat4`) vs the resolved form

188304 // (`vec.Vec4[f32]`) would otherwise spuriously fail the comparison.

189- if checker_lhs_type.len > 0 && !has_smartcast && !struct_type.contains('[') {

305+ if checker_lhs_type.len > 0 && !has_smartcast && !struct_type.contains('[')

306+ && !is_alias_operator {

190307 checker_struct_type := t.struct_lookup_name(checker_lhs_type)

191308 if checker_struct_type.len > 0 && checker_struct_type != struct_type {

192309 return none

@@ -217,6 +334,7 @@ fn (mut t Transformer) transform_infix_struct_ops(_id flat.NodeId, node flat.Nod

217334 } else {

218335 arr2(call_lhs, call_rhs)

219336 }

337+ t.mark_fn_used_name(call_info.name)

220338 call := t.make_call_typed(call_info.name, args, node.typ)

221339 if call_info.negate {

222340 return t.make_prefix(.not, call)

@@ -229,6 +347,12 @@ fn (mut t Transformer) transform_infix_struct_ops(_id flat.NodeId, node flat.Nod

229347 if !t.has_struct_operator_fn(struct_type, '==') {

230348 lhs := t.stable_expr_for_reuse(lhs_id)

231349 rhs := t.stable_expr_for_reuse(t.a.children[node.children_start + 1])

350+ if field_eq := t.make_struct_field_eq_expr(lhs, rhs, struct_type) {

351+ if node.op == .ne {

352+ return t.make_prefix(.not, field_eq)

353+ }

354+ return field_eq

355+ }

232356 cmp := t.make_call_typed('memcmp', arr3(t.make_prefix(.amp, lhs), t.make_prefix(.amp, rhs),

233357 t.make_sizeof_type(struct_type)), 'int')

234358 return t.make_infix(node.op, cmp, t.make_int_literal(0))

@@ -239,6 +363,7 @@ fn (mut t Transformer) transform_infix_struct_ops(_id flat.NodeId, node flat.Nod

239363 }

240364 lhs := t.transform_expr(lhs_id)

241365 rhs := t.transform_expr(t.a.children[node.children_start + 1])

366+ t.mark_fn_used_name(eq_fn)

242367 eq_call := t.make_call_typed(eq_fn, arr2(lhs, rhs), 'bool')

243368 if node.op == .ne {

244369 return t.make_prefix(.not, eq_call)

@@ -282,6 +407,7 @@ fn (mut t Transformer) transform_transformed_struct_eq(node flat.Node, lhs flat.

282407 } else {

283408 arr2(call_lhs, call_rhs)

284409 }

410+ t.mark_fn_used_name(call_info.name)

285411 call := t.make_call_typed(call_info.name, args, node.typ)

286412 if call_info.negate {

287413 return t.make_prefix(.not, call)

@@ -297,6 +423,7 @@ fn (mut t Transformer) transform_transformed_struct_eq(node flat.Node, lhs flat.

297423 }

298424 call_lhs := t.stable_transformed_expr_for_reuse(lhs, lhs_type, 'eq_lhs')

299425 call_rhs := t.stable_transformed_expr_for_reuse(rhs, rhs_type, 'eq_rhs')

426+ t.mark_fn_used_name(eq_fn)

300427 eq_call := t.make_call_typed(eq_fn, arr2(call_lhs, call_rhs), 'bool')

301428 if node.op == .ne {

302429 return t.make_prefix(.not, eq_call)

@@ -400,19 +527,34 @@ fn struct_operator_symbol(op flat.Op) ?string {

400527

401528// struct_operator_fn_name supports struct operator fn name handling for Transformer.

402529fn (t &Transformer) struct_operator_fn_name(struct_type string, op_name string) ?string {

403- method_name := '${struct_type}.${op_name}'

404530 require_used := op_name in ['==', '!=']

405- if t.is_known_operator_fn_name(method_name, require_used) {

406- return method_name

531+ for receiver in t.operator_receiver_candidates(struct_type) {

532+ method_name := '${receiver}.${op_name}'

533+ if t.is_known_operator_fn_name(method_name, require_used) {

534+ return method_name

535+ }

536+ cmethod_name := c_name(method_name)

537+ if t.is_known_operator_fn_name(cmethod_name, require_used) {

538+ return cmethod_name

539+ }

540+ if name := t.generic_struct_operator_fn_name(receiver, op_name) {

541+ return name

542+ }

407543 }

408- cmethod_name := c_name(method_name)

409- if t.is_known_operator_fn_name(cmethod_name, require_used) {

410- return cmethod_name

544+ return none

545+}

546+

547+fn (t &Transformer) operator_receiver_candidates(struct_type string) []string {

548+ mut candidates := []string{cap: 2}

549+ if struct_type.len == 0 {

550+ return candidates

411551 }

412- if name := t.generic_struct_operator_fn_name(struct_type, op_name) {

413- return name

552+ candidates << struct_type

553+ if !struct_type.contains('.') && t.cur_module.len > 0 && t.cur_module != 'main'

554+ && t.cur_module != 'builtin' {

555+ candidates << '${t.cur_module}.${struct_type}'

414556 }

415- return none

557+ return candidates

416558}

417559

418560// generic_struct_instance_name returns the resolved generic-struct instance type

@@ -941,10 +1083,13 @@ fn (mut t Transformer) lower_array_membership_expr(base_id flat.NodeId, needle_i

9411083 cond := t.make_infix(.lt, t.make_ident(idx_name), t.make_selector(base, 'len', 'int'))

9421084 post := t.make_expr_stmt(t.make_postfix(t.make_ident(idx_name), .inc))

9431085 elem_expr := t.array_get_value(base, t.make_ident(idx_name), elem_type)

1086+ pending_start := t.pending_stmts.len

9441087 eq_expr := t.make_membership_eq_expr(elem_expr, needle, elem_type)

1088+ mut loop_body := t.pending_stmts[pending_start..].clone()

1089+ t.pending_stmts = t.pending_stmts[..pending_start].clone()

9451090 assign_true := t.make_assign(t.make_ident(result_name), t.make_bool_literal(true))

9461091 then_block := t.make_block(arr1(assign_true))

947- loop_body := arr1(t.make_if(eq_expr, then_block, t.make_empty()))

1092+ loop_body << t.make_if(eq_expr, then_block, t.make_empty())

9481093 prefix << t.make_for_stmt(init, cond, post, loop_body, src)

9491094 for stmt in prefix {

9501095 t.pending_stmts << stmt

@@ -983,11 +1128,61 @@ fn (mut t Transformer) lower_array_index_expr(base_id flat.NodeId, needle_id fla

9831128 cond := t.make_infix(.lt, t.make_ident(idx_name), t.make_selector(base, 'len', 'int'))

9841129 post := t.make_expr_stmt(t.make_postfix(t.make_ident(idx_name), .inc))

9851130 elem_expr := t.array_get_value(base, t.make_ident(idx_name), elem_type)

1131+ pending_start := t.pending_stmts.len

9861132 eq_expr := t.make_membership_eq_expr(elem_expr, needle, elem_type)

1133+ mut loop_body := t.pending_stmts[pending_start..].clone()

1134+ t.pending_stmts = t.pending_stmts[..pending_start].clone()

9871135 not_found := t.make_infix(.lt, t.make_ident(result_name), t.make_int_literal(0))

9881136 found_cond := t.make_infix(.logical_and, not_found, eq_expr)

9891137 assign_idx := t.make_assign(t.make_ident(result_name), t.make_ident(idx_name))

990- loop_body := arr1(t.make_if(found_cond, t.make_block(arr1(assign_idx)), t.make_empty()))

1138+ loop_body << t.make_if(found_cond, t.make_block(arr1(assign_idx)), t.make_empty())

1139+ prefix << t.make_for_stmt(init, cond, post, loop_body, src)

1140+ for stmt in prefix {

1141+ t.pending_stmts << stmt

1142+ }

1143+ return t.make_ident(result_name)

1144+}

1145+

1146+// lower_array_last_index_expr builds lower array last index expr data for transform.

1147+fn (mut t Transformer) lower_array_last_index_expr(base_id flat.NodeId, needle_id flat.NodeId, base_type string, receiver_first bool, src flat.Node) ?flat.NodeId {

1148+ clean_base_type := t.membership_container_type(base_type)

1149+ if !clean_base_type.starts_with('[]') && clean_base_type != 'array' {

1150+ return none

1151+ }

1152+ mut elem_type := if clean_base_type.starts_with('[]') { clean_base_type[2..] } else { '' }

1153+ if elem_type.len == 0 {

1154+ elem_type = t.membership_container_type(t.node_type(needle_id))

1155+ }

1156+ if elem_type.len == 0 {

1157+ return none

1158+ }

1159+ mut base := flat.empty_node

1160+ mut needle := flat.empty_node

1161+ if receiver_first {

1162+ base = t.stable_array_expr_for_membership(base_id, base_type, clean_base_type)

1163+ needle = t.stable_expr_for_reuse(needle_id)

1164+ } else {

1165+ needle = t.stable_expr_for_reuse(needle_id)

1166+ base = t.stable_array_expr_for_membership(base_id, base_type, clean_base_type)

1167+ }

1168+ mut prefix := []flat.NodeId{}

1169+ t.drain_pending(mut prefix)

1170+ result_name := t.new_temp('last_index')

1171+ idx_name := t.new_temp('last_index_idx')

1172+ prefix << t.make_decl_assign_typed(result_name, t.make_int_literal(-1), 'int')

1173+ init := t.make_decl_assign_typed(idx_name, t.make_infix(.minus, t.make_selector(base, 'len',

1174+ 'int'), t.make_int_literal(1)), 'int')

1175+ cond := t.make_infix(.ge, t.make_ident(idx_name), t.make_int_literal(0))

1176+ post := t.make_expr_stmt(t.make_postfix(t.make_ident(idx_name), .dec))

1177+ elem_expr := t.array_get_value(base, t.make_ident(idx_name), elem_type)

1178+ pending_start := t.pending_stmts.len

1179+ eq_expr := t.make_membership_eq_expr(elem_expr, needle, elem_type)

1180+ mut loop_body := t.pending_stmts[pending_start..].clone()

1181+ t.pending_stmts = t.pending_stmts[..pending_start].clone()

1182+ not_found := t.make_infix(.lt, t.make_ident(result_name), t.make_int_literal(0))

1183+ found_cond := t.make_infix(.logical_and, not_found, eq_expr)

1184+ assign_idx := t.make_assign(t.make_ident(result_name), t.make_ident(idx_name))

1185+ loop_body << t.make_if(found_cond, t.make_block(arr1(assign_idx)), t.make_empty())

9911186 prefix << t.make_for_stmt(init, cond, post, loop_body, src)

9921187 for stmt in prefix {

9931188 t.pending_stmts << stmt

@@ -1007,23 +1202,78 @@ fn (mut t Transformer) stable_array_expr_for_membership(id flat.NodeId, raw_type

10071202

10081203// make_membership_eq_expr builds make membership eq expr data for transform.

10091204fn (mut t Transformer) make_membership_eq_expr(lhs flat.NodeId, rhs flat.NodeId, elem_type string) flat.NodeId {

1205+ return t.make_membership_eq_expr_with_seen(lhs, rhs, elem_type, []string{})

1206+}

1207+

1208+fn (mut t Transformer) make_membership_eq_expr_with_seen(lhs flat.NodeId, rhs flat.NodeId, elem_type string, seen []string) flat.NodeId {

1209+ if t.membership_type_is_pointer(elem_type) {

1210+ return t.make_infix(.eq, lhs, rhs)

1211+ }

10101212 mut clean := t.membership_container_type(elem_type)

10111213 if clean == 'string' {

10121214 return t.make_call_typed('string__eq', arr2(lhs, rhs), 'bool')

10131215 }

1216+ map_type := t.clean_map_type(clean)

1217+ if map_type.starts_with('map[') {

1218+ _, value_type := t.map_type_parts(map_type)

1219+ if value_type.len > 0 && t.map_value_needs_element_eq(value_type) {

1220+ src := if int(lhs) >= 0 && int(lhs) < t.a.nodes.len {

1221+ t.a.nodes[int(lhs)]

1222+ } else {

1223+ flat.Node{}

1224+ }

1225+ return t.make_map_elementwise_eq_call_with_seen(lhs, rhs, map_type, src, seen)

1226+ }

1227+ return t.make_call_typed('v3_map_map_eq', arr2(lhs, rhs), 'bool')

1228+ }

10141229 if clean.starts_with('[]') {

10151230 inner := clean[2..]

10161231 if inner == 'string' {

10171232 return t.make_call_typed('array_eq_string', arr2(lhs, rhs), 'bool')

10181233 }

1234+ if t.array_elem_needs_element_eq(inner) {

1235+ src := if int(lhs) >= 0 && int(lhs) < t.a.nodes.len {

1236+ t.a.nodes[int(lhs)]

1237+ } else {

1238+ flat.Node{}

1239+ }

1240+ return t.make_array_elementwise_eq_call_with_seen(lhs, rhs, inner, clean, clean, src,

1241+ seen)

1242+ }

1243+ if inner.starts_with('[]') {

1244+ return t.make_call_typed('array_eq_array', arr3(lhs, rhs,

1245+ t.make_int_literal(array_nested_eq_depth(clean))), 'bool')

1246+ }

10191247 return t.make_call_typed('array_eq_raw', arr3(lhs, rhs, t.make_sizeof_type(inner)), 'bool')

10201248 }

1249+ if t.is_fixed_array_type(clean) {

1250+ clean = t.resolved_fixed_array_canonical_type(clean)

1251+ if t.type_needs_semantic_eq(clean) {

1252+ if fixed_eq := t.make_fixed_array_elementwise_eq_expr_with_seen(lhs, rhs, clean, seen) {

1253+ return fixed_eq

1254+ }

1255+ }

1256+ lhs_value := t.stable_transformed_expr_for_reuse(lhs, clean, 'fixed_eq_lhs')

1257+ rhs_value := t.stable_transformed_expr_for_reuse(rhs, clean, 'fixed_eq_rhs')

1258+ cmp := t.make_call_typed('memcmp', arr3(t.make_prefix(.amp, lhs_value), t.make_prefix(.amp,

1259+ rhs_value), t.make_sizeof_type(clean)), 'int')

1260+ return t.make_infix(.eq, cmp, t.make_int_literal(0))

1261+ }

10211262 struct_type := t.struct_lookup_name(clean)

10221263 if struct_type.len > 0 {

10231264 method_name := '${struct_type}.=='

10241265 if t.is_known_fn_name(method_name) {

1266+ t.mark_fn_used_name(method_name)

10251267 return t.make_call(method_name, arr2(lhs, rhs))

10261268 }

1269+ if struct_type in seen {

1270+ cmp := t.make_call_typed('memcmp', arr3(t.make_prefix(.amp, lhs),

1271+ t.make_prefix(.amp, rhs), t.make_sizeof_type(struct_type)), 'int')

1272+ return t.make_infix(.eq, cmp, t.make_int_literal(0))

1273+ }

1274+ if field_eq := t.make_struct_field_eq_expr_with_seen(lhs, rhs, struct_type, seen) {

1275+ return field_eq

1276+ }

10271277 cmp := t.make_call_typed('memcmp', arr3(t.make_prefix(.amp, lhs), t.make_prefix(.amp, rhs),

10281278 t.make_sizeof_type(struct_type)), 'int')

10291279 return t.make_infix(.eq, cmp, t.make_int_literal(0))

@@ -1031,6 +1281,203 @@ fn (mut t Transformer) make_membership_eq_expr(lhs flat.NodeId, rhs flat.NodeId,

10311281 return t.make_infix(.eq, lhs, rhs)

10321282}

10331283

1284+fn (t &Transformer) array_elem_needs_element_eq(elem_type string) bool {

1285+ if t.membership_type_is_pointer(elem_type) {

1286+ return false

1287+ }

1288+ clean := t.membership_container_type(elem_type)

1289+ return clean != 'string' && t.type_needs_semantic_eq(clean)

1290+}

1291+

1292+fn (t &Transformer) map_value_needs_element_eq(value_type string) bool {

1293+ if t.membership_type_is_pointer(value_type) {

1294+ return false

1295+ }

1296+ clean := t.membership_container_type(value_type)

1297+ if t.is_fixed_array_type(clean) {

1298+ return true

1299+ }

1300+ return t.type_needs_semantic_eq(clean)

1301+}

1302+

1303+fn (t &Transformer) type_needs_semantic_eq(typ string) bool {

1304+ if t.membership_type_is_pointer(typ) {

1305+ return false

1306+ }

1307+ clean := t.membership_container_type(typ)

1308+ if clean == 'string' {

1309+ return true

1310+ }

1311+ if t.clean_map_type(clean).starts_with('map[') {

1312+ return true

1313+ }

1314+ if clean.starts_with('[]') {

1315+ return t.type_needs_semantic_eq(clean[2..])

1316+ }

1317+ if t.is_fixed_array_type(clean) {

1318+ elem_type := fixed_array_elem_type(clean)

1319+ return elem_type.len > 0 && t.type_needs_semantic_eq(elem_type)

1320+ }

1321+ return t.struct_lookup_name(clean).len > 0

1322+}

1323+

1324+fn (mut t Transformer) make_array_elementwise_eq_call(lhs flat.NodeId, rhs flat.NodeId, elem_type string, lhs_type string, rhs_type string, src flat.Node) flat.NodeId {

1325+ return t.make_array_elementwise_eq_call_with_seen(lhs, rhs, elem_type, lhs_type, rhs_type, src,

1326+ []string{})

1327+}

1328+

1329+fn (mut t Transformer) make_array_elementwise_eq_call_with_seen(lhs flat.NodeId, rhs flat.NodeId, elem_type string, lhs_type string, rhs_type string, src flat.Node, seen []string) flat.NodeId {

1330+ mut clean_elem := t.membership_container_type(elem_type)

1331+ if t.is_fixed_array_type(clean_elem) {

1332+ clean_elem = t.resolved_fixed_array_canonical_type(clean_elem)

1333+ }

1334+ lhs_value := t.stable_transformed_expr_for_reuse(lhs, lhs_type, 'arr_eq_lhs')

1335+ rhs_value := t.stable_transformed_expr_for_reuse(rhs, rhs_type, 'arr_eq_rhs')

1336+ result_name := t.new_temp('arr_eq')

1337+ idx_name := t.new_temp('arr_eq_idx')

1338+ len_eq := t.make_infix(.eq, t.make_selector(lhs_value, 'len', 'int'), t.make_selector(rhs_value,

1339+ 'len', 'int'))

1340+ t.pending_stmts << t.make_decl_assign_typed(result_name, len_eq, 'bool')

1341+ init := t.make_decl_assign_typed(idx_name, t.make_int_literal(0), 'int')

1342+ in_bounds := t.make_infix(.lt, t.make_ident(idx_name), t.make_selector(lhs_value, 'len', 'int'))

1343+ cond := t.make_infix(.logical_and, t.make_ident(result_name), in_bounds)

1344+ post := t.make_expr_stmt(t.make_postfix(t.make_ident(idx_name), .inc))

1345+ lhs_elem := t.array_get_value(lhs_value, t.make_ident(idx_name), clean_elem)

1346+ rhs_elem := t.array_get_value(rhs_value, t.make_ident(idx_name), clean_elem)

1347+ pending_start := t.pending_stmts.len

1348+ elem_eq := t.make_membership_eq_expr_with_seen(lhs_elem, rhs_elem, clean_elem, seen)

1349+ mut body_stmts := t.pending_stmts[pending_start..].clone()

1350+ t.pending_stmts = t.pending_stmts[..pending_start].clone()

1351+ set_false := t.make_assign(t.make_ident(result_name), t.make_bool_literal(false))

1352+ body_stmts << t.make_if(t.make_prefix(.not, t.make_paren(elem_eq)),

1353+ t.make_block(arr1(set_false)), t.make_empty())

1354+ t.pending_stmts << t.make_for_stmt(init, cond, post, body_stmts, src)

1355+ result := t.make_ident(result_name)

1356+ t.a.nodes[int(result)].typ = 'bool'

1357+ return result

1358+}

1359+

1360+fn (mut t Transformer) make_fixed_array_elementwise_eq_expr(lhs flat.NodeId, rhs flat.NodeId, fixed_type string) ?flat.NodeId {

1361+ return t.make_fixed_array_elementwise_eq_expr_with_seen(lhs, rhs, fixed_type, []string{})

1362+}

1363+

1364+fn (mut t Transformer) make_fixed_array_elementwise_eq_expr_with_seen(lhs flat.NodeId, rhs flat.NodeId, fixed_type string, seen []string) ?flat.NodeId {

1365+ clean_fixed_type := t.resolved_fixed_array_canonical_type(fixed_type)

1366+ elem_type := fixed_array_elem_type(clean_fixed_type)

1367+ if elem_type.len == 0 {

1368+ return none

1369+ }

1370+ lhs_value := t.stable_transformed_expr_for_reuse(lhs, clean_fixed_type, 'fixed_eq_lhs')

1371+ rhs_value := t.stable_transformed_expr_for_reuse(rhs, clean_fixed_type, 'fixed_eq_rhs')

1372+ result_name := t.new_temp('fixed_eq')

1373+ idx_name := t.new_temp('fixed_eq_idx')

1374+ t.pending_stmts << t.make_decl_assign_typed(result_name, t.make_bool_literal(true), 'bool')

1375+ init := t.make_decl_assign_typed(idx_name, t.make_int_literal(0), 'int')

1376+ cond := t.make_infix(.lt, t.make_ident(idx_name), t.make_fixed_array_len_expr(clean_fixed_type))

1377+ post := t.make_expr_stmt(t.make_postfix(t.make_ident(idx_name), .inc))

1378+ lhs_elem := t.make_index(lhs_value, t.make_ident(idx_name), elem_type)

1379+ rhs_elem := t.make_index(rhs_value, t.make_ident(idx_name), elem_type)

1380+ pending_start := t.pending_stmts.len

1381+ elem_eq := t.make_membership_eq_expr_with_seen(lhs_elem, rhs_elem, elem_type, seen)

1382+ mut body_stmts := t.pending_stmts[pending_start..].clone()

1383+ t.pending_stmts = t.pending_stmts[..pending_start].clone()

1384+ set_false := t.make_assign(t.make_ident(result_name), t.make_bool_literal(false))

1385+ body_stmts << t.make_if(t.make_prefix(.not, t.make_paren(elem_eq)),

1386+ t.make_block(arr1(set_false)), t.make_empty())

1387+ t.pending_stmts << t.make_for_stmt(init,

1388+ t.make_infix(.logical_and, t.make_ident(result_name), cond), post, body_stmts, flat.Node{})

1389+ result := t.make_ident(result_name)

1390+ t.a.nodes[int(result)].typ = 'bool'

1391+ return result

1392+}

1393+

1394+fn (mut t Transformer) make_map_elementwise_eq_call(lhs flat.NodeId, rhs flat.NodeId, map_type string, src flat.Node) flat.NodeId {

1395+ return t.make_map_elementwise_eq_call_with_seen(lhs, rhs, map_type, src, []string{})

1396+}

1397+

1398+fn (mut t Transformer) make_map_elementwise_eq_call_with_seen(lhs flat.NodeId, rhs flat.NodeId, map_type string, src flat.Node, seen []string) flat.NodeId {

1399+ key_type, raw_value_type := t.map_type_parts(map_type)

1400+ value_type := if t.is_fixed_array_type(raw_value_type) {

1401+ t.resolved_fixed_array_canonical_type(raw_value_type)

1402+ } else {

1403+ raw_value_type

1404+ }

1405+ if key_type.len == 0 || value_type.len == 0 {

1406+ return t.make_call_typed('v3_map_map_eq', arr2(lhs, rhs), 'bool')

1407+ }

1408+ lhs_value := t.stable_transformed_expr_for_reuse(lhs, map_type, 'map_eq_lhs')

1409+ rhs_value := t.stable_transformed_expr_for_reuse(rhs, map_type, 'map_eq_rhs')

1410+ result_name := t.new_temp('map_eq')

1411+ zero_name := t.new_temp('map_eq_zero')

1412+ key_name := t.new_temp('map_eq_key')

1413+ lhs_val_name := t.new_temp('map_eq_val')

1414+ len_eq := t.make_infix(.eq, t.make_selector(lhs_value, 'len', 'int'), t.make_selector(rhs_value,

1415+ 'len', 'int'))

1416+ t.pending_stmts << t.make_decl_assign_typed(result_name, len_eq, 'bool')

1417+ t.pending_stmts << t.make_decl_assign_typed(zero_name, t.zero_value_for_type(value_type),

1418+ value_type)

1419+ t.set_var_type(key_name, key_type)

1420+ t.set_var_type(lhs_val_name, value_type)

1421+ key_ident := t.make_ident(key_name)

1422+ val_ident := t.make_ident(lhs_val_name)

1423+ rhs_exists := t.make_map_exists_expr(rhs_value, map_type, key_name)

1424+ rhs_val := t.make_map_get_expr(rhs_value, map_type, key_name, zero_name, value_type)

1425+ pending_start := t.pending_stmts.len

1426+ value_eq := t.make_membership_eq_expr_with_seen(t.make_ident(lhs_val_name), rhs_val,

1427+ value_type, seen)

1428+ mut body := t.pending_stmts[pending_start..].clone()

1429+ t.pending_stmts = t.pending_stmts[..pending_start].clone()

1430+ missing := t.make_prefix(.not, t.make_paren(rhs_exists))

1431+ value_diff := t.make_prefix(.not, t.make_paren(value_eq))

1432+ failed := t.make_infix(.logical_or, missing, value_diff)

1433+ active := t.make_infix(.logical_and, t.make_ident(result_name), failed)

1434+ set_false := t.make_assign(t.make_ident(result_name), t.make_bool_literal(false))

1435+ body << t.make_if(active, t.make_block(arr1(set_false)), t.make_empty())

1436+ start := t.a.children.len

1437+ t.a.children << key_ident

1438+ t.a.children << val_ident

1439+ t.a.children << lhs_value

1440+ for stmt in body {

1441+ t.a.children << stmt

1442+ }

1443+ t.pending_stmts << t.a.add_node(flat.Node{

1444+ kind: .for_in_stmt

1445+ children_start: start

1446+ children_count: flat.child_count(3 + body.len)

1447+ pos: src.pos

1448+ value: '3'

1449+ })

1450+ result := t.make_ident(result_name)

1451+ t.a.nodes[int(result)].typ = 'bool'

1452+ return result

1453+}

1454+

1455+fn (mut t Transformer) make_struct_field_eq_expr(lhs flat.NodeId, rhs flat.NodeId, struct_type string) ?flat.NodeId {

1456+ return t.make_struct_field_eq_expr_with_seen(lhs, rhs, struct_type, []string{})

1457+}

1458+

1459+fn (mut t Transformer) make_struct_field_eq_expr_with_seen(lhs flat.NodeId, rhs flat.NodeId, struct_type string, seen []string) ?flat.NodeId {

1460+ info := t.lookup_struct_info(struct_type) or { return none }

1461+ mut next_seen := seen.clone()

1462+ next_seen << struct_type

1463+ mut eq := flat.empty_node

1464+ for field in info.fields {

1465+ field_type := if field.typ.len > 0 { field.typ } else { field.raw_typ }

1466+ lhs_field := t.make_selector(lhs, field.name, field_type)

1467+ rhs_field := t.make_selector(rhs, field.name, field_type)

1468+ field_eq := if t.membership_type_is_pointer(field_type) {

1469+ t.make_infix(.eq, lhs_field, rhs_field)

1470+ } else {

1471+ t.make_membership_eq_expr_with_seen(lhs_field, rhs_field, field_type, next_seen)

1472+ }

1473+ eq = if int(eq) < 0 { field_eq } else { t.make_infix(.logical_and, eq, field_eq) }

1474+ }

1475+ if int(eq) < 0 {

1476+ return t.make_bool_literal(true)

1477+ }

1478+ return eq

1479+}

1480+

10341481// selector_field_type supports selector field type handling for Transformer.

10351482fn (t &Transformer) selector_field_type(node flat.Node) string {

10361483 if node.value.len == 0 {

@@ -1083,6 +1530,78 @@ fn (t &Transformer) membership_container_type(typ string) string {

10831530 return t.normalize_type_alias(clean)

10841531}

10851532

1533+fn (t &Transformer) membership_type_is_pointer(typ string) bool {

1534+ mut clean := t.normalize_type_alias(typ).trim_space()

1535+ for {

1536+ if clean.starts_with('shared ') {

1537+ clean = clean[7..].trim_space()

1538+ continue

1539+ }

1540+ if clean.starts_with('mut ') {

1541+ clean = clean[4..].trim_space()

1542+ continue

1543+ }

1544+ break

1545+ }

1546+ return clean.starts_with('&')

1547+}

1548+

1549+fn (t &Transformer) equality_type_is_array_pointer(typ string) bool {

1550+ mut clean := typ.trim_space()

1551+ for clean.starts_with('shared ') {

1552+ clean = clean[7..].trim_space()

1553+ }

1554+ if clean.starts_with('mut ') {

1555+ return false

1556+ }

1557+ clean = t.normalize_type_alias(clean).trim_space()

1558+ for clean.starts_with('shared ') {

1559+ clean = clean[7..].trim_space()

1560+ }

1561+ if !clean.starts_with('&') {

1562+ return false

1563+ }

1564+ container := t.membership_container_type(clean)

1565+ return container.starts_with('[]') || container == 'array'

1566+}

1567+

1568+fn (t &Transformer) equality_expr_is_string_pointer(id flat.NodeId, typ string) bool {

1569+ if int(id) < 0 || int(id) >= t.a.nodes.len {

1570+ return false

1571+ }

1572+ node := t.a.nodes[int(id)]

1573+ if node.kind in [.string_literal, .string_interp] {

1574+ return false

1575+ }

1576+ mut clean := typ.trim_space()

1577+ for clean.starts_with('shared ') {

1578+ clean = clean[7..].trim_space()

1579+ }

1580+ if clean.starts_with('mut ') {

1581+ return false

1582+ }

1583+ clean = t.normalize_type_alias(clean).trim_space()

1584+ for clean.starts_with('shared ') {

1585+ clean = clean[7..].trim_space()

1586+ }

1587+ return clean == '&string'

1588+}

1589+

1590+fn (t &Transformer) equality_type_is_map_pointer(typ string) bool {

1591+ mut clean := typ.trim_space()

1592+ for clean.starts_with('shared ') {

1593+ clean = clean[7..].trim_space()

1594+ }

1595+ if clean.starts_with('mut ') {

1596+ return false

1597+ }

1598+ clean = t.normalize_type_alias(clean).trim_space()

1599+ for clean.starts_with('shared ') {

1600+ clean = clean[7..].trim_space()

1601+ }

1602+ return clean.starts_with('&') && t.clean_map_type(clean).starts_with('map[')

1603+}

1604+

10861605// membership_container_is_pointer_array supports membership_container_is_pointer_array handling.

10871606fn (t &Transformer) membership_container_is_pointer_array(typ string) bool {

10881607 mut clean := t.normalize_type_alias(typ).trim_space()

@@ -1121,7 +1640,10 @@ fn (mut t Transformer) stable_expr_for_reuse(id flat.NodeId) flat.NodeId {

11211640 return expr

11221641 }

11231642 tmp_name := t.new_temp('in_lhs')

1124- tmp_typ := t.node_type(id)

1643+ mut tmp_typ := t.node_type(expr)

1644+ if tmp_typ.len == 0 {

1645+ tmp_typ = t.node_type(id)

1646+ }

11251647 decl := t.make_decl_assign(tmp_name, expr)

11261648 if tmp_typ.len > 0 {

11271649 t.a.nodes[int(decl)].typ = tmp_typ

@@ -1442,11 +1964,27 @@ pub fn (mut t Transformer) make_int_literal(value int) flat.NodeId {

14421964 return t.a.add_val(.int_literal, '${value}')

14431965}

14441966

1967+pub fn (mut t Transformer) make_int_literal_typed(value string, typ string) flat.NodeId {

1968+ return t.a.add_node(flat.Node{

1969+ kind: .int_literal

1970+ value: value

1971+ typ: typ

1972+ })

1973+}

1974+

14451975// make_float_literal builds make float literal data for transform.

14461976pub fn (mut t Transformer) make_float_literal(value string) flat.NodeId {

14471977 return t.a.add_val(.float_literal, value)

14481978}

14491979

1980+pub fn (mut t Transformer) make_float_literal_typed(value string, typ string) flat.NodeId {

1981+ return t.a.add_node(flat.Node{

1982+ kind: .float_literal

1983+ value: value

1984+ typ: typ

1985+ })

1986+}

1987+

14501988// make_bool_literal builds make bool literal data for transform.

14511989pub fn (mut t Transformer) make_bool_literal(value bool) flat.NodeId {

14521990 return t.a.add_val(.bool_literal, if value { 'true' } else { 'false' })

@@ -1484,7 +2022,7 @@ fn (t &Transformer) is_fixed_array_type(s string) bool {

14842022 if len_text.contains(',') || isnil(t.tc) {

14852023 return false

14862024 }

1487- return t.tc.const_int_value(len_text, []string{}) != none

2025+ return t.tc.const_int_value_in_module(len_text, t.cur_module, []string{}) != none

14882026}

14892027

14902028// fixed_array_len supports fixed array len handling for transform.

@@ -1494,6 +2032,12 @@ fn fixed_array_len(s string) int {

14942032

14952033// fixed_array_len_text supports fixed array len text handling for transform.

14962034fn fixed_array_len_text(s string) string {

2035+ if !s.starts_with('[') {

2036+ _, dims := transform_postfix_fixed_array_parts(s)

2037+ if dims.len > 0 {

2038+ return dims[0]

2039+ }

2040+ }

14972041 return s.all_after('[').all_before(']').trim_space()

14982042}

14992043

@@ -1502,7 +2046,45 @@ fn fixed_array_elem_type(s string) string {

15022046 if s.starts_with('[') {

15032047 return s.all_after(']')

15042048 }

1505- return s.all_before('[')

2049+ elem, dims := transform_postfix_fixed_array_parts(s)

2050+ if dims.len == 0 {

2051+ return s.all_before('[')

2052+ }

2053+ mut out := elem

2054+ for i := dims.len - 1; i > 0; i-- {

2055+ out += '[${dims[i]}]'

2056+ }

2057+ return out

2058+}

2059+

2060+fn fixed_array_canonical_type(s string) string {

2061+ if !s.starts_with('[') {

2062+ return s

2063+ }

2064+ elem_type := fixed_array_canonical_type(fixed_array_elem_type(s))

2065+ len_text := fixed_array_len_text(s)

2066+ return '${elem_type}[${len_text}]'

2067+}

2068+

2069+fn (t &Transformer) resolved_fixed_array_canonical_type(s string) string {

2070+ clean := fixed_array_canonical_type(s)

2071+ if !t.is_fixed_array_type(clean) {

2072+ return clean

2073+ }

2074+ elem_type := fixed_array_elem_type(clean)

2075+ resolved_elem := if t.is_fixed_array_type(elem_type) {

2076+ t.resolved_fixed_array_canonical_type(elem_type)

2077+ } else {

2078+ elem_type

2079+ }

2080+ len_text := fixed_array_len_text(clean)

2081+ if is_decimal_text(len_text) || isnil(t.tc) {

2082+ return '${resolved_elem}[${len_text}]'

2083+ }

2084+ if v := t.tc.const_int_value_in_module(len_text, t.cur_module, []string{}) {

2085+ return '${resolved_elem}[${v}]'

2086+ }

2087+ return '${resolved_elem}[${len_text}]'

15062088}

15072089

15082090// is_decimal_text reports whether is decimal text applies in transform.

@@ -1529,7 +2111,7 @@ fn (mut t Transformer) make_fixed_array_len_expr(s string) flat.NodeId {

15292111 // the raw expression as an ident would c_name-mangle it into an undeclared

15302112 // identifier such as `segs_+_1`.

15312113 if !isnil(t.tc) {

1532- if v := t.tc.const_int_value(len_text, []string{}) {

2114+ if v := t.tc.const_int_value_in_module(len_text, t.cur_module, []string{}) {

15332115 return t.make_int_literal(v)

15342116 }

15352117 }

vlib/v3/transform/fn.v +443 -24

@@ -409,7 +409,7 @@ fn (mut t Transformer) normalize_generic_call_expr(id flat.NodeId, node flat.Nod

409409 if base.kind !in [.ident, .selector] {

410410 return id

411411 }

412- type_arg := t.generic_call_type_arg_name(t.a.child(&fn_node, 1))

412+ type_arg := t.generic_call_type_args_name(fn_node)

413413 if type_arg.len == 0 {

414414 return id

415415 }

@@ -453,6 +453,33 @@ fn (t &Transformer) generic_call_type_arg_name(id flat.NodeId) string {

453453 }

454454 return '${base}.${node.value}'

455455 }

456+ .index {

457+ if node.children_count < 2 || node.value == 'range' {

458+ return ''

459+ }

460+ base := t.generic_call_type_arg_name(t.a.child(&node, 0))

461+ if base.len == 0 {

462+ return ''

463+ }

464+ mut args := []string{}

465+ for i in 1 .. node.children_count {

466+ arg := t.generic_call_type_arg_name(t.a.child(&node, i))

467+ if arg.len == 0 {

468+ return ''

469+ }

470+ args << arg

471+ }

472+ return '${base}[${args.join(', ')}]'

473+ }

474+ .array_init {

475+ if node.value.len > 0 {

476+ return '[]${node.value}'

477+ }

478+ return ''

479+ }

480+ .map_init {

481+ return node.value

482+ }

456483 .prefix {

457484 if node.children_count == 0 {

458485 return ''

@@ -472,6 +499,21 @@ fn (t &Transformer) generic_call_type_arg_name(id flat.NodeId) string {

472499 }

473500}

474501

502+fn (t &Transformer) generic_call_type_args_name(index_node flat.Node) string {

503+ if index_node.kind != .index || index_node.children_count < 2 || index_node.value == 'range' {

504+ return ''

505+ }

506+ mut args := []string{}

507+ for i in 1 .. index_node.children_count {

508+ arg := t.generic_call_type_arg_name(t.a.child(&index_node, i))

509+ if arg.len == 0 {

510+ return ''

511+ }

512+ args << arg

513+ }

514+ return args.join(', ')

515+}

516+

475517// transform_call_args transforms all children of a call expression.

476518// child[0] is the function expression, children[1..n] are arguments.

477519fn (mut t Transformer) transform_call_args(id flat.NodeId, node flat.Node) flat.NodeId {

@@ -485,7 +527,10 @@ fn (mut t Transformer) transform_call_args(id flat.NodeId, node flat.Node) flat.

485527 })

486528 }

487529 call_name := t.call_name_for_node(id, node)

488- params := t.call_param_types(call_name)

530+ mut params := t.call_param_types(call_name)

531+ if concrete_params := t.concrete_generic_call_param_types(id, node) {

532+ params = concrete_params.clone()

533+ }

489534 param_offset := t.call_param_offset(call_name, node, params)

490535 is_variadic := t.call_is_variadic(call_name)

491536 variadic_idx := if is_variadic && params.len > 0 && params[params.len - 1] is types.Array {

@@ -515,6 +560,13 @@ fn (mut t Transformer) transform_call_args(id flat.NodeId, node flat.Node) flat.

515560 if variadic_idx >= 0 && param_idx == variadic_idx {

516561 variadic_type := params[variadic_idx]

517562 if variadic_type is types.Array {

563+ if arg_node.kind == .prefix && arg_node.value == '...'

564+ && arg_node.children_count > 0 {

565+ spread_id := t.a.child(&arg_node, 0)

566+ new_children << t.transform_call_arg_for_param(spread_id, param_type)

567+ i++

568+ break

569+ }

518570 remaining := int(node.children_count) - i

519571 if remaining == 1 {

520572 arg_type := t.node_type(arg_id)

@@ -675,13 +727,13 @@ fn (t &Transformer) call_param_offset(call_name string, node flat.Node, params [

675727 if _ := t.static_assoc_fn_name(base_id, fn_node.value) {

676728 return 0

677729 }

678- if t.receiver_method_param_offset(base_id, node, params) == 1 {

679- return 1

680- }

681730 method_name := t.resolve_receiver_method_name(base_id, fn_node.value)

682731 if method_name.len == 0 {

683732 return 0

684733 }

734+ if t.receiver_method_param_offset(base_id, node, params) == 1 {

735+ return 1

736+ }

685737 if call_name.len == 0 || call_name == method_name || call_name == c_name(method_name) {

686738 return 1

687739 }

@@ -882,6 +934,9 @@ fn (mut t Transformer) transform_call_arg_for_param(arg_id flat.NodeId, param_ty

882934 return const_arg

883935 }

884936 }

937+ if param_type.len > 0 && t.type_text_has_generic_placeholder(param_type, t.cur_module) {

938+ return t.transform_expr(arg_id)

939+ }

885940 return t.transform_expr_for_type(arg_id, param_type)

886941}

887942

@@ -1454,11 +1509,29 @@ fn (mut t Transformer) wrap_string_conversion(expr flat.NodeId, typ string) flat

14541509 'bool' {

14551510 return t.make_call_typed('bool.str', arr1(expr), 'string')

14561511 }

1457- 'u8', 'byte', 'u16', 'u32', 'u64' {

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

14581513 return t.make_call_typed('strconv__format_uint', arr2(expr, t.make_int_literal(10)),

14591514 'string')

14601515 }

1461- 'int', 'int literal', 'i8', 'i16', 'i32', 'i64', 'isize', 'usize' {

1516+ 'u64' {

1517+ return t.make_call_typed('u64.str', arr1(expr), 'string')

1518+ }

1519+ 'int', 'int literal' {

1520+ return t.make_call_typed('int.str', arr1(expr), 'string')

1521+ }

1522+ 'i8' {

1523+ return t.make_call_typed('i8.str', arr1(expr), 'string')

1524+ }

1525+ 'i16' {

1526+ return t.make_call_typed('i16.str', arr1(expr), 'string')

1527+ }

1528+ 'i32' {

1529+ return t.make_call_typed('i32.str', arr1(expr), 'string')

1530+ }

1531+ 'i64' {

1532+ return t.make_call_typed('i64.str', arr1(expr), 'string')

1533+ }

1534+ 'isize' {

14621535 return t.make_call_typed('strconv__format_int', arr2(expr, t.make_int_literal(10)),

14631536 'string')

14641537 }

@@ -1508,9 +1581,10 @@ fn (mut t Transformer) wrap_string_conversion(expr flat.NodeId, typ string) flat

15081581 return t.wrap_string_conversion(arr, '[]${elem_type}')

15091582 } else if clean_typ.len > 0 && clean_typ.starts_with('[]') {

15101583 return t.lower_array_str(expr, clean_typ)

1584+ } else if clean_typ.len > 0 && clean_typ.starts_with('map[') {

1585+ return t.lower_map_str(expr, clean_typ)

15111586 } else if clean_typ == 'rune' {

1512- return t.make_call_typed('strconv__format_int', arr2(expr, t.make_int_literal(10)),

1513- 'string')

1587+ return t.make_call_typed('rune.str', arr1(expr), 'string')

15141588 } else {

15151589 return expr

15161590 }

@@ -1518,6 +1592,132 @@ fn (mut t Transformer) wrap_string_conversion(expr flat.NodeId, typ string) flat

15181592 }

15191593}

15201594

1595+fn (mut t Transformer) wrap_formatted_string_conversion(expr flat.NodeId, typ string, format string) flat.NodeId {

1596+ if format.len == 0 {

1597+ return t.wrap_string_conversion(expr, typ)

1598+ }

1599+ mut clean_typ := typ

1600+ if clean_typ.starts_with('&') {

1601+ clean_typ = clean_typ[1..]

1602+ }

1603+ if clean_typ.starts_with('builtin.') {

1604+ clean_typ = clean_typ.all_after_last('.')

1605+ }

1606+ if precision := fixed_decimal_precision(format) {

1607+ if clean_typ in ['f32', 'f64', 'float_literal'] {

1608+ arg := if clean_typ == 'f64' {

1609+ expr

1610+ } else {

1611+ t.make_cast('f64', expr, 'f64')

1612+ }

1613+ return t.make_call_typed('v3_f64_fixed', arr2(arg, t.make_int_literal(precision)),

1614+ 'string')

1615+ }

1616+ }

1617+ if format == 'c' {

1618+ if clean_typ in ['u8', 'byte', 'char', 'rune', 'int'] {

1619+ arg := if clean_typ == 'int' {

1620+ expr

1621+ } else {

1622+ t.make_cast('int', expr, 'int')

1623+ }

1624+ return t.make_call_typed('v3_char_string', arr1(arg), 'string')

1625+ }

1626+ }

1627+ if base := integer_format_base(format) {

1628+ if clean_typ in ['u8', 'byte', 'u16', 'u32', 'u64', 'usize'] {

1629+ return t.make_call_typed('strconv__format_uint', arr2(expr, t.make_int_literal(base)),

1630+ 'string')

1631+ }

1632+ if clean_typ in ['int', 'i8', 'i16', 'i32', 'i64', 'isize'] {

1633+ return t.make_call_typed('strconv__format_int', arr2(expr, t.make_int_literal(base)),

1634+ 'string')

1635+ }

1636+ }

1637+ if width := zero_padded_decimal_width(format) {

1638+ if clean_typ in ['int', 'i8', 'i16', 'i32', 'i64', 'isize', 'usize', 'u8', 'byte', 'u16',

1639+ 'u32', 'u64'] {

1640+ if clean_typ in ['u64', 'usize', 'u32', 'u16', 'u8', 'byte'] {

1641+ arg := if clean_typ == 'u64' {

1642+ expr

1643+ } else {

1644+ t.make_cast('u64', expr, 'u64')

1645+ }

1646+ return t.make_call_typed('v3_u64_zpad', arr2(arg, t.make_int_literal(width)),

1647+ 'string')

1648+ }

1649+ if clean_typ in ['i64', 'isize', 'i32', 'i16', 'i8'] {

1650+ arg := if clean_typ == 'i64' {

1651+ expr

1652+ } else {

1653+ t.make_cast('i64', expr, 'i64')

1654+ }

1655+ return t.make_call_typed('v3_i64_zpad', arr2(arg, t.make_int_literal(width)),

1656+ 'string')

1657+ }

1658+ return t.make_call_typed('v3_int_zpad', arr2(expr, t.make_int_literal(width)), 'string')

1659+ }

1660+ }

1661+ return t.wrap_string_conversion(expr, typ)

1662+}

1663+

1664+fn fixed_decimal_precision(format string) ?int {

1665+ if format.len < 3 || format[0] != `.` || format[format.len - 1] != `f` {

1666+ return none

1667+ }

1668+ mut n := 0

1669+ for i in 1 .. format.len - 1 {

1670+ ch := format[i]

1671+ if ch < `0` || ch > `9` {

1672+ return none

1673+ }

1674+ n = n * 10 + int(ch - `0`)

1675+ }

1676+ return n

1677+}

1678+

1679+fn integer_format_base(format string) ?int {

1680+ match format {

1681+ 'x' {

1682+ return 16

1683+ }

1684+ 'o' {

1685+ return 8

1686+ }

1687+ else {}

1688+ }

1689+

1690+ return none

1691+}

1692+

1693+fn zero_padded_decimal_width(format string) ?int {

1694+ if format.len < 2 || format[0] != `0` {

1695+ return none

1696+ }

1697+ mut end := format.len

1698+ if format[end - 1] == `d` {

1699+ end--

1700+ }

1701+ if end >= 3 && format[end - 2] == `.` && format[end - 1] == `0` {

1702+ end -= 2

1703+ }

1704+ if end <= 1 {

1705+ return none

1706+ }

1707+ mut width := 0

1708+ for i in 1 .. end {

1709+ ch := format[i]

1710+ if ch < `0` || ch > `9` {

1711+ return none

1712+ }

1713+ width = width * 10 + int(ch - `0`)

1714+ }

1715+ if width <= 0 {

1716+ return none

1717+ }

1718+ return width

1719+}

1720+

15211721// append_string builds `result = result + piece` using the runtime string concat helper.

15221722// Using string__plus directly (instead of `+=`) keeps the synthesized node independent of

15231723// type resolution for the freshly-introduced temp.

@@ -1570,6 +1770,68 @@ fn (mut t Transformer) lower_array_str(arr_expr flat.NodeId, base_type string) f

15701770 return t.make_ident(result_name)

15711771}

15721772

1773+fn (mut t Transformer) lower_map_str(map_expr flat.NodeId, map_type string) flat.NodeId {

1774+ key_type, value_type := t.map_type_parts(map_type)

1775+ if key_type.len == 0 || value_type.len == 0 {

1776+ return map_expr

1777+ }

1778+ lowered := t.transform_expr_for_type(map_expr, map_type)

1779+ return t.make_call_typed('v3_map_str', arr4(lowered,

1780+ t.make_int_literal(t.map_str_kind_for_type(key_type)),

1781+ t.make_int_literal(t.map_str_kind_for_type(value_type)),

1782+ t.make_int_literal(t.map_str_fixed_len_for_type(value_type))), 'string')

1783+}

1784+

1785+fn (t &Transformer) map_str_kind_for_type(typ string) int {

1786+ mut clean := t.normalize_type_alias(typ).trim_space()

1787+ if clean.starts_with('builtin.') {

1788+ clean = clean.all_after_last('.')

1789+ }

1790+ match clean {

1791+ 'string' {

1792+ return 1

1793+ }

1794+ 'rune' {

1795+ return 4

1796+ }

1797+ 'isize', 'char', 'i8', 'i16', 'i32', 'i64', 'int' {

1798+ return 2

1799+ }

1800+ 'usize', 'u8', 'byte', 'u16', 'u32', 'u64' {

1801+ return 3

1802+ }

1803+ 'f32', 'f64' {

1804+ return 5

1805+ }

1806+ 'bool' {

1807+ return 7

1808+ }

1809+ else {

1810+ if clean.starts_with('[]') && clean[2..] in ['f32', 'f64'] {

1811+ return 6

1812+ }

1813+ if transform_type_text_is_fixed_array(clean) {

1814+ elem := t.normalize_type_alias(fixed_array_elem_type(clean))

1815+ if elem == 'f32' {

1816+ return 9

1817+ }

1818+ if elem == 'f64' {

1819+ return 6

1820+ }

1821+ }

1822+ return 0

1823+ }

1824+ }

1825+}

1826+

1827+fn (t &Transformer) map_str_fixed_len_for_type(typ string) int {

1828+ clean := t.normalize_type_alias(typ).trim_space()

1829+ if transform_type_text_is_fixed_array(clean) {

1830+ return fixed_array_len(clean)

1831+ }

1832+ return 0

1833+}

1834+

15731835// wrap_optional_string_conversion transforms wrap optional string conversion data for transform.

15741836fn (mut t Transformer) wrap_optional_string_conversion(expr flat.NodeId, typ string) flat.NodeId {

15751837 opt_type := t.qualify_optional_type(typ)

@@ -1720,8 +1982,8 @@ fn (mut t Transformer) try_lower_array_method_call(call_id flat.NodeId, node fla

17201982 return none

17211983 }

17221984 array_builtin_method := t.array_builtin_method_name(fn_node.value) or { '' }

1723- if fn_node.value !in ['clone', 'reverse', 'contains', 'index', 'join', 'any', 'all', 'count',

1724- 'equals', 'prepend', 'insert', 'push_many'] {

1985+ if fn_node.value !in ['clone', 'reverse', 'contains', 'index', 'last_index', 'join', 'any',

1986+ 'all', 'count', 'equals', 'prepend', 'insert', 'push_many', 'str'] {

17251987 if fn_node.value !in ['filter', 'map', 'sort', 'sorted', 'sort_with_compare', 'sorted_with_compare']

17261988 && array_builtin_method.len == 0 {

17271989 return none

@@ -1729,6 +1991,14 @@ fn (mut t Transformer) try_lower_array_method_call(call_id flat.NodeId, node fla

17291991 }

17301992 base_id := t.a.children[fn_node.children_start]

17311993 mut base_type := t.node_type(base_id)

1994+ base_node := t.a.nodes[int(base_id)]

1995+ if (array_type_has_generic_placeholder(base_type) || base_type.contains('unknown'))

1996+ && base_node.kind == .call {

1997+ concrete_base_type := t.concrete_generic_call_return_type(base_id, base_node)

1998+ if concrete_base_type.starts_with('[]') {

1999+ base_type = concrete_base_type

2000+ }

2001+ }

17322002 if (!base_type.starts_with('[]') && !t.is_fixed_array_type(base_type)) || base_type == 'array' {

17332003 lvalue_base_type := t.lvalue_type(base_id)

17342004 if lvalue_base_type.starts_with('[]') || t.is_fixed_array_type(lvalue_base_type) {

@@ -1736,7 +2006,6 @@ fn (mut t Transformer) try_lower_array_method_call(call_id flat.NodeId, node fla

17362006 }

17372007 }

17382008 if !base_type.starts_with('[]') && !t.is_fixed_array_type(base_type) {

1739- base_node := t.a.nodes[int(base_id)]

17402009 if base_node.kind in [.call, .selector, .as_expr] {

17412010 new_base := t.transform_expr(base_id)

17422011 new_base_type := t.node_type(new_base)

@@ -1762,7 +2031,20 @@ fn (mut t Transformer) try_lower_array_method_call(call_id flat.NodeId, node fla

17622031 }

17632032 }

17642033 }

2034+ if fn_node.value == 'str' && base_node.kind == .call {

2035+ new_base := t.transform_expr(base_id)

2036+ new_base_type := t.node_type(new_base)

2037+ if new_base_type.starts_with('[]') {

2038+ return t.lower_array_str(new_base, new_base_type)

2039+ }

2040+ if new_base_type.starts_with('map[') {

2041+ return t.lower_map_str(new_base, new_base_type)

2042+ }

2043+ }

17652044 clean_base_type := if base_type.starts_with('&') { base_type[1..] } else { base_type }

2045+ if fn_node.value == 'str' && t.is_builder_receiver(base_id, base_type) {

2046+ return none

2047+ }

17662048 if t.is_fixed_array_type(clean_base_type) && fn_node.value == 'pointers'

17672049 && array_builtin_method.len > 0 {

17682050 method_name := t.resolve_receiver_method_name(base_id, fn_node.value)

@@ -1860,6 +2142,9 @@ fn (mut t Transformer) try_lower_array_method_call(call_id flat.NodeId, node fla

18602142 'count' {

18612143 return t.lower_array_count_call(node, fn_node, clean_base_type)

18622144 }

2145+ 'str' {

2146+ return t.wrap_string_conversion(t.transform_expr(base_id), clean_base_type)

2147+ }

18632148 'equals' {

18642149 if node.children_count < 2 {

18652150 return none

@@ -1872,6 +2157,14 @@ fn (mut t Transformer) try_lower_array_method_call(call_id flat.NodeId, node fla

18722157 }

18732158 receiver := t.transform_expr(base_id)

18742159 arg := t.transform_expr(t.a.children[node.children_start + 1])

2160+ if t.array_elem_needs_element_eq(elem_type) {

2161+ return t.make_array_elementwise_eq_call(receiver, arg, elem_type, clean_base_type,

2162+ clean_base_type, node)

2163+ }

2164+ if elem_type.starts_with('[]') {

2165+ return t.make_call_typed('array_eq_array', arr3(receiver, arg,

2166+ t.make_int_literal(array_nested_eq_depth(clean_base_type))), 'bool')

2167+ }

18752168 if elem_type == 'string' {

18762169 return t.make_call_typed('array_eq_string', arr2(receiver, arg), 'bool')

18772170 }

@@ -1891,10 +2184,7 @@ fn (mut t Transformer) try_lower_array_method_call(call_id flat.NodeId, node fla

18912184 t.mark_fn_used(method_name)

18922185 return t.make_call_typed(method_name, args, ret_type)

18932186 }

1894- receiver := t.transform_expr(base_id)

1895- t.mark_fn_used('array__clone')

1896- return t.make_call_typed('array__clone', arr1(t.runtime_addr(receiver, base_type)),

1897- clean_base_type)

2187+ return t.make_array_clone_call(base_id, base_type)

18982188 }

18992189 'reverse' {

19002190 mut receiver := t.transform_expr(base_id)

@@ -1934,6 +2224,16 @@ fn (mut t Transformer) try_lower_array_method_call(call_id flat.NodeId, node fla

19342224 fn_name := if elem_type == 'string' { 'array_index_string' } else { 'array_index_int' }

19352225 return t.make_call_typed(fn_name, arr2(receiver, arg), 'int')

19362226 }

2227+ 'last_index' {

2228+ if node.children_count < 2 {

2229+ return none

2230+ }

2231+ arg_id := t.a.children[node.children_start + 1]

2232+ if lowered := t.lower_array_last_index_expr(base_id, arg_id, base_type, true, node) {

2233+ return lowered

2234+ }

2235+ return none

2236+ }

19372237 'join' {

19382238 if node.children_count < 2 {

19392239 return none

@@ -1953,6 +2253,9 @@ fn (mut t Transformer) try_lower_array_method_call(call_id flat.NodeId, node fla

19532253 t.mark_fn_used(method_name)

19542254 return t.make_call_typed(method_name, args, ret_type)

19552255 }

2256+ if fn_node.value in ['pop', 'clear', 'trim'] {

2257+ t.mark_fn_used('array__${fn_node.value}')

2258+ }

19562259 return none

19572260 }

19582261 if array_builtin_method.len > 0 {

@@ -1982,8 +2285,8 @@ fn (t &Transformer) array_builtin_method_name(method string) ?string {

19822285}

19832286

19842287fn array_method_stays_in_cgen(method string) bool {

1985- return method in ['last', 'first', 'delete_last', 'pop', 'clear', 'repeat', 'repeat_to_depth',

1986- 'trim', 'ensure_cap', 'delete', 'free', 'str', 'bytestr', 'wait']

2288+ return method in ['last', 'first', 'delete_last', 'pop', 'pop_left', 'clear', 'repeat',

2289+ 'repeat_to_depth', 'trim', 'ensure_cap', 'delete', 'free', 'str', 'bytestr', 'wait']

19872290}

19882291

19892292// try_lower_map_method_call supports try lower map method call handling for Transformer.

@@ -1998,7 +2301,10 @@ fn (mut t Transformer) try_lower_map_method_call(call_id flat.NodeId, node flat.

19982301 return none

19992302 }

20002303 base_id := t.a.children[fn_node.children_start]

2001- base_type := t.node_type(base_id)

2304+ mut base_type := t.node_type(base_id)

2305+ if base_type.len == 0 {

2306+ base_type = t.checker_node_type(base_id)

2307+ }

20022308 clean_type := t.clean_map_type(base_type)

20032309 if !clean_type.starts_with('map[') {

20042310 return none

@@ -2136,6 +2442,7 @@ fn (mut t Transformer) lift_fn_literal(_id flat.NodeId, node flat.Node) flat.Nod

21362442 mut param_names := []string{}

21372443 mut capture_names := []string{}

21382444 mut capture_types := map[string]string{}

2445+ mut capture_globals := map[string]string{}

21392446 mut body_ids := []flat.NodeId{}

21402447 for i in 0 .. node.children_count {

21412448 child_id := t.a.child(&node, i)

@@ -2164,6 +2471,7 @@ fn (mut t Transformer) lift_fn_literal(_id flat.NodeId, node flat.Node) flat.Nod

21642471 }

21652472 capture_names << child.value

21662473 capture_types[child.value] = capture_type

2474+ capture_globals[child.value] = '${name}_${child.value}'

21672475 }

21682476 } else {

21692477 body_ids << child_id

@@ -2188,14 +2496,20 @@ fn (mut t Transformer) lift_fn_literal(_id flat.NodeId, node flat.Node) flat.Nod

21882496 continue

21892497 }

21902498 capture_type := capture_types[capture_name] or { continue }

2499+ capture_global := capture_globals[capture_name] or { continue }

2500+ t.add_fn_literal_capture_global(capture_global, capture_type)

2501+ t.pending_stmts << t.make_assign(t.make_ident(capture_global), t.make_ident(capture_name))

21912502 t.set_var_type(capture_name, capture_type)

2192- lifted_body << t.make_decl_assign_typed(capture_name, t.zero_value_for_type(capture_type),

2503+ lifted_body << t.make_decl_assign_typed(capture_name, t.make_ident(capture_global),

21932504 capture_type)

21942505 }

21952506 for body_id in body_ids {

21962507 lifted_body << body_id

21972508 }

2509+ outer_pending := t.pending_stmts.clone()

2510+ t.pending_stmts.clear()

21982511 new_body := t.transform_stmts(lifted_body)

2512+ t.pending_stmts = outer_pending

21992513 t.var_types = saved_vars

22002514 t.cur_fn_name = saved_fn_name

22012515 t.cur_fn_ret_type = saved_ret_type

@@ -2241,6 +2555,34 @@ fn (mut t Transformer) lift_fn_literal(_id flat.NodeId, node flat.Node) flat.Nod

22412555 return t.make_ident(name)

22422556}

22432557

2558+fn (mut t Transformer) add_fn_literal_capture_global(name string, typ string) {

2559+ if typ.len == 0 {

2560+ return

2561+ }

2562+ if t.cur_module.len > 0 {

2563+ t.a.add_node(flat.Node{

2564+ kind: .module_decl

2565+ value: t.cur_module

2566+ })

2567+ }

2568+ field := t.a.add_node(flat.Node{

2569+ kind: .field_decl

2570+ value: name

2571+ typ: typ

2572+ })

2573+ start := t.a.children.len

2574+ t.a.children << field

2575+ t.a.add_node(flat.Node{

2576+ kind: .global_decl

2577+ children_start: start

2578+ children_count: 1

2579+ })

2580+ t.globals[name] = typ

2581+ if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {

2582+ t.globals['${t.cur_module}.${name}'] = typ

2583+ }

2584+}

2585+

22442586// try_lower_builtin_call checks if a call is to a builtin that needs special lowering.

22452587// Returns none for most calls so the caller falls through to generic call transform.

22462588fn (mut t Transformer) try_lower_builtin_call(_id flat.NodeId, node flat.Node) ?flat.NodeId {

@@ -2292,6 +2634,12 @@ fn (mut t Transformer) try_lower_builtin_call(_id flat.NodeId, node flat.Node) ?

22922634 return none

22932635 }

22942636 name := fn_node.value

2637+ if name in ['maxof', 'minof'] && node.value.len > 0 && node.children_count == 1

2638+ && t.is_std_minmaxof_call(_id, name) {

2639+ if value := t.try_lower_minmaxof_call(name, node.value) {

2640+ return value

2641+ }

2642+ }

22952643 match name {

22962644 'copy' {

22972645 return t.try_lower_copy_call(node)

@@ -2327,6 +2675,54 @@ fn (mut t Transformer) try_lower_builtin_call(_id flat.NodeId, node flat.Node) ?

23272675 }

23282676}

23292677

2678+fn (t &Transformer) is_std_minmaxof_call(id flat.NodeId, name string) bool {

2679+ if isnil(t.tc) {

2680+ return false

2681+ }

2682+ resolved := t.tc.resolved_call_name(id) or { return false }

2683+ return resolved == 'math.${name}'

2684+}

2685+

2686+fn (mut t Transformer) try_lower_minmaxof_call(name string, raw_type string) ?flat.NodeId {

2687+ mut typ := raw_type

2688+ if !isnil(t.tc) {

2689+ typ = t.normalize_type_in_module(raw_type, t.cur_module)

2690+ }

2691+ if typ.starts_with('builtin.') {

2692+ typ = typ.all_after_last('.')

2693+ }

2694+ value := if name == 'maxof' {

2695+ match typ {

2696+ 'i8' { '127' }

2697+ 'i16' { '32767' }

2698+ 'int', 'i32' { '2147483647' }

2699+ 'i64' { '9223372036854775807' }

2700+ 'u8' { '255' }

2701+ 'u16' { '65535' }

2702+ 'u32' { '4294967295' }

2703+ 'u64' { '18446744073709551615' }

2704+ 'f32' { '3.40282346638528859811704183484516925440e+38' }

2705+ 'f64' { '1.797693134862315708145274237317043567981e+308' }

2706+ else { return none }

2707+ }

2708+ } else {

2709+ match typ {

2710+ 'i8' { '-128' }

2711+ 'i16' { '-32768' }

2712+ 'int', 'i32' { '(-2147483647 - 1)' }

2713+ 'i64' { '(-9223372036854775807 - 1)' }

2714+ 'u8', 'u16', 'u32', 'u64' { '0' }

2715+ 'f32' { '-3.40282346638528859811704183484516925440e+38' }

2716+ 'f64' { '-1.797693134862315708145274237317043567981e+308' }

2717+ else { return none }

2718+ }

2719+ }

2720+ if typ in ['f32', 'f64'] {

2721+ return t.make_float_literal_typed(value, typ)

2722+ }

2723+ return t.make_int_literal_typed(value, typ)

2724+}

2725+

23302726fn (mut t Transformer) try_lower_copy_call(node flat.Node) ?flat.NodeId {

23312727 if node.children_count != 3 {

23322728 return none

@@ -2602,6 +2998,7 @@ fn (mut t Transformer) try_lower_receiver_method_call(id flat.NodeId, node flat.

26022998 if base_type.len == 0 {

26032999 base_type = t.lvalue_type(base_id)

26043000 }

3001+ base_is_pointer := base_type.starts_with('&')

26053002 if base_type.starts_with('&') {

26063003 base_type = base_type[1..]

26073004 }

@@ -2615,6 +3012,13 @@ fn (mut t Transformer) try_lower_receiver_method_call(id flat.NodeId, node flat.

26153012 if base_type == '[]rune' && method == 'string' {

26163013 return t.make_call_typed('Array_rune__string', arr1(t.transform_expr(base_id)), 'string')

26173014 }

3015+ if method == 'str' && t.is_array_transform_call(base_id) {

3016+ base := t.transform_expr(base_id)

3017+ transformed_type := t.node_type(base)

3018+ if transformed_type.starts_with('[]') {

3019+ return t.wrap_string_conversion(base, transformed_type)

3020+ }

3021+ }

26183022 if method == 'str' && t.is_builder_receiver(base_id, base_type) {

26193023 method_name := 'strings.Builder.str'

26203024 args := t.transform_receiver_method_args(node, base_id, method_name)

@@ -2629,11 +3033,14 @@ fn (mut t Transformer) try_lower_receiver_method_call(id flat.NodeId, node flat.

26293033 }

26303034 return t.wrap_string_conversion(t.transform_expr(base_id), base_type)

26313035 }

3036+ if builtin_base_type == 'string' && method == 'hex' && !base_is_pointer {

3037+ return t.make_call_typed('string.hex', arr1(t.transform_expr(base_id)), 'string')

3038+ }

26323039 if base_type == '[]u8' || base_type == '[]byte' {

26333040 if method == 'bytestr' {

26343041 return t.make_call_typed('Array_u8__bytestr', arr1(t.transform_expr(base_id)), 'string')

26353042 }

2636- if method == 'hex' {

3043+ if method == 'hex' && !base_is_pointer {

26373044 return t.make_call_typed('Array_u8__hex', arr1(t.transform_expr(base_id)), 'string')

26383045 }

26393046 }

@@ -2649,7 +3056,8 @@ fn (mut t Transformer) try_lower_receiver_method_call(id flat.NodeId, node flat.

26493056 if builtin_base_type == 'u8' && method in ['is_space', 'is_digit', 'is_hex_digit', 'is_letter'] {

26503057 return t.make_call_typed('u8__${method}', arr1(t.transform_expr(base_id)), 'bool')

26513058 }

2652- if builtin_base_type in ['u8', 'i8', 'u16', 'i16', 'u32', 'int', 'u64', 'i64', 'rune']

3059+ if !base_is_pointer

3060+ && builtin_base_type in ['u8', 'i8', 'u16', 'i16', 'u32', 'int', 'u64', 'i64', 'rune']

26533061 && method in ['hex', 'hex_full'] {

26543062 return t.make_call_typed('${builtin_base_type}__${method}',

26553063 arr1(t.transform_expr(base_id)), 'string')

@@ -3296,6 +3704,13 @@ fn (mut t Transformer) transform_receiver_method_args_with_base(node flat.Node,

32963704 if variadic_idx >= 0 && param_idx == variadic_idx {

32973705 variadic_type := params[variadic_idx]

32983706 if variadic_type is types.Array {

3707+ if arg_node.kind == .prefix && arg_node.value == '...'

3708+ && arg_node.children_count > 0 {

3709+ spread_id := t.a.child(&arg_node, 0)

3710+ args << t.transform_call_arg_for_param(spread_id, param_type)

3711+ i++

3712+ break

3713+ }

32993714 remaining := int(node.children_count) - i

33003715 if remaining == 1 {

33013716 arg_type := t.node_type(arg_id)

@@ -3474,7 +3889,8 @@ fn (t &Transformer) get_call_return_type(id flat.NodeId, node flat.Node) string

34743889 }

34753890 }

34763891 }

3477- if fn_node.kind == .selector && fn_node.value in ['clone', 'reverse']

3892+ if fn_node.kind == .selector

3893+ && fn_node.value in ['clone', 'reverse', 'repeat', 'repeat_to_depth']

34783894 && fn_node.children_count > 0 {

34793895 base_id := t.a.child(fn_node, 0)

34803896 base_type := t.node_type(base_id)

@@ -3500,7 +3916,10 @@ fn (t &Transformer) get_call_return_type(id flat.NodeId, node flat.Node) string

35003916 if fn_node.kind == .selector && fn_node.value in ['keys', 'values']

35013917 && fn_node.children_count > 0 {

35023918 base_id := t.a.child(fn_node, 0)

3503- base_type := t.node_type(base_id)

3919+ mut base_type := t.node_type(base_id)

3920+ if base_type.len == 0 {

3921+ base_type = t.checker_node_type(base_id)

3922+ }

35043923 clean_type := t.clean_map_type(base_type)

35053924 if clean_type.starts_with('map[') {

35063925 elem_type := if fn_node.value == 'keys' {

vlib/v3/transform/for.v +114 -9

@@ -86,6 +86,9 @@ fn (mut t Transformer) transform_for_in_body(id flat.NodeId, node flat.Node) []f

8686 if header_count < 3 || node.children_count < 3 {

8787 return arr1(id)

8888 }

89+ if t.cur_fn_is_generic {

90+ return arr1(id)

91+ }

8992 key_id := t.a.child(&node, 0) // loop var ident — pass through (do not transform a binding)

9093 val_id := t.a.child(&node, 1) // may be flat.empty_node (-1)

9194 container_id := t.a.child(&node, 2)

@@ -117,6 +120,11 @@ fn (mut t Transformer) transform_for_in_body(id flat.NodeId, node flat.Node) []f

117120 return t.lower_indexed_for_in(id, node, key_id, val_id, container_id, pool_iter, has_index,

118121 body_ids)

119122 }

123+ if iter_elem_type := t.iterator_for_in_elem_type(iter_type) {

124+ body_ids := t.a.children_of(&node)[header_count..].clone()

125+ return t.lower_iterator_for_in(id, node, key_id, val_id, container_id, iter_type,

126+ iter_elem_type, has_index, body_ids)

127+ }

120128 effective_iter := if iter_type.starts_with('...') {

121129 '[]' + iter_type[3..]

122130 } else if iter_type.starts_with('&[]') {

@@ -179,11 +187,17 @@ fn (mut t Transformer) rebuild_for_in_stmt(_id flat.NodeId, node flat.Node) []fl

179187 false

180188 }

181189 if header_count == 4 || container_is_range {

182- // range `for i in 0 .. n`: single loop var (child0) is an int

190+ // range `for i in 0 .. n`: single loop var (child0) follows the lower bound

183191 if int(key_id) >= 0 {

184192 key_name := t.a.nodes[int(key_id)].value

185193 if key_name.len > 0 {

186- t.set_var_type(key_name, 'int')

194+ low_id := if container_is_range {

195+ range_node := t.a.nodes[int(container_id)]

196+ t.a.child(&range_node, 0)

197+ } else {

198+ container_id

199+ }

200+ t.set_var_type(key_name, t.range_loop_var_type_name(low_id))

187201 }

188202 }

189203 } else if has_index {

@@ -249,6 +263,17 @@ fn (mut t Transformer) rebuild_for_in_stmt(_id flat.NodeId, node flat.Node) []fl

249263 }))

250264}

251265

266+fn (t &Transformer) iterator_for_in_elem_type(iter_type string) ?string {

267+ mut clean := iter_type.trim_space()

268+ if clean.starts_with('&') {

269+ clean = clean[1..]

270+ }

271+ if clean == 'RunesIterator' || clean == 'builtin.RunesIterator' {

272+ return 'rune'

273+ }

274+ return none

275+}

276+

252277// lower_range_for_in builds lower range for in data for transform.

253278fn (mut t Transformer) lower_range_for_in(id flat.NodeId, node flat.Node, key_id flat.NodeId, low_id flat.NodeId, high_id flat.NodeId, body_ids []flat.NodeId) []flat.NodeId {

254279 if int(key_id) < 0 {

@@ -258,12 +283,13 @@ fn (mut t Transformer) lower_range_for_in(id flat.NodeId, node flat.Node, key_id

258283 if key.kind != .ident || key.value.len == 0 {

259284 return arr1(id)

260285 }

261- t.set_var_type(key.value, 'int')

286+ range_type := t.range_loop_var_type_name(low_id)

287+ t.set_var_type(key.value, range_type)

262288 low := t.transform_expr(low_id)

263289 high := t.stable_expr_for_reuse(high_id)

264290 mut prefix := []flat.NodeId{}

265291 t.drain_pending(mut prefix)

266- init := t.make_decl_assign_typed(key.value, low, 'int')

292+ init := t.make_decl_assign_typed(key.value, low, range_type)

267293 cond := t.make_infix(.lt, t.make_ident(key.value), high)

268294 post := t.make_expr_stmt(t.make_postfix(t.make_ident(key.value), .inc))

269295 new_body := t.transform_stmts(body_ids)

@@ -271,6 +297,71 @@ fn (mut t Transformer) lower_range_for_in(id flat.NodeId, node flat.Node, key_id

271297 return prefix

272298}

273299

300+fn (t &Transformer) range_loop_var_type_name(low_id flat.NodeId) string {

301+ typ := t.node_type(low_id)

302+ if typ.len == 0 {

303+ return 'int'

304+ }

305+ clean := t.normalize_type_alias(typ)

306+ if t.is_integer_type_name(clean) {

307+ return typ

308+ }

309+ return 'int'

310+}

311+

312+fn (mut t Transformer) lower_iterator_for_in(id flat.NodeId, node flat.Node, key_id flat.NodeId, val_id flat.NodeId, container_id flat.NodeId, iter_type string, elem_type string, has_index bool, body_ids []flat.NodeId) []flat.NodeId {

313+ if int(key_id) < 0 {

314+ return arr1(id)

315+ }

316+ key := t.a.nodes[int(key_id)]

317+ if key.kind != .ident || key.value.len == 0 {

318+ return arr1(id)

319+ }

320+ mut elem_name := key.value

321+ if has_index {

322+ if int(val_id) < 0 {

323+ return arr1(id)

324+ }

325+ val := t.a.nodes[int(val_id)]

326+ if val.kind != .ident || val.value.len == 0 {

327+ return arr1(id)

328+ }

329+ elem_name = val.value

330+ }

331+ iter_name := t.new_temp('iter')

332+ next_name := t.new_temp('iter_next')

333+ iter_expr := t.transform_expr(container_id)

334+ mut prefix := []flat.NodeId{}

335+ t.drain_pending(mut prefix)

336+ prefix << t.make_decl_assign_typed(iter_name, iter_expr, iter_type)

337+ init := if has_index {

338+ t.set_var_type(key.value, 'int')

339+ t.make_decl_assign_typed(key.value, t.make_int_literal(0), 'int')

340+ } else {

341+ t.make_empty()

342+ }

343+ t.set_var_type(elem_name, elem_type)

344+ next_call := t.make_call_typed('RunesIterator.next', arr1(t.make_prefix(.amp,

345+ t.make_ident(iter_name))), '?${elem_type}')

346+ next_decl := t.make_decl_assign_typed(next_name, next_call, '?${elem_type}')

347+ no_value := t.make_prefix(.not, t.make_selector(t.make_ident(next_name), 'ok', 'bool'))

348+ break_if_done := t.make_if(no_value, t.make_block(arr1(t.a.add(.break_stmt))), t.make_empty())

349+ elem_decl := t.make_decl_assign_typed(elem_name, t.make_selector(t.make_ident(next_name),

350+ 'value', elem_type), elem_type)

351+ mut loop_body := []flat.NodeId{}

352+ loop_body << next_decl

353+ loop_body << break_if_done

354+ loop_body << elem_decl

355+ loop_body << t.transform_stmts(body_ids)

356+ post := if has_index {

357+ t.make_expr_stmt(t.make_postfix(t.make_ident(key.value), .inc))

358+ } else {

359+ t.make_empty()

360+ }

361+ prefix << t.make_for_stmt(init, t.make_bool_literal(true), post, loop_body, node)

362+ return prefix

363+}

364+

274365// lower_indexed_for_in builds lower indexed for in data for transform.

275366fn (mut t Transformer) lower_indexed_for_in(id flat.NodeId, node flat.Node, key_id flat.NodeId, val_id flat.NodeId, container_id flat.NodeId, iter_type string, has_index bool, body_ids []flat.NodeId) []flat.NodeId {

276367 if int(key_id) < 0 {

@@ -285,7 +376,9 @@ fn (mut t Transformer) lower_indexed_for_in(id flat.NodeId, node flat.Node, key_

285376 t.drain_pending(mut prefix)

286377 mut actual_iter_type := iter_type

287378 container_type := t.node_type(container)

288- if container_type.len > 0 && !for_iter_type_has_generic_placeholder(actual_iter_type)

379+ if container_type.len > 0 && container_type !in ['array', 'map', 'unknown']

380+ && for_iter_type_is_container(container_type)

381+ && !for_iter_type_has_generic_placeholder(actual_iter_type)

289382 && !(t.is_fixed_array_type(actual_iter_type) && !t.is_fixed_array_type(container_type)) {

290383 actual_iter_type = container_type

291384 }

@@ -377,9 +470,6 @@ fn (mut t Transformer) make_for_stmt(init flat.NodeId, cond flat.NodeId, post fl

377470

378471// detect_for_in_type resolves detect for in type information for transform.

379472fn (mut t Transformer) detect_for_in_type(node flat.Node) string {

380- if node.typ.len > 0 {

381- return node.typ

382- }

383473 header_count := node.value.int()

384474 container_idx := if header_count >= 3 { header_count - 1 } else { 2 }

385475 if node.children_count > container_idx {

@@ -387,11 +477,26 @@ fn (mut t Transformer) detect_for_in_type(node flat.Node) string {

387477 if fixed_array_type := t.detect_for_in_global_fixed_array_type(iter_id) {

388478 return fixed_array_type

389479 }

390- return t.node_type(iter_id)

480+ iter_type := t.node_type(iter_id)

481+ if iter_type.len > 0

482+ && (node.typ.len == 0 || for_iter_type_has_generic_placeholder(node.typ)

483+ || for_iter_type_is_container(iter_type)) {

484+ return iter_type

485+ }

486+ }

487+ if node.typ.len > 0 {

488+ return node.typ

391489 }

392490 return ''

393491}

394492

493+fn for_iter_type_is_container(iter_type string) bool {

494+ clean := iter_type.trim_space()

495+ return clean == 'string' || clean.starts_with('[]') || clean.starts_with('&[]')

496+ || clean.starts_with('...') || clean.starts_with('map[') || clean.starts_with('&map[')

497+ || clean.starts_with('[')

498+}

499+

395500fn (t &Transformer) detect_for_in_global_fixed_array_type(id flat.NodeId) ?string {

396501 if int(id) < 0 {

397502 return none

vlib/v3/transform/if.v +9 -3

@@ -61,7 +61,7 @@ fn (mut t Transformer) try_expand_if_guard(_id flat.NodeId, node flat.Node) ?[]f

6161 }

6262 }

6363 }

64- if value_decls.len == 0 {

64+ if value_decls.len == 0 && lhs.value != '_' && value_type != 'void' {

6565 value_decls << t.make_decl_assign_typed(lhs.value, t.make_selector(t.make_ident(tmp_name),

6666 'value', value_type), value_type)

6767 }

@@ -78,7 +78,9 @@ fn (mut t Transformer) try_expand_if_guard(_id flat.NodeId, node flat.Node) ?[]f

7878 }

7979 }

8080 } else {

81- t.set_var_type(lhs.value, value_type)

81+ if lhs.value != '_' && value_type != 'void' {

82+ t.set_var_type(lhs.value, value_type)

83+ }

8284 }

8385 mut then_children := []flat.NodeId{}

8486 for value_decl in value_decls {

@@ -107,13 +109,17 @@ fn (mut t Transformer) try_expand_if_guard(_id flat.NodeId, node flat.Node) ?[]f

107109}

108110

109111// optional_result_expr_type_name supports optional result expr type name handling for Transformer.

110-fn (t &Transformer) optional_result_expr_type_name(id flat.NodeId) string {

112+fn (mut t Transformer) optional_result_expr_type_name(id flat.NodeId) string {

111113 if int(id) < 0 {

112114 return ''

113115 }

114116 if !isnil(t.tc) {

115117 node := t.a.nodes[int(id)]

116118 if node.kind == .call {

119+ concrete_ret := t.concrete_generic_call_return_type(id, node)

120+ if t.is_optional_type_name(concrete_ret) {

121+ return concrete_ret

122+ }

117123 if name := t.tc.resolved_call_name(id) {

118124 if ret := t.tc.fn_ret_types[name] {

119125 ret_name := ret.name()

vlib/v3/transform/map.v +26 -4

@@ -34,6 +34,23 @@ fn (mut t Transformer) make_new_map_call(map_type string) flat.NodeId {

3434 return t.make_call_typed('new_map', args, map_type)

3535}

3636

37+fn (t &Transformer) new_map_call_type(node flat.Node) string {

38+ if node.kind != .call || node.children_count < 3 {

39+ return ''

40+ }

41+ callee := t.a.child_node(&node, 0)

42+ if callee.kind != .ident || callee.value != 'new_map' {

43+ return ''

44+ }

45+ key_size := t.a.child_node(&node, 1)

46+ value_size := t.a.child_node(&node, 2)

47+ if key_size.kind != .sizeof_expr || value_size.kind != .sizeof_expr || key_size.value.len == 0

48+ || value_size.value.len == 0 {

49+ return ''

50+ }

51+ return 'map[${key_size.value}]${value_size.value}'

52+}

53+

3754// map_callback_names supports map callback names handling for transform.

3855fn map_callback_names(key_type string) (string, string, string, string) {

3956 if key_type == 'string' {

@@ -91,9 +108,14 @@ fn (mut t Transformer) map_index_info(index_id flat.NodeId) ?MapIndexInfo {

91108

92109// make_map_get_expr builds make map get expr data for transform.

93110fn (mut t Transformer) make_map_get_expr(map_expr flat.NodeId, base_type string, key_name string, zero_name string, value_type string) flat.NodeId {

111+ clean_value_type := if t.is_fixed_array_type(value_type) {

112+ fixed_array_canonical_type(value_type)

113+ } else {

114+ value_type

115+ }

94116 call := t.make_call_typed('map__get', arr3(t.runtime_addr(map_expr, base_type), t.make_prefix(.amp,

95117 t.make_ident(key_name)), t.make_prefix(.amp, t.make_ident(zero_name))), 'voidptr')

96- cast := t.make_cast('&${value_type}', call, '&${value_type}')

118+ cast := t.make_cast('&${clean_value_type}', call, '&${clean_value_type}')

97119 return t.make_prefix(.mul, cast)

98120}

99121

@@ -458,15 +480,15 @@ fn (mut t Transformer) lower_map_init_to_runtime(id flat.NodeId, node flat.Node)

458480 for i := 0; i + 1 < node.children_count; i += 2 {

459481 key_name := t.new_temp('map_key')

460482 value_name := t.new_temp('map_val')

461- t.pending_stmts << t.make_decl_assign_typed(key_name,

462- t.transform_expr(t.a.child(&node, i)), key_type)

483+ t.pending_stmts << t.make_decl_assign_typed(key_name, t.transform_expr_for_type(t.a.child(&node, i),

484+ key_type), key_type)

463485 value_id := t.a.child(&node, i + 1)

464486 value := if value_type.starts_with('&') && t.is_sum_type_name(value_type[1..]) {

465487 t.transform_expr_for_type(value_id, value_type)

466488 } else if value_type in t.sum_types || t.resolve_sum_name(value_type) in t.sum_types {

467489 t.wrap_sum_value(value_id, value_type)

468490 } else {

469- t.transform_expr(value_id)

491+ t.transform_expr_for_type(value_id, value_type)

470492 }

471493 t.pending_stmts << t.make_decl_assign_typed(value_name, value, value_type)

472494 call := t.make_call_typed('map__set', arr3(t.make_prefix(.amp, t.make_ident(tmp_name)), t.make_prefix(.amp,

vlib/v3/transform/monomorphize.v +562 -19

@@ -60,11 +60,15 @@ fn (mut t Transformer) monomorphize_pass() []string {

6060 call_module := t.node_module_map_cache[i] or { cur_module }

6161 decl_key, args := t.generic_call_specialization(flat.NodeId(i), node,

6262 call_module, decls) or { continue }

63+ decl := decls[decl_key] or { continue }

64+ if !t.call_has_source_generic_args(node)

65+ && t.generic_args_contain_alias(args, decl.module) {

66+ t.a.nodes[i].value = args.join(', ')

67+ }

6368 spec_key := generic_fn_spec_key(decl_key, args)

6469 if emitted[spec_key] {

6570 continue

6671 }

67- decl := decls[decl_key] or { continue }

6872 generated << t.generated_fn_used_names(decl,

6973 t.emit_generic_fn_specialization(decl, args), args)

7074 emitted[spec_key] = true

@@ -563,6 +567,7 @@ fn (mut t Transformer) transform_specialized_fn_body(clone_id flat.NodeId, modul

563567 old_fn_name := t.cur_fn_name

564568 old_ret_type := t.cur_fn_ret_type

565569 old_var_types := t.var_types.clone()

570+ old_is_generic := t.cur_fn_is_generic

566571 t.cur_module = module_name

567572 t.cur_file = file_name

568573 if !isnil(t.tc) {

@@ -577,6 +582,7 @@ fn (mut t Transformer) transform_specialized_fn_body(clone_id flat.NodeId, modul

577582 t.cur_fn_name = old_fn_name

578583 t.cur_fn_ret_type = old_ret_type

579584 t.var_types = old_var_types

585+ t.cur_fn_is_generic = old_is_generic

580586}

581587

582588fn (mut t Transformer) generated_fn_used_names(decl GenericFnDecl, clone_id flat.NodeId, args []string) []string {

@@ -597,6 +603,7 @@ fn (mut t Transformer) specialize_cloned_fn_signature(clone_id flat.NodeId, decl

597603 params := t.generic_fn_param_names(decl.node, decl.module)

598604 t.a.nodes[int(clone_id)].typ = t.specialized_signature_type_text(decl, decl.node.typ, args,

599605 params)

606+ t.a.nodes[int(clone_id)].generic_params = []string{}

600607 mut dst_params := []flat.NodeId{}

601608 clone := t.a.nodes[int(clone_id)]

602609 for i in 0 .. clone.children_count {

@@ -819,6 +826,10 @@ fn (mut t Transformer) rewrite_generic_calls(decls map[string]GenericFnDecl, tem

819826 decl_key, args := t.generic_call_specialization(flat.NodeId(i), node, call_module,

820827 decls) or { continue }

821828 decl := decls[decl_key] or { continue }

829+ if !t.call_has_source_generic_args(node)

830+ && t.generic_args_contain_alias(args, decl.module) {

831+ t.a.nodes[i].value = args.join(', ')

832+ }

822833 if t.generic_decl_is_receiver_method(decl.node) {

823834 t.rewrite_generic_method_call(flat.NodeId(i), node, decl, args)

824835 } else {

@@ -859,6 +870,44 @@ fn (mut t Transformer) concrete_generic_call_return_type(id flat.NodeId, node fl

859870 return t.normalize_type_alias(ret)

860871}

861872

873+fn (mut t Transformer) concrete_generic_call_param_types(id flat.NodeId, node flat.Node) ?[]types.Type {

874+ if node.kind != .call || node.children_count == 0 || isnil(t.tc) {

875+ return none

876+ }

877+ decls := t.cached_generic_fn_decls()

878+ if decls.len == 0 {

879+ return none

880+ }

881+ decl_key := t.generic_call_decl_key(id, node, t.cur_module, decls) or { return none }

882+ decl := decls[decl_key] or { return none }

883+ if t.should_skip_generic_call_specialization(decl_key) {

884+ return none

885+ }

886+ mut args := []string{}

887+ if explicit := t.explicit_generic_call_args(node, t.cur_module) {

888+ args = explicit.clone()

889+ } else {

890+ args = t.infer_generic_call_args_from_params(decl, node) or { return none }

891+ }

892+ if args.len == 0 || t.generic_args_have_placeholders(args) {

893+ return none

894+ }

895+ params := t.generic_fn_param_names(decl.node, decl.module)

896+ mut result := []types.Type{}

897+ for i in 0 .. decl.node.children_count {

898+ child := t.a.child_node(&decl.node, i)

899+ if child.kind != .param {

900+ continue

901+ }

902+ param_type := t.specialized_signature_type_text(decl, child.typ, args, params)

903+ result << t.tc.parse_type(param_type)

904+ }

905+ if result.len == 0 {

906+ return none

907+ }

908+ return result

909+}

910+

862911fn (mut t Transformer) cached_generic_fn_decls() map[string]GenericFnDecl {

863912 if !t.generic_fn_decls_ready {

864913 t.generic_fn_decls_cache = t.collect_generic_fn_decls()

@@ -885,10 +934,7 @@ fn (mut t Transformer) infer_generic_call_args_from_params(decl GenericFnDecl, n

885934 param_idx++

886935 continue

887936 }

888- mut arg_type := t.node_type(arg_id)

889- if arg_type.len == 0 {

890- arg_type = t.generic_arg_expr_type(arg_id)

891- }

937+ arg_type := t.generic_call_arg_type_for_inference(arg_id)

892938 if arg_type.len > 0 {

893939 infer_generic_type_args(child.typ, arg_type, mut inferred)

894940 if is_recv_param {

@@ -910,10 +956,14 @@ fn (mut t Transformer) rewrite_generic_plain_call(id flat.NodeId, node flat.Node

910956 spec_value := specialized_generic_fn_value(decl.node.value, args)

911957 spec_name := transform_qualified_fn_name(decl.module, spec_value)

912958 ret_typ := t.specialized_fn_return_type_text(decl, args)

959+ param_types := t.specialized_generic_call_param_type_texts(decl, args)

913960 mut children := []flat.NodeId{cap: int(node.children_count)}

914961 children << t.make_ident(spec_name)

915962 for i in 1 .. node.children_count {

916- children << t.a.child(&node, i)

963+ arg_id := t.a.child(&node, i)

964+ param_idx := i - 1

965+ param_type := if param_idx < param_types.len { param_types[param_idx] } else { '' }

966+ children << t.retype_generic_call_literal_arg(arg_id, param_type)

917967 }

918968 start := t.a.children.len

919969 for child in children {

@@ -931,6 +981,159 @@ fn (mut t Transformer) rewrite_generic_plain_call(id flat.NodeId, node flat.Node

931981 t.clear_resolved_call(id)

932982}

933983

984+fn (mut t Transformer) specialized_generic_call_param_type_texts(decl GenericFnDecl, args []string) []string {

985+ params := t.generic_fn_param_names(decl.node, decl.module)

986+ mut result := []string{}

987+ for i in 0 .. decl.node.children_count {

988+ child := t.a.child_node(&decl.node, i)

989+ if child.kind != .param {

990+ continue

991+ }

992+ result << t.specialized_call_target_type_text(decl, child.typ, args, params)

993+ }

994+ return result

995+}

996+

997+fn (mut t Transformer) specialized_call_target_type_text(decl GenericFnDecl, typ string, args []string, params []string) string {

998+ substituted := substitute_generic_type_text_with_params(typ, args, params)

999+ return t.qualify_specialized_signature_type_text(substituted, decl)

1000+}

1001+

1002+fn (mut t Transformer) retype_generic_call_literal_arg(arg_id flat.NodeId, param_type string) flat.NodeId {

1003+ if int(arg_id) < 0 || param_type.len == 0 {

1004+ return arg_id

1005+ }

1006+ node := t.a.nodes[int(arg_id)]

1007+ if node.kind == .array_literal

1008+ && t.generic_call_array_literal_can_retype_inline(node, param_type) {

1009+ return t.transform_expr_for_type(arg_id, param_type)

1010+ }

1011+ if node.kind == .ident && node.value.contains('arr_lit') && node.typ.starts_with('[]')

1012+ && param_type.starts_with('[]') && node.typ != param_type {

1013+ if t.retype_lowered_array_literal_temp(node.value, param_type) {

1014+ t.a.nodes[int(arg_id)].typ = param_type

1015+ t.set_var_type(node.value, param_type)

1016+ }

1017+ }

1018+ return arg_id

1019+}

1020+

1021+fn (t &Transformer) generic_call_array_literal_can_retype_inline(node flat.Node, param_type string) bool {

1022+ if !param_type.starts_with('[]') {

1023+ return false

1024+ }

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

1026+ child := t.a.child_node(&node, i)

1027+ if child.kind !in [.int_literal, .float_literal, .bool_literal, .char_literal,

1028+ .string_literal, .cast_expr, .as_expr] {

1029+ return false

1030+ }

1031+ }

1032+ return true

1033+}

1034+

1035+fn (mut t Transformer) retype_lowered_array_literal_temp(name string, array_type string) bool {

1036+ if !array_type.starts_with('[]') {

1037+ return false

1038+ }

1039+ elem_type := array_type[2..]

1040+ mut changed := false

1041+ for i in 0 .. t.a.nodes.len {

1042+ node := t.a.nodes[i]

1043+ if node.kind == .decl_assign && node.children_count >= 2 {

1044+ lhs_id := t.a.child(&node, 0)

1045+ lhs := t.a.nodes[int(lhs_id)]

1046+ if lhs.kind == .ident && lhs.value == name {

1047+ rhs_id := t.a.child(&node, 1)

1048+ t.a.nodes[i].typ = array_type

1049+ t.a.nodes[int(lhs_id)].typ = array_type

1050+ t.set_var_type(name, array_type)

1051+ t.retype_array_new_call(rhs_id, elem_type, array_type)

1052+ changed = true

1053+ }

1054+ }

1055+ if node.kind == .call && t.call_is_array_push_to_name(node, name) {

1056+ value_name := t.array_push_value_name(node) or { continue }

1057+ if t.retype_decl_assign_name(value_name, elem_type) {

1058+ changed = true

1059+ }

1060+ }

1061+ }

1062+ return changed

1063+}

1064+

1065+fn (mut t Transformer) retype_array_new_call(call_id flat.NodeId, elem_type string, array_type string) {

1066+ if int(call_id) < 0 || int(call_id) >= t.a.nodes.len {

1067+ return

1068+ }

1069+ call := t.a.nodes[int(call_id)]

1070+ if call.kind != .call || call.children_count < 2 {

1071+ return

1072+ }

1073+ callee := t.a.child_node(&call, 0)

1074+ if callee.kind != .ident || callee.value != 'array_new' {

1075+ return

1076+ }

1077+ sizeof_id := t.a.child(&call, 1)

1078+ if int(sizeof_id) >= 0 && int(sizeof_id) < t.a.nodes.len {

1079+ if t.a.nodes[int(sizeof_id)].kind == .sizeof_expr {

1080+ t.a.nodes[int(sizeof_id)].value = elem_type

1081+ }

1082+ }

1083+ t.a.nodes[int(call_id)].typ = array_type

1084+}

1085+

1086+fn (t &Transformer) call_is_array_push_to_name(node flat.Node, name string) bool {

1087+ if node.children_count < 3 {

1088+ return false

1089+ }

1090+ callee := t.a.child_node(&node, 0)

1091+ if callee.kind != .ident || callee.value != 'array_push' {

1092+ return false

1093+ }

1094+ arg := t.a.child_node(&node, 1)

1095+ if arg.kind != .prefix || arg.children_count == 0 {

1096+ return false

1097+ }

1098+ base := t.a.child_node(arg, 0)

1099+ return base.kind == .ident && base.value == name

1100+}

1101+

1102+fn (t &Transformer) array_push_value_name(node flat.Node) ?string {

1103+ if node.children_count < 3 {

1104+ return none

1105+ }

1106+ arg := t.a.child_node(&node, 2)

1107+ if arg.kind != .prefix || arg.children_count == 0 {

1108+ return none

1109+ }

1110+ base := t.a.child_node(arg, 0)

1111+ if base.kind != .ident || base.value.len == 0 {

1112+ return none

1113+ }

1114+ return base.value

1115+}

1116+

1117+fn (mut t Transformer) retype_decl_assign_name(name string, typ string) bool {

1118+ mut changed := false

1119+ for i in 0 .. t.a.nodes.len {

1120+ node := t.a.nodes[i]

1121+ if node.kind != .decl_assign || node.children_count < 1 {

1122+ continue

1123+ }

1124+ lhs_id := t.a.child(&node, 0)

1125+ lhs := t.a.nodes[int(lhs_id)]

1126+ if lhs.kind != .ident || lhs.value != name {

1127+ continue

1128+ }

1129+ t.a.nodes[i].typ = typ

1130+ t.a.nodes[int(lhs_id)].typ = typ

1131+ t.set_var_type(name, typ)

1132+ changed = true

1133+ }

1134+ return changed

1135+}

1136+

9341137// call_is_selector_form reports whether a call node embeds its receiver inside the

9351138// callee (`recv.method(args)`) rather than passing it as an explicit child

9361139// (`Type.method(recv, args)`, the ident-lowered form).

@@ -954,7 +1157,7 @@ fn (mut t Transformer) rewrite_generic_method_call(id flat.NodeId, node flat.Nod

9541157 // specialized function explicitly, passing the receiver as the first argument

9551158 // (exactly like a plain generic call). Pure struct-generic methods keep the

9561159 // selector callee, which cgen resolves via the already-monomorphized receiver.

957- if decl.node.generic_params.len > 0 {

1160+ if t.generic_decl_has_method_level_params(decl) {

9581161 t.rewrite_method_level_generic_call(id, node, decl, args)

9591162 return

9601163 }

@@ -1087,8 +1290,15 @@ fn (mut t Transformer) generic_call_specialization(id flat.NodeId, node flat.Nod

10871290 if t.should_skip_generic_call_specialization(decl_key) {

10881291 return none

10891292 }

1090- if args := t.explicit_generic_call_args(node, module_name) {

1091- if args.len > 0 && !t.generic_args_have_placeholders(args) {

1293+ if t.call_has_source_generic_args(node) {

1294+ if args := t.explicit_generic_call_args(node, module_name) {

1295+ if args.len > 0 && !t.generic_args_have_placeholders(args) {

1296+ return decl_key, args

1297+ }

1298+ }

1299+ } else if args := t.explicit_generic_call_args(node, module_name) {

1300+ if args.len > 0 && !t.generic_args_have_placeholders(args)

1301+ && t.generic_args_contain_alias(args, module_name) {

10921302 return decl_key, args

10931303 }

10941304 }

@@ -1097,9 +1307,22 @@ fn (mut t Transformer) generic_call_specialization(id flat.NodeId, node flat.Nod

10971307 return decl_key, args

10981308 }

10991309 }

1310+ if args := t.explicit_generic_call_args(node, module_name) {

1311+ if args.len > 0 && !t.generic_args_have_placeholders(args) {

1312+ return decl_key, args

1313+ }

1314+ }

11001315 return none

11011316}

11021317

1318+fn (t &Transformer) call_has_source_generic_args(node flat.Node) bool {

1319+ if node.children_count == 0 {

1320+ return false

1321+ }

1322+ fn_node := t.a.child_node(&node, 0)

1323+ return fn_node.kind == .index && fn_node.children_count >= 2 && fn_node.value != 'range'

1324+}

1325+

11031326fn (t &Transformer) should_skip_generic_call_specialization(decl_key string) bool {

11041327 return decl_key in ['json.decode', 'json2.decode', 'x.json2.decode', 'json.encode',

11051328 'json2.encode', 'x.json2.encode', 'veb.run_at', 'veb.run_new']

@@ -1236,10 +1459,11 @@ fn (t &Transformer) generic_call_arg_count_matches_decl(node flat.Node, decl Gen

12361459 // (`Type.method(recv, args)`) carries the receiver as a real child, so

12371460 // children = [callee, recv, args...] and actual == children_count - 1.

12381461 is_receiver := decl.node.value.contains('.')

1462+ actual_args := t.generic_call_effective_arg_count(node)

12391463 actual := if is_receiver && t.call_is_selector_form(node) {

1240- int(node.children_count)

1464+ actual_args + 1

12411465 } else {

1242- int(node.children_count) - 1

1466+ actual_args

12431467 }

12441468 if is_variadic {

12451469 return actual >= param_count - 1

@@ -1247,6 +1471,31 @@ fn (t &Transformer) generic_call_arg_count_matches_decl(node flat.Node, decl Gen

12471471 return actual == param_count

12481472}

12491473

1474+fn (t &Transformer) generic_call_effective_arg_count(node flat.Node) int {

1475+ if node.children_count <= 1 {

1476+ return 0

1477+ }

1478+ mut count := 0

1479+ mut i := 1

1480+ for i < node.children_count {

1481+ arg := t.a.child_node(&node, i)

1482+ if arg.kind == .field_init {

1483+ count++

1484+ for i < node.children_count {

1485+ field := t.a.child_node(&node, i)

1486+ if field.kind != .field_init {

1487+ break

1488+ }

1489+ i++

1490+ }

1491+ continue

1492+ }

1493+ count++

1494+ i++

1495+ }

1496+ return count

1497+}

1498+

12501499fn (t &Transformer) local_concrete_fn_shadows_generic(name string, module_name string, decls map[string]GenericFnDecl) bool {

12511500 qname := transform_qualified_fn_name(module_name, name)

12521501 if name in decls || qname in decls {

@@ -1299,7 +1548,7 @@ fn (t &Transformer) explicit_generic_call_args(node flat.Node, module_name strin

12991548 if fn_node.kind != .index || fn_node.children_count < 2 || fn_node.value == 'range' {

13001549 return none

13011550 }

1302- type_arg := t.generic_call_type_arg_name(t.a.child(fn_node, 1))

1551+ type_arg := t.generic_call_type_args_name(fn_node)

13031552 if type_arg.len == 0 {

13041553 return none

13051554 }

@@ -1343,10 +1592,7 @@ fn (mut t Transformer) infer_generic_call_args(decl GenericFnDecl, _id flat.Node

13431592 param_idx++

13441593 continue

13451594 }

1346- mut arg_type := t.node_type(arg_id)

1347- if arg_type.len == 0 {

1348- arg_type = t.generic_arg_expr_type(arg_id)

1349- }

1595+ arg_type := t.generic_call_arg_type_for_inference(arg_id)

13501596 if arg_type.len > 0 {

13511597 infer_generic_type_args(child.typ, arg_type, mut inferred)

13521598 if is_recv_param {

@@ -1369,6 +1615,9 @@ fn (mut t Transformer) infer_generic_call_args(decl GenericFnDecl, _id flat.Node

13691615}

13701616

13711617fn (t &Transformer) generic_arg_for_decl_module(arg string, module_name string) string {

1618+ if t.generic_arg_is_alias_name(arg, module_name) {

1619+ return t.qualify_generic_arg_for_decl_module(arg, module_name)

1620+ }

13721621 normalized := t.normalize_type_in_module(arg, module_name)

13731622 qualified := t.qualify_generic_arg_for_decl_module(normalized, module_name)

13741623 if qualified != normalized {

@@ -1380,6 +1629,36 @@ fn (t &Transformer) generic_arg_for_decl_module(arg string, module_name string)

13801629 return normalized

13811630}

13821631

1632+fn (t &Transformer) generic_arg_is_alias_name(arg string, module_name string) bool {

1633+ clean := arg.trim_space()

1634+ if clean.len == 0 || isnil(t.tc) {

1635+ return false

1636+ }

1637+ if clean in t.tc.type_aliases {

1638+ return true

1639+ }

1640+ if module_name.len > 0 && module_name != 'main' && module_name != 'builtin' {

1641+ if '${module_name}.${clean}' in t.tc.type_aliases {

1642+ return true

1643+ }

1644+ }

1645+ for alias, _ in t.tc.type_aliases {

1646+ if alias.all_after_last('.') == clean {

1647+ return true

1648+ }

1649+ }

1650+ return false

1651+}

1652+

1653+fn (t &Transformer) generic_args_contain_alias(args []string, module_name string) bool {

1654+ for arg in args {

1655+ if t.generic_arg_is_alias_name(arg, module_name) {

1656+ return true

1657+ }

1658+ }

1659+ return false

1660+}

1661+

13831662fn (t &Transformer) qualify_generic_arg_for_decl_module(arg string, module_name string) string {

13841663 clean := arg.trim_space()

13851664 if clean.len == 0 {

@@ -1448,6 +1727,31 @@ fn (t &Transformer) generic_arg_expr_type(id flat.NodeId) string {

14481727 }

14491728 node := t.a.nodes[int(id)]

14501729 match node.kind {

1730+ .array_literal {

1731+ if node.children_count > 0 {

1732+ child_id := t.a.child(&node, 0)

1733+ mut elem_type := t.generic_arg_expr_type(child_id)

1734+ if elem_type.len == 0 {

1735+ elem_type = t.node_type(child_id)

1736+ }

1737+ if elem_type.len > 0 {

1738+ return '[]${elem_type}'

1739+ }

1740+ }

1741+ if node.typ.len > 0 {

1742+ return t.normalize_type_alias(node.typ)

1743+ }

1744+ }

1745+ .fn_literal, .lambda_expr {

1746+ if fn_type := t.fn_value_type_name(id) {

1747+ return fn_type

1748+ }

1749+ }

1750+ .cast_expr, .as_expr {

1751+ if node.value.len > 0 {

1752+ return node.value

1753+ }

1754+ }

14511755 .infix {

14521756 if node.children_count >= 2 {

14531757 left_type := t.node_type(t.a.child(&node, 0))

@@ -1488,7 +1792,31 @@ fn (t &Transformer) generic_arg_expr_type(id flat.NodeId) string {

14881792 else {}

14891793 }

14901794

1491- return ''

1795+ return t.node_type(id)

1796+}

1797+

1798+fn (t &Transformer) generic_call_arg_type_for_inference(id flat.NodeId) string {

1799+ if int(id) < 0 || int(id) >= t.a.nodes.len {

1800+ return ''

1801+ }

1802+ node := t.a.nodes[int(id)]

1803+ if node.kind in [.array_literal, .fn_literal, .lambda_expr] {

1804+ typ := t.generic_arg_expr_type(id)

1805+ if typ.len > 0 {

1806+ return typ

1807+ }

1808+ }

1809+ if node.typ.len > 0 {

1810+ typ := t.normalize_type_alias(node.typ)

1811+ if typ.len > 0 && !t.generic_arg_is_unresolved(typ) {

1812+ return typ

1813+ }

1814+ }

1815+ mut typ := t.node_type(id)

1816+ if typ.len == 0 {

1817+ typ = t.generic_arg_expr_type(id)

1818+ }

1819+ return typ

14921820}

14931821

14941822fn (t &Transformer) infer_generic_receiver_suffix_args(param_type string, arg_type string, mut inferred map[string]string) {

@@ -1591,6 +1919,9 @@ fn infer_generic_type_args(param_type string, arg_type string, mut inferred map[

15911919 if param.len == 0 || arg.len == 0 {

15921920 return

15931921 }

1922+ if arg == 'unknown' || arg == 'generic' {

1923+ return

1924+ }

15941925 if is_generic_fn_placeholder_name(param) {

15951926 if param !in inferred {

15961927 inferred[param] = arg

@@ -1624,6 +1955,10 @@ fn infer_generic_type_args(param_type string, arg_type string, mut inferred map[

16241955 infer_generic_type_args(param[1..], arg[1..], mut inferred)

16251956 return

16261957 }

1958+ if param.starts_with('fn') && arg.starts_with('fn') {

1959+ infer_generic_fn_type_args(param, arg, mut inferred)

1960+ return

1961+ }

16271962 if param.starts_with('map[') && arg.starts_with('map[') {

16281963 p_end := generic_matching_bracket(param, 3)

16291964 a_end := generic_matching_bracket(arg, 3)

@@ -1646,6 +1981,90 @@ fn infer_generic_type_args(param_type string, arg_type string, mut inferred map[

16461981 }

16471982}

16481983

1984+fn infer_generic_fn_type_args(param string, arg string, mut inferred map[string]string) {

1985+ p_params, p_ret := fn_type_text_parts(param) or { return }

1986+ a_params, a_ret := fn_type_text_parts(arg) or { return }

1987+ for i, p_param in p_params {

1988+ if i >= a_params.len {

1989+ break

1990+ }

1991+ infer_generic_type_args(generic_fn_type_param_payload(p_param),

1992+ generic_fn_type_param_payload(a_params[i]), mut inferred)

1993+ }

1994+ if p_ret.len > 0 && a_ret.len > 0 {

1995+ infer_generic_type_args(p_ret, a_ret, mut inferred)

1996+ }

1997+}

1998+

1999+fn fn_type_text_parts(typ string) ?([]string, string) {

2000+ clean := typ.trim_space()

2001+ open := clean.index_u8(`(`)

2002+ if open < 0 {

2003+ return none

2004+ }

2005+ mut depth := 1

2006+ mut close := open + 1

2007+ for close < clean.len {

2008+ if clean[close] == `(` {

2009+ depth++

2010+ } else if clean[close] == `)` {

2011+ depth--

2012+ if depth == 0 {

2013+ break

2014+ }

2015+ }

2016+ close++

2017+ }

2018+ if close >= clean.len {

2019+ return none

2020+ }

2021+ params_text := clean[open + 1..close]

2022+ params := split_fn_type_params(params_text)

2023+ ret := clean[close + 1..].trim_space()

2024+ return params, ret

2025+}

2026+

2027+fn split_fn_type_params(s string) []string {

2028+ mut parts := []string{}

2029+ mut depth := 0

2030+ mut start := 0

2031+ for i := 0; i < s.len; i++ {

2032+ c := s[i]

2033+ if c == `(` || c == `[` {

2034+ depth++

2035+ } else if c == `)` || c == `]` {

2036+ depth--

2037+ } else if c == `,` && depth == 0 {

2038+ part := s[start..i].trim_space()

2039+ if part.len > 0 {

2040+ parts << part

2041+ }

2042+ start = i + 1

2043+ }

2044+ }

2045+ tail := s[start..].trim_space()

2046+ if tail.len > 0 {

2047+ parts << tail

2048+ }

2049+ return parts

2050+}

2051+

2052+fn generic_fn_type_param_payload(param string) string {

2053+ mut text := param.trim_space()

2054+ if text.starts_with('mut ') {

2055+ text = text[4..].trim_space()

2056+ }

2057+ space := generic_top_level_space_index(text)

2058+ if space > 0 {

2059+ head := text[..space].trim_space()

2060+ tail := text[space + 1..].trim_space()

2061+ if generic_fn_type_param_head_is_name(head, tail) {

2062+ return tail

2063+ }

2064+ }

2065+ return text

2066+}

2067+

16492068fn (mut t Transformer) clone_generic_fn_node(node flat.Node, args []string) flat.NodeId {

16502069 return t.clone_generic_node_from(node, args, true)

16512070}

@@ -1674,6 +2093,8 @@ fn (mut t Transformer) clone_generic_node_from(node flat.Node, args []string, is

16742093 for i in 0 .. node.children_count {

16752094 children << t.clone_generic_node(t.a.child(&node, i), args)

16762095 }

2096+ cloned_typ := t.subst_type(node.typ, args)

2097+ t.retarget_cloned_new_map_call(node, mut children, cloned_typ)

16772098 start := t.a.children.len

16782099 for child in children {

16792100 t.a.children << child

@@ -1690,11 +2111,30 @@ fn (mut t Transformer) clone_generic_node_from(node flat.Node, args []string, is

16902111 pos: node.pos

16912112 children_start: start

16922113 children_count: flat.child_count(children.len)

1693- typ: t.subst_type(node.typ, args)

2114+ typ: cloned_typ

16942115 value: cloned_value

16952116 })

16962117}

16972118

2119+fn (mut t Transformer) retarget_cloned_new_map_call(node flat.Node, mut children []flat.NodeId, map_type string) {

2120+ if node.kind != .call || children.len < 7 || !map_type.starts_with('map[') {

2121+ return

2122+ }

2123+ callee := t.a.nodes[int(children[0])]

2124+ if callee.kind != .ident || callee.value != 'new_map' {

2125+ return

2126+ }

2127+ key_type, _ := t.map_type_parts(map_type)

2128+ if key_type.len == 0 {

2129+ return

2130+ }

2131+ hash_fn, eq_fn, clone_fn, free_fn := map_callback_names(key_type)

2132+ children[3] = t.make_ident(hash_fn)

2133+ children[4] = t.make_ident(eq_fn)

2134+ children[5] = t.make_ident(clone_fn)

2135+ children[6] = t.make_ident(free_fn)

2136+}

2137+

16982138fn (mut t Transformer) copy_cloned_resolution(src_id flat.NodeId, dst_id flat.NodeId) {

16992139 if isnil(t.tc) {

17002140 return

@@ -1704,7 +2144,8 @@ fn (mut t Transformer) copy_cloned_resolution(src_id flat.NodeId, dst_id flat.No

17042144 if src_idx < 0 || dst_idx < 0 {

17052145 return

17062146 }

1707- if src_idx < t.tc.resolved_call_set.len && t.tc.resolved_call_set[src_idx] {

2147+ if src_idx < t.tc.resolved_call_set.len && t.tc.resolved_call_set[src_idx]

2148+ && !t.resolved_call_is_generic_fn(t.tc.resolved_call_names[src_idx]) {

17082149 for t.tc.resolved_call_names.len <= dst_idx {

17092150 t.tc.resolved_call_names << ''

17102151 t.tc.resolved_call_set << false

@@ -1722,6 +2163,26 @@ fn (mut t Transformer) copy_cloned_resolution(src_id flat.NodeId, dst_id flat.No

17222163 }

17232164}

17242165

2166+fn (t &Transformer) resolved_call_is_generic_fn(name string) bool {

2167+ if name.len == 0 || isnil(t.tc) {

2168+ return false

2169+ }

2170+ if name in t.tc.fn_generic_params {

2171+ return true

2172+ }

2173+ base := generic_fn_decl_base_value(name)

2174+ if base in t.tc.fn_generic_params {

2175+ return true

2176+ }

2177+ if name.contains('.') {

2178+ short := name.all_after_last('.')

2179+ if short in t.tc.fn_generic_params {

2180+ return true

2181+ }

2182+ }

2183+ return false

2184+}

2185+

17252186fn substitute_generic_node_value(node flat.Node, args []string) string {

17262187 match node.kind {

17272188 .call, .array_init, .struct_init, .cast_expr, .as_expr, .sizeof_expr, .typeof_expr,

@@ -1862,6 +2323,31 @@ fn (t &Transformer) generic_decl_is_receiver_method(node flat.Node) bool {

18622323 return node.value.contains('.')

18632324}

18642325

2326+fn (mut t Transformer) generic_decl_has_method_level_params(decl GenericFnDecl) bool {

2327+ all_params := t.generic_fn_param_names(decl.node, decl.module)

2328+ if all_params.len == 0 {

2329+ return false

2330+ }

2331+ mut receiver_params := []string{}

2332+ if decl.node.value.contains('.') {

2333+ receiver := decl.node.value.all_before_last('.')

2334+ t.collect_generic_param_names_from_type(receiver, decl.module, mut receiver_params)

2335+ }

2336+ for i in 0 .. decl.node.children_count {

2337+ child := t.a.child_node(&decl.node, i)

2338+ if child.kind == .param {

2339+ t.collect_generic_param_names_from_type(child.typ, decl.module, mut receiver_params)

2340+ break

2341+ }

2342+ }

2343+ for param in all_params {

2344+ if param !in receiver_params {

2345+ return true

2346+ }

2347+ }

2348+ return false

2349+}

2350+

18652351fn (mut t Transformer) generic_fn_param_names(node flat.Node, module_name string) []string {

18662352 mut names := []string{}

18672353 for param in node.generic_params {

@@ -2087,6 +2573,13 @@ fn substitute_generic_type_text(typ string, args []string) string {

20872573 1..], args)

20882574 }

20892575 }

2576+ if clean.starts_with('(') && clean.ends_with(')') && clean.contains(',') {

2577+ mut parts := []string{}

2578+ for part in split_generic_args(clean[1..clean.len - 1]) {

2579+ parts << substitute_generic_type_text(part, args)

2580+ }

2581+ return '(' + parts.join(', ') + ')'

2582+ }

20902583 base, nested_args, ok := generic_app_parts(clean)

20912584 if ok {

20922585 mut resolved_args := []string{}

@@ -2151,6 +2644,13 @@ fn substitute_generic_type_text_with_params(typ string, args []string, params []

21512644 substitute_generic_type_text_with_params(clean[bracket_end + 1..], args, params)

21522645 }

21532646 }

2647+ if clean.starts_with('(') && clean.ends_with(')') && clean.contains(',') {

2648+ mut parts := []string{}

2649+ for part in split_generic_args(clean[1..clean.len - 1]) {

2650+ parts << substitute_generic_type_text_with_params(part, args, params)

2651+ }

2652+ return '(' + parts.join(', ') + ')'

2653+ }

21542654 if clean.starts_with('fn(') || clean.starts_with('fn (') {

21552655 // Substitute the generic params inside a function-type parameter so a specialized

21562656 // method body emits e.g. `fn (string) int`, not the placeholder `fn (T) int` (which

@@ -2294,12 +2794,55 @@ fn (t &Transformer) subst_node_value(node flat.Node, args []string) string {

22942794 .is_expr, .type_decl, .field_decl, .param {

22952795 return t.subst_type(node.value, args)

22962796 }

2797+ .comptime_if {

2798+ return t.subst_comptime_type_condition(node.value, args)

2799+ }

22972800 else {

22982801 return node.value

22992802 }

23002803 }

23012804}

23022805

2806+fn (t &Transformer) subst_comptime_type_condition(cond string, args []string) string {

2807+ clean := cond.trim_space()

2808+ if clean.starts_with('(') {

2809+ end := comptime_condition_matching_paren(clean, 0)

2810+ if end == clean.len - 1 {

2811+ inner := t.subst_comptime_type_condition(clean[1..clean.len - 1], args)

2812+ return '(${inner})'

2813+ }

2814+ }

2815+ or_idx := comptime_condition_top_level_index(clean, '||')

2816+ if or_idx >= 0 {

2817+ left := t.subst_comptime_type_condition(clean[..or_idx], args)

2818+ right := t.subst_comptime_type_condition(clean[or_idx + 2..], args)

2819+ return '${left} || ${right}'

2820+ }

2821+ and_idx := comptime_condition_top_level_index(clean, '&&')

2822+ if and_idx >= 0 {

2823+ left := t.subst_comptime_type_condition(clean[..and_idx], args)

2824+ right := t.subst_comptime_type_condition(clean[and_idx + 2..], args)

2825+ return '${left} && ${right}'

2826+ }

2827+ for op in [' !is ', ' is '] {

2828+ op_idx := comptime_condition_top_level_index(clean, op)

2829+ if op_idx >= 0 {

2830+ left := clean[..op_idx].trim_space()

2831+ right := clean[op_idx + op.len..].trim_space()

2832+ return '${t.subst_type(left, args)}${op}${t.subst_type(right, args)}'

2833+ }

2834+ }

2835+ if clean.starts_with('!') {

2836+ inner_raw := clean[1..].trim_space()

2837+ inner := t.subst_comptime_type_condition(inner_raw, args)

2838+ if inner_raw.starts_with('(') {

2839+ return '!(${inner})'

2840+ }

2841+ return '!${inner}'

2842+ }

2843+ return clean

2844+}

2845+

23032846// active_generic_param_index returns the position of a placeholder name within the

23042847// active generic params, or the positional fallback when none are active.

23052848fn (t &Transformer) active_generic_param_index(name string) int {

vlib/v3/transform/or.v +110 -1

@@ -11,6 +11,13 @@ struct ArrayIndexInfo {

1111 value_type string

1212}

1313

14+struct StringSliceInfo {

15+ base_id flat.NodeId

16+ start_id flat.NodeId

17+ end_id flat.NodeId

18+ has_end bool

19+}

20+

1421// EnumFromStringInfo stores enum from string info metadata used by transform.

1522struct EnumFromStringInfo {

1623 enum_type string

@@ -65,6 +72,81 @@ fn (mut t Transformer) is_array_index_or_expr(node flat.Node) bool {

6572 return true

6673}

6774

75+fn (mut t Transformer) string_slice_info(index_id flat.NodeId) ?StringSliceInfo {

76+ if int(index_id) < 0 {

77+ return none

78+ }

79+ expr := t.a.nodes[int(index_id)]

80+ if expr.kind != .index || expr.children_count < 2 || expr.value != 'range' {

81+ return none

82+ }

83+ base_id := t.a.child(&expr, 0)

84+ base_type := t.resolve_expr_type(base_id)

85+ if t.normalize_type_alias(base_type) != 'string' {

86+ return none

87+ }

88+ return StringSliceInfo{

89+ base_id: base_id

90+ start_id: t.a.child(&expr, 1)

91+ end_id: if expr.children_count > 2 { t.a.child(&expr, 2) } else { flat.empty_node }

92+ has_end: expr.children_count > 2

93+ }

94+}

95+

96+fn (mut t Transformer) is_string_slice_or_expr(node flat.Node) bool {

97+ if node.kind != .or_expr || node.children_count < 2 {

98+ return false

99+ }

100+ _ := t.string_slice_info(t.a.child(&node, 0)) or { return false }

101+ return true

102+}

103+

104+fn (mut t Transformer) transform_string_slice_or_expr(id flat.NodeId, node flat.Node) flat.NodeId {

105+ if node.children_count < 2 {

106+ return id

107+ }

108+ expr_id := t.a.child(&node, 0)

109+ body_id := t.a.child(&node, 1)

110+ info := t.string_slice_info(expr_id) or { return id }

111+ base_expr := t.stable_expr_for_reuse(info.base_id)

112+ start_name := t.new_temp('str_start')

113+ end_name := t.new_temp('str_end')

114+ val_name := t.new_temp('str_slice')

115+ outer_pending := t.pending_stmts.clone()

116+ t.pending_stmts.clear()

117+ start_expr := if int(info.start_id) >= 0 && t.a.nodes[int(info.start_id)].kind != .empty {

118+ t.transform_expr(info.start_id)

119+ } else {

120+ t.make_int_literal(0)

121+ }

122+ end_expr := if info.has_end {

123+ t.transform_expr(info.end_id)

124+ } else {

125+ t.make_selector(base_expr, 'len', 'int')

126+ }

127+ mut prelude := []flat.NodeId{}

128+ t.drain_pending(mut prelude)

129+ prelude << t.make_decl_assign_typed(start_name, start_expr, 'int')

130+ prelude << t.make_decl_assign_typed(end_name, end_expr, 'int')

131+ prelude << t.make_decl_assign_typed(val_name, t.zero_value_for_type('string'), 'string')

132+ start_ident := t.make_ident(start_name)

133+ lower_ok := t.make_infix(.ge, start_ident, t.make_int_literal(0))

134+ ordered := t.make_infix(.le, t.make_ident(start_name), t.make_ident(end_name))

135+ upper_ok := t.make_infix(.le, t.make_ident(end_name), t.make_selector(base_expr, 'len', 'int'))

136+ bounds_ok := t.make_infix(.logical_and, t.make_infix(.logical_and, lower_ok, ordered), upper_ok)

137+ slice_call := t.make_call_typed('string__substr', arr3(base_expr, t.make_ident(start_name),

138+ t.make_ident(end_name)), 'string')

139+ then_block := t.make_block(arr1(t.make_assign(t.make_ident(val_name), slice_call)))

140+ else_block :=

141+ t.make_block(t.lower_or_body_to_stmts(body_id, val_name, 'string', node.value, ''))

142+ t.pending_stmts = outer_pending

143+ for stmt in prelude {

144+ t.pending_stmts << stmt

145+ }

146+ t.pending_stmts << t.make_if(bounds_ok, then_block, else_block)

147+ return t.make_ident(val_name)

148+}

149+

68150// transform_array_index_or_expr transforms transform array index or expr data for transform.

69151fn (mut t Transformer) transform_array_index_or_expr(id flat.NodeId, node flat.Node) flat.NodeId {

70152 if node.children_count < 2 {

@@ -229,8 +311,23 @@ fn (mut t Transformer) transform_enum_from_string_or_expr(id flat.NodeId, node f

229311}

230312

231313// or_expr_types supports or expr types handling for Transformer.

232-fn (t &Transformer) or_expr_types(expr_id flat.NodeId, fallback_type string) (string, string) {

314+fn (mut t Transformer) or_expr_types(expr_id flat.NodeId, fallback_type string) (string, string) {

315+ expr_node := if int(expr_id) >= 0 && int(expr_id) < t.a.nodes.len {

316+ t.a.nodes[int(expr_id)]

317+ } else {

318+ flat.Node{}

319+ }

233320 if !isnil(t.tc) {

321+ if expr_node.kind == .call {

322+ concrete_ret := t.concrete_generic_call_return_type(expr_id, expr_node)

323+ if t.is_optional_type_name(concrete_ret) {

324+ return concrete_ret, t.optional_base_type(concrete_ret)

325+ }

326+ call_ret := t.get_call_return_type(expr_id, expr_node)

327+ if t.is_optional_type_name(call_ret) {

328+ return call_ret, t.optional_base_type(call_ret)

329+ }

330+ }

234331 if typ := t.tc.expr_type(expr_id) {

235332 if typ is types.OptionType {

236333 base_name := t.value_type_name(typ.base_type)

@@ -343,6 +440,18 @@ fn (mut t Transformer) zero_value_for_type(typ string) flat.NodeId {

343440 if clean.starts_with('map[') {

344441 return t.make_map_init(clean)

345442 }

443+ if t.is_fixed_array_type(clean) {

444+ fixed_type := fixed_array_canonical_type(clean)

445+ len_text := fixed_array_len_text(fixed_type)

446+ if is_decimal_text(len_text) {

447+ elem_type := fixed_array_elem_type(fixed_type)

448+ mut values := []flat.NodeId{cap: len_text.int()}

449+ for _ in 0 .. len_text.int() {

450+ values << t.zero_value_for_type(elem_type)

451+ }

452+ return t.make_array_literal_typed(values, fixed_type)

453+ }

454+ }

346455 if clean == 'string' {

347456 return t.make_string_literal('')

348457 }

vlib/v3/transform/return.v +24 -24

@@ -54,12 +54,18 @@ fn (t &Transformer) branch_tail_expr(block_id flat.NodeId) flat.NodeId {

5454

5555// make_return builds a `return <val>` statement node with the given type.

5656fn (mut t Transformer) make_return(val flat.NodeId, ret_typ string) flat.NodeId {

57+ return t.make_return_values(arr1(val), ret_typ)

58+}

59+

60+fn (mut t Transformer) make_return_values(vals []flat.NodeId, ret_typ string) flat.NodeId {

5761 start := t.a.children.len

58- t.a.children << val

62+ for val in vals {

63+ t.a.children << val

64+ }

5965 return t.a.add_node(flat.Node{

6066 kind: .return_stmt

6167 children_start: start

62- children_count: 1

68+ children_count: flat.child_count(vals.len)

6369 typ: ret_typ

6470 })

6571}

@@ -135,7 +141,8 @@ fn (mut t Transformer) try_expand_return_optional_expr(node flat.Node) ?[]flat.N

135141 ok_cond := t.make_selector(tmp_ident, 'ok', 'bool')

136142 value := t.make_selector(t.make_ident(tmp_name), 'value', t.optional_base_type(expr_type))

137143 then_block := t.make_block(arr1(t.make_return(value, ret_type)))

138- else_block := t.make_block(arr1(t.make_none_return_stmt()))

144+ err_expr := t.make_selector(t.make_ident(tmp_name), 'err', 'IError')

145+ else_block := t.make_block(arr1(t.make_none_return_stmt_with_err_expr(err_expr)))

139146 result << t.make_if(ok_cond, then_block, else_block)

140147 return result

141148}

@@ -166,7 +173,7 @@ fn (t &Transformer) return_expr_is_optional_result(id flat.NodeId) bool {

166173

167174// return_block_from_branch builds a block that keeps leading statements

168175// (transformed) and turns the tail expression of the branch into a `return`.

169-fn (mut t Transformer) return_block_from_branch(branch_id flat.NodeId, ret_typ string) flat.NodeId {

176+fn (mut t Transformer) return_block_from_branch(branch_id flat.NodeId, ret_typ string, extra_return_vals []flat.NodeId) flat.NodeId {

170177 branch := t.a.nodes[int(branch_id)]

171178 if branch.kind == .return_stmt {

172179 mut all := []flat.NodeId{}

@@ -180,16 +187,9 @@ fn (mut t Transformer) return_block_from_branch(branch_id flat.NodeId, ret_typ s

180187 if branch.kind != .block {

181188 // single expression branch: just `return <expr>`

182189 mut all := []flat.NodeId{}

183- // Convert a fixed-array branch value (e.g. a fixed-array const) to a dynamic

184- // array when the function returns `[]T`, matching a plain `return <expr>` and

185- // the `return match {...}` path (match_branch_return_block).

186- ret_val := if converted := t.fixed_array_return_value(branch_id) {

187- converted

188- } else {

189- t.wrap_sum_return_expr(branch_id)

190- }

190+ ret_vals := t.return_values_with_extra(branch_id, extra_return_vals)

191191 t.drain_pending(mut all)

192- all << t.make_return(ret_val, ret_typ)

192+ all << t.make_return_values(ret_vals, ret_typ)

193193 return t.make_block(all)

194194 }

195195 mut stmt_ids := []flat.NodeId{}

@@ -217,20 +217,16 @@ fn (mut t Transformer) return_block_from_branch(branch_id flat.NodeId, ret_typ s

217217 }

218218 return t.make_block(all)

219219 }

220- ret_val := if converted := t.fixed_array_return_value(tail_expr) {

221- converted

222- } else {

223- t.wrap_sum_return_expr(tail_expr)

224- }

220+ ret_vals := t.return_values_with_extra(tail_expr, extra_return_vals)

225221 t.drain_pending(mut all)

226- ret := t.make_return(ret_val, ret_typ)

222+ ret := t.make_return_values(ret_vals, ret_typ)

227223 all << ret

228224 return t.make_block(all)

229225}

230226

231227// build_return_if_chain recursively converts an if_expr (possibly an else-if

232228// chain) into an if-statement whose branch tails are `return` statements.

233-fn (mut t Transformer) build_return_if_chain(if_id flat.NodeId, ret_typ string) flat.NodeId {

229+fn (mut t Transformer) build_return_if_chain(if_id flat.NodeId, ret_typ string, extra_return_vals []flat.NodeId) flat.NodeId {

234230 if_node := t.a.nodes[int(if_id)]

235231 cond_id := t.a.child(&if_node, 0)

236232 cond_smartcasts := t.extract_all_is_exprs(cond_id)

@@ -239,7 +235,7 @@ fn (mut t Transformer) build_return_if_chain(if_id flat.NodeId, ret_typ string)

239235 for info in cond_smartcasts {

240236 t.push_smartcast(info.expr_name, info.variant_name, info.sum_type_name)

241237 }

242- then_block := t.return_block_from_branch(then_id, ret_typ)

238+ then_block := t.return_block_from_branch(then_id, ret_typ, extra_return_vals)

243239 for _ in cond_smartcasts {

244240 t.pop_smartcast()

245241 }

@@ -249,10 +245,10 @@ fn (mut t Transformer) build_return_if_chain(if_id flat.NodeId, ret_typ string)

249245 else_node := t.a.nodes[int(else_id)]

250246 if else_node.kind == .if_expr {

251247 // else-if chain: recurse, wrap resulting if-stmt in a block

252- inner := t.build_return_if_chain(else_id, ret_typ)

248+ inner := t.build_return_if_chain(else_id, ret_typ, extra_return_vals)

253249 else_block = t.make_block(arr1(inner))

254250 } else {

255- else_block = t.return_block_from_branch(else_id, ret_typ)

251+ else_block = t.return_block_from_branch(else_id, ret_typ, extra_return_vals)

256252 }

257253 }

258254 return t.make_if(new_cond, then_block, else_block)

@@ -281,7 +277,11 @@ fn (mut t Transformer) try_expand_return_if(_id flat.NodeId, node flat.Node) ?[]

281277 if val_node.kind != .if_expr || val_node.children_count < 3 {

282278 return none

283279 }

284- return arr1(t.build_return_if_chain(val_id, node.typ))

280+ mut extra_return_vals := []flat.NodeId{}

281+ for i in 1 .. node.children_count {

282+ extra_return_vals << t.a.child(&node, i)

283+ }

284+ return arr1(t.build_return_if_chain(val_id, node.typ, extra_return_vals))

285285}

286286

287287// match_branch_return_block supports match branch return block handling for Transformer.

vlib/v3/transform/struct.v +3 -0

@@ -552,6 +552,9 @@ fn (mut t Transformer) fixed_array_value_to_array(value_id flat.NodeId, fixed_ty

552552// transform_array_init_expr transforms .array_init nodes (e.g. `[]int{len: n}`).

553553// Recursively transforms any child expressions (len, cap, init values).

554554fn (mut t Transformer) transform_array_init_expr(id flat.NodeId, node flat.Node) flat.NodeId {

555+ if fixed := t.transform_fixed_array_init_expr(node) {

556+ return fixed

557+ }

555558 lowered := t.lower_array_init_to_runtime(id, node)

556559 if lowered != id {

557560 return lowered

vlib/v3/transform/transform.v +432 -57

@@ -27,6 +27,16 @@ fn arr3(a flat.NodeId, b flat.NodeId, c flat.NodeId) []flat.NodeId {

2727 return r

2828}

2929

30+// arr4 supports arr4 handling for transform.

31+fn arr4(a flat.NodeId, b flat.NodeId, c flat.NodeId, d flat.NodeId) []flat.NodeId {

32+ mut r := []flat.NodeId{}

33+ r << a

34+ r << b

35+ r << c

36+ r << d

37+ return r

38+}

39+

3040// node_kind_id supports node kind id handling for transform.

3141fn node_kind_id(node flat.Node) int {

3242 mut kind_id := node.kind_id

@@ -64,6 +74,7 @@ mut:

6474 cur_module string

6575 cur_fn_name string

6676 cur_fn_ret_type string

77+ cur_fn_is_generic bool

6778 var_types []VarTypeBinding

6879 pointer_value_lvalues map[string]bool

6980 temp_counter int

@@ -217,7 +228,7 @@ pub fn monomorphize_with_used(mut a flat.FlatAst, tc &types.TypeChecker, used_fn

217228 t.used_fns[c_name(name)] = true

218229 }

219230 t.materialize_generic_structs()

220- return augmented_used_fns

231+ return t.used_fns

221232}

222233

223234fn new_transformer(mut a flat.FlatAst, tc &types.TypeChecker, used_fns map[string]bool) Transformer {

@@ -232,6 +243,34 @@ fn new_transformer(mut a flat.FlatAst, tc &types.TypeChecker, used_fns map[strin

232243 }

233244}

234245

246+fn (mut t Transformer) mark_fn_used_name(name string) {

247+ if name.len == 0 {

248+ return

249+ }

250+ t.used_fns[name] = true

251+ t.used_fns[c_name(name)] = true

252+ if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {

253+ needs_module_prefix := !name.contains('.') || local_method_fn_name_needs_module_prefix(name)

254+ if needs_module_prefix && !name.starts_with('${t.cur_module}.') {

255+ qname := '${t.cur_module}.${name}'

256+ t.used_fns[qname] = true

257+ t.used_fns[c_name(qname)] = true

258+ }

259+ }

260+}

261+

262+fn local_method_fn_name_needs_module_prefix(name string) bool {

263+ if !name.contains('.') {

264+ return false

265+ }

266+ receiver_name := name.all_before('.')

267+ if receiver_name.len == 0 {

268+ return false

269+ }

270+ first := receiver_name[0]

271+ return first >= `A` && first <= `Z`

272+}

273+

235274fn (mut t Transformer) prepare() {

236275 t.collect_types()

237276 t.collect_const_suffixes()

@@ -860,8 +899,8 @@ fn (t &Transformer) clone_ast_base(base_nodes int, base_children int) &flat.Flat

860899// fork_worker builds a worker Transformer that shares this transformer's

861900// read-only collected maps (structs, sum types, fn return types, …) and

862901// operates on its own cloned AST `ast` and forked TypeChecker `wtc`. All

863-// per-function mutable state, helper-root tracking, and memoization caches are

864-// reset/private so the worker can run on its own thread.

902+// per-function mutable state, helper-root tracking, used-fn additions, and

903+// memoization caches are reset/private so the worker can run on its own thread.

865904fn (t &Transformer) fork_worker(ast &flat.FlatAst, wtc &types.TypeChecker) &Transformer {

866905 mut w := *t

867906 w.a = ast

@@ -880,6 +919,7 @@ fn (t &Transformer) fork_worker(ast &flat.FlatAst, wtc &types.TypeChecker) &Tran

880919 w.escaping_amp_ptrs = map[string]bool{}

881920 w.escaping_amp_sources = map[string]bool{}

882921 w.heaped_amp_locals = map[string]bool{}

922+ w.used_fns = t.used_fns.clone()

883923 w.temp_counter = 0

884924 w.cur_file = ''

885925 w.cur_module = ''

@@ -891,6 +931,14 @@ fn (t &Transformer) fork_worker(ast &flat.FlatAst, wtc &types.TypeChecker) &Tran

891931 return &w

892932}

893933

934+fn (mut t Transformer) merge_worker_used_fns(w &Transformer) {

935+ for name, used in w.used_fns {

936+ if used {

937+ t.used_fns[name] = true

938+ }

939+ }

940+}

941+

894942// merge_worker folds a finished worker's transformed output back into the master

895943// AST. The worker created its new nodes/children at indices base_nodes/base_children

896944// (matching the master at fork time); here they are appended to the master and every

@@ -1169,15 +1217,7 @@ fn (mut t Transformer) transform_const_string_interp(_id flat.NodeId, node flat.

11691217 mut parts := []flat.NodeId{cap: int(node.children_count)}

11701218 for i in 0 .. node.children_count {

11711219 child_id := t.a.child(&node, i)

1172- transformed := t.transform_expr(child_id)

1173- mut typ := t.reliable_stringify_type(child_id)

1174- if typ.len == 0 {

1175- typ = t.node_type(transformed)

1176- }

1177- if typ.len == 0 {

1178- typ = 'string'

1179- }

1180- parts << t.wrap_string_conversion(transformed, typ)

1220+ parts << t.transform_string_interp_part(child_id)

11811221 }

11821222 mut expr := parts[0]

11831223 for i in 1 .. parts.len {

@@ -1193,6 +1233,31 @@ fn (mut t Transformer) transform_const_string_interp(_id flat.NodeId, node flat.

11931233 return t.make_block(stmts)

11941234}

11951235

1236+fn (mut t Transformer) transform_string_interp_part(child_id flat.NodeId) flat.NodeId {

1237+ mut expr_id := child_id

1238+ mut format := ''

1239+ child := t.a.nodes[int(child_id)]

1240+ if child.kind == .directive && child.value == 'string_interp_format' && child.children_count > 0 {

1241+ expr_id = t.a.child(&child, 0)

1242+ format = child.typ

1243+ }

1244+ transformed := t.transform_expr(expr_id)

1245+ mut typ := t.node_type(transformed)

1246+ if typ.len == 0 {

1247+ typ = t.reliable_stringify_type(transformed)

1248+ }

1249+ if typ.len == 0 {

1250+ typ = t.reliable_stringify_type(expr_id)

1251+ }

1252+ if typ.len == 0 {

1253+ typ = t.node_type(expr_id)

1254+ }

1255+ if typ.len == 0 {

1256+ typ = 'string'

1257+ }

1258+ return t.wrap_formatted_string_conversion(transformed, typ, format)

1259+}

1260+

11961261// transform_fn_body transforms transform fn body data for transform.

11971262// try_heap_escaping_amp reports whether a `p := &v` decl is an escaping address of

11981263// a value local that must be heap-copied: `p` is in escaping_amp_ptrs (returned)

@@ -1407,6 +1472,8 @@ fn (mut t Transformer) collect_return_escape_idents(id flat.NodeId, mut names ma

14071472fn (mut t Transformer) transform_fn_body(fn_idx int) {

14081473 fn_node := t.a.nodes[fn_idx]

14091474 t.cur_fn_name = fn_node.value

1475+ old_is_generic := t.cur_fn_is_generic

1476+ t.cur_fn_is_generic = t.fn_decl_has_unresolved_generics(fn_node, t.cur_module)

14101477 param_count := t.fn_body_param_count(fn_node)

14111478 param_types := t.fn_body_param_types(fn_node, param_count)

14121479 t.cur_fn_ret_type = t.fn_body_return_type(fn_node)

@@ -1482,6 +1549,7 @@ fn (mut t Transformer) transform_fn_body(fn_idx int) {

14821549 generic_params: fn_node.generic_params

14831550 }

14841551 t.smartcast_stack.clear()

1552+ t.cur_fn_is_generic = old_is_generic

14851553}

14861554

14871555// fn_body_param_types supports fn body param types handling for Transformer.

@@ -1723,6 +1791,9 @@ pub fn (mut t Transformer) transform_stmt(id flat.NodeId) []flat.NodeId {

17231791 .block {

17241792 return t.transform_block_stmt(id, node)

17251793 }

1794+ .comptime_if {

1795+ return t.transform_comptime_if_stmt(id, node)

1796+ }

17261797 .if_expr {

17271798 return t.transform_if_stmt(id, node)

17281799 }

@@ -2074,16 +2145,7 @@ fn (mut t Transformer) transform_return_stmt(id flat.NodeId, node flat.Node) []f

20742145 mut new_children := []flat.NodeId{cap: int(node.children_count)}

20752146 for i in 0 .. node.children_count {

20762147 child_id := t.a.child(&node, i)

2077- if converted := t.fixed_array_return_value(child_id) {

2078- new_children << converted

2079- } else if copied := t.heap_copy_local_address_return(child_id) {

2080- new_children << copied

2081- } else if t.cur_fn_ret_type.len > 0 && t.cur_fn_ret_type !in t.sum_types

2082- && !t.is_optional_type_name(t.cur_fn_ret_type) {

2083- new_children << t.transform_expr_for_type(child_id, t.cur_fn_ret_type)

2084- } else {

2085- new_children << t.wrap_sum_return_expr(child_id)

2086- }

2148+ new_children << t.transform_return_child(child_id, i, int(node.children_count))

20872149 }

20882150 start := t.a.children.len

20892151 for nc in new_children {

@@ -2101,6 +2163,41 @@ fn (mut t Transformer) transform_return_stmt(id flat.NodeId, node flat.Node) []f

21012163 return t.with_pending_before(new_id)

21022164}

21032165

2166+fn (mut t Transformer) return_values_with_extra(first_id flat.NodeId, extra_ids []flat.NodeId) []flat.NodeId {

2167+ total := extra_ids.len + 1

2168+ mut vals := []flat.NodeId{cap: total}

2169+ vals << t.transform_return_child(first_id, 0, total)

2170+ for i, extra_id in extra_ids {

2171+ vals << t.transform_return_child(extra_id, i + 1, total)

2172+ }

2173+ return vals

2174+}

2175+

2176+fn (mut t Transformer) transform_return_child(child_id flat.NodeId, child_index int, total_children int) flat.NodeId {

2177+ if converted := t.fixed_array_return_value(child_id) {

2178+ return converted

2179+ }

2180+ if copied := t.heap_copy_local_address_return(child_id) {

2181+ return copied

2182+ }

2183+ target_type := t.return_child_target_type(child_index, total_children)

2184+ if target_type.len > 0 && target_type !in t.sum_types && !t.is_optional_type_name(target_type) {

2185+ return t.transform_expr_for_type(child_id, target_type)

2186+ }

2187+ return t.wrap_sum_return_expr(child_id)

2188+}

2189+

2190+fn (t &Transformer) return_child_target_type(child_index int, total_children int) string {

2191+ if total_children > 1 && !isnil(t.tc) && t.cur_fn_ret_type.len > 0 {

2192+ if items := multi_return_types_from_type(t.tc.parse_type(t.cur_fn_ret_type), total_children) {

2193+ if child_index >= 0 && child_index < items.len {

2194+ return items[child_index].name()

2195+ }

2196+ }

2197+ }

2198+ return t.cur_fn_ret_type

2199+}

2200+

21042201// heap_copy_local_address_return supports heap copy local address return handling for Transformer.

21052202fn (mut t Transformer) heap_copy_local_address_return(child_id flat.NodeId) ?flat.NodeId {

21062203 if !t.cur_fn_ret_type.starts_with('&') || int(child_id) < 0 {

@@ -2402,16 +2499,25 @@ fn (mut t Transformer) try_lower_struct_compound_assign(node flat.Node) ?[]flat.

24022499 if lhs.kind != .ident || lhs.value.len == 0 {

24032500 return none

24042501 }

2405- mut lhs_type := t.original_expr_type(lhs_id)

2502+ mut lhs_type := t.var_type(lhs.value)

2503+ if lhs_type.len == 0 {

2504+ lhs_type = t.original_expr_type(lhs_id)

2505+ }

24062506 if lhs_type.starts_with('&') {

24072507 return none

24082508 }

2409- struct_type := t.struct_lookup_name(lhs_type)

2410- if struct_type.len == 0 {

2509+ mut operator_type := t.struct_lookup_name(lhs_type)

2510+ if operator_type.len == 0 {

2511+ if _ := t.struct_operator_fn_name(lhs_type, op_name) {

2512+ operator_type = lhs_type

2513+ }

2514+ }

2515+ if operator_type.len == 0 {

24112516 return none

24122517 }

2413- method_name := t.struct_operator_fn_name(struct_type, op_name) or { return none }

2518+ method_name := t.struct_operator_fn_name(operator_type, op_name) or { return none }

24142519 rhs := t.transform_expr_for_type(rhs_id, lhs_type)

2520+ t.mark_fn_used_name(method_name)

24152521 call := t.make_call_typed(method_name, arr2(t.make_ident(lhs.value), rhs), lhs_type)

24162522 return arr1(t.make_assign(t.make_ident(lhs.value), call))

24172523}

@@ -2654,11 +2760,46 @@ fn (mut t Transformer) transform_expr_for_type(id flat.NodeId, target_type strin

26542760 return lowered

26552761 }

26562762 }

2763+ if node.kind == .postfix && node.op == .not && node.children_count == 1 {

2764+ child_id := t.a.child(&node, 0)

2765+ child := t.a.nodes[int(child_id)]

2766+ if child.kind == .array_literal {

2767+ if lowered := t.transform_fixed_array_literal_for_type(child_id, child, target_type) {

2768+ return lowered

2769+ }

2770+ }

2771+ }

26572772 if node.kind == .array_init {

26582773 if lowered := t.transform_empty_array_init_for_type(node, target_type) {

26592774 return lowered

26602775 }

26612776 }

2777+ if node.kind == .map_init {

2778+ clean_target := t.clean_map_type(target_type)

2779+ if clean_target.starts_with('map[') {

2780+ mut map_node := node

2781+ map_node.value = clean_target

2782+ map_node.typ = clean_target

2783+ return t.lower_map_init_to_runtime(id, map_node)

2784+ }

2785+ }

2786+ if target_type in ['f32', 'f64'] && node.kind == .infix

2787+ && node.op in [.plus, .minus, .mul, .div] && node.children_count >= 2 {

2788+ lhs := t.transform_expr_for_type(t.a.child(&node, 0), target_type)

2789+ rhs := t.transform_expr_for_type(t.a.child(&node, 1), target_type)

2790+ start := t.a.children.len

2791+ t.a.children << lhs

2792+ t.a.children << rhs

2793+ return t.a.add_node(flat.Node{

2794+ kind: .infix

2795+ op: node.op

2796+ children_start: start

2797+ children_count: 2

2798+ pos: node.pos

2799+ value: node.value

2800+ typ: target_type

2801+ })

2802+ }

26622803 }

26632804 expr := t.transform_expr(id)

26642805 return t.coerce_transformed_expr_to_type(expr, id, target_type)

@@ -2778,6 +2919,9 @@ fn (mut t Transformer) coerce_transformed_expr_to_type(expr flat.NodeId, source_

27782919 if expr_type.len == 0 || expr_type == target {

27792920 return expr

27802921 }

2922+ if target in ['f32', 'f64'] && t.is_integer_type_name(expr_type) {

2923+ return t.make_cast(target, expr, target)

2924+ }

27812925 if target.starts_with('&') {

27822926 if t.expr_is_nil_like(source_id) {

27832927 t.a.nodes[int(expr)].typ = target

@@ -3101,9 +3245,14 @@ fn (mut t Transformer) transform_decl_assign_stmt(id flat.NodeId, node flat.Node

31013245 rhs_id := t.a.child(&node, 1)

31023246 rhs := t.a.nodes[int(rhs_id)]

31033247 if rhs.kind == .call {

3104- call_typ := t.get_call_return_type(rhs_id, rhs)

3105- if call_typ.len > 0 {

3248+ call_typ := t.node_type(rhs_id)

3249+ if decl_type_is_usable(call_typ) {

31063250 typ = call_typ

3251+ } else if !decl_type_is_usable(typ) {

3252+ raw_call_typ := t.get_call_return_type(rhs_id, rhs)

3253+ if raw_call_typ.len > 0 {

3254+ typ = raw_call_typ

3255+ }

31073256 }

31083257 generic_typ := t.concrete_generic_call_return_type(rhs_id, rhs)

31093258 if generic_typ.len > 0 {

@@ -3204,7 +3353,7 @@ fn (mut t Transformer) transform_decl_assign_stmt(id flat.NodeId, node flat.Node

32043353 }

32053354 }

32063355 }

3207- if node.children_count == 2 && node.typ.len == 0 {

3356+ if node.children_count == 2 && !decl_type_is_usable(node.typ) {

32083357 lhs := t.a.nodes[int(new_children[0])]

32093358 if lhs.kind == .ident && lhs.value.len > 0 {

32103359 rhs_typ := t.node_type(new_children[1])

@@ -3870,6 +4019,207 @@ fn (mut t Transformer) transform_block_stmt(_id flat.NodeId, node flat.Node) []f

38704019 return arr1(new_block)

38714020}

38724021

4022+fn (mut t Transformer) transform_comptime_if_stmt(_id flat.NodeId, node flat.Node) []flat.NodeId {

4023+ take_then := t.comptime_type_condition_value(node.value) or { return arr1(_id) }

4024+ branch_index := if take_then { 0 } else { 1 }

4025+ if branch_index >= node.children_count {

4026+ return []flat.NodeId{}

4027+ }

4028+ branch_id := t.a.child(&node, branch_index)

4029+ branch := t.a.nodes[int(branch_id)]

4030+ if branch.kind == .block {

4031+ return t.transform_stmts(t.a.children_of(&branch))

4032+ }

4033+ return t.transform_stmt(branch_id)

4034+}

4035+

4036+fn comptime_condition_matching_paren(s string, start int) int {

4037+ mut paren_depth := 0

4038+ mut bracket_depth := 0

4039+ for i in start .. s.len {

4040+ match s[i] {

4041+ `(` {

4042+ paren_depth++

4043+ }

4044+ `)` {

4045+ paren_depth--

4046+ if paren_depth == 0 && bracket_depth == 0 {

4047+ return i

4048+ }

4049+ }

4050+ `[` {

4051+ bracket_depth++

4052+ }

4053+ `]` {

4054+ bracket_depth--

4055+ }

4056+ else {}

4057+ }

4058+ }

4059+ return s.len

4060+}

4061+

4062+fn comptime_condition_strip_outer_parens(cond string) string {

4063+ mut clean := cond.trim_space()

4064+ for clean.len >= 2 && clean.starts_with('(') {

4065+ end := comptime_condition_matching_paren(clean, 0)

4066+ if end != clean.len - 1 {

4067+ break

4068+ }

4069+ clean = clean[1..clean.len - 1].trim_space()

4070+ }

4071+ return clean

4072+}

4073+

4074+fn comptime_condition_top_level_index(s string, needle string) int {

4075+ if needle.len == 0 || s.len < needle.len {

4076+ return -1

4077+ }

4078+ mut paren_depth := 0

4079+ mut bracket_depth := 0

4080+ for i := 0; i <= s.len - needle.len; i++ {

4081+ match s[i] {

4082+ `(` {

4083+ paren_depth++

4084+ }

4085+ `)` {

4086+ if paren_depth > 0 {

4087+ paren_depth--

4088+ }

4089+ }

4090+ `[` {

4091+ bracket_depth++

4092+ }

4093+ `]` {

4094+ if bracket_depth > 0 {

4095+ bracket_depth--

4096+ }

4097+ }

4098+ else {}

4099+ }

4100+

4101+ if paren_depth == 0 && bracket_depth == 0 && s[i..i + needle.len] == needle {

4102+ return i

4103+ }

4104+ }

4105+ return -1

4106+}

4107+

4108+fn (mut t Transformer) comptime_type_condition_value(cond string) ?bool {

4109+ clean := comptime_condition_strip_outer_parens(cond)

4110+ if clean == 'true' {

4111+ return true

4112+ }

4113+ if clean == 'false' {

4114+ return false

4115+ }

4116+ or_idx := comptime_condition_top_level_index(clean, '||')

4117+ if or_idx >= 0 {

4118+ left := t.comptime_type_condition_value(clean[..or_idx]) or { return none }

4119+ if left {

4120+ return true

4121+ }

4122+ return t.comptime_type_condition_value(clean[or_idx + 2..])

4123+ }

4124+ and_idx := comptime_condition_top_level_index(clean, '&&')

4125+ if and_idx >= 0 {

4126+ left := t.comptime_type_condition_value(clean[..and_idx]) or { return none }

4127+ if !left {

4128+ return false

4129+ }

4130+ return t.comptime_type_condition_value(clean[and_idx + 2..])

4131+ }

4132+ for op in [' !is ', ' is '] {

4133+ op_idx := comptime_condition_top_level_index(clean, op)

4134+ if op_idx >= 0 {

4135+ left := clean[..op_idx].trim_space()

4136+ right := clean[op_idx + op.len..].trim_space()

4137+ matches := t.comptime_type_matches(left, right) or { return none }

4138+ return if op == ' is ' { matches } else { !matches }

4139+ }

4140+ }

4141+ if clean.starts_with('!') {

4142+ value := t.comptime_type_condition_value(clean[1..]) or { return none }

4143+ return !value

4144+ }

4145+ return none

4146+}

4147+

4148+fn (mut t Transformer) comptime_type_matches(actual string, expected string) ?bool {

4149+ clean_actual := actual.trim_space()

4150+ clean_expected := expected.trim_space()

4151+ if clean_actual.len == 0 || clean_expected.len == 0

4152+ || is_generic_fn_placeholder_name(clean_actual) {

4153+ return none

4154+ }

4155+ normalized := t.normalize_type_alias(clean_actual)

4156+ match clean_expected {

4157+ '$array' {

4158+ return normalized.starts_with('[]') || transform_type_text_is_fixed_array(normalized)

4159+ }

4160+ '$map' {

4161+ return normalized.starts_with('map[')

4162+ }

4163+ '$function' {

4164+ return normalized.starts_with('fn(') || normalized.starts_with('fn (')

4165+ }

4166+ '$option' {

4167+ return normalized.starts_with('?')

4168+ }

4169+ '$int' {

4170+ if typ := types.builtin_type(normalized) {

4171+ return typ.is_integer()

4172+ }

4173+ return false

4174+ }

4175+ '$float' {

4176+ if typ := types.builtin_type(normalized) {

4177+ return typ.is_float()

4178+ }

4179+ return false

4180+ }

4181+ '$struct' {

4182+ return !isnil(t.tc) && normalized in t.tc.structs

4183+ }

4184+ '$enum' {

4185+ return !isnil(t.tc) && normalized in t.tc.enum_names

4186+ }

4187+ '$alias' {

4188+ if isnil(t.tc) {

4189+ return false

4190+ }

4191+ if clean_actual in t.tc.type_aliases {

4192+ return true

4193+ }

4194+ if !clean_actual.contains('.') && t.cur_module.len > 0 && t.cur_module != 'main'

4195+ && t.cur_module != 'builtin' {

4196+ return '${t.cur_module}.${clean_actual}' in t.tc.type_aliases

4197+ }

4198+ return false

4199+ }

4200+ '$sumtype' {

4201+ return !isnil(t.tc) && normalized in t.tc.sum_types

4202+ }

4203+ '$interface' {

4204+ return !isnil(t.tc) && normalized in t.tc.interface_names

4205+ }

4206+ else {}

4207+ }

4208+

4209+ return normalized == t.normalize_type_alias(clean_expected)

4210+}

4211+

4212+fn transform_type_text_is_fixed_array(typ string) bool {

4213+ if typ.starts_with('[]') || typ.starts_with('[?') {

4214+ return false

4215+ }

4216+ if typ.starts_with('[') {

4217+ end := typ.index_u8(`]`)

4218+ return end > 1

4219+ }

4220+ return typ.contains('[') && typ.ends_with(']') && is_decimal_text(fixed_array_len_text(typ))

4221+}

4222+

38734223// transform_block_expr transforms transform block expr data for transform.

38744224fn (mut t Transformer) transform_block_expr(_id flat.NodeId, node flat.Node) flat.NodeId {

38754225 mut child_ids := []flat.NodeId{cap: int(node.children_count)}

@@ -4114,6 +4464,15 @@ fn (mut t Transformer) transform_call_expr(id flat.NodeId, node flat.Node) flat.

41144464 call_id := t.normalize_generic_call_expr(id, node)

41154465 mut call_node := t.a.nodes[int(call_id)]

41164466 mut resolved_typ := t.concrete_generic_call_return_type(call_id, call_node)

4467+ if resolved_typ.len == 0 && call_node.typ.len > 0 {

4468+ call_typ := t.normalize_type_alias(call_node.typ)

4469+ if call_typ !in ['array', 'map', 'unknown'] {

4470+ resolved_typ = call_typ

4471+ }

4472+ }

4473+ if resolved_typ.len == 0 {

4474+ resolved_typ = t.new_map_call_type(call_node)

4475+ }

41174476 if resolved_typ.len == 0 {

41184477 resolved_typ = t.get_call_return_type(call_id, call_node)

41194478 }

@@ -4133,6 +4492,9 @@ fn (mut t Transformer) transform_call_expr(id flat.NodeId, node flat.Node) flat.

41334492 if lowered := t.try_lower_builtin_call(call_id, call_node) {

41344493 return lowered

41354494 }

4495+ if lowered := t.try_lower_array_repeat_call(call_id, call_node) {

4496+ return lowered

4497+ }

41364498 if lowered := t.try_lower_join_path_call(call_id, call_node) {

41374499 return lowered

41384500 }

@@ -4230,9 +4592,11 @@ fn (mut t Transformer) transform_index_expr(id flat.NodeId, node flat.Node) flat

42304592 // Children unchanged: update the type annotation in place (applying the same

42314593 // `typ = node.value when empty` fixup the rebuild would) instead of copying the node.

42324594 mut index_typ := node.typ

4233- if index_typ.len == 0 && node.value.len > 0 {

4234- elem_type := t.index_expr_type(id, node)

4235- index_typ = if elem_type.len > 0 { elem_type } else { node.value }

4595+ elem_type := t.index_expr_type(id, node)

4596+ if elem_type == 'u8' || (index_typ.len == 0 && elem_type.len > 0) {

4597+ index_typ = elem_type

4598+ } else if index_typ.len == 0 && node.value.len > 0 {

4599+ index_typ = node.value

42364600 }

42374601 if !changed {

42384602 if index_typ.len > 0 {

@@ -4273,21 +4637,7 @@ fn (mut t Transformer) transform_string_interp(_id flat.NodeId, node flat.Node)

42734637 mut hoisting := false

42744638 for i in 0 .. node.children_count {

42754639 child_id := t.a.child(&node, i)

4276- transformed := t.transform_expr(child_id)

4277- mut typ := t.node_type(transformed)

4278- if typ.len == 0 {

4279- typ = t.reliable_stringify_type(transformed)

4280- }

4281- if typ.len == 0 {

4282- typ = t.reliable_stringify_type(child_id)

4283- }

4284- if typ.len == 0 {

4285- typ = t.node_type(child_id)

4286- }

4287- if typ.len == 0 {

4288- typ = 'string'

4289- }

4290- part := t.wrap_string_conversion(transformed, typ)

4640+ part := t.transform_string_interp_part(child_id)

42914641 mut part_stmts := []flat.NodeId{}

42924642 t.drain_pending(mut part_stmts)

42934643 if !hoisting && part_stmts.len == 0 {

@@ -4666,6 +5016,9 @@ fn (mut t Transformer) transform_or_expr(id flat.NodeId, node flat.Node) flat.No

46665016 if t.is_array_index_or_expr(node) {

46675017 return t.transform_array_index_or_expr(id, node)

46685018 }

5019+ if t.is_string_slice_or_expr(node) {

5020+ return t.transform_string_slice_or_expr(id, node)

5021+ }

46695022 if t.is_enum_from_string_or_expr(node) {

46705023 return t.transform_enum_from_string_or_expr(id, node)

46715024 }

@@ -5008,6 +5361,12 @@ fn (mut t Transformer) transform_postfix_expr(id flat.NodeId, node flat.Node) fl

50085361 }

50095362 child_id := t.a.child(&node, 0)

50105363 child := t.a.nodes[int(child_id)]

5364+ if node.op == .not && child.kind == .array_literal {

5365+ node_type := t.node_type(id)

5366+ if lowered := t.transform_fixed_array_literal_for_type(child_id, child, node_type) {

5367+ return lowered

5368+ }

5369+ }

50115370 new_child := if child.kind == .ident && t.pointer_value_lvalues[child.value] {

50125371 t.make_paren(t.make_prefix(.mul, t.make_ident(child.value)))

50135372 } else {

@@ -5078,7 +5437,11 @@ fn (mut t Transformer) transform_cast_expr(id flat.NodeId, node flat.Node) flat.

50785437 mut new_children := []flat.NodeId{cap: int(node.children_count)}

50795438 for i in 0 .. node.children_count {

50805439 child_id := t.a.child(&node, i)

5081- new_children << t.transform_expr(child_id)

5440+ if target_type in ['f32', 'f64'] {

5441+ new_children << t.transform_expr_for_type(child_id, target_type)

5442+ } else {

5443+ new_children << t.transform_expr(child_id)

5444+ }

50825445 }

50835446 start := t.a.children.len

50845447 for nc in new_children {

@@ -5460,7 +5823,12 @@ pub fn (mut t Transformer) new_temp(prefix string) string {

54605823

54615824// make_ident builds make ident data for transform.

54625825pub fn (mut t Transformer) make_ident(name string) flat.NodeId {

5463- return t.a.add_val(.ident, name)

5826+ id := t.a.add_val(.ident, name)

5827+ typ := t.var_type(name)

5828+ if typ.len > 0 {

5829+ t.a.nodes[int(id)].typ = typ

5830+ }

5831+ return id

54645832}

54655833

54665834// make_decl_assign builds make decl assign data for transform.

@@ -5943,6 +6311,16 @@ fn (t &Transformer) resolve_expr_type(id flat.NodeId) string {

59436311 if concrete_typ.len > 0 {

59446312 return concrete_typ

59456313 }

6314+ new_map_typ := t.new_map_call_type(node)

6315+ if new_map_typ.len > 0 {

6316+ return new_map_typ

6317+ }

6318+ if node.typ.len > 0 {

6319+ typ := t.normalize_type_alias(node.typ)

6320+ if typ !in ['array', 'map', 'unknown'] {

6321+ return typ

6322+ }

6323+ }

59466324 mut ret := t.get_call_return_type(id, node)

59476325 if ret.len == 0 {

59486326 ret = t.current_call_return_type(node)

@@ -5950,12 +6328,6 @@ fn (t &Transformer) resolve_expr_type(id flat.NodeId) string {

59506328 if ret.len > 0 {

59516329 return ret

59526330 }

5953- if node.typ.len > 0 {

5954- typ := t.normalize_type_alias(node.typ)

5955- if typ !in ['array', 'map'] {

5956- return typ

5957- }

5958- }

59596331 return ''

59606332 }

59616333 .cast_expr {

@@ -6049,6 +6421,9 @@ fn (t &Transformer) resolve_expr_type(id flat.NodeId) string {

60496421 if node.op == .not {

60506422 return 'bool'

60516423 }

6424+ if node.op in [.plus, .minus, .bit_not] {

6425+ return child_type

6426+ }

60526427 }

60536428 return ''

60546429 }

@@ -6062,7 +6437,7 @@ fn (t &Transformer) resolve_expr_type(id flat.NodeId) string {

60626437 return 'f64'

60636438 }

60646439 .char_literal {

6065- return 'u8'

6440+ return 'rune'

60666441 }

60676442 .string_literal, .string_interp {

60686443 return 'string'

@@ -6089,7 +6464,7 @@ fn (t &Transformer) resolve_expr_type(id flat.NodeId) string {

60896464 if node.op == .plus && lhs_type == 'string' {

60906465 return 'string'

60916466 }

6092- rhs_type := t.resolve_expr_type(t.a.child(&node, 1))

6467+ rhs_type := t.node_type(t.a.child(&node, 1))

60936468 if node.op == .plus && rhs_type == 'string' {

60946469 return 'string'

60956470 }

vlib/v3/transform/transform_d_parallel.v +1 -0

@@ -113,6 +113,7 @@ fn (mut t Transformer) run_parallel_transform(items []FnWorkItem, base_nodes int

113113 // node numbering stays deterministic for reproducible builds.

114114 for ci in 0 .. thread_count {

115115 ww := unsafe { &Transformer(workers[ci]) }

116+ t.merge_worker_used_fns(ww)

116117 t.merge_worker(ww, chunks[ci + 1], base_nodes, base_children)

117118 // The worker's cloned base AST is no longer needed after merge.

118119 unsafe {

vlib/v3/transform/type_propagation.v +27 -2

@@ -54,6 +54,17 @@ fn (t &Transformer) decl_rhs_type(id flat.NodeId) string {

5454 if fn_type := t.fn_value_type_name(id) {

5555 return fn_type

5656 }

57+ if int(id) >= 0 {

58+ node := t.a.nodes[int(id)]

59+ if node.kind == .map_init {

60+ if node.typ.starts_with('map[') {

61+ return node.typ

62+ }

63+ if node.value.starts_with('map[') {

64+ return node.value

65+ }

66+ }

67+ }

5768 return t.node_type(id)

5869}

5970

@@ -576,6 +587,14 @@ fn (t &Transformer) normalize_type_alias_uncached(typ string) string {

576587 if typ.starts_with('[]') {

577588 return '[]' + t.normalize_type_alias(typ[2..])

578589 }

590+ if typ.starts_with('map[') {

591+ bracket_end := generic_matching_bracket(typ, 3)

592+ if bracket_end < typ.len {

593+ key := t.normalize_type_alias(typ[4..bracket_end])

594+ value := t.normalize_type_alias(typ[bracket_end + 1..])

595+ return 'map[${key}]${value}'

596+ }

597+ }

579598 if typ.starts_with('?') {

580599 return '?' + t.normalize_type_alias(typ[1..])

581600 }

@@ -720,7 +739,9 @@ fn (t &Transformer) resolve_index_elem_type(node flat.Node) string {

720739 }

721740 base_type = ptr_elem_type

722741 }

723- if node.value == 'range' {

742+ is_slice := node.value == 'range'

743+ || (node.children_count > 1 && t.a.child_node(&node, 1).kind == .range)

744+ if is_slice {

724745 if base_type == 'string' {

725746 return 'string'

726747 }

@@ -753,6 +774,10 @@ fn (t &Transformer) resolve_index_elem_type(node flat.Node) string {

753774}

754775

755776fn (t &Transformer) index_expr_type(id flat.NodeId, node flat.Node) string {

777+ resolved_elem_type := t.resolve_index_elem_type(node)

778+ if resolved_elem_type == 'u8' {

779+ return resolved_elem_type

780+ }

756781 if !isnil(t.tc) {

757782 if typ := t.tc.expr_type(id) {

758783 name := typ.name()

@@ -761,7 +786,7 @@ fn (t &Transformer) index_expr_type(id flat.NodeId, node flat.Node) string {

761786 }

762787 }

763788 }

764- return t.resolve_index_elem_type(node)

789+ return resolved_elem_type

765790}

766791

767792// node_type returns the v-type string for an expression node, preferring the

vlib/v3/types/checker.v +1067 -82

@@ -202,6 +202,7 @@ pub mut:

202202 const_types map[string]Type

203203 const_exprs map[string]flat.NodeId

204204 const_modules map[string]string

205+ const_files map[string]string

205206 const_suffixes map[string]string // dot-suffix -> full const key (O(1) lookup; '' if ambiguous)

206207 imports map[string]string // alias -> short module name

207208 file_imports map[string]string

@@ -284,6 +285,7 @@ pub fn TypeChecker.new(a &flat.FlatAst) TypeChecker {

284285 const_types: map[string]Type{}

285286 const_exprs: map[string]flat.NodeId{}

286287 const_modules: map[string]string{}

288+ const_files: map[string]string{}

287289 const_suffixes: map[string]string{}

288290 imports: map[string]string{}

289291 file_imports: map[string]string{}

@@ -710,9 +712,12 @@ pub fn (mut tc TypeChecker) collect(a &flat.FlatAst) {

710712 tc.const_types[qname] = unknown_type('pending const `${qname}`')

711713 tc.const_exprs[qname] = a.child(f, 0)

712714 tc.const_modules[qname] = tc.cur_module

715+ tc.const_files[qname] = tc.cur_file

713716 } else if f.kind == .const_field && f.typ.len > 0 {

714717 qname := tc.qualify_name(f.value)

715718 tc.const_types[qname] = tc.parse_type(f.typ)

719+ tc.const_modules[qname] = tc.cur_module

720+ tc.const_files[qname] = tc.cur_file

716721 }

717722 }

718723 }

@@ -762,10 +767,12 @@ fn (mut tc TypeChecker) resolve_const_types() {

762767 return

763768 }

764769 saved_module := tc.cur_module

770+ saved_file := tc.cur_file

765771 for _ in 0 .. tc.const_exprs.len {

766772 mut changed := false

767773 for name, expr_id in tc.const_exprs {

768774 tc.cur_module = tc.const_modules[name] or { '' }

775+ tc.cur_file = tc.const_files[name] or { '' }

769776 mut new_type := tc.resolve_type(expr_id)

770777 new_type = tc.const_type_from_initializer(name, new_type)

771778 if new_type is Unknown {

@@ -788,6 +795,7 @@ fn (mut tc TypeChecker) resolve_const_types() {

788795 }

789796 }

790797 tc.cur_module = saved_module

798+ tc.cur_file = saved_file

791799}

792800

793801// const_type_from_initializer converts const type from initializer data for types.

@@ -832,6 +840,37 @@ fn (tc &TypeChecker) const_type_from_initializer(name string, typ Type) Type {

832840 return typ

833841}

834842

843+fn (tc &TypeChecker) const_type_for_name(name string) ?Type {

844+ qname := tc.qualify_name(name)

845+ if typ := tc.const_types[qname] {

846+ return tc.const_type_from_initializer(qname, typ)

847+ }

848+ if typ := tc.const_types[name] {

849+ return tc.const_type_from_initializer(name, typ)

850+ }

851+ return none

852+}

853+

854+fn (tc &TypeChecker) const_type_for_selector(node flat.Node) ?Type {

855+ if node.kind != .selector || node.children_count == 0 {

856+ return none

857+ }

858+ base_node := tc.a.child_node(&node, 0)

859+ if base_node.kind != .ident {

860+ return none

861+ }

862+ resolved := tc.resolve_import_alias(base_node.value) or { base_node.value }

863+ qname := '${resolved}.${node.value}'

864+ if typ := tc.const_types[qname] {

865+ return tc.const_type_from_initializer(qname, typ)

866+ }

867+ if key := tc.const_key_for_suffix(qname) {

868+ typ := tc.const_types[key] or { unknown_type('unknown const `${key}`') }

869+ return tc.const_type_from_initializer(key, typ)

870+ }

871+ return none

872+}

873+

835874// qualify_fn_name supports qualify fn name handling for TypeChecker.

836875pub fn (tc &TypeChecker) qualify_fn_name(name string) string {

837876 if tc.cur_module.len == 0 || tc.cur_module == 'main' || tc.cur_module == 'builtin' {

@@ -866,6 +905,9 @@ pub fn (tc &TypeChecker) qualify_name(name string) string {

866905 if name.starts_with('?') {

867906 return '?' + tc.qualify_name(name[1..])

868907 }

908+ if name.starts_with('!') {

909+ return '!' + tc.qualify_name(name[1..])

910+ }

869911 if name.contains('.') {

870912 return tc.resolve_imported_type_text(name)

871913 }

@@ -1336,7 +1378,8 @@ fn (mut tc TypeChecker) annotate_struct_init_expected_exprs(node flat.Node) {

13361378}

13371379

13381380fn (mut tc TypeChecker) annotate_call_expected_exprs(id flat.NodeId, node flat.Node) {

1339- info := tc.resolve_call_info(id, node) or { return }

1381+ info0 := tc.resolve_call_info(id, node) or { return }

1382+ info := tc.specialized_plain_generic_call_info(node, info0)

13401383 if info.name.len > 0 && !is_array_dsl_call_name(info.name) {

13411384 tc.remember_resolved_call(id, info.name)

13421385 }

@@ -1409,7 +1452,7 @@ fn (mut tc TypeChecker) annotate_for_in(_id flat.NodeId, node flat.Node) {

14091452 tc.annotate_node(container_id)

14101453 has_val := int(val_id) >= 0

14111454 if header == 4 {

1412- tc.insert_loop_var(key_id, Type(int_))

1455+ tc.insert_loop_var(key_id, tc.range_loop_var_type(container_id))

14131456 tc.annotate_node(tc.a.child(&node, 3))

14141457 } else {

14151458 clean := unwrap_pointer(tc.resolve_type(container_id))

@@ -1441,10 +1484,17 @@ fn (mut tc TypeChecker) annotate_for_in(_id flat.NodeId, node flat.Node) {

14411484 } else {

14421485 tc.insert_loop_var(key_id, Type(u8_))

14431486 }

1487+ } else if elem_type := iterator_for_in_elem_type(clean) {

1488+ if has_val {

1489+ tc.insert_loop_var(key_id, Type(int_))

1490+ tc.insert_loop_var(val_id, elem_type)

1491+ } else {

1492+ tc.insert_loop_var(key_id, elem_type)

1493+ }

14441494 } else {

14451495 container := tc.a.nodes[int(container_id)]

14461496 if container.kind == .range {

1447- tc.insert_loop_var(key_id, Type(int_))

1497+ tc.insert_loop_var(key_id, tc.range_loop_var_type(tc.a.child(&container, 0)))

14481498 }

14491499 }

14501500 }

@@ -1453,6 +1503,22 @@ fn (mut tc TypeChecker) annotate_for_in(_id flat.NodeId, node flat.Node) {

14531503 }

14541504}

14551505

1506+fn iterator_for_in_elem_type(typ Type) ?Type {

1507+ name := typ.name()

1508+ if name == 'RunesIterator' || name == 'builtin.RunesIterator' {

1509+ return Type(rune_)

1510+ }

1511+ return none

1512+}

1513+

1514+fn (tc &TypeChecker) range_loop_var_type(low_id flat.NodeId) Type {

1515+ low_type := tc.resolve_type(low_id)

1516+ if fn_param_unalias_type(low_type).is_integer() {

1517+ return low_type

1518+ }

1519+ return Type(int_)

1520+}

1521+

14561522// insert_loop_var updates insert loop var state for types.

14571523fn (mut tc TypeChecker) insert_loop_var(id flat.NodeId, typ Type) {

14581524 if int(id) < 0 {

@@ -1673,7 +1739,8 @@ pub fn (mut tc TypeChecker) check_semantics() {

16731739 tc.check_fn_body(node)

16741740 tc.cur_fn_node_id = -1

16751741 is_disabled_stub := node.value in tc.a.disabled_fns

1676- if !type_allows_implicit_return(tc.cur_fn_ret_type)

1742+ if tc.cur_fn_ret_type !is Unknown

1743+ && !type_allows_implicit_return(tc.cur_fn_ret_type)

16771744 && !tc.fn_body_definitely_returns(node) && !is_disabled_stub

16781745 && tc.should_diagnose(flat.NodeId(i)) {

16791746 tc.record_error(.return_mismatch,

@@ -2916,6 +2983,10 @@ fn (mut tc TypeChecker) check_node(id flat.NodeId) {

29162983 tc.check_block(node)

29172984 return

29182985 }

2986+ if node.kind == .comptime_if {

2987+ tc.check_comptime_if(id, node)

2988+ return

2989+ }

29192990 if kind_id == 46 {

29202991 tc.check_for_stmt(node)

29212992 return

@@ -2997,15 +3068,18 @@ fn (mut tc TypeChecker) check_node(id flat.NodeId) {

29973068 if node.kind == .array_literal {

29983069 for i in 0 .. node.children_count {

29993070 tc.reject_stored_method_value(tc.a.child(&node, i))

3071+ tc.reject_stored_capturing_fn_literal(tc.a.child(&node, i))

30003072 }

30013073 } else if node.kind == .map_init {

30023074 // children alternate key, value, key, value, ...; check the value positions.

30033075 for j := 1; j < node.children_count; j += 2 {

30043076 tc.reject_stored_method_value(tc.a.child(&node, j))

3077+ tc.reject_stored_capturing_fn_literal(tc.a.child(&node, j))

30053078 }

30063079 } else if node.kind == .infix && node.op == .left_shift && node.children_count >= 2 {

30073080 if unwrap_pointer(tc.resolve_type(tc.a.child(&node, 0))) is Array {

30083081 tc.reject_stored_method_value(tc.a.child(&node, 1))

3082+ tc.reject_stored_capturing_fn_literal(tc.a.child(&node, 1))

30093083 }

30103084 }

30113085

@@ -3014,6 +3088,186 @@ fn (mut tc TypeChecker) check_node(id flat.NodeId) {

30143088 }

30153089}

30163090

3091+fn (mut tc TypeChecker) check_comptime_if(_id flat.NodeId, node flat.Node) {

3092+ take_then := tc.comptime_type_condition_value(node.value) or { return }

3093+ branch_index := if take_then { 0 } else { 1 }

3094+ if branch_index >= node.children_count {

3095+ return

3096+ }

3097+ tc.check_node(tc.a.child(&node, branch_index))

3098+}

3099+

3100+fn (mut tc TypeChecker) comptime_type_condition_value(cond string) ?bool {

3101+ clean := comptime_condition_strip_outer_parens(cond)

3102+ if clean == 'true' {

3103+ return true

3104+ }

3105+ if clean == 'false' {

3106+ return false

3107+ }

3108+ or_idx := comptime_condition_top_level_index(clean, '||')

3109+ if or_idx >= 0 {

3110+ left := tc.comptime_type_condition_value(clean[..or_idx]) or { return none }

3111+ if left {

3112+ return true

3113+ }

3114+ return tc.comptime_type_condition_value(clean[or_idx + 2..])

3115+ }

3116+ and_idx := comptime_condition_top_level_index(clean, '&&')

3117+ if and_idx >= 0 {

3118+ left := tc.comptime_type_condition_value(clean[..and_idx]) or { return none }

3119+ if !left {

3120+ return false

3121+ }

3122+ return tc.comptime_type_condition_value(clean[and_idx + 2..])

3123+ }

3124+ for op in [' !is ', ' is '] {

3125+ op_idx := comptime_condition_top_level_index(clean, op)

3126+ if op_idx >= 0 {

3127+ left := clean[..op_idx].trim_space()

3128+ right := clean[op_idx + op.len..].trim_space()

3129+ matches := tc.comptime_type_matches(left, right) or { return none }

3130+ return if op == ' is ' { matches } else { !matches }

3131+ }

3132+ }

3133+ if clean.starts_with('!') {

3134+ value := tc.comptime_type_condition_value(clean[1..]) or { return none }

3135+ return !value

3136+ }

3137+ return none

3138+}

3139+

3140+fn (mut tc TypeChecker) comptime_type_matches(actual string, expected string) ?bool {

3141+ clean_actual := actual.trim_space()

3142+ clean_expected := expected.trim_space()

3143+ if clean_actual.len == 0 || clean_expected.len == 0

3144+ || (is_bare_generic_param(clean_actual) && !tc.type_name_known(clean_actual)) {

3145+ return none

3146+ }

3147+ actual_type := tc.comptime_type_match_type(clean_actual)

3148+ normalized := actual_type.name()

3149+ match clean_expected {

3150+ '$array' {

3151+ return actual_type is Array || actual_type is ArrayFixed

3152+ }

3153+ '$map' {

3154+ return actual_type is Map

3155+ }

3156+ '$function' {

3157+ return actual_type is FnType

3158+ }

3159+ '$option' {

3160+ return actual_type is OptionType

3161+ }

3162+ '$int' {

3163+ return actual_type.is_integer()

3164+ }

3165+ '$float' {

3166+ return actual_type.is_float()

3167+ }

3168+ '$struct' {

3169+ return normalized in tc.structs

3170+ }

3171+ '$enum' {

3172+ return normalized in tc.enum_names

3173+ }

3174+ '$alias' {

3175+ return clean_actual in tc.type_aliases

3176+ || tc.qualify_name(clean_actual) in tc.type_aliases

3177+ }

3178+ '$sumtype' {

3179+ return normalized in tc.sum_types

3180+ }

3181+ '$interface' {

3182+ return normalized in tc.interface_names

3183+ }

3184+ else {}

3185+ }

3186+

3187+ expected_type := tc.comptime_type_match_type(clean_expected)

3188+ return normalized == expected_type.name()

3189+}

3190+

3191+fn (mut tc TypeChecker) comptime_type_match_type(type_text string) Type {

3192+ typ := tc.parse_type(type_text)

3193+ if typ is Alias {

3194+ return typ.base_type

3195+ }

3196+ return typ

3197+}

3198+

3199+fn comptime_condition_matching_paren(s string, start int) int {

3200+ mut paren_depth := 0

3201+ mut bracket_depth := 0

3202+ for i in start .. s.len {

3203+ match s[i] {

3204+ `(` {

3205+ paren_depth++

3206+ }

3207+ `)` {

3208+ paren_depth--

3209+ if paren_depth == 0 && bracket_depth == 0 {

3210+ return i

3211+ }

3212+ }

3213+ `[` {

3214+ bracket_depth++

3215+ }

3216+ `]` {

3217+ bracket_depth--

3218+ }

3219+ else {}

3220+ }

3221+ }

3222+ return s.len

3223+}

3224+

3225+fn comptime_condition_strip_outer_parens(cond string) string {

3226+ mut clean := cond.trim_space()

3227+ for clean.len >= 2 && clean.starts_with('(') {

3228+ end := comptime_condition_matching_paren(clean, 0)

3229+ if end != clean.len - 1 {

3230+ break

3231+ }

3232+ clean = clean[1..clean.len - 1].trim_space()

3233+ }

3234+ return clean

3235+}

3236+

3237+fn comptime_condition_top_level_index(s string, needle string) int {

3238+ if needle.len == 0 || s.len < needle.len {

3239+ return -1

3240+ }

3241+ mut paren_depth := 0

3242+ mut bracket_depth := 0

3243+ for i := 0; i <= s.len - needle.len; i++ {

3244+ match s[i] {

3245+ `(` {

3246+ paren_depth++

3247+ }

3248+ `)` {

3249+ if paren_depth > 0 {

3250+ paren_depth--

3251+ }

3252+ }

3253+ `[` {

3254+ bracket_depth++

3255+ }

3256+ `]` {

3257+ if bracket_depth > 0 {

3258+ bracket_depth--

3259+ }

3260+ }

3261+ else {}

3262+ }

3263+

3264+ if paren_depth == 0 && bracket_depth == 0 && s[i..].starts_with(needle) {

3265+ return i

3266+ }

3267+ }

3268+ return -1

3269+}

3270+

30173271// check_select_stmt validates a `select { ... }` statement. A receive branch

30183272// `val := <-ch` binds `val` (the channel's element type) in the branch body's

30193273// scope; other branches (sends, bare conditions, `else`) are checked as-is.

@@ -3059,6 +3313,10 @@ fn (mut tc TypeChecker) check_array_init(node flat.Node) {

30593313 child_id := tc.a.child(&node, i)

30603314 child := tc.a.nodes[int(child_id)]

30613315 if child.kind == .field_init && child.value == 'init' {

3316+ if child.children_count > 0 {

3317+ tc.reject_stored_method_value(tc.a.child(&child, 0))

3318+ tc.reject_stored_capturing_fn_literal(tc.a.child(&child, 0))

3319+ }

30623320 tc.push_scope()

30633321 tc.cur_scope.insert('index', Type(int_))

30643322 tc.check_node(child_id)

@@ -3173,7 +3431,7 @@ fn (mut tc TypeChecker) check_for_in_stmt(node flat.Node) {

31733431 tc.check_node(container_id)

31743432 has_val := int(val_id) >= 0

31753433 if header == 4 {

3176- tc.insert_loop_var(key_id, Type(int_))

3434+ tc.insert_loop_var(key_id, tc.range_loop_var_type(container_id))

31773435 tc.check_node(tc.a.child(&node, 3))

31783436 } else {

31793437 clean := unwrap_pointer(tc.resolve_type(container_id))

@@ -3205,10 +3463,17 @@ fn (mut tc TypeChecker) check_for_in_stmt(node flat.Node) {

32053463 } else {

32063464 tc.insert_loop_var(key_id, Type(u8_))

32073465 }

3466+ } else if elem_type := iterator_for_in_elem_type(clean) {

3467+ if has_val {

3468+ tc.insert_loop_var(key_id, Type(int_))

3469+ tc.insert_loop_var(val_id, elem_type)

3470+ } else {

3471+ tc.insert_loop_var(key_id, elem_type)

3472+ }

32083473 } else {

32093474 container := tc.a.nodes[int(container_id)]

32103475 if container.kind == .range {

3211- tc.insert_loop_var(key_id, Type(int_))

3476+ tc.insert_loop_var(key_id, tc.range_loop_var_type(tc.a.child(&container, 0)))

32123477 } else if tc.should_diagnose(container_id) {

32133478 tc.record_error(.cannot_index, 'cannot iterate over `${clean.name()}`',

32143479 container_id)

@@ -3246,6 +3511,7 @@ fn (mut tc TypeChecker) check_decl_assign(id flat.NodeId, node flat.Node) {

32463511 }

32473512 tc.insert_decl_lhs(lhs_id, expected)

32483513 tc.track_method_value_local(lhs_id, rhs_id)

3514+ tc.reject_stored_capturing_fn_literal(rhs_id)

32493515 i += 2

32503516 }

32513517}

@@ -3491,6 +3757,7 @@ fn (mut tc TypeChecker) check_assign(id flat.NodeId, node flat.Node) {

34913757 } else {

34923758 tc.track_method_value_local(lhs_id, rhs_id)

34933759 }

3760+ tc.reject_stored_capturing_fn_literal(rhs_id)

34943761 }

34953762 i += 2

34963763 }

@@ -3599,6 +3866,7 @@ fn (mut tc TypeChecker) check_return(id flat.NodeId, node flat.Node) {

35993866 // return c.report }`); reject it rather than emitting invalid C.

36003867 for i in 0 .. node.children_count {

36013868 tc.reject_stored_method_value(tc.a.child(&node, i))

3869+ tc.reject_stored_capturing_fn_literal(tc.a.child(&node, i))

36023870 }

36033871 expected := tc.cur_fn_ret_type

36043872 if expected is Void {

@@ -3658,12 +3926,32 @@ fn (mut tc TypeChecker) check_return(id flat.NodeId, node flat.Node) {

36583926 child_id := tc.a.child(&node, 0)

36593927 tc.check_node(child_id)

36603928 actual := tc.resolve_expr(child_id, expected)

3661- if !tc.type_compatible(actual, expected) {

3929+ if !tc.return_type_compatible(actual, expected) {

36623930 tc.type_mismatch(.return_mismatch,

36633931 'cannot return `${actual.name()}` as `${expected.name()}`', id)

36643932 }

36653933}

36663934

3935+fn (tc &TypeChecker) return_type_compatible(actual Type, expected Type) bool {

3936+ return tc.type_compatible(actual, expected)

3937+}

3938+

3939+fn contextual_payload_type(typ Type) ?Type {

3940+ if typ is OptionType {

3941+ if typ.base_type is Void {

3942+ return none

3943+ }

3944+ return typ.base_type

3945+ }

3946+ if typ is ResultType {

3947+ if typ.base_type is Void {

3948+ return none

3949+ }

3950+ return typ.base_type

3951+ }

3952+ return none

3953+}

3954+

36673955fn multi_return_payload_type(typ Type) ?MultiReturn {

36683956 if typ is MultiReturn {

36693957 return typ

@@ -3695,6 +3983,12 @@ fn (mut tc TypeChecker) check_call(id flat.NodeId, node flat.Node) {

36953983 tc.check_call_arg_types(id, node, info)

36963984 return

36973985 }

3986+ if tc.is_unsupported_hex_call(node) {

3987+ if tc.should_diagnose(id) {

3988+ tc.record_error(.unknown_fn, 'unknown function `${tc.call_display_name(node)}`', id)

3989+ }

3990+ return

3991+ }

36983992 if tc.call_has_ambiguous_selective_import(node) {

36993993 tc.record_error(.unknown_fn, 'ambiguous selective import `${tc.call_display_name(node)}`',

37003994 id)

@@ -3746,7 +4040,7 @@ fn (mut tc TypeChecker) resolve_call_info(id flat.NodeId, node flat.Node) ?CallI

37464040 return none

37474041 }

37484042 fn_node := tc.a.child_node(&node, 0)

3749- if info := tc.resolve_generic_call_info(fn_node) {

4043+ if info := tc.resolve_generic_call_info(id, fn_node) {

37504044 return info

37514045 }

37524046 if fn_node.kind == .selector {

@@ -3762,6 +4056,12 @@ fn (mut tc TypeChecker) resolve_call_info(id flat.NodeId, node flat.Node) ?CallI

37624056 return tc.call_info(mod_name, false)

37634057 }

37644058 }

4059+ if base_node.value == tc.cur_module {

4060+ mod_name := '${tc.cur_module}.${fn_node.value}'

4061+ if mod_name in tc.fn_ret_types {

4062+ return tc.call_info(mod_name, false)

4063+ }

4064+ }

37654065 qbase := tc.qualify_name(base_node.value)

37664066 static_name := '${qbase}.${fn_node.value}'

37674067 if static_name in tc.fn_ret_types && (qbase in tc.structs

@@ -3805,6 +4105,9 @@ fn (mut tc TypeChecker) resolve_call_info(id flat.NodeId, node flat.Node) ?CallI

38054105 }

38064106 base_type := tc.resolve_type(base_id)

38074107 clean := unwrap_pointer(base_type)

4108+ if info := tc.pointer_builtin_method_call_info(base_type, fn_node.value) {

4109+ return info

4110+ }

38084111 if clean is Channel && fn_node.value == 'close' {

38094112 return CallInfo{

38104113 name: 'chan.close'

@@ -3814,38 +4117,77 @@ fn (mut tc TypeChecker) resolve_call_info(id flat.NodeId, node flat.Node) ?CallI

38144117 params_known: true

38154118 }

38164119 }

3817- if clean is Array {

3818- for mname in exact_array_receiver_method_candidates(clean, fn_node.value, tc.cur_module) {

3819- if mname in tc.fn_ret_types {

3820- return tc.call_info(mname, true)

3821- }

3822- }

3823- }

3824- if clean is Array && fn_node.value == 'clone' {

4120+ if clean is String && fn_node.value == 'hex' && tc.is_builtin_hex_receiver(base_type) {

38254121 return CallInfo{

3826- name: 'array_clone'

4122+ name: 'string.hex'

38274123 params: tarr1(base_type)

3828- return_type: base_type

4124+ return_type: Type(string_)

38294125 has_receiver: true

38304126 params_known: true

38314127 }

38324128 }

3833- if clean is Map && fn_node.value == 'clone' {

3834- return CallInfo{

3835- name: ''

3836- params: tarr1(base_type)

3837- return_type: base_type

3838- has_receiver: true

3839- params_known: true

4129+ if clean is Alias {

4130+ if _ := array_type_from_receiver(clean) {

4131+ for mname in receiver_method_name_candidates(clean, fn_node.value, tc.cur_module) {

4132+ if checker_is_raw_collection_method_name(mname, 'array.')

4133+ || mname !in tc.fn_ret_types {

4134+ continue

4135+ }

4136+ if !tc.method_can_be_called_on_receiver(base_type, fn_node.value, mname) {

4137+ continue

4138+ }

4139+ return tc.call_info(mname, true)

4140+ }

4141+ }

4142+ }

4143+ if clean_array := array_type_from_receiver(clean) {

4144+ for mname in exact_array_receiver_method_candidates(clean_array, fn_node.value,

4145+ tc.cur_module) {

4146+ if mname in tc.fn_ret_types {

4147+ return tc.call_info(mname, true)

4148+ }

4149+ }

4150+ if fn_node.value in ['clone', 'reverse'] {

4151+ return CallInfo{

4152+ name: 'array.${fn_node.value}'

4153+ params: tarr1(base_type)

4154+ return_type: base_type

4155+ has_receiver: true

4156+ params_known: true

4157+ }

4158+ }

4159+ }

4160+ if clean_map := map_type_from_receiver(clean) {

4161+ _ = clean_map

4162+ for mname in receiver_method_name_candidates(clean, fn_node.value, tc.cur_module) {

4163+ if checker_is_raw_collection_method_name(mname, 'map.') || mname !in tc.fn_ret_types {

4164+ continue

4165+ }

4166+ if !tc.method_can_be_called_on_receiver(base_type, fn_node.value, mname) {

4167+ continue

4168+ }

4169+ return tc.call_info(mname, true)

4170+ }

4171+ match fn_node.value {

4172+ 'clone' {

4173+ return CallInfo{

4174+ name: ''

4175+ params: tarr1(base_type)

4176+ return_type: base_type

4177+ has_receiver: true

4178+ params_known: true

4179+ }

4180+ }

4181+ else {}

38404182 }

38414183 }

3842- if clean is Map {

4184+ if clean_map := map_type_from_receiver(clean) {

38434185 if fn_node.value == 'keys' {

38444186 return CallInfo{

38454187 name: 'map.keys'

38464188 params: tarr1(base_type)

38474189 return_type: Type(Array{

3848- elem_type: clean.key_type

4190+ elem_type: clean_map.key_type

38494191 })

38504192 has_receiver: true

38514193 params_known: true

@@ -3856,7 +4198,7 @@ fn (mut tc TypeChecker) resolve_call_info(id flat.NodeId, node flat.Node) ?CallI

38564198 name: 'map.values'

38574199 params: tarr1(base_type)

38584200 return_type: Type(Array{

3859- elem_type: clean.value_type

4201+ elem_type: clean_map.value_type

38604202 })

38614203 has_receiver: true

38624204 params_known: true

@@ -3864,22 +4206,25 @@ fn (mut tc TypeChecker) resolve_call_info(id flat.NodeId, node flat.Node) ?CallI

38644206 }

38654207 map_method := 'map.${fn_node.value}'

38664208 if map_method in tc.fn_ret_types {

4209+ if info := tc.map_builtin_call_info(base_type, clean_map, fn_node.value, map_method) {

4210+ return info

4211+ }

38674212 return tc.call_info(map_method, true)

38684213 }

38694214 }

3870- if clean is Array {

4215+ if clean_array := array_type_from_receiver(clean) {

38714216 match fn_node.value {

3872- 'first', 'last', 'pop' {

4217+ 'first', 'last', 'pop', 'pop_left' {

38734218 return CallInfo{

38744219 name: ''

38754220 params: tarr1(base_type)

3876- return_type: clean.elem_type

4221+ return_type: clean_array.elem_type

38774222 has_receiver: true

38784223 params_known: true

38794224 }

38804225 }

38814226 'contains' {

3882- elem_type := tc.array_contains_elem_type(base_node, clean)

4227+ elem_type := tc.array_contains_elem_type(base_node, clean_array)

38834228 return CallInfo{

38844229 name: ''

38854230 params: tarr2(base_type, elem_type)

@@ -3900,12 +4245,23 @@ fn (mut tc TypeChecker) resolve_call_info(id flat.NodeId, node flat.Node) ?CallI

39004245 'index', 'last_index' {

39014246 return CallInfo{

39024247 name: ''

3903- params: tarr2(base_type, clean.elem_type)

4248+ params: tarr2(base_type, clean_array.elem_type)

39044249 return_type: Type(int_)

39054250 has_receiver: true

39064251 params_known: true

39074252 }

39084253 }

4254+ 'hex' {

4255+ if tc.is_builtin_hex_receiver(base_type) {

4256+ return CallInfo{

4257+ name: '[]u8.hex'

4258+ params: tarr1(base_type)

4259+ return_type: Type(string_)

4260+ has_receiver: true

4261+ params_known: true

4262+ }

4263+ }

4264+ }

39094265 'repeat' {

39104266 return CallInfo{

39114267 name: 'array.repeat_to_depth'

@@ -3946,6 +4302,20 @@ fn (mut tc TypeChecker) resolve_call_info(id flat.NodeId, node flat.Node) ?CallI

39464302 params_known: true

39474303 }

39484304 }

4305+ 'insert', 'prepend' {

4306+ params := if fn_node.value == 'insert' {

4307+ tarr3(base_type, Type(int_), clean_array.elem_type)

4308+ } else {

4309+ tarr2(base_type, clean_array.elem_type)

4310+ }

4311+ return CallInfo{

4312+ name: 'array.${fn_node.value}'

4313+ params: params

4314+ return_type: Type(void_)

4315+ has_receiver: true

4316+ params_known: true

4317+ }

4318+ }

39494319 'filter' {

39504320 return CallInfo{

39514321 name: 'array.filter'

@@ -3985,6 +4355,50 @@ fn (mut tc TypeChecker) resolve_call_info(id flat.NodeId, node flat.Node) ?CallI

39854355 params_known: true

39864356 }

39874357 }

4358+ 'sort_with_compare' {

4359+ return CallInfo{

4360+ name: 'array.sort_with_compare'

4361+ params: [

4362+ Type(Pointer{

4363+ base_type: base_type

4364+ }),

4365+ Type(FnType{

4366+ params: [

4367+ Type(Pointer{

4368+ base_type: clean_array.elem_type

4369+ }),

4370+ Type(Pointer{

4371+ base_type: clean_array.elem_type

4372+ }),

4373+ ]

4374+ return_type: Type(int_)

4375+ }),

4376+ ]

4377+ return_type: Type(void_)

4378+ has_receiver: true

4379+ params_known: true

4380+ }

4381+ }

4382+ 'sorted_with_compare' {

4383+ return CallInfo{

4384+ name: 'array.sorted_with_compare'

4385+ params: [base_type,

4386+ Type(FnType{

4387+ params: [

4388+ Type(Pointer{

4389+ base_type: clean_array.elem_type

4390+ }),

4391+ Type(Pointer{

4392+ base_type: clean_array.elem_type

4393+ }),

4394+ ]

4395+ return_type: Type(int_)

4396+ })]

4397+ return_type: base_type

4398+ has_receiver: true

4399+ params_known: true

4400+ }

4401+ }

39884402 'sort' {

39894403 mut params := tarr1(Type(Pointer{

39904404 base_type: base_type

@@ -4018,7 +4432,7 @@ fn (mut tc TypeChecker) resolve_call_info(id flat.NodeId, node flat.Node) ?CallI

40184432 }

40194433 type_name := resolve_type_name_for_method(clean)

40204434 if type_name.len > 0 {

4021- if fn_node.value == 'str' && clean is Primitive {

4435+ if fn_node.value == 'str' && (clean is Primitive || clean is Char || clean is Rune) {

40224436 return CallInfo{

40234437 name: ''

40244438 params: tarr1(base_type)

@@ -4029,6 +4443,9 @@ fn (mut tc TypeChecker) resolve_call_info(id flat.NodeId, node flat.Node) ?CallI

40294443 }

40304444 for mname in receiver_method_name_candidates(clean, fn_node.value, tc.cur_module) {

40314445 if mname in tc.fn_ret_types {

4446+ if !tc.method_can_be_called_on_receiver(base_type, fn_node.value, mname) {

4447+ continue

4448+ }

40324449 if clean_map := map_type_from_receiver(clean) {

40334450 if info := tc.map_builtin_call_info(base_type, clean_map, fn_node.value,

40344451 mname)

@@ -4102,6 +4519,24 @@ fn (mut tc TypeChecker) resolve_call_info(id flat.NodeId, node flat.Node) ?CallI

41024519 return tc.call_info(mname, true)

41034520 }

41044521 }

4522+ if fn_node.value == 'str' {

4523+ return CallInfo{

4524+ name: ''

4525+ params: tarr1(base_type)

4526+ return_type: Type(string_)

4527+ has_receiver: true

4528+ params_known: true

4529+ }

4530+ }

4531+ if fn_node.value == 'hex' && tc.is_builtin_hex_receiver(base_type) {

4532+ return CallInfo{

4533+ name: ''

4534+ params: tarr1(base_type)

4535+ return_type: Type(string_)

4536+ has_receiver: true

4537+ params_known: true

4538+ }

4539+ }

41054540 return none

41064541 }

41074542 if fn_node.kind == .ident {

@@ -4119,6 +4554,16 @@ fn (mut tc TypeChecker) resolve_call_info(id flat.NodeId, node flat.Node) ?CallI

41194554 params_known: true

41204555 }

41214556 }

4557+ if fn_node.value == 'malloc' {

4558+ return CallInfo{

4559+ name: 'malloc'

4560+ params: tarr1(Type(ISize{}))

4561+ return_type: Type(Pointer{

4562+ base_type: Type(u8_)

4563+ })

4564+ params_known: true

4565+ }

4566+ }

41224567 if typ := tc.cur_scope.lookup(fn_node.value) {

41234568 if fn_typ := fn_type_from_type(typ) {

41244569 return CallInfo{

@@ -4151,17 +4596,99 @@ fn (mut tc TypeChecker) resolve_call_info(id flat.NodeId, node flat.Node) ?CallI

41514596 return none

41524597}

41534598

4154-fn (mut tc TypeChecker) resolve_generic_call_info(fn_node flat.Node) ?CallInfo {

4599+fn (tc &TypeChecker) is_builtin_hex_receiver(typ Type) bool {

4600+ if tc.type_is_pointer_receiver(typ) {

4601+ return false

4602+ }

4603+ clean := typ

4604+ if clean is Alias {

4605+ return tc.is_builtin_hex_receiver(clean.base_type)

4606+ }

4607+ if clean is Array {

4608+ return is_byte_type(clean.elem_type)

4609+ }

4610+ if clean is String {

4611+ return true

4612+ }

4613+ if clean is Primitive {

4614+ return prim_c_type_from(clean.props, clean.size) in ['u8', 'i8', 'u16', 'i16', 'u32', 'int',

4615+ 'u64', 'i64']

4616+ }

4617+ return clean is Rune || clean is Char

4618+}

4619+

4620+fn (tc &TypeChecker) type_is_pointer_receiver(typ Type) bool {

4621+ if typ is Pointer {

4622+ return true

4623+ }

4624+ if typ is Alias {

4625+ return tc.type_is_pointer_receiver(typ.base_type)

4626+ }

4627+ return false

4628+}

4629+

4630+fn (tc &TypeChecker) method_can_be_called_on_receiver(receiver Type, method string, method_name string) bool {

4631+ if method != 'hex' || !tc.type_is_pointer_receiver(receiver) {

4632+ return true

4633+ }

4634+ params := tc.fn_param_types[method_name] or { return false }

4635+ if params.len == 0 {

4636+ return false

4637+ }

4638+ return tc.type_is_pointer_receiver(params[0])

4639+}

4640+

4641+fn (tc &TypeChecker) receiver_expr_is_pointer(id flat.NodeId) bool {

4642+ if int(id) < 0 {

4643+ return false

4644+ }

4645+ node := tc.a.nodes[int(id)]

4646+ if node.kind == .ident {

4647+ if typ := tc.cur_scope.lookup(node.value) {

4648+ return tc.type_is_pointer_receiver(typ)

4649+ }

4650+ }

4651+ if node.typ.starts_with('&') {

4652+ return true

4653+ }

4654+ return tc.type_is_pointer_receiver(tc.resolve_type(id))

4655+}

4656+

4657+fn is_byte_type(typ Type) bool {

4658+ if typ is Alias {

4659+ return is_byte_type(typ.base_type)

4660+ }

4661+ return typ is Primitive && typ.props.has(.integer) && typ.props.has(.unsigned) && typ.size == 8

4662+}

4663+

4664+fn (mut tc TypeChecker) resolve_generic_call_info(id flat.NodeId, fn_node flat.Node) ?CallInfo {

41554665 if fn_node.kind != .index || fn_node.children_count < 2 || fn_node.value == 'range' {

41564666 return none

41574667 }

41584668 base_id := tc.a.child(&fn_node, 0)

4159- type_arg_id := tc.a.child(&fn_node, 1)

4160- type_arg := tc.generic_call_type_arg_name(type_arg_id)

4161- if type_arg.len == 0 {

4669+ type_args := tc.generic_call_type_arg_names(fn_node)

4670+ if type_args.len == 0 {

41624671 return none

41634672 }

41644673 base_node := tc.a.nodes[int(base_id)]

4674+ if base_node.kind == .selector && base_node.children_count > 0 {

4675+ recv_id := tc.a.child(&base_node, 0)

4676+ base_type := tc.resolve_type(recv_id)

4677+ clean := unwrap_pointer(base_type)

4678+ type_name := resolve_type_name_for_method(clean)

4679+ if type_name.len > 0 {

4680+ call_name := '${type_name}.${base_node.value}'

4681+ if call_name in tc.fn_ret_types {

4682+ if tc.explicit_generic_arg_count_mismatch(call_name, type_args, id) {

4683+ return tc.failed_explicit_generic_call_info(call_name)

4684+ }

4685+ if info := tc.explicit_generic_call_info(call_name, true, type_args) {

4686+ return info

4687+ }

4688+ return tc.call_info(call_name, true)

4689+ }

4690+ }

4691+ }

41654692 call_name := tc.generic_call_base_name(base_node) or { return none }

41664693 if is_veb_run_at_call_name(call_name) {

41674694 return CallInfo{

@@ -4176,20 +4703,90 @@ fn (mut tc TypeChecker) resolve_generic_call_info(fn_node flat.Node) ?CallInfo {

41764703 if call_name !in tc.fn_ret_types {

41774704 return none

41784705 }

4179- if is_decode_call_name(call_name) {

4180- return CallInfo{

4181- name: call_name

4182- params: tc.fn_param_types[call_name] or { []Type{} }

4183- return_type: Type(ResultType{

4184- base_type: tc.parse_type(type_arg)

4185- })

4186- has_receiver: false

4187- is_variadic: tc.fn_variadic[call_name] or { false }

4188- is_c_variadic: tc.c_variadic_fns[call_name] or { false }

4189- params_known: call_name in tc.fn_param_types

4190- }

4706+ if is_decode_call_name(call_name) {

4707+ if type_args.len != 1 {

4708+ if tc.should_diagnose(id) {

4709+ tc.record_error(.call_arg_mismatch,

4710+ 'generic argument count mismatch for `${call_name}`: expected 1, got ${type_args.len}',

4711+ id)

4712+ }

4713+ return tc.failed_explicit_generic_call_info(call_name)

4714+ }

4715+ return CallInfo{

4716+ name: call_name

4717+ params: tc.fn_param_types[call_name] or { []Type{} }

4718+ return_type: Type(ResultType{

4719+ base_type: tc.parse_type(type_args[0])

4720+ })

4721+ has_receiver: false

4722+ is_variadic: tc.fn_variadic[call_name] or { false }

4723+ is_c_variadic: tc.c_variadic_fns[call_name] or { false }

4724+ params_known: call_name in tc.fn_param_types

4725+ }

4726+ }

4727+ if tc.explicit_generic_arg_count_mismatch(call_name, type_args, id) {

4728+ return tc.failed_explicit_generic_call_info(call_name)

4729+ }

4730+ if info := tc.explicit_generic_call_info(call_name, false, type_args) {

4731+ return info

4732+ }

4733+ return tc.call_info(call_name, false)

4734+}

4735+

4736+fn (mut tc TypeChecker) explicit_generic_arg_count_mismatch(name string, type_args []string, id flat.NodeId) bool {

4737+ generic_params := tc.fn_generic_params[name] or { return false }

4738+ if type_args.len == generic_params.len {

4739+ return false

4740+ }

4741+ if tc.should_diagnose(id) {

4742+ tc.record_error(.call_arg_mismatch,

4743+ 'generic argument count mismatch for `${name}`: expected ${generic_params.len}, got ${type_args.len}',

4744+ id)

4745+ }

4746+ return true

4747+}

4748+

4749+fn (tc &TypeChecker) failed_explicit_generic_call_info(name string) CallInfo {

4750+ return CallInfo{

4751+ name: name

4752+ params: []Type{}

4753+ return_type: unknown_type('invalid explicit generic call `${name}`')

4754+ params_known: false

4755+ }

4756+}

4757+

4758+fn (mut tc TypeChecker) explicit_generic_call_info(name string, has_receiver bool, type_args []string) ?CallInfo {

4759+ generic_params := tc.fn_generic_params[name] or { return none }

4760+ param_texts := tc.fn_param_type_texts[name] or { return none }

4761+ if generic_params.len == 0 || type_args.len != generic_params.len {

4762+ return none

4763+ }

4764+ mut concrete_args := []string{cap: generic_params.len}

4765+ for i in 0 .. generic_params.len {

4766+ concrete_args << tc.qualify_type_text(type_args[i])

4767+ }

4768+ mut sub_params := []Type{}

4769+ for param_text in param_texts {

4770+ sub_params << tc.parse_fn_signature_type(name, subst_generic_text(param_text,

4771+ concrete_args, generic_params))

4772+ }

4773+ ret_text := tc.fn_ret_type_texts[name] or { '' }

4774+ sub_ret := if ret_text.len > 0 {

4775+ tc.parse_fn_signature_type(name,

4776+ subst_generic_text(ret_text, concrete_args, generic_params))

4777+ } else {

4778+ tc.fn_ret_types[name] or { Type(void_) }

4779+ }

4780+ return CallInfo{

4781+ name: name

4782+ params: sub_params

4783+ return_type: sub_ret

4784+ has_receiver: has_receiver

4785+ is_variadic: tc.fn_variadic[name] or { false }

4786+ is_c_variadic: tc.c_variadic_fns[name] or { false }

4787+ params_known: true

4788+ has_implicit_veb_ctx: tc.fn_implicit_veb_ctx[name] or { false }

41914789 }

4192- return tc.call_info(call_name, false)

41934790}

41944791

41954792fn is_decode_call_name(name string) bool {

@@ -4230,6 +4827,21 @@ fn (tc &TypeChecker) generic_call_base_name(base_node flat.Node) ?string {

42304827 return none

42314828}

42324829

4830+fn (tc &TypeChecker) generic_call_type_arg_names(index_node flat.Node) []string {

4831+ if index_node.kind != .index || index_node.children_count < 2 || index_node.value == 'range' {

4832+ return []string{}

4833+ }

4834+ mut args := []string{}

4835+ for i in 1 .. index_node.children_count {

4836+ arg := tc.generic_call_type_arg_name(tc.a.child(&index_node, i))

4837+ if arg.len == 0 {

4838+ return []string{}

4839+ }

4840+ args << arg

4841+ }

4842+ return args

4843+}

4844+

42334845fn (tc &TypeChecker) generic_call_type_arg_name(id flat.NodeId) string {

42344846 if int(id) < 0 {

42354847 return ''

@@ -4254,11 +4866,18 @@ fn (tc &TypeChecker) generic_call_type_arg_name(id flat.NodeId) string {

42544866 return ''

42554867 }

42564868 base := tc.generic_call_type_arg_name(tc.a.child(&node, 0))

4257- arg := tc.generic_call_type_arg_name(tc.a.child(&node, 1))

4258- if base.len == 0 || arg.len == 0 {

4869+ if base.len == 0 {

42594870 return ''

42604871 }

4261- return '${base}[${arg}]'

4872+ mut args := []string{}

4873+ for i in 1 .. node.children_count {

4874+ arg := tc.generic_call_type_arg_name(tc.a.child(&node, i))

4875+ if arg.len == 0 {

4876+ return ''

4877+ }

4878+ args << arg

4879+ }

4880+ return '${base}[${args.join(', ')}]'

42624881 }

42634882 .array_init {

42644883 if node.value.len > 0 {

@@ -4266,6 +4885,9 @@ fn (tc &TypeChecker) generic_call_type_arg_name(id flat.NodeId) string {

42664885 }

42674886 return ''

42684887 }

4888+ .map_init {

4889+ return node.value

4890+ }

42694891 .prefix {

42704892 if node.children_count == 0 {

42714893 return ''

@@ -4311,6 +4933,16 @@ fn (tc &TypeChecker) call_info(name string, has_receiver bool) CallInfo {

43114933 }

43124934}

43134935

4936+fn array_type_from_receiver(t Type) ?Array {

4937+ if t is Array {

4938+ return t

4939+ }

4940+ if t is Alias {

4941+ return array_type_from_receiver(t.base_type)

4942+ }

4943+ return none

4944+}

4945+

43144946fn map_type_from_receiver(t Type) ?Map {

43154947 if t is Map {

43164948 return t

@@ -4498,12 +5130,65 @@ fn print_style_param_accepts_string(typ Type) bool {

44985130 return clean is String

44995131}

45005132

5133+// array_insert_prepend_many_arg_compatible reports whether an insert/prepend

5134+// value argument is a many-element operand for the receiver array.

5135+fn (tc &TypeChecker) array_insert_prepend_many_arg_compatible(node flat.Node, info CallInfo, param_idx int, actual Type) bool {

5136+ if info.name !in ['array.insert', 'array.prepend'] {

5137+ return false

5138+ }

5139+ if (info.name == 'array.insert' && param_idx != 2)

5140+ || (info.name == 'array.prepend' && param_idx != 1) {

5141+ return false

5142+ }

5143+ if info.params.len == 0 {

5144+ return false

5145+ }

5146+ mut receiver_type := info.params[0]

5147+ if node.children_count > 0 {

5148+ fn_node := tc.a.child_node(&node, 0)

5149+ if fn_node.kind == .selector && fn_node.children_count > 0 {

5150+ receiver_id := tc.a.child(fn_node, 0)

5151+ receiver_type = tc.cached_expr_type(receiver_id) or { tc.resolve_type(receiver_id) }

5152+ }

5153+ }

5154+ elem_type := array_like_elem_type(unwrap_pointer(receiver_type)) or {

5155+ array_like_elem_type(unwrap_pointer(info.params[0])) or { return false }

5156+ }

5157+ mut clean := actual

5158+ for _ in 0 .. 8 {

5159+ if clean is Alias {

5160+ clean = clean.base_type

5161+ continue

5162+ }

5163+ break

5164+ }

5165+ if clean is Array {

5166+ return tc.receiver_compatible(clean.elem_type, elem_type)

5167+ }

5168+ if clean is ArrayFixed {

5169+ return tc.receiver_compatible(clean.elem_type, elem_type)

5170+ }

5171+ return false

5172+}

5173+

45015174// check_call_arg_types validates check call arg types state for types.

45025175fn (mut tc TypeChecker) check_call_arg_types(id flat.NodeId, node flat.Node, info0 CallInfo) {

45035176 info := tc.specialized_plain_generic_call_info(node, info0)

45045177 if node.children_count == 0 {

45055178 return

45065179 }

5180+ if info.name in ['map.keys', 'map.values'] {

5181+ for i in 1 .. node.children_count {

5182+ tc.check_node(tc.call_arg_value(tc.a.child(&node, i)))

5183+ }

5184+ arg_count := node.children_count - 1

5185+ if arg_count != 0 && tc.should_diagnose(id) {

5186+ tc.record_error(.call_arg_mismatch,

5187+ 'argument count mismatch for `${tc.call_display_name(node)}`: expected 0, got ${arg_count}',

5188+ id)

5189+ }

5190+ return

5191+ }

45075192 if !info.params_known {

45085193 for i in 1 .. node.children_count {

45095194 tc.check_node(tc.call_arg_value(tc.a.child(&node, i)))

@@ -4602,12 +5287,13 @@ fn (mut tc TypeChecker) check_call_arg_types(id flat.NodeId, node flat.Node, inf

46025287 expected_raw := expected

46035288 if info.is_variadic && param_idx == info.params.len - 1 && expected_raw is Array {

46045289 elem_type := array_elem_type(expected_raw)

4605- actual := tc.resolve_expr(arg_id, expected)

5290+ actual := tc.resolve_expr(arg_id, elem_type)

46065291 actual_name := actual.name()

4607- expected_name := '[]${elem_type.name()}'

5292+ expected_name := elem_type.name()

46085293 actual_raw := actual

46095294 if actual is Array {

4610- if !tc.receiver_compatible(actual_raw, expected) {

5295+ if !tc.receiver_compatible(actual_raw, elem_type)

5296+ && !tc.receiver_compatible(actual_raw, expected) {

46115297 tc.type_mismatch(.call_arg_mismatch, 'cannot use `${actual_name}` as argument ${

46125298 param_idx + 1} to `${tc.call_display_name(node)}`; expected `${expected_name}`',

46135299 id)

@@ -4627,6 +5313,12 @@ fn (mut tc TypeChecker) check_call_arg_types(id flat.NodeId, node flat.Node, inf

46275313 actual = tc.resolve_expr(arg_id, expected)

46285314 }

46295315 if !tc.receiver_compatible(actual, expected) {

5316+ if tc.array_insert_prepend_many_arg_compatible(node, info, param_idx, actual) {

5317+ continue

5318+ }

5319+ if tc.array_dsl_fn_arg_compatible(node, info, param_idx, actual) {

5320+ continue

5321+ }

46305322 if tc.is_os_args_contains_call(node) && param_idx == 1 && actual is String {

46315323 continue

46325324 }

@@ -4644,14 +5336,26 @@ fn (mut tc TypeChecker) specialized_plain_generic_call_info(node flat.Node, info

46445336 return info

46455337 }

46465338 mut inferred := map[string]string{}

4647- for i, param_text in param_texts {

4648- arg_idx := i + 1

5339+ mut first_param_idx := 0

5340+ if info.has_receiver && param_texts.len > 0 {

5341+ fn_node := tc.a.child_node(&node, 0)

5342+ if fn_node.kind == .selector && fn_node.children_count > 0 {

5343+ recv_id := tc.a.child(fn_node, 0)

5344+ actual := tc.resolve_type(recv_id)

5345+ tc.infer_generic_type_text_from_type(param_texts[0], actual, generic_params, mut

5346+ inferred)

5347+ first_param_idx = 1

5348+ }

5349+ }

5350+ for param_idx in first_param_idx .. param_texts.len {

5351+ arg_idx := param_idx - first_param_idx + 1

46495352 if arg_idx >= node.children_count {

46505353 break

46515354 }

46525355 arg_id := tc.call_arg_value(tc.a.child(&node, arg_idx))

46535356 actual := tc.resolve_type(arg_id)

4654- tc.infer_generic_type_text_from_type(param_text, actual, generic_params, mut inferred)

5357+ tc.infer_generic_type_text_from_type(param_texts[param_idx], actual, generic_params, mut

5358+ inferred)

46555359 }

46565360 mut concrete_args := []string{cap: generic_params.len}

46575361 for param in generic_params {

@@ -4803,6 +5507,9 @@ fn (mut tc TypeChecker) array_map_return_elem_type(node flat.Node) Type {

48035507 tc.check_node(arg_id)

48045508 elem_type := tc.resolve_type(arg_id)

48055509 tc.pop_scope()

5510+ if fn_typ := fn_type_from_type(elem_type) {

5511+ return fn_typ.return_type

5512+ }

48065513 if elem_type is Void || elem_type is Unknown {

48075514 return Type(void_)

48085515 }

@@ -4835,6 +5542,66 @@ fn (tc &TypeChecker) is_os_args_contains_call(node flat.Node) bool {

48355542 return parent.kind == .ident && parent.value == 'os'

48365543}

48375544

5545+fn (tc &TypeChecker) pointer_builtin_method_call_info(base_type Type, method string) ?CallInfo {

5546+ receiver := pointer_builtin_receiver_name(base_type)

5547+ if receiver.len == 0 {

5548+ return none

5549+ }

5550+ if receiver in ['charptr', 'byteptr'] && method in ['vstring', 'vstring_with_len'] {

5551+ mut params := tarr1(base_type)

5552+ if method == 'vstring_with_len' {

5553+ params << Type(int_)

5554+ }

5555+ return CallInfo{

5556+ name: '${receiver}.${method}'

5557+ params: params

5558+ return_type: Type(string_)

5559+ has_receiver: true

5560+ params_known: true

5561+ }

5562+ }

5563+ if receiver in ['byteptr', 'voidptr'] && method == 'vbytes' {

5564+ return CallInfo{

5565+ name: '${receiver}.${method}'

5566+ params: [base_type, Type(int_)]

5567+ return_type: Type(Array{

5568+ elem_type: Type(u8_)

5569+ })

5570+ has_receiver: true

5571+ params_known: true

5572+ }

5573+ }

5574+ return none

5575+}

5576+

5577+fn pointer_builtin_receiver_name(typ Type) string {

5578+ if typ is Alias {

5579+ if typ.name in ['charptr', 'byteptr', 'voidptr'] {

5580+ return typ.name

5581+ }

5582+ return pointer_builtin_receiver_name(typ.base_type)

5583+ }

5584+ if typ is Pointer {

5585+ base := typ.base_type

5586+ if base is Alias {

5587+ if base.name == 'byte' {

5588+ return 'byteptr'

5589+ }

5590+ return pointer_builtin_receiver_name(base)

5591+ }

5592+ if base is Char {

5593+ return 'charptr'

5594+ }

5595+ if base is Void {

5596+ return 'voidptr'

5597+ }

5598+ if base is Primitive && prim_name(base) == 'u8' {

5599+ return 'byteptr'

5600+ }

5601+ }

5602+ return ''

5603+}

5604+

48385605// min_required_arg_count supports min required arg count handling for TypeChecker.

48395606fn (tc &TypeChecker) min_required_arg_count(info CallInfo) int {

48405607 if info.is_variadic && info.params.len > 0 {

@@ -4878,6 +5645,26 @@ fn (tc &TypeChecker) call_arg_needs_array_dsl_scope(name string, param_idx int)

48785645 return param_idx == 1 && is_array_dsl_call_name(name)

48795646}

48805647

5648+fn (tc &TypeChecker) array_dsl_fn_arg_compatible(node flat.Node, info CallInfo, param_idx int, actual Type) bool {

5649+ if param_idx != 1 || info.name !in ['array.filter', 'array.map'] {

5650+ return false

5651+ }

5652+ fn_typ := fn_type_from_type(actual) or { return false }

5653+ if fn_typ.params.len != 1 {

5654+ return false

5655+ }

5656+ arr := tc.call_receiver_array_type(node) or { return false }

5657+ param := fn_param_type(fn_typ, 0)

5658+ if !tc.receiver_compatible(param, arr.elem_type)

5659+ && !tc.receiver_compatible(arr.elem_type, param) {

5660+ return false

5661+ }

5662+ if info.name == 'array.filter' {

5663+ return tc.type_compatible(fn_typ.return_type, Type(bool_))

5664+ }

5665+ return true

5666+}

5667+

48815668// is_array_dsl_call_name reports whether is array dsl call name applies in types.

48825669fn is_array_dsl_call_name(name string) bool {

48835670 return name in ['array.filter', 'array.any', 'array.all', 'array.count', 'array.map',

@@ -5321,6 +6108,9 @@ fn array_like_elem_type(t Type) ?Type {

53216108 if t is ArrayFixed {

53226109 return t.elem_type

53236110 }

6111+ if t is Alias {

6112+ return array_like_elem_type(t.base_type)

6113+ }

53246114 return none

53256115}

53266116

@@ -5443,11 +6233,20 @@ fn (tc &TypeChecker) is_known_call(node flat.Node) bool {

54436233 return true

54446234 }

54456235 base_type := tc.resolve_type(tc.a.child(fn_node, 0))

6236+ if fn_node.value == 'hex' && tc.type_is_pointer_receiver(base_type) {

6237+ return false

6238+ }

54466239 clean_type := unwrap_pointer(base_type)

54476240 if clean_type is Array || clean_type is ArrayFixed || clean_type is Map {

6241+ if fn_node.value == 'hex' {

6242+ return tc.is_builtin_hex_receiver(base_type)

6243+ }

54486244 return true

54496245 }

54506246 if clean_type is String {

6247+ if fn_node.value == 'hex' {

6248+ return tc.is_builtin_hex_receiver(base_type)

6249+ }

54516250 return 'string.${fn_node.value}' in tc.fn_ret_types

54526251 }

54536252 if clean_type is Alias {

@@ -5501,6 +6300,22 @@ fn (tc &TypeChecker) is_known_call(node flat.Node) bool {

55016300 return false

55026301}

55036302

6303+fn (tc &TypeChecker) is_unsupported_hex_call(node flat.Node) bool {

6304+ if node.children_count == 0 {

6305+ return false

6306+ }

6307+ fn_node := tc.a.child_node(&node, 0)

6308+ if fn_node.kind != .selector || fn_node.value != 'hex' || fn_node.children_count == 0 {

6309+ return false

6310+ }

6311+ base_id := tc.a.child(fn_node, 0)

6312+ if tc.receiver_expr_is_pointer(base_id) {

6313+ return true

6314+ }

6315+ base_type := tc.resolve_type(base_id)

6316+ return !tc.is_builtin_hex_receiver(base_type)

6317+}

6318+

55046319fn (tc &TypeChecker) call_has_ambiguous_selective_import(node flat.Node) bool {

55056320 if node.children_count == 0 {

55066321 return false

@@ -5653,6 +6468,7 @@ fn (mut tc TypeChecker) check_if_guard(id flat.NodeId, node flat.Node) []LocalBi

56536468 tc.check_node(rhs_id)

56546469 rhs_type := tc.resolve_type(rhs_id)

56556470 mut payload := Type(void_)

6471+ is_optional_result := rhs_type is OptionType || rhs_type is ResultType

56566472 if rhs_type is OptionType {

56576473 payload = rhs_type.base_type

56586474 } else if rhs_type is ResultType {

@@ -5666,7 +6482,7 @@ fn (mut tc TypeChecker) check_if_guard(id flat.NodeId, node flat.Node) []LocalBi

56666482 }

56676483 }

56686484 }

5669- if payload is Void {

6485+ if payload is Void && !is_optional_result {

56706486 if tc.should_diagnose(id) {

56716487 tc.record_error(.condition_mismatch,

56726488 'if guard expression must be optional or result, not `${rhs_type.name()}`', id)

@@ -5719,7 +6535,9 @@ fn (mut tc TypeChecker) check_match_stmt(_id flat.NodeId, node flat.Node) {

57196535 cond_id := tc.a.child(branch, 0)

57206536 cond := tc.a.node(cond_id)

57216537 if pattern := tc.match_type_pattern(cond) {

5722- tc.smartcasts[subject_key] = tc.parse_type(pattern)

6538+ if tc.type_symbol_known(pattern) {

6539+ tc.smartcasts[subject_key] = tc.parse_type(pattern)

6540+ }

57236541 }

57246542 }

57256543 tc.push_scope()

@@ -5987,6 +6805,7 @@ fn (mut tc TypeChecker) check_struct_init(id flat.NodeId, node flat.Node) {

59876805 // A method value stored in a struct field escapes the evaluation site (several

59886806 // instances from the same `Foo{cb: obj.method}` site would share one receiver).

59896807 tc.reject_stored_method_value(value_id)

6808+ tc.reject_stored_capturing_fn_literal(value_id)

59906809 mut expected := Type(void_)

59916810 if field.value.len > 0 {

59926811 mut found := false

@@ -6069,6 +6888,48 @@ fn (mut tc TypeChecker) reject_stored_method_value(id flat.NodeId) {

60696888 }

60706889}

60716890

6891+fn (tc &TypeChecker) expr_is_capturing_fn_literal_value(id flat.NodeId) bool {

6892+ if int(id) < 0 || int(id) >= tc.a.nodes.len {

6893+ return false

6894+ }

6895+ node := tc.a.nodes[int(id)]

6896+ match node.kind {

6897+ .fn_literal {

6898+ return tc.fn_literal_has_captures(node)

6899+ }

6900+ .cast_expr, .paren, .expr_stmt {

6901+ if node.children_count == 0 {

6902+ return false

6903+ }

6904+ return tc.expr_is_capturing_fn_literal_value(tc.a.child(&node, 0))

6905+ }

6906+ else {

6907+ return false

6908+ }

6909+ }

6910+}

6911+

6912+fn (tc &TypeChecker) fn_literal_has_captures(node flat.Node) bool {

6913+ if node.kind != .fn_literal {

6914+ return false

6915+ }

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

6917+ child := tc.a.child_node(&node, i)

6918+ if child.kind == .ident && child.value.len > 0 {

6919+ return true

6920+ }

6921+ }

6922+ return false

6923+}

6924+

6925+fn (mut tc TypeChecker) reject_stored_capturing_fn_literal(id flat.NodeId) {

6926+ if tc.expr_is_capturing_fn_literal_value(id) && tc.should_diagnose(id) {

6927+ tc.record_error(.assignment_mismatch,

6928+ 'a capturing fn literal cannot be stored or returned (no closure capture); pass it directly as a callback argument',

6929+ id)

6930+ }

6931+}

6932+

60726933// check_selector validates check selector state for types.

60736934fn (mut tc TypeChecker) check_selector(id flat.NodeId, node flat.Node) {

60746935 if node.children_count == 0 {

@@ -6446,6 +7307,14 @@ fn (mut tc TypeChecker) resolve_expr(id flat.NodeId, expected Type) Type {

64467307 return expected

64477308 }

64487309 }

7310+ if payload := contextual_payload_type(expected) {

7311+ if payload is Array || payload is ArrayFixed {

7312+ actual := tc.resolve_expr(id, payload)

7313+ if tc.type_compatible(actual, payload) {

7314+ return actual

7315+ }

7316+ }

7317+ }

64497318 if node.kind == .enum_val && expected is Enum {

64507319 if tc.enum_value_matches(node.value, expected.name) {

64517320 tc.register_synth_type(id, expected_raw)

@@ -6477,6 +7346,17 @@ fn (mut tc TypeChecker) resolve_expr(id flat.NodeId, expected Type) Type {

64777346 return expected_raw

64787347 }

64797348 }

7349+ if node.kind == .postfix && node.children_count == 1 {

7350+ child_id := tc.a.child(&node, 0)

7351+ child := tc.a.nodes[int(child_id)]

7352+ if node.op == .not && child.kind == .array_literal {

7353+ actual := tc.resolve_expr(child_id, expected)

7354+ if tc.receiver_compatible(actual, expected) {

7355+ tc.register_synth_type(id, expected_raw)

7356+ return expected_raw

7357+ }

7358+ }

7359+ }

64807360 if node.kind == .array_literal {

64817361 mut elem_expected := Type(void_)

64827362 mut expected_is_array := false

@@ -6816,6 +7696,9 @@ fn (tc &TypeChecker) type_compatible(actual Type, expected Type) bool {

68167696 if actual is Unknown || expected is Unknown {

68177697 return true

68187698 }

7699+ if type_contains_unknown(actual) || type_contains_unknown(expected) {

7700+ return true

7701+ }

68197702 if actual is Alias && expected is Alias {

68207703 return tc.type_compatible(actual.base_type, expected.base_type)

68217704 }

@@ -6847,7 +7730,7 @@ fn (tc &TypeChecker) type_compatible(actual Type, expected Type) bool {

68477730 return tc.type_compatible(actual, expected.base_type)

68487731 }

68497732 if expected is ResultType {

6850- if is_ierror_type(actual) {

7733+ if is_ierror_type(actual) || tc.type_embeds_error(actual) {

68517734 return true

68527735 }

68537736 if actual is ResultType {

@@ -6949,6 +7832,49 @@ fn (tc &TypeChecker) type_compatible(actual Type, expected Type) bool {

69497832 return false

69507833}

69517834

7835+fn type_contains_unknown(typ Type) bool {

7836+ if typ is Unknown {

7837+ return true

7838+ }

7839+ if typ is Alias {

7840+ return type_contains_unknown(typ.base_type)

7841+ }

7842+ if typ is Array {

7843+ return type_contains_unknown(typ.elem_type)

7844+ }

7845+ if typ is ArrayFixed {

7846+ return type_contains_unknown(typ.elem_type)

7847+ }

7848+ if typ is Map {

7849+ return type_contains_unknown(typ.key_type) || type_contains_unknown(typ.value_type)

7850+ }

7851+ if typ is Pointer {

7852+ return type_contains_unknown(typ.base_type)

7853+ }

7854+ if typ is OptionType {

7855+ return type_contains_unknown(typ.base_type)

7856+ }

7857+ if typ is ResultType {

7858+ return type_contains_unknown(typ.base_type)

7859+ }

7860+ if typ is FnType {

7861+ for param in typ.params {

7862+ if type_contains_unknown(param) {

7863+ return true

7864+ }

7865+ }

7866+ return type_contains_unknown(typ.return_type)

7867+ }

7868+ if typ is MultiReturn {

7869+ for part in typ.types {

7870+ if type_contains_unknown(part) {

7871+ return true

7872+ }

7873+ }

7874+ }

7875+ return false

7876+}

7877+

69527878fn (tc &TypeChecker) fn_param_compatible(actual Type, expected Type) bool {

69537879 if actual is Unknown || expected is Unknown {

69547880 return false

@@ -7020,6 +7946,23 @@ fn is_ierror_type(t Type) bool {

70207946 return false

70217947}

70227948

7949+fn (tc &TypeChecker) type_embeds_error(t Type) bool {

7950+ clean := unwrap_pointer(t)

7951+ if clean is Alias {

7952+ return tc.type_embeds_error(clean.base_type)

7953+ }

7954+ if clean is Struct {

7955+ struct_name := clean.name

7956+ if struct_name == 'Error' || struct_name.ends_with('.Error') {

7957+ return true

7958+ }

7959+ return tc.receiver_embeds(clean, Type(Struct{

7960+ name: 'Error'

7961+ }))

7962+ }

7963+ return false

7964+}

7965+

70237966// is_runtime_array_type reports whether is runtime array type applies in types.

70247967fn is_runtime_array_type(t Type) bool {

70257968 if t is Alias {

@@ -7079,20 +8022,36 @@ fn const_expr_paren_wraps_whole(s string) bool {

70798022

70808023// const_int_value supports const int value handling for TypeChecker.

70818024pub fn (tc &TypeChecker) const_int_value(name string, seen []string) ?int {

8025+ return tc.const_int_value_in_module(name, tc.cur_module, seen)

8026+}

8027+

8028+// const_int_value_in_module supports const int value handling for a specific module.

8029+pub fn (tc &TypeChecker) const_int_value_in_module(name string, module_name string, seen []string) ?int {

70828030 if name in seen {

70838031 return none

70848032 }

70858033 mut candidates := []string{}

70868034 candidates << name

8035+ if module_name.len > 0 && module_name != 'main' && module_name != 'builtin'

8036+ && !name.contains('.') {

8037+ candidates << '${module_name}.${name}'

8038+ }

70878039 qname := tc.qualify_name(name)

70888040 if qname != name {

70898041 candidates << qname

70908042 }

8043+ if key := tc.const_key_for_suffix(name) {

8044+ candidates << key

8045+ }

70918046 for key in candidates {

8047+ if key in seen {

8048+ continue

8049+ }

70928050 if expr_id := tc.const_exprs[key] {

70938051 mut next_seen := seen.clone()

70948052 next_seen << key

7095- return tc.const_int_expr(expr_id, next_seen)

8053+ const_module := tc.const_modules[key] or { module_name }

8054+ return tc.const_int_expr(expr_id, const_module, next_seen)

70968055 }

70978056 }

70988057 if v := v_int_literal_value(name) {

@@ -7166,8 +8125,8 @@ pub fn (tc &TypeChecker) const_int_value(name string, seen []string) ?int {

71668125 if lhs.len == 0 || rhs.len == 0 {

71678126 continue

71688127 }

7169- l := tc.const_int_value(lhs, seen) or { return none }

7170- r := tc.const_int_value(rhs, seen) or { return none }

8128+ l := tc.const_int_value_in_module(lhs, module_name, seen) or { return none }

8129+ r := tc.const_int_value_in_module(rhs, module_name, seen) or { return none }

71718130 if (op == '/' || op == '%') && r == 0 {

71728131 return none

71738132 }

@@ -7192,7 +8151,7 @@ pub fn (tc &TypeChecker) const_int_value(name string, seen []string) ?int {

71928151}

71938152

71948153// const_int_expr supports const int expr handling for TypeChecker.

7195-fn (tc &TypeChecker) const_int_expr(id flat.NodeId, seen []string) ?int {

8154+fn (tc &TypeChecker) const_int_expr(id flat.NodeId, module_name string, seen []string) ?int {

71968155 if int(id) < 0 {

71978156 return none

71988157 }

@@ -7204,18 +8163,18 @@ fn (tc &TypeChecker) const_int_expr(id flat.NodeId, seen []string) ?int {

72048163 }

72058164 }

72068165 .ident {

7207- return tc.const_int_value(node.value, seen)

8166+ return tc.const_int_value_in_module(node.value, module_name, seen)

72088167 }

72098168 .paren {

72108169 if node.children_count > 0 {

7211- return tc.const_int_expr(tc.a.child(&node, 0), seen)

8170+ return tc.const_int_expr(tc.a.child(&node, 0), module_name, seen)

72128171 }

72138172 }

72148173 .prefix {

72158174 if node.children_count == 0 {

72168175 return none

72178176 }

7218- value := tc.const_int_expr(tc.a.child(&node, 0), seen) or { return none }

8177+ value := tc.const_int_expr(tc.a.child(&node, 0), module_name, seen) or { return none }

72198178 return match node.op {

72208179 .minus { -value }

72218180 .plus { value }

@@ -7227,8 +8186,8 @@ fn (tc &TypeChecker) const_int_expr(id flat.NodeId, seen []string) ?int {

72278186 if node.children_count < 2 {

72288187 return none

72298188 }

7230- left := tc.const_int_expr(tc.a.child(&node, 0), seen) or { return none }

7231- right := tc.const_int_expr(tc.a.child(&node, 1), seen) or { return none }

8189+ left := tc.const_int_expr(tc.a.child(&node, 0), module_name, seen) or { return none }

8190+ right := tc.const_int_expr(tc.a.child(&node, 1), module_name, seen) or { return none }

72328191 match node.op {

72338192 .plus {

72348193 return left + right

@@ -7732,7 +8691,7 @@ fn method_type_name(t Type) string {

77328691 return 'string'

77338692 }

77348693 if t is Primitive {

7735- return prim_c_type_from(t.props, t.size)

8694+ return prim_name(t)

77368695 }

77378696 if t is ISize {

77388697 return 'isize'

@@ -8527,6 +9486,11 @@ pub fn (tc &TypeChecker) resolve_type(id flat.NodeId) Type {

85279486 return typ

85289487 }

85299488 }

9489+ if node.kind == .selector {

9490+ if typ := tc.const_type_for_selector(node) {

9491+ return typ

9492+ }

9493+ }

85309494 if node.typ.len > 0 && node.typ != 'unknown' && !(kind_id == 12

85319495 && node.typ in ['int', 'array', 'map']) {

85329496 return tc.parse_type(node.typ)

@@ -8542,7 +9506,7 @@ pub fn (tc &TypeChecker) resolve_type(id flat.NodeId) Type {

85429506 return Type(bool_)

85439507 }

85449508 .char_literal {

8545- return Type(u8_)

9509+ return Type(rune_)

85469510 }

85479511 .string_literal, .string_interp {

85489512 return Type(string_)

@@ -8801,7 +9765,8 @@ pub fn (tc &TypeChecker) resolve_type(id flat.NodeId) Type {

88019765 }

88029766 }

88039767 }

8804- if clean_type is Primitive && fn_node.value == 'str' {

9768+ if fn_node.value == 'str'

9769+ && (clean_type is Primitive || clean_type is Char || clean_type is Rune) {

88059770 return Type(string_)

88069771 }

88079772 if (clean_type is Void || clean_type is Primitive)

@@ -9082,16 +10047,33 @@ pub fn (tc &TypeChecker) resolve_type(id flat.NodeId) Type {

908210047 .array_literal {

908310048 if node.children_count > 0 {

908410049 elem_type := tc.resolve_type(tc.a.child(&node, 0))

9085- return Type(ArrayFixed{

10050+ return Type(Array{

908610051 elem_type: elem_type

9087- len: node.children_count

908810052 })

908910053 }

9090- return Type(ArrayFixed{

10054+ return Type(Array{

909110055 elem_type: Type(int_)

9092- len: 0

909310056 })

909410057 }

10058+ .postfix {

10059+ if node.children_count == 0 {

10060+ return unknown_type('missing postfix expression')

10061+ }

10062+ child_id := tc.a.child(&node, 0)

10063+ child := tc.a.nodes[int(child_id)]

10064+ if node.op == .not && child.kind == .array_literal {

10065+ elem_type := if child.children_count > 0 {

10066+ tc.resolve_type(tc.a.child(&child, 0))

10067+ } else {

10068+ Type(int_)

10069+ }

10070+ return Type(ArrayFixed{

10071+ elem_type: elem_type

10072+ len: child.children_count

10073+ })

10074+ }

10075+ return tc.resolve_type(child_id)

10076+ }

909510077 .index {

909610078 return tc.resolve_index_type(node)

909710079 }

@@ -9318,7 +10300,7 @@ fn (tc &TypeChecker) c_type_uncached(t Type) string {

931810300 return 'size_t'

931910301 }

932010302 if t is Primitive {

9321- return prim_c_type_from(t.props, t.size)

10303+ return prim_c_type(t)

932210304 }

932310305 if t is Array {

932410306 return 'Array'

@@ -9417,10 +10399,13 @@ fn (tc &TypeChecker) fn_ptr_return_c_type(t Type) string {

941710399}

941810400

941910401fn (tc &TypeChecker) optional_c_type_name(base_type Type) string {

9420- if base_type is Void || base_type is Primitive || base_type is Enum {

10402+ if base_type is Void {

942110403 return 'Optional'

942210404 }

942310405 inner_ct := tc.c_type(base_type)

10406+ if inner_ct == 'int' {

10407+ return 'Optional'

10408+ }

942410409 return 'Optional_${inner_ct.replace('*', 'ptr').replace(' ', '_')}'

942510410}

942610411

@@ -9645,7 +10630,7 @@ fn resolve_type_name_for_method(t Type) string {

964510630 return 'map[${nested_type_name(t.key_type)}]${nested_type_name(t.value_type)}'

964610631 }

964710632 if t is Primitive {

9648- return prim_c_type_from(t.props, t.size)

10633+ return prim_name(t)

964910634 }

965010635 return ''

965110636}

vlib/v3/v3.v +112 -5

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

299299 mut user_files := []string{}

300300 if input_file.ends_with('.v') {

301301 user_files << input_file

302+ user_files = expand_single_test_file_inputs(user_files, prefs)

302303 } else if os.is_dir(input_file) {

303304 user_files = pref.get_v_files_from_dir(input_file, prefs.user_defines, prefs.target_os)

304305 for subdir in vmod_subdirs(input_file) {

@@ -545,6 +546,96 @@ fn vmod_subdirs(dir string) []string {

545546 return subdirs

546547}

547548

549+fn expand_single_test_file_inputs(user_files []string, prefs &pref.Preferences) []string {

550+ mut expanded := []string{}

551+ mut seen := map[string]bool{}

552+ for file in user_files {

553+ if pref.is_test_file_for_backend(file, prefs.backend) {

554+ module_name := declared_module_in_file(file)

555+ if module_name.len > 0 && module_name != 'main' && module_name != 'builtin' {

556+ for module_file in same_dir_module_source_files(file, module_name, prefs) {

557+ append_unique_file(mut expanded, mut seen, module_file)

558+ }

559+ }

560+ }

561+ append_unique_file(mut expanded, mut seen, file)

562+ }

563+ return expanded

564+}

565+

566+fn same_dir_module_source_files(test_file string, module_name string, prefs &pref.Preferences) []string {

567+ dir := os.dir(test_file)

568+ mut files := []string{}

569+ for file in pref.get_v_files_from_dir(dir, prefs.user_defines, prefs.target_os) {

570+ if declared_module_in_file(file) == module_name {

571+ files << file

572+ }

573+ }

574+ return files

575+}

576+

577+fn append_unique_file(mut files []string, mut seen map[string]bool, file string) {

578+ key := os.real_path(file)

579+ if seen[key] {

580+ return

581+ }

582+ seen[key] = true

583+ files << file

584+}

585+

586+fn declared_module_in_file(path string) string {

587+ content := os.read_file(path) or { return '' }

588+ mut in_block_comment := false

589+ mut in_attr := false

590+ for raw_line in content.split_into_lines() {

591+ mut line := raw_line.trim_space()

592+ if in_block_comment {

593+ if end := line.index('*/') {

594+ line = line[end + 2..].trim_space()

595+ in_block_comment = false

596+ } else {

597+ continue

598+ }

599+ }

600+ if in_attr {

601+ if line.contains(']') {

602+ in_attr = false

603+ }

604+ continue

605+ }

606+ for line.starts_with('/*') {

607+ if end := line.index('*/') {

608+ line = line[end + 2..].trim_space()

609+ } else {

610+ in_block_comment = true

611+ line = ''

612+ break

613+ }

614+ }

615+ if line.len == 0 || line.starts_with('//') {

616+ continue

617+ }

618+ if line.starts_with('@[') || line.starts_with('[') {

619+ if !line.contains(']') {

620+ in_attr = true

621+ }

622+ continue

623+ }

624+ if line.starts_with('module ') {

625+ mut module_name := line[7..]

626+ if comment := module_name.index('//') {

627+ module_name = module_name[..comment]

628+ }

629+ if comment := module_name.index('/*') {

630+ module_name = module_name[..comment]

631+ }

632+ return module_name.trim_space()

633+ }

634+ return ''

635+ }

636+ return ''

637+}

638+

548639fn project_root_for_files(files []string) string {

549640 for file in files {

550641 root := nearest_vmod_root_for_file(file)

@@ -655,11 +746,6 @@ fn validate_test_file_harness_inputs(a &flat.FlatAst, tc &types.TypeChecker, tes

655746 if !is_user_test_file_node(a, file_idx, file_node, selected_files) {

656747 continue

657748 }

658- module_name := test_file_module_name(a, file_node)

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

660- errors << 'no runnable tests in ${file_node.value}: only module main test files are supported'

661- continue

662- }

663749 if test_file_has_executable_top_level_stmt(a, file_node) {

664750 errors << 'invalid test file ${file_node.value}: executable top-level statements are not supported in test files'

665751 continue

@@ -846,6 +932,7 @@ fn resolve_imports(mut a flat.FlatAst, mut p parser.Parser, prefs &pref.Preferen

846932 mut parsed_modules := map[string]bool{}

847933 parsed_modules['builtin'] = true

848934 parsed_modules['main'] = true

935+ seed_initial_modules(a, initial_files, mut parsed_modules)

849936

850937 // Backend modules excluded by the active configuration are never parsed: their

851938 // dispatch in main() is gated out by the matching `$if !skip_* ?`, so nothing

@@ -905,6 +992,26 @@ fn resolve_imports(mut a flat.FlatAst, mut p parser.Parser, prefs &pref.Preferen

905992 normalize_import_module_identities(mut a, prefs, first_file, project_root)

906993}

907994

995+fn seed_initial_modules(a &flat.FlatAst, initial_files []string, mut parsed_modules map[string]bool) {

996+ mut selected_files := map[string]bool{}

997+ for file in initial_files {

998+ selected_files[file] = true

999+ selected_files[os.real_path(file)] = true

1000+ }

1001+ for file_idx, file_node in a.nodes {

1002+ if file_idx < a.user_code_start || file_node.kind != .file || file_node.value.len == 0 {

1003+ continue

1004+ }

1005+ if !selected_files[file_node.value] && !selected_files[os.real_path(file_node.value)] {

1006+ continue

1007+ }

1008+ module_name := test_file_module_name(a, file_node)

1009+ if module_name.len > 0 {

1010+ parsed_modules[module_name] = true

1011+ }

1012+ }

1013+}

1014+

9081015fn canonicalize_imported_module_name(mut a flat.FlatAst, first_node int, import_path string) {

9091016 if import_path.len == 0 {

9101017 return