v2 / vlib / v / gen / c / struct.v
1203 lines · 1164 sloc · 38.82 KB · 142f665ddd1b56426ca611b16c8dc7088aa05190
Raw
1// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4module c
5
6import v.ast
7
8const skip_struct_init = ['struct stat', 'struct addrinfo']
9
10fn (mut g Gen) struct_init(node ast.StructInit) {
11 mut is_update_tmp_var := false
12 mut tmp_update_var := ''
13 base_node_typ := if node.generic_typ != 0 {
14 if node.is_short_syntax || node.typ.has_flag(.generic) || node.typ == ast.void_type {
15 // Short syntax inits and still-generic types: use generic_typ so the cgen
16 // can resolve it using the current concrete types.
17 node.generic_typ
18 } else {
19 // Explicitly typed inits (e.g. V2d[bool]{...}): the type in node.typ
20 // was written in source and should be preserved. Using node.generic_typ
21 // (e.g. V2d[T]) would incorrectly substitute T with the enclosing
22 // function's concrete type parameter.
23 node.typ
24 }
25 } else {
26 node.typ
27 }
28 if node.has_update_expr && !node.update_expr.is_lvalue() {
29 is_update_tmp_var = true
30
31 tmp_update_var = g.new_tmp_var()
32 s := if g.inside_ternary > 0 { g.go_before_ternary() } else { g.go_before_last_stmt() }
33 g.empty_line = true
34
35 styp := g.styp(node.update_expr_type)
36 g.write('${styp} ${tmp_update_var} = ')
37 g.expr(node.update_expr)
38 g.writeln(';')
39 g.empty_line = false
40
41 g.write(s)
42 }
43 unalised_typ := g.table.unaliased_type(base_node_typ)
44 styp := if g.table.sym(unalised_typ).language == .v {
45 g.styp(unalised_typ).replace('*', '')
46 } else {
47 g.styp(base_node_typ)
48 }
49 mut shared_styp := '' // only needed for shared x := St{...
50 if styp in skip_struct_init {
51 // needed for c++ compilers
52 g.go_back(3)
53 return
54 }
55 resolved_node_type := g.recheck_concrete_type(base_node_typ)
56 unwrapped_typ := g.unwrap_generic(resolved_node_type)
57 struct_init_typ := if node.typ.has_flag(.generic) && resolved_node_type != 0 {
58 resolved_node_type
59 } else {
60 node.typ
61 }
62 mut sym := g.table.final_sym(unwrapped_typ)
63 old_cur_struct_init_typ := g.cur_struct_init_typ
64 if node.typ != 0 {
65 g.cur_struct_init_typ = node.typ
66 }
67 g.zero_struct_init_stack << g.zero_struct_init_type(struct_init_typ)
68 defer {
69 g.cur_struct_init_typ = old_cur_struct_init_typ
70 g.zero_struct_init_stack.delete_last()
71 }
72 if sym.kind == .sum_type {
73 if unwrapped_typ.is_ptr() {
74 // handle promotions to a sumtype for generic functions like this one: `fn (d Struct) a[T]() T { return d }`
75 // the value should be on the heap, since it is not known where it will be used:
76 sumtype_type := unwrapped_typ.set_nr_muls(0)
77 sumtype_name := g.styp(sumtype_type)
78 g.write('HEAP(${sumtype_name}, (')
79 g.write(g.type_default_sumtype(sumtype_type, sym))
80 g.write('))')
81 } else {
82 g.write(g.type_default_sumtype(unwrapped_typ, sym))
83 }
84 return
85 } else if sym.kind == .map || (sym.kind == .array && node.init_fields.len == 0) {
86 g.write(g.type_default(unwrapped_typ))
87 return
88 }
89 is_amp := g.is_amp
90 is_multiline := node.init_fields.len > 5
91 g.is_amp = false // reset the flag immediately so that other struct inits in this expr are handled correctly
92 if is_amp {
93 g.go_back(1) // delete the `&` already generated in `prefix_expr()
94 }
95 mut aligned := 0
96 mut is_anon := false
97 mut is_array_fixed_struct_init := false // return T{} where T is fixed array
98 mut is_ptr_heap_init := false
99 if mut sym.info is ast.Struct {
100 if attr := sym.info.attrs.find_first('aligned') {
101 aligned = if attr.arg == '' { 0 } else { attr.arg.int() }
102 }
103 is_anon = sym.info.is_anon
104 }
105 mut is_generic_default := sym.kind !in [.struct, .array_fixed, .generic_inst]
106 && base_node_typ.has_flag(.generic) // T{}
107 is_array := sym.kind in [.array_fixed, .array]
108 if sym.kind == .array_fixed {
109 arr_info := sym.array_fixed_info()
110 is_array_fixed_struct_init = g.inside_return
111 && g.table.final_sym(arr_info.elem_type).kind == .struct
112 }
113
114 // detect if we need type casting on msvc initialization
115 const_msvc_init := g.is_cc_msvc && g.inside_const && !g.inside_cast && g.inside_array_item
116 if is_amp && g.can_use_direct_heap_struct_init(node, sym, aligned, const_msvc_init) {
117 g.direct_heap_struct_init(node, styp, sym.info as ast.Struct, sym.language)
118 return
119 }
120
121 if !g.inside_cinit && !is_anon && !is_generic_default && !is_array && !const_msvc_init {
122 g.write('(')
123 defer(fn) {
124 g.write(')')
125 }
126 }
127 if is_anon && !node.typ.has_flag(.option) {
128 if node.language == .v {
129 g.write('(${styp})')
130 }
131 g.writeln('{')
132 } else if g.is_shared && !g.inside_opt_data && !g.is_arraymap_set {
133 mut shared_typ := node.typ.set_flag(.shared_f)
134 shared_styp = g.styp(shared_typ)
135 g.writeln('(${shared_styp}*)__dup${shared_styp}(&(${shared_styp}){.mtx = {0}, .val =(${styp}){')
136 } else if is_amp || g.inside_cast_in_heap > 0 {
137 if node.typ.has_flag(.option) {
138 basetyp := g.base_type(node.typ)
139 if aligned != 0 {
140 g.write('(${basetyp}*)builtin__memdup_align(&(${basetyp}){')
141 } else {
142 g.write_heap_alloc(basetyp, node.typ.clear_option_and_result())
143 if is_multiline {
144 g.writeln('(${basetyp}){')
145 } else {
146 g.write('(${basetyp}){')
147 }
148 }
149 } else {
150 if aligned != 0 {
151 g.write('(${styp}*)builtin__memdup_align(&(${styp}){')
152 } else {
153 g.write_heap_alloc(styp, unwrapped_typ)
154 if is_multiline {
155 g.writeln('(${styp}){')
156 } else {
157 g.write('(${styp}){')
158 }
159 }
160 }
161 } else if struct_init_typ.is_ptr() {
162 mut resolved_ptr_type := g.unwrap_generic(g.recheck_concrete_type(struct_init_typ))
163 if resolved_ptr_type == 0 {
164 resolved_ptr_type = struct_init_typ
165 }
166 pointee_type := resolved_ptr_type.set_nr_muls(0)
167 basetyp := g.styp(pointee_type)
168 pointee_sym := g.table.final_sym(pointee_type)
169 // For primitive pointer types (e.g. T{} where T = &int), the default
170 // value is a null pointer, not a compound literal address.
171 if pointee_sym.kind !in [.struct, .array_fixed, .array, .sum_type, .interface, .map]
172 && !pointee_sym.is_heap() && node.init_fields.len == 0 {
173 g.write('0')
174 return
175 }
176 // We're generating a compound literal address `&(type){...}`,
177 // so is_generic_default must not suppress the content and closing brace.
178 is_generic_default = false
179 if pointee_sym.is_heap() {
180 is_ptr_heap_init = true
181 if aligned != 0 {
182 g.write('(${basetyp}*)builtin__memdup_align(&(${basetyp}){')
183 } else {
184 g.write_heap_alloc(basetyp, pointee_type)
185 if is_multiline {
186 g.writeln('(${basetyp}){')
187 } else {
188 g.write('(${basetyp}){')
189 }
190 }
191 } else if is_multiline {
192 g.writeln('&(${basetyp}){')
193 } else {
194 g.write('&(${basetyp}){')
195 }
196 } else if node.typ.has_flag(.option) {
197 tmp_var := g.new_tmp_var()
198 s := if g.inside_ternary > 0 { g.go_before_ternary() } else { g.go_before_last_stmt() }
199 g.empty_line = true
200
201 base_styp := g.styp(node.typ.clear_option_and_result())
202 g.writeln('${styp} ${tmp_var} = {0};')
203
204 if node.init_fields.len > 0 || node.typ.has_flag(.generic) {
205 g.write('builtin___option_ok(&(${base_styp}[]) { ')
206 } else {
207 g.write('builtin___option_none(&(${base_styp}[]) { ')
208 }
209 g.struct_init(ast.StructInit{
210 ...node
211 typ: g.unwrap_generic(node.typ).clear_option_and_result()
212 })
213 g.writeln('}, (${option_name}*)&${tmp_var}, sizeof(${base_styp}));')
214 g.empty_line = false
215 g.write2(s, tmp_var)
216 return
217 } else if g.inside_cinit {
218 if is_multiline {
219 g.writeln('{')
220 } else {
221 g.write('{')
222 }
223 } else {
224 // alias to pointer type
225 if (g.table.sym(node.typ).kind == .alias && g.table.unaliased_type(node.typ).is_ptr())
226 || (!sym.is_int() && node.typ.has_flag(.generic) && unwrapped_typ.is_ptr()) {
227 g.write('&')
228 }
229 if is_array || const_msvc_init {
230 if !is_array_fixed_struct_init {
231 g.write('{')
232 }
233 } else if is_multiline {
234 g.writeln('(${styp}){')
235 } else if is_generic_default {
236 default_val := g.type_default(node.typ)
237 if default_val == '{0}' {
238 g.write('(${styp}){0}')
239 } else {
240 g.write(default_val)
241 }
242 } else {
243 g.write('(${styp}){')
244 }
245 }
246 mut inited_fields := map[string]int{}
247 if is_multiline {
248 g.indent++
249 }
250 // User set fields
251 mut initialized := false
252 mut old_is_shared := g.is_shared
253 for i, init_field in node.init_fields {
254 if !init_field.typ.has_flag(.shared_f) {
255 g.is_shared = false
256 }
257 mut field_name := init_field.name
258 if node.no_keys && sym.kind == .struct {
259 info := sym.info as ast.Struct
260 if info.fields.len == node.init_fields.len {
261 field_name = info.fields[i].name
262 }
263 }
264 inited_fields[field_name] = i
265 if sym.kind != .struct && (sym.kind == .string || !sym.is_primitive()) {
266 if init_field.typ == 0 {
267 g.checker_bug('struct init, field.typ is 0', init_field.pos)
268 }
269 g.struct_init_field(init_field, sym.language)
270 if i != node.init_fields.len - 1 {
271 if is_multiline {
272 g.writeln(',')
273 } else {
274 g.write(', ')
275 }
276 }
277 initialized = true
278 }
279 g.is_shared = old_is_shared
280 }
281 g.is_shared = old_is_shared
282 // The rest of the fields are zeroed.
283 // `inited_fields` is a list of fields that have been init'ed, they are skipped
284 mut nr_fields := 1
285 if sym.kind == .struct {
286 mut info := sym.info as ast.Struct
287 nr_fields = info.fields.len
288 if info.is_union && node.init_fields.len > 1 {
289 verror('union must not have more than 1 initializer')
290 }
291 if !info.is_union {
292 old_is_shared2 := g.is_shared
293 mut used_embed_fields := []string{}
294 init_field_names := info.fields.map(it.name)
295 // fields that are initialized but belong to the embedding
296 init_fields_to_embed := node.init_fields.filter(it.name !in init_field_names)
297 for embed in info.embeds {
298 embed_sym := g.table.sym(embed)
299 embed_name := embed_sym.embed_name()
300 if embed_name !in inited_fields {
301 mut embed_info := ast.Struct{}
302 mut has_embed_struct_info := false
303 if embed_sym.info is ast.Struct {
304 embed_info = embed_sym.info
305 has_embed_struct_info = true
306 } else {
307 final_embed_sym := g.table.final_sym(embed)
308 if final_embed_sym.info is ast.Struct {
309 embed_info = final_embed_sym.info
310 has_embed_struct_info = true
311 }
312 }
313 if has_embed_struct_info {
314 embed_field_names := embed_info.fields.map(it.name)
315 fields_to_embed := init_fields_to_embed.filter(
316 it.name !in used_embed_fields && it.name in embed_field_names)
317 used_embed_fields << fields_to_embed.map(it.name)
318 default_init := ast.StructInit{
319 ...node
320 typ: embed
321 is_update_embed: true
322 init_fields: init_fields_to_embed
323 }
324 inside_cast_in_heap := g.inside_cast_in_heap
325 g.inside_cast_in_heap = 0 // prevent use of pointers in child structs
326
327 g.write('.${embed_name} = ')
328 g.struct_init(default_init)
329
330 g.inside_cast_in_heap = inside_cast_in_heap // restore value for further struct inits
331 if is_multiline {
332 g.writeln(',')
333 } else {
334 g.write(',')
335 }
336 initialized = true
337 } else {
338 // Embedded fn/interface/alias fields do not have child fields to recurse into.
339 if g.zero_struct_field(ast.StructField{
340 name: embed_name
341 typ: embed
342 })
343 {
344 if is_multiline {
345 g.writeln(',')
346 } else {
347 g.write(',')
348 }
349 initialized = true
350 }
351 }
352 }
353 }
354 g.is_shared = old_is_shared2
355 }
356 for mut field in info.fields {
357 g.is_shared = field.typ.has_flag(.shared_f)
358 if mut sym.info is ast.Struct {
359 mut found_equal_fields := 0
360 field_name, field_name_len := field.name, field.name.len
361 for mut sifield in sym.info.fields {
362 if sifield.name.len == field_name_len && sifield.name == field_name {
363 found_equal_fields++
364 break
365 }
366 }
367 if found_equal_fields == 0 {
368 continue
369 }
370 }
371 if already_inited_node_field_index := inited_fields[field.name] {
372 mut sfield := node.init_fields[already_inited_node_field_index]
373 if sfield.typ == 0 {
374 continue
375 }
376 sfield.expected_type = g.recheck_concrete_type(field.typ)
377 if sfield.expected_type.has_flag(.generic) && g.cur_fn != unsafe { nil } {
378 mut t_generic_names := g.cur_fn.generic_names.clone()
379 mut t_concrete_types := g.cur_concrete_types.clone()
380 ts := g.table.sym(resolved_node_type)
381 if ts.generic_types.len > 0 && ts.generic_types.len == info.generic_types.len
382 && ts.generic_types != info.generic_types {
383 t_generic_names = info.generic_types.map(g.table.sym(it).name)
384 t_concrete_types = []
385 for t_typ in ts.generic_types {
386 if !t_typ.has_flag(.generic) {
387 t_concrete_types << t_typ
388 } else if g.table.sym(t_typ).kind == .any {
389 tname := g.table.sym(t_typ).name
390 index := g.cur_fn.generic_names.index(tname)
391 if index >= 0 && index < g.cur_concrete_types.len {
392 t_concrete_types << g.cur_concrete_types[index]
393 }
394 } else {
395 if tt := g.table.convert_generic_type(t_typ,
396 g.cur_fn.generic_names, g.cur_concrete_types)
397 {
398 t_concrete_types << tt
399 }
400 }
401 }
402 }
403 if tt := g.table.convert_generic_type(sfield.expected_type, t_generic_names,
404 t_concrete_types)
405 {
406 sfield.expected_type = tt
407 }
408 }
409 if g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0 {
410 old_inside_return := g.inside_return
411 old_inside_return_expr := g.inside_return_expr
412 g.inside_return = false
413 g.inside_return_expr = false
414 resolved_sfield_typ := g.resolved_expr_type(ast.Expr(sfield.expr), sfield.typ)
415 g.inside_return = old_inside_return
416 g.inside_return_expr = old_inside_return_expr
417 if resolved_sfield_typ != 0 {
418 sfield.typ = g.unwrap_generic(g.recheck_concrete_type(resolved_sfield_typ))
419 }
420 }
421 if node.no_keys && sym.kind == .struct {
422 sym_info := sym.info as ast.Struct
423 if sym_info.fields.len == node.init_fields.len {
424 sfield.name = sym_info.fields[already_inited_node_field_index].name
425 }
426 }
427 g.struct_init_field(sfield, sym.language)
428 if is_multiline {
429 g.writeln(',')
430 } else {
431 g.write(',')
432 }
433 initialized = true
434 continue
435 }
436 if info.is_union {
437 // unions thould have exactly one explicit initializer
438 continue
439 }
440 field_name := c_name(field.name)
441 if field.typ in info.embeds {
442 continue
443 }
444 if node.has_update_expr {
445 mut is_arr_fixed := false
446 g.write('.${field_name} = ')
447 if is_update_tmp_var {
448 g.write(tmp_update_var)
449 } else {
450 update_expr_sym := g.table.final_sym(field.typ)
451 if update_expr_sym.info is ast.ArrayFixed {
452 is_arr_fixed = true
453 // workaround for tcc bug, is_auto_deref_var := ... issue #24331
454 is_auto_deref_var := node.update_expr.is_auto_deref_var()
455 g.fixed_array_update_expr_field(g.expr_string(node.update_expr),
456 node.update_expr_type, field.name, is_auto_deref_var,
457 update_expr_sym.info.elem_type, update_expr_sym.info.size,
458 node.is_update_embed)
459 } else {
460 g.write('(')
461 g.expr(node.update_expr)
462 g.write(')')
463 }
464 }
465 if !is_arr_fixed {
466 g.write(g.dot_or_ptr(node.update_expr_type))
467 if node.is_update_embed {
468 g.write(g.get_embed_field_name(node.update_expr_type, field.name))
469 }
470 g.write(c_name(field.name))
471 }
472 } else {
473 if !g.zero_struct_field(field) {
474 nr_fields--
475 continue
476 }
477 }
478 if is_multiline {
479 g.writeln(',')
480 } else {
481 g.write(',')
482 }
483 initialized = true
484 }
485 g.is_shared = old_is_shared
486 } else if is_array_fixed_struct_init {
487 arr_info := sym.array_fixed_info()
488
489 save_inside_array_fixed_struct := g.inside_array_fixed_struct
490 g.inside_array_fixed_struct = is_array_fixed_struct_init
491 defer(fn) {
492 g.inside_array_fixed_struct = save_inside_array_fixed_struct
493 }
494
495 g.fixed_array_init(ast.ArrayInit{
496 pos: node.pos
497 is_fixed: true
498 typ: unwrapped_typ
499 exprs: [ast.empty_expr]
500 elem_type: arr_info.elem_type
501 }, g.unwrap(unwrapped_typ), '', g.is_amp)
502 initialized = true
503 }
504 if is_multiline {
505 g.indent--
506 }
507
508 if !initialized && !is_generic_default {
509 if nr_fields > 0 {
510 g.write('0')
511 } else {
512 g.write('E_STRUCT')
513 }
514 }
515
516 if !is_array_fixed_struct_init && (!is_generic_default || is_ptr_heap_init) {
517 g.write('}')
518 }
519 if g.is_shared && !g.inside_opt_data && !g.is_arraymap_set {
520 if aligned != 0 {
521 g.write('}, sizeof(${shared_styp}), ${aligned})')
522 } else {
523 g.write('}, sizeof(${shared_styp}))')
524 }
525 } else if is_amp || g.inside_cast_in_heap > 0 {
526 if node.typ.has_flag(.option) {
527 basetyp := g.base_type(node.typ)
528 if aligned != 0 {
529 g.write(', sizeof(${basetyp}), ${aligned})')
530 } else {
531 g.write_heap_alloc_close(node.typ.clear_option_and_result())
532 }
533 } else {
534 if aligned != 0 {
535 g.write(', sizeof(${styp}), ${aligned})')
536 } else {
537 g.write_heap_alloc_close(unwrapped_typ)
538 }
539 }
540 } else if is_ptr_heap_init {
541 mut resolved_ptr_type := g.unwrap_generic(g.recheck_concrete_type(struct_init_typ))
542 if resolved_ptr_type == 0 {
543 resolved_ptr_type = struct_init_typ
544 }
545 pointee_type := resolved_ptr_type.set_nr_muls(0)
546 basetyp := g.styp(pointee_type)
547 if aligned != 0 {
548 g.write(', sizeof(${basetyp}), ${aligned})')
549 } else {
550 g.write_heap_alloc_close(pointee_type)
551 }
552 }
553}
554
555fn (mut g Gen) can_use_direct_heap_struct_init(node ast.StructInit, sym ast.TypeSymbol, aligned int, const_msvc_init bool) bool {
556 if g.is_shared || g.inside_cast_in_heap > 0 || g.inside_cinit || g.inside_const
557 || g.inside_global_decl || aligned != 0 || const_msvc_init || node.typ.has_flag(.option)
558 || node.has_update_expr || sym.kind != .struct {
559 return false
560 }
561 if sym.info !is ast.Struct {
562 return false
563 }
564 info := sym.info as ast.Struct
565 if info.is_anon || info.is_union || info.embeds.len > 0
566 || node.init_fields.len != info.fields.len {
567 return false
568 }
569 for init_field in node.init_fields {
570 if g.need_tmp_var_in_expr(init_field.expr) {
571 return false
572 }
573 }
574 if node.no_keys {
575 return true
576 }
577 field_names := info.fields.map(it.name)
578 return node.init_fields.all(it.name in field_names)
579}
580
581fn (mut g Gen) direct_heap_struct_init(node ast.StructInit, styp string, info ast.Struct, language ast.Language) {
582 stmt_str := if g.inside_ternary > 0 {
583 g.go_before_ternary().trim_space()
584 } else {
585 g.go_before_last_stmt().trim_space()
586 }
587 g.empty_line = true
588 tmp_var := g.new_tmp_var()
589 if info.is_empty_struct() {
590 g.writeln('${styp}* ${tmp_var} = HEAP(${styp}, ((${styp}){E_STRUCT}));')
591 } else {
592 g.writeln('${styp}* ${tmp_var} = (${styp}*)builtin___v_malloc(sizeof(${styp}) == 0 ? 1 : sizeof(${styp}));')
593 }
594 for i, init_field in node.init_fields {
595 mut resolved_field := init_field
596 if node.no_keys {
597 resolved_field.name = info.fields[i].name
598 }
599 if resolved_field.typ == 0 || resolved_field.expected_type == 0 {
600 // Resolve from struct field info - needed for generic struct inits
601 // where the checker left init_field.typ/expected_type unset
602 for f in info.fields {
603 if f.name == resolved_field.name {
604 field_typ := g.unwrap_generic(f.typ)
605 if resolved_field.typ == 0 {
606 resolved_field.typ = field_typ
607 }
608 if resolved_field.expected_type == 0 {
609 resolved_field.expected_type = field_typ
610 }
611 break
612 }
613 }
614 if resolved_field.typ == 0 {
615 g.checker_bug('struct init, field.typ is 0', resolved_field.pos)
616 }
617 }
618 if g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0 {
619 old_inside_return := g.inside_return
620 old_inside_return_expr := g.inside_return_expr
621 g.inside_return = false
622 g.inside_return_expr = false
623 resolved_field_typ := g.resolved_expr_type(ast.Expr(resolved_field.expr),
624 resolved_field.typ)
625 g.inside_return = old_inside_return
626 g.inside_return_expr = old_inside_return_expr
627 if resolved_field_typ != 0 {
628 resolved_field.typ = g.unwrap_generic(g.recheck_concrete_type(resolved_field_typ))
629 }
630 }
631 g.struct_init_ptr_field(tmp_var, resolved_field, language)
632 g.writeln(';')
633 }
634 g.set_current_pos_as_last_stmt_pos()
635 g.write2(stmt_str, ' ')
636 g.write(tmp_var)
637}
638
639fn (mut g Gen) get_embed_field_name(field_type ast.Type, field_name string) string {
640 update_sym := g.table.sym(field_type)
641 _, embeds := g.table.find_field_from_embeds(update_sym, field_name) or {
642 ast.StructField{}, []ast.Type{}
643 }
644 mut s := ''
645 for embed in embeds {
646 esym := g.table.sym(embed)
647 ename := esym.embed_name()
648 if embed.is_ptr() {
649 s += '${ename}->'
650 } else {
651 s += '${ename}.'
652 }
653 }
654 return s
655}
656
657fn (mut g Gen) init_shared_field(field ast.StructField) {
658 field_typ := field.typ.deref()
659 shared_styp := g.styp(field_typ)
660 g.write('(${shared_styp}*)__dup${shared_styp}(&(${shared_styp}){.mtx= {0}, .val=')
661 if field.has_default_expr {
662 // avoid generate shared assign inside expr
663 old_is_shared := g.is_shared
664 g.is_shared = false
665 g.expr(field.default_expr)
666 g.is_shared = old_is_shared
667 } else {
668 g.write(g.type_default(field_typ.clear_flag(.shared_f)))
669 }
670 g.write('}, sizeof(${shared_styp}))')
671}
672
673fn (mut g Gen) zero_struct_init_type(typ ast.Type) ast.Type {
674 mut resolved_typ := g.unwrap_generic(g.recheck_concrete_type(typ))
675 if resolved_typ == 0 {
676 resolved_typ = typ
677 }
678 return resolved_typ.clear_option_and_result().clear_flag(.shared_f).clear_flag(.atomic_f)
679}
680
681fn (mut g Gen) zero_struct_init_would_recurse(typ ast.Type) bool {
682 resolved_typ := g.zero_struct_init_type(typ)
683 return resolved_typ in g.zero_struct_init_stack
684}
685
686fn (mut g Gen) write_zero_struct_init(default_init ast.StructInit) {
687 if g.zero_struct_init_would_recurse(default_init.typ) {
688 g.write(g.type_default(default_init.typ))
689 return
690 }
691 g.struct_init(default_init)
692}
693
694fn (mut g Gen) zero_struct_field(field ast.StructField) bool {
695 old_inside_cast_in_heap := g.inside_cast_in_heap
696 g.inside_cast_in_heap = 0
697 defer {
698 g.inside_cast_in_heap = old_inside_cast_in_heap
699 }
700 sym := g.table.sym(field.typ)
701 final_sym := g.table.final_sym(field.typ)
702 field_name := if sym.language == .v { c_name(field.name) } else { field.name }
703 if sym.info is ast.Struct {
704 if sym.info.fields.len == 0 {
705 // For empty struct fields, check if it's a shared field
706 if field.typ.has_flag(.shared_f) {
707 g.write('.${field_name} = ')
708 g.init_shared_field(field)
709 return true
710 }
711 return false
712 } else if !field.has_default_expr {
713 mut has_option_field := false
714 if sym.info.is_shared || field.typ.has_flag(.shared_f) {
715 g.write('.${field_name} = ')
716 g.init_shared_field(field)
717 return true
718 }
719 for fd in sym.info.fields {
720 if fd.typ.has_flag(.option) {
721 has_option_field = true
722 break
723 }
724 }
725 if has_option_field || field.anon_struct_decl.fields.len > 0 {
726 default_init := ast.StructInit{
727 typ: field.typ
728 language: field.anon_struct_decl.language
729 }
730 g.write('.${field_name} = ')
731 if field.typ.has_flag(.option) {
732 if field.is_recursive || field.typ.is_ptr() {
733 g.expr_with_opt(ast.None{}, ast.none_type, field.typ)
734 } else {
735 tmp_var := g.new_tmp_var()
736 g.expr_with_tmp_var(default_init, field.typ, field.typ, tmp_var, true)
737 }
738 } else {
739 g.write_zero_struct_init(default_init)
740 }
741 return true
742 } else if sym.language == .v && !field.typ.is_ptr() && sym.mod != 'builtin'
743 && !sym.info.is_empty_struct() {
744 default_init := ast.StructInit{
745 typ: field.typ
746 }
747 g.write('.${field_name} = ')
748 g.write_zero_struct_init(default_init)
749 return true
750 }
751 }
752 }
753 g.write('.${field_name} = ')
754 if field.has_default_expr {
755 if sym.kind in [.sum_type, .interface] {
756 if field.typ.has_flag(.option) {
757 g.expr_with_opt(field.default_expr, field.default_expr_typ, field.typ)
758 } else {
759 g.expr_with_cast(field.default_expr, field.default_expr_typ, field.typ)
760 }
761 return true
762 }
763
764 if field.default_expr is ast.None {
765 g.gen_option_error(field.typ, ast.None{})
766 return true
767 } else if field.typ.has_flag(.option) {
768 tmp_var := g.new_tmp_var()
769 g.expr_with_tmp_var(field.default_expr, field.default_expr_typ, field.typ, tmp_var,
770 true)
771 return true
772 } else if field.typ.has_flag(.result) && !field.default_expr_typ.has_flag(.result) {
773 tmp_var := g.new_tmp_var()
774 g.expr_with_tmp_var(field.default_expr, field.default_expr_typ, field.typ, tmp_var,
775 true)
776 return true
777 } else if final_sym.info is ast.ArrayFixed && field.default_expr !is ast.ArrayInit {
778 old_inside_memset := g.inside_memset
779 g.inside_memset = true
780 tmp_var := g.expr_with_var(field.default_expr, field.default_expr_typ,
781
782 field.default_expr !is ast.CallExpr && field.default_expr !is ast.CastExpr)
783 g.fixed_array_var_init(tmp_var, false, final_sym.info.elem_type, final_sym.info.size)
784 g.inside_memset = old_inside_memset
785 return true
786 } else if field.default_expr is ast.CastExpr {
787 resolved_field_type := g.unwrap_generic(field.typ)
788 resolved_default_type := g.unwrap_generic(field.default_expr.typ)
789 if resolved_field_type != 0 && resolved_default_type == 0 {
790 g.expr_with_cast(field.default_expr.expr, field.default_expr.expr_type,
791 resolved_field_type)
792 return true
793 }
794 } else if field.typ.has_flag(.shared_f) {
795 g.init_shared_field(field)
796 return true
797 }
798 old_expected_cast_type := g.expected_cast_type
799 g.expected_cast_type = field.typ
800 g.expr(field.default_expr)
801 g.expected_cast_type = old_expected_cast_type
802 } else if field.typ.has_flag(.option) {
803 g.gen_option_error(field.typ, ast.None{})
804 return true
805 } else if sym.info is ast.SumType && !field.typ.is_any_kind_of_pointer() {
806 g.write(g.type_default_sumtype(field.typ, sym))
807 return true
808 } else if sym.info is ast.ArrayFixed {
809 elem_is_option := sym.info.elem_type.has_flag(.option)
810 g.write('{')
811 if !elem_is_option && field.typ.has_flag(.shared_f) {
812 g.write('0')
813 } else {
814 default_str := g.type_default(sym.info.elem_type)
815 for i in 0 .. sym.info.size {
816 if elem_is_option {
817 g.gen_option_error(sym.info.elem_type, ast.None{})
818 } else {
819 g.write(default_str)
820 }
821 if i != sym.info.size - 1 {
822 g.write(', ')
823 }
824 }
825 }
826 g.write('}')
827 } else if field.typ.has_flag(.shared_f) {
828 g.init_shared_field(field)
829 } else {
830 g.write(g.type_default(field.typ))
831 }
832 return true
833}
834
835fn (mut g Gen) struct_decl(s ast.Struct, name string, is_anon bool, is_option bool) {
836 if s.is_generic {
837 return
838 }
839 if name.contains('_T_') {
840 if s.is_union {
841 g.typedefs.writeln('typedef union ${name} ${name};')
842 } else {
843 g.typedefs.writeln('typedef struct ${name} ${name};')
844 }
845 }
846 // TODO: avoid buffer manip
847 start_pos := g.type_definitions.len
848
849 mut pre_pragma := ''
850 mut post_pragma := ''
851
852 for attr in s.attrs {
853 match attr.name {
854 '_pack' {
855 pre_pragma += '#pragma pack(push, ${attr.arg})\n'
856 post_pragma += '#pragma pack(pop)'
857 }
858 'packed' {
859 pre_pragma += '#pragma pack(push, 1)\n'
860 post_pragma += '#pragma pack(pop)'
861 }
862 else {}
863 }
864 }
865
866 is_minify := s.is_minify
867 g.type_definitions.writeln(pre_pragma)
868
869 mut aligned_attr := ''
870 if attr := s.attrs.find_first('aligned') {
871 attr_arg := if attr.arg == '' { '' } else { ' (${attr.arg})' }
872 aligned_attr += if g.is_cc_msvc {
873 '__declspec(align${attr_arg})'
874 } else {
875 ' __attribute__((aligned${attr_arg}))'
876 }
877 }
878 if is_anon {
879 option_prefix := if is_option { '_option_' } else { '' }
880 if s.is_shared {
881 g.type_definitions.write_string('\t${option_prefix}__shared__${name}* ')
882 } else {
883 g.type_definitions.write_string('\t${option_prefix}${name} ')
884 }
885 return
886 } else if s.is_union {
887 if g.is_cc_msvc && aligned_attr != '' {
888 g.type_definitions.writeln('union ${aligned_attr} ${name} {')
889 } else {
890 g.type_definitions.writeln('union ${name} {')
891 }
892 } else {
893 if g.is_cc_msvc && aligned_attr != '' {
894 g.type_definitions.writeln('struct ${aligned_attr} ${name} {')
895 } else {
896 g.type_definitions.writeln('struct ${name} {')
897 }
898 }
899
900 if s.fields.len > 0 || s.embeds.len > 0 {
901 for field in s.fields {
902 // Some of these structs may want to contain
903 // options that may not be defined at this point
904 // if this is the case then we are going to
905 // buffer manip out in front of the struct
906 // write the option in and then continue
907 // FIXME: for parallel cgen (two different files using the same option in struct fields)
908 if field.typ.has_flag(.option) {
909 // Dont use g.styp() here because it will register
910 // option and we dont want that
911 styp, base := g.option_type_name(field.typ)
912 lock g.done_options {
913 if base !in g.done_options {
914 g.done_options << base
915 last_text := g.type_definitions.after(start_pos).clone()
916 g.type_definitions.go_back_to(start_pos)
917 g.typedefs.writeln('typedef struct ${styp} ${styp};')
918 g.type_definitions.writeln('${g.option_type_text(styp, base)};')
919 g.type_definitions.write_string(last_text)
920 }
921 }
922 }
923 if field.typ.has_flag(.result) {
924 // Dont use g.styp() here because it will register
925 // result and we dont want that
926 styp, base := g.result_type_name(field.typ)
927 lock g.done_results {
928 if base !in g.done_results {
929 g.done_results << base
930 last_text := g.type_definitions.after(start_pos).clone()
931 g.type_definitions.go_back_to(start_pos)
932 g.typedefs.writeln('typedef struct ${styp} ${styp};')
933 g.type_definitions.writeln('${g.result_type_text(styp, base)};')
934 g.type_definitions.write_string(last_text)
935 }
936 }
937 }
938 type_name := g.styp(field.typ)
939 field_name := c_name(field.name)
940 volatile_prefix := if field.is_volatile { 'volatile ' } else { '' }
941 mut size_suffix := ''
942 mut is_enum_bitfield := false
943 if is_minify && !g.is_cc_msvc && !g.pref.output_cross_c && !field.typ.has_flag(.option)
944 && !field.typ.has_flag(.result) {
945 if field.typ == ast.bool_type_idx {
946 size_suffix = ' : 1'
947 } else {
948 field_sym := g.table.sym(field.typ)
949 if field_sym.info is ast.Enum {
950 if !field_sym.info.is_flag && !field_sym.info.uses_exprs {
951 mut bits_needed := 0
952 mut l := field_sym.info.vals.len
953 for l > 0 {
954 bits_needed++
955 l >>= 1
956 }
957 size_suffix = ' : ${bits_needed}'
958 is_enum_bitfield = true
959 }
960 }
961 }
962 }
963 field_sym := g.table.sym(field.typ)
964 mut field_is_anon := false
965 if field_sym.info is ast.Struct {
966 if field_sym.info.is_anon {
967 field_is_anon = true
968 // Recursively generate code for this anon struct (this is the field's type)
969 g.struct_decl(field_sym.info, field_sym.cname, true,
970 field.typ.has_flag(.option))
971 // Now the field's name
972 g.type_definitions.writeln(' ${field_name}${size_suffix};')
973 }
974 }
975 if !field_is_anon {
976 actual_type := if is_enum_bitfield {
977 enum_info := field_sym.info as ast.Enum
978 if enum_info.typ == ast.int_type {
979 // default enum base type (int): C bit-field signedness is
980 // implementation-defined; force unsigned to avoid Clang treating
981 // it as signed (which corrupts values >= 64 in a 7-bit field, etc.)
982 'unsigned int'
983 } else {
984 // explicit base type (as u8, as i8, as i32, …): the typedef
985 // already carries the correct signedness, so respect the user's choice
986 type_name
987 }
988 } else {
989 type_name
990 }
991 g.type_definitions.writeln('\t${volatile_prefix}${actual_type} ${field_name}${size_suffix};')
992 }
993 }
994 } else {
995 g.type_definitions.writeln('\tE_STRUCT_DECL;')
996 }
997 ti_attrs := if !g.is_cc_msvc && s.attrs.contains('packed') {
998 '__attribute__((__packed__))'
999 } else {
1000 ''
1001 }
1002 g.type_definitions.write_string('}${ti_attrs}')
1003 if !g.is_cc_msvc && aligned_attr != '' {
1004 g.type_definitions.write_string(' ${aligned_attr}')
1005 }
1006 if !is_anon {
1007 g.type_definitions.write_string(';')
1008 }
1009 g.type_definitions.writeln('')
1010 if post_pragma != '' {
1011 g.type_definitions.writeln(post_pragma)
1012 }
1013}
1014
1015fn (mut g Gen) struct_init_field(sfield ast.StructInitField, language ast.Language) {
1016 field_name := if language == .v { c_name(sfield.name) } else { sfield.name }
1017 g.write('.${field_name} = ')
1018 // Cast function pointers to the struct field's declared function type alias.
1019 // The checker coerces V types to match, but C signatures can differ
1020 // (e.g. callback returning &Result vs ThreadCB returning voidptr).
1021 // This prevents -Werror=incompatible-pointer-types under -cstrict.
1022 field_unwrap_sym := g.table.final_sym(sfield.typ)
1023 if field_unwrap_sym.kind == .function && !sfield.expected_type.has_option_or_result()
1024 && g.cur_struct_init_typ != 0 {
1025 struct_sym := g.table.sym(g.cur_struct_init_typ)
1026 if struct_sym.info is ast.Struct {
1027 if struct_sym.info.is_typedef {
1028 // For @[typedef] C structs, the actual C field types may differ
1029 // from V's declarations (e.g. C has `const char*` but V uses `char*`).
1030 // Use __typeof__ to cast to the actual C struct field type.
1031 c_struct_name := if struct_sym.cname.starts_with('C__') {
1032 struct_sym.cname[3..]
1033 } else {
1034 struct_sym.cname
1035 }
1036 g.write('(__typeof__(((${c_struct_name}){}).${sfield.name}))')
1037 } else {
1038 for f in struct_sym.info.fields {
1039 if f.name == sfield.name {
1040 // Only cast named function type aliases (e.g. ThreadCB),
1041 // not anonymous function types from generic structs which
1042 // may generate non-existent type names.
1043 fsym := g.table.sym(f.typ)
1044 if !fsym.name.starts_with('fn ') {
1045 field_styp := g.styp(f.typ)
1046 expr_styp := g.styp(sfield.typ)
1047 if field_styp != expr_styp {
1048 g.write('(${field_styp})')
1049 }
1050 }
1051 break
1052 }
1053 }
1054 }
1055 }
1056 }
1057 g.struct_init_field_value(sfield)
1058}
1059
1060fn (mut g Gen) struct_init_ptr_field(target string, sfield ast.StructInitField, language ast.Language) {
1061 field_name := if language == .v { c_name(sfield.name) } else { sfield.name }
1062 g.write('${target}->${field_name} = ')
1063 g.struct_init_field_value(sfield)
1064}
1065
1066fn (mut g Gen) struct_init_field_value(sfield ast.StructInitField) {
1067 old_inside_return := g.inside_return
1068 old_inside_return_expr := g.inside_return_expr
1069 g.inside_return = false
1070 g.inside_return_expr = false
1071 defer {
1072 g.inside_return = old_inside_return
1073 g.inside_return_expr = old_inside_return_expr
1074 }
1075 field_type_sym := g.table.sym(sfield.typ)
1076 mut cloned := false
1077 if g.is_autofree && !sfield.typ.is_ptr() && field_type_sym.kind in [.array, .string] {
1078 // array fn args should not be cloned into struct fields: the caller owns and
1079 // frees the underlying data, so cloning would break mutation aliasing
1080 // (e.g. `for mut x in iter`) and leak the clone. strings are still cloned
1081 // since they may be freed before the struct field is used.
1082 is_fn_arg := field_type_sym.kind == .array && sfield.expr is ast.Ident
1083 && sfield.expr.obj is ast.Var && (sfield.expr.obj as ast.Var).is_arg
1084 if !is_fn_arg {
1085 if g.gen_clone_assignment(sfield.expected_type, sfield.expr, sfield.typ, false) {
1086 cloned = true
1087 }
1088 }
1089 }
1090 if !cloned {
1091 inside_cast_in_heap := g.inside_cast_in_heap
1092 g.inside_cast_in_heap = 0 // prevent use of pointers in child structs
1093
1094 field_unwrap_typ := g.unwrap_generic(sfield.typ)
1095 field_unwrap_sym := g.table.final_sym(field_unwrap_typ)
1096 expected_unwrap_typ := g.unwrap_generic(sfield.expected_type)
1097 expected_unwrap_sym := g.table.final_sym(expected_unwrap_typ)
1098 is_auto_deref_var := sfield.expr.is_auto_deref_var()
1099 if expected_unwrap_sym.info is ast.ArrayFixed && sfield.expr is ast.ArrayInit
1100 && !sfield.expr.is_fixed && !sfield.expr.has_len && !sfield.expr.has_init
1101 && sfield.expr.exprs.len > 0 && !sfield.expected_type.has_flag(.option) {
1102 fixed_array_expr := ast.ArrayInit{
1103 ...sfield.expr
1104 is_fixed: true
1105 has_val: true
1106 elem_type: expected_unwrap_sym.info.elem_type
1107 typ: expected_unwrap_typ
1108 }
1109 g.fixed_array_init(fixed_array_expr, g.unwrap(expected_unwrap_typ), '', false)
1110 } else if field_unwrap_sym.info is ast.ArrayFixed && !sfield.expected_type.has_flag(.option) {
1111 match sfield.expr {
1112 ast.Ident, ast.SelectorExpr {
1113 g.fixed_array_var_init(g.expr_string(ast.Expr(sfield.expr)), is_auto_deref_var,
1114 field_unwrap_sym.info.elem_type, field_unwrap_sym.info.size)
1115 }
1116 ast.CastExpr, ast.CallExpr {
1117 tmp_var := g.expr_with_var(ast.Expr(sfield.expr), sfield.expected_type, false)
1118 g.fixed_array_var_init(tmp_var, false, field_unwrap_sym.info.elem_type,
1119 field_unwrap_sym.info.size)
1120 }
1121 ast.ArrayInit {
1122 if sfield.expr.has_index {
1123 tmp_var := g.expr_with_var(ast.Expr(sfield.expr), sfield.expected_type,
1124 false)
1125 g.fixed_array_var_init(tmp_var, false, field_unwrap_sym.info.elem_type,
1126 field_unwrap_sym.info.size)
1127 } else if sfield.expr.has_callexpr {
1128 tmp_var := g.expr_with_fixed_array(ast.Expr(sfield.expr), sfield.typ,
1129 sfield.expected_type)
1130 g.fixed_array_var_init(tmp_var, false, field_unwrap_sym.info.elem_type,
1131 field_unwrap_sym.info.size)
1132 } else {
1133 g.struct_init_field_default(field_unwrap_typ, sfield, field_unwrap_sym)
1134 }
1135 }
1136 else {
1137 g.struct_init_field_default(field_unwrap_typ, sfield, field_unwrap_sym)
1138 }
1139 }
1140 } else {
1141 g.struct_init_field_default(field_unwrap_typ, sfield, field_unwrap_sym)
1142 }
1143 g.inside_cast_in_heap = inside_cast_in_heap // restore value for further struct inits
1144 }
1145}
1146
1147fn (mut g Gen) struct_init_field_default(field_unwrap_typ ast.Type, sfield &ast.StructInitField, field_unwrap_sym ast.TypeSymbol) {
1148 if field_unwrap_typ != ast.voidptr_type && field_unwrap_typ != ast.nil_type
1149 && (sfield.expected_type.is_ptr() && !sfield.expected_type.has_flag(.shared_f))
1150 && !sfield.expected_type.has_flag(.option) && !field_unwrap_typ.is_any_kind_of_pointer()
1151 && !field_unwrap_typ.is_number() {
1152 g.write('/* autoref */&')
1153 }
1154
1155 if (sfield.expected_type.has_flag(.option) && !field_unwrap_typ.has_flag(.option))
1156 || (sfield.expected_type.has_flag(.result) && !field_unwrap_typ.has_flag(.result)) {
1157 g.expr_with_opt(sfield.expr, field_unwrap_typ, sfield.expected_type)
1158 } else if sfield.expr is ast.LambdaExpr && sfield.expected_type.has_flag(.option) {
1159 g.expr_opt_with_cast(ast.Expr(sfield.expr), field_unwrap_typ, sfield.expected_type)
1160 } else if field_unwrap_sym.kind == .function && sfield.expected_type.has_flag(.option) {
1161 tmp_out_var := g.new_tmp_var()
1162 g.expr_with_tmp_var(sfield.expr, field_unwrap_typ, sfield.expected_type, tmp_out_var, true)
1163 } else {
1164 // When a smartcast variable (e.g. `tree` smartcast to `Empty`) is used as a
1165 // struct init field that expects the original sumtype (e.g. `Tree[T]`), prevent
1166 // the smartcast unwrapping so the variable is emitted as-is (the sumtype value).
1167 if sfield.expr is ast.Ident {
1168 exp_sym := g.table.final_sym(g.unwrap_generic(sfield.expected_type))
1169 if exp_sym.kind == .sum_type {
1170 scope := g.file.scope.innermost(sfield.expr.pos.pos)
1171 if v := scope.find_var(sfield.expr.name) {
1172 if v.smartcasts.len > 0 {
1173 g.prevent_sum_type_unwrapping_once = true
1174 }
1175 }
1176 }
1177 }
1178 g.left_is_opt = true
1179 g.expr_with_cast(sfield.expr, field_unwrap_typ, sfield.expected_type)
1180 }
1181}
1182
1183// struct_has_large_fixed_array returns true if the struct type contains
1184// fixed arrays whose total size exceeds the threshold (64KB).
1185// This is used to determine whether to avoid stack-allocated compound literals
1186// which can cause stack overflow for large structs.
1187fn (g &Gen) struct_has_large_fixed_array(typ ast.Type) bool {
1188 sym := g.table.final_sym(typ)
1189 if sym.info is ast.Struct {
1190 for field in sym.info.fields {
1191 field_sym := g.table.final_sym(field.typ)
1192 if field_sym.info is ast.ArrayFixed {
1193 // Check if this fixed array is large (> 64KB)
1194 // Conservative estimate: 8 bytes per element
1195 size := i64(field_sym.info.size) * 8
1196 if size > 65536 {
1197 return true
1198 }
1199 }
1200 }
1201 }
1202 return false
1203}
1204