v2 / vlib / v / tests / fns / closure_test.v
427 lines · 383 sloc · 8.01 KB · c629ae348c266474c413968d102d81f47e7b781d
Raw
1import arrays { filter_indexed, flat_map, flat_map_indexed, fold, map_indexed, max }
2
3fn test_decl_assignment() {
4 my_var := 12
5 c1 := fn [my_var] () int {
6 return my_var
7 }
8 assert c1() == 12
9}
10
11fn test_assignment() {
12 v1 := 1
13 mut c := fn [v1] () int {
14 return v1
15 }
16 v2 := 3
17 c = fn [v2] () int {
18 return v2
19 }
20 assert c() == 3
21}
22
23fn call_anon_with_3(f fn (int) int) int {
24 return f(3)
25}
26
27fn test_closure_in_call() {
28 my_var := int(12)
29 r1 := call_anon_with_3(fn [my_var] (add int) int {
30 return my_var + add
31 })
32 assert r1 == 15
33}
34
35fn create_simple_counter() fn () int {
36 mut c := -1
37 return fn [mut c] () int {
38 c++
39 return c
40 }
41}
42
43fn override_stack() {
44 // just create some variables to modify the stack
45 a := 1
46 b := a + 2
47 c := b + 3
48 d := c + 4
49 e := d + 5
50 f := e + 6
51 g := f + 7
52 _ = g
53}
54
55fn test_closure_exit_original_scope() {
56 mut c := create_simple_counter()
57 assert c() == 0
58 override_stack()
59 assert c() == 1
60}
61
62fn test_vars_are_changed() {
63 mut my_var := 1
64 f1 := fn [mut my_var] (expected int) {
65 assert my_var == expected
66 }
67 f1(1)
68 my_var = 3
69 f1(1)
70 my_var = -1
71 f1(1)
72}
73
74struct Counter {
75mut:
76 i u64
77}
78
79fn (mut c Counter) incr() {
80 c.i++
81}
82
83fn (mut c Counter) next() u64 {
84 c.incr()
85 return c.i
86}
87
88fn test_call_methods() {
89 mut c := Counter{}
90 f1 := fn [mut c] () u64 {
91 return c.next()
92 }
93 assert f1() == 1
94 assert f1() == 2
95 c.incr()
96 assert f1() == 3
97}
98
99fn test_call_methods_on_pointer() {
100 mut c := &Counter{}
101 f1 := fn [mut c] () u64 {
102 return c.next()
103 }
104 assert f1() == 1
105 assert f1() == 2
106 c.incr()
107 assert f1() == 4
108}
109
110/*
111fn test_methods_as_variables() {
112 mut c := Counter{}
113 f1 := c.next
114 assert f1() == 1
115 assert f1() == 2
116 c.incr()
117 assert f1() == 3
118}
119*/
120
121/*
122fn takes_callback(get fn () u64) u64 {
123 return get()
124}
125
126fn test_methods_as_callback() {
127 mut c := Counter{}
128 assert takes_callback(c.next) == 1
129}
130*/
131
132struct ZeroSize {}
133
134fn test_zero_size_ctx() {
135 ctx := ZeroSize{}
136 c1 := fn [ctx] () int {
137 return 123
138 }
139 assert c1() == 123
140}
141
142/*
143fn test_go_call_closure() {
144 my_var := 12
145 ch := chan int{}
146 go fn [my_var, ch] () {
147 ch <- my_var
148 }()
149 assert <-ch == 12
150 go fn [ch] (arg int) {
151 ch <- arg
152 }(15)
153 assert <-ch == 15
154}
155*/
156
157fn test_closures_with_ifstmt() {
158 a := 1
159 f := fn [a] (x int) int {
160 if a > x { return 1 } else { return -1 }
161 }
162 g := fn [a] () int {
163 if true {
164 return 1
165 }
166 return 0
167 }
168 assert f(0) == 1
169 assert g() == 1
170}
171
172struct Command {
173 a int = 1234
174 b int = 2345
175 c int = 3456
176 d int = 4567
177 e int = 5678
178}
179
180struct App {}
181
182fn test_larger_closure_parameters() {
183 mut app := &App{}
184 eprintln('app ptr: ${u64(app)}')
185 f := fn [mut app] (cmd Command) u64 {
186 p := u64(app)
187 println('>> p: ${p}')
188 println('>> cmd: ${cmd}')
189 assert cmd.a == 1234
190 assert cmd.b == 2345
191 assert cmd.c == 3456
192 assert cmd.d == 4567
193 assert cmd.e == 5678
194 return p
195 }
196 cmd := Command{}
197 res := f(cmd)
198 println('> res: ${res} | sizeof Command: ${sizeof(Command)}')
199 assert res == u64(app)
200}
201
202fn test_closure_in_for_in_loop() {
203 a := [2, 4, 6, 9]
204 for v in a {
205 func := fn [v] (msg string) string {
206 return '${msg}: ${v}'
207 }
208 res := func('hello')
209 assert res == 'hello: ${v}'
210 // dump(res)
211 }
212}
213
214struct ClosureFilterProduct {
215mut:
216 cross_sell_skus []string
217}
218
219fn test_filter_indexed_closure_capture_of_for_mut_value() {
220 mut products := [ClosureFilterProduct{
221 cross_sell_skus: ['sku-a', 'sku-b']
222 }]
223 for mut product in products {
224 product.cross_sell_skus = filter_indexed[string](product.cross_sell_skus, fn [products, product] (idx int, css string) bool {
225 product_snapshot := '${product}'
226 products_snapshot := '${products}'
227 return idx == 0 && css == 'sku-a' && product_snapshot.len > 0
228 && products_snapshot.len > 0
229 })
230 }
231 assert products[0].cross_sell_skus == ['sku-a']
232}
233
234fn ret_two() (int, int) {
235 return 2, 5
236}
237
238fn test_closure_over_variable_that_is_returned_from_a_multi_value_function() {
239 one, two := ret_two()
240 a := fn [one] () {
241 println(one)
242 }
243 a()
244 println(two)
245}
246
247fn test_cross_var_assign_without_inherited() {
248 f := fn () {
249 mut left := 1
250 mut right := 2
251 left, right = right, left
252 assert left == 2 && right == 1
253 }
254 f()
255}
256
257fn test_cross_var_assign_with_inherited() {
258 mut left := 1
259 mut right := 2
260 f := fn [mut left, mut right] () {
261 left, right = right, left
262 assert left == 2 && right == 1
263 }
264 f()
265}
266
267// for issue 20498
268// test array / string / map as closure params with -autofree
269fn get_func_that_contains_closure() fn () {
270 arr := [1, 2, 3]
271 str := '${'a'}bcabc' // alloc on heap
272 m := {
273 'key1': 'abcabc'
274 'key2': 'abcabc'
275 }
276 return fn [arr, str, m] () {
277 assert arr == [1, 2, 3]
278 assert str == 'abcabc'
279 assert m == {
280 'key1': 'abcabc'
281 'key2': 'abcabc'
282 }
283 }
284}
285
286fn test_array_string_and_map_as_closure_params_with_autofree() {
287 func := get_func_that_contains_closure()
288 func()
289 assert true
290}
291
292// for issue 20208
293// phenomenon: cgen fails when the closure arg is auto_heap and is not reference.
294@[heap]
295struct Abc {
296 value int
297}
298
299@[heap]
300struct Container {
301 abc &Abc = unsafe { nil }
302}
303
304fn (mut c Container) m() fn () {
305 mut cr := &c
306 assert voidptr(c.abc) == voidptr(cr.abc)
307 f := fn [mut cr] () {
308 assert cr.abc.value == 1234
309 }
310 return f
311}
312
313fn test_auto_heap_var_and_non_ptr_as_closure_arg() {
314 mut c := &Container{
315 abc: &Abc{1234}
316 }
317 f := c.m()
318 f()
319 assert true
320}
321
322fn test_nested_closure_capture_context() {
323 data := nested_closure_capture_data()
324 assert nested_closure_capture_trace(data) == nested_closure_capture_trace_ref(data)
325 assert nested_closure_capture_part1(data) == 21
326 part2 := nested_closure_capture_part2(data) or { panic(err) }
327 assert part2 == 8
328}
329
330fn nested_closure_capture_data() [][]int {
331 return [
332 [3, 0, 3, 7, 3],
333 [2, 5, 5, 1, 2],
334 [6, 5, 3, 3, 2],
335 [3, 3, 5, 4, 9],
336 [3, 5, 3, 9, 0],
337 ]
338}
339
340fn nested_closure_capture_trace(data [][]int) []int {
341 return flat_map_indexed[[]int, int](data, fn [data] (y int, row []int) []int {
342 return flat_map_indexed[int, int](row, fn [data, y] (x int, _ int) []int {
343 return flat_map_indexed[[]int, int](nested_closure_grid_cross_walk(data.len, x, y), fn [data, x, y] (i int, grid_walk []int) []int {
344 return grid_walk.map(fn [data, x, y, i] (pos int) int {
345 return if i < 2 {
346 data[y][pos]
347 } else {
348 data[pos][x]
349 }
350 })
351 })
352 })
353 })
354}
355
356fn nested_closure_capture_trace_ref(data [][]int) []int {
357 mut out := []int{}
358 for y, row in data {
359 for x, _ in row {
360 for i, grid_walk in nested_closure_grid_cross_walk(data.len, x, y) {
361 for pos in grid_walk {
362 out << if i < 2 {
363 data[y][pos]
364 } else {
365 data[pos][x]
366 }
367 }
368 }
369 }
370 }
371 return out
372}
373
374fn nested_closure_capture_part1(data [][]int) int {
375 return flat_map_indexed[[]int, bool](data, fn [data] (y int, row []int) []bool {
376 return map_indexed(row, fn [data, y] (x int, height int) bool {
377 return map_indexed(nested_closure_grid_cross_walk(data.len, x, y), fn [data, x, y, height] (i int, grid_walk []int) bool {
378 return grid_walk.map(fn [data, x, y, i] (pos int) int {
379 return if i < 2 {
380 data[y][pos]
381 } else {
382 data[pos][x]
383 }
384 }).all(it < height)
385 }).any(it)
386 }).filter(it)
387 }).len
388}
389
390fn nested_closure_capture_part2(data [][]int) !int {
391 return max(flat_map_indexed[[]int, int](data, fn [data] (y int, row []int) []int {
392 return map_indexed(row, fn [data, y] (x int, height int) int {
393 return fold(map_indexed(nested_closure_grid_cross_walk(data.len, x, y), fn [data, x, y, height] (i int, grid_walk []int) int {
394 mut count := 0
395 heights := grid_walk.map(fn [data, x, y, i] (pos int) int {
396 return if i < 2 {
397 data[y][pos]
398 } else {
399 data[pos][x]
400 }
401 })
402 for h in heights {
403 count++
404 if h >= height {
405 break
406 }
407 }
408 return count
409 }), 1, fn (acc int, elem int) int {
410 return acc * elem
411 })
412 })
413 }))!
414}
415
416fn nested_closure_range(size int, start int) [][]int {
417 return [
418 []int{len: start, init: start - index - 1},
419 []int{len: size - start - 1, init: start + 1 + index},
420 ]
421}
422
423fn nested_closure_grid_cross_walk(size int, start_x int, start_y int) [][]int {
424 return flat_map[int, []int]([start_x, start_y], fn [size] (elem int) [][]int {
425 return nested_closure_range(size, elem)
426 })
427}
428