Files

1061 lines
28 KiB
V

module c
import v3.flat
import v3.types
fn c_field_name(name string) string {
if name.starts_with('&') {
return c_field_name(name[1..])
}
if name.starts_with('ptr') && name.len > 3 && name[3..].contains('__') {
return c_name(name[3..])
}
if name.starts_with('ptr') && name.len > 3 && name[3..].contains('.') {
return c_name(name[3..])
}
return c_name(name)
}
fn (mut g FlatGen) gen_struct_init(node flat.Node) {
if node.value.starts_with('chan ') {
g.gen_channel_init(node)
return
}
if g.gen_lowered_sum_init(node) {
return
}
name := g.struct_init_c_type_name(node.value)
if node.children_count == 0 && g.is_scalar_zero_init_type(node.value, name) {
g.write(g.scalar_zero_init(name))
return
}
g.write('(${name}){')
mut set_fields := map[string]bool{}
mut has_field := false
if g.is_interface_type_name(node.value) {
if tid := g.interface_init_typ_id(node) {
g.write('._typ = ${tid}')
has_field = true
}
}
for i in 0 .. node.children_count {
field := g.a.child_node(&node, i)
if has_field {
g.write(', ')
}
value_id := g.a.child(field, 0)
if field.value.len == 0 {
if sf := g.struct_field_at(node.value, i) {
if heap_copy_type := g.heap_copy_type_for_sum_pointer_field(node.value, sf.name,
value_id)
{
inner_ct := g.tc.c_type(heap_copy_type)
g.write('(${inner_ct}*)memdup(')
g.gen_expr(value_id)
g.write(', sizeof(${inner_ct}))')
} else {
g.gen_expr_with_expected_type(value_id, sf.typ)
}
set_fields[sf.name] = true
} else {
g.gen_expr(value_id)
}
} else {
g.write('.${c_field_name(field.value)} = ')
if heap_copy_type := g.heap_copy_type_for_sum_pointer_field(node.value, field.value,
value_id)
{
inner_ct := g.tc.c_type(heap_copy_type)
g.write('(${inner_ct}*)memdup(')
g.gen_expr(value_id)
g.write(', sizeof(${inner_ct}))')
} else {
if ftyp := g.struct_field_type(node.value, field.value) {
g.gen_expr_with_expected_type(value_id, ftyp)
} else {
g.gen_expr(value_id)
}
}
set_fields[field.value] = true
}
has_field = true
}
qname := g.tc.qualify_name(node.value)
sname := if qname in g.tc.structs { qname } else { node.value }
if sname in g.tc.structs {
has_field = g.gen_struct_default_fields(node.value, mut set_fields, has_field)
for f in g.tc.structs[sname] {
if f.name in set_fields {
continue
}
if f.typ is types.Map {
if has_field {
g.write(', ')
}
g.write('.${c_field_name(f.name)} = ')
g.write_new_map(f.typ.key_type, f.typ.value_type)
has_field = true
} else if f.typ is types.Array {
c_elem := g.tc.c_type(f.typ.elem_type)
if has_field {
g.write(', ')
}
g.write('.${c_field_name(f.name)} = array_new(sizeof(${c_elem}), 0, 0)')
has_field = true
} else if g.field_needs_default_init(f.typ) {
if has_field {
g.write(', ')
}
g.write('.${c_field_name(f.name)} = ')
g.gen_default_value_for_type(f.typ)
has_field = true
}
}
}
g.write('}')
}
fn (mut g FlatGen) gen_lowered_sum_init(node flat.Node) bool {
sum_name := g.resolve_sum_name(node.value)
if sum_name !in g.tc.sum_types || node.children_count == 0 {
return false
}
name := g.struct_init_c_type_name(node.value)
g.write('(${name}){')
for i in 0 .. node.children_count {
field := g.a.child_node(&node, i)
if i > 0 {
g.write(', ')
}
g.write('.${c_field_name(field.value)} = ')
g.gen_lowered_sum_field_value(sum_name, field)
}
g.write('}')
return true
}
fn (mut g FlatGen) gen_lowered_sum_field_value(sum_name string, field &flat.Node) {
child_id := g.a.child(field, 0)
if field.value != 'typ' {
mut variant := ''
if field.typ.starts_with('&') {
variant = field.typ[1..]
} else if field.typ.len > 0 {
variant = field.typ
} else {
for v in g.tc.sum_types[sum_name] {
if g.sum_field_name(v) == field.value {
variant = v
break
}
}
}
if variant.len > 0 {
variant = g.resolve_variant(sum_name, variant)
inner_type := g.tc.parse_type(variant)
inner_ct := g.tc.c_type(inner_type)
child_type := g.tc.resolve_type(child_id)
g.write('(${inner_ct}*)memdup(')
if child_type is types.Pointer && g.type_names_match(child_type.base_type, inner_type) {
g.gen_expr(child_id)
} else {
g.write('(${inner_ct}[]){')
g.gen_expr_with_expected_type(child_id, inner_type)
g.write('}')
}
g.write(', sizeof(${inner_ct}))')
return
}
}
if field.typ.len > 0 {
g.gen_expr_with_expected_type(child_id, g.tc.parse_type(field.typ))
} else {
g.gen_expr(child_id)
}
}
fn (mut g FlatGen) gen_channel_init(node flat.Node) {
elem_type := g.tc.parse_type(node.value[5..])
elem_ct := g.tc.c_type(elem_type)
g.write('sync__new_channel_st((u32)(')
if cap_id := channel_init_field(node, g.a, 'cap') {
g.gen_expr(cap_id)
} else {
g.write('0')
}
g.write('), (u32)(sizeof(${elem_ct})))')
}
fn channel_init_field(node flat.Node, a &flat.FlatAst, name string) ?flat.NodeId {
for i in 0 .. node.children_count {
field := a.child_node(&node, i)
if field.kind == .field_init && field.value == name && field.children_count > 0 {
return a.child(field, 0)
}
}
return none
}
fn (mut g FlatGen) gen_heap_struct_init(node flat.Node) {
name := g.struct_init_c_type_name(node.value)
sum_name := g.resolve_sum_name(node.value)
is_sum_literal := sum_name in g.tc.sum_types
g.write('(${name}*)memdup(&(${name}){')
mut set_fields := map[string]bool{}
mut has_field := false
if g.is_interface_type_name(node.value) {
if tid := g.interface_init_typ_id(node) {
g.write('._typ = ${tid}')
has_field = true
}
}
for i in 0 .. node.children_count {
field := g.a.child_node(&node, i)
if has_field {
g.write(', ')
}
g.write('.${c_field_name(field.value)} = ')
value_id := g.a.child(field, 0)
if is_sum_literal {
g.gen_lowered_sum_field_value(sum_name, field)
} else if heap_copy_type := g.heap_copy_type_for_sum_pointer_field(node.value, field.value,
value_id)
{
inner_ct := g.tc.c_type(heap_copy_type)
g.write('(${inner_ct}*)memdup(')
g.gen_expr(value_id)
g.write(', sizeof(${inner_ct}))')
} else {
if ftyp := g.struct_field_type(node.value, field.value) {
g.gen_expr_with_expected_type(value_id, ftyp)
} else {
g.gen_expr(value_id)
}
}
set_fields[field.value] = true
has_field = true
}
qname := g.tc.qualify_name(node.value)
sname := if qname in g.tc.structs { qname } else { node.value }
if sname in g.tc.structs {
has_field = g.gen_struct_default_fields(node.value, mut set_fields, has_field)
for f in g.tc.structs[sname] {
if f.name in set_fields {
continue
}
if f.typ is types.Map {
if has_field {
g.write(', ')
}
g.write('.${c_field_name(f.name)} = ')
g.write_new_map(f.typ.key_type, f.typ.value_type)
has_field = true
} else if f.typ is types.Array {
c_elem := g.tc.c_type(f.typ.elem_type)
if has_field {
g.write(', ')
}
g.write('.${c_field_name(f.name)} = array_new(sizeof(${c_elem}), 0, 0)')
has_field = true
} else if g.field_needs_default_init(f.typ) {
if has_field {
g.write(', ')
}
g.write('.${c_field_name(f.name)} = ')
g.gen_default_value_for_type(f.typ)
has_field = true
}
}
}
g.write('}, sizeof(${name}))')
}
fn (g &FlatGen) heap_copy_type_for_sum_pointer_field(type_name string, field_name string, value_id flat.NodeId) ?types.Type {
resolved_sum := g.resolve_sum_name(type_name)
if resolved_sum !in g.tc.sum_types || int(value_id) < 0 {
return none
}
value := g.a.nodes[int(value_id)]
if !g.pointer_variant_arg_needs_heap_copy(value) {
return none
}
for variant in g.tc.sum_types[resolved_sum] {
if g.sum_field_name(variant) != field_name
|| !g.variant_references_sum(variant, resolved_sum) {
continue
}
variant_type := g.tc.parse_type(g.resolve_variant(resolved_sum, variant))
return variant_type
}
return none
}
fn (mut g FlatGen) gen_struct_default_fields(type_name string, mut set_fields map[string]bool, has_field bool) bool {
mut has := has_field
info := g.find_struct_decl(type_name) or { return has }
old_module := g.tc.cur_module
g.tc.cur_module = info.module
for i in 0 .. info.node.children_count {
field := g.a.child_node(&info.node, i)
if field.kind != .field_decl || field.children_count == 0 || field.value in set_fields {
continue
}
if has {
g.write(', ')
}
g.write('.${c_name(field.value)} = ')
g.gen_expr_with_expected_type(g.a.child(field, 0), g.tc.parse_type(field.typ))
set_fields[field.value] = true
has = true
}
g.tc.cur_module = old_module
return has
}
fn (mut g FlatGen) gen_default_value_for_type(typ types.Type) {
raw_typ := typ
if typ is types.Struct && !typ.name.starts_with('C.') {
ct := g.tc.c_type(raw_typ)
g.write('(${ct}){')
mut set_fields := map[string]bool{}
mut has_field := g.gen_struct_default_fields(typ.name, mut set_fields, false)
mut sname := g.tc.qualify_name(typ.name)
if typ.name in g.tc.structs {
sname = typ.name
}
if sname in g.tc.structs {
for f in g.tc.structs[sname] {
if f.name in set_fields {
continue
}
if f.typ is types.Map {
if has_field {
g.write(', ')
}
g.write('.${c_name(f.name)} = ')
g.write_new_map(f.typ.key_type, f.typ.value_type)
has_field = true
} else if f.typ is types.Array {
c_elem := g.tc.c_type(f.typ.elem_type)
if has_field {
g.write(', ')
}
g.write('.${c_name(f.name)} = array_new(sizeof(${c_elem}), 0, 0)')
has_field = true
} else if g.field_needs_default_init(f.typ) {
if has_field {
g.write(', ')
}
g.write('.${c_name(f.name)} = ')
g.gen_default_value_for_type(f.typ)
has_field = true
}
}
}
g.write('}')
return
}
ct := g.tc.c_type(typ)
if g.is_scalar_c_type(ct) {
g.write(g.scalar_zero_init(ct))
return
}
g.write('(${ct}){0}')
}
// field_needs_default_init reports whether an unset field of type `typ` must be
// explicitly default-initialized in a struct literal — i.e. it is a by-value
// struct whose type carries field defaults that C's `{0}` would not apply
// (e.g. `min_len int = 999999`).
fn (mut g FlatGen) field_needs_default_init(typ types.Type) bool {
if typ is types.Struct && !typ.name.starts_with('C.') {
return g.struct_has_field_defaults(typ.name)
}
return false
}
// struct_has_field_defaults reports whether building `type_name` as a struct
// literal would set any non-zero field: a field with an explicit default
// (`x int = 5`), or a by-value struct field whose own type has such defaults.
// Returns false for structs with interface/sum-typed field defaults, since the
// codegen default path cannot box those values.
fn (mut g FlatGen) struct_has_field_defaults(type_name string) bool {
mut visited := map[string]bool{}
return g.struct_has_field_defaults_inner(type_name, mut visited)
}
fn (mut g FlatGen) struct_has_field_defaults_inner(type_name string, mut visited map[string]bool) bool {
if type_name in visited {
return false
}
visited[type_name] = true
info := g.find_struct_decl(type_name) or { return false }
old_module := g.tc.cur_module
g.tc.cur_module = info.module
defer {
g.tc.cur_module = old_module
}
mut found := false
for i in 0 .. info.node.children_count {
field := g.a.child_node(&info.node, i)
if field.kind != .field_decl {
continue
}
ftyp := g.tc.parse_type(field.typ)
if field.children_count > 0 {
// Defaults for interface/sum-typed fields require boxing the value into
// the interface/sum representation, which the codegen default path cannot
// do. Treat the whole struct as unsafe to default-emit (leave it
// zero-initialized, as before) rather than emit an unboxed value.
if ftyp is types.SumType || ftyp is types.Interface {
return false
}
found = true
}
if ftyp is types.Struct && !ftyp.name.starts_with('C.') {
if g.struct_has_field_defaults_inner(ftyp.name, mut visited) {
found = true
}
}
}
return found
}
// gen_params_struct_arg emits a struct literal for a `@[params]` argument passed as
// trailing `key: value` call args (e.g. `atof64(s, allow_extra_chars: true)`).
// `node` is the call node; field_init children are read from `field_start` onward.
fn (mut g FlatGen) gen_params_struct_arg(typ types.Type, node flat.Node, field_start int) {
raw_typ := typ
if typ is types.Struct {
ct := g.tc.c_type(raw_typ)
g.write('(${ct}){')
mut set_fields := map[string]bool{}
mut has_field := false
for i in field_start .. node.children_count {
field := g.a.child_node(&node, i)
if field.kind != .field_init || field.children_count == 0 {
continue
}
if has_field {
g.write(', ')
}
g.write('.${c_name(field.value)} = ')
if ftyp := g.struct_field_type(typ.name, field.value) {
g.gen_expr_with_expected_type(g.a.child(field, 0), ftyp)
} else {
g.gen_expr(g.a.child(field, 0))
}
set_fields[field.value] = true
has_field = true
}
mut sname := g.tc.qualify_name(typ.name)
if typ.name in g.tc.structs {
sname = typ.name
}
has_field = g.gen_struct_default_fields(typ.name, mut set_fields, has_field)
if sname in g.tc.structs {
for f in g.tc.structs[sname] {
if f.name in set_fields {
continue
}
if f.typ is types.Map {
if has_field {
g.write(', ')
}
g.write('.${c_name(f.name)} = ')
g.write_new_map(f.typ.key_type, f.typ.value_type)
has_field = true
} else if f.typ is types.Array {
c_elem := g.tc.c_type(f.typ.elem_type)
if has_field {
g.write(', ')
}
g.write('.${c_name(f.name)} = array_new(sizeof(${c_elem}), 0, 0)')
has_field = true
} else if g.field_needs_default_init(f.typ) {
if has_field {
g.write(', ')
}
g.write('.${c_name(f.name)} = ')
g.gen_default_value_for_type(f.typ)
has_field = true
}
}
}
g.write('}')
return
}
g.gen_default_value_for_type(typ)
}
fn (g &FlatGen) is_scalar_zero_init_type(type_name string, c_type string) bool {
if type_name in g.tc.structs || g.tc.qualify_name(type_name) in g.tc.structs {
return false
}
if _ := g.find_struct_decl(type_name) {
return false
}
return g.is_scalar_c_type(c_type)
}
fn (g &FlatGen) is_scalar_c_type(c_type string) bool {
if c_type.ends_with('*') {
return true
}
return c_type in ['bool', 'char', 'byte', 'u8', 'i8', 'u16', 'i16', 'u32', 'i32', 'u64', 'i64',
'int', 'isize', 'usize', 'size_t', 'ptrdiff_t', 'float', 'double', 'voidptr']
}
fn (g &FlatGen) scalar_zero_init(c_type string) string {
if c_type in ['float', 'double'] {
return '0.0'
}
return '0'
}
struct StructDeclInfo {
node flat.Node
module string
full_name string
}
fn (mut g FlatGen) struct_init_c_type_name(type_name string) string {
typ := g.tc.parse_type(type_name)
if typ is types.OptionType || typ is types.ResultType {
return g.optional_type_name(typ)
}
info := g.find_struct_decl(type_name) or { return g.tc.c_type(g.tc.parse_type(type_name)) }
if info.full_name.starts_with('C.') {
return g.tc.c_type(g.tc.parse_type(info.full_name))
}
return c_name(info.full_name)
}
fn (g &FlatGen) find_struct_decl(type_name string) ?StructDeclInfo {
short_name := if type_name.contains('.') { type_name.all_after_last('.') } else { type_name }
preferred_name := if !type_name.contains('.') && g.tc.cur_module.len > 0
&& g.tc.cur_module != 'main' && g.tc.cur_module != 'builtin' {
'${g.tc.cur_module}.${type_name}'
} else {
type_name
}
if info := g.struct_decl_infos[preferred_name] {
if info.node.value == short_name {
return info
}
}
if type_name.contains('.') {
if info := g.struct_decl_infos[type_name] {
return info
}
} else {
if info := g.struct_decl_short_infos[type_name] {
return info
}
}
return none
}
fn (g &FlatGen) struct_field_type(type_name string, field_name string) ?types.Type {
qname := g.tc.qualify_name(type_name)
if fields := g.tc.structs[qname] {
for f in fields {
if f.name == field_name {
return f.typ
}
}
}
if fields := g.tc.structs[type_name] {
for f in fields {
if f.name == field_name {
return f.typ
}
}
}
if info := g.find_struct_decl(type_name) {
if fields := g.tc.structs[info.full_name] {
for f in fields {
if f.name == field_name {
return f.typ
}
}
}
}
return none
}
fn (g &FlatGen) struct_field_at(type_name string, index int) ?types.StructField {
if index < 0 {
return none
}
qname := g.tc.qualify_name(type_name)
if fields := g.tc.structs[qname] {
if index < fields.len {
return fields[index]
}
}
if fields := g.tc.structs[type_name] {
if index < fields.len {
return fields[index]
}
}
if info := g.find_struct_decl(type_name) {
if fields := g.tc.structs[info.full_name] {
if index < fields.len {
return fields[index]
}
}
}
return none
}
fn (g &FlatGen) struct_field_type_at(type_name string, index int) ?types.Type {
if field := g.struct_field_at(type_name, index) {
return field.typ
}
return none
}
fn (mut g FlatGen) gen_return_assoc(node flat.Node) {
ct := g.tc.c_type(g.tc.parse_type(node.value))
tmp := g.tmp_name()
g.write('${ct} ${tmp} = ')
g.gen_expr(g.a.child(&node, 0))
g.writeln(';')
for i in 1 .. node.children_count {
field := g.a.child_node(&node, i)
if field.kind == .field_init && field.children_count > 0 {
g.write('${tmp}.${c_name(field.value)} = ')
if ftyp := g.struct_field_type(node.value, field.value) {
g.gen_expr_with_expected_type(g.a.child(field, 0), ftyp)
} else {
g.gen_expr(g.a.child(field, 0))
}
g.writeln(';')
}
}
g.writeln('return ${tmp};')
}
fn (mut g FlatGen) gen_assoc_expr(node flat.Node) {
ct := g.tc.c_type(g.tc.parse_type(node.value))
tmp := g.tmp_name()
g.write('({${ct} ${tmp} = ')
g.gen_expr(g.a.child(&node, 0))
g.write(';')
for i in 1 .. node.children_count {
field := g.a.child_node(&node, i)
if field.kind == .field_init && field.children_count > 0 {
g.write(' ${tmp}.${c_name(field.value)} = ')
if ftyp := g.struct_field_type(node.value, field.value) {
g.gen_expr_with_expected_type(g.a.child(field, 0), ftyp)
} else {
g.gen_expr(g.a.child(field, 0))
}
g.write(';')
}
}
g.write(' ${tmp};})')
}
fn (mut g FlatGen) gen_heap_assoc_expr(node flat.Node) {
ct := g.tc.c_type(g.tc.parse_type(node.value))
tmp := g.tmp_name()
g.write('({${ct} ${tmp} = ')
g.gen_expr(g.a.child(&node, 0))
g.write(';')
for i in 1 .. node.children_count {
field := g.a.child_node(&node, i)
if field.kind == .field_init && field.children_count > 0 {
g.write(' ${tmp}.${c_name(field.value)} = ')
if ftyp := g.struct_field_type(node.value, field.value) {
g.gen_expr_with_expected_type(g.a.child(field, 0), ftyp)
} else {
g.gen_expr(g.a.child(field, 0))
}
g.write(';')
}
}
g.write(' (${ct}*)memdup(&${tmp}, sizeof(${ct}));})')
}
fn (mut g FlatGen) gen_map_init(id flat.NodeId, node flat.Node) {
if node.value.len > 0 {
map_type := g.tc.parse_type(node.value)
if map_type is types.Map {
g.write_new_map(map_type.key_type, map_type.value_type)
return
}
}
if node.typ.len > 0 {
map_type := g.tc.parse_type(node.typ)
if map_type is types.Map {
g.write_new_map(map_type.key_type, map_type.value_type)
return
}
}
if g.expected_expr_type is types.Map {
g.write_new_map(g.expected_expr_type.key_type, g.expected_expr_type.value_type)
return
}
resolved_type := g.tc.resolve_type(id)
if resolved_type is types.Map {
g.write_new_map(resolved_type.key_type, resolved_type.value_type)
return
}
g.write('new_map(sizeof(int), sizeof(int), 0, 0, 0, 0)')
}
fn (mut g FlatGen) write_new_map(key_type types.Type, value_type types.Type) {
c_key := g.tc.c_type(key_type)
c_val := g.tc.c_type(value_type)
hash_fn, eq_fn, clone_fn, free_fn := g.map_callback_names(key_type)
g.write('new_map(sizeof(${c_key}), sizeof(${c_val}), ${hash_fn}, ${eq_fn}, ${clone_fn}, ${free_fn})')
}
fn (g &FlatGen) map_callback_names(key_type types.Type) (string, string, string, string) {
if key_type is types.String {
return 'v3_map_hash_string', 'v3_map_eq_string', 'v3_map_clone_string', 'v3_map_free_string'
}
c_key := g.tc.c_type(key_type)
size_suffix := match c_key {
'u8', 'i8', 'bool', 'char' { '1' }
'u16', 'i16' { '2' }
'i64', 'u64', 'isize', 'usize', 'voidptr' { '8' }
else { '4' }
}
return 'v3_map_hash_int_${size_suffix}', 'v3_map_eq_int_${size_suffix}', 'v3_map_clone_int_${size_suffix}', 'v3_map_free_nop'
}
fn (g &FlatGen) skip_builtin_struct(name string) bool {
_ = g
if name.starts_with('C.') {
return true
}
return false
}
fn (mut g FlatGen) emit_interface_struct(name string) {
cn := c_name(name)
g.writeln('struct ${cn} {')
g.writeln('\tint _typ;')
if cn == 'IError' {
g.writeln('\tvoid* _object;')
g.writeln('\tstring message;')
g.writeln('\tint code;')
} else {
// pointer to the boxed concrete value, used by method dispatch
g.writeln('\tvoid* _object;')
}
for field in g.tc.interface_fields[name] or { []types.StructField{} } {
ct := g.tc.c_type(field.typ)
g.writeln('\t${ct} ${c_name(field.name)};')
}
g.writeln('};')
g.writeln('')
}
fn (mut g FlatGen) struct_decls() {
for name, _ in g.tc.structs {
if g.skip_builtin_struct(name) {
continue
}
tag := if name in g.tc.unions { 'union' } else { 'struct' }
g.writeln('typedef ${tag} ${c_name(name)} ${c_name(name)};')
}
for name, variants in g.tc.sum_types {
g.writeln('typedef struct ${c_name(name)} ${c_name(name)};')
_ = variants
}
for name, _ in g.interfaces {
g.writeln('typedef struct ${c_name(name)} ${c_name(name)};')
}
if g.has_builtins {
g.writeln('typedef array Array;')
}
mut emitted := map[string]bool{}
mut remaining := map[string]bool{}
mut remaining_cnames := map[string]bool{}
mut iface_remaining := map[string]bool{}
for name, _ in g.interfaces {
iface_remaining[name] = true
remaining_cnames[c_name(name)] = true
}
for name, _ in g.tc.structs {
if g.skip_builtin_struct(name) {
continue
}
remaining[name] = true
remaining_cnames[c_name(name)] = true
}
mut sum_remaining := map[string]bool{}
for name, _ in g.tc.sum_types {
sum_remaining[name] = true
remaining_cnames[c_name(name)] = true
}
if 'string' in remaining {
g.emit_struct('string')
emitted['string'] = true
remaining.delete('string')
remaining_cnames.delete('string')
}
mut has_ierror := false
for name, _ in iface_remaining {
if c_name(name) == 'IError' {
g.emit_interface_struct(name)
emitted['IError'] = true
iface_remaining.delete(name)
remaining_cnames.delete('IError')
has_ierror = true
break
}
}
err_field := if has_ierror { 'IError err; ' } else { '' }
g.writeln('typedef struct Optional { bool ok; ${err_field}int value; } Optional;')
g.writeln('')
for _ in 0 .. 30 {
if remaining.len == 0 && iface_remaining.len == 0 && sum_remaining.len == 0 {
break
}
mut progress := false
mut emitted_ifaces := []string{}
for name, _ in iface_remaining {
cn := c_name(name)
mut can_emit := true
if cn == 'IError' {
if 'string' !in emitted && 'string' in remaining_cnames {
can_emit = false
}
}
// An interface struct embeds its declared data fields by value, so the
// field types must be fully defined first (same constraint as structs).
for field in g.tc.interface_fields[name] or { []types.StructField{} } {
if field.typ is types.Pointer {
continue
}
fct := g.tc.c_type(field.typ)
if fct !in emitted && fct != cn && fct in remaining_cnames {
can_emit = false
break
}
}
if can_emit {
g.emit_interface_struct(name)
emitted[cn] = true
remaining_cnames.delete(cn)
emitted_ifaces << name
progress = true
}
}
for name in emitted_ifaces {
iface_remaining.delete(name)
}
mut emitted_structs := []string{}
for name, _ in remaining {
cn := c_name(name)
if cn in emitted {
remaining_cnames.delete(cn)
emitted_structs << name
progress = true
continue
}
mut can_emit := true
if name in g.tc.structs {
for f in g.tc.structs[name] {
if f.typ is types.Pointer {
continue
}
mut ct := ''
if f.typ is types.ArrayFixed {
ct = g.tc.c_type(f.typ.elem_type)
} else if f.typ is types.OptionType {
ct = g.tc.c_type(f.typ.base_type)
} else if f.typ is types.ResultType {
ct = g.tc.c_type(f.typ.base_type)
} else {
ct = g.tc.c_type(f.typ)
}
if ct !in emitted && ct != cn && ct in remaining_cnames {
can_emit = false
break
}
}
}
if can_emit {
if fields := g.tc.structs[name] {
g.emit_struct_option_typedefs(fields)
}
g.emit_struct(name)
emitted[cn] = true
remaining_cnames.delete(cn)
emitted_structs << name
progress = true
}
}
for name in emitted_structs {
remaining.delete(name)
}
mut emitted_sums := []string{}
for name, _ in sum_remaining {
cn := c_name(name)
mut can_emit_sum := true
if name in g.tc.sum_types {
for v in g.tc.sum_types[name] {
if g.variant_references_sum(v, name) {
continue
}
vt := g.tc.parse_type(v)
if vt is types.SumType {
if vt.name in sum_remaining {
can_emit_sum = false
break
}
}
vct := g.tc.c_type(vt)
if vct !in emitted && vct in remaining_cnames {
can_emit_sum = false
break
}
}
}
if can_emit_sum {
g.emit_sum_type(name)
emitted[cn] = true
remaining_cnames.delete(cn)
emitted_sums << name
progress = true
}
}
for name in emitted_sums {
sum_remaining.delete(name)
}
if !progress {
break
}
}
for name, _ in iface_remaining {
cn := c_name(name)
g.emit_interface_struct(name)
emitted[cn] = true
}
for name, _ in sum_remaining {
g.emit_sum_type(name)
}
for name, _ in remaining {
if fields := g.tc.structs[name] {
g.emit_struct_option_typedefs(fields)
}
g.emit_struct(name)
}
}
fn (mut g FlatGen) type_forward_decls() {
for name, _ in g.tc.structs {
if g.skip_builtin_struct(name) {
continue
}
tag := if name in g.tc.unions { 'union' } else { 'struct' }
g.writeln('typedef ${tag} ${c_name(name)} ${c_name(name)};')
}
for name, _ in g.tc.sum_types {
g.writeln('typedef struct ${c_name(name)} ${c_name(name)};')
}
for name, _ in g.interfaces {
g.writeln('typedef struct ${c_name(name)} ${c_name(name)};')
}
if g.has_builtins {
g.writeln('typedef array Array;')
}
g.writeln('')
}
fn (mut g FlatGen) emit_struct(name string) {
if name in g.tc.structs {
fields := g.tc.structs[name]
tag := if name in g.tc.unions { 'union' } else { 'struct' }
g.writeln('${tag} ${c_name(name)} {')
if fields.len == 0 {
g.writeln('\tint _dummy;')
}
for f in fields {
g.write_struct_field(name, f)
}
g.writeln('};')
g.writeln('')
}
}
fn (g &FlatGen) is_generic_struct(name string) bool {
if info := g.struct_decl_infos[name] {
return info.node.typ.contains('generic')
}
short_name := if name.contains('.') { name.all_after_last('.') } else { name }
if info := g.struct_decl_short_infos[short_name] {
return info.full_name == name && info.node.typ.contains('generic')
}
return false
}
fn (mut g FlatGen) write_struct_field(_struct_name string, f types.StructField) {
mut field_type := f.typ
if f.typ is types.Alias {
field_type = f.typ.base_type
}
raw_field_type := field_type
if field_type is types.FnType {
ct := g.resolve_fn_ptr_type(g.tc.c_type(raw_field_type))
g.writeln('\t${ct} ${c_name(f.name)};')
} else if f.typ is types.ArrayFixed {
c_elem := g.tc.c_type(f.typ.elem_type)
len_expr := g.fixed_array_len_value(f.typ)
g.writeln('\t${c_elem} ${c_name(f.name)}[${len_expr}];')
} else {
mut ct := if f.typ is types.OptionType || f.typ is types.ResultType {
g.optional_type_name(f.typ)
} else {
g.tc.c_type(f.typ)
}
if ct.starts_with('fn_ptr:') {
ct = g.resolve_fn_ptr_type(ct)
}
g.writeln('\t${ct} ${c_name(f.name)};')
}
}
fn (mut g FlatGen) preseed_struct_fn_ptr_types() {
for _, fields in g.tc.structs {
for f in fields {
mut field_type := f.typ
if f.typ is types.Alias {
field_type = f.typ.base_type
}
raw_field_type := field_type
if field_type is types.FnType {
ct := g.tc.c_type(raw_field_type)
g.resolve_fn_ptr_type(ct)
}
}
}
}
fn (mut g FlatGen) emit_struct_option_typedefs(fields []types.StructField) {
mut wrote := false
for f in fields {
if f.typ is types.OptionType || f.typ is types.ResultType {
opt_name := g.optional_type_name(f.typ)
if opt_name == 'Optional' {
continue
}
if val_type := g.needed_optional_types[opt_name] {
if g.emit_optional_typedef(opt_name, val_type) {
wrote = true
}
}
}
}
if wrote {
g.writeln('')
}
}