vxx / vlib / v3 / gen / c / array.v
723 lines · 699 sloc · 21.36 KB · 2b81918e62db9ab38787518603a7f93bee80868c
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.value_c_type(elem_type)
49 sizeof_elem := g.value_sizeof_target(elem_type)
50 count := node.children_count
51 if count == 0 {
52 g.write('array_new(sizeof(${sizeof_elem}), 0, 0)')
53 return
54 }
55 g.write('new_array_from_c_array(${count}, ${count}, sizeof(${sizeof_elem}), (${c_elem}[]){')
56 for i in 0 .. count {
57 if i > 0 {
58 g.write(', ')
59 }
60 // Emit each element against the concrete element type, not the enclosing
61 // `expected_expr_type` (which is the whole array). A bare generic struct element
62 // (`Box{..}` in a `[]Box[int]` literal) otherwise sees the array type, fails the
63 // `generic_struct_init_instance_type` array skip, and is emitted as the bare `Box`
64 // while the array storage is `Box_int` — incompatible C.
65 g.gen_expr_with_expected_type(g.a.child(&node, i), elem_type)
66 }
67 g.write('})')
68}
69
70fn (mut g FlatGen) gen_array_literal_ptr_arg(node flat.Node, elem_type types.Type) {
71 c_elem := g.value_c_type(elem_type)
72 g.write('(${c_elem}[]){')
73 for i in 0 .. node.children_count {
74 if i > 0 {
75 g.write(', ')
76 }
77 g.gen_expr_with_expected_type(g.a.child(&node, i), elem_type)
78 }
79 if node.children_count == 0 {
80 g.write('0')
81 }
82 g.write('}')
83}
84
85fn (mut g FlatGen) gen_pointer_arg_from_array_literal(node flat.Node, expected types.Type) bool {
86 if node.kind != .array_literal {
87 return false
88 }
89 if expected is types.Pointer {
90 g.gen_array_literal_ptr_arg(node, expected.base_type)
91 return true
92 }
93 return false
94}
95
96// gen_fixed_array_data_arg emits fixed array data arg output for c.
97fn (mut g FlatGen) gen_fixed_array_data_arg(id flat.NodeId, arr types.ArrayFixed) {
98 node := g.a.nodes[int(id)]
99 if node.kind == .array_literal {
100 c_elem := g.value_c_type(arr.elem_type)
101 g.write('(${c_elem}[]){')
102 for i in 0 .. node.children_count {
103 if i > 0 {
104 g.write(', ')
105 }
106 g.gen_expr(g.a.child(&node, i))
107 }
108 g.write('}')
109 return
110 }
111 if node.kind == .paren && node.children_count > 0 {
112 g.gen_fixed_array_data_arg(g.a.child(&node, 0), arr)
113 return
114 }
115 if node.kind == .postfix && node.children_count > 0 {
116 child_id := g.a.child(&node, 0)
117 child := g.a.nodes[int(child_id)]
118 if child.kind == .array_literal {
119 g.gen_fixed_array_data_arg(child_id, arr)
120 return
121 }
122 }
123 // A fixed-array value (e.g. `[4]u8` color) is sometimes represented as a dynamic
124 // `Array`; a C fixed-array parameter decays to `elem*`, so pass the data pointer.
125 if g.tc.resolve_type(id) is types.Array {
126 elem_ct := g.tc.c_type(arr.elem_type)
127 g.write('(${elem_ct}*)(')
128 g.gen_expr(id)
129 g.write(').data')
130 return
131 }
132 g.gen_expr(id)
133}
134
135// gen_array_push_many_stmt emits array push many stmt output for c.
136fn (mut g FlatGen) gen_array_push_many_stmt(lhs_id flat.NodeId, rhs_id flat.NodeId) {
137 lhs_is_ptr := g.tc.resolve_type(lhs_id) is types.Pointer
138 amp := if lhs_is_ptr { '' } else { '&' }
139 rhs_type := types.unwrap_pointer(g.tc.resolve_type(rhs_id))
140 if rhs_fixed := array_fixed_type(rhs_type) {
141 g.write('array_push_many_ptr(${amp}')
142 gen_expr_lvalue(mut g, lhs_id)
143 g.write(', ')
144 g.gen_fixed_array_data_arg(rhs_id, rhs_fixed)
145 len_expr := g.fixed_array_len_value(rhs_fixed)
146 g.writeln(', ${len_expr});')
147 return
148 }
149 tmp := g.tmp_name()
150 g.write('{ Array ${tmp} = ')
151 g.gen_expr(rhs_id)
152 g.writeln(';')
153 g.write('array__push_many(${amp}')
154 gen_expr_lvalue(mut g, lhs_id)
155 g.writeln(', ${tmp}.data, ${tmp}.len); }')
156}
157
158// gen_slice_expr emits slice expr output for c.
159fn (mut g FlatGen) gen_slice_expr(node flat.Node, base_id flat.NodeId, base_type types.Type) {
160 start_node := g.a.child_node(&node, 1)
161 has_start := start_node.kind != .empty
162 has_end := node.children_count > 2
163 base_str := g.expr_to_string(base_id)
164 is_array, is_ptr, _ := array_index_info(base_type)
165 is_fixed_array, fixed_is_ptr, fixed := fixed_array_index_info(base_type)
166 start_str := if has_start { g.expr_to_string(g.a.child(&node, 1)) } else { '0' }
167 end_str := if has_end {
168 g.expr_to_string(g.a.child(&node, 2))
169 } else if is_fixed_array {
170 g.fixed_array_len_value(fixed)
171 } else if is_array && is_ptr {
172 '${base_str}->len'
173 } else {
174 '${base_str}.len'
175 }
176 if base_type is types.String {
177 g.write('string__substr(${base_str}, ${start_str}, ${end_str})')
178 } else if is_fixed_array {
179 c_elem := g.tc.c_type(fixed.elem_type)
180 data_str := if fixed_is_ptr { '(*${base_str})' } else { base_str }
181 // Evaluate the slice bounds once so side-effecting expressions such as
182 // `arr[i++..limit()]` are not run multiple times in the generated C.
183 start_tmp := g.tmp_name()
184 count_tmp := g.tmp_name()
185 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}]); })')
186 } else if is_array {
187 arr_str := if is_ptr { '*${base_str}' } else { base_str }
188 g.write('array_slice(${arr_str}, ${start_str}, ${end_str})')
189 } else {
190 g.write('string__substr(${base_str}, ${start_str}, ${end_str})')
191 }
192}
193
194// gen_array_method_call emits array method call output for c.
195fn (mut g FlatGen) gen_array_method_call(node flat.Node, fn_node &flat.Node, arr types.Array) {
196 c_elem := g.value_c_type(arr.elem_type)
197 base_id := g.a.child(fn_node, 0)
198 base_node := g.a.nodes[int(base_id)]
199 is_ptr := if base_node.kind == .ident {
200 g.tc.resolve_type(base_id) is types.Pointer
201 } else {
202 false
203 }
204 dot := if is_ptr { '->' } else { '.' }
205 match fn_node.value {
206 'clone' {
207 g.write('array__clone(')
208 if !is_ptr {
209 g.write('&')
210 }
211 g.gen_expr(base_id)
212 g.write(')')
213 }
214 'last' {
215 g.write('*(${c_elem}*)array_get(')
216 g.gen_expr(base_id)
217 g.write(', ')
218 g.gen_expr(base_id)
219 g.write('.len - 1)')
220 }
221 'first' {
222 g.write('*(${c_elem}*)array_get(')
223 g.gen_expr(base_id)
224 g.write(', 0)')
225 }
226 'delete_last' {
227 g.write('array__delete_last(')
228 if !is_ptr {
229 g.write('&')
230 }
231 g.gen_expr(base_id)
232 g.write(')')
233 }
234 'pop' {
235 amp := if is_ptr { '' } else { '&' }
236 g.write('*(${c_elem}*)array__pop(${amp}')
237 g.gen_expr(base_id)
238 g.write(')')
239 }
240 'pop_left' {
241 amp := if is_ptr { '' } else { '&' }
242 g.write('*(${c_elem}*)array__pop_left(${amp}')
243 g.gen_expr(base_id)
244 g.write(')')
245 }
246 'clear' {
247 amp := if is_ptr { '' } else { '&' }
248 g.write('array__clear(${amp}')
249 g.gen_expr(base_id)
250 g.write(')')
251 }
252 'push_many' {
253 amp := if is_ptr { '' } else { '&' }
254 g.write('array_push_many_ptr(${amp}')
255 g.gen_expr(base_id)
256 g.write(', ')
257 g.gen_expr(g.a.child(&node, 1))
258 g.write(', ')
259 g.gen_expr(g.a.child(&node, 2))
260 g.write(')')
261 }
262 'repeat' {
263 g.write('array__repeat_to_depth(')
264 g.gen_expr(base_id)
265 g.write(', ')
266 g.gen_expr(g.a.child(&node, 1))
267 g.write(', 0)')
268 }
269 'repeat_to_depth' {
270 g.write('array__repeat_to_depth(')
271 g.gen_expr(base_id)
272 g.write(', ')
273 g.gen_expr(g.a.child(&node, 1))
274 g.write(', ')
275 g.gen_expr(g.a.child(&node, 2))
276 g.write(')')
277 }
278 'trim' {
279 amp := if is_ptr { '' } else { '&' }
280 g.write('array__trim(${amp}')
281 g.gen_expr(base_id)
282 g.write(', ')
283 g.gen_expr(g.a.child(&node, 1))
284 g.write(')')
285 }
286 'ensure_cap' {
287 amp := if is_ptr { '' } else { '&' }
288 g.write('array_ensure_cap(${amp}')
289 g.gen_expr(base_id)
290 g.write(', ')
291 g.gen_expr(g.a.child(&node, 1))
292 g.write(')')
293 }
294 'delete' {
295 amp := if is_ptr { '' } else { '&' }
296 g.write('array_delete(${amp}')
297 g.gen_expr(base_id)
298 g.write(', ')
299 g.gen_expr(g.a.child(&node, 1))
300 g.write(')')
301 }
302 'prepend' {
303 amp := if is_ptr { '' } else { '&' }
304 g.write('array__prepend(${amp}')
305 g.gen_expr(base_id)
306 g.write(', &(${c_elem}[]){')
307 g.gen_expr(g.a.child(&node, 1))
308 g.write('})')
309 }
310 'free' {
311 g.write('array__free(')
312 if !is_ptr {
313 g.write('&')
314 }
315 g.gen_expr(base_id)
316 g.write(')')
317 }
318 'pointers' {
319 g.gen_array_pointers_expr(base_id, is_ptr)
320 }
321 'str' {
322 amp := if is_ptr { '' } else { '&' }
323 g.write('strings__Builder__str(${amp}')
324 g.gen_expr(base_id)
325 g.write(')')
326 }
327 'join' {
328 g.write('Array_string__join(')
329 g.gen_expr(base_id)
330 g.write(', ')
331 g.gen_expr(g.a.child(&node, 1))
332 g.write(')')
333 }
334 'bytestr' {
335 g.write('u8__vstring_with_len((u8*)')
336 g.gen_expr(base_id)
337 g.write('${dot}data, ')
338 g.gen_expr(base_id)
339 g.write('${dot}len)')
340 }
341 'contains' {
342 contains_fn := 'array_contains_${array_lookup_suffix(arr.elem_type)}'
343 g.write('${contains_fn}(')
344 g.gen_expr(base_id)
345 g.write(', ')
346 g.gen_expr(g.a.child(&node, 1))
347 g.write(')')
348 }
349 'index' {
350 index_fn := 'array_index_${array_lookup_suffix(arr.elem_type)}'
351 g.write('${index_fn}(')
352 g.gen_expr(base_id)
353 g.write(', ')
354 g.gen_expr(g.a.child(&node, 1))
355 g.write(')')
356 }
357 'last_index' {
358 if suffix := array_last_index_suffix(arr.elem_type) {
359 index_fn := 'array_last_index_${suffix}'
360 g.write('${index_fn}(')
361 g.gen_expr(base_id)
362 g.write(', ')
363 g.gen_expr(g.a.child(&node, 1))
364 g.write(')')
365 } else {
366 g.write('array_last_index_raw(')
367 g.gen_expr(base_id)
368 g.write(', &(${c_elem}[]){')
369 g.gen_expr_with_expected_type(g.a.child(&node, 1), arr.elem_type)
370 g.write('})')
371 }
372 }
373 'hex' {
374 g.write('Array_u8__hex(')
375 if base_node.kind == .array_literal {
376 g.gen_expr_with_expected_type(base_id, types.Type(types.Array{
377 elem_type: types.Type(types.u8_)
378 }))
379 } else {
380 g.gen_expr(base_id)
381 }
382 g.write(')')
383 }
384 'wait' {
385 // Only a thread array supports `.wait()` (joining every spawned thread and,
386 // for non-void payloads, collecting their return values into a fresh `[]T`).
387 // The element carries the thread payload in its name (`thread`/`thread T`).
388 // Any other element type is not a thread, so route it through the normal
389 // method fallback instead of joining arbitrary array data as pthread_t handles.
390 mut is_thread := false
391 elem := arr.elem_type
392 if elem is types.Struct {
393 tn := elem.name.trim_space()
394 is_thread = tn == 'thread' || tn.starts_with('thread ')
395 }
396 if is_thread {
397 g.gen_thread_array_wait(base_id, is_ptr, arr.elem_type)
398 } else {
399 g.gen_array_method_call_fallback(node, fn_node.value, base_id, is_ptr, arr)
400 }
401 }
402 else {
403 g.gen_array_method_call_fallback(node, fn_node.value, base_id, is_ptr, arr)
404 }
405 }
406}
407
408// gen_array_method_call_fallback emits a call for an array method that has no dedicated
409// codegen arm: it resolves a `[]T.method` function when one is registered, and otherwise
410// emits the selector itself as a direct call. Shared by the catch-all `else` arm and by
411// `.wait()` on non-thread arrays (which is unsupported and falls through here rather than
412// joining elements as thread handles).
413fn (mut g FlatGen) gen_array_method_call_fallback(node flat.Node, mname string, base_id flat.NodeId, is_ptr bool, arr types.Array) {
414 best_mname := g.array_method_fallback_for_receiver(mname, arr)
415 if best_mname.len > 0 {
416 g.write(c_name(best_mname))
417 g.write('(')
418 ptypes := g.tc.fn_param_types[best_mname]
419 wants_ptr := ptypes.len > 0 && ptypes[0] is types.Pointer
420 if wants_ptr && !is_ptr {
421 g.write('&')
422 } else if !wants_ptr && is_ptr {
423 g.write('*')
424 }
425 g.gen_expr(base_id)
426 for i in 1 .. node.children_count {
427 g.write(', ')
428 g.gen_expr(g.a.child(&node, i))
429 }
430 g.write(')')
431 } else {
432 g.gen_expr(g.a.child(&node, 0))
433 g.write('(')
434 g.gen_expr(base_id)
435 g.write(')')
436 }
437}
438
439// gen_array_pointers_expr emits `array.pointers()` without compiling the erased
440// raw `array` builtin body, which has no concrete element type in v3 Cgen.
441fn (mut g FlatGen) gen_array_pointers_expr(base_id flat.NodeId, is_ptr bool) {
442 base_type := types.unwrap_pointer(g.tc.resolve_type(base_id))
443 if fixed := array_fixed_type(base_type) {
444 g.gen_fixed_array_pointers_expr(base_id, is_ptr, fixed)
445 return
446 }
447 tmp := g.tmp_count
448 g.tmp_count++
449 src_name := '__arr_ptrs_src_${tmp}'
450 res_name := '__arr_ptrs_res_${tmp}'
451 idx_name := '__arr_ptrs_i_${tmp}'
452 g.write('({ Array ${src_name} = ')
453 if is_ptr {
454 g.write('*')
455 }
456 g.gen_expr(base_id)
457 g.write('; Array ${res_name} = array_new(sizeof(voidptr), ${src_name}.len, ${src_name}.len); for (int ${idx_name} = 0; ${idx_name} < ${src_name}.len; ${idx_name}++) { ((voidptr*)${res_name}.data)[${idx_name}] = (voidptr)((u8*)${src_name}.data + ((u64)${idx_name} * (u64)${src_name}.element_size)); } ${res_name}; })')
458}
459
460fn (mut g FlatGen) gen_fixed_array_pointers_expr(base_id flat.NodeId, is_ptr bool, fixed types.ArrayFixed) {
461 tmp := g.tmp_count
462 g.tmp_count++
463 src_name := '__arr_ptrs_fixed_src_${tmp}'
464 res_name := '__arr_ptrs_res_${tmp}'
465 idx_name := '__arr_ptrs_i_${tmp}'
466 len_text := g.fixed_array_len_value(fixed)
467 if !is_ptr && !g.expr_is_addressable(base_id) {
468 panic('fixed array .pointers receiver should be addressable after checking')
469 }
470 g.write('({ ')
471 g.write('typeof(')
472 if is_ptr {
473 g.gen_expr(base_id)
474 } else {
475 g.write('&(')
476 g.gen_expr(base_id)
477 g.write(')')
478 }
479 g.write(') ${src_name} = ')
480 if is_ptr {
481 g.gen_expr(base_id)
482 } else {
483 g.write('&(')
484 g.gen_expr(base_id)
485 g.write(')')
486 }
487 g.write('; Array ${res_name} = array_new(sizeof(voidptr), ${len_text}, ${len_text}); for (int ${idx_name} = 0; ${idx_name} < ${len_text}; ${idx_name}++) { ((voidptr*)${res_name}.data)[${idx_name}] = (voidptr)&((*${src_name})[${idx_name}]); } ${res_name}; })')
488}
489
490// gen_thread_array_wait emits a call to the (lazily generated) wait function for a
491// `[]thread T` receiver. The element type carries the thread's return type in its
492// name (`thread T`); a bare `thread` denotes a void payload.
493fn (mut g FlatGen) gen_thread_array_wait(base_id flat.NodeId, is_ptr bool, elem_type types.Type) {
494 mut ret_name := ''
495 if elem_type is types.Struct {
496 trimmed := elem_type.name.trim_space()
497 if trimmed != 'thread' && trimmed.starts_with('thread ') {
498 ret_name = trimmed[7..].trim_space()
499 }
500 }
501 fn_name := g.ensure_thread_arr_wait_fn(ret_name)
502 g.write('${fn_name}(')
503 if is_ptr {
504 g.write('*')
505 }
506 g.gen_expr(base_id)
507 g.write(')')
508}
509
510// ensure_thread_arr_wait_fn registers (once per payload type) a function that joins
511// every thread handle in the array and, for a non-void payload, copies each thread's
512// heap-returned value into a result `[]T` (freeing the per-thread allocation).
513fn (mut g FlatGen) ensure_thread_arr_wait_fn(ret_name string) string {
514 is_void := ret_name.len == 0
515 // Match the ABI return type the spawn wrapper stores (gen_spawn_expr): an
516 // option/result payload is `Optional_T`, a fixed-array payload its `_v_ret_*`
517 // wrapper — not the bare `c_type`, or the malloc'd and read-back layouts diverge.
518 ret_ct := if is_void { 'void' } else { g.fn_return_type_name(g.tc.parse_type(ret_name)) }
519 key := 'threadwait|${ret_ct}'
520 if name := g.spawn_wrapper_names[key] {
521 return name
522 }
523 // Sanitize the payload C type (`Foo*`, `void*`, ...) into an identifier fragment
524 // — `c_name` does not strip `*`, so a raw pointer return type would otherwise put
525 // an asterisk in the helper symbol.
526 name := c_name('__v_thread_arr_wait_${types.c_type_name_part(ret_ct)}')
527 g.spawn_wrapper_names[key] = name
528 if is_void {
529 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); } }'
530 } else {
531 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; }'
532 }
533 return name
534}
535
536// array_lookup_suffix supports array lookup suffix handling for c.
537fn array_lookup_suffix(elem_type types.Type) string {
538 if elem_type is types.String {
539 return 'string'
540 }
541 if elem_type is types.Primitive {
542 if elem_type.props.has(.unsigned) && elem_type.size == 8 {
543 return 'u8'
544 }
545 }
546 return 'int'
547}
548
549fn array_last_index_suffix(elem_type types.Type) ?string {
550 elem_name := elem_type.name()
551 return match elem_name {
552 'string' { 'string' }
553 'u8', 'byte' { 'u8' }
554 'int' { 'int' }
555 else { none }
556 }
557}
558
559// array_method_fallback supports array method fallback handling for FlatGen.
560fn (mut g FlatGen) array_method_fallback(method string) string {
561 if method in g.array_method_cache {
562 return g.array_method_cache[method]
563 }
564 suffix := '.${method}'
565 mut best_mname := ''
566 for mname, _ in g.tc.fn_param_types {
567 if mname.ends_with(suffix) {
568 if best_mname.len == 0 || mname.len > best_mname.len {
569 best_mname = mname
570 }
571 }
572 }
573 g.array_method_cache[method] = best_mname
574 return best_mname
575}
576
577fn (mut g FlatGen) array_method_fallback_for_receiver(method string, arr types.Array) string {
578 key := '${arr.elem_type.name()}.${method}'
579 if key in g.array_method_cache {
580 return g.array_method_cache[key]
581 }
582 suffix := '.${method}'
583 mut best_mname := ''
584 for mname, ptypes in g.tc.fn_param_types {
585 if !mname.ends_with(suffix) || ptypes.len == 0 {
586 continue
587 }
588 recv := types.unwrap_pointer(ptypes[0])
589 if recv is types.Array && g.array_elem_type_matches(recv.elem_type, arr.elem_type) {
590 if best_mname.len == 0 || mname.len > best_mname.len {
591 best_mname = mname
592 }
593 }
594 }
595 if best_mname.len == 0 {
596 best_mname = g.array_method_fallback(method)
597 }
598 g.array_method_cache[key] = best_mname
599 return best_mname
600}
601
602fn (g &FlatGen) array_elem_type_matches(expected types.Type, actual types.Type) bool {
603 if expected.name() == actual.name() {
604 return true
605 }
606 return g.tc.c_type(expected) == g.tc.c_type(actual)
607}
608
609// gen_map_ref_arg emits map ref arg output for c.
610fn (mut g FlatGen) gen_map_ref_arg(base_id flat.NodeId, base_type types.Type) {
611 if base_type is types.Pointer {
612 g.gen_expr(base_id)
613 } else {
614 g.write('&')
615 g.gen_expr(base_id)
616 }
617}
618
619// gen_index_assign emits index assign output for c.
620fn (mut g FlatGen) gen_index_assign(node flat.Node) {
621 lhs_id := g.a.child(&node, 0)
622 lhs := g.a.nodes[int(lhs_id)]
623 if lhs.kind == .index {
624 base_id := g.a.child(&lhs, 0)
625 base_type := g.tc.resolve_type(base_id)
626 clean_base := types.unwrap_pointer(base_type)
627 if clean_base is types.Map {
628 c_key := g.value_c_type(clean_base.key_type)
629 c_val := g.value_c_type(clean_base.value_type)
630 is_ptr := base_type is types.Pointer
631 if is_ptr {
632 g.write('map__set(')
633 } else {
634 g.write('map__set(&')
635 }
636 g.gen_expr(base_id)
637 g.write(', &(${c_key}[]){')
638 g.gen_expr_with_expected_type(g.a.child(&lhs, 1), clean_base.key_type)
639 g.write('}, &(${c_val}[]){')
640 g.gen_expr_with_expected_type(g.a.child(&node, 1), clean_base.value_type)
641 g.writeln('});')
642 return
643 }
644 if base_type is types.Pointer {
645 ptr_type := base_type
646 if ptr_type.base_type is types.Void {
647 g.write('((u8*)')
648 g.gen_expr(base_id)
649 g.write(')[')
650 g.gen_expr(g.a.child(&lhs, 1))
651 g.write('] = ')
652 g.gen_expr(g.a.child(&node, 1))
653 g.writeln(';')
654 return
655 }
656 }
657 mut arr_type := types.Array{}
658 mut is_array_base := false
659 if base_type is types.Array {
660 arr_type = base_type
661 is_array_base = true
662 } else if base_type is types.Pointer {
663 ptr_type := base_type
664 ptr_base := ptr_type.base_type
665 if ptr_base is types.Array {
666 arr_type = ptr_base
667 is_array_base = true
668 }
669 }
670 if is_array_base {
671 c_elem := g.value_c_type(arr_type.elem_type)
672 tmp := g.tmp_count
673 g.tmp_count++
674 g.write('{ array* _a${tmp} = ')
675 if base_type is types.Pointer {
676 g.gen_expr(base_id)
677 } else {
678 g.write('&')
679 g.gen_expr(base_id)
680 }
681 g.write('; int _i${tmp} = ')
682 g.gen_expr(g.a.child(&lhs, 1))
683 g.write('; array__set(_a${tmp}, _i${tmp}, &(${c_elem}[]){')
684 if op := compound_assign_to_infix_op(node.op) {
685 if arr_type.elem_type is types.String && op == .plus {
686 g.write('string__plus(')
687 g.write('*(string*)array_get(*_a${tmp}, _i${tmp})')
688 g.write(', ')
689 g.gen_expr_as_string(g.a.child(&node, 1))
690 g.write(')')
691 } else {
692 g.write('(')
693 g.write('*(${c_elem}*)array_get(*_a${tmp}, _i${tmp})')
694 g.write(' ${g.op_str(op)} ')
695 g.gen_expr_with_expected_type(g.a.child(&node, 1), arr_type.elem_type)
696 g.write(')')
697 }
698 } else {
699 g.gen_expr_with_expected_type(g.a.child(&node, 1), arr_type.elem_type)
700 }
701 g.writeln('}); }')
702 return
703 }
704 }
705 g.gen_assign(node)
706}
707
708fn compound_assign_to_infix_op(op flat.Op) ?flat.Op {
709 match op {
710 .plus_assign { return flat.Op.plus }
711 .minus_assign { return flat.Op.minus }
712 .mul_assign { return flat.Op.mul }
713 .div_assign { return flat.Op.div }
714 .mod_assign { return flat.Op.mod }
715 .amp_assign { return flat.Op.amp }
716 .pipe_assign { return flat.Op.pipe }
717 .xor_assign { return flat.Op.xor }
718 .left_shift_assign { return flat.Op.left_shift }
719 .right_shift_assign { return flat.Op.right_shift }
720 .right_shift_unsigned_assign { return flat.Op.right_shift_unsigned }
721 else { return none }
722 }
723}
724