v4 / vlib / v / gen / c / struct.v
1220 lines · 1181 sloc · 39.89 KB · d48d789285f1311ec69eec318bb98974342ebd63
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 || g.cur_fn == unsafe { nil }
559 || g.stmt_path_pos.len <= g.inside_ternary {
560 return false
561 }
562 if sym.info !is ast.Struct {
563 return false
564 }
565 info := sym.info as ast.Struct
566 if info.is_anon || info.is_union || info.embeds.len > 0
567 || node.init_fields.len != info.fields.len {
568 return false
569 }
570 for init_field in node.init_fields {
571 if g.need_tmp_var_in_expr(init_field.expr) {
572 return false
573 }
574 }
575 if node.no_keys {
576 return true
577 }
578 field_names := info.fields.map(it.name)
579 return node.init_fields.all(it.name in field_names)
580}
581
582fn (mut g Gen) direct_heap_struct_init(node ast.StructInit, styp string, info ast.Struct, language ast.Language) {
583 stmt_pos_idx := g.stmt_path_pos.len - (1 + g.inside_ternary)
584 stmt_str := g.out.cut_to(g.stmt_path_pos[stmt_pos_idx]).trim_space()
585 g.empty_line = true
586 tmp_var := g.new_tmp_var()
587 if info.is_empty_struct() {
588 g.writeln('${styp}* ${tmp_var} = HEAP(${styp}, ((${styp}){E_STRUCT}));')
589 } else {
590 g.writeln('${styp}* ${tmp_var} = (${styp}*)builtin___v_malloc(sizeof(${styp}) == 0 ? 1 : sizeof(${styp}));')
591 }
592 for i, init_field in node.init_fields {
593 mut resolved_field := init_field
594 if node.no_keys {
595 resolved_field.name = info.fields[i].name
596 }
597 if resolved_field.typ == 0 || resolved_field.expected_type == 0 {
598 // Resolve from struct field info - needed for generic struct inits
599 // where the checker left init_field.typ/expected_type unset
600 for f in info.fields {
601 if f.name == resolved_field.name {
602 field_typ := g.unwrap_generic(f.typ)
603 if resolved_field.typ == 0 {
604 resolved_field.typ = field_typ
605 }
606 if resolved_field.expected_type == 0 {
607 resolved_field.expected_type = field_typ
608 }
609 break
610 }
611 }
612 if resolved_field.typ == 0 {
613 g.checker_bug('struct init, field.typ is 0', resolved_field.pos)
614 }
615 }
616 if g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0 {
617 old_inside_return := g.inside_return
618 old_inside_return_expr := g.inside_return_expr
619 g.inside_return = false
620 g.inside_return_expr = false
621 resolved_field_typ := g.resolved_expr_type(ast.Expr(resolved_field.expr),
622 resolved_field.typ)
623 g.inside_return = old_inside_return
624 g.inside_return_expr = old_inside_return_expr
625 if resolved_field_typ != 0 {
626 resolved_field.typ = g.unwrap_generic(g.recheck_concrete_type(resolved_field_typ))
627 }
628 }
629 g.struct_init_ptr_field(tmp_var, resolved_field, language)
630 g.writeln(';')
631 }
632 // Keep the cut position current without leaving stale entries for later hoists.
633 g.stmt_path_pos[stmt_pos_idx] = g.out.len
634 g.write2(stmt_str, ' ')
635 g.write(tmp_var)
636}
637
638fn (mut g Gen) get_embed_field_name(field_type ast.Type, field_name string) string {
639 update_sym := g.table.sym(field_type)
640 _, embeds := g.table.find_field_from_embeds(update_sym, field_name) or {
641 ast.StructField{}, []ast.Type{}
642 }
643 mut s := ''
644 for embed in embeds {
645 esym := g.table.sym(embed)
646 ename := esym.embed_name()
647 if embed.is_ptr() {
648 s += '${ename}->'
649 } else {
650 s += '${ename}.'
651 }
652 }
653 return s
654}
655
656fn (mut g Gen) init_shared_field(field ast.StructField) {
657 field_typ := field.typ.deref()
658 shared_styp := g.styp(field_typ)
659 g.write('(${shared_styp}*)__dup${shared_styp}(&(${shared_styp}){.mtx= {0}, .val=')
660 if field.has_default_expr {
661 // avoid generate shared assign inside expr
662 old_is_shared := g.is_shared
663 g.is_shared = false
664 g.expr(field.default_expr)
665 g.is_shared = old_is_shared
666 } else {
667 g.write(g.type_default(field_typ.clear_flag(.shared_f)))
668 }
669 g.write('}, sizeof(${shared_styp}))')
670}
671
672fn (mut g Gen) zero_struct_init_type(typ ast.Type) ast.Type {
673 mut resolved_typ := g.unwrap_generic(g.recheck_concrete_type(typ))
674 if resolved_typ == 0 {
675 resolved_typ = typ
676 }
677 return resolved_typ.clear_option_and_result().clear_flag(.shared_f).clear_flag(.atomic_f)
678}
679
680fn (mut g Gen) zero_struct_init_would_recurse(typ ast.Type) bool {
681 resolved_typ := g.zero_struct_init_type(typ)
682 return resolved_typ in g.zero_struct_init_stack
683}
684
685fn (mut g Gen) write_zero_struct_init(default_init ast.StructInit) {
686 if g.zero_struct_init_would_recurse(default_init.typ) {
687 g.write(g.type_default(default_init.typ))
688 return
689 }
690 g.struct_init(default_init)
691}
692
693fn (mut g Gen) zero_struct_field(field ast.StructField) bool {
694 old_inside_cast_in_heap := g.inside_cast_in_heap
695 g.inside_cast_in_heap = 0
696 defer {
697 g.inside_cast_in_heap = old_inside_cast_in_heap
698 }
699 sym := g.table.sym(field.typ)
700 final_sym := g.table.final_sym(field.typ)
701 field_name := if sym.language == .v { c_name(field.name) } else { field.name }
702 if sym.info is ast.Struct {
703 if sym.info.fields.len == 0 {
704 // For empty struct fields, check if it's a shared field
705 if field.typ.has_flag(.shared_f) {
706 g.write('.${field_name} = ')
707 g.init_shared_field(field)
708 return true
709 }
710 return false
711 } else if !field.has_default_expr {
712 mut has_option_field := false
713 if sym.info.is_shared || field.typ.has_flag(.shared_f) {
714 g.write('.${field_name} = ')
715 g.init_shared_field(field)
716 return true
717 }
718 for fd in sym.info.fields {
719 if fd.typ.has_flag(.option) {
720 has_option_field = true
721 break
722 }
723 }
724 if has_option_field || field.anon_struct_decl.fields.len > 0 {
725 default_init := ast.StructInit{
726 typ: field.typ
727 language: field.anon_struct_decl.language
728 }
729 g.write('.${field_name} = ')
730 if field.typ.has_flag(.option) {
731 if field.is_recursive || field.typ.is_ptr() {
732 g.expr_with_opt(ast.None{}, ast.none_type, field.typ)
733 } else {
734 tmp_var := g.new_tmp_var()
735 g.expr_with_tmp_var(default_init, field.typ, field.typ, tmp_var, true)
736 }
737 } else {
738 g.write_zero_struct_init(default_init)
739 }
740 return true
741 } else if sym.language == .v && !field.typ.is_ptr() && sym.mod != 'builtin'
742 && !sym.info.is_empty_struct() {
743 default_init := ast.StructInit{
744 typ: field.typ
745 }
746 g.write('.${field_name} = ')
747 g.write_zero_struct_init(default_init)
748 return true
749 }
750 }
751 }
752 g.write('.${field_name} = ')
753 if field.has_default_expr {
754 if sym.kind in [.sum_type, .interface] {
755 if field.typ.has_flag(.option) {
756 g.expr_with_opt(field.default_expr, field.default_expr_typ, field.typ)
757 } else {
758 g.expr_with_cast(field.default_expr, field.default_expr_typ, field.typ)
759 }
760 return true
761 }
762
763 if field.default_expr is ast.None {
764 g.gen_option_error(field.typ, ast.None{})
765 return true
766 } else if field.typ.has_flag(.option) {
767 tmp_var := g.new_tmp_var()
768 g.expr_with_tmp_var(field.default_expr, field.default_expr_typ, field.typ, tmp_var,
769 true)
770 return true
771 } else if field.typ.has_flag(.result) && !field.default_expr_typ.has_flag(.result) {
772 tmp_var := g.new_tmp_var()
773 g.expr_with_tmp_var(field.default_expr, field.default_expr_typ, field.typ, tmp_var,
774 true)
775 return true
776 } else if final_sym.info is ast.ArrayFixed && field.default_expr !is ast.ArrayInit {
777 old_inside_memset := g.inside_memset
778 g.inside_memset = true
779 tmp_var := g.expr_with_var(field.default_expr, field.default_expr_typ,
780
781 field.default_expr !is ast.CallExpr && field.default_expr !is ast.CastExpr)
782 g.fixed_array_var_init(tmp_var, false, final_sym.info.elem_type, final_sym.info.size)
783 g.inside_memset = old_inside_memset
784 return true
785 } else if field.default_expr is ast.CastExpr {
786 resolved_field_type := g.unwrap_generic(field.typ)
787 resolved_default_type := g.unwrap_generic(field.default_expr.typ)
788 if resolved_field_type != 0 && resolved_default_type == 0 {
789 g.expr_with_cast(field.default_expr.expr, field.default_expr.expr_type,
790 resolved_field_type)
791 return true
792 }
793 } else if field.typ.has_flag(.shared_f) {
794 g.init_shared_field(field)
795 return true
796 }
797 old_expected_cast_type := g.expected_cast_type
798 g.expected_cast_type = field.typ
799 g.expr(field.default_expr)
800 g.expected_cast_type = old_expected_cast_type
801 } else if field.typ.has_flag(.option) {
802 g.gen_option_error(field.typ, ast.None{})
803 return true
804 } else if sym.info is ast.SumType && !field.typ.is_any_kind_of_pointer() {
805 g.write(g.type_default_sumtype(field.typ, sym))
806 return true
807 } else if sym.info is ast.ArrayFixed {
808 elem_is_option := sym.info.elem_type.has_flag(.option)
809 g.write('{')
810 if !elem_is_option && field.typ.has_flag(.shared_f) {
811 g.write('0')
812 } else {
813 default_str := g.type_default(sym.info.elem_type)
814 for i in 0 .. sym.info.size {
815 if elem_is_option {
816 g.gen_option_error(sym.info.elem_type, ast.None{})
817 } else {
818 g.write(default_str)
819 }
820 if i != sym.info.size - 1 {
821 g.write(', ')
822 }
823 }
824 }
825 g.write('}')
826 } else if field.typ.has_flag(.shared_f) {
827 g.init_shared_field(field)
828 } else {
829 g.write(g.type_default(field.typ))
830 }
831 return true
832}
833
834fn (mut g Gen) struct_decl(s ast.Struct, name string, is_anon bool, is_option bool) {
835 if s.is_generic {
836 return
837 }
838 if name.contains('_T_') {
839 if s.is_union {
840 g.typedefs.writeln('typedef union ${name} ${name};')
841 } else {
842 g.typedefs.writeln('typedef struct ${name} ${name};')
843 }
844 }
845 // TODO: avoid buffer manip
846 start_pos := g.type_definitions.len
847
848 mut pre_pragma := ''
849 mut post_pragma := ''
850
851 for attr in s.attrs {
852 match attr.name {
853 '_pack' {
854 pre_pragma += '#pragma pack(push, ${attr.arg})\n'
855 post_pragma += '#pragma pack(pop)'
856 }
857 'packed' {
858 pre_pragma += '#pragma pack(push, 1)\n'
859 post_pragma += '#pragma pack(pop)'
860 }
861 else {}
862 }
863 }
864
865 is_minify := s.is_minify
866 g.type_definitions.writeln(pre_pragma)
867
868 mut aligned_attr := ''
869 if attr := s.attrs.find_first('aligned') {
870 attr_arg := if attr.arg == '' { '' } else { ' (${attr.arg})' }
871 aligned_attr += if g.is_cc_msvc {
872 '__declspec(align${attr_arg})'
873 } else {
874 ' __attribute__((aligned${attr_arg}))'
875 }
876 }
877 if is_anon {
878 option_prefix := if is_option { '_option_' } else { '' }
879 if s.is_shared {
880 g.type_definitions.write_string('\t${option_prefix}__shared__${name}* ')
881 } else {
882 g.type_definitions.write_string('\t${option_prefix}${name} ')
883 }
884 return
885 } else if s.is_union {
886 if g.is_cc_msvc && aligned_attr != '' {
887 g.type_definitions.writeln('union ${aligned_attr} ${name} {')
888 } else {
889 g.type_definitions.writeln('union ${name} {')
890 }
891 } else {
892 if g.is_cc_msvc && aligned_attr != '' {
893 g.type_definitions.writeln('struct ${aligned_attr} ${name} {')
894 } else {
895 g.type_definitions.writeln('struct ${name} {')
896 }
897 }
898
899 if s.fields.len > 0 || s.embeds.len > 0 {
900 for field in s.fields {
901 // Some of these structs may want to contain
902 // options that may not be defined at this point
903 // if this is the case then we are going to
904 // buffer manip out in front of the struct
905 // write the option in and then continue
906 // FIXME: for parallel cgen (two different files using the same option in struct fields)
907 if field.typ.has_flag(.option) {
908 // Dont use g.styp() here because it will register
909 // option and we dont want that
910 styp, base := g.option_type_name(field.typ)
911 lock g.done_options {
912 if base !in g.done_options {
913 g.done_options << base
914 last_text := g.type_definitions.after(start_pos).clone()
915 g.type_definitions.go_back_to(start_pos)
916 g.typedefs.writeln('typedef struct ${styp} ${styp};')
917 g.type_definitions.writeln('${g.option_type_text(styp, base)};')
918 g.type_definitions.write_string(last_text)
919 }
920 }
921 }
922 if field.typ.has_flag(.result) {
923 // Dont use g.styp() here because it will register
924 // result and we dont want that
925 styp, base := g.result_type_name(field.typ)
926 lock g.done_results {
927 if base !in g.done_results {
928 g.done_results << base
929 last_text := g.type_definitions.after(start_pos).clone()
930 g.type_definitions.go_back_to(start_pos)
931 g.typedefs.writeln('typedef struct ${styp} ${styp};')
932 g.type_definitions.writeln('${g.result_type_text(styp, base)};')
933 g.type_definitions.write_string(last_text)
934 }
935 }
936 }
937 type_name := g.styp(field.typ)
938 field_name := c_name(field.name)
939 volatile_prefix := if field.is_volatile { 'volatile ' } else { '' }
940 mut size_suffix := ''
941 mut is_enum_bitfield := false
942 if is_minify && !g.is_cc_msvc && !g.pref.output_cross_c && !field.typ.has_flag(.option)
943 && !field.typ.has_flag(.result) {
944 if field.typ == ast.bool_type_idx {
945 size_suffix = ' : 1'
946 } else {
947 field_sym := g.table.sym(field.typ)
948 if field_sym.info is ast.Enum {
949 if !field_sym.info.is_flag && !field_sym.info.uses_exprs {
950 mut bits_needed := 0
951 mut l := field_sym.info.vals.len
952 for l > 0 {
953 bits_needed++
954 l >>= 1
955 }
956 size_suffix = ' : ${bits_needed}'
957 is_enum_bitfield = true
958 }
959 }
960 }
961 }
962 field_sym := g.table.sym(field.typ)
963 mut field_is_anon := false
964 if field_sym.info is ast.Struct {
965 if field_sym.info.is_anon {
966 field_is_anon = true
967 // Recursively generate code for this anon struct (this is the field's type)
968 g.struct_decl(field_sym.info, field_sym.cname, true,
969 field.typ.has_flag(.option))
970 // Now the field's name
971 g.type_definitions.writeln(' ${field_name}${size_suffix};')
972 }
973 }
974 if !field_is_anon {
975 actual_type := if is_enum_bitfield {
976 enum_info := field_sym.info as ast.Enum
977 if enum_info.typ == ast.int_type {
978 // default enum base type (int): C bit-field signedness is
979 // implementation-defined; force unsigned to avoid Clang treating
980 // it as signed (which corrupts values >= 64 in a 7-bit field, etc.)
981 'unsigned int'
982 } else {
983 // explicit base type (as u8, as i8, as i32, …): the typedef
984 // already carries the correct signedness, so respect the user's choice
985 type_name
986 }
987 } else {
988 type_name
989 }
990 g.type_definitions.writeln('\t${volatile_prefix}${actual_type} ${field_name}${size_suffix};')
991 }
992 }
993 } else {
994 g.type_definitions.writeln('\tE_STRUCT_DECL;')
995 }
996 ti_attrs := if !g.is_cc_msvc && s.attrs.contains('packed') {
997 '__attribute__((__packed__))'
998 } else {
999 ''
1000 }
1001 g.type_definitions.write_string('}${ti_attrs}')
1002 if !g.is_cc_msvc && aligned_attr != '' {
1003 g.type_definitions.write_string(' ${aligned_attr}')
1004 }
1005 if !is_anon {
1006 g.type_definitions.write_string(';')
1007 }
1008 g.type_definitions.writeln('')
1009 if post_pragma != '' {
1010 g.type_definitions.writeln(post_pragma)
1011 }
1012}
1013
1014fn (mut g Gen) struct_init_field(sfield ast.StructInitField, language ast.Language) {
1015 field_name := if language == .v { c_name(sfield.name) } else { sfield.name }
1016 g.write('.${field_name} = ')
1017 // Cast function pointers to the struct field's declared function type alias.
1018 // The checker coerces V types to match, but C signatures can differ
1019 // (e.g. callback returning &Result vs ThreadCB returning voidptr).
1020 // This prevents -Werror=incompatible-pointer-types under -cstrict.
1021 field_unwrap_sym := g.table.final_sym(sfield.typ)
1022 if field_unwrap_sym.kind == .function && !sfield.expected_type.has_option_or_result()
1023 && g.cur_struct_init_typ != 0 {
1024 struct_sym := g.table.sym(g.cur_struct_init_typ)
1025 if struct_sym.info is ast.Struct {
1026 if struct_sym.info.is_typedef {
1027 // For @[typedef] C structs, the actual C field types may differ
1028 // from V's declarations (e.g. C has `const char*` but V uses `char*`).
1029 // Use __typeof__ to cast to the actual C struct field type.
1030 c_struct_name := if struct_sym.cname.starts_with('C__') {
1031 struct_sym.cname[3..]
1032 } else {
1033 struct_sym.cname
1034 }
1035 g.write('(__typeof__(((${c_struct_name}){}).${sfield.name}))')
1036 } else {
1037 for f in struct_sym.info.fields {
1038 if f.name == sfield.name {
1039 // Only cast named function type aliases (e.g. ThreadCB),
1040 // not anonymous function types from generic structs which
1041 // may generate non-existent type names.
1042 fsym := g.table.sym(f.typ)
1043 if !fsym.name.starts_with('fn ') {
1044 field_styp := g.styp(f.typ)
1045 expr_styp := g.styp(sfield.typ)
1046 if field_styp != expr_styp {
1047 g.write('(${field_styp})')
1048 }
1049 }
1050 break
1051 }
1052 }
1053 }
1054 }
1055 }
1056 g.struct_init_field_value(sfield)
1057}
1058
1059fn (mut g Gen) struct_init_ptr_field(target string, sfield ast.StructInitField, language ast.Language) {
1060 field_name := if language == .v { c_name(sfield.name) } else { sfield.name }
1061 g.write('${target}->${field_name} = ')
1062 g.struct_init_field_value(sfield)
1063}
1064
1065fn (mut g Gen) struct_init_field_value(sfield ast.StructInitField) {
1066 old_inside_return := g.inside_return
1067 old_inside_return_expr := g.inside_return_expr
1068 g.inside_return = false
1069 g.inside_return_expr = false
1070 defer {
1071 g.inside_return = old_inside_return
1072 g.inside_return_expr = old_inside_return_expr
1073 }
1074 field_type_sym := g.table.sym(sfield.typ)
1075 mut cloned := false
1076 if g.is_autofree && !sfield.typ.is_ptr() && field_type_sym.kind in [.array, .string] {
1077 // array fn args should not be cloned into struct fields: the caller owns and
1078 // frees the underlying data, so cloning would break mutation aliasing
1079 // (e.g. `for mut x in iter`) and leak the clone. strings are still cloned
1080 // since they may be freed before the struct field is used.
1081 is_fn_arg := field_type_sym.kind == .array && sfield.expr is ast.Ident
1082 && sfield.expr.obj is ast.Var && (sfield.expr.obj as ast.Var).is_arg
1083 if !is_fn_arg {
1084 if g.gen_clone_assignment(sfield.expected_type, sfield.expr, sfield.typ, false) {
1085 cloned = true
1086 }
1087 }
1088 }
1089 if !cloned {
1090 inside_cast_in_heap := g.inside_cast_in_heap
1091 g.inside_cast_in_heap = 0 // prevent use of pointers in child structs
1092 // A field value's option-ness is governed by the field's own type, not by an
1093 // outer `&Struct{...}` -> interface conversion. Without resetting this, an
1094 // option field initialized from an option value gets wrongly unwrapped while
1095 // `inside_interface_cast` is still set from the surrounding cast.
1096 inside_interface_cast := g.inside_interface_cast
1097 g.inside_interface_cast = false
1098
1099 field_unwrap_typ := g.unwrap_generic(sfield.typ)
1100 field_unwrap_sym := g.table.final_sym(field_unwrap_typ)
1101 expected_unwrap_typ := g.unwrap_generic(sfield.expected_type)
1102 expected_unwrap_sym := g.table.final_sym(expected_unwrap_typ)
1103 is_auto_deref_var := sfield.expr.is_auto_deref_var()
1104 if expected_unwrap_sym.kind == .map && sfield.expr is ast.MapInit
1105 && !sfield.expected_type.has_option_or_result()
1106 && !sfield.expected_type.has_flag(.shared_f)
1107 && !sfield.expected_type.has_flag(.atomic_f) && !sfield.expected_type.is_ptr() {
1108 expected_map_info := expected_unwrap_sym.map_info()
1109 g.map_init(ast.MapInit{
1110 ...sfield.expr
1111 typ: expected_unwrap_typ
1112 key_type: expected_map_info.key_type
1113 value_type: expected_map_info.value_type
1114 })
1115 } else if expected_unwrap_sym.info is ast.ArrayFixed && sfield.expr is ast.ArrayInit
1116 && !sfield.expr.is_fixed && !sfield.expr.has_len && !sfield.expr.has_init
1117 && sfield.expr.exprs.len > 0 && !sfield.expected_type.has_flag(.option) {
1118 fixed_array_expr := ast.ArrayInit{
1119 ...sfield.expr
1120 is_fixed: true
1121 has_val: true
1122 elem_type: expected_unwrap_sym.info.elem_type
1123 typ: expected_unwrap_typ
1124 }
1125 g.fixed_array_init(fixed_array_expr, g.unwrap(expected_unwrap_typ), '', false)
1126 } else if field_unwrap_sym.info is ast.ArrayFixed && !sfield.expected_type.has_flag(.option) {
1127 match sfield.expr {
1128 ast.Ident, ast.SelectorExpr {
1129 g.fixed_array_var_init(g.expr_string(ast.Expr(sfield.expr)), is_auto_deref_var,
1130 field_unwrap_sym.info.elem_type, field_unwrap_sym.info.size)
1131 }
1132 ast.CastExpr, ast.CallExpr {
1133 tmp_var := g.expr_with_var(ast.Expr(sfield.expr), sfield.expected_type, false)
1134 g.fixed_array_var_init(tmp_var, false, field_unwrap_sym.info.elem_type,
1135 field_unwrap_sym.info.size)
1136 }
1137 ast.ArrayInit {
1138 if sfield.expr.has_index {
1139 tmp_var := g.expr_with_var(ast.Expr(sfield.expr), sfield.expected_type,
1140 false)
1141 g.fixed_array_var_init(tmp_var, false, field_unwrap_sym.info.elem_type,
1142 field_unwrap_sym.info.size)
1143 } else if sfield.expr.has_callexpr {
1144 tmp_var := g.expr_with_fixed_array(ast.Expr(sfield.expr), sfield.typ,
1145 sfield.expected_type)
1146 g.fixed_array_var_init(tmp_var, false, field_unwrap_sym.info.elem_type,
1147 field_unwrap_sym.info.size)
1148 } else {
1149 g.struct_init_field_default(field_unwrap_typ, sfield, field_unwrap_sym)
1150 }
1151 }
1152 else {
1153 g.struct_init_field_default(field_unwrap_typ, sfield, field_unwrap_sym)
1154 }
1155 }
1156 } else {
1157 g.struct_init_field_default(field_unwrap_typ, sfield, field_unwrap_sym)
1158 }
1159 g.inside_cast_in_heap = inside_cast_in_heap // restore value for further struct inits
1160 g.inside_interface_cast = inside_interface_cast
1161 }
1162}
1163
1164fn (mut g Gen) struct_init_field_default(field_unwrap_typ ast.Type, sfield &ast.StructInitField, field_unwrap_sym ast.TypeSymbol) {
1165 if field_unwrap_typ != ast.voidptr_type && field_unwrap_typ != ast.nil_type
1166 && (sfield.expected_type.is_ptr() && !sfield.expected_type.has_flag(.shared_f))
1167 && !sfield.expected_type.has_flag(.option) && !field_unwrap_typ.is_any_kind_of_pointer()
1168 && !field_unwrap_typ.is_number() {
1169 g.write('/* autoref */&')
1170 }
1171
1172 if (sfield.expected_type.has_flag(.option) && !field_unwrap_typ.has_flag(.option))
1173 || (sfield.expected_type.has_flag(.result) && !field_unwrap_typ.has_flag(.result)) {
1174 g.expr_with_opt(sfield.expr, field_unwrap_typ, sfield.expected_type)
1175 } else if sfield.expr is ast.LambdaExpr && sfield.expected_type.has_flag(.option) {
1176 g.expr_opt_with_cast(ast.Expr(sfield.expr), field_unwrap_typ, sfield.expected_type)
1177 } else if field_unwrap_sym.kind == .function && sfield.expected_type.has_flag(.option) {
1178 tmp_out_var := g.new_tmp_var()
1179 g.expr_with_tmp_var(sfield.expr, field_unwrap_typ, sfield.expected_type, tmp_out_var, true)
1180 } else {
1181 // When a smartcast variable (e.g. `tree` smartcast to `Empty`) is used as a
1182 // struct init field that expects the original sumtype (e.g. `Tree[T]`), prevent
1183 // the smartcast unwrapping so the variable is emitted as-is (the sumtype value).
1184 if sfield.expr is ast.Ident {
1185 exp_sym := g.table.final_sym(g.unwrap_generic(sfield.expected_type))
1186 if exp_sym.kind == .sum_type {
1187 scope := g.file.scope.innermost(sfield.expr.pos.pos)
1188 if v := scope.find_var(sfield.expr.name) {
1189 if v.smartcasts.len > 0 {
1190 g.prevent_sum_type_unwrapping_once = true
1191 }
1192 }
1193 }
1194 }
1195 g.left_is_opt = true
1196 g.expr_with_cast(sfield.expr, field_unwrap_typ, sfield.expected_type)
1197 }
1198}
1199
1200// struct_has_large_fixed_array returns true if the struct type contains
1201// fixed arrays whose total size exceeds the threshold (64KB).
1202// This is used to determine whether to avoid stack-allocated compound literals
1203// which can cause stack overflow for large structs.
1204fn (g &Gen) struct_has_large_fixed_array(typ ast.Type) bool {
1205 sym := g.table.final_sym(typ)
1206 if sym.info is ast.Struct {
1207 for field in sym.info.fields {
1208 field_sym := g.table.final_sym(field.typ)
1209 if field_sym.info is ast.ArrayFixed {
1210 // Check if this fixed array is large (> 64KB)
1211 // Conservative estimate: 8 bytes per element
1212 size := i64(field_sym.info.size) * 8
1213 if size > 65536 {
1214 return true
1215 }
1216 }
1217 }
1218 }
1219 return false
1220}
1221