v / vlib / v2 / markused / minimal_runtime_roots_test.v
1995 lines · 1927 sloc · 50.49 KB · e78b7311ad580c800e5a3a0bd0afe3876e609685
Raw
1module markused
2
3import v2.ast
4import v2.token
5import v2.types
6
7fn minimal_pos(id int) token.Pos {
8 return token.Pos{
9 offset: id
10 id: id
11 }
12}
13
14fn minimal_ident(name string, id int) ast.Ident {
15 return ast.Ident{
16 name: name
17 pos: minimal_pos(id)
18 }
19}
20
21fn minimal_selector(lhs string, rhs string, id int) ast.SelectorExpr {
22 return ast.SelectorExpr{
23 lhs: minimal_ident(lhs, id)
24 rhs: minimal_ident(rhs, id + 1)
25 pos: minimal_pos(id)
26 }
27}
28
29fn minimal_c_selector(name string, id int) ast.SelectorExpr {
30 return minimal_selector('C', name, id)
31}
32
33fn minimal_c_call(name string, id int, args []ast.Expr) ast.CallExpr {
34 return ast.CallExpr{
35 lhs: minimal_c_selector(name, id)
36 args: args
37 pos: minimal_pos(id)
38 }
39}
40
41fn mark_used_flat_minimal(files []ast.File, env &types.Environment) map[string]bool {
42 flat := ast.flatten_files(files)
43 return mark_used_flat_with_options(&flat, env, MarkUsedOptions{
44 minimal_runtime_roots: true
45 })
46}
47
48fn local_call_minimal_files() []ast.File {
49 return [
50 ast.File{
51 mod: 'main'
52 name: 'local_calls.v'
53 stmts: [
54 ast.Stmt(ast.FnDecl{
55 name: 'main'
56 typ: ast.FnType{}
57 pos: minimal_pos(10)
58 stmts: [
59 ast.Stmt(ast.ExprStmt{
60 expr: ast.CallExpr{
61 lhs: ast.Expr(minimal_ident('foo', 11))
62 pos: minimal_pos(11)
63 }
64 }),
65 ]
66 }),
67 ast.Stmt(ast.FnDecl{
68 name: 'foo'
69 typ: ast.FnType{}
70 pos: minimal_pos(12)
71 stmts: [
72 ast.Stmt(ast.ExprStmt{
73 expr: ast.CallExpr{
74 lhs: ast.Expr(minimal_ident('status', 13))
75 pos: minimal_pos(13)
76 }
77 }),
78 ]
79 }),
80 ast.Stmt(ast.FnDecl{
81 name: 'status'
82 typ: ast.FnType{}
83 pos: minimal_pos(14)
84 }),
85 ast.Stmt(ast.FnDecl{
86 name: 'dead'
87 typ: ast.FnType{}
88 pos: minimal_pos(15)
89 }),
90 ]
91 },
92 ]
93}
94
95fn assert_local_call_minimal_used(used map[string]bool, files []ast.File, env &types.Environment, label string) {
96 main_key := decl_key('main', files[0].stmts[0] as ast.FnDecl, env)
97 foo_key := decl_key('main', files[0].stmts[1] as ast.FnDecl, env)
98 status_key := decl_key('main', files[0].stmts[2] as ast.FnDecl, env)
99 dead_key := decl_key('main', files[0].stmts[3] as ast.FnDecl, env)
100
101 assert used[main_key], '${label}: main root was not kept'
102 assert used[foo_key], '${label}: local foo call was not marked'
103 assert used[status_key], '${label}: transitive local status call was not marked'
104 assert !used[dead_key], '${label}: unused local function should stay pruned'
105}
106
107fn test_minimal_runtime_roots_pointer_guards_accept_low_canonical_pointers() {
108 assert sumtype_payload_word_is_valid(1, 0x10000)
109 assert !sumtype_payload_word_is_valid(1, 0)
110 assert !sumtype_payload_word_is_valid(1, 3)
111 assert string_ok('foo')
112 assert string_ok('status')
113}
114
115fn test_minimal_runtime_roots_keep_local_calls_legacy() {
116 mut env := types.Environment.new()
117 files := local_call_minimal_files()
118 used := mark_used_with_options(files, env, MarkUsedOptions{
119 minimal_runtime_roots: true
120 })
121 assert_local_call_minimal_used(used, files, env, 'legacy')
122}
123
124fn test_minimal_runtime_roots_keep_local_calls_flat() {
125 mut env := types.Environment.new()
126 files := local_call_minimal_files()
127 used := mark_used_flat_minimal(files, env)
128 assert_local_call_minimal_used(used, files, env, 'flat')
129}
130
131fn selective_import_minimal_files() []ast.File {
132 return [
133 ast.File{
134 mod: 'main'
135 name: 'examples/submodule/main.v'
136 imports: [
137 ast.ImportStmt{
138 name: 'mymodules'
139 alias: 'mymodules'
140 symbols: [ast.Expr(minimal_ident('add_xy', 60))]
141 },
142 ast.ImportStmt{
143 name: 'mymodules.submodule'
144 alias: 'submodule'
145 symbols: [ast.Expr(minimal_ident('sub_xy', 61))]
146 },
147 ]
148 stmts: [
149 ast.Stmt(ast.FnDecl{
150 name: 'main'
151 typ: ast.FnType{}
152 pos: minimal_pos(62)
153 stmts: [
154 ast.Stmt(ast.ExprStmt{
155 expr: ast.CallExpr{
156 lhs: ast.Expr(minimal_ident('add_xy', 63))
157 pos: minimal_pos(63)
158 }
159 }),
160 ast.Stmt(ast.ExprStmt{
161 expr: ast.CallExpr{
162 lhs: ast.Expr(minimal_ident('sub_xy', 64))
163 pos: minimal_pos(64)
164 }
165 }),
166 ]
167 }),
168 ]
169 },
170 ast.File{
171 mod: 'mymodules'
172 name: 'examples/submodule/mymodules/main_functions.v'
173 stmts: [
174 ast.Stmt(ast.FnDecl{
175 name: 'add_xy'
176 typ: ast.FnType{}
177 pos: minimal_pos(65)
178 }),
179 ast.Stmt(ast.FnDecl{
180 name: 'unused_add'
181 typ: ast.FnType{}
182 pos: minimal_pos(66)
183 }),
184 ]
185 },
186 ast.File{
187 mod: 'submodule'
188 name: 'examples/submodule/mymodules/submodule/sub_functions.v'
189 stmts: [
190 ast.Stmt(ast.FnDecl{
191 name: 'sub_xy'
192 typ: ast.FnType{}
193 pos: minimal_pos(67)
194 }),
195 ast.Stmt(ast.FnDecl{
196 name: 'unused_sub'
197 typ: ast.FnType{}
198 pos: minimal_pos(68)
199 }),
200 ]
201 },
202 ]
203}
204
205fn assert_selective_import_minimal_used(used map[string]bool, files []ast.File, env &types.Environment, label string) {
206 main_key := decl_key('main', files[0].stmts[0] as ast.FnDecl, env)
207 add_key := decl_key('mymodules', files[1].stmts[0] as ast.FnDecl, env)
208 unused_add_key := decl_key('mymodules', files[1].stmts[1] as ast.FnDecl, env)
209 sub_key := decl_key('submodule', files[2].stmts[0] as ast.FnDecl, env)
210 unused_sub_key := decl_key('submodule', files[2].stmts[1] as ast.FnDecl, env)
211
212 assert used[main_key], '${label}: main root was not kept'
213 assert used[add_key], '${label}: selective import add_xy was not resolved to mymodules__add_xy'
214 assert used[sub_key], '${label}: selective import sub_xy was not resolved to submodule__sub_xy'
215 assert !used[unused_add_key], '${label}: unused mymodules function should stay pruned'
216 assert !used[unused_sub_key], '${label}: unused submodule function should stay pruned'
217}
218
219fn test_minimal_runtime_roots_keep_selective_imported_function_calls_legacy() {
220 mut env := types.Environment.new()
221 files := selective_import_minimal_files()
222 used := mark_used_with_options(files, env, MarkUsedOptions{
223 minimal_runtime_roots: true
224 })
225 assert_selective_import_minimal_used(used, files, env, 'legacy')
226}
227
228fn test_minimal_runtime_roots_keep_selective_imported_function_calls_flat() {
229 mut env := types.Environment.new()
230 files := selective_import_minimal_files()
231 used := mark_used_flat_minimal(files, env)
232 assert_selective_import_minimal_used(used, files, env, 'flat')
233}
234
235fn selective_import_shadow_minimal_files(use_function_value bool) []ast.File {
236 mut main_body := []ast.Stmt{}
237 if use_function_value {
238 main_body << ast.Stmt(ast.AssignStmt{
239 op: .decl_assign
240 lhs: [ast.Expr(minimal_ident('f', 150))]
241 rhs: [ast.Expr(minimal_ident('add_xy', 151))]
242 pos: minimal_pos(150)
243 })
244 } else {
245 main_body << ast.Stmt(ast.ExprStmt{
246 expr: ast.CallExpr{
247 lhs: ast.Expr(minimal_ident('add_xy', 152))
248 pos: minimal_pos(152)
249 }
250 })
251 }
252 return [
253 ast.File{
254 mod: 'main'
255 name: 'shadow/main.v'
256 imports: [
257 ast.ImportStmt{
258 name: 'mymodules'
259 alias: 'mymodules'
260 symbols: [ast.Expr(minimal_ident('add_xy', 153))]
261 },
262 ]
263 stmts: [
264 ast.Stmt(ast.FnDecl{
265 name: 'main'
266 typ: ast.FnType{}
267 pos: minimal_pos(154)
268 stmts: main_body
269 }),
270 ast.Stmt(ast.FnDecl{
271 name: 'add_xy'
272 typ: ast.FnType{}
273 pos: minimal_pos(155)
274 }),
275 ]
276 },
277 ast.File{
278 mod: 'mymodules'
279 name: 'shadow/mymodules/main_functions.v'
280 stmts: [
281 ast.Stmt(ast.FnDecl{
282 name: 'add_xy'
283 typ: ast.FnType{}
284 pos: minimal_pos(156)
285 }),
286 ast.Stmt(ast.FnDecl{
287 name: 'unused_remote'
288 typ: ast.FnType{}
289 pos: minimal_pos(157)
290 }),
291 ]
292 },
293 ast.File{
294 mod: 'othermod'
295 name: 'shadow/othermod/main_functions.v'
296 stmts: [
297 ast.Stmt(ast.FnDecl{
298 name: 'add_xy'
299 typ: ast.FnType{}
300 pos: minimal_pos(158)
301 }),
302 ]
303 },
304 ]
305}
306
307fn assert_selective_import_shadow_uses_local(used map[string]bool, files []ast.File, env &types.Environment, label string) {
308 main_key := decl_key('main', files[0].stmts[0] as ast.FnDecl, env)
309 local_add_key := decl_key('main', files[0].stmts[1] as ast.FnDecl, env)
310 imported_add_key := decl_key('mymodules', files[1].stmts[0] as ast.FnDecl, env)
311 unused_remote_key := decl_key('mymodules', files[1].stmts[1] as ast.FnDecl, env)
312 other_add_key := decl_key('othermod', files[2].stmts[0] as ast.FnDecl, env)
313
314 assert used[main_key], '${label}: main root was not kept'
315 assert used[local_add_key], '${label}: local add_xy should shadow the selective import'
316 assert !used[imported_add_key], '${label}: shadowed selective import should stay pruned'
317 assert !used[unused_remote_key], '${label}: unused imported-module function should stay pruned'
318 assert !used[other_add_key], '${label}: bare global add_xy fallback marked a colliding module function'
319}
320
321fn test_minimal_runtime_roots_selective_import_call_uses_local_shadow_legacy() {
322 mut env := types.Environment.new()
323 files := selective_import_shadow_minimal_files(false)
324 used := mark_used_with_options(files, env, MarkUsedOptions{
325 minimal_runtime_roots: true
326 })
327 assert_selective_import_shadow_uses_local(used, files, env, 'legacy call')
328}
329
330fn test_minimal_runtime_roots_selective_import_call_uses_local_shadow_flat() {
331 mut env := types.Environment.new()
332 files := selective_import_shadow_minimal_files(false)
333 used := mark_used_flat_minimal(files, env)
334 assert_selective_import_shadow_uses_local(used, files, env, 'flat call')
335}
336
337fn test_minimal_runtime_roots_selective_import_function_value_uses_local_shadow_legacy() {
338 mut env := types.Environment.new()
339 files := selective_import_shadow_minimal_files(true)
340 used := mark_used_with_options(files, env, MarkUsedOptions{
341 minimal_runtime_roots: true
342 })
343 assert_selective_import_shadow_uses_local(used, files, env, 'legacy function value')
344}
345
346fn test_minimal_runtime_roots_selective_import_function_value_uses_local_shadow_flat() {
347 mut env := types.Environment.new()
348 files := selective_import_shadow_minimal_files(true)
349 used := mark_used_flat_minimal(files, env)
350 assert_selective_import_shadow_uses_local(used, files, env, 'flat function value')
351}
352
353fn selective_import_function_value_minimal_files() []ast.File {
354 return [
355 ast.File{
356 mod: 'main'
357 name: 'fnvalue/main.v'
358 imports: [
359 ast.ImportStmt{
360 name: 'mymodules'
361 alias: 'mymodules'
362 symbols: [ast.Expr(minimal_ident('add_xy', 160))]
363 },
364 ]
365 stmts: [
366 ast.Stmt(ast.FnDecl{
367 name: 'main'
368 typ: ast.FnType{}
369 pos: minimal_pos(161)
370 stmts: [
371 ast.Stmt(ast.AssignStmt{
372 op: .decl_assign
373 lhs: [ast.Expr(minimal_ident('f', 162))]
374 rhs: [ast.Expr(minimal_ident('add_xy', 163))]
375 pos: minimal_pos(162)
376 }),
377 ]
378 }),
379 ]
380 },
381 ast.File{
382 mod: 'mymodules'
383 name: 'fnvalue/mymodules/main_functions.v'
384 stmts: [
385 ast.Stmt(ast.FnDecl{
386 name: 'add_xy'
387 typ: ast.FnType{}
388 pos: minimal_pos(164)
389 }),
390 ast.Stmt(ast.FnDecl{
391 name: 'unused_remote'
392 typ: ast.FnType{}
393 pos: minimal_pos(165)
394 }),
395 ]
396 },
397 ast.File{
398 mod: 'othermod'
399 name: 'fnvalue/othermod/main_functions.v'
400 stmts: [
401 ast.Stmt(ast.FnDecl{
402 name: 'add_xy'
403 typ: ast.FnType{}
404 pos: minimal_pos(166)
405 }),
406 ]
407 },
408 ]
409}
410
411fn assert_selective_import_function_value_uses_imported_target(used map[string]bool, files []ast.File, env &types.Environment, label string) {
412 main_key := decl_key('main', files[0].stmts[0] as ast.FnDecl, env)
413 imported_add_key := decl_key('mymodules', files[1].stmts[0] as ast.FnDecl, env)
414 unused_remote_key := decl_key('mymodules', files[1].stmts[1] as ast.FnDecl, env)
415 other_add_key := decl_key('othermod', files[2].stmts[0] as ast.FnDecl, env)
416
417 assert used[main_key], '${label}: main root was not kept'
418 assert used[imported_add_key], '${label}: selective import function value did not resolve to mymodules__add_xy'
419 assert !used[unused_remote_key], '${label}: unused imported-module function should stay pruned'
420 assert !used[other_add_key], '${label}: bare global add_xy fallback marked a colliding module function'
421}
422
423fn test_minimal_runtime_roots_keep_selective_imported_function_values_legacy() {
424 mut env := types.Environment.new()
425 files := selective_import_function_value_minimal_files()
426 used := mark_used_with_options(files, env, MarkUsedOptions{
427 minimal_runtime_roots: true
428 })
429 assert_selective_import_function_value_uses_imported_target(used, files, env,
430 'legacy function value')
431}
432
433fn test_minimal_runtime_roots_keep_selective_imported_function_values_flat() {
434 mut env := types.Environment.new()
435 files := selective_import_function_value_minimal_files()
436 used := mark_used_flat_minimal(files, env)
437 assert_selective_import_function_value_uses_imported_target(used, files, env,
438 'flat function value')
439}
440
441fn test_minimal_runtime_roots_selective_import_add_fn_name_indices_legacy() {
442 mut env := types.Environment.new()
443 files := selective_import_function_value_minimal_files()
444 mut w := new_walker(files, env, MarkUsedOptions{
445 minimal_runtime_roots: true
446 })
447 w.collect_defs()
448 w.cur_fn_file = files[0].name
449 mut indices := []int{}
450 w.add_fn_name_indices('add_xy', 'main', mut indices)
451 target_key := decl_key('mymodules', files[1].stmts[0] as ast.FnDecl, env)
452
453 assert indices.len == 1
454 assert w.fns[indices[0]].key == target_key
455}
456
457fn test_minimal_runtime_roots_selective_import_add_fn_name_indices_flat() {
458 mut env := types.Environment.new()
459 files := selective_import_function_value_minimal_files()
460 flat := ast.flatten_files(files)
461 mut w := new_walker(files, env, MarkUsedOptions{
462 minimal_runtime_roots: true
463 })
464 w.collect_defs_from_flat(&flat)
465 w.cur_fn_file = files[0].name
466 mut indices := []int{}
467 w.add_fn_name_indices('add_xy', 'main', mut indices)
468 target_key := decl_key('mymodules', files[1].stmts[0] as ast.FnDecl, env)
469
470 assert indices.len == 1
471 assert w.fns[indices[0]].key == target_key
472}
473
474fn test_minimal_runtime_roots_keep_explicit_calls_only() {
475 mut env := types.Environment.new()
476 files := [
477 ast.File{
478 mod: 'main'
479 name: 'main.v'
480 stmts: [
481 ast.Stmt(ast.FnDecl{
482 name: 'main'
483 typ: ast.FnType{}
484 pos: minimal_pos(120)
485 stmts: [
486 ast.Stmt(ast.ExprStmt{
487 expr: ast.CallExpr{
488 lhs: ast.Ident{
489 name: 'println'
490 pos: minimal_pos(121)
491 }
492 pos: minimal_pos(121)
493 }
494 }),
495 ]
496 }),
497 ]
498 },
499 ast.File{
500 mod: 'builtin'
501 name: 'vlib/builtin/builtin.v'
502 stmts: [
503 ast.Stmt(ast.FnDecl{
504 name: 'println'
505 typ: ast.FnType{}
506 pos: minimal_pos(122)
507 stmts: [
508 ast.Stmt(ast.ExprStmt{
509 expr: ast.CallExpr{
510 lhs: ast.Ident{
511 name: 'write_stdout'
512 pos: minimal_pos(123)
513 }
514 pos: minimal_pos(123)
515 }
516 }),
517 ]
518 }),
519 ast.Stmt(ast.FnDecl{
520 name: 'write_stdout'
521 typ: ast.FnType{}
522 pos: minimal_pos(124)
523 }),
524 ast.Stmt(ast.FnDecl{
525 name: 'print_backtrace'
526 typ: ast.FnType{}
527 pos: minimal_pos(125)
528 }),
529 ast.Stmt(ast.FnDecl{
530 name: 'builtin_keep_helper'
531 typ: ast.FnType{}
532 pos: minimal_pos(126)
533 }),
534 ]
535 },
536 ast.File{
537 mod: 'time'
538 name: 'time.v'
539 stmts: [
540 ast.Stmt(ast.StructDecl{
541 name: 'Time'
542 }),
543 ast.Stmt(ast.FnDecl{
544 is_method: true
545 receiver: ast.Parameter{
546 name: 't'
547 typ: ast.Ident{
548 name: 'Time'
549 pos: minimal_pos(127)
550 }
551 pos: minimal_pos(127)
552 }
553 name: 'str'
554 typ: ast.FnType{}
555 pos: minimal_pos(128)
556 }),
557 ]
558 },
559 ast.File{
560 mod: 'dep'
561 name: 'dep.v'
562 stmts: [
563 ast.Stmt(ast.FnDecl{
564 name: 'init'
565 typ: ast.FnType{}
566 pos: minimal_pos(129)
567 }),
568 ]
569 },
570 ]
571 used := mark_used_with_options(files, env, MarkUsedOptions{
572 minimal_runtime_roots: true
573 })
574 main_key := decl_key('main', files[0].stmts[0] as ast.FnDecl, env)
575 println_key := decl_key('builtin', files[1].stmts[0] as ast.FnDecl, env)
576 write_stdout_key := decl_key('builtin', files[1].stmts[1] as ast.FnDecl, env)
577 print_backtrace_key := decl_key('builtin', files[1].stmts[2] as ast.FnDecl, env)
578 builtin_keep_key := decl_key('builtin', files[1].stmts[3] as ast.FnDecl, env)
579 time_str_key := decl_key('time', files[2].stmts[1] as ast.FnDecl, env)
580 dep_init_key := decl_key('dep', files[3].stmts[0] as ast.FnDecl, env)
581
582 assert used[main_key]
583 assert used[println_key]
584 assert used[write_stdout_key]
585 assert !used[print_backtrace_key]
586 assert !used[builtin_keep_key]
587 assert !used[time_str_key]
588 assert !used[dep_init_key]
589}
590
591fn test_minimal_runtime_roots_keep_functions_used_as_values() {
592 mut env := types.Environment.new()
593 files := [
594 ast.File{
595 mod: 'main'
596 name: 'main.v'
597 stmts: [
598 ast.Stmt(ast.FnDecl{
599 name: 'main'
600 typ: ast.FnType{}
601 pos: minimal_pos(180)
602 stmts: [
603 ast.Stmt(ast.AssignStmt{
604 op: .decl_assign
605 lhs: [ast.Expr(minimal_ident('f', 181))]
606 rhs: [ast.Expr(minimal_ident('assigned_cleanup', 182))]
607 pos: minimal_pos(181)
608 }),
609 ast.Stmt(ast.ExprStmt{
610 expr: ast.CallExpr{
611 lhs: minimal_ident('run', 183)
612 args: [ast.Expr(minimal_ident('argument_cleanup', 184))]
613 pos: minimal_pos(183)
614 }
615 }),
616 ast.Stmt(ast.AssignStmt{
617 op: .decl_assign
618 lhs: [ast.Expr(minimal_ident('g', 185))]
619 rhs: [
620 ast.Expr(ast.CallExpr{
621 lhs: minimal_ident('choose_cleanup', 186)
622 pos: minimal_pos(186)
623 }),
624 ]
625 pos: minimal_pos(185)
626 }),
627 ]
628 }),
629 ast.Stmt(ast.FnDecl{
630 name: 'run'
631 typ: ast.FnType{}
632 pos: minimal_pos(187)
633 }),
634 ast.Stmt(ast.FnDecl{
635 name: 'choose_cleanup'
636 typ: ast.FnType{}
637 pos: minimal_pos(188)
638 stmts: [
639 ast.Stmt(ast.ReturnStmt{
640 exprs: [
641 ast.Expr(minimal_ident('returned_cleanup', 189)),
642 ]
643 }),
644 ]
645 }),
646 ast.Stmt(ast.FnDecl{
647 name: 'assigned_cleanup'
648 typ: ast.FnType{}
649 pos: minimal_pos(190)
650 }),
651 ast.Stmt(ast.FnDecl{
652 name: 'argument_cleanup'
653 typ: ast.FnType{}
654 pos: minimal_pos(191)
655 }),
656 ast.Stmt(ast.FnDecl{
657 name: 'returned_cleanup'
658 typ: ast.FnType{}
659 pos: minimal_pos(192)
660 }),
661 ast.Stmt(ast.FnDecl{
662 name: 'unused_cleanup'
663 typ: ast.FnType{}
664 pos: minimal_pos(193)
665 }),
666 ]
667 },
668 ]
669 used := mark_used_with_options(files, env, MarkUsedOptions{
670 minimal_runtime_roots: true
671 })
672 main_key := decl_key('main', files[0].stmts[0] as ast.FnDecl, env)
673 run_key := decl_key('main', files[0].stmts[1] as ast.FnDecl, env)
674 choose_key := decl_key('main', files[0].stmts[2] as ast.FnDecl, env)
675 assigned_key := decl_key('main', files[0].stmts[3] as ast.FnDecl, env)
676 argument_key := decl_key('main', files[0].stmts[4] as ast.FnDecl, env)
677 returned_key := decl_key('main', files[0].stmts[5] as ast.FnDecl, env)
678 unused_key := decl_key('main', files[0].stmts[6] as ast.FnDecl, env)
679
680 assert used[main_key]
681 assert used[run_key]
682 assert used[choose_key]
683 assert used[assigned_key]
684 assert used[argument_key]
685 assert used[returned_key]
686 assert !used[unused_key]
687}
688
689fn test_minimal_runtime_roots_keep_functions_used_as_const_values() {
690 mut env := types.Environment.new()
691 files := [
692 ast.File{
693 mod: 'main'
694 name: 'main.v'
695 stmts: [
696 ast.Stmt(ast.ConstDecl{
697 fields: [
698 ast.FieldInit{
699 name: 'cb'
700 value: ast.Expr(minimal_ident('abc', 200))
701 },
702 ]
703 }),
704 ast.Stmt(ast.FnDecl{
705 name: 'main'
706 typ: ast.FnType{}
707 pos: minimal_pos(201)
708 stmts: [
709 ast.Stmt(ast.ExprStmt{
710 expr: ast.CallExpr{
711 lhs: minimal_ident('cb', 202)
712 pos: minimal_pos(202)
713 }
714 }),
715 ]
716 }),
717 ast.Stmt(ast.FnDecl{
718 name: 'abc'
719 typ: ast.FnType{}
720 pos: minimal_pos(203)
721 }),
722 ast.Stmt(ast.FnDecl{
723 name: 'unused'
724 typ: ast.FnType{}
725 pos: minimal_pos(204)
726 }),
727 ]
728 },
729 ]
730 used := mark_used_with_options(files, env, MarkUsedOptions{
731 minimal_runtime_roots: true
732 })
733 main_key := decl_key('main', files[0].stmts[1] as ast.FnDecl, env)
734 abc_key := decl_key('main', files[0].stmts[2] as ast.FnDecl, env)
735 unused_key := decl_key('main', files[0].stmts[3] as ast.FnDecl, env)
736
737 assert used[main_key]
738 assert used[abc_key]
739 assert !used[unused_key]
740}
741
742fn test_minimal_runtime_roots_flat_keeps_const_alias_return_and_const_field_function_values() {
743 mut env := types.Environment.new()
744 files := [
745 ast.File{
746 mod: 'main'
747 name: 'main.v'
748 stmts: [
749 ast.Stmt(ast.ConstDecl{
750 fields: [
751 ast.FieldInit{
752 name: 'cb'
753 value: ast.Expr(minimal_ident('alias_cleanup', 300))
754 },
755 ]
756 }),
757 ast.Stmt(ast.FnDecl{
758 name: 'main'
759 typ: ast.FnType{}
760 pos: minimal_pos(301)
761 stmts: [
762 ast.Stmt(ast.ExprStmt{
763 expr: ast.CallExpr{
764 lhs: minimal_ident('cb', 302)
765 pos: minimal_pos(302)
766 }
767 }),
768 ast.Stmt(ast.ExprStmt{
769 expr: ast.CallExpr{
770 lhs: minimal_ident('choose_cleanup', 303)
771 pos: minimal_pos(303)
772 }
773 }),
774 ast.Stmt(ast.ConstDecl{
775 fields: [
776 ast.FieldInit{
777 name: 'local_cb'
778 value: ast.Expr(minimal_ident('field_cleanup', 304))
779 },
780 ]
781 }),
782 ]
783 }),
784 ast.Stmt(ast.FnDecl{
785 name: 'choose_cleanup'
786 typ: ast.FnType{}
787 pos: minimal_pos(305)
788 stmts: [
789 ast.Stmt(ast.ReturnStmt{
790 exprs: [
791 ast.Expr(minimal_ident('returned_cleanup', 306)),
792 ]
793 }),
794 ]
795 }),
796 ast.Stmt(ast.FnDecl{
797 name: 'alias_cleanup'
798 typ: ast.FnType{}
799 pos: minimal_pos(307)
800 }),
801 ast.Stmt(ast.FnDecl{
802 name: 'returned_cleanup'
803 typ: ast.FnType{}
804 pos: minimal_pos(308)
805 }),
806 ast.Stmt(ast.FnDecl{
807 name: 'field_cleanup'
808 typ: ast.FnType{}
809 pos: minimal_pos(309)
810 }),
811 ast.Stmt(ast.FnDecl{
812 name: 'unused_cleanup'
813 typ: ast.FnType{}
814 pos: minimal_pos(310)
815 }),
816 ]
817 },
818 ]
819 used := mark_used_flat_minimal(files, env)
820 main_key := decl_key('main', files[0].stmts[1] as ast.FnDecl, env)
821 choose_key := decl_key('main', files[0].stmts[2] as ast.FnDecl, env)
822 alias_key := decl_key('main', files[0].stmts[3] as ast.FnDecl, env)
823 returned_key := decl_key('main', files[0].stmts[4] as ast.FnDecl, env)
824 field_key := decl_key('main', files[0].stmts[5] as ast.FnDecl, env)
825 unused_key := decl_key('main', files[0].stmts[6] as ast.FnDecl, env)
826
827 assert used[main_key]
828 assert used[choose_key]
829 assert used[alias_key]
830 assert used[returned_key]
831 assert used[field_key]
832 assert !used[unused_key]
833}
834
835fn test_minimal_runtime_roots_keep_qualified_const_function_aliases() {
836 mut env := types.Environment.new()
837 files := [
838 ast.File{
839 mod: 'main'
840 name: 'main.v'
841 imports: [
842 ast.ImportStmt{
843 name: 'dep'
844 alias: 'd'
845 is_aliased: true
846 },
847 ast.ImportStmt{
848 name: 'tools'
849 },
850 ]
851 stmts: [
852 ast.Stmt(ast.ConstDecl{
853 fields: [
854 ast.FieldInit{
855 name: 'dep_cb'
856 value: ast.Expr(minimal_selector('d', 'cleanup', 210))
857 },
858 ast.FieldInit{
859 name: 'tools_cb'
860 value: ast.Expr(minimal_selector('tools', 'teardown', 213))
861 },
862 ast.FieldInit{
863 name: 'c_cb'
864 value: ast.Expr(minimal_c_selector('cleanup', 216))
865 },
866 ast.FieldInit{
867 name: 'field_cb'
868 value: ast.Expr(minimal_selector('hooks', 'cleanup', 219))
869 },
870 ]
871 }),
872 ast.Stmt(ast.FnDecl{
873 name: 'main'
874 typ: ast.FnType{}
875 pos: minimal_pos(220)
876 stmts: [
877 ast.Stmt(ast.ExprStmt{
878 expr: ast.CallExpr{
879 lhs: minimal_ident('dep_cb', 221)
880 pos: minimal_pos(221)
881 }
882 }),
883 ast.Stmt(ast.ExprStmt{
884 expr: ast.CallExpr{
885 lhs: minimal_ident('tools_cb', 222)
886 pos: minimal_pos(222)
887 }
888 }),
889 ast.Stmt(ast.ExprStmt{
890 expr: ast.CallExpr{
891 lhs: minimal_ident('c_cb', 223)
892 pos: minimal_pos(223)
893 }
894 }),
895 ast.Stmt(ast.ExprStmt{
896 expr: ast.CallExpr{
897 lhs: minimal_ident('field_cb', 224)
898 pos: minimal_pos(224)
899 }
900 }),
901 ]
902 }),
903 ast.Stmt(ast.FnDecl{
904 name: 'cleanup'
905 typ: ast.FnType{}
906 pos: minimal_pos(225)
907 }),
908 ]
909 },
910 ast.File{
911 mod: 'dep'
912 name: 'dep.v'
913 stmts: [
914 ast.Stmt(ast.FnDecl{
915 name: 'cleanup'
916 typ: ast.FnType{}
917 pos: minimal_pos(226)
918 }),
919 ast.Stmt(ast.FnDecl{
920 name: 'unused'
921 typ: ast.FnType{}
922 pos: minimal_pos(227)
923 }),
924 ]
925 },
926 ast.File{
927 mod: 'tools'
928 name: 'tools.v'
929 stmts: [
930 ast.Stmt(ast.FnDecl{
931 name: 'teardown'
932 typ: ast.FnType{}
933 pos: minimal_pos(228)
934 }),
935 ast.Stmt(ast.FnDecl{
936 name: 'unused'
937 typ: ast.FnType{}
938 pos: minimal_pos(229)
939 }),
940 ]
941 },
942 ]
943 used := mark_used_with_options(files, env, MarkUsedOptions{
944 minimal_runtime_roots: true
945 })
946 main_key := decl_key('main', files[0].stmts[1] as ast.FnDecl, env)
947 main_cleanup_key := decl_key('main', files[0].stmts[2] as ast.FnDecl, env)
948 dep_cleanup_key := decl_key('dep', files[1].stmts[0] as ast.FnDecl, env)
949 dep_unused_key := decl_key('dep', files[1].stmts[1] as ast.FnDecl, env)
950 tools_teardown_key := decl_key('tools', files[2].stmts[0] as ast.FnDecl, env)
951 tools_unused_key := decl_key('tools', files[2].stmts[1] as ast.FnDecl, env)
952
953 assert used[main_key]
954 assert used[dep_cleanup_key]
955 assert used[tools_teardown_key]
956 assert !used[main_cleanup_key]
957 assert !used[dep_unused_key]
958 assert !used[tools_unused_key]
959}
960
961fn test_minimal_runtime_roots_keep_functions_used_in_composite_literals() {
962 mut env := types.Environment.new()
963 files := [
964 ast.File{
965 mod: 'main'
966 name: 'main.v'
967 stmts: [
968 ast.Stmt(ast.StructDecl{
969 name: 'Hooks'
970 fields: [
971 ast.FieldDecl{
972 name: 'cb'
973 },
974 ]
975 }),
976 ast.Stmt(ast.FnDecl{
977 name: 'main'
978 typ: ast.FnType{}
979 pos: minimal_pos(220)
980 stmts: [
981 ast.Stmt(ast.AssignStmt{
982 op: .decl_assign
983 lhs: [ast.Expr(minimal_ident('hook', 221))]
984 rhs: [
985 ast.Expr(ast.InitExpr{
986 typ: ast.Expr(minimal_ident('Hooks', 222))
987 fields: [
988 ast.FieldInit{
989 name: 'cb'
990 value: ast.Expr(minimal_ident('struct_cleanup', 223))
991 },
992 ]
993 pos: minimal_pos(222)
994 }),
995 ]
996 pos: minimal_pos(221)
997 }),
998 ast.Stmt(ast.AssignStmt{
999 op: .decl_assign
1000 lhs: [
1001 ast.Expr(minimal_ident('items', 224)),
1002 ]
1003 rhs: [
1004 ast.Expr(ast.ArrayInitExpr{
1005 exprs: [
1006 ast.Expr(minimal_ident('array_cleanup', 225)),
1007 ]
1008 pos: minimal_pos(224)
1009 }),
1010 ]
1011 pos: minimal_pos(224)
1012 }),
1013 ast.Stmt(ast.AssignStmt{
1014 op: .decl_assign
1015 lhs: [
1016 ast.Expr(minimal_ident('init_items', 226)),
1017 ]
1018 rhs: [
1019 ast.Expr(ast.ArrayInitExpr{
1020 init: ast.Expr(minimal_ident('init_cleanup', 227))
1021 pos: minimal_pos(226)
1022 }),
1023 ]
1024 pos: minimal_pos(226)
1025 }),
1026 ast.Stmt(ast.AssignStmt{
1027 op: .decl_assign
1028 lhs: [
1029 ast.Expr(minimal_ident('tuple', 228)),
1030 ]
1031 rhs: [
1032 ast.Expr(ast.Tuple{
1033 exprs: [
1034 ast.Expr(minimal_ident('tuple_cleanup', 229)),
1035 ]
1036 pos: minimal_pos(228)
1037 }),
1038 ]
1039 pos: minimal_pos(228)
1040 }),
1041 ast.Stmt(ast.AssignStmt{
1042 op: .decl_assign
1043 lhs: [
1044 ast.Expr(minimal_ident('assoc_hook', 230)),
1045 ]
1046 rhs: [
1047 ast.Expr(ast.AssocExpr{
1048 typ: ast.Expr(minimal_ident('Hooks', 231))
1049 expr: ast.Expr(minimal_ident('hook', 232))
1050 fields: [
1051 ast.FieldInit{
1052 name: 'cb'
1053 value: ast.Expr(minimal_ident('assoc_cleanup', 233))
1054 },
1055 ]
1056 pos: minimal_pos(230)
1057 }),
1058 ]
1059 pos: minimal_pos(230)
1060 }),
1061 ast.Stmt(ast.AssignStmt{
1062 op: .decl_assign
1063 lhs: [
1064 ast.Expr(minimal_ident('lookup', 234)),
1065 ]
1066 rhs: [
1067 ast.Expr(ast.MapInitExpr{
1068 keys: [
1069 ast.Expr(ast.StringLiteral{
1070 value: 'cleanup'
1071 pos: minimal_pos(235)
1072 }),
1073 ]
1074 vals: [
1075 ast.Expr(minimal_ident('map_cleanup', 236)),
1076 ]
1077 pos: minimal_pos(234)
1078 }),
1079 ]
1080 pos: minimal_pos(234)
1081 }),
1082 ]
1083 }),
1084 ast.Stmt(ast.FnDecl{
1085 name: 'struct_cleanup'
1086 typ: ast.FnType{}
1087 pos: minimal_pos(237)
1088 }),
1089 ast.Stmt(ast.FnDecl{
1090 name: 'array_cleanup'
1091 typ: ast.FnType{}
1092 pos: minimal_pos(238)
1093 }),
1094 ast.Stmt(ast.FnDecl{
1095 name: 'init_cleanup'
1096 typ: ast.FnType{}
1097 pos: minimal_pos(239)
1098 }),
1099 ast.Stmt(ast.FnDecl{
1100 name: 'tuple_cleanup'
1101 typ: ast.FnType{}
1102 pos: minimal_pos(240)
1103 }),
1104 ast.Stmt(ast.FnDecl{
1105 name: 'assoc_cleanup'
1106 typ: ast.FnType{}
1107 pos: minimal_pos(241)
1108 }),
1109 ast.Stmt(ast.FnDecl{
1110 name: 'map_cleanup'
1111 typ: ast.FnType{}
1112 pos: minimal_pos(242)
1113 }),
1114 ast.Stmt(ast.FnDecl{
1115 name: 'unused_cleanup'
1116 typ: ast.FnType{}
1117 pos: minimal_pos(243)
1118 }),
1119 ]
1120 },
1121 ]
1122 used := mark_used_with_options(files, env, MarkUsedOptions{
1123 minimal_runtime_roots: true
1124 })
1125 main_key := decl_key('main', files[0].stmts[1] as ast.FnDecl, env)
1126 struct_key := decl_key('main', files[0].stmts[2] as ast.FnDecl, env)
1127 array_key := decl_key('main', files[0].stmts[3] as ast.FnDecl, env)
1128 init_key := decl_key('main', files[0].stmts[4] as ast.FnDecl, env)
1129 tuple_key := decl_key('main', files[0].stmts[5] as ast.FnDecl, env)
1130 assoc_key := decl_key('main', files[0].stmts[6] as ast.FnDecl, env)
1131 map_key := decl_key('main', files[0].stmts[7] as ast.FnDecl, env)
1132 unused_key := decl_key('main', files[0].stmts[8] as ast.FnDecl, env)
1133
1134 assert used[main_key]
1135 assert used[struct_key]
1136 assert used[array_key]
1137 assert used[init_key]
1138 assert used[tuple_key]
1139 assert used[assoc_key]
1140 assert used[map_key]
1141 assert !used[unused_key]
1142}
1143
1144fn test_minimal_runtime_roots_do_not_treat_c_globals_as_v_functions() {
1145 mut env := types.Environment.new()
1146 files := [
1147 ast.File{
1148 mod: 'main'
1149 name: 'main.v'
1150 stmts: [
1151 ast.Stmt(ast.FnDecl{
1152 name: 'main'
1153 typ: ast.FnType{}
1154 pos: minimal_pos(140)
1155 stmts: [
1156 ast.Stmt(ast.ExprStmt{
1157 expr: ast.CallExpr{
1158 lhs: ast.Ident{
1159 name: 'touch_c_stdio_globals'
1160 pos: minimal_pos(141)
1161 }
1162 pos: minimal_pos(141)
1163 }
1164 }),
1165 ]
1166 }),
1167 ]
1168 },
1169 ast.File{
1170 mod: 'builtin'
1171 name: 'vlib/builtin/stdio_globals.v'
1172 stmts: [
1173 ast.Stmt(ast.FnDecl{
1174 name: 'touch_c_stdio_globals'
1175 typ: ast.FnType{}
1176 pos: minimal_pos(142)
1177 stmts: [
1178 ast.Stmt(ast.ExprStmt{
1179 expr: ast.SelectorExpr{
1180 lhs: ast.Ident{
1181 name: 'C'
1182 pos: minimal_pos(143)
1183 }
1184 rhs: ast.Ident{
1185 name: 'stdout'
1186 pos: minimal_pos(144)
1187 }
1188 pos: minimal_pos(144)
1189 }
1190 }),
1191 ast.Stmt(ast.ExprStmt{
1192 expr: ast.SelectorExpr{
1193 lhs: ast.Ident{
1194 name: 'C'
1195 pos: minimal_pos(145)
1196 }
1197 rhs: ast.Ident{
1198 name: 'stderr'
1199 pos: minimal_pos(146)
1200 }
1201 pos: minimal_pos(146)
1202 }
1203 }),
1204 ]
1205 }),
1206 ]
1207 },
1208 ast.File{
1209 mod: 'os'
1210 name: 'vlib/os/file.c.v'
1211 stmts: [
1212 ast.Stmt(ast.FnDecl{
1213 name: 'stdout'
1214 typ: ast.FnType{}
1215 pos: minimal_pos(147)
1216 }),
1217 ast.Stmt(ast.FnDecl{
1218 name: 'stderr'
1219 typ: ast.FnType{}
1220 pos: minimal_pos(148)
1221 }),
1222 ]
1223 },
1224 ]
1225 used := mark_used_with_options(files, env, MarkUsedOptions{
1226 minimal_runtime_roots: true
1227 })
1228 main_key := decl_key('main', files[0].stmts[0] as ast.FnDecl, env)
1229 touch_key := decl_key('builtin', files[1].stmts[0] as ast.FnDecl, env)
1230 os_stdout_key := decl_key('os', files[2].stmts[0] as ast.FnDecl, env)
1231 os_stderr_key := decl_key('os', files[2].stmts[1] as ast.FnDecl, env)
1232
1233 assert used[main_key]
1234 assert used[touch_key]
1235 assert !used[os_stdout_key]
1236 assert !used[os_stderr_key]
1237}
1238
1239fn test_minimal_runtime_roots_do_not_keep_unreached_windows_stdin_crt_helpers() {
1240 mut env := types.Environment.new()
1241 files := [
1242 ast.File{
1243 mod: 'main'
1244 name: 'main.v'
1245 stmts: [
1246 ast.Stmt(ast.FnDecl{
1247 name: 'main'
1248 typ: ast.FnType{}
1249 pos: minimal_pos(160)
1250 stmts: [
1251 ast.Stmt(ast.ExprStmt{
1252 expr: ast.CallExpr{
1253 lhs: minimal_ident('println', 161)
1254 pos: minimal_pos(161)
1255 }
1256 }),
1257 ]
1258 }),
1259 ]
1260 },
1261 ast.File{
1262 mod: 'builtin'
1263 name: 'vlib/builtin/builtin_windows.c.v'
1264 stmts: [
1265 ast.Stmt(ast.FnDecl{
1266 name: 'println'
1267 typ: ast.FnType{}
1268 pos: minimal_pos(162)
1269 stmts: [
1270 ast.Stmt(ast.ExprStmt{
1271 expr: ast.CallExpr{
1272 lhs: minimal_ident('write_stdout', 163)
1273 pos: minimal_pos(163)
1274 }
1275 }),
1276 ]
1277 }),
1278 ast.Stmt(ast.FnDecl{
1279 name: 'write_stdout'
1280 typ: ast.FnType{}
1281 pos: minimal_pos(164)
1282 }),
1283 ast.Stmt(ast.FnDecl{
1284 name: 'read_from_std_input_handle'
1285 typ: ast.FnType{}
1286 pos: minimal_pos(165)
1287 stmts: [
1288 ast.Stmt(ast.ExprStmt{
1289 expr: minimal_c_call('GetStdHandle', 166, [
1290 ast.Expr(minimal_c_selector('STD_INPUT_HANDLE', 167)),
1291 ])
1292 }),
1293 ]
1294 }),
1295 ast.Stmt(ast.FnDecl{
1296 name: 'fd_from_crt_handle'
1297 typ: ast.FnType{}
1298 pos: minimal_pos(168)
1299 stmts: [
1300 ast.Stmt(ast.ExprStmt{
1301 expr: minimal_c_call('_get_osfhandle', 169, [])
1302 }),
1303 ]
1304 }),
1305 ast.Stmt(ast.FnDecl{
1306 name: 'touch_c_stdin_global'
1307 typ: ast.FnType{}
1308 pos: minimal_pos(170)
1309 stmts: [
1310 ast.Stmt(ast.ExprStmt{
1311 expr: minimal_c_selector('stdin', 171)
1312 }),
1313 ]
1314 }),
1315 ]
1316 },
1317 ast.File{
1318 mod: 'os'
1319 name: 'vlib/os/file.c.v'
1320 stmts: [
1321 ast.Stmt(ast.FnDecl{
1322 name: 'fread'
1323 typ: ast.FnType{}
1324 pos: minimal_pos(172)
1325 stmts: [
1326 ast.Stmt(ast.ExprStmt{
1327 expr: minimal_c_call('fread', 173, [])
1328 }),
1329 ]
1330 }),
1331 ast.Stmt(ast.FnDecl{
1332 name: 'read_file_chunk'
1333 typ: ast.FnType{}
1334 pos: minimal_pos(174)
1335 stmts: [
1336 ast.Stmt(ast.ExprStmt{
1337 expr: ast.CallExpr{
1338 lhs: minimal_ident('fread', 175)
1339 pos: minimal_pos(175)
1340 }
1341 }),
1342 ]
1343 }),
1344 ast.Stmt(ast.FnDecl{
1345 name: 'stdin'
1346 typ: ast.FnType{}
1347 pos: minimal_pos(176)
1348 }),
1349 ]
1350 },
1351 ]
1352 used := mark_used_with_options(files, env, MarkUsedOptions{
1353 minimal_runtime_roots: true
1354 })
1355 main_key := decl_key('main', files[0].stmts[0] as ast.FnDecl, env)
1356 println_key := decl_key('builtin', files[1].stmts[0] as ast.FnDecl, env)
1357 write_stdout_key := decl_key('builtin', files[1].stmts[1] as ast.FnDecl, env)
1358 std_input_handle_key := decl_key('builtin', files[1].stmts[2] as ast.FnDecl, env)
1359 crt_handle_key := decl_key('builtin', files[1].stmts[3] as ast.FnDecl, env)
1360 stdin_global_key := decl_key('builtin', files[1].stmts[4] as ast.FnDecl, env)
1361 fread_key := decl_key('os', files[2].stmts[0] as ast.FnDecl, env)
1362 read_file_chunk_key := decl_key('os', files[2].stmts[1] as ast.FnDecl, env)
1363 os_stdin_key := decl_key('os', files[2].stmts[2] as ast.FnDecl, env)
1364
1365 assert used[main_key]
1366 assert used[println_key]
1367 assert used[write_stdout_key]
1368 assert !used[std_input_handle_key]
1369 assert !used[crt_handle_key]
1370 assert !used[stdin_global_key]
1371 assert !used[fread_key]
1372 assert !used[read_file_chunk_key]
1373 assert !used[os_stdin_key]
1374}
1375
1376fn test_minimal_runtime_roots_keep_array_push_noscan_wrapper() {
1377 mut env := types.Environment.new()
1378 files := [
1379 ast.File{
1380 mod: 'main'
1381 name: 'main.v'
1382 stmts: [
1383 ast.Stmt(ast.FnDecl{
1384 name: 'main'
1385 typ: ast.FnType{}
1386 pos: minimal_pos(320)
1387 stmts: [
1388 ast.Stmt(ast.ExprStmt{
1389 expr: ast.CallExpr{
1390 lhs: ast.Ident{
1391 name: 'builtin__array_push_noscan'
1392 pos: minimal_pos(321)
1393 }
1394 pos: minimal_pos(321)
1395 }
1396 }),
1397 ]
1398 }),
1399 ]
1400 },
1401 ast.File{
1402 mod: 'builtin'
1403 name: 'vlib/builtin/array_notd_gcboehm_opt.v'
1404 stmts: [
1405 ast.Stmt(ast.FnDecl{
1406 is_method: true
1407 receiver: ast.Parameter{
1408 name: 'a'
1409 typ: ast.Ident{
1410 name: 'array'
1411 pos: minimal_pos(322)
1412 }
1413 pos: minimal_pos(322)
1414 }
1415 name: 'push_noscan'
1416 typ: ast.FnType{}
1417 pos: minimal_pos(323)
1418 stmts: [
1419 ast.Stmt(ast.ExprStmt{
1420 expr: ast.CallExpr{
1421 lhs: ast.SelectorExpr{
1422 lhs: ast.Ident{
1423 name: 'a'
1424 pos: minimal_pos(324)
1425 }
1426 rhs: ast.Ident{
1427 name: 'push'
1428 pos: minimal_pos(327)
1429 }
1430 pos: minimal_pos(327)
1431 }
1432 pos: minimal_pos(327)
1433 }
1434 }),
1435 ]
1436 }),
1437 ast.Stmt(ast.FnDecl{
1438 is_method: true
1439 receiver: ast.Parameter{
1440 name: 'a'
1441 typ: ast.Ident{
1442 name: 'array'
1443 pos: minimal_pos(325)
1444 }
1445 pos: minimal_pos(325)
1446 }
1447 name: 'push'
1448 typ: ast.FnType{}
1449 pos: minimal_pos(326)
1450 }),
1451 ]
1452 },
1453 ]
1454 used := mark_used_with_options(files, env, MarkUsedOptions{
1455 minimal_runtime_roots: true
1456 })
1457 push_noscan_key := decl_key('builtin', files[1].stmts[0] as ast.FnDecl, env)
1458 push_key := decl_key('builtin', files[1].stmts[1] as ast.FnDecl, env)
1459 assert used[push_noscan_key]
1460 assert used[push_key]
1461}
1462
1463fn minimal_array_eq_runtime_dependency_files() []ast.File {
1464 return [
1465 ast.File{
1466 mod: 'main'
1467 name: 'array_eq_transformed.v'
1468 stmts: [
1469 ast.Stmt(ast.FnDecl{
1470 name: 'main'
1471 typ: ast.FnType{}
1472 pos: minimal_pos(360)
1473 stmts: [
1474 ast.Stmt(ast.ExprStmt{
1475 expr: ast.CallExpr{
1476 lhs: ast.Expr(minimal_ident('array__eq', 360))
1477 args: [
1478 ast.Expr(minimal_ident('path', 361)),
1479 ast.Expr(minimal_ident('expected', 362)),
1480 ]
1481 pos: minimal_pos(363)
1482 }
1483 }),
1484 ]
1485 }),
1486 ]
1487 },
1488 ast.File{
1489 mod: 'builtin'
1490 name: 'vlib/builtin/map.v'
1491 stmts: [
1492 ast.Stmt(ast.FnDecl{
1493 name: 'map_map_eq'
1494 typ: ast.FnType{}
1495 pos: minimal_pos(364)
1496 }),
1497 ast.Stmt(ast.FnDecl{
1498 name: 'map_clone_string'
1499 typ: ast.FnType{}
1500 pos: minimal_pos(365)
1501 }),
1502 ]
1503 },
1504 ]
1505}
1506
1507fn minimal_array_eq_runtime_dependency_env() &types.Environment {
1508 return types.Environment.new()
1509}
1510
1511fn test_minimal_runtime_roots_transformed_array_eq_keeps_map_map_eq_dependency_legacy() {
1512 mut env := minimal_array_eq_runtime_dependency_env()
1513 files := minimal_array_eq_runtime_dependency_files()
1514 used := mark_used_with_options(files, env, MarkUsedOptions{
1515 minimal_runtime_roots: true
1516 })
1517 map_map_eq_key := decl_key('builtin', files[1].stmts[0] as ast.FnDecl, env)
1518 unused_key := decl_key('builtin', files[1].stmts[1] as ast.FnDecl, env)
1519
1520 assert used[map_map_eq_key]
1521 assert !used[unused_key]
1522}
1523
1524fn test_minimal_runtime_roots_transformed_array_eq_keeps_map_map_eq_dependency_flat() {
1525 mut env := minimal_array_eq_runtime_dependency_env()
1526 files := minimal_array_eq_runtime_dependency_files()
1527 used := mark_used_flat_minimal(files, env)
1528 map_map_eq_key := decl_key('builtin', files[1].stmts[0] as ast.FnDecl, env)
1529 unused_key := decl_key('builtin', files[1].stmts[1] as ast.FnDecl, env)
1530
1531 assert used[map_map_eq_key]
1532 assert !used[unused_key]
1533}
1534
1535fn test_minimal_runtime_roots_do_not_seed_array_rune_string_helper_or_generic_array_methods() {
1536 mut env := types.Environment.new()
1537 array_rune_type := ast.Expr(ast.Type(ast.ArrayType{
1538 elem_type: ast.Expr(ast.Ident{
1539 name: 'rune'
1540 pos: minimal_pos(430)
1541 })
1542 }))
1543 files := [
1544 ast.File{
1545 mod: 'main'
1546 name: 'main.v'
1547 stmts: [
1548 ast.Stmt(ast.FnDecl{
1549 name: 'main'
1550 typ: ast.FnType{}
1551 pos: minimal_pos(431)
1552 }),
1553 ]
1554 },
1555 ast.File{
1556 mod: 'builtin'
1557 name: 'vlib/builtin/rune.v'
1558 stmts: [
1559 ast.Stmt(ast.FnDecl{
1560 is_method: true
1561 receiver: ast.Parameter{
1562 name: 'arr'
1563 typ: ast.Expr(ast.Ident{
1564 name: 'array'
1565 pos: minimal_pos(440)
1566 })
1567 pos: minimal_pos(441)
1568 }
1569 name: 'string'
1570 typ: ast.FnType{}
1571 pos: minimal_pos(442)
1572 }),
1573 ast.Stmt(ast.FnDecl{
1574 is_method: true
1575 receiver: ast.Parameter{
1576 name: 'ra'
1577 typ: array_rune_type
1578 pos: minimal_pos(432)
1579 }
1580 name: 'string'
1581 typ: ast.FnType{}
1582 pos: minimal_pos(433)
1583 stmts: [
1584 ast.Stmt(ast.ExprStmt{
1585 expr: ast.CallExpr{
1586 lhs: ast.Ident{
1587 name: 'strings__new_builder'
1588 pos: minimal_pos(434)
1589 }
1590 pos: minimal_pos(434)
1591 }
1592 }),
1593 ]
1594 }),
1595 ast.Stmt(ast.FnDecl{
1596 is_method: true
1597 receiver: ast.Parameter{
1598 name: 'sa'
1599 typ: ast.Expr(ast.Type(ast.ArrayType{
1600 elem_type: ast.Expr(ast.Ident{
1601 name: 'string'
1602 pos: minimal_pos(443)
1603 })
1604 }))
1605 pos: minimal_pos(444)
1606 }
1607 name: 'string'
1608 typ: ast.FnType{}
1609 pos: minimal_pos(445)
1610 }),
1611 ast.Stmt(ast.FnDecl{
1612 is_method: true
1613 receiver: ast.Parameter{
1614 name: 'sa'
1615 typ: ast.Expr(ast.Type(ast.ArrayType{
1616 elem_type: ast.Expr(ast.Ident{
1617 name: 'string'
1618 pos: minimal_pos(446)
1619 })
1620 }))
1621 pos: minimal_pos(447)
1622 }
1623 name: 'str'
1624 typ: ast.FnType{}
1625 pos: minimal_pos(448)
1626 }),
1627 ast.Stmt(ast.FnDecl{
1628 name: 'unused_rune_helper'
1629 typ: ast.FnType{}
1630 pos: minimal_pos(435)
1631 }),
1632 ]
1633 },
1634 ast.File{
1635 mod: 'strings'
1636 name: 'vlib/strings/builder.c.v'
1637 stmts: [
1638 ast.Stmt(ast.FnDecl{
1639 name: 'new_builder'
1640 typ: ast.FnType{}
1641 pos: minimal_pos(436)
1642 stmts: [
1643 ast.Stmt(ast.ExprStmt{
1644 expr: ast.CallExpr{
1645 lhs: ast.Ident{
1646 name: 'prepare_builder'
1647 pos: minimal_pos(437)
1648 }
1649 pos: minimal_pos(437)
1650 }
1651 }),
1652 ]
1653 }),
1654 ast.Stmt(ast.FnDecl{
1655 name: 'prepare_builder'
1656 typ: ast.FnType{}
1657 pos: minimal_pos(438)
1658 }),
1659 ast.Stmt(ast.FnDecl{
1660 name: 'unused_builder'
1661 typ: ast.FnType{}
1662 pos: minimal_pos(439)
1663 }),
1664 ]
1665 },
1666 ]
1667 used := mark_used_with_options(files, env, MarkUsedOptions{
1668 minimal_runtime_roots: true
1669 })
1670 main_key := decl_key('main', files[0].stmts[0] as ast.FnDecl, env)
1671 generic_array_string_key := decl_key('builtin', files[1].stmts[0] as ast.FnDecl, env)
1672 array_rune_string_key := decl_key('builtin', files[1].stmts[1] as ast.FnDecl, env)
1673 array_string_string_key := decl_key('builtin', files[1].stmts[2] as ast.FnDecl, env)
1674 array_string_str_key := decl_key('builtin', files[1].stmts[3] as ast.FnDecl, env)
1675 unused_rune_helper_key := decl_key('builtin', files[1].stmts[4] as ast.FnDecl, env)
1676 new_builder_key := decl_key('strings', files[2].stmts[0] as ast.FnDecl, env)
1677 prepare_builder_key := decl_key('strings', files[2].stmts[1] as ast.FnDecl, env)
1678 unused_builder_key := decl_key('strings', files[2].stmts[2] as ast.FnDecl, env)
1679
1680 assert used[main_key]
1681 assert !used[generic_array_string_key]
1682 assert !used[array_rune_string_key]
1683 assert !used[array_string_string_key]
1684 assert !used[array_string_str_key]
1685 assert !used[new_builder_key]
1686 assert !used[prepare_builder_key]
1687 assert !used[unused_rune_helper_key]
1688 assert !used[unused_builder_key]
1689}
1690
1691fn array_rune_string_free_exact_target_files() []ast.File {
1692 return [
1693 ast.File{
1694 mod: 'main'
1695 name: 'main.v'
1696 stmts: [
1697 ast.Stmt(ast.FnDecl{
1698 name: 'main'
1699 typ: ast.FnType{}
1700 pos: minimal_pos(500)
1701 stmts: [
1702 ast.Stmt(ast.ExprStmt{
1703 expr: ast.CallExpr{
1704 lhs: ast.Ident{
1705 name: 'builtin__Array_rune__string'
1706 pos: minimal_pos(501)
1707 }
1708 pos: minimal_pos(501)
1709 }
1710 }),
1711 ]
1712 }),
1713 ]
1714 },
1715 ast.File{
1716 mod: 'builtin'
1717 name: 'vlib/builtin/rune.v'
1718 stmts: [
1719 ast.Stmt(ast.FnDecl{
1720 is_method: true
1721 receiver: ast.Parameter{
1722 name: 'ra'
1723 typ: ast.Expr(ast.Type(ast.ArrayType{
1724 elem_type: ast.Expr(ast.Ident{
1725 name: 'rune'
1726 pos: minimal_pos(502)
1727 })
1728 }))
1729 pos: minimal_pos(503)
1730 }
1731 name: 'string'
1732 typ: ast.FnType{}
1733 pos: minimal_pos(504)
1734 stmts: [
1735 ast.Stmt(ast.ExprStmt{
1736 expr: ast.CallExpr{
1737 lhs: ast.Ident{
1738 name: 'strings__Builder__free'
1739 pos: minimal_pos(505)
1740 }
1741 pos: minimal_pos(505)
1742 }
1743 }),
1744 ]
1745 }),
1746 ast.Stmt(ast.FnDecl{
1747 is_method: true
1748 receiver: ast.Parameter{
1749 name: 'a'
1750 typ: ast.Expr(ast.Ident{
1751 name: 'array'
1752 pos: minimal_pos(506)
1753 })
1754 pos: minimal_pos(507)
1755 }
1756 name: 'free'
1757 typ: ast.FnType{}
1758 pos: minimal_pos(508)
1759 }),
1760 ]
1761 },
1762 ast.File{
1763 mod: 'strings'
1764 name: 'vlib/strings/builder.v'
1765 stmts: [
1766 ast.Stmt(ast.StructDecl{
1767 name: 'Builder'
1768 }),
1769 ast.Stmt(ast.FnDecl{
1770 is_method: true
1771 receiver: ast.Parameter{
1772 name: 'b'
1773 typ: ast.Expr(ast.SelectorExpr{
1774 lhs: ast.Ident{
1775 name: 'strings'
1776 pos: minimal_pos(509)
1777 }
1778 rhs: ast.Ident{
1779 name: 'Builder'
1780 pos: minimal_pos(510)
1781 }
1782 pos: minimal_pos(509)
1783 })
1784 pos: minimal_pos(511)
1785 }
1786 name: 'free'
1787 typ: ast.FnType{}
1788 pos: minimal_pos(512)
1789 }),
1790 ]
1791 },
1792 ]
1793}
1794
1795fn assert_array_rune_string_marks_exact_builder_free(used map[string]bool, files []ast.File, env &types.Environment, label string) {
1796 main_key := decl_key('main', files[0].stmts[0] as ast.FnDecl, env)
1797 array_rune_string_key := decl_key('builtin', files[1].stmts[0] as ast.FnDecl, env)
1798 array_free_key := decl_key('builtin', files[1].stmts[1] as ast.FnDecl, env)
1799 builder_free_key := decl_key('strings', files[2].stmts[1] as ast.FnDecl, env)
1800
1801 assert used[main_key], '${label}: main root was not kept'
1802 assert used[array_rune_string_key], '${label}: Array_rune.string was not reached'
1803 assert used[builder_free_key], '${label}: exact strings__Builder__free target was pruned'
1804 assert !used[array_free_key], '${label}: array__free alias replaced the exact Builder.free target'
1805}
1806
1807fn test_minimal_runtime_roots_array_rune_string_keeps_exact_builder_free_legacy() {
1808 mut env := types.Environment.new()
1809 files := array_rune_string_free_exact_target_files()
1810 used := mark_used_with_options(files, env, MarkUsedOptions{
1811 minimal_runtime_roots: true
1812 })
1813 assert_array_rune_string_marks_exact_builder_free(used, files, env, 'legacy')
1814}
1815
1816fn test_minimal_runtime_roots_array_rune_string_keeps_exact_builder_free_flat() {
1817 mut env := types.Environment.new()
1818 files := array_rune_string_free_exact_target_files()
1819 used := mark_used_flat_minimal(files, env)
1820 assert_array_rune_string_marks_exact_builder_free(used, files, env, 'flat')
1821}
1822
1823fn test_minimal_runtime_roots_keep_synthesized_import_init_calls() {
1824 mut env := types.Environment.new()
1825 files := [
1826 ast.File{
1827 mod: 'main'
1828 name: 'main.v'
1829 imports: [
1830 ast.ImportStmt{
1831 name: 'dep'
1832 },
1833 ]
1834 stmts: [
1835 ast.Stmt(ast.FnDecl{
1836 name: 'main'
1837 typ: ast.FnType{}
1838 pos: minimal_pos(220)
1839 stmts: [
1840 ast.Stmt(ast.ExprStmt{
1841 expr: ast.CallExpr{
1842 lhs: ast.Ident{
1843 name: 'dep__init'
1844 pos: minimal_pos(221)
1845 }
1846 pos: minimal_pos(221)
1847 }
1848 }),
1849 ast.Stmt(ast.ExprStmt{
1850 expr: ast.CallExpr{
1851 lhs: ast.Ident{
1852 name: 'dep____v_init_consts_dep'
1853 pos: minimal_pos(222)
1854 }
1855 pos: minimal_pos(222)
1856 }
1857 }),
1858 ]
1859 }),
1860 ]
1861 },
1862 ast.File{
1863 mod: 'dep'
1864 name: 'dep.v'
1865 stmts: [
1866 ast.Stmt(ast.FnDecl{
1867 name: 'init'
1868 typ: ast.FnType{}
1869 pos: minimal_pos(223)
1870 stmts: [
1871 ast.Stmt(ast.ExprStmt{
1872 expr: ast.CallExpr{
1873 lhs: ast.Ident{
1874 name: 'touch'
1875 pos: minimal_pos(224)
1876 }
1877 pos: minimal_pos(224)
1878 }
1879 }),
1880 ]
1881 }),
1882 ast.Stmt(ast.FnDecl{
1883 name: '__v_init_consts_dep'
1884 typ: ast.FnType{}
1885 pos: minimal_pos(225)
1886 stmts: [
1887 ast.Stmt(ast.ExprStmt{
1888 expr: ast.CallExpr{
1889 lhs: ast.Ident{
1890 name: 'make_value'
1891 pos: minimal_pos(226)
1892 }
1893 pos: minimal_pos(226)
1894 }
1895 }),
1896 ]
1897 }),
1898 ast.Stmt(ast.FnDecl{
1899 name: 'touch'
1900 typ: ast.FnType{}
1901 pos: minimal_pos(227)
1902 }),
1903 ast.Stmt(ast.FnDecl{
1904 name: 'make_value'
1905 typ: ast.FnType{}
1906 pos: minimal_pos(228)
1907 }),
1908 ast.Stmt(ast.FnDecl{
1909 name: 'unused'
1910 typ: ast.FnType{}
1911 pos: minimal_pos(229)
1912 }),
1913 ]
1914 },
1915 ]
1916 used := mark_used_with_options(files, env, MarkUsedOptions{
1917 minimal_runtime_roots: true
1918 })
1919 main_key := decl_key('main', files[0].stmts[0] as ast.FnDecl, env)
1920 dep_init_key := decl_key('dep', files[1].stmts[0] as ast.FnDecl, env)
1921 dep_const_init_key := decl_key('dep', files[1].stmts[1] as ast.FnDecl, env)
1922 touch_key := decl_key('dep', files[1].stmts[2] as ast.FnDecl, env)
1923 make_value_key := decl_key('dep', files[1].stmts[3] as ast.FnDecl, env)
1924 unused_key := decl_key('dep', files[1].stmts[4] as ast.FnDecl, env)
1925
1926 assert used[main_key]
1927 assert used[dep_init_key]
1928 assert used[dep_const_init_key]
1929 assert used[touch_key]
1930 assert used[make_value_key]
1931 assert !used[unused_key]
1932}
1933
1934fn test_minimal_runtime_roots_do_not_seed_forbidden_imports() {
1935 mut env := types.Environment.new()
1936 forbidden_modules := ['os', 'time', 'io', 'dl', 'sha256', 'binary']
1937 mut imports := []ast.ImportStmt{cap: forbidden_modules.len}
1938 for mod_name in forbidden_modules {
1939 imports << ast.ImportStmt{
1940 name: mod_name
1941 }
1942 }
1943 mut files := [
1944 ast.File{
1945 mod: 'main'
1946 name: 'main.v'
1947 imports: imports
1948 stmts: [
1949 ast.Stmt(ast.FnDecl{
1950 name: 'main'
1951 typ: ast.FnType{}
1952 pos: minimal_pos(320)
1953 }),
1954 ]
1955 },
1956 ]
1957 for i, mod_name in forbidden_modules {
1958 files << ast.File{
1959 mod: mod_name
1960 name: '${mod_name}.v'
1961 stmts: [
1962 ast.Stmt(ast.FnDecl{
1963 name: 'init'
1964 typ: ast.FnType{}
1965 pos: minimal_pos(330 + i * 10)
1966 }),
1967 ast.Stmt(ast.FnDecl{
1968 name: '__v_init_consts_${mod_name}'
1969 typ: ast.FnType{}
1970 pos: minimal_pos(331 + i * 10)
1971 }),
1972 ast.Stmt(ast.FnDecl{
1973 name: 'runtime_entry'
1974 typ: ast.FnType{}
1975 pos: minimal_pos(332 + i * 10)
1976 }),
1977 ]
1978 }
1979 }
1980 used := mark_used_with_options(files, env, MarkUsedOptions{
1981 minimal_runtime_roots: true
1982 })
1983
1984 main_key := decl_key('main', files[0].stmts[0] as ast.FnDecl, env)
1985 assert used[main_key]
1986 for file_idx in 1 .. files.len {
1987 mod_name := files[file_idx].mod
1988 init_key := decl_key(mod_name, files[file_idx].stmts[0] as ast.FnDecl, env)
1989 const_init_key := decl_key(mod_name, files[file_idx].stmts[1] as ast.FnDecl, env)
1990 runtime_entry_key := decl_key(mod_name, files[file_idx].stmts[2] as ast.FnDecl, env)
1991 assert !used[init_key], '${mod_name}.init must not be a minimal root'
1992 assert !used[const_init_key], '${mod_name} const init must not be a minimal root'
1993 assert !used[runtime_entry_key], '${mod_name}.runtime_entry must not be a minimal root'
1994 }
1995}
1996