v4 / vlib / v / tests / skip_unused / generic_fn_instantiation_pruning_test.v
274 lines · 267 sloc · 7.3 KB · 1bd23ec35474c798d48348dac2aad8cf246188d4
Raw
1import os
2
3const vexe = @VEXE
4
5fn test_skip_unused_prunes_unused_generic_fn_instantiations() {
6 tmp_dir := os.join_path(os.vtmp_dir(), 'v_issue_26019')
7 os.mkdir_all(tmp_dir) or { panic(err) }
8 defer {
9 os.rmdir_all(tmp_dir) or {}
10 }
11 source_path := os.join_path(tmp_dir, 'issue_26019.v')
12 source := [
13 'module main',
14 '',
15 'fn new[T]() map[u8]T {',
16 '\tx := map[u8]T{}',
17 '\treturn x',
18 '}',
19 '',
20 'fn not_used_fn() map[u8]int {',
21 '\tx := new[int]()',
22 '\treturn x',
23 '}',
24 '',
25 'fn main() {',
26 '\tx := new[u32]()',
27 '\tdump(x)',
28 '}',
29 ].join('\n')
30 os.write_file(source_path, source) or { panic(err) }
31 res := os.execute('${os.quoted_path(vexe)} -o - ${os.quoted_path(source_path)}')
32 if res.exit_code != 0 {
33 panic(res.output)
34 }
35 assert res.output.contains('VV_LOC Map_u8_u32 main__new_T_u32(void)')
36 assert !res.output.contains('VV_LOC Map_u8_int main__new_T_int(void)')
37}
38
39fn test_skip_unused_keeps_generic_offsetof_struct_instantiations() {
40 tmp_dir := os.join_path(os.vtmp_dir(), 'v_issue_27283_generic_offsetof')
41 os.mkdir_all(tmp_dir) or { panic(err) }
42 defer {
43 os.rmdir_all(tmp_dir) or {}
44 }
45 source_path := os.join_path(tmp_dir, 'issue_27283_generic_offsetof.v')
46 binary_path := os.join_path(tmp_dir, 'issue_27283_generic_offsetof')
47 source := [
48 'module main',
49 '',
50 'struct Box[T] {',
51 '\ta int',
52 '\tb T',
53 '}',
54 '',
55 'fn off[T]() u32 {',
56 '\treturn __offsetof(Box[T], b)',
57 '}',
58 '',
59 'fn main() {',
60 '\tprintln(off[int]())',
61 '\tprintln(off[string]())',
62 '}',
63 ].join('\n')
64 os.write_file(source_path, source) or { panic(err) }
65 res :=
66 os.execute('${os.quoted_path(vexe)} -skip-unused -o ${os.quoted_path(binary_path)} ${os.quoted_path(source_path)}')
67 if res.exit_code != 0 {
68 panic(res.output)
69 }
70}
71
72fn test_skip_unused_does_not_emit_impl_methods_for_interface_extensions() {
73 tmp_dir := os.join_path(os.vtmp_dir(), 'v_skip_unused_interface_extension_collision')
74 os.mkdir_all(tmp_dir) or { panic(err) }
75 defer {
76 os.rmdir_all(tmp_dir) or {}
77 }
78 source_path := os.join_path(tmp_dir, 'interface_extension_collision.v')
79 source := [
80 'module main',
81 '',
82 'import crypto.internal.subtle',
83 '',
84 'interface Elem {}',
85 '',
86 'struct Thing {}',
87 '',
88 'struct Holder {',
89 '\titems []Elem',
90 '}',
91 '',
92 'fn (el Elem) equal(_ Elem) bool {',
93 '\treturn true',
94 '}',
95 '',
96 'fn (t Thing) equal(_ Thing) bool {',
97 '\treturn subtle.constant_time_compare([u8(1)], [u8(1)]) == 1',
98 '}',
99 '',
100 'fn (h Holder) ok() bool {',
101 '\tfor i, item in h.items {',
102 '\t\tfor j, obj in h.items {',
103 '\t\t\tif i == j {',
104 '\t\t\t\treturn item.equal(obj)',
105 '\t\t\t}',
106 '\t\t}',
107 '\t}',
108 '\treturn false',
109 '}',
110 '',
111 'fn main() {',
112 '\th := Holder{items: [Elem(Thing{}), Elem(Thing{})]}',
113 '\tprintln(h.ok())',
114 '}',
115 ].join('\n')
116 os.write_file(source_path, source) or { panic(err) }
117 res := os.execute('${os.quoted_path(vexe)} -o - ${os.quoted_path(source_path)}')
118 if res.exit_code != 0 {
119 panic(res.output)
120 }
121 assert res.output.contains('VV_LOC bool main__Elem_equal(')
122 assert !res.output.contains('main__Thing_equal(')
123}
124
125fn test_skip_unused_keeps_json2_embedded_struct_decode_helpers() {
126 tmp_dir := os.join_path(os.vtmp_dir(), 'v_issue_26928')
127 os.mkdir_all(tmp_dir) or { panic(err) }
128 defer {
129 os.rmdir_all(tmp_dir) or {}
130 }
131 source_path := os.join_path(tmp_dir, 'issue_26928.v')
132 source := [
133 'module main',
134 '',
135 'import time',
136 'import x.json2',
137 '',
138 'struct Meta {',
139 '\tcreated_at ?time.Time',
140 '}',
141 '',
142 'struct Req {',
143 '\tMeta',
144 '\tname string',
145 '}',
146 '',
147 'fn main() {',
148 '\t_ := json2.decode[Req](\'{"name":"x"}\') or { panic(err) }',
149 '}',
150 ].join('\n')
151 os.write_file(source_path, source) or { panic(err) }
152 res := os.execute('${os.quoted_path(vexe)} -w -o - ${os.quoted_path(source_path)}')
153 if res.exit_code != 0 {
154 panic(res.output)
155 }
156 assert res.output.contains('x__json2__decode_struct_key_T_main__Req')
157 assert res.output.contains('x__json2__check_required_struct_fields_T_main__Req')
158 // the `?time.Time` payload of the embedded `Meta` is decoded through a generic
159 // instantiation reachable only via comptime `$for field` codegen; skip-unused
160 // must keep it. Assert the payload decoder itself rather than a specific json2
161 // helper name, so the test does not break when those helpers are refactored.
162 assert res.output.contains('x__json2__Decoder_decode_value_T_time__Time')
163}
164
165fn test_skip_unused_marks_dependencies_inside_generic_anon_fns() {
166 tmp_dir := os.join_path(os.vtmp_dir(), 'v_generic_anon_fn_dependencies')
167 os.mkdir_all(tmp_dir) or { panic(err) }
168 defer {
169 os.rmdir_all(tmp_dir) or {}
170 }
171 source_path := os.join_path(tmp_dir, 'generic_anon_fn_dependencies.v')
172 binary_path := os.join_path(tmp_dir, 'generic_anon_fn_dependencies')
173 source := [
174 'module main',
175 '',
176 'struct Holder[T] {',
177 '\tdata []T',
178 '}',
179 '',
180 'fn (h &Holder[T]) nmap[T](others []&Holder[T], f fn ([]T) T) T {',
181 '\treturn f([h.data[0], others[0].data[0]])',
182 '}',
183 '',
184 'fn (a &Holder[T]) subtract[T](b &Holder[T]) T {',
185 '\treturn a.nmap([b], fn [T] (xs []T) T {',
186 '\t\tx := xs[0]',
187 '\t\ty := xs[1]',
188 '\t\t$if T is string {',
189 "\t\t\treturn x.replace(y, '')",
190 '\t\t} $else {',
191 '\t\t\treturn x - y',
192 '\t\t}',
193 '\t})',
194 '}',
195 '',
196 'fn unused_string() {',
197 "\ta := &Holder[string]{data: ['abc']}",
198 "\tb := &Holder[string]{data: ['b']}",
199 '\tprintln(a.subtract(b))',
200 '}',
201 '',
202 'fn main() {',
203 '\ta := &Holder[int]{data: [1]}',
204 '\tb := &Holder[int]{data: [2]}',
205 '\tprintln(a.subtract(b))',
206 '}',
207 ].join('\n')
208 os.write_file(source_path, source) or { panic(err) }
209 res :=
210 os.execute('${os.quoted_path(vexe)} -d no_backtrace -o ${os.quoted_path(binary_path)} ${os.quoted_path(source_path)}')
211 if res.exit_code != 0 {
212 panic(res.output)
213 }
214}
215
216fn test_skip_unused_keeps_generic_next_for_for_in_when_direct_calls_use_other_types() {
217 // Regression for https://github.com/vlang/v/issues/27147 .
218 // A `for in` over a generic iterator instantiation (e.g. `Range[big.Integer]`)
219 // must emit the matching `next` instantiation, even when other code calls
220 // methods on a different instantiation of the same generic struct directly
221 // (e.g. `iter.next()` on a `Range[int]`).
222 tmp_dir := os.join_path(os.vtmp_dir(), 'v_issue_27147')
223 os.mkdir_all(tmp_dir) or { panic(err) }
224 defer {
225 os.rmdir_all(tmp_dir) or {}
226 }
227 source_path := os.join_path(tmp_dir, 'issue_27147.v')
228 binary_path := os.join_path(tmp_dir, 'issue_27147')
229 source := [
230 'module main',
231 '',
232 'import math.big',
233 '',
234 'struct Range[T] {',
235 '\tstart T',
236 '\tend T',
237 '\tstep T',
238 'mut:',
239 '\tcur T',
240 '}',
241 '',
242 'pub fn (mut r Range[T]) next() ?T {',
243 '\tif r.cur > r.end {',
244 '\t\treturn none',
245 '\t}',
246 '\tdefer { r.cur += r.step }',
247 '\treturn r.cur',
248 '}',
249 '',
250 'pub fn (mut r Range[T]) reset() {',
251 '\tr.cur = r.start',
252 '}',
253 '',
254 'pub fn range[T](start T, end T, step T) Range[T] {',
255 '\treturn Range[T]{start: start, end: end, step: step, cur: start}',
256 '}',
257 '',
258 'fn main() {',
259 '\tmut iter := range(0, 5, 1)',
260 '\titer.reset()',
261 '\titer.next()',
262 '\tend := big.integer_from_int(3)',
263 '\tfor i in range[big.Integer](big.zero_int, end, big.one_int) {',
264 '\t\tprintln(i)',
265 '\t}',
266 '}',
267 ].join('\n')
268 os.write_file(source_path, source) or { panic(err) }
269 res :=
270 os.execute('${os.quoted_path(vexe)} -o ${os.quoted_path(binary_path)} ${os.quoted_path(source_path)}')
271 if res.exit_code != 0 {
272 panic(res.output)
273 }
274}
275