Skip to content

Commit

Permalink
parser,ast,cgen: support nested unions with field union {, to impro…
Browse files Browse the repository at this point in the history
…ve interoperability with C (similar to `field struct {`) (#23539)
  • Loading branch information
spytheman authored Jan 20, 2025
1 parent 907c089 commit eb1f52a
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 7 deletions.
11 changes: 9 additions & 2 deletions vlib/v/ast/table.v
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,11 @@ pub mut:
pointer_size int
// cache for type_to_str_using_aliases
cached_type_to_str map[u64]string
anon_struct_names map[string]int // anon struct name -> struct sym idx
// counter for anon struct, avoid name conflicts.
// counters and maps for anon structs and unions, to avoid name conflicts.
anon_struct_names map[string]int // anon struct name -> struct sym idx
anon_struct_counter int
anon_union_names map[string]int // anon union name -> union sym idx
anon_union_counter int
}

// used by vls to avoid leaks
Expand Down Expand Up @@ -912,6 +914,11 @@ pub fn (mut t Table) register_anon_struct(name string, sym_idx int) {
t.anon_struct_names[name] = sym_idx
}

@[inline]
pub fn (mut t Table) register_anon_union(name string, sym_idx int) {
t.anon_union_names[name] = sym_idx
}

pub fn (t &Table) known_type(name string) bool {
return t.type_idxs[name] != 0 || t.parsing_type == name || name in ['i32', 'byte']
}
Expand Down
8 changes: 8 additions & 0 deletions vlib/v/fmt/tests/union_with_nested_union_keep.vv
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
union MyNested1 {
a i32
b i32
nested_union union {
c f32
d char
}
}
4 changes: 4 additions & 0 deletions vlib/v/gen/c/cgen.v
Original file line number Diff line number Diff line change
Expand Up @@ -1413,6 +1413,10 @@ fn (mut g Gen) write_results() {
ck := c_name(k)
g.typedefs.writeln('typedef struct ${ck} ${ck};')
}
for k, _ in g.table.anon_union_names {
ck := c_name(k)
g.typedefs.writeln('typedef union ${ck} ${ck};')
}
}

fn (mut g Gen) find_or_register_shared(t ast.Type, base string) string {
Expand Down
22 changes: 17 additions & 5 deletions vlib/v/parser/struct.v
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,13 @@ fn (mut p Parser) struct_decl(is_anon bool) ast.StructDecl {
return ast.StructDecl{}
}
mut name := if is_anon {
p.table.anon_struct_counter++
'_VAnonStruct${p.table.anon_struct_counter}'
if is_union {
p.table.anon_union_counter++
'_VAnonUnion${p.table.anon_union_counter}'
} else {
p.table.anon_struct_counter++
'_VAnonStruct${p.table.anon_struct_counter}'
}
} else {
p.check_name()
}
Expand Down Expand Up @@ -241,8 +246,11 @@ fn (mut p Parser) struct_decl(is_anon bool) ast.StructDecl {
// struct field
field_name = p.check_name()
p.inside_struct_field_decl = true
if p.tok.kind == .key_struct
|| (p.tok.kind == .key_shared && p.peek_tok.kind == .key_struct) {
is_anon_struct := p.tok.kind == .key_struct
|| (p.tok.kind == .key_shared && p.peek_tok.kind == .key_struct)
is_anon_union := p.tok.kind == .key_union
|| (p.tok.kind == .key_shared && p.peek_tok.kind == .key_union)
if is_anon_struct || is_anon_union {
// Anon structs
field_is_shared := p.tok.kind == .key_shared
p.anon_struct_decl = p.struct_decl(true)
Expand Down Expand Up @@ -402,7 +410,11 @@ fn (mut p Parser) struct_decl(is_anon bool) ast.StructDecl {
}
mut ret := p.table.register_sym(sym)
if is_anon {
p.table.register_anon_struct(name, ret)
if is_union {
p.table.register_anon_union(name, ret)
} else {
p.table.register_anon_struct(name, ret)
}
}
// allow duplicate c struct declarations
if ret == -1 && language != .c {
Expand Down
28 changes: 28 additions & 0 deletions vlib/v/tests/unions/nested_union_test.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
union MyNested1 {
mut:
a i32
b i32
nested_union union {
mut:
c f32
d char
}
}

fn test_nested_unions() {
mut m := MyNested1{}
unsafe {
m.a = 12
assert m.b == 12
println(m.a)
m.b = -99
assert m.b == -99
println(m.b)
m.nested_union.c = 3.14
assert m.nested_union.c == 3.14
println(m.nested_union.c)
m.nested_union.d = 88
println(int(m.nested_union.d))
assert int(m.nested_union.d) == 88
}
}

0 comments on commit eb1f52a

Please sign in to comment.