vq / vlib / v / tests / usecache_interface_index_symbol_test.v
177 lines · 160 sloc · 5.12 KB · 0b1ee86f742567f2fcb3c534b0a49a6dddba36ab
Raw
1// vtest build: !windows
2import os
3
4const vexe = @VEXE
5
6// Regression test for https://github.com/vlang/v/issues/27330
7//
8// With -usecache, modules like `builtin` are compiled separately in
9// build_module mode, where the interface type-table index is emitted as
10// `extern const u32 ..._index;` and referenced. The main program must therefore
11// provide a real, externally-linked `const u32 ..._index = N;` definition - a
12// compile-time `enum` constant has no linker symbol, so the reference would be
13// undefined at link time (e.g. `undefined symbol: _IError_None___index` on
14// FreeBSD/clang).
15fn test_usecache_interface_index_is_real_symbol() {
16 tmp_dir := os.join_path(os.vtmp_dir(), 'v_issue_27330')
17 os.mkdir_all(tmp_dir) or { panic(err) }
18 defer {
19 os.rmdir_all(tmp_dir) or {}
20 }
21 source_path := os.join_path(tmp_dir, 'issue_27330.v')
22 os.write_file(source_path, "fn main() {\n\tprintln('hello world')\n}\n") or { panic(err) }
23
24 // -o - dumps the generated C of the main program to stdout.
25 res := os.execute('${os.quoted_path(vexe)} -usecache -o - ${os.quoted_path(source_path)}')
26 if res.exit_code != 0 {
27 panic(res.output)
28 }
29 // The index must be a real (externally-linked) definition, not a bare enum.
30 // The separate enum keeps an integer constant expression available for C
31 // contexts like switch case labels.
32 assert res.output.contains('enum { _IError_None___index_enum =')
33 assert res.output.contains('const u32 _IError_None___index = _IError_None___index_enum;')
34 assert !res.output.contains('enum { _IError_None___index =')
35
36 // Sanity check: without -usecache the compile-time enum form is kept (it is
37 // the tcc-friendly form and needs no external symbol in a single build).
38 res2 := os.execute('${os.quoted_path(vexe)} -o - ${os.quoted_path(source_path)}')
39 if res2.exit_code != 0 {
40 panic(res2.output)
41 }
42 assert res2.output.contains('enum { _IError_None___index =')
43}
44
45fn test_usecache_shared_interface_lock_uses_enum_index_in_case_labels() {
46 tmp_dir := os.join_path(os.vtmp_dir(), 'v_issue_27330_shared')
47 os.mkdir_all(tmp_dir) or { panic(err) }
48 defer {
49 os.rmdir_all(tmp_dir) or {}
50 }
51 source_path := os.join_path(tmp_dir, 'shared_interface.v')
52 os.write_file(source_path, '
53interface MyInterface {
54 foo() string
55}
56
57struct MyStruct {
58pub mut:
59 fooer shared MyInterface
60}
61
62struct MyImplementor {
63mut:
64 num int
65}
66
67fn (m MyImplementor) foo() string {
68 return "Hello World!"
69}
70
71fn main() {
72 shared imp := MyImplementor{
73 num: 1
74 }
75 s := MyStruct{
76 fooer: imp
77 }
78 lock s.fooer {
79 println(s.fooer.foo())
80 }
81}
82') or {
83 panic(err)
84 }
85
86 res := os.execute('${os.quoted_path(vexe)} -usecache -o - ${os.quoted_path(source_path)}')
87 if res.exit_code != 0 {
88 panic(res.output)
89 }
90 assert res.output.contains('enum { _main__MyInterface_main__MyImplementor_index_enum =')
91 assert res.output.contains('const u32 _main__MyInterface_main__MyImplementor_index = _main__MyInterface_main__MyImplementor_index_enum;')
92 assert res.output.contains('case _main__MyInterface_main__MyImplementor_index_enum:')
93 assert !res.output.contains('case _main__MyInterface_main__MyImplementor_index:')
94}
95
96fn test_usecache_build_module_shared_interface_lock_uses_canonical_index_symbol() {
97 root := os.join_path(os.vtmp_dir(), 'v_issue_27330_shared_module_${os.getpid()}')
98 cache_dir := os.join_path(root, '.cache')
99 vtmp_dir := os.join_path(root, '.vtmp')
100 os.rmdir_all(root) or {}
101 defer {
102 os.rmdir_all(root) or {}
103 }
104 os.mkdir_all(os.join_path(root, 'maker'))!
105 os.mkdir_all(vtmp_dir)!
106 os.write_file(os.join_path(root, 'v.mod'),
107 "Module {\n\tname: 'v_issue_27330_shared_module'\n}\n") or { panic(err) }
108 os.write_file(os.join_path(root, 'maker', 'maker.v'), '
109module maker
110
111interface MyInterface {
112 foo() string
113}
114
115struct MyStruct {
116pub mut:
117 fooer shared MyInterface
118}
119
120struct MyImplementor {
121mut:
122 num int
123}
124
125fn (m MyImplementor) foo() string {
126 return "Hello World!"
127}
128
129pub fn exercise() {
130 shared imp := MyImplementor{
131 num: 1
132 }
133 s := MyStruct{
134 fooer: imp
135 }
136 lock s.fooer {
137 println(s.fooer.foo())
138 }
139}
140') or {
141 panic(err)
142 }
143
144 old_vcache := os.getenv_opt('VCACHE') or { '' }
145 old_vtmp := os.getenv_opt('VTMP') or { '' }
146 os.setenv('VCACHE', cache_dir, true)
147 os.setenv('VTMP', vtmp_dir, true)
148 defer {
149 if old_vcache.len == 0 {
150 os.unsetenv('VCACHE')
151 } else {
152 os.setenv('VCACHE', old_vcache, true)
153 }
154 if old_vtmp.len == 0 {
155 os.unsetenv('VTMP')
156 } else {
157 os.setenv('VTMP', old_vtmp, true)
158 }
159 }
160 mut p := os.new_process(vexe)
161 p.set_work_folder(root)
162 p.set_args(['-keepc', 'build-module', 'maker'])
163 p.set_redirect_stdio()
164 p.wait()
165 stdout := p.stdout_slurp()
166 stderr := p.stderr_slurp()
167 exit_code := p.code
168 p.close()
169 assert exit_code == 0, '${stdout}${stderr}'
170 generated_c_path := os.join_path(vtmp_dir, 'maker.tmp.c')
171 assert os.exists(generated_c_path)
172 generated_c := os.read_file(generated_c_path)!
173 assert generated_c.contains('extern const u32 _maker__MyInterface_maker__MyImplementor_index;')
174 assert generated_c.contains('if (x->val._typ == _maker__MyInterface_maker__MyImplementor_index) {')
175 assert !generated_c.contains('enum { _maker__MyInterface_maker__MyImplementor_index_enum =')
176 assert !generated_c.contains('case _maker__MyInterface_maker__MyImplementor_index_enum:')
177}
178