v4 / vlib / v3 / tests / selective_import_codegen_test.v
827 lines · 682 sloc · 19.51 KB · 288feee4702b35beadb4037d02b96b5c8674156b
Raw
1import os
2
3const selective_import_vexe = @VEXE
4const selective_import_tests_dir = os.dir(@FILE)
5const selective_import_v3_dir = os.dir(selective_import_tests_dir)
6const selective_import_vlib_dir = os.dir(selective_import_v3_dir)
7const selective_import_v3_src = os.join_path(selective_import_v3_dir, 'v3.v')
8
9fn selective_import_build_v3() string {
10 v3_bin := os.join_path(os.temp_dir(), 'v3_selective_import_test_${os.getpid()}')
11 os.rm(v3_bin) or {}
12 build :=
13 os.execute('${selective_import_vexe} -gc none -path "${selective_import_vlib_dir}|@vlib|@vmodules" -o ${v3_bin} ${selective_import_v3_src}')
14 assert build.exit_code == 0, build.output
15 return v3_bin
16}
17
18fn selective_import_write_file(root string, rel string, src string) {
19 path := os.join_path(root, rel)
20 os.mkdir_all(os.dir(path)) or { panic(err) }
21 os.write_file(path, src) or { panic(err) }
22}
23
24fn selective_import_write_project(name string, main_src string) string {
25 return selective_import_write_project_with_extra(name, main_src, {})
26}
27
28fn selective_import_write_project_with_extra(name string, main_src string, extra_files map[string]string) string {
29 root := os.join_path(os.temp_dir(), 'v3_selective_import_${name}_${os.getpid()}')
30 os.rmdir_all(root) or {}
31 selective_import_write_file(root, 'v.mod', "Module { name: 'selective_import_test' }\n")
32 selective_import_write_file(root, 'mymodules/main_functions.v', 'module mymodules
33
34pub fn add_xy(x int, y int) int {
35 return x + y
36}
37
38pub fn hidden_xy(x int, y int) int {
39 return x * 100 + y
40}
41')
42 selective_import_write_file(root, 'mymodules/submodule/sub_functions.v', 'module submodule
43
44pub fn sub_xy(x int, y int) int {
45 return x - y
46}
47')
48 selective_import_write_file(root, 'main.v', main_src)
49 for rel, src in extra_files {
50 selective_import_write_file(root, rel, src)
51 }
52 return root
53}
54
55fn selective_import_compile_run(v3_bin string, name string, main_src string) (string, string) {
56 root := selective_import_write_project(name, main_src)
57 return selective_import_compile_run_root(v3_bin, root)
58}
59
60fn selective_import_compile_run_with_extra(v3_bin string, name string, main_src string, extra_files map[string]string) (string, string) {
61 root := selective_import_write_project_with_extra(name, main_src, extra_files)
62 return selective_import_compile_run_root(v3_bin, root)
63}
64
65fn selective_import_compile_run_root(v3_bin string, root string) (string, string) {
66 bin := os.join_path(root, 'out')
67 compile := os.execute('${v3_bin} ${root} -b c -o ${bin}')
68 assert compile.exit_code == 0, compile.output
69 run := os.execute(bin)
70 assert run.exit_code == 0, run.output
71 generated := os.read_file(bin + '.c') or { panic(err) }
72 return run.output.trim_space(), generated
73}
74
75fn selective_import_compile_bad(v3_bin string, name string, main_src string) string {
76 root := selective_import_write_project(name, main_src)
77 return selective_import_compile_bad_root(v3_bin, name, root)
78}
79
80fn selective_import_compile_bad_with_extra(v3_bin string, name string, main_src string, extra_files map[string]string) string {
81 root := selective_import_write_project_with_extra(name, main_src, extra_files)
82 return selective_import_compile_bad_root(v3_bin, name, root)
83}
84
85fn selective_import_compile_bad_root(v3_bin string, name string, root string) string {
86 bin := os.join_path(root, 'out')
87 compile := os.execute('${v3_bin} ${root} -b c -o ${bin}')
88 assert compile.exit_code != 0, '${name}: compile unexpectedly succeeded: ${compile.output}'
89 assert !compile.output.contains('C compilation failed'), compile.output
90 return compile.output
91}
92
93fn selective_import_ambiguous_modules() map[string]string {
94 return {
95 'a/a.v': 'module a
96
97pub fn hit() int {
98 return 1
99}
100'
101 'b/b.v': 'module b
102
103pub fn hit() int {
104 return 2
105}
106'
107 }
108}
109
110fn selective_import_type_collision_modules() map[string]string {
111 return {
112 'geometry/geometry.v': 'module geometry
113
114pub struct Point {
115pub:
116 x int
117}
118
119pub struct Size {
120pub:
121 w int
122}
123
124pub type ItemId = int
125
126pub enum Mode {
127 on = 7
128 off = 8
129}
130
131@[flag]
132pub enum Perm {
133 a
134 b
135}
136
137pub struct Box[T] {
138pub:
139 value T
140}
141'
142 'pixels/pixels.v': 'module pixels
143
144pub struct Point {
145pub:
146 x int
147}
148
149pub struct Size {
150pub:
151 w int
152}
153
154pub type ItemId = int
155
156pub enum Mode {
157 on = 70
158 off = 80
159}
160
161@[flag]
162pub enum Perm {
163 a
164 c
165 b
166}
167
168pub struct Box[T] {
169pub:
170 other T
171}
172'
173 }
174}
175
176fn test_selective_import_calls_module_and_submodule_functions() {
177 v3_bin := selective_import_build_v3()
178 output, generated := selective_import_compile_run(v3_bin, 'positive', 'module main
179
180import mymodules { add_xy }
181import mymodules.submodule { sub_xy }
182
183fn main() {
184 println(int_str(add_xy(2, 3)))
185 println(int_str(sub_xy(10, 7)))
186}
187')
188 assert output == '5\n3'
189 assert generated.contains('mymodules__add_xy(2, 3)'), generated
190 assert generated.contains('submodule__sub_xy(10, 7)'), generated
191}
192
193fn test_selective_import_inside_generic_clone_keeps_source_file_symbol() {
194 v3_bin := selective_import_build_v3()
195 output, generated := selective_import_compile_run_with_extra(v3_bin,
196 'generic_clone_selective_import', 'module main
197
198import worker
199
200fn main() {
201 println(int_str(worker.use_add[int](0, 2, 3)))
202}
203', {
204 'worker/worker.v': 'module worker
205
206import mymodules { add_xy }
207import other
208
209pub fn use_add[T](marker T, x int, y int) int {
210 _ = marker
211 return add_xy(x, y)
212}
213'
214 'other/other.v': 'module other
215
216pub fn add_xy(x int, y int) int {
217 return x * 100 + y
218}
219'
220 })
221 assert output == '5'
222 assert generated.contains('mymodules__add_xy(x, y)'), generated
223 assert !generated.contains('other__add_xy(x, y)'), generated
224}
225
226fn test_selective_import_explicit_generic_call_keeps_selected_symbol() {
227 v3_bin := selective_import_build_v3()
228 output, generated := selective_import_compile_run_with_extra(v3_bin,
229 'explicit_generic_selective_import', 'module main
230
231import util { id }
232import other
233
234fn main() {
235 println(int_str(id[int](1)))
236}
237', {
238 'util/util.v': 'module util
239
240pub fn id[T](x T) T {
241 return x
242}
243'
244 'other/other.v': 'module other
245
246pub fn id[T](x T) T {
247 return x
248}
249'
250 })
251 assert output == '1'
252 assert generated.contains('util__id_T_v_int(1)'), generated
253 assert !generated.contains('other__id_T_v_int(1)'), generated
254}
255
256fn test_selective_import_fn_value_inside_generic_clone_keeps_source_file_symbol() {
257 v3_bin := selective_import_build_v3()
258 output, generated := selective_import_compile_run_with_extra(v3_bin,
259 'generic_clone_selective_import_fn_value', 'module main
260
261import worker
262
263fn main() {
264 println(int_str(worker.use_add_cb[int](0, 2, 3)))
265}
266', {
267 'worker/worker.v': 'module worker
268
269import mymodules { add_xy }
270import other
271
272fn takes(cb fn (int, int) int, x int, y int) int {
273 return cb(x, y)
274}
275
276pub fn use_add_cb[T](marker T, x int, y int) int {
277 _ = marker
278 return takes(add_xy, x, y)
279}
280'
281 'other/other.v': 'module other
282
283pub fn add_xy(x int, y int) int {
284 return x * 100 + y
285}
286'
287 })
288 assert output == '5'
289 assert generated.contains('worker__takes(mymodules__add_xy, x, y)'), generated
290 assert !generated.contains('worker__takes(add_xy, x, y)'), generated
291 assert !generated.contains('worker__takes(other__add_xy, x, y)'), generated
292}
293
294fn test_selective_import_type_inside_generic_clone_signature_keeps_source_file_symbol() {
295 v3_bin := selective_import_build_v3()
296 output, generated := selective_import_compile_run_with_extra(v3_bin,
297 'generic_clone_selective_import_type_signature', 'module main
298
299import worker
300
301fn main() {
302 p := worker.make_point[int](7)
303 println(int_str(worker.take_point[int](p, 2) + p.x))
304}
305', {
306 'worker/worker.v': 'module worker
307
308import geometry { Point }
309import pixels
310
311pub fn make_point[T](x T) Point {
312 _ = x
313 return Point{
314 x: 3
315 }
316}
317
318pub fn take_point[T](p Point, x T) int {
319 _ = x
320 return p.x + 4
321}
322'
323 'geometry/geometry.v': 'module geometry
324
325pub struct Point {
326pub:
327 x int
328}
329'
330 'pixels/pixels.v': 'module pixels
331
332pub struct Point {
333pub:
334 x int
335}
336'
337 })
338 assert output == '10'
339 assert generated.contains('geometry__Point worker__make_point_T_v_int(int x)'), generated
340 assert generated.contains('int worker__take_point_T_v_int(geometry__Point p, int x)'), generated
341 assert !generated.contains('\nPoint worker__make_point_T_v_int(int x)'), generated
342 assert !generated.contains('\npixels__Point worker__make_point_T_v_int(int x)'), generated
343 assert !generated.contains('\nint worker__take_point_T_v_int(Point p, int x)'), generated
344 assert !generated.contains('\nint worker__take_point_T_v_int(pixels__Point p, int x)'), generated
345}
346
347fn test_selective_import_symbol_can_be_used_as_function_value() {
348 v3_bin := selective_import_build_v3()
349 output, generated := selective_import_compile_run(v3_bin, 'fn_value', 'module main
350
351import mymodules { add_xy }
352
353fn main() {
354 f := add_xy
355 println(int_str(f(2, 3)))
356}
357')
358 assert output == '5'
359 assert generated.contains('mymodules__add_xy'), generated
360 assert generated.contains('f(2, 3)'), generated
361 assert !generated.contains('int f = mymodules__add_xy'), generated
362}
363
364fn test_selective_import_function_value_roots_exact_symbol_with_imported_homonym() {
365 v3_bin := selective_import_build_v3()
366 output, generated := selective_import_compile_run_with_extra(v3_bin,
367 'fn_value_imported_homonym', 'module main
368
369import a { choose }
370import b
371
372fn main() {
373 f := choose
374 println(int_str(f()))
375}
376', {
377 'a/a.v': 'module a
378
379pub fn choose() int {
380 return 11
381}
382'
383 'b/b.v': 'module b
384
385pub fn choose() int {
386 return 99
387}
388'
389 })
390 assert output == '11'
391 assert generated.contains('a__choose'), generated
392 assert !generated.contains('b__choose'), generated
393}
394
395fn test_selective_import_does_not_import_other_symbols_by_suffix() {
396 v3_bin := selective_import_build_v3()
397 output := selective_import_compile_bad(v3_bin, 'hidden_symbol', 'module main
398
399import mymodules { add_xy }
400
401fn main() {
402 println(int_str(hidden_xy(2, 3)))
403}
404')
405 assert output.contains('unknown function `hidden_xy`'), output
406}
407
408fn test_selective_import_scope_is_file_local() {
409 v3_bin := selective_import_build_v3()
410 output := selective_import_compile_bad_with_extra(v3_bin, 'file_local', 'module main
411
412import mymodules { add_xy }
413
414fn main() {
415 println(int_str(add_xy(1, 2)))
416}
417', {
418 'other.v': 'module main
419
420fn from_other_file() int {
421 return add_xy(3, 4)
422}
423'
424 })
425 assert output.contains('unknown function `add_xy`'), output
426}
427
428fn test_selective_import_same_symbol_from_two_modules_is_ambiguous() {
429 v3_bin := selective_import_build_v3()
430 output := selective_import_compile_bad_with_extra(v3_bin, 'ambiguous_symbol', 'module main
431
432import a { hit }
433import b { hit }
434
435fn main() {
436 println(int_str(hit()))
437}
438',
439 selective_import_ambiguous_modules())
440 assert output.contains('ambiguous selective import `hit`'), output
441}
442
443fn test_duplicate_selective_import_fails_even_when_unused() {
444 v3_bin := selective_import_build_v3()
445 output := selective_import_compile_bad_with_extra(v3_bin, 'ambiguous_unused', 'module main
446
447import a { hit }
448import b { hit }
449
450fn main() {
451 println("ok")
452}
453',
454 selective_import_ambiguous_modules())
455 assert output.contains('ambiguous selective import `hit`'), output
456}
457
458fn test_duplicate_selective_import_fails_even_with_local_homonym() {
459 v3_bin := selective_import_build_v3()
460 output := selective_import_compile_bad_with_extra(v3_bin, 'ambiguous_local_homonym', 'module main
461
462import a { hit }
463import b { hit }
464
465fn hit() int {
466 return 3
467}
468
469fn main() {
470 println(int_str(hit()))
471}
472',
473 selective_import_ambiguous_modules())
474 assert output.contains('ambiguous selective import `hit`'), output
475}
476
477fn test_duplicate_selective_import_function_value_reports_ambiguous() {
478 v3_bin := selective_import_build_v3()
479 output := selective_import_compile_bad_with_extra(v3_bin, 'ambiguous_fn_value', 'module main
480
481import a { hit }
482import b { hit }
483
484fn main() {
485 f := hit
486 println(int_str(f()))
487}
488',
489 selective_import_ambiguous_modules())
490 assert output.contains('ambiguous selective import `hit`'), output
491 assert !output.contains('unknown identifier `hit`'), output
492}
493
494fn test_local_function_keeps_priority_over_selective_import_homonym() {
495 v3_bin := selective_import_build_v3()
496 output, generated := selective_import_compile_run(v3_bin, 'local_homonym', 'module main
497
498import mymodules { add_xy }
499
500fn add_xy(x int, y int) int {
501 return x * y
502}
503
504fn main() {
505 println(int_str(add_xy(2, 3)))
506}
507')
508 assert output == '6'
509 assert generated.contains('int add_xy(int x, int y)'), generated
510 assert generated.contains('int__str(add_xy(2, 3))'), generated
511 assert !generated.contains('int__str(mymodules__add_xy(2, 3))'), generated
512}
513
514fn test_module_homonym_function_signature_uses_module_key() {
515 v3_bin := selective_import_build_v3()
516 output, generated := selective_import_compile_run_with_extra(v3_bin,
517 'module_homonym_signature', 'module main
518
519import foo
520
521fn value() int {
522 return 7
523}
524
525fn main() {
526 println(int_str(value()))
527 println(foo.value())
528}
529', {
530 'foo/foo.v': 'module foo
531
532pub fn value() string {
533 return "foo"
534}
535'
536 })
537 assert output == '7\nfoo'
538 assert generated.contains('int value(void);'), generated
539 assert generated.contains('string foo__value(void);'), generated
540 assert generated.contains('string foo__value(void) {'), generated
541 assert !generated.contains('int foo__value(void);'), generated
542}
543
544fn test_selective_import_with_module_alias_keeps_symbol_authority() {
545 v3_bin := selective_import_build_v3()
546 output, generated := selective_import_compile_run(v3_bin, 'alias', 'module main
547
548import mymodules as mm { add_xy }
549
550fn main() {
551 println(int_str(add_xy(4, 5)))
552}
553')
554 assert output == '9'
555 assert generated.contains('mymodules__add_xy(4, 5)'), generated
556 assert !generated.contains('mm__add_xy'), generated
557}
558
559fn test_selective_import_resolves_struct_collision() {
560 v3_bin := selective_import_build_v3()
561 output, generated := selective_import_compile_run_with_extra(v3_bin, 'struct_collision', 'module main
562
563import geometry { Point }
564import pixels
565
566fn main() {
567 p := Point{x: 7}
568 println(int_str(p.x))
569}
570',
571 selective_import_type_collision_modules())
572 assert output == '7'
573 assert generated.contains('geometry__Point p ='), generated
574 assert !generated.contains('pixels__Point p ='), generated
575}
576
577fn test_selective_import_resolves_generic_struct_collision() {
578 v3_bin := selective_import_build_v3()
579 output, generated := selective_import_compile_run_with_extra(v3_bin,
580 'generic_struct_collision', 'module main
581
582import geometry { Box }
583import pixels
584
585fn main() {
586 b := Box[int]{value: 7}
587 p := pixels.Box[int]{other: 8}
588 println(int_str(b.value + p.other))
589}
590',
591 selective_import_type_collision_modules())
592 assert output == '15'
593 assert generated.contains('struct geometry__Box_int'), generated
594 assert generated.contains('struct pixels__Box_int'), generated
595 assert generated.contains('geometry__Box_int b ='), generated
596 assert generated.contains('pixels__Box_int p ='), generated
597}
598
599fn test_selective_import_resolves_generic_struct_field_from_decl_file() {
600 v3_bin := selective_import_build_v3()
601 extra := {
602 'types/types.v': 'module types
603
604pub struct Thing {
605pub:
606 v int
607}
608'
609 'other/other.v': 'module other
610
611pub struct Thing {
612pub:
613 v int
614}
615'
616 'box/box.v': 'module box
617
618import types { Thing }
619
620pub struct Box[T] {
621pub:
622 thing Thing
623 value T
624}
625'
626 }
627 output, generated := selective_import_compile_run_with_extra(v3_bin,
628 'generic_struct_field_selective_import', 'module main
629
630import box { Box }
631import other
632
633fn main() {
634 b := Box[int]{value: 7}
635 o := other.Thing{v: 8}
636 println(int_str(b.value + o.v))
637}
638',
639 extra)
640 assert output == '15'
641 assert generated.contains('struct box__Box_int'), generated
642 assert generated.contains('types__Thing thing;'), generated
643 assert !generated.contains('box__Thing thing;'), generated
644 assert !generated.contains('other__Thing thing;'), generated
645}
646
647fn test_selective_import_resolves_generic_struct_method_signature_from_decl_file() {
648 v3_bin := selective_import_build_v3()
649 extra := {
650 'types/types.v': 'module types
651
652pub struct Thing {
653pub:
654 v int
655}
656'
657 'other/other.v': 'module other
658
659pub struct Thing {
660pub:
661 v int
662}
663'
664 'box/box.v': 'module box
665
666import types { Thing }
667
668pub struct Box[T] {
669pub:
670 thing Thing
671 value T
672}
673
674pub fn (b Box[T]) combine(thing Thing) int {
675 return b.value + thing.v
676}
677'
678 }
679 output, generated := selective_import_compile_run_with_extra(v3_bin,
680 'generic_struct_method_signature_selective_import', 'module main
681
682import box { Box }
683import other
684
685fn main() {
686 b := Box[int]{value: 7}
687 _ := other.Thing{v: 1}
688 println(int_str(b.combine(b.thing)))
689}
690',
691 extra)
692 assert output == '7'
693 assert generated.contains('int box__Box_int__combine(box__Box_int b, types__Thing thing)'), generated
694 assert !generated.contains('int box__Box_int__combine(box__Box_int b, box__Thing thing)'), generated
695 assert !generated.contains('int box__Box_int__combine(box__Box_int b, other__Thing thing)'), generated
696}
697
698fn test_selective_import_resolves_alias_collision() {
699 v3_bin := selective_import_build_v3()
700 output, _ := selective_import_compile_run_with_extra(v3_bin, 'alias_collision', 'module main
701
702import geometry { ItemId }
703import pixels
704
705fn value(id ItemId) int {
706 return int(id)
707}
708
709fn main() {
710 println(int_str(value(ItemId(9))))
711}
712',
713 selective_import_type_collision_modules())
714 assert output == '9'
715}
716
717fn test_selective_import_resolves_enum_collision() {
718 v3_bin := selective_import_build_v3()
719 output, generated := selective_import_compile_run_with_extra(v3_bin, 'enum_collision', 'module main
720
721import geometry { Mode }
722import pixels
723
724fn is_on(mode Mode) bool {
725 return mode == .on
726}
727
728fn main() {
729 if is_on(.on) {
730 println("on")
731 }
732}
733',
734 selective_import_type_collision_modules())
735 assert output == 'on'
736 assert generated.contains('return mode == 7;'), generated
737 assert generated.contains('is_on(7)'), generated
738 assert !generated.contains('return mode == 70;'), generated
739 assert !generated.contains('is_on(70)'), generated
740}
741
742fn test_selective_import_resolves_flag_enum_collision() {
743 v3_bin := selective_import_build_v3()
744 output, generated := selective_import_compile_run_with_extra(v3_bin, 'flag_enum_collision', 'module main
745
746import geometry { Perm }
747import pixels
748
749fn main() {
750 m := Perm.a | .b
751 println(int_str(int(m)))
752}
753',
754 selective_import_type_collision_modules())
755 assert output == '3'
756 assert generated.contains('int m = 1 | 2;'), generated
757 assert !generated.contains('int m = 1 | 4;'), generated
758 assert !generated.contains('Perm.a'), generated
759}
760
761fn test_duplicate_selective_import_type_fails() {
762 v3_bin := selective_import_build_v3()
763 output := selective_import_compile_bad_with_extra(v3_bin, 'ambiguous_type', 'module main
764
765import geometry { Point }
766import pixels { Point }
767
768fn main() {
769 p := Point{x: 1}
770 println(int_str(p.x))
771}
772',
773 selective_import_type_collision_modules())
774 assert output.contains('ambiguous selective import `Point`'), output
775}
776
777fn test_duplicate_selective_import_enum_selector_fails() {
778 v3_bin := selective_import_build_v3()
779 output := selective_import_compile_bad_with_extra(v3_bin, 'ambiguous_enum_selector', 'module main
780
781import geometry { Mode }
782import pixels { Mode }
783
784fn main() {
785 mode := Mode.on
786 println(int_str(int(mode)))
787}
788',
789 selective_import_type_collision_modules())
790 assert output.contains('ambiguous selective import `Mode`'), output
791}
792
793fn test_selective_import_enum_selector_keeps_selected_authority() {
794 v3_bin := selective_import_build_v3()
795 output := selective_import_compile_bad_with_extra(v3_bin, 'enum_selector_authority', 'module main
796
797import geometry { Mode }
798import pixels
799
800fn takes_pixel(mode pixels.Mode) {}
801
802fn main() {
803 takes_pixel(Mode.on)
804}
805',
806 selective_import_type_collision_modules())
807 assert output.contains('cannot use `geometry.Mode` as argument 1 to `takes_pixel`; expected `pixels.Mode`'), output
808}
809
810fn test_unselected_type_from_selective_import_is_not_resolved_by_suffix() {
811 v3_bin := selective_import_build_v3()
812 output := selective_import_compile_bad_with_extra(v3_bin, 'unselected_type', 'module main
813
814import geometry { Point }
815import pixels
816
817fn use_size(s Size) int {
818 return s.w
819}
820
821fn main() {
822 println("ok")
823}
824',
825 selective_import_type_collision_modules())
826 assert output.contains('unknown type `Size`'), output
827}
828