Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

parser,ast,cgen: support nested unions with field union {, to improve c interoperability (similar to field struct {) #23539

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
}
}
Loading