v2 / vlib / v / slow_tests / valgrind / 1.strings_and_arrays.v
448 lines · 396 sloc · 8.08 KB · 8e35f4d9848f7ad35d857a187dddbfd2eca5e19d
Raw
1import strings
2
3// This program is built and run via Valgrind to ensure there are no leaks with -autofree
4fn simple() {
5 nums := [1, 2, 3] // local array must be freed
6 println(nums)
7 nums_copy := nums.clone() // array assignments call .clone()
8 println(nums_copy)
9 name := 'Peter' // string literals mustn't be freed
10 str_inter := 'hello, ${name}' // concatenated strings must be freed
11 println(str_inter)
12 // nums.free() // this should result in a double free and a CI error
13 if true {
14 // test the freeing of local vars in a new scope
15 nums2 := [4, 5, 6]
16 str_inter2 := 'hello, ${name}'
17 println(str_inter2)
18 println(nums2)
19 }
20 arr := return_array([])
21 println(arr)
22}
23
24fn return_array(array_arg []string) []int { // array argument must not be freed
25 s := [1, 2, 3] // escaping array must not be freed
26 return s
27}
28
29fn return_option(array_arg []string) ?Foo { // array argument must not be freed
30 s := get_foo()? // escaping option must not be freed
31 return s
32}
33
34fn get_foo() ?Foo {
35 return Foo{}
36}
37
38fn handle_strings(s string, p string) int {
39 return 0
40}
41
42fn handle_int(n int) {
43}
44
45fn add_strings(a string, b string) string {
46 return a + b
47}
48
49fn str_tmp_expr() {
50 println('a' + 'b') // tmp expression result must be freed
51 handle_strings('c' + 'd', 'e' + 'f') // multiple tmp expressions must be freed
52 handle_int(handle_strings('x' + 'y', 'f')) // exprs 2 levels deep must bee freed
53 handle_strings('1', add_strings('2', '3')) // test a fn that returns a string
54}
55
56fn str_tmp_expr_advanced() {
57 // t1 = 'c' + 'd'
58 // t2 = 'e + f'
59 // t3 = add_strings(t2, 'g')
60 // handle_strings(t1, t3)
61 // t1.free()
62 // t2.free()
63 // t3.free()
64 //
65 handle_strings('c' + 'd', add_strings('e' + 'f', 'g')) // both lvl 1 and lvl2 exprs must be freed
66}
67
68fn str_tmp_expr_advanced_var_decl() {
69 a :=
70 handle_strings('c' + 'd', add_strings('e' + 'f', 'g')) // both lvl 1 and lvl2 exprs must be freed
71 println(a)
72}
73
74struct Foo {
75 a int
76 b string
77}
78
79fn str_inter() {
80 a := 10
81 println('a = ${a}')
82 // foo := Foo{10, 'x' + 'x'}
83 // println('foo = ${foo}') // TODO
84}
85
86fn str_replace() {
87 mut s := 'hello world'
88 s =
89 s.replace('hello', 'hi') // s can't be freed as usual before the assignment, since it's used in the right expr
90 println(s)
91
92 mut s2 := 'aa' + 'bb'
93 s2 = s2.replace('a', 'c')
94 println(s2)
95 /*
96 r := s.replace('hello', 'hi')
97 cloned := s.replace('hello', 'hi').clone()
98 cloned2 := r.clone()
99 println(s)
100 println(r)
101 */
102}
103
104fn fooo(s string) {
105}
106
107fn str_replace2() {
108 mut s := 'hello world'
109 s = s.replace('hello', 'hi').replace('world', 'planet')
110 println(s)
111}
112
113fn reassign_str() {
114 mut z := '1' + '2'
115 if true {
116 println('KEK')
117 z = 'foo'
118 }
119 mut x := 'a'
120 x = 'b' // nothing has to be freed here
121
122 mut s := 'a' + 'b'
123 s = 'x' + 'y' // 'a' + 'b' must be freed before the re-assignment
124 s = s + '!' // old s ref must be copied and freed after the assignment, since s is still used in the right expr
125 println(z)
126 println(x)
127 println(s)
128}
129
130struct Foo2 {
131mut:
132 nums []int
133}
134
135fn reassign_arr() {
136 mut x := [1, 2, 3]
137 // x must be freed before the re-assignment
138 x = [4, 5, 6]
139 mut foo := Foo2{[10, 20, 30]}
140 foo.nums = [40, 50, 60] // same with struct fields
141 foo.nums = [70, 80, 90]
142 println(x)
143 // TODO: remove this once structs are freed automatically
144 unsafe {
145 foo.nums.free()
146 }
147}
148
149fn match_expr() string {
150 x := 2
151 res := match x {
152 1 { 'one' }
153 2 { 'two' }
154 else { 'unknown' }
155 }
156
157 return res
158}
159
160fn opt(s string) ?int {
161 return 1
162}
163
164fn option_str() {
165 q := 'select'
166 s := 'query: select'
167 // option fn args must be freed
168 pos2 := opt('query:${q}') or {
169 // pos := s.index('query: ${q}') or {
170 println('exiting')
171 return
172 }
173 println(pos2 + 1)
174 // option method args must be freed
175 pos := s.index('query: ${q}') or {
176 println('exiting')
177 return
178 }
179 println(pos + 1)
180 // test assigning an option to an existing var
181 mut p := 0
182 for {
183 p = opt('query:${q}') or { break }
184 break
185 }
186 println(p)
187}
188
189fn return_error_with_freed_expr() !string {
190 if true {
191 msg := 'oops'
192 return error('hm ${msg}')
193 }
194 return 'ok'
195}
196
197fn option_return() {
198 return_error_with_freed_expr() or { return }
199}
200
201fn handle_string(s string) bool {
202 return true
203}
204
205fn if_cond() {
206 // handle_string('1' + '2')
207 if handle_string('1' + '2') {
208 // if '1' + '2' == '12' {
209 println('yes')
210 } else {
211 println('no')
212 }
213}
214
215fn addition_with_tmp_expr() {
216 x := 1 + handle_strings('a' + 'b', 'c')
217 println(x)
218}
219
220fn tt() {
221 // time.parse_rfc2822('1234')
222}
223
224fn get_string(s string) string {
225 return s.clone() // TODO: handle returning the argument without clone()
226}
227
228fn if_expr() string {
229 a := if true { get_string('a' + 'b') } else { get_string('c' + 'd') }
230 return a
231}
232
233fn return_if_expr() string {
234 return if true { get_string('a' + 'b') } else { get_string('c' + 'd') }
235}
236
237fn loop_map() {
238 m := {
239 'UK': 'London'
240 'France': 'Paris'
241 }
242 // keys must be freed
243 for country, capital in m {
244 println(country + capital)
245 }
246}
247
248fn free_map() {
249 nums := [1, 2, 3]
250 println(nums)
251 /*
252 nums2 := nums.map(it + handle_strings('a' + 'b', 'c'))
253 println(nums2)
254 */
255}
256
257fn free_inside_opt_block() {
258 x := opt('a' + 'b') or {
259 get_string('c' + 'd') // c+d must be freed before a+b
260 return
261 }
262 println(x)
263}
264
265fn free_before_return() {
266 s := 'a' + 'b'
267 println(s)
268 if true {
269 return
270 }
271}
272
273fn free_before_return_bool() bool {
274 s := 'a' + 'b'
275 println(s)
276 return true
277}
278
279fn free_before_break() {
280 s := 'a' + 'b'
281 println(s)
282 for {
283 q := [1, 2, 3]
284 println(q)
285 break
286 }
287 for {
288 aa := [1, 2, 3]
289 println(aa)
290 if true {
291 // breaking should free only vars in the closest for loop's scope
292 // `qq`, not `s`
293 break
294 }
295 // nested 1
296 for {
297 bb := [4, 5, 6]
298 println(bb)
299 if true {
300 break
301 }
302 // nested 2
303 for {
304 cc := [7, 8, 9]
305 println(cc)
306 if true {
307 if true {
308 break
309 }
310 break
311 }
312 }
313 }
314 }
315 mut i := 0
316 for {
317 i++
318 qq := [1, 2, 3]
319 println(qq)
320 if i > 10 {
321 break
322 }
323 if true {
324 continue
325 }
326 }
327 x := ['1', '2', '3']
328 for n in x {
329 f := 'f'
330 println('${n} => ${f}')
331 }
332}
333
334struct User {
335 name string
336 age int
337}
338
339fn free_array_except_returned_element() {
340 user := get_user()
341 println(user)
342}
343
344fn get_user() User {
345 users := [User{'Peter', 25}, User{'Alice', 21}]
346 user := users[0] // has to be cloned, since `users` are going to be freed at the end of the function
347 return user
348}
349
350fn get_user2() User {
351 users := [User{'Peter', 25}, User{'Alice', 21}]
352 return users[0] // has to be cloned, since `users` are going to be freed at the end of the function
353}
354
355fn string_array_get() {
356 s := ['a', 'b', 'c']
357 x := s[0]
358 println(x)
359 println(s)
360}
361
362fn comptime_if() {
363 // compif pos used to be 0, if it was the first statement in a block, vars wouldn't be freed
364 $if macos {
365 println('macos')
366 }
367 s := 'a' + 'b'
368 println(s)
369}
370
371fn anon_fn() {
372}
373
374fn return_sb_str() string {
375 mut sb := strings.new_builder(100)
376 sb.write_string('hello')
377 return sb.str() // sb should be freed, but only after .str() is called
378}
379
380fn parse_header0(s string) !string {
381 if !s.contains(':') {
382 return error('missing colon in header')
383 }
384 words := s.split_nth(':', 2)
385 x := words[0]
386 return x
387}
388
389fn parse_header1(s string) !string {
390 if !s.contains(':') {
391 return error('missing colon in header')
392 }
393 words := s.split_nth(':', 2)
394 return words[0]
395}
396
397// TODO: remove this @[manualfree] tag
398@[manualfree]
399fn advanced_options() {
400 s := parse_header0('foo:bar') or { return }
401 s2 := parse_header1('foo:bar') or { return }
402 _ := s.len + s2.len // avoid warning for unused variables
403 // TODO: fix -autofree, so that it adds this free automatically:
404 unsafe { s.free() }
405 unsafe { s2.free() }
406}
407
408fn main() {
409 println('start')
410 simple()
411 reassign_str()
412 reassign_arr()
413 str_tmp_expr()
414 str_tmp_expr_advanced()
415 str_tmp_expr_advanced_var_decl()
416 str_inter()
417 match_expr()
418 option_str()
419 // option_return()
420 str_replace()
421 str_replace2()
422 if_cond()
423 addition_with_tmp_expr()
424 q := if_expr()
425 println(q)
426 s := return_if_expr()
427 println(s)
428 free_inside_opt_block()
429 comptime_if()
430 free_before_return()
431 free_before_return_bool()
432 free_before_break()
433 s2 := return_sb_str()
434 println(s2)
435 // free_map()
436 // loop_map()
437 advanced_options()
438 free_array_except_returned_element()
439 println('end')
440}
441
442/*
443s := s.replace().replace()
444tmp := s.replace()
445s.free()
446s = tmp.replace()
447tmp.free()
448*/
449