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