v4 / vlib / v3 / gen / c / array.v
557 lines · 538 sloc · 16.32 KB · 351709f1031b64c6175612e9e459d8216eeca7f5
Raw
1module c
2
3import v3.flat
4import v3.types
5
6// array_like_type supports array like type handling for c.
7fn array_like_type(t types.Type) ?types.Array {
8 if t is types.Array {
9 return t
10 }
11 if t is types.Alias {
12 base := t.base_type
13 if base is types.Array {
14 return base
15 }
16 }
17 return none
18}
19
20// array_fixed_type supports array fixed type handling for c.
21fn array_fixed_type(t types.Type) ?types.ArrayFixed {
22 if t is types.ArrayFixed {
23 return t
24 }
25 if t is types.Alias {
26 base := t.base_type
27 if base is types.ArrayFixed {
28 return base
29 }
30 }
31 return none
32}
33
34fn fixed_array_index_info(t types.Type) (bool, bool, types.ArrayFixed) {
35 if fixed := array_fixed_type(t) {
36 return true, false, fixed
37 }
38 if t is types.Pointer {
39 if fixed := array_fixed_type(t.base_type) {
40 return true, true, fixed
41 }
42 }
43 return false, false, types.ArrayFixed{}
44}
45
46// gen_array_literal_value emits array literal value output for c.
47fn (mut g FlatGen) gen_array_literal_value(node flat.Node, elem_type types.Type) {
48 c_elem := g.tc.c_type(elem_type)
49 count := node.children_count
50 if count == 0 {
51 g.write('array_new(sizeof(${c_elem}), 0, 0)')
52 return
53 }
54 g.write('new_array_from_c_array(${count}, ${count}, sizeof(${c_elem}), (${c_elem}[]){')
55 for i in 0 .. count {
56 if i > 0 {
57 g.write(', ')
58 }
59 // Emit each element against the concrete element type, not the enclosing
60 // `expected_expr_type` (which is the whole array). A bare generic struct element
61 // (`Box{..}` in a `[]Box[int]` literal) otherwise sees the array type, fails the
62 // `generic_struct_init_instance_type` array skip, and is emitted as the bare `Box`
63 // while the array storage is `Box_int` — incompatible C.
64 g.gen_expr_with_expected_type(g.a.child(&node, i), elem_type)
65 }
66 g.write('})')
67}
68
69fn (mut g FlatGen) gen_array_literal_ptr_arg(node flat.Node, elem_type types.Type) {
70 c_elem := g.tc.c_type(elem_type)
71 g.write('(${c_elem}[]){')
72 for i in 0 .. node.children_count {
73 if i > 0 {
74 g.write(', ')
75 }
76 g.gen_expr_with_expected_type(g.a.child(&node, i), elem_type)
77 }
78 if node.children_count == 0 {
79 g.write('0')
80 }
81 g.write('}')
82}
83
84fn (mut g FlatGen) gen_pointer_arg_from_array_literal(node flat.Node, expected types.Type) bool {
85 if node.kind != .array_literal {
86 return false
87 }
88 if expected is types.Pointer {
89 g.gen_array_literal_ptr_arg(node, expected.base_type)
90 return true
91 }
92 return false
93}
94
95// gen_fixed_array_data_arg emits fixed array data arg output for c.
96fn (mut g FlatGen) gen_fixed_array_data_arg(id flat.NodeId, arr types.ArrayFixed) {
97 node := g.a.nodes[int(id)]
98 if node.kind == .array_literal {
99 c_elem := g.tc.c_type(arr.elem_type)
100 g.write('(${c_elem}[]){')
101 for i in 0 .. node.children_count {
102 if i > 0 {
103 g.write(', ')
104 }
105 g.gen_expr(g.a.child(&node, i))
106 }
107 g.write('}')
108 return
109 }
110 if node.kind == .postfix && node.children_count > 0 {
111 child_id := g.a.child(&node, 0)
112 child := g.a.nodes[int(child_id)]
113 if child.kind == .array_literal {
114 g.gen_fixed_array_data_arg(child_id, arr)
115 return
116 }
117 }
118 // A fixed-array value (e.g. `[4]u8` color) is sometimes represented as a dynamic
119 // `Array`; a C fixed-array parameter decays to `elem*`, so pass the data pointer.
120 if g.tc.resolve_type(id) is types.Array {
121 elem_ct := g.tc.c_type(arr.elem_type)
122 g.write('(${elem_ct}*)(')
123 g.gen_expr(id)
124 g.write(').data')
125 return
126 }
127 g.gen_expr(id)
128}
129
130// gen_array_push_many_stmt emits array push many stmt output for c.
131fn (mut g FlatGen) gen_array_push_many_stmt(lhs_id flat.NodeId, rhs_id flat.NodeId) {
132 lhs_is_ptr := g.tc.resolve_type(lhs_id) is types.Pointer
133 amp := if lhs_is_ptr { '' } else { '&' }
134 rhs_type := types.unwrap_pointer(g.tc.resolve_type(rhs_id))
135 if rhs_fixed := array_fixed_type(rhs_type) {
136 g.write('array_push_many_ptr(${amp}')
137 gen_expr_lvalue(mut g, lhs_id)
138 g.write(', ')
139 g.gen_fixed_array_data_arg(rhs_id, rhs_fixed)
140 len_expr := g.fixed_array_len_value(rhs_fixed)
141 g.writeln(', ${len_expr});')
142 return
143 }
144 tmp := g.tmp_name()
145 g.write('{ Array ${tmp} = ')
146 g.gen_expr(rhs_id)
147 g.writeln(';')
148 g.write('array__push_many(${amp}')
149 gen_expr_lvalue(mut g, lhs_id)
150 g.writeln(', ${tmp}.data, ${tmp}.len); }')
151}
152
153// gen_slice_expr emits slice expr output for c.
154fn (mut g FlatGen) gen_slice_expr(node flat.Node, base_id flat.NodeId, base_type types.Type) {
155 start_node := g.a.child_node(&node, 1)
156 has_start := start_node.kind != .empty
157 has_end := node.children_count > 2
158 base_str := g.expr_to_string(base_id)
159 is_array, is_ptr, _ := array_index_info(base_type)
160 is_fixed_array, fixed_is_ptr, fixed := fixed_array_index_info(base_type)
161 start_str := if has_start { g.expr_to_string(g.a.child(&node, 1)) } else { '0' }
162 end_str := if has_end {
163 g.expr_to_string(g.a.child(&node, 2))
164 } else if is_fixed_array {
165 g.fixed_array_len_value(fixed)
166 } else if is_array && is_ptr {
167 '${base_str}->len'
168 } else {
169 '${base_str}.len'
170 }
171 if base_type is types.String {
172 g.write('string__substr(${base_str}, ${start_str}, ${end_str})')
173 } else if is_fixed_array {
174 c_elem := g.tc.c_type(fixed.elem_type)
175 data_str := if fixed_is_ptr { '(*${base_str})' } else { base_str }
176 // Evaluate the slice bounds once so side-effecting expressions such as
177 // `arr[i++..limit()]` are not run multiple times in the generated C.
178 start_tmp := g.tmp_name()
179 count_tmp := g.tmp_name()
180 g.write('({ int ${start_tmp} = (${start_str}); int ${count_tmp} = (${end_str}) - ${start_tmp}; new_array_from_c_array(${count_tmp}, ${count_tmp}, sizeof(${c_elem}), &${data_str}[${start_tmp}]); })')
181 } else if is_array {
182 arr_str := if is_ptr { '*${base_str}' } else { base_str }
183 g.write('array_slice(${arr_str}, ${start_str}, ${end_str})')
184 } else {
185 g.write('string__substr(${base_str}, ${start_str}, ${end_str})')
186 }
187}
188
189// gen_array_method_call emits array method call output for c.
190fn (mut g FlatGen) gen_array_method_call(node flat.Node, fn_node &flat.Node, arr types.Array) {
191 c_elem := g.value_c_type(arr.elem_type)
192 base_id := g.a.child(fn_node, 0)
193 base_node := g.a.nodes[int(base_id)]
194 is_ptr := if base_node.kind == .ident {
195 g.tc.resolve_type(base_id) is types.Pointer
196 } else {
197 false
198 }
199 dot := if is_ptr { '->' } else { '.' }
200 match fn_node.value {
201 'clone' {
202 g.write('array__clone(')
203 if !is_ptr {
204 g.write('&')
205 }
206 g.gen_expr(base_id)
207 g.write(')')
208 }
209 'last' {
210 g.write('*(${c_elem}*)array_get(')
211 g.gen_expr(base_id)
212 g.write(', ')
213 g.gen_expr(base_id)
214 g.write('.len - 1)')
215 }
216 'first' {
217 g.write('*(${c_elem}*)array_get(')
218 g.gen_expr(base_id)
219 g.write(', 0)')
220 }
221 'delete_last' {
222 g.gen_expr(base_id)
223 g.write('${dot}len--')
224 }
225 'pop' {
226 g.write('({ ${c_elem} _pop${g.tmp_count} = *(${c_elem}*)array_get(')
227 g.gen_expr(base_id)
228 g.write(', ')
229 g.gen_expr(base_id)
230 g.write('${dot}len - 1); ')
231 g.gen_expr(base_id)
232 g.write('${dot}len--; _pop${g.tmp_count}; })')
233 g.tmp_count++
234 }
235 'clear' {
236 g.gen_expr(base_id)
237 g.write('${dot}len = 0')
238 }
239 'push_many' {
240 amp := if is_ptr { '' } else { '&' }
241 g.write('array_push_many_ptr(${amp}')
242 g.gen_expr(base_id)
243 g.write(', ')
244 g.gen_expr(g.a.child(&node, 1))
245 g.write(', ')
246 g.gen_expr(g.a.child(&node, 2))
247 g.write(')')
248 }
249 'repeat' {
250 g.write('array__repeat_to_depth(')
251 g.gen_expr(base_id)
252 g.write(', ')
253 g.gen_expr(g.a.child(&node, 1))
254 g.write(', 0)')
255 }
256 'repeat_to_depth' {
257 g.write('array__repeat_to_depth(')
258 g.gen_expr(base_id)
259 g.write(', ')
260 g.gen_expr(g.a.child(&node, 1))
261 g.write(', ')
262 g.gen_expr(g.a.child(&node, 2))
263 g.write(')')
264 }
265 'trim' {
266 g.gen_expr(base_id)
267 g.write('${dot}len = ')
268 g.gen_expr(g.a.child(&node, 1))
269 }
270 'ensure_cap' {
271 amp := if is_ptr { '' } else { '&' }
272 g.write('array_ensure_cap(${amp}')
273 g.gen_expr(base_id)
274 g.write(', ')
275 g.gen_expr(g.a.child(&node, 1))
276 g.write(')')
277 }
278 'delete' {
279 amp := if is_ptr { '' } else { '&' }
280 g.write('array_delete(${amp}')
281 g.gen_expr(base_id)
282 g.write(', ')
283 g.gen_expr(g.a.child(&node, 1))
284 g.write(')')
285 }
286 'prepend' {
287 amp := if is_ptr { '' } else { '&' }
288 g.write('array__prepend(${amp}')
289 g.gen_expr(base_id)
290 g.write(', &(${c_elem}[]){')
291 g.gen_expr(g.a.child(&node, 1))
292 g.write('})')
293 }
294 'free' {
295 g.write('array__free(')
296 if !is_ptr {
297 g.write('&')
298 }
299 g.gen_expr(base_id)
300 g.write(')')
301 }
302 'str' {
303 amp := if is_ptr { '' } else { '&' }
304 g.write('strings__Builder__str(${amp}')
305 g.gen_expr(base_id)
306 g.write(')')
307 }
308 'join' {
309 g.write('Array_string__join(')
310 g.gen_expr(base_id)
311 g.write(', ')
312 g.gen_expr(g.a.child(&node, 1))
313 g.write(')')
314 }
315 'bytestr' {
316 g.write('u8__vstring_with_len((u8*)')
317 g.gen_expr(base_id)
318 g.write('${dot}data, ')
319 g.gen_expr(base_id)
320 g.write('${dot}len)')
321 }
322 'contains' {
323 contains_fn := 'array_contains_${array_lookup_suffix(arr.elem_type)}'
324 g.write('${contains_fn}(')
325 g.gen_expr(base_id)
326 g.write(', ')
327 g.gen_expr(g.a.child(&node, 1))
328 g.write(')')
329 }
330 'index' {
331 index_fn := 'array_index_${array_lookup_suffix(arr.elem_type)}'
332 g.write('${index_fn}(')
333 g.gen_expr(base_id)
334 g.write(', ')
335 g.gen_expr(g.a.child(&node, 1))
336 g.write(')')
337 }
338 'wait' {
339 // Only a thread array supports `.wait()` (joining every spawned thread and,
340 // for non-void payloads, collecting their return values into a fresh `[]T`).
341 // The element carries the thread payload in its name (`thread`/`thread T`).
342 // Any other element type is not a thread, so route it through the normal
343 // method fallback instead of joining arbitrary array data as pthread_t handles.
344 mut is_thread := false
345 elem := arr.elem_type
346 if elem is types.Struct {
347 tn := elem.name.trim_space()
348 is_thread = tn == 'thread' || tn.starts_with('thread ')
349 }
350 if is_thread {
351 g.gen_thread_array_wait(base_id, is_ptr, arr.elem_type)
352 } else {
353 g.gen_array_method_call_fallback(node, fn_node.value, base_id, is_ptr)
354 }
355 }
356 else {
357 g.gen_array_method_call_fallback(node, fn_node.value, base_id, is_ptr)
358 }
359 }
360}
361
362// gen_array_method_call_fallback emits a call for an array method that has no dedicated
363// codegen arm: it resolves a `[]T.method` function when one is registered, and otherwise
364// emits the selector itself as a direct call. Shared by the catch-all `else` arm and by
365// `.wait()` on non-thread arrays (which is unsupported and falls through here rather than
366// joining elements as thread handles).
367fn (mut g FlatGen) gen_array_method_call_fallback(node flat.Node, mname string, base_id flat.NodeId, is_ptr bool) {
368 best_mname := g.array_method_fallback(mname)
369 if best_mname.len > 0 {
370 g.write(c_name(best_mname))
371 g.write('(')
372 ptypes := g.tc.fn_param_types[best_mname]
373 wants_ptr := ptypes.len > 0 && ptypes[0] is types.Pointer
374 if wants_ptr && !is_ptr {
375 g.write('&')
376 } else if !wants_ptr && is_ptr {
377 g.write('*')
378 }
379 g.gen_expr(base_id)
380 for i in 1 .. node.children_count {
381 g.write(', ')
382 g.gen_expr(g.a.child(&node, i))
383 }
384 g.write(')')
385 } else {
386 g.gen_expr(g.a.child(&node, 0))
387 g.write('(')
388 g.gen_expr(base_id)
389 g.write(')')
390 }
391}
392
393// gen_thread_array_wait emits a call to the (lazily generated) wait function for a
394// `[]thread T` receiver. The element type carries the thread's return type in its
395// name (`thread T`); a bare `thread` denotes a void payload.
396fn (mut g FlatGen) gen_thread_array_wait(base_id flat.NodeId, is_ptr bool, elem_type types.Type) {
397 mut ret_name := ''
398 if elem_type is types.Struct {
399 trimmed := elem_type.name.trim_space()
400 if trimmed != 'thread' && trimmed.starts_with('thread ') {
401 ret_name = trimmed[7..].trim_space()
402 }
403 }
404 fn_name := g.ensure_thread_arr_wait_fn(ret_name)
405 g.write('${fn_name}(')
406 if is_ptr {
407 g.write('*')
408 }
409 g.gen_expr(base_id)
410 g.write(')')
411}
412
413// ensure_thread_arr_wait_fn registers (once per payload type) a function that joins
414// every thread handle in the array and, for a non-void payload, copies each thread's
415// heap-returned value into a result `[]T` (freeing the per-thread allocation).
416fn (mut g FlatGen) ensure_thread_arr_wait_fn(ret_name string) string {
417 is_void := ret_name.len == 0
418 // Match the ABI return type the spawn wrapper stores (gen_spawn_expr): an
419 // option/result payload is `Optional_T`, a fixed-array payload its `_v_ret_*`
420 // wrapper — not the bare `c_type`, or the malloc'd and read-back layouts diverge.
421 ret_ct := if is_void { 'void' } else { g.fn_return_type_name(g.tc.parse_type(ret_name)) }
422 key := 'threadwait|${ret_ct}'
423 if name := g.spawn_wrapper_names[key] {
424 return name
425 }
426 // Sanitize the payload C type (`Foo*`, `void*`, ...) into an identifier fragment
427 // — `c_name` does not strip `*`, so a raw pointer return type would otherwise put
428 // an asterisk in the helper symbol.
429 name := c_name('__v_thread_arr_wait_${types.c_type_name_part(ret_ct)}')
430 g.spawn_wrapper_names[key] = name
431 if is_void {
432 g.spawn_wrapper_defs << 'static void ${name}(Array a) { for (int __i = 0; __i < a.len; __i++) { void* __r = NULL; pthread_join((pthread_t)(((void**)a.data)[__i]), &__r); if (__r) free(__r); } }'
433 } else {
434 g.spawn_wrapper_defs << 'static Array ${name}(Array a) { Array __res = array_new(sizeof(${ret_ct}), a.len, a.len); for (int __i = 0; __i < a.len; __i++) { void* __r = NULL; pthread_join((pthread_t)(((void**)a.data)[__i]), &__r); if (__r) { ((${ret_ct}*)__res.data)[__i] = *(${ret_ct}*)__r; free(__r); } } return __res; }'
435 }
436 return name
437}
438
439// array_lookup_suffix supports array lookup suffix handling for c.
440fn array_lookup_suffix(elem_type types.Type) string {
441 if elem_type is types.String {
442 return 'string'
443 }
444 if elem_type is types.Primitive {
445 if elem_type.props.has(.unsigned) && elem_type.size == 8 {
446 return 'u8'
447 }
448 }
449 return 'int'
450}
451
452// array_method_fallback supports array method fallback handling for FlatGen.
453fn (mut g FlatGen) array_method_fallback(method string) string {
454 if method in g.array_method_cache {
455 return g.array_method_cache[method]
456 }
457 suffix := '.${method}'
458 mut best_mname := ''
459 for mname, _ in g.tc.fn_param_types {
460 if mname.ends_with(suffix) {
461 if best_mname.len == 0 || mname.len > best_mname.len {
462 best_mname = mname
463 }
464 }
465 }
466 g.array_method_cache[method] = best_mname
467 return best_mname
468}
469
470// gen_map_ref_arg emits map ref arg output for c.
471fn (mut g FlatGen) gen_map_ref_arg(base_id flat.NodeId, base_type types.Type) {
472 if base_type is types.Pointer {
473 g.gen_expr(base_id)
474 } else {
475 g.write('&')
476 g.gen_expr(base_id)
477 }
478}
479
480// gen_map_delete emits map delete output for c.
481fn (mut g FlatGen) gen_map_delete(node flat.Node, fn_node &flat.Node, m types.Map, base_type types.Type) {
482 c_key := g.tc.c_type(m.key_type)
483 g.write('map__delete(')
484 g.gen_map_ref_arg(g.a.child(fn_node, 0), base_type)
485 g.write(', &(${c_key}[]){')
486 g.gen_expr(g.a.child(&node, 1))
487 g.write('})')
488}
489
490// gen_index_assign emits index assign output for c.
491fn (mut g FlatGen) gen_index_assign(node flat.Node) {
492 lhs_id := g.a.child(&node, 0)
493 lhs := g.a.nodes[int(lhs_id)]
494 if lhs.kind == .index {
495 base_id := g.a.child(&lhs, 0)
496 base_type := g.tc.resolve_type(base_id)
497 clean_base := types.unwrap_pointer(base_type)
498 if clean_base is types.Map {
499 c_key := g.value_c_type(clean_base.key_type)
500 c_val := g.value_c_type(clean_base.value_type)
501 is_ptr := base_type is types.Pointer
502 if is_ptr {
503 g.write('map__set(')
504 } else {
505 g.write('map__set(&')
506 }
507 g.gen_expr(base_id)
508 g.write(', &(${c_key}[]){')
509 g.gen_expr_with_expected_type(g.a.child(&lhs, 1), clean_base.key_type)
510 g.write('}, &(${c_val}[]){')
511 g.gen_expr_with_expected_type(g.a.child(&node, 1), clean_base.value_type)
512 g.writeln('});')
513 return
514 }
515 if base_type is types.Pointer {
516 ptr_type := base_type
517 if ptr_type.base_type is types.Void {
518 g.write('((u8*)')
519 g.gen_expr(base_id)
520 g.write(')[')
521 g.gen_expr(g.a.child(&lhs, 1))
522 g.write('] = ')
523 g.gen_expr(g.a.child(&node, 1))
524 g.writeln(';')
525 return
526 }
527 }
528 mut arr_type := types.Array{}
529 mut is_array_base := false
530 if base_type is types.Array {
531 arr_type = base_type
532 is_array_base = true
533 } else if base_type is types.Pointer {
534 ptr_type := base_type
535 ptr_base := ptr_type.base_type
536 if ptr_base is types.Array {
537 arr_type = ptr_base
538 is_array_base = true
539 }
540 }
541 if is_array_base {
542 c_elem := g.value_c_type(arr_type.elem_type)
543 g.write('array_set(')
544 if base_type is types.Pointer {
545 g.write('*')
546 }
547 g.gen_expr(base_id)
548 g.write(', ')
549 g.gen_expr(g.a.child(&lhs, 1))
550 g.write(', &(${c_elem}[]){')
551 g.gen_expr_with_expected_type(g.a.child(&node, 1), arr_type.elem_type)
552 g.writeln('});')
553 return
554 }
555 }
556 g.gen_assign(node)
557}
558