v / vlib / v2 / gen / cleanc / array_append_codegen_test.v
485 lines · 423 sloc · 11.35 KB · a7153322629091f3f2d79f0bf318d0fb2c13c425
Raw
1// vtest build: macos
2module cleanc
3
4import os
5import v2.parser
6import v2.pref as vpref
7import v2.token
8import v2.transformer
9import v2.types
10
11fn generate_array_append_c_for_test(code string) string {
12 return generate_array_append_c_for_test_files([code])
13}
14
15fn generate_array_append_c_for_test_files(sources []string) string {
16 tmp_dir := os.join_path(os.temp_dir(), 'v2_array_append_codegen_test_${os.getpid()}')
17 os.rmdir_all(tmp_dir) or {}
18 os.mkdir_all(tmp_dir) or { panic('failed to create temp dir') }
19 defer {
20 os.rmdir_all(tmp_dir) or {}
21 }
22 mut paths := []string{cap: sources.len}
23 for i, code in sources {
24 tmp_file := os.join_path(tmp_dir, 'file_${i}.v')
25 os.write_file(tmp_file, code) or { panic('failed to write temp file') }
26 paths << tmp_file
27 }
28 prefs := &vpref.Preferences{
29 backend: .cleanc
30 no_parallel: true
31 }
32 mut file_set := token.FileSet.new()
33 mut par := parser.Parser.new(prefs)
34 files := par.parse_files(paths, mut file_set)
35 env := types.Environment.new()
36 mut checker := types.Checker.new(prefs, file_set, env)
37 checker.check_files(files)
38 mut trans := transformer.Transformer.new_with_pref(env, prefs)
39 mut gen := Gen.new_with_env_and_pref(trans.transform_files(files), env, prefs)
40 return gen.gen()
41}
42
43fn test_generate_c_uses_push_many_for_array_slice_append() {
44 csrc := generate_array_append_c_for_test('
45fn main() {
46 mut dst := []u8{}
47 haystack := [u8(1), 2, 3]
48 dst << haystack[1..3]
49}
50')
51 assert csrc.contains('array__push_many(')
52 assert !csrc.contains('&(u8[1]){array__slice(haystack, 1, 3)}')
53}
54
55fn test_generate_c_uses_push_many_for_array_literal_append() {
56 csrc := generate_array_append_c_for_test('
57fn main() {
58 mut dst := []u8{}
59 dst << [u8(`$`)]
60}
61')
62 assert csrc.contains('array__push_many(')
63 assert !csrc.contains('&(u8[1]){new_array_from_c_array(')
64}
65
66fn test_generate_c_uses_push_many_for_local_array_append() {
67 csrc := generate_array_append_c_for_test('
68fn source() []u8 {
69 return [u8(1), 2]
70}
71
72fn main() {
73 mut dst := []u8{}
74 src := source()
75 dst << src
76}
77')
78 assert csrc.contains('array__push_many(')
79 assert !csrc.contains('&(u8[1]){src}')
80}
81
82fn test_generate_c_push_many_to_mut_array_param_uses_param_pointer() {
83 csrc := generate_array_append_c_for_test('
84fn extend(mut dst []usize, src []usize) {
85 dst << src
86}
87')
88 assert csrc.contains('array__push_many(')
89 assert csrc.contains('array__push_many(((array*)(dst)),')
90 assert !csrc.contains('array__push_many((array*)&dst,')
91 assert !csrc.contains('array__push_many(((array*)(&dst)),')
92}
93
94fn test_generate_c_lowered_push_many_to_mut_array_param_uses_param_pointer() {
95 csrc := generate_array_append_c_for_test('
96struct Index {
97 entries map[string][]usize
98}
99
100fn (idx Index) matches_into(candidate string, mut matches []usize) {
101 if hits := idx.entries[candidate] {
102 matches << hits
103 }
104}
105')
106 assert csrc.contains('array__push_many(')
107 assert csrc.contains('array__push_many(((array*)(matches)),')
108 assert !csrc.contains('array__push_many((array*)&matches,')
109 assert !csrc.contains('array__push_many(((array*)(&matches)),')
110}
111
112fn test_generate_c_uses_push_many_for_fn_pointer_array_return_append() {
113 csrc := generate_array_append_c_for_test('
114fn source(data []u8) []u8 {
115 return data
116}
117
118fn consume(hash_func fn ([]u8) []u8) {
119 mut dst := []u8{}
120 src := hash_func(dst)
121 dst << src
122}
123
124fn main() {
125 consume(source)
126}
127')
128 assert csrc.contains('array__push_many(')
129 assert !csrc.contains('&(u8[1]){src}')
130}
131
132fn test_generate_c_keeps_bitwise_or_inside_array_append_value() {
133 csrc := generate_array_append_c_for_test('
134fn main() {
135 mut bytes := []u8{}
136 n1 := u8(1)
137 n0 := u8(2)
138 bytes << (n1 << 4) | n0
139}
140')
141 assert csrc.contains('array__push(')
142 assert csrc.contains('| n0')
143 assert !csrc.contains('array__push_many(')
144 assert !csrc.contains('_arr_push_many_tmp_')
145 assert !csrc.contains('}) | n0')
146}
147
148fn test_generate_c_wraps_mut_u8_array_param_bitwise_append_value() {
149 csrc := generate_array_append_c_for_test('
150fn write_int(mut out []u8, value u64, high_bits u8) {
151 out << high_bits | u8(value)
152}
153')
154 assert csrc.contains('array__push(((array*)(out)), &(u8[1]){')
155 assert !csrc.contains('array__push(((array*)(out)), (high_bits | ((u8)(value))))')
156}
157
158fn test_generate_c_appends_for_mut_pointer_value_to_array() {
159 csrc := generate_array_append_c_for_test('
160struct PullRequest {
161mut:
162 repo_name string
163}
164
165fn copy(mut prs []PullRequest) []PullRequest {
166 mut out := []PullRequest{}
167 for mut pr in prs {
168 pr.repo_name = "x"
169 out << pr
170 }
171 return out
172}
173')
174 assert csrc.contains('array__push(((array*)(&out)), pr);')
175 assert !csrc.contains('array__push(((array*)(&out)), &(PullRequest[1]){pr});')
176}
177
178fn test_generate_c_indexes_local_array_that_shadows_function_name() {
179 csrc := generate_array_append_c_for_test('
180fn bytes() []u8 {
181 return []u8{}
182}
183
184fn main() {
185 mut bytes := [u8(1)]
186 bytes[0] &= u8(3)
187}
188')
189 assert csrc.contains('((u8*)bytes.data)[((int)(0))] &= ((u8)(3));')
190 assert !csrc.contains('bytes &= ((u8)(3));')
191}
192
193fn test_generate_c_uses_typed_zero_for_array_tuple_if_declarations() {
194 csrc := generate_array_append_c_for_test('
195fn pick(operand_a []u64, operand_b []u64) {
196 mut a, mut b := if operand_a.len >= operand_b.len {
197 operand_a, operand_b
198 } else {
199 operand_b, operand_a
200 }
201 _ = a
202 _ = b
203}
204')
205 assert csrc.contains('Array_u64 a = ((Array_u64){0});')
206 assert csrc.contains('Array_u64 b = ((Array_u64){0});')
207 assert !csrc.contains('Array_u64 a = 0;')
208 assert !csrc.contains('Array_u64 b = 0;')
209}
210
211fn test_generate_c_uses_default_init_for_empty_array_struct_fields() {
212 csrc := generate_array_append_c_for_test('
213struct Pool {
214 items []voidptr
215}
216
217fn main() {
218 pool := Pool{
219 items: []
220 }
221 _ = pool.items.len
222}
223 ')
224 assert csrc.contains('.items = __new_array_with_default_noscan(0, 0, sizeof(void*)')
225 assert !csrc.contains('.items = new_array_from_c_array(0, 0, sizeof(void*)')
226}
227
228fn test_generate_c_specializes_generic_pointer_array_push_literal() {
229 csrc := generate_array_append_c_for_test('
230struct Foo {}
231
232fn get_results_ref[T](items []voidptr) []&T {
233 mut res := []&T{cap: items.len}
234 for i in 0 .. items.len {
235 res << unsafe { &T(items[i]) }
236 }
237 return res
238}
239
240fn main() {
241 items := []voidptr{}
242 res := get_results_ref[Foo](items)
243 _ = res.len
244}
245 ')
246 assert csrc.contains('(Foo*[1]){((Foo*)')
247 assert !csrc.contains('(T*[1])')
248 assert !csrc.contains('stdatomic__T')
249}
250
251fn test_generate_c_uses_captured_mut_array_pointer_for_closure_push() {
252 csrc := generate_array_append_c_for_test('
253fn consume(cb fn (int) bool) {
254 _ = cb(1)
255}
256
257fn main() {
258 mut values := []int{}
259 consume(fn [mut values] (value int) bool {
260 values << value
261 return true
262 })
263}
264 ')
265 assert csrc.contains('Array_int* values = _anon_fn_')
266 assert csrc.contains('array__push(((array*)(values)),')
267 assert !csrc.contains('array__push(((array*)(&values)),')
268}
269
270fn test_generate_c_updates_captured_mut_scalar_for_closure_postfix() {
271 csrc := generate_array_append_c_for_test('
272fn consume(cb fn () bool) {
273 _ = cb()
274}
275
276fn main() {
277 mut count := u64(0)
278 consume(fn [mut count] () bool {
279 count++
280 return true
281 })
282}
283 ')
284 assert csrc.contains('u64* count = _anon_fn_')
285 assert csrc.contains('(*count)++')
286 assert !csrc.contains('count++;')
287}
288
289fn test_generate_c_evaluates_generic_comptime_is_against_implements() {
290 csrc := generate_array_append_c_for_test('
291interface Writer {
292 write([]u8) !int
293}
294
295struct Buffer implements Writer {}
296
297fn (mut b Buffer) write(buf []u8) !int {
298 _ = b
299 return buf.len
300}
301
302fn choose[T]() int {
303 $if T is Writer {
304 return 1
305 } $else {
306 return 2
307 }
308}
309
310fn main() {
311 _ = choose[Buffer]()
312}
313 ')
314 assert csrc.contains('return 1;')
315 assert !csrc.contains('return 2;')
316}
317
318fn test_generate_c_specializes_comptime_pointer_field_generic_call_by_field_type() {
319 csrc := generate_array_append_c_for_test('
320fn struct_field_should_encode[T](val T) bool {
321 _ = val
322 return true
323}
324
325fn encode_fields[T](val T) bool {
326 mut ok := true
327 $for field in T.fields {
328 ok = struct_field_should_encode(val.$(field.name))
329 }
330 return ok
331}
332
333struct Node {
334 next &Node = unsafe { nil }
335}
336
337fn main() {
338 _ = encode_fields(Node{})
339}
340')
341 assert csrc.contains('struct_field_should_encode_T_Nodeptr(val.next)')
342 assert !csrc.contains('struct_field_should_encode_T_Node((*val.next))')
343}
344
345fn test_generate_c_specializes_comptime_generic_pointer_field_by_field_type() {
346 csrc := generate_array_append_c_for_test('
347module json2
348
349struct ValueInfo {}
350
351struct Node[T] {
352 value T
353 next &Node[T] = unsafe { nil }
354}
355
356struct Decoder {
357 current_node &Node[ValueInfo] = unsafe { nil }
358}
359
360struct EncoderFieldInfo {}
361
362struct Encoder {}
363
364fn (mut encoder Encoder) encode_value[T](val T) {
365 $if T is $pointer {
366 encoder.encode_value(*val)
367 } $else $if T is $struct {
368 _ = encoder.encode_fields[T](val)
369 }
370}
371
372fn (mut encoder Encoder) cached_field_infos[T]() []EncoderFieldInfo {
373 return []EncoderFieldInfo{}
374}
375
376fn (mut encoder Encoder) encode_struct_field_value[T](val T) {
377 $if T.indirections == 1 {
378 encoder.encode_value(*val)
379 } $else {
380 encoder.encode_value(val)
381 }
382}
383
384fn struct_field_should_encode[T](field_info EncoderFieldInfo, val T) bool {
385 _ = field_info
386 _ = val
387 return true
388}
389
390fn (mut encoder Encoder) encode_fields[T](val T) bool {
391 field_infos := encoder.cached_field_infos[T]()
392 mut ok := true
393 mut i := 0
394 $for field in T.fields {
395 field_info := field_infos[i]
396 ok = struct_field_should_encode(field_info, val.$(field.name))
397 encoder.encode_struct_field_value(val.$(field.name))
398 i++
399 }
400 return ok
401}
402
403fn use_encoder() {
404 mut encoder := Encoder{}
405 _ = encoder.encode_fields(Decoder{})
406 _ = encoder.encode_fields(Node[ValueInfo]{})
407}
408')
409 assert csrc.contains('json2__struct_field_should_encode_T_json2_Nodeptr(field_info, val.current_node)')
410 assert csrc.contains('json2__Encoder__encode_struct_field_value_T_json2_Nodeptr(encoder, val.current_node)')
411 assert csrc.contains('json2__Encoder__cached_field_infos_T_json2_Decoder(encoder)')
412 assert csrc.contains('Array_json2__EncoderFieldInfo json2__Encoder__cached_field_infos_T_json2_Node(json2__Encoder* encoder)')
413 assert !csrc.contains('json2__struct_field_should_encode_T_json2_Node(field_info, (*val.current_node))')
414 assert !csrc.contains('json2__Encoder__encode_struct_field_value_T_json2_Node(encoder, (*val.current_node))')
415}
416
417fn test_generate_c_extracts_array_sum_variant_for_generic_array_param() {
418 csrc := generate_array_append_c_for_test_files([
419 '
420module orm
421
422pub struct InfixType {
423 right int
424}
425
426pub type Primitive = InfixType | []InfixType | int
427
428pub fn tenant_filter_array_primitive_type[T](value []T) int {
429 return value.len
430}
431
432pub fn tenant_filter_primitive_type(value Primitive) int {
433 return match value {
434 InfixType {
435 2
436 }
437 []InfixType {
438 tenant_filter_array_primitive_type(value)
439 }
440 int {
441 3
442 }
443 }
444}
445',
446 '
447module main
448
449import orm
450
451fn main() {
452 _ = orm.tenant_filter_primitive_type([]orm.InfixType{})
453}
454',
455 ])
456 assert csrc.contains('orm__tenant_filter_array_primitive_type_T_orm_InfixType(((((Array_orm__InfixType*)(value._data._Array_orm__InfixType))')
457 assert !csrc.contains('orm__tenant_filter_array_primitive_type_T_orm_InfixType(((((orm__InfixType*)(value._data._InfixType))')
458}
459
460fn test_generate_c_lowers_global_interface_generic_method_call() {
461 csrc := generate_array_append_c_for_test('
462interface Rng {
463 intn(max int) !int
464}
465
466__global default_rng &Rng
467
468fn (mut rng Rng) element[T](array []T) !T {
469 if array.len == 0 {
470 return error("empty")
471 }
472 return array[rng.intn(array.len)!]
473}
474
475fn element[T](array []T) !T {
476 return default_rng.element[T](array)
477}
478
479fn main() {
480 _ = element[u8]([u8(1)]) or { u8(0) }
481}
482')
483 assert csrc.contains('Rng__element_T_u8(default_rng, _v_array)')
484 assert !csrc.contains('u8 _val = (_v_array)')
485}
486