vxx2 / vlib / v3 / gen / c / struct.v
1924 lines · 1847 sloc · 58.01 KB · 2b81918e62db9ab38787518603a7f93bee80868c
Raw
1module c
2
3import v3.flat
4import v3.types
5
6// c_field_name supports c field name handling for c.
7fn c_field_name(name string) string {
8 if name.starts_with('&') {
9 return c_field_name(name[1..])
10 }
11 if name.starts_with('ptr') && name.len > 3 && name[3..].contains('__') {
12 return c_name(name[3..])
13 }
14 if name.starts_with('ptr') && name.len > 3 && name[3..].contains('.') {
15 return c_name(name[3..])
16 }
17 return c_name(name)
18}
19
20// struct_init_fields_key returns the key under which the initialized struct's checked fields
21// (and their concrete types) live. For a bare generic literal that adopts a concrete instance
22// (`Box{..}` where `Box[int]` is expected) that is the instance key `Box[int]`; the bare `Box`
23// entry is removed by monomorphization, so field-type lookups and omitted default-field
24// emission must use the instance key or they miss fields like `items []T` (leaving invalid
25// zeroed array/map metadata). Falls back to the given key for non-generic structs.
26fn (g &FlatGen) struct_init_fields_key(type_name string, fallback string) string {
27 if inst := g.generic_struct_init_instance_name(type_name) {
28 if inst in g.tc.structs {
29 return inst
30 }
31 }
32 return fallback
33}
34
35fn (mut g FlatGen) gen_struct_field_expr(value_id flat.NodeId, expected types.Type) {
36 if g.gen_callback_fn_value_for_expected_type(value_id, expected) {
37 return
38 }
39 g.gen_expr_with_expected_type(value_id, expected)
40}
41
42fn (mut g FlatGen) gen_struct_field_expr_for_field(value_id flat.NodeId, struct_name string, field_name string, expected types.Type) {
43 if c_abi_fn := g.struct_field_c_abi_fn_ptr_type(struct_name, field_name) {
44 if g.gen_callback_fn_value_for_field_c_abi(value_id, expected, c_abi_fn) {
45 return
46 }
47 }
48 g.gen_struct_field_expr(value_id, expected)
49}
50
51// gen_struct_init emits struct init output for c.
52fn (mut g FlatGen) gen_struct_init(node flat.Node) {
53 init_module := g.tc.cur_module
54 if node.value.starts_with('chan ') {
55 g.gen_channel_init(node)
56 return
57 }
58 if g.gen_lowered_sum_init(node) {
59 return
60 }
61 mut name := g.struct_init_c_type_name(node.value)
62 // A bare generic struct literal (`Vec4{..}`) carries no type args; when the
63 // surrounding expected type fixes them (e.g. a `Vec4[f32]` return), emit the
64 // concrete instance name so it matches the materialized struct.
65 if inst := g.generic_struct_init_instance_ct(node.value) {
66 name = inst
67 }
68 // A bare generic literal stores its fields under the concrete instance key (`Box[int]`);
69 // the bare `node.value` (`Box`) entry is removed by monomorphization, so resolve the
70 // instance for the fixed-array-field test, field-type lookups, and omitted-default emission.
71 lookup_name := g.struct_init_fields_key(node.value, node.value)
72 if node.children_count == 0 && g.is_scalar_zero_init_type(node.value, name) {
73 g.write(g.scalar_zero_init(name))
74 return
75 }
76 if !g.is_interface_type_name(node.value)
77 && g.struct_init_has_fixed_array_field(node, lookup_name) {
78 g.gen_struct_init_with_fixed_array_fields(node, name, init_module)
79 return
80 }
81 g.write('(${name}){')
82 mut allowed_fields := map[string]bool{}
83 if fields := g.struct_fields_for_type(lookup_name) {
84 for f in fields {
85 allowed_fields[f.name] = true
86 }
87 }
88 mut set_fields := map[string]bool{}
89 mut has_field := false
90 if g.is_interface_type_name(node.value) {
91 if tid := g.interface_init_typ_id(node) {
92 g.write('._typ = ${tid}')
93 has_field = true
94 }
95 }
96 for i in 0 .. node.children_count {
97 field := g.a.child_node(&node, i)
98 if field.value.len > 0 && allowed_fields.len > 0 && field.value !in allowed_fields {
99 continue
100 }
101 if has_field {
102 g.write(', ')
103 }
104 value_id := g.a.child(field, 0)
105 if field.value.len == 0 {
106 if sf := g.struct_field_at(lookup_name, i) {
107 if heap_copy_type := g.heap_copy_type_for_sum_pointer_field(node.value, sf.name,
108 value_id)
109 {
110 inner_ct := g.tc.c_type(heap_copy_type)
111 g.write('(${inner_ct}*)memdup(')
112 g.gen_expr(value_id)
113 g.write(', sizeof(${inner_ct}))')
114 } else {
115 g.gen_struct_field_expr_for_field(value_id, node.value, sf.name, sf.typ)
116 }
117 set_fields[sf.name] = true
118 } else {
119 g.gen_expr(value_id)
120 }
121 } else {
122 g.write('.${c_field_name(field.value)} = ')
123 if heap_copy_type := g.heap_copy_type_for_sum_pointer_field(node.value, field.value,
124 value_id)
125 {
126 inner_ct := g.tc.c_type(heap_copy_type)
127 g.write('(${inner_ct}*)memdup(')
128 g.gen_expr(value_id)
129 g.write(', sizeof(${inner_ct}))')
130 } else {
131 if ftyp := g.struct_field_type(lookup_name, field.value) {
132 if g.struct_field_value_is_plainly_incompatible(value_id, ftyp) {
133 g.gen_default_value_for_type(ftyp)
134 } else {
135 g.gen_struct_field_expr_for_field(value_id, node.value, field.value, ftyp)
136 }
137 } else {
138 g.gen_expr(value_id)
139 }
140 }
141 set_fields[field.value] = true
142 }
143 has_field = true
144 }
145 after_fields_module := g.tc.cur_module
146 g.tc.cur_module = init_module
147 sname := g.struct_init_resolved_decl_name(node.value)
148 g.tc.cur_module = after_fields_module
149 has_field = g.gen_struct_default_fields(sname, mut set_fields, has_field)
150 defaults_key := if lookup_name in g.tc.structs { lookup_name } else { sname }
151 if defaults_key in g.tc.structs {
152 for f in g.tc.structs[defaults_key] {
153 if f.name in set_fields {
154 continue
155 }
156 if f.typ is types.Map {
157 if has_field {
158 g.write(', ')
159 }
160 g.write('.${c_field_name(f.name)} = ')
161 g.write_new_map(f.typ.key_type, f.typ.value_type)
162 has_field = true
163 } else if f.typ is types.Array {
164 c_elem := g.tc.c_type(f.typ.elem_type)
165 if has_field {
166 g.write(', ')
167 }
168 g.write('.${c_field_name(f.name)} = array_new(sizeof(${c_elem}), 0, 0)')
169 has_field = true
170 } else if g.field_needs_default_init(f.typ) {
171 if has_field {
172 g.write(', ')
173 }
174 g.write('.${c_field_name(f.name)} = ')
175 g.gen_default_value_for_type(f.typ)
176 has_field = true
177 }
178 }
179 }
180 g.write('}')
181}
182
183fn (mut g FlatGen) struct_init_has_fixed_array_field(node flat.Node, type_name string) bool {
184 for i in 0 .. node.children_count {
185 field := g.a.child_node(&node, i)
186 if field.value.len == 0 {
187 if sf := g.struct_field_at(type_name, i) {
188 if sf.typ is types.ArrayFixed {
189 return true
190 }
191 }
192 continue
193 }
194 if ftyp := g.struct_field_type(type_name, field.value) {
195 if ftyp is types.ArrayFixed {
196 return true
197 }
198 }
199 }
200 return false
201}
202
203fn (mut g FlatGen) gen_struct_init_with_fixed_array_fields(node flat.Node, name string, init_module string) {
204 g.gen_struct_init_with_fixed_array_fields_impl(node, name, init_module, false)
205}
206
207// gen_struct_init_with_fixed_array_fields_impl builds a struct that has fixed-array
208// fields via a temp + per-field `memcpy` (array members can't be assigned in a
209// compound literal). When `heap`, the temp is `memdup`'d and a pointer returned,
210// for `&Struct{...}` initializers.
211fn (mut g FlatGen) gen_struct_init_with_fixed_array_fields_impl(node flat.Node, name string, init_module string, heap bool) {
212 tmp := g.tmp_name()
213 if heap {
214 g.write('(${name}*)')
215 }
216 g.write('({${name} ${tmp} = (${name}){')
217 // A bare generic literal stores its fields under the concrete instance key (`Box[int]`);
218 // the bare `node.value` (`Box`) entry is removed by monomorphization, so resolve the
219 // instance for the field lookups and omitted-default emission below.
220 lookup_name := g.struct_init_fields_key(node.value, node.value)
221 mut allowed_fields := map[string]bool{}
222 if fields := g.struct_fields_for_type(lookup_name) {
223 for f in fields {
224 allowed_fields[f.name] = true
225 }
226 }
227 mut fixed_fields := []string{}
228 mut fixed_values := []flat.NodeId{}
229 mut fixed_field_types := []types.Type{}
230 mut set_fields := map[string]bool{}
231 mut has_field := false
232 for i in 0 .. node.children_count {
233 field := g.a.child_node(&node, i)
234 if field.value.len > 0 && allowed_fields.len > 0 && field.value !in allowed_fields {
235 continue
236 }
237 value_id := g.a.child(field, 0)
238 if field.value.len == 0 {
239 if sf := g.struct_field_at(lookup_name, i) {
240 if sf.typ is types.ArrayFixed {
241 fixed_fields << sf.name
242 fixed_values << value_id
243 fixed_field_types << sf.typ
244 set_fields[sf.name] = true
245 continue
246 }
247 if has_field {
248 g.write(', ')
249 }
250 g.write('.${c_field_name(sf.name)} = ')
251 g.gen_struct_field_expr_for_field(value_id, node.value, sf.name, sf.typ)
252 set_fields[sf.name] = true
253 has_field = true
254 } else {
255 if has_field {
256 g.write(', ')
257 }
258 g.gen_expr(value_id)
259 has_field = true
260 }
261 } else {
262 ftyp := g.struct_field_type(lookup_name, field.value) or { types.Type(types.void_) }
263 if ftyp is types.ArrayFixed {
264 fixed_fields << field.value
265 fixed_values << value_id
266 fixed_field_types << ftyp
267 set_fields[field.value] = true
268 continue
269 }
270 if has_field {
271 g.write(', ')
272 }
273 g.write('.${c_field_name(field.value)} = ')
274 if heap_copy_type := g.heap_copy_type_for_sum_pointer_field(node.value, field.value,
275 value_id)
276 {
277 inner_ct := g.tc.c_type(heap_copy_type)
278 g.write('(${inner_ct}*)memdup(')
279 g.gen_expr(value_id)
280 g.write(', sizeof(${inner_ct}))')
281 } else if ftyp !is types.Void {
282 if g.struct_field_value_is_plainly_incompatible(value_id, ftyp) {
283 g.gen_default_value_for_type(ftyp)
284 } else {
285 g.gen_struct_field_expr_for_field(value_id, node.value, field.value, ftyp)
286 }
287 } else {
288 g.gen_expr(value_id)
289 }
290 set_fields[field.value] = true
291 has_field = true
292 }
293 }
294 after_fields_module := g.tc.cur_module
295 g.tc.cur_module = init_module
296 sname := g.struct_init_resolved_decl_name(node.value)
297 g.tc.cur_module = after_fields_module
298 has_field = g.gen_struct_default_fields(sname, mut set_fields, has_field)
299 defaults_key := if lookup_name in g.tc.structs { lookup_name } else { sname }
300 if defaults_key in g.tc.structs {
301 for f in g.tc.structs[defaults_key] {
302 if f.name in set_fields {
303 continue
304 }
305 if f.typ is types.Map {
306 if has_field {
307 g.write(', ')
308 }
309 g.write('.${c_field_name(f.name)} = ')
310 g.write_new_map(f.typ.key_type, f.typ.value_type)
311 has_field = true
312 } else if f.typ is types.Array {
313 c_elem := g.tc.c_type(f.typ.elem_type)
314 if has_field {
315 g.write(', ')
316 }
317 g.write('.${c_field_name(f.name)} = array_new(sizeof(${c_elem}), 0, 0)')
318 has_field = true
319 } else if g.field_needs_default_init(f.typ) {
320 if has_field {
321 g.write(', ')
322 }
323 g.write('.${c_field_name(f.name)} = ')
324 g.gen_default_value_for_type(f.typ)
325 has_field = true
326 }
327 }
328 }
329 g.write('};')
330 for i in 0 .. fixed_fields.len {
331 cfield := c_field_name(fixed_fields[i])
332 g.write(' memcpy(${tmp}.${cfield}, ')
333 g.gen_fixed_array_copy_source(fixed_values[i], fixed_field_types[i])
334 g.write(', sizeof(${tmp}.${cfield}));')
335 }
336 if heap {
337 g.write(' memdup(&${tmp}, sizeof(${name}));})')
338 } else {
339 g.write(' ${tmp};})')
340 }
341}
342
343// gen_fixed_array_copy_source emits a `memcpy` source for assigning into a fixed
344// array. A raw array literal becomes a typed compound literal (a valid expression
345// that decays to a pointer); a dynamic array value copies from its `.data` buffer;
346// other fixed-array expressions (variables, fields, unwrapped calls) decay as-is.
347fn (mut g FlatGen) gen_fixed_array_copy_source(value_id flat.NodeId, field_type types.Type) {
348 val_node := g.a.node(value_id)
349 if val_node.kind == .array_literal {
350 g.write('(${g.tc.c_type(field_type)})')
351 g.gen_expr(value_id)
352 return
353 }
354 if val_node.kind == .paren && val_node.children_count > 0 {
355 g.gen_fixed_array_copy_source(g.a.child(val_node, 0), field_type)
356 return
357 }
358 val_type := types.unwrap_pointer(g.usable_expr_type(value_id))
359 if val_type is types.Array {
360 g.write('(')
361 g.gen_expr(value_id)
362 g.write(').data')
363 return
364 }
365 g.gen_expr(value_id)
366}
367
368// gen_lowered_sum_init emits lowered sum init output for c.
369fn (mut g FlatGen) gen_lowered_sum_init(node flat.Node) bool {
370 sum_name := g.resolve_sum_name(node.value)
371 if sum_name !in g.tc.sum_types || node.children_count == 0 {
372 return false
373 }
374 name := g.struct_init_c_type_name(node.value)
375 g.write('(${name}){')
376 for i in 0 .. node.children_count {
377 field := g.a.child_node(&node, i)
378 if i > 0 {
379 g.write(', ')
380 }
381 g.write('.${c_field_name(field.value)} = ')
382 g.gen_lowered_sum_field_value(sum_name, field)
383 }
384 g.write('}')
385 return true
386}
387
388// gen_lowered_sum_field_value emits lowered sum field value output for c.
389fn (mut g FlatGen) gen_lowered_sum_field_value(sum_name string, field &flat.Node) {
390 child_id := g.a.child(field, 0)
391 if field.value != 'typ' {
392 mut variant := ''
393 if field.typ.starts_with('&') {
394 variant = field.typ[1..]
395 } else if field.typ.len > 0 {
396 variant = field.typ
397 } else {
398 for v in g.tc.sum_types[sum_name] {
399 if g.sum_field_name(v) == field.value {
400 variant = v
401 break
402 }
403 }
404 }
405 if variant.len > 0 {
406 variant = g.resolve_variant(sum_name, variant)
407 inner_type := g.tc.parse_type(variant)
408 inner_ct := g.tc.c_type(inner_type)
409 child_type := g.tc.resolve_type(child_id)
410 g.write('(${inner_ct}*)memdup(')
411 if child_type is types.Pointer && g.type_names_match(child_type.base_type, inner_type) {
412 g.gen_expr(child_id)
413 } else {
414 g.write('(${inner_ct}[]){')
415 g.gen_expr_with_expected_type(child_id, inner_type)
416 g.write('}')
417 }
418 g.write(', sizeof(${inner_ct}))')
419 return
420 }
421 }
422 if field.typ.len > 0 {
423 g.gen_expr_with_expected_type(child_id, g.tc.parse_type(field.typ))
424 } else {
425 g.gen_expr(child_id)
426 }
427}
428
429// gen_channel_init emits channel init output for c.
430fn (mut g FlatGen) gen_channel_init(node flat.Node) {
431 elem_type := g.tc.parse_type(node.value[5..])
432 elem_ct := g.tc.c_type(elem_type)
433 g.write('sync__new_channel_st((u32)(')
434 if cap_id := channel_init_field(node, g.a, 'cap') {
435 g.gen_expr(cap_id)
436 } else {
437 g.write('0')
438 }
439 g.write('), (u32)(sizeof(${elem_ct})))')
440}
441
442// channel_init_field supports channel init field handling for c.
443fn channel_init_field(node flat.Node, a &flat.FlatAst, name string) ?flat.NodeId {
444 for i in 0 .. node.children_count {
445 field := a.child_node(&node, i)
446 if field.kind == .field_init && field.value == name && field.children_count > 0 {
447 return a.child(field, 0)
448 }
449 }
450 return none
451}
452
453// gen_heap_struct_init emits heap struct init output for c.
454fn (mut g FlatGen) gen_heap_struct_init(node flat.Node) {
455 init_module := g.tc.cur_module
456 mut name := g.struct_init_c_type_name(node.value)
457 // A bare generic heap literal (`&Vec4{..}`) carries no type args; when the
458 // surrounding expected type fixes them (e.g. a `&Vec4[f32]` return), emit the
459 // concrete instance name so the materialized struct matches the value path.
460 if inst := g.generic_struct_init_instance_ct(node.value) {
461 name = inst
462 }
463 sum_name := g.resolve_sum_name(node.value)
464 is_sum_literal := sum_name in g.tc.sum_types
465 // A bare generic literal stores its fields under the concrete instance key (`Box[int]`);
466 // the bare `node.value` (`Box`) entry is removed by monomorphization, so resolve the
467 // instance for the fixed-array-field test, field-type lookups, and omitted-default emission.
468 lookup_name := g.struct_init_fields_key(node.value, node.value)
469 if !is_sum_literal && !g.is_interface_type_name(node.value)
470 && g.struct_init_has_fixed_array_field(node, lookup_name) {
471 // Fixed-array fields can't be set in the `&(T){...}` compound literal; build
472 // via a temp + memcpy and memdup the result.
473 g.gen_struct_init_with_fixed_array_fields_impl(node, name, init_module, true)
474 return
475 }
476 g.write('(${name}*)memdup(&(${name}){')
477 mut allowed_fields := map[string]bool{}
478 if fields := g.struct_fields_for_type(lookup_name) {
479 for f in fields {
480 allowed_fields[f.name] = true
481 }
482 }
483 mut set_fields := map[string]bool{}
484 mut has_field := false
485 if g.is_interface_type_name(node.value) {
486 if tid := g.interface_init_typ_id(node) {
487 g.write('._typ = ${tid}')
488 has_field = true
489 }
490 }
491 for i in 0 .. node.children_count {
492 field := g.a.child_node(&node, i)
493 if field.value.len > 0 && allowed_fields.len > 0 && field.value !in allowed_fields {
494 continue
495 }
496 if has_field {
497 g.write(', ')
498 }
499 value_id := g.a.child(field, 0)
500 if field.value.len == 0 {
501 // Positional initializer (empty field name): emit a positional C value mapped
502 // to the field at this index (mirrors gen_struct_init); a `. = v` designator
503 // is invalid C.
504 if sf := g.struct_field_at(lookup_name, i) {
505 if heap_copy_type := g.heap_copy_type_for_sum_pointer_field(lookup_name, sf.name,
506 value_id)
507 {
508 inner_ct := g.tc.c_type(heap_copy_type)
509 g.write('(${inner_ct}*)memdup(')
510 g.gen_expr(value_id)
511 g.write(', sizeof(${inner_ct}))')
512 } else {
513 g.gen_struct_field_expr_for_field(value_id, node.value, sf.name, sf.typ)
514 }
515 set_fields[sf.name] = true
516 } else {
517 g.gen_expr(value_id)
518 }
519 has_field = true
520 continue
521 }
522 g.write('.${c_field_name(field.value)} = ')
523 if is_sum_literal {
524 g.gen_lowered_sum_field_value(sum_name, field)
525 } else if heap_copy_type := g.heap_copy_type_for_sum_pointer_field(node.value, field.value,
526 value_id)
527 {
528 inner_ct := g.tc.c_type(heap_copy_type)
529 g.write('(${inner_ct}*)memdup(')
530 g.gen_expr(value_id)
531 g.write(', sizeof(${inner_ct}))')
532 } else {
533 if ftyp := g.struct_field_type(lookup_name, field.value) {
534 if g.struct_field_value_is_plainly_incompatible(value_id, ftyp) {
535 g.gen_default_value_for_type(ftyp)
536 } else {
537 g.gen_struct_field_expr_for_field(value_id, node.value, field.value, ftyp)
538 }
539 } else {
540 g.gen_expr(value_id)
541 }
542 }
543 set_fields[field.value] = true
544 has_field = true
545 }
546 after_fields_module := g.tc.cur_module
547 g.tc.cur_module = init_module
548 sname := g.struct_init_resolved_decl_name(node.value)
549 g.tc.cur_module = after_fields_module
550 has_field = g.gen_struct_default_fields(sname, mut set_fields, has_field)
551 defaults_key := if lookup_name in g.tc.structs { lookup_name } else { sname }
552 if defaults_key in g.tc.structs {
553 for f in g.tc.structs[defaults_key] {
554 if f.name in set_fields {
555 continue
556 }
557 if f.typ is types.Map {
558 if has_field {
559 g.write(', ')
560 }
561 g.write('.${c_field_name(f.name)} = ')
562 g.write_new_map(f.typ.key_type, f.typ.value_type)
563 has_field = true
564 } else if f.typ is types.Array {
565 c_elem := g.tc.c_type(f.typ.elem_type)
566 if has_field {
567 g.write(', ')
568 }
569 g.write('.${c_field_name(f.name)} = array_new(sizeof(${c_elem}), 0, 0)')
570 has_field = true
571 } else if g.field_needs_default_init(f.typ) {
572 if has_field {
573 g.write(', ')
574 }
575 g.write('.${c_field_name(f.name)} = ')
576 g.gen_default_value_for_type(f.typ)
577 has_field = true
578 }
579 }
580 }
581 g.write('}, sizeof(${name}))')
582}
583
584// heap_copy_type_for_sum_pointer_field supports heap_copy_type_for_sum_pointer_field handling in c.
585fn (g &FlatGen) heap_copy_type_for_sum_pointer_field(type_name string, field_name string, value_id flat.NodeId) ?types.Type {
586 resolved_sum := g.resolve_sum_name(type_name)
587 if resolved_sum !in g.tc.sum_types || int(value_id) < 0 {
588 return none
589 }
590 value := g.a.nodes[int(value_id)]
591 if !g.pointer_variant_arg_needs_heap_copy(value) {
592 return none
593 }
594 for variant in g.tc.sum_types[resolved_sum] {
595 if g.sum_field_name(variant) != field_name
596 || !g.variant_references_sum(variant, resolved_sum) {
597 continue
598 }
599 variant_type := g.tc.parse_type(g.resolve_variant(resolved_sum, variant))
600 return variant_type
601 }
602 return none
603}
604
605// gen_struct_default_fields emits struct default fields output for c.
606fn (mut g FlatGen) gen_struct_default_fields(type_name string, mut set_fields map[string]bool, has_field bool) bool {
607 mut has := has_field
608 info := g.find_struct_decl(type_name) or { return has }
609 old_module := g.tc.cur_module
610 g.tc.cur_module = info.module
611 for i in 0 .. info.node.children_count {
612 field := g.a.child_node(&info.node, i)
613 if field.kind != .field_decl || field.children_count == 0 || field.value in set_fields {
614 continue
615 }
616 if has {
617 g.write(', ')
618 }
619 g.write('.${c_name(field.value)} = ')
620 g.gen_struct_field_expr_for_field(g.a.child(field, 0), info.full_name, field.value, g.struct_default_field_type(info,
621 field))
622 set_fields[field.value] = true
623 has = true
624 }
625 g.tc.cur_module = old_module
626 return has
627}
628
629fn (mut g FlatGen) struct_default_field_type(info StructDeclInfo, field flat.Node) types.Type {
630 if field.typ.len > 0 && !field.typ.contains('.') && info.module.len > 0 && info.module != 'main'
631 && info.module != 'builtin' {
632 qtyp := '${info.module}.${field.typ}'
633 if qtyp in g.tc.enum_names || qtyp in g.tc.structs || qtyp in g.tc.sum_types
634 || qtyp in g.tc.interface_names {
635 return g.tc.parse_type(qtyp)
636 }
637 }
638 return g.tc.parse_type(field.typ)
639}
640
641// gen_default_value_for_type emits default value for type output for c.
642fn (mut g FlatGen) gen_default_value_for_type(typ types.Type) {
643 raw_typ := typ
644 if typ is types.OptionType || typ is types.ResultType {
645 ct := g.optional_type_name(typ)
646 g.write('(${ct}){0}')
647 return
648 }
649 if typ is types.Struct && !typ.name.starts_with('C.') {
650 ct := g.tc.c_type(raw_typ)
651 g.write('(${ct}){')
652 mut set_fields := map[string]bool{}
653 mut has_field := g.gen_struct_default_fields(typ.name, mut set_fields, false)
654 mut sname := g.tc.qualify_name(typ.name)
655 if typ.name in g.tc.structs {
656 sname = typ.name
657 }
658 if sname in g.tc.structs {
659 for f in g.tc.structs[sname] {
660 if f.name in set_fields {
661 continue
662 }
663 if f.typ is types.Map {
664 if has_field {
665 g.write(', ')
666 }
667 g.write('.${c_name(f.name)} = ')
668 g.write_new_map(f.typ.key_type, f.typ.value_type)
669 has_field = true
670 } else if f.typ is types.Array {
671 c_elem := g.tc.c_type(f.typ.elem_type)
672 if has_field {
673 g.write(', ')
674 }
675 g.write('.${c_name(f.name)} = array_new(sizeof(${c_elem}), 0, 0)')
676 has_field = true
677 } else if g.field_needs_default_init(f.typ) {
678 if has_field {
679 g.write(', ')
680 }
681 g.write('.${c_name(f.name)} = ')
682 g.gen_default_value_for_type(f.typ)
683 has_field = true
684 }
685 }
686 }
687 g.write('}')
688 return
689 }
690 ct := g.tc.c_type(typ)
691 if g.is_scalar_c_type(ct) {
692 g.write(g.scalar_zero_init(ct))
693 return
694 }
695 g.write('(${ct}){0}')
696}
697
698fn (g &FlatGen) struct_field_value_is_plainly_incompatible(value_id flat.NodeId, field_type types.Type) bool {
699 value_type := g.tc.resolve_type(value_id)
700 if field_type is types.Primitive && value_type is types.Struct {
701 return true
702 }
703 if field_type is types.Primitive && value_type is types.SumType {
704 return true
705 }
706 return false
707}
708
709// field_needs_default_init reports whether an unset field of type `typ` must be
710// explicitly default-initialized in a struct literal — i.e. it is a by-value
711// struct whose type carries field defaults that C's `{0}` would not apply
712// (e.g. `min_len int = 999999`).
713fn (mut g FlatGen) field_needs_default_init(typ types.Type) bool {
714 if typ is types.Struct && !typ.name.starts_with('C.') {
715 return g.struct_has_field_defaults(typ.name)
716 }
717 return false
718}
719
720// struct_has_field_defaults reports whether building `type_name` as a struct
721// literal would set any non-zero field: a field with an explicit default
722// (`x int = 5`), or a by-value struct field whose own type has such defaults.
723// Returns false for structs with interface/sum-typed field defaults, since the
724// codegen default path cannot box those values.
725fn (mut g FlatGen) struct_has_field_defaults(type_name string) bool {
726 mut visited := map[string]bool{}
727 return g.struct_has_field_defaults_inner(type_name, mut visited)
728}
729
730// struct_has_field_defaults_inner converts struct has field defaults inner data for c.
731fn (mut g FlatGen) struct_has_field_defaults_inner(type_name string, mut visited map[string]bool) bool {
732 if type_name in visited {
733 return false
734 }
735 visited[type_name] = true
736 info := g.find_struct_decl(type_name) or { return false }
737 old_module := g.tc.cur_module
738 g.tc.cur_module = info.module
739 defer {
740 g.tc.cur_module = old_module
741 }
742 mut found := false
743 for i in 0 .. info.node.children_count {
744 field := g.a.child_node(&info.node, i)
745 if field.kind != .field_decl {
746 continue
747 }
748 ftyp := g.tc.parse_type(field.typ)
749 if field.children_count > 0 {
750 // Defaults for interface/sum-typed fields require boxing the value into
751 // the interface/sum representation, which the codegen default path cannot
752 // do. Treat the whole struct as unsafe to default-emit (leave it
753 // zero-initialized, as before) rather than emit an unboxed value.
754 if ftyp is types.SumType || ftyp is types.Interface {
755 return false
756 }
757 found = true
758 }
759 if ftyp is types.Struct && !ftyp.name.starts_with('C.') {
760 if g.struct_has_field_defaults_inner(ftyp.name, mut visited) {
761 found = true
762 }
763 }
764 }
765 return found
766}
767
768// gen_params_struct_arg emits a struct literal for a `@[params]` argument passed as
769// trailing `key: value` call args (e.g. `atof64(s, allow_extra_chars: true)`).
770// `node` is the call node; field_init children are read from `field_start` onward.
771fn (mut g FlatGen) gen_params_struct_arg(typ types.Type, node flat.Node, field_start int) {
772 raw_typ := typ
773 if typ is types.Struct {
774 ct := g.tc.c_type(raw_typ)
775 g.write('(${ct}){')
776 mut set_fields := map[string]bool{}
777 mut has_field := false
778 for i in field_start .. node.children_count {
779 field := g.a.child_node(&node, i)
780 if field.kind != .field_init || field.children_count == 0 {
781 continue
782 }
783 if has_field {
784 g.write(', ')
785 }
786 g.write('.${c_name(field.value)} = ')
787 if ftyp := g.struct_field_type(typ.name, field.value) {
788 g.gen_struct_field_expr_for_field(g.a.child(field, 0), typ.name, field.value, ftyp)
789 } else {
790 g.gen_expr(g.a.child(field, 0))
791 }
792 set_fields[field.value] = true
793 has_field = true
794 }
795 mut sname := g.tc.qualify_name(typ.name)
796 if typ.name in g.tc.structs {
797 sname = typ.name
798 }
799 has_field = g.gen_struct_default_fields(typ.name, mut set_fields, has_field)
800 if sname in g.tc.structs {
801 for f in g.tc.structs[sname] {
802 if f.name in set_fields {
803 continue
804 }
805 if f.typ is types.Map {
806 if has_field {
807 g.write(', ')
808 }
809 g.write('.${c_name(f.name)} = ')
810 g.write_new_map(f.typ.key_type, f.typ.value_type)
811 has_field = true
812 } else if f.typ is types.Array {
813 c_elem := g.tc.c_type(f.typ.elem_type)
814 if has_field {
815 g.write(', ')
816 }
817 g.write('.${c_name(f.name)} = array_new(sizeof(${c_elem}), 0, 0)')
818 has_field = true
819 } else if g.field_needs_default_init(f.typ) {
820 if has_field {
821 g.write(', ')
822 }
823 g.write('.${c_name(f.name)} = ')
824 g.gen_default_value_for_type(f.typ)
825 has_field = true
826 }
827 }
828 }
829 g.write('}')
830 return
831 }
832 g.gen_default_value_for_type(typ)
833}
834
835// is_scalar_zero_init_type reports whether is scalar zero init type applies in c.
836fn (g &FlatGen) is_scalar_zero_init_type(type_name string, c_type string) bool {
837 if type_name in g.tc.structs || g.tc.qualify_name(type_name) in g.tc.structs {
838 return false
839 }
840 if _ := g.find_struct_decl(type_name) {
841 return false
842 }
843 return g.is_scalar_c_type(c_type)
844}
845
846// is_scalar_c_type reports whether is scalar c type applies in c.
847fn (g &FlatGen) is_scalar_c_type(c_type string) bool {
848 if c_type.ends_with('*') {
849 return true
850 }
851 return c_type in ['bool', 'char', 'byte', 'u8', 'i8', 'u16', 'i16', 'u32', 'i32', 'u64', 'i64',
852 'int', 'isize', 'usize', 'size_t', 'ptrdiff_t', 'float', 'double', 'voidptr']
853}
854
855// is_aggregate_zero_init_type reports whether is aggregate zero init type applies in c.
856fn (g &FlatGen) is_aggregate_zero_init_type(typ types.Type, c_type string) bool {
857 if g.is_scalar_c_type(c_type) {
858 return false
859 }
860 return match typ {
861 types.Alias {
862 g.is_aggregate_zero_init_type(typ.base_type, c_type)
863 }
864 types.Array, types.ArrayFixed, types.Channel, types.Map, types.String, types.Struct,
865 types.Interface, types.SumType, types.OptionType, types.ResultType, types.MultiReturn {
866 true
867 }
868 else {
869 false
870 }
871 }
872}
873
874// can_use_global_brace_zero_init reports whether can use global brace zero init applies in c.
875fn (mut g FlatGen) can_use_global_brace_zero_init(typ types.Type, c_type string) bool {
876 return g.is_aggregate_zero_init_type(typ, c_type) && !g.has_zero_sized_leading_init_slot(typ)
877}
878
879// has_zero_sized_leading_init_slot reports whether has zero sized leading init slot applies in c.
880fn (mut g FlatGen) has_zero_sized_leading_init_slot(typ types.Type) bool {
881 mut visited := map[string]bool{}
882 return g.has_zero_sized_leading_init_slot_inner(typ, mut visited)
883}
884
885// has_zero_sized_leading_init_slot_inner reports has_zero_sized_leading_init_slot_inner logic in c.
886fn (mut g FlatGen) has_zero_sized_leading_init_slot_inner(typ types.Type, mut visited map[string]bool) bool {
887 return match typ {
888 types.Alias {
889 g.has_zero_sized_leading_init_slot_inner(typ.base_type, mut visited)
890 }
891 types.ArrayFixed {
892 if g.fixed_array_len_is_zero(typ) {
893 true
894 } else {
895 g.has_zero_sized_leading_init_slot_inner(typ.elem_type, mut visited)
896 }
897 }
898 types.Struct {
899 if info := g.find_struct_decl(typ.name) {
900 if info.full_name in visited {
901 false
902 } else {
903 visited[info.full_name] = true
904 old_module := g.tc.cur_module
905 g.tc.cur_module = info.module
906 first := g.struct_field_at(info.full_name, 0) or {
907 g.tc.cur_module = old_module
908 return false
909 }
910 has := g.has_zero_sized_leading_init_slot_inner(first.typ, mut visited)
911 g.tc.cur_module = old_module
912 has
913 }
914 } else {
915 if typ.name in visited {
916 false
917 } else {
918 visited[typ.name] = true
919 first := g.struct_field_at(typ.name, 0) or { return false }
920 g.has_zero_sized_leading_init_slot_inner(first.typ, mut visited)
921 }
922 }
923 }
924 else {
925 false
926 }
927 }
928}
929
930// scalar_zero_init supports scalar zero init handling for FlatGen.
931fn (g &FlatGen) scalar_zero_init(c_type string) string {
932 if c_type in ['float', 'double'] {
933 return '0.0'
934 }
935 return '0'
936}
937
938// StructDeclInfo stores struct decl info metadata used by c.
939struct StructDeclInfo {
940 node flat.Node
941 module string
942 full_name string
943}
944
945// struct_init_c_type_name supports struct init c type name handling for FlatGen.
946// generic_struct_init_instance_ct returns the concrete-instance C type name for a
947// bare generic struct literal whose type args are pinned by the surrounding expected
948// type (e.g. `Vec4{..}` written where a `Vec4[f32]` is expected). Returns none when
949// the literal is already specialized, the base is not a generic struct, or the
950// expected type is not a matching concrete instance.
951fn (g &FlatGen) generic_struct_init_instance_ct(type_name string) ?string {
952 return g.tc.c_type(g.generic_struct_init_instance_type(type_name)?)
953}
954
955// generic_struct_init_instance_name is the concrete-instance V type name (`Box[int]`)
956// for a bare generic struct literal, so field and default lookups use the materialized
957// key under which the struct's fields are stored (the bare `Box` entry is removed by
958// monomorphization), not just the emitted C type.
959fn (g &FlatGen) generic_struct_init_instance_name(type_name string) ?string {
960 return g.generic_struct_init_instance_type(type_name)?.name()
961}
962
963// generic_struct_init_instance_type resolves the concrete generic instance a bare literal
964// adopts from the surrounding expected/return type (e.g. `Vec4{..}` -> `Vec4[f32]`).
965// Returns none when the literal is already specialized, the base is not a generic struct,
966// or the expected type is not a matching concrete instance.
967fn (g &FlatGen) generic_struct_init_instance_type(type_name string) ?types.Type {
968 if type_name.contains('[') {
969 return none
970 }
971 short := type_name.all_after_last('.')
972 // Prefer the explicit expected type; fall back to the enclosing function's return
973 // type ONLY for a literal in return position (a bare generic literal there carries
974 // no expected_expr_type) — otherwise a `Box{...}` in a local decl / argument whose
975 // expected type is the bare `Box` would be wrongly materialised as `Box_int`. Only
976 // adopt a candidate whose generic base matches the literal's base, so unrelated
977 // expected types never rename the struct.
978 //
979 // Note: `tc.struct_generic_params` is empty by cgen time, so the candidate's
980 // shape (a `Base[args]` instance whose base short-name equals the literal's) is
981 // the sole evidence that this bare literal is a generic struct instantiation.
982 mut candidates := [g.expected_expr_type]
983 if g.in_return {
984 candidates << g.cur_fn_ret
985 }
986 for cand in candidates {
987 // Unwrap a pointer so a `&Box[int]` expected type still matches a bare `Box`
988 // literal — the heap path (`&Box{..}`) needs the struct (`Box_int`), not the
989 // pointer, type name.
990 base_cand := types.unwrap_pointer(cand)
991 // A fixed/dynamic array type is not a generic struct instance even though its
992 // `.name()` renders like one (`[2]Foo` -> `Foo[2]`); skip it so a `Foo{..}` element
993 // of a `[2]Foo` literal keeps its element type instead of adopting the array type.
994 if base_cand is types.ArrayFixed || base_cand is types.Array {
995 continue
996 }
997 cand_name := base_cand.name()
998 if !cand_name.contains('[') {
999 continue
1000 }
1001 cand_base := cand_name.all_before('[')
1002 if cand_base.len == 0 || cand_base.all_after_last('.') != short {
1003 continue
1004 }
1005 return base_cand
1006 }
1007 return none
1008}
1009
1010fn (mut g FlatGen) struct_init_c_type_name(type_name string) string {
1011 typ := g.tc.parse_type(type_name)
1012 if typ is types.OptionType || typ is types.ResultType {
1013 return g.optional_type_name(typ)
1014 }
1015 info := g.find_struct_decl(type_name) or { return g.tc.c_type(g.tc.parse_type(type_name)) }
1016 if info.full_name.starts_with('C.') {
1017 return g.tc.c_type(g.tc.parse_type(info.full_name))
1018 }
1019 return c_name(info.full_name)
1020}
1021
1022// find_struct_decl resolves find struct decl information for c.
1023fn (g &FlatGen) find_struct_decl(type_name string) ?StructDeclInfo {
1024 if info := g.find_struct_decl_preferred(type_name) {
1025 return info
1026 }
1027 if alias_target := g.struct_type_alias_target(type_name) {
1028 if info := g.find_struct_decl_preferred(alias_target) {
1029 return info
1030 }
1031 if info := g.find_struct_decl_fallback(alias_target) {
1032 return info
1033 }
1034 }
1035 return g.find_struct_decl_fallback(type_name)
1036}
1037
1038fn (g &FlatGen) find_struct_decl_preferred(type_name string) ?StructDeclInfo {
1039 short_name := if type_name.contains('.') { type_name.all_after_last('.') } else { type_name }
1040 preferred_name := if !type_name.contains('.') && g.tc.cur_module.len > 0
1041 && g.tc.cur_module != 'main' && g.tc.cur_module != 'builtin' {
1042 '${g.tc.cur_module}.${type_name}'
1043 } else {
1044 type_name
1045 }
1046 if info := g.struct_decl_infos[preferred_name] {
1047 if info.node.value == short_name {
1048 return info
1049 }
1050 }
1051 if type_name.contains('.') {
1052 if info := g.struct_decl_infos[type_name] {
1053 return info
1054 }
1055 }
1056 return none
1057}
1058
1059fn (g &FlatGen) find_struct_decl_fallback(type_name string) ?StructDeclInfo {
1060 if type_name.contains('.') {
1061 return none
1062 }
1063 if info := g.struct_decl_short_infos[type_name] {
1064 return info
1065 }
1066 return none
1067}
1068
1069fn (g &FlatGen) struct_type_alias_target(type_name string) ?string {
1070 qname := g.tc.qualify_name(type_name)
1071 if target := g.tc.type_aliases[qname] {
1072 return target
1073 }
1074 if target := g.tc.type_aliases[type_name] {
1075 return target
1076 }
1077 return none
1078}
1079
1080fn (g &FlatGen) struct_init_resolved_decl_name(type_name string) string {
1081 if info := g.find_struct_decl(type_name) {
1082 return info.full_name
1083 }
1084 qname := g.tc.qualify_name(type_name)
1085 if qname in g.tc.structs {
1086 return qname
1087 }
1088 return type_name
1089}
1090
1091// struct_field_type supports struct field type handling for FlatGen.
1092fn (g &FlatGen) struct_field_type(type_name string, field_name string) ?types.Type {
1093 fields := g.struct_fields_for_type(type_name) or { return none }
1094 for f in fields {
1095 if f.name == field_name {
1096 return f.typ
1097 }
1098 }
1099 return none
1100}
1101
1102fn (g &FlatGen) struct_field_c_abi_fn_ptr_type(type_name string, field_name string) ?string {
1103 if info := g.find_struct_decl(type_name) {
1104 if typ := g.tc.struct_field_c_abi_fn_ptr_type(info.full_name, field_name) {
1105 return typ
1106 }
1107 }
1108 if typ := g.tc.struct_field_c_abi_fn_ptr_type(type_name, field_name) {
1109 return typ
1110 }
1111 if !type_name.contains('.') {
1112 qname := g.tc.qualify_name(type_name)
1113 if qname != type_name {
1114 if typ := g.tc.struct_field_c_abi_fn_ptr_type(qname, field_name) {
1115 return typ
1116 }
1117 }
1118 }
1119 return none
1120}
1121
1122// precompute_embedded_fields records, per struct type, only its embedded fields (those
1123// whose field name is the embedded type name). Most structs have none. Done once so the
1124// per-selector embedded-field resolution doesn't rescan (and re-c_name) every field of
1125// the receiver struct on every field access — a major cgen cost after #27538.
1126fn (mut g FlatGen) precompute_embedded_fields() {
1127 for type_name, fields in g.tc.structs {
1128 mut emb := []types.StructField{}
1129 for field in fields {
1130 if g.embedded_field_type_name(field).len > 0 {
1131 emb << field
1132 }
1133 }
1134 g.embedded_fields_by_type[type_name] = emb
1135 }
1136}
1137
1138// struct_embedded_fields returns the embedded fields of a type (mirrors
1139// struct_fields_for_type's key resolution against the precomputed map). Returns an empty
1140// slice for non-embedding structs, which is the common case.
1141fn (g &FlatGen) struct_embedded_fields(type_name string) []types.StructField {
1142 if emb := g.embedded_fields_by_type[type_name] {
1143 return emb
1144 }
1145 qname := g.tc.qualify_name(type_name)
1146 if emb := g.embedded_fields_by_type[qname] {
1147 return emb
1148 }
1149 if info := g.find_struct_decl(type_name) {
1150 if emb := g.embedded_fields_by_type[info.full_name] {
1151 return emb
1152 }
1153 }
1154 if type_name.contains('.') {
1155 short_name := type_name.all_after_last('.')
1156 if emb := g.embedded_fields_by_type[short_name] {
1157 return emb
1158 }
1159 }
1160 return []
1161}
1162
1163fn (g &FlatGen) struct_fields_for_type(type_name string) ?[]types.StructField {
1164 if info := g.find_struct_decl(type_name) {
1165 if fields := g.tc.structs[info.full_name] {
1166 return fields
1167 }
1168 }
1169 if type_name.contains('.') {
1170 if fields := g.tc.structs[type_name] {
1171 return fields
1172 }
1173 } else {
1174 qname := g.tc.qualify_name(type_name)
1175 if qname != type_name {
1176 if fields := g.tc.structs[qname] {
1177 return fields
1178 }
1179 }
1180 }
1181 if fields := g.tc.structs[type_name] {
1182 return fields
1183 }
1184 if type_name.contains('.') {
1185 short_name := type_name.all_after_last('.')
1186 if fields := g.tc.structs[short_name] {
1187 return fields
1188 }
1189 }
1190 return none
1191}
1192
1193fn (g &FlatGen) embedded_field_type_name(field types.StructField) string {
1194 clean_type := types.unwrap_pointer(field.typ)
1195 field_type_name := clean_type.name()
1196 if field_type_name.len == 0 {
1197 return ''
1198 }
1199 mut names := [field_type_name]
1200 base_name := types.generic_base_name(field_type_name)
1201 if base_name != field_type_name {
1202 names << base_name
1203 }
1204 short_field := if field.name.contains('.') { field.name.all_after_last('.') } else { field.name }
1205 for name in names {
1206 short_type := if name.contains('.') { name.all_after_last('.') } else { name }
1207 if field.name == name || short_field == short_type || c_name(field.name) == c_name(name) {
1208 return field_type_name
1209 }
1210 }
1211 return ''
1212}
1213
1214fn (g &FlatGen) direct_struct_field_exists(type_name string, field_name string) bool {
1215 fields := g.struct_fields_for_type(type_name) or { return false }
1216 for field in fields {
1217 if field.name == field_name {
1218 return true
1219 }
1220 }
1221 return false
1222}
1223
1224fn (g &FlatGen) embedded_field_for_promoted_field(type_name string, field_name string) ?types.StructField {
1225 path := g.embedded_field_path_for_promoted_field(type_name, field_name) or { return none }
1226 if path.len == 0 {
1227 return none
1228 }
1229 return path[0]
1230}
1231
1232fn (g &FlatGen) direct_embedded_field_for_selector(base_type types.Type, field_name string) ?types.StructField {
1233 type_name := g.type_lookup_name(base_type)
1234 if type_name.len == 0 {
1235 return none
1236 }
1237 // Only the embedded fields (precomputed) can match — no need to scan every field.
1238 for field in g.struct_embedded_fields(type_name) {
1239 embedded_type_name := g.embedded_field_type_name(field)
1240 if embedded_type_name.len == 0 {
1241 continue
1242 }
1243 short_type := if embedded_type_name.contains('.') {
1244 embedded_type_name.all_after_last('.')
1245 } else {
1246 embedded_type_name
1247 }
1248 if field_name == embedded_type_name || field_name == short_type
1249 || c_name(field_name) == c_name(embedded_type_name) {
1250 return field
1251 }
1252 }
1253 return none
1254}
1255
1256fn (g &FlatGen) embedded_field_path_for_promoted_field(type_name string, field_name string) ?[]types.StructField {
1257 for field in g.struct_embedded_fields(type_name) {
1258 embedded_type_name := g.embedded_field_type_name(field)
1259 if embedded_type_name.len == 0 {
1260 continue
1261 }
1262 if g.direct_struct_field_exists(embedded_type_name, field_name) {
1263 return [field]
1264 }
1265 if nested := g.embedded_field_path_for_promoted_field(embedded_type_name, field_name) {
1266 mut path := [field]
1267 path << nested
1268 return path
1269 }
1270 }
1271 return none
1272}
1273
1274fn (g &FlatGen) embedded_field_path_for_promoted_selector(base_type types.Type, field_name string) ?[]types.StructField {
1275 type_name := g.type_lookup_name(base_type)
1276 if type_name.len == 0 {
1277 return none
1278 }
1279 return g.embedded_field_path_for_promoted_field(type_name, field_name)
1280}
1281
1282fn (g &FlatGen) embedded_field_for_promoted_selector(base_type types.Type, field_name string) ?types.StructField {
1283 type_name := g.type_lookup_name(base_type)
1284 if type_name.len == 0 {
1285 return none
1286 }
1287 return g.embedded_field_for_promoted_field(type_name, field_name)
1288}
1289
1290fn (g &FlatGen) type_lookup_name(typ types.Type) string {
1291 clean_type := types.unwrap_pointer(typ)
1292 if clean_type is types.Alias {
1293 return clean_type.base_type.name()
1294 }
1295 return clean_type.name()
1296}
1297
1298// struct_field_at supports struct field at handling for FlatGen.
1299fn (g &FlatGen) struct_field_at(type_name string, index int) ?types.StructField {
1300 if index < 0 {
1301 return none
1302 }
1303 fields := g.struct_fields_for_type(type_name) or { return none }
1304 if index < fields.len {
1305 return fields[index]
1306 }
1307 return none
1308}
1309
1310// struct_field_type_at supports struct field type at handling for FlatGen.
1311fn (g &FlatGen) struct_field_type_at(type_name string, index int) ?types.Type {
1312 if field := g.struct_field_at(type_name, index) {
1313 return field.typ
1314 }
1315 return none
1316}
1317
1318// gen_return_assoc emits return assoc output for c.
1319fn (mut g FlatGen) gen_return_assoc(node flat.Node) {
1320 tmp := g.tmp_name()
1321 g.gen_assoc_return_tmp(node, tmp)
1322 g.writeln('return ${tmp};')
1323}
1324
1325fn (mut g FlatGen) gen_assoc_return_tmp(node flat.Node, tmp string) {
1326 ct := g.tc.c_type(g.tc.parse_type(node.value))
1327 g.write('${ct} ${tmp} = ')
1328 g.gen_expr(g.a.child(&node, 0))
1329 g.writeln(';')
1330 for i in 1 .. node.children_count {
1331 field := g.a.child_node(&node, i)
1332 if field.kind == .field_init && field.children_count > 0 {
1333 g.write('${tmp}.${c_name(field.value)} = ')
1334 if ftyp := g.struct_field_type(node.value, field.value) {
1335 g.gen_struct_field_expr_for_field(g.a.child(field, 0), node.value, field.value,
1336 ftyp)
1337 } else {
1338 g.gen_expr(g.a.child(field, 0))
1339 }
1340 g.writeln(';')
1341 }
1342 }
1343}
1344
1345// gen_assoc_expr emits assoc expr output for c.
1346fn (mut g FlatGen) gen_assoc_expr(node flat.Node) {
1347 ct := g.tc.c_type(g.tc.parse_type(node.value))
1348 tmp := g.tmp_name()
1349 g.write('({${ct} ${tmp} = ')
1350 g.gen_expr(g.a.child(&node, 0))
1351 g.write(';')
1352 for i in 1 .. node.children_count {
1353 field := g.a.child_node(&node, i)
1354 if field.kind == .field_init && field.children_count > 0 {
1355 g.write(' ${tmp}.${c_name(field.value)} = ')
1356 if ftyp := g.struct_field_type(node.value, field.value) {
1357 g.gen_struct_field_expr_for_field(g.a.child(field, 0), node.value, field.value,
1358 ftyp)
1359 } else {
1360 g.gen_expr(g.a.child(field, 0))
1361 }
1362 g.write(';')
1363 }
1364 }
1365 g.write(' ${tmp};})')
1366}
1367
1368// gen_heap_assoc_expr emits heap assoc expr output for c.
1369fn (mut g FlatGen) gen_heap_assoc_expr(node flat.Node) {
1370 ct := g.tc.c_type(g.tc.parse_type(node.value))
1371 tmp := g.tmp_name()
1372 g.write('({${ct} ${tmp} = ')
1373 g.gen_expr(g.a.child(&node, 0))
1374 g.write(';')
1375 for i in 1 .. node.children_count {
1376 field := g.a.child_node(&node, i)
1377 if field.kind == .field_init && field.children_count > 0 {
1378 g.write(' ${tmp}.${c_name(field.value)} = ')
1379 if ftyp := g.struct_field_type(node.value, field.value) {
1380 g.gen_struct_field_expr_for_field(g.a.child(field, 0), node.value, field.value,
1381 ftyp)
1382 } else {
1383 g.gen_expr(g.a.child(field, 0))
1384 }
1385 g.write(';')
1386 }
1387 }
1388 g.write(' (${ct}*)memdup(&${tmp}, sizeof(${ct}));})')
1389}
1390
1391// gen_map_init emits map init output for c.
1392fn (mut g FlatGen) gen_map_init(id flat.NodeId, node flat.Node) {
1393 if node.value.len > 0 {
1394 map_type := g.tc.parse_type(node.value)
1395 if map_type is types.Map {
1396 g.write_new_map(map_type.key_type, map_type.value_type)
1397 return
1398 }
1399 }
1400 if node.typ.len > 0 {
1401 map_type := g.tc.parse_type(node.typ)
1402 if map_type is types.Map {
1403 g.write_new_map(map_type.key_type, map_type.value_type)
1404 return
1405 }
1406 }
1407 if g.expected_expr_type is types.Map {
1408 g.write_new_map(g.expected_expr_type.key_type, g.expected_expr_type.value_type)
1409 return
1410 }
1411 resolved_type := g.tc.resolve_type(id)
1412 if resolved_type is types.Map {
1413 g.write_new_map(resolved_type.key_type, resolved_type.value_type)
1414 return
1415 }
1416 g.write('new_map(sizeof(int), sizeof(int), 0, 0, 0, 0)')
1417}
1418
1419// write_new_map writes new map output for c.
1420fn (mut g FlatGen) write_new_map(key_type types.Type, value_type types.Type) {
1421 c_key := g.value_sizeof_target(key_type)
1422 c_val := g.value_sizeof_target(value_type)
1423 hash_fn, eq_fn, clone_fn, free_fn := g.map_callback_names(key_type)
1424 g.write('new_map(sizeof(${c_key}), sizeof(${c_val}), ${hash_fn}, ${eq_fn}, ${clone_fn}, ${free_fn})')
1425}
1426
1427// map_callback_names supports map callback names handling for FlatGen.
1428fn (g &FlatGen) map_callback_names(key_type types.Type) (string, string, string, string) {
1429 if key_type is types.String {
1430 return 'map_hash_string', 'map_eq_string', 'map_clone_string', 'map_free_string'
1431 }
1432 c_key := g.tc.c_type(key_type)
1433 size_suffix := match c_key {
1434 'u8', 'i8', 'bool', 'char' { '1' }
1435 'u16', 'i16' { '2' }
1436 'i64', 'u64', 'isize', 'usize', 'voidptr' { '8' }
1437 else { '4' }
1438 }
1439
1440 return 'map_hash_int_${size_suffix}', 'map_eq_int_${size_suffix}', 'map_clone_int_${size_suffix}', 'map_free_nop'
1441}
1442
1443// skip_builtin_struct supports skip builtin struct handling for FlatGen.
1444fn (g &FlatGen) skip_builtin_struct(name string) bool {
1445 if name.starts_with('C.') && g.inlined_c_structs[name[2..]] {
1446 return true
1447 }
1448 return name in c_preamble_defined_structs
1449}
1450
1451const c_preamble_defined_structs = {
1452 'C.DIR': true
1453 'C.FILE': true
1454 'C.CONDITION_VARIABLE': true
1455 'C.IError': true
1456 'C.SRWLOCK': true
1457 'C.addrinfo': true
1458 'C.atomic_uintptr_t': true
1459 'C.dirent': true
1460 'C.epoll_data': true
1461 'C.epoll_data_t': true
1462 'C.epoll_event': true
1463 'C.Event': true
1464 'C.fd_set': true
1465 'C.CHAR_INFO': true
1466 'C.CONSOLE_SCREEN_BUFFER_INFO': true
1467 'C.COORD': true
1468 'C.FOCUS_EVENT_RECORD': true
1469 'C.INPUT_RECORD': true
1470 'C.KEY_EVENT_RECORD': true
1471 'C.kevent': true
1472 'C.MENU_EVENT_RECORD': true
1473 'C.MOUSE_EVENT_RECORD': true
1474 'C.pthread_t': true
1475 'C.pthread_cond_t': true
1476 'C.pthread_condattr_t': true
1477 'C.pthread_attr_t': true
1478 'C.pthread_mutex_t': true
1479 'C.pthread_rwlock_t': true
1480 'C.pthread_rwlockattr_t': true
1481 'C.rusage': true
1482 'C.sem_t': true
1483 'C.SECURITY_ATTRIBUTES': true
1484 'C.sigset_t': true
1485 'C.SMALL_RECT': true
1486 'C.OVERLAPPED': true
1487 'C.sockaddr': true
1488 'C.sockaddr_in': true
1489 'C.sockaddr_in6': true
1490 'C.sockaddr_un': true
1491 'C.stat': true
1492 'C.statvfs': true
1493 'C.termios': true
1494 'C.timeval': true
1495 'C.timespec': true
1496 'C.tm': true
1497 'C.uChar': true
1498 'C.utsname': true
1499 'C.wchar_t': true
1500 'C.WINDOW_BUFFER_SIZE_RECORD': true
1501 'C.winsize': true
1502}
1503
1504fn c_struct_needs_typedef(name string) bool {
1505 if !name.starts_with('C.') {
1506 return true
1507 }
1508 raw := name[2..]
1509 if raw.len > 0 && raw[0] >= `a` && raw[0] <= `z` && !raw.ends_with('_t') {
1510 return false
1511 }
1512 return true
1513}
1514
1515// emit_interface_struct emits emit interface struct output for c.
1516fn (mut g FlatGen) emit_interface_struct(name string) {
1517 cn := c_name(name)
1518 g.writeln('struct ${cn} {')
1519 g.writeln('\tint _typ;')
1520 if cn == 'IError' {
1521 g.writeln('\tvoid* _object;')
1522 g.writeln('\tstring message;')
1523 g.writeln('\tint code;')
1524 } else {
1525 // pointer to the boxed concrete value, used by method dispatch
1526 g.writeln('\tvoid* _object;')
1527 }
1528 for field in g.tc.interface_fields[name] or { []types.StructField{} } {
1529 ct := g.tc.c_type(field.typ)
1530 g.writeln('\t${ct} ${c_name(field.name)};')
1531 }
1532 g.writeln('};')
1533 g.writeln('')
1534}
1535
1536// struct_decls supports struct decls handling for FlatGen.
1537fn (mut g FlatGen) struct_decls() {
1538 // Fixed-array typedefs whose element is a struct are emitted interleaved with the
1539 // structs below (right after the element struct is defined), so struct fields that
1540 // reference them resolve. Primitive-element ones were already emitted earlier.
1541 fixed_array_needed := g.collect_fixed_array_typedefs_needed()
1542 for name, _ in g.tc.structs {
1543 if g.skip_builtin_struct(name) {
1544 continue
1545 }
1546 if !c_struct_needs_typedef(name) {
1547 continue
1548 }
1549 tag := if name in g.tc.unions { 'union' } else { 'struct' }
1550 g.writeln('typedef ${tag} ${c_name(name)} ${c_name(name)};')
1551 }
1552 for name, variants in g.tc.sum_types {
1553 g.writeln('typedef struct ${c_name(name)} ${c_name(name)};')
1554 _ = variants
1555 }
1556 for name, _ in g.interfaces {
1557 g.writeln('typedef struct ${c_name(name)} ${c_name(name)};')
1558 }
1559 if g.has_builtins {
1560 g.writeln('typedef array Array;')
1561 }
1562 mut emitted := map[string]bool{}
1563 mut remaining := map[string]bool{}
1564 mut remaining_cnames := map[string]bool{}
1565 mut iface_remaining := map[string]bool{}
1566 for name, _ in g.interfaces {
1567 iface_remaining[name] = true
1568 remaining_cnames[c_name(name)] = true
1569 }
1570 for name, _ in g.tc.structs {
1571 if g.skip_builtin_struct(name) {
1572 continue
1573 }
1574 remaining[name] = true
1575 remaining_cnames[c_name(name)] = true
1576 }
1577 mut sum_remaining := map[string]bool{}
1578 for name, _ in g.tc.sum_types {
1579 sum_remaining[name] = true
1580 remaining_cnames[c_name(name)] = true
1581 }
1582 if 'string' in remaining {
1583 g.emit_struct('string')
1584 emitted['string'] = true
1585 remaining.delete('string')
1586 remaining_cnames.delete('string')
1587 }
1588 mut has_ierror := false
1589 for name, _ in iface_remaining {
1590 if c_name(name) == 'IError' {
1591 g.emit_interface_struct(name)
1592 emitted['IError'] = true
1593 iface_remaining.delete(name)
1594 remaining_cnames.delete('IError')
1595 has_ierror = true
1596 break
1597 }
1598 }
1599 err_field := if has_ierror { 'IError err; ' } else { '' }
1600 g.writeln('typedef struct Optional { bool ok; ${err_field}int value; } Optional;')
1601 g.writeln('')
1602 if g.has_builtins && 'array' in remaining {
1603 g.emit_struct('array')
1604 emitted['array'] = true
1605 emitted['Array'] = true
1606 remaining.delete('array')
1607 remaining_cnames.delete('array')
1608 }
1609 for _ in 0 .. 30 {
1610 if remaining.len == 0 && iface_remaining.len == 0 && sum_remaining.len == 0 {
1611 break
1612 }
1613 mut progress := false
1614 mut emitted_ifaces := []string{}
1615 for name, _ in iface_remaining {
1616 cn := c_name(name)
1617 mut can_emit := true
1618 if cn == 'IError' {
1619 if 'string' !in emitted && 'string' in remaining_cnames {
1620 can_emit = false
1621 }
1622 }
1623 // An interface struct embeds its declared data fields by value, so the
1624 // field types must be fully defined first (same constraint as structs).
1625 for field in g.tc.interface_fields[name] or { []types.StructField{} } {
1626 if field.typ is types.Pointer {
1627 continue
1628 }
1629 fct := g.tc.c_type(field.typ)
1630 if fct !in emitted && fct != cn && fct in remaining_cnames {
1631 can_emit = false
1632 break
1633 }
1634 }
1635 if can_emit {
1636 g.emit_interface_struct(name)
1637 emitted[cn] = true
1638 remaining_cnames.delete(cn)
1639 emitted_ifaces << name
1640 progress = true
1641 }
1642 }
1643 for name in emitted_ifaces {
1644 iface_remaining.delete(name)
1645 }
1646 mut emitted_structs := []string{}
1647 for name, _ in remaining {
1648 cn := c_name(name)
1649 if cn in emitted {
1650 remaining_cnames.delete(cn)
1651 emitted_structs << name
1652 progress = true
1653 continue
1654 }
1655 mut can_emit := true
1656 if name in g.tc.structs {
1657 for f in g.tc.structs[name] {
1658 if f.typ is types.Pointer {
1659 continue
1660 }
1661 mut ct := ''
1662 if f.typ is types.ArrayFixed {
1663 ct = g.tc.c_type(f.typ.elem_type)
1664 } else if f.typ is types.OptionType {
1665 ct = g.tc.c_type(f.typ.base_type)
1666 } else if f.typ is types.ResultType {
1667 ct = g.tc.c_type(f.typ.base_type)
1668 } else {
1669 ct = g.tc.c_type(f.typ)
1670 }
1671 if ct !in emitted && ct != cn && ct in remaining_cnames {
1672 can_emit = false
1673 break
1674 }
1675 }
1676 }
1677 if can_emit {
1678 if fields := g.tc.structs[name] {
1679 g.emit_struct_option_typedefs(fields)
1680 }
1681 g.emit_struct(name)
1682 emitted[cn] = true
1683 g.emit_ready_fixed_array_typedefs(fixed_array_needed, emitted)
1684 remaining_cnames.delete(cn)
1685 emitted_structs << name
1686 progress = true
1687 }
1688 }
1689 for name in emitted_structs {
1690 remaining.delete(name)
1691 }
1692 mut emitted_sums := []string{}
1693 for name, _ in sum_remaining {
1694 cn := c_name(name)
1695 mut can_emit_sum := true
1696 if name in g.tc.sum_types {
1697 for v in g.tc.sum_types[name] {
1698 if g.variant_references_sum(v, name) {
1699 continue
1700 }
1701 vt := g.tc.parse_type(v)
1702 if vt is types.SumType {
1703 if vt.name in sum_remaining {
1704 can_emit_sum = false
1705 break
1706 }
1707 }
1708 vct := g.tc.c_type(vt)
1709 if vct !in emitted && vct in remaining_cnames {
1710 can_emit_sum = false
1711 break
1712 }
1713 }
1714 }
1715 if can_emit_sum {
1716 g.emit_sum_type(name)
1717 emitted[cn] = true
1718 remaining_cnames.delete(cn)
1719 emitted_sums << name
1720 progress = true
1721 }
1722 }
1723 for name in emitted_sums {
1724 sum_remaining.delete(name)
1725 }
1726 if !progress {
1727 break
1728 }
1729 }
1730 for name, _ in iface_remaining {
1731 cn := c_name(name)
1732 g.emit_interface_struct(name)
1733 emitted[cn] = true
1734 }
1735 for name, _ in sum_remaining {
1736 g.emit_sum_type(name)
1737 }
1738 for name, _ in remaining {
1739 if fields := g.tc.structs[name] {
1740 g.emit_struct_option_typedefs(fields)
1741 }
1742 g.emit_struct(name)
1743 }
1744}
1745
1746// type_forward_decls returns type forward decls data for FlatGen.
1747fn (mut g FlatGen) type_forward_decls() {
1748 for name, _ in g.tc.structs {
1749 if g.skip_builtin_struct(name) {
1750 continue
1751 }
1752 if !c_struct_needs_typedef(name) {
1753 continue
1754 }
1755 tag := if name in g.tc.unions { 'union' } else { 'struct' }
1756 g.writeln('typedef ${tag} ${c_name(name)} ${c_name(name)};')
1757 }
1758 for name, _ in g.tc.sum_types {
1759 g.writeln('typedef struct ${c_name(name)} ${c_name(name)};')
1760 }
1761 for name, _ in g.interfaces {
1762 g.writeln('typedef struct ${c_name(name)} ${c_name(name)};')
1763 }
1764 if g.has_builtins {
1765 g.writeln('typedef array Array;')
1766 }
1767 g.writeln('')
1768}
1769
1770// emit_struct emits emit struct output for c.
1771fn (mut g FlatGen) emit_struct(name string) {
1772 old_module := g.tc.cur_module
1773 g.tc.cur_module = g.fixed_array_typedef_type_module(name, old_module)
1774 if name in g.tc.structs {
1775 fields := g.tc.structs[name]
1776 tag := if name in g.tc.unions { 'union' } else { 'struct' }
1777 g.writeln('${tag} ${c_name(name)} {')
1778 if fields.len == 0 {
1779 g.writeln('\tint _dummy;')
1780 }
1781 for f in fields {
1782 g.write_struct_field(name, f)
1783 }
1784 g.writeln('};')
1785 g.writeln('')
1786 }
1787 g.tc.cur_module = old_module
1788}
1789
1790// is_generic_struct reports whether is generic struct applies in c.
1791fn (g &FlatGen) is_generic_struct(name string) bool {
1792 if info := g.struct_decl_infos[name] {
1793 return info.node.typ.contains('generic')
1794 }
1795 short_name := if name.contains('.') { name.all_after_last('.') } else { name }
1796 if info := g.struct_decl_short_infos[short_name] {
1797 return info.full_name == name && info.node.typ.contains('generic')
1798 }
1799 return false
1800}
1801
1802// write_struct_field writes struct field output for c.
1803fn (mut g FlatGen) write_struct_field(_struct_name string, f types.StructField) {
1804 if f.typ is types.Void {
1805 g.writeln('\tint ${c_name(f.name)};')
1806 return
1807 }
1808 mut field_type := f.typ
1809 if f.typ is types.Alias {
1810 field_type = f.typ.base_type
1811 }
1812 raw_field_type := field_type
1813 if field_type is types.FnType {
1814 c_abi_fn := g.struct_field_c_abi_fn_ptr_type(_struct_name, f.name) or {
1815 g.tc.c_type(raw_field_type)
1816 }
1817 ct := g.resolve_fn_ptr_type(c_abi_fn)
1818 g.writeln('\t${ct} ${c_name(f.name)};')
1819 } else if f.typ is types.ArrayFixed {
1820 c_elem, dims := g.fixed_array_decl_parts(f.typ)
1821 g.writeln('\t${c_elem} ${c_name(f.name)}${dims};')
1822 } else {
1823 mut ct := if f.typ is types.OptionType || f.typ is types.ResultType {
1824 g.optional_type_name(f.typ)
1825 } else {
1826 g.tc.c_type(f.typ)
1827 }
1828 if ct.starts_with('fn_ptr:') {
1829 ct = g.resolve_fn_ptr_type(ct)
1830 }
1831 if ct == 'void' {
1832 ct = 'int'
1833 }
1834 g.writeln('\t${ct} ${c_name(f.name)};')
1835 }
1836}
1837
1838// preseed_struct_fn_ptr_types supports preseed struct fn ptr types handling for FlatGen.
1839fn (mut g FlatGen) preseed_struct_fn_ptr_types() {
1840 for struct_name, fields in g.tc.structs {
1841 for f in fields {
1842 if c_abi_fn := g.struct_field_c_abi_fn_ptr_type(struct_name, f.name) {
1843 g.resolve_fn_ptr_type(c_abi_fn)
1844 continue
1845 }
1846 g.preseed_fn_ptr_type(f.typ)
1847 }
1848 }
1849}
1850
1851fn (mut g FlatGen) preseed_global_fn_ptr_types() {
1852 for _, typ in g.global_types {
1853 g.preseed_fn_ptr_type(typ)
1854 }
1855}
1856
1857fn (mut g FlatGen) preseed_fn_ptr_type(typ types.Type) {
1858 if typ is types.Alias {
1859 g.preseed_fn_ptr_type(typ.base_type)
1860 return
1861 }
1862 if typ is types.FnType {
1863 ct := g.tc.c_type(typ)
1864 g.resolve_fn_ptr_type(ct)
1865 for param in typ.params {
1866 g.preseed_fn_ptr_type(param)
1867 }
1868 g.preseed_fn_ptr_type(typ.return_type)
1869 return
1870 }
1871 if typ is types.Array {
1872 g.preseed_fn_ptr_type(typ.elem_type)
1873 return
1874 }
1875 if typ is types.ArrayFixed {
1876 g.preseed_fn_ptr_type(typ.elem_type)
1877 return
1878 }
1879 if typ is types.Map {
1880 g.preseed_fn_ptr_type(typ.key_type)
1881 g.preseed_fn_ptr_type(typ.value_type)
1882 return
1883 }
1884 if typ is types.Pointer {
1885 g.preseed_fn_ptr_type(typ.base_type)
1886 return
1887 }
1888 if typ is types.OptionType {
1889 g.optional_type_name(typ)
1890 g.preseed_fn_ptr_type(typ.base_type)
1891 return
1892 }
1893 if typ is types.ResultType {
1894 g.optional_type_name(typ)
1895 g.preseed_fn_ptr_type(typ.base_type)
1896 return
1897 }
1898 if typ is types.MultiReturn {
1899 for item in typ.types {
1900 g.preseed_fn_ptr_type(item)
1901 }
1902 }
1903}
1904
1905// emit_struct_option_typedefs emits emit struct option typedefs output for c.
1906fn (mut g FlatGen) emit_struct_option_typedefs(fields []types.StructField) {
1907 mut wrote := false
1908 for f in fields {
1909 if f.typ is types.OptionType || f.typ is types.ResultType {
1910 opt_name := g.optional_type_name(f.typ)
1911 if opt_name == 'Optional' {
1912 continue
1913 }
1914 if val_type := g.needed_optional_types[opt_name] {
1915 if g.emit_optional_typedef(opt_name, val_type) {
1916 wrote = true
1917 }
1918 }
1919 }
1920 }
1921 if wrote {
1922 g.writeln('')
1923 }
1924}
1925