From 1601de1b78a041d600bd8da66d1cb6eaef06d6ef Mon Sep 17 00:00:00 2001 From: Felipe Pena Date: Fri, 28 Feb 2025 17:02:42 -0300 Subject: [PATCH] v: fix anon struct option support (fix #23789) (fix #23824) (#23830) --- vlib/v/ast/ast.v | 1 + vlib/v/ast/str.v | 7 +++++- vlib/v/fmt/struct.v | 3 +++ vlib/v/gen/c/cgen.v | 2 +- vlib/v/gen/c/struct.v | 11 +++++---- vlib/v/parser/parse_type.v | 6 ++++- vlib/v/parser/struct.v | 2 ++ .../v/tests/options/option_anon_struct_test.v | 23 +++++++++++++++++++ .../options/option_none_to_anon_struct_test.v | 16 +++++++++++++ 9 files changed, 63 insertions(+), 8 deletions(-) create mode 100644 vlib/v/tests/options/option_anon_struct_test.v create mode 100644 vlib/v/tests/options/option_none_to_anon_struct_test.v diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 218076af0..5c793e0ee 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -431,6 +431,7 @@ pub: global_pos int = -1 // __global: module_pos int = -1 // module: is_union bool + is_option bool attrs []Attr pre_comments []Comment end_comments []Comment diff --git a/vlib/v/ast/str.v b/vlib/v/ast/str.v index 63e2ce828..2ff288bfb 100644 --- a/vlib/v/ast/str.v +++ b/vlib/v/ast/str.v @@ -186,7 +186,12 @@ fn (t &Table) stringify_fn_after_name(node &FnDecl, mut f strings.Builder, cur_m f.write_string(param.name) param_sym := t.sym(param.typ) if param_sym.info is Struct && param_sym.info.is_anon { - f.write_string(' struct {') + if param.typ.has_flag(.option) { + f.write_string(' ?') + } else { + f.write_string(' ') + } + f.write_string('struct {') for field in param_sym.info.fields { f.write_string(' ${field.name} ${t.type_to_str(field.typ)}') if field.has_default_expr { diff --git a/vlib/v/fmt/struct.v b/vlib/v/fmt/struct.v index db26de4cd..030f10b52 100644 --- a/vlib/v/fmt/struct.v +++ b/vlib/v/fmt/struct.v @@ -10,6 +10,9 @@ pub fn (mut f Fmt) struct_decl(node ast.StructDecl, is_anon bool) { if node.is_pub && !is_anon { f.write('pub ') } + if node.is_option { + f.write('?') + } if node.is_union { f.write('union') } else { diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index cd77a72a7..bb5d8a4ce 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -6504,7 +6504,7 @@ fn (mut g Gen) write_types(symbols []&ast.TypeSymbol) { } } } - g.struct_decl(sym.info, name, false) + g.struct_decl(sym.info, name, false, false) struct_names[name] = true } } diff --git a/vlib/v/gen/c/struct.v b/vlib/v/gen/c/struct.v index f1985463b..06a6f5242 100644 --- a/vlib/v/gen/c/struct.v +++ b/vlib/v/gen/c/struct.v @@ -77,7 +77,7 @@ fn (mut g Gen) struct_init(node ast.StructInit) { g.write(')') } } - if is_anon { + if is_anon && !node.typ.has_flag(.option) { if node.language == .v { g.write('(${styp})') } @@ -541,7 +541,7 @@ fn (mut g Gen) zero_struct_field(field ast.StructField) bool { return true } -fn (mut g Gen) struct_decl(s ast.Struct, name string, is_anon bool) { +fn (mut g Gen) struct_decl(s ast.Struct, name string, is_anon bool, is_option bool) { if s.is_generic { return } @@ -585,10 +585,11 @@ fn (mut g Gen) struct_decl(s ast.Struct, name string, is_anon bool) { } } if is_anon { + option_prefix := if is_option { '_option_' } else { '' } if s.is_shared { - g.type_definitions.write_string('\t__shared__${name}* ') + g.type_definitions.write_string('\t${option_prefix}__shared__${name}* ') } else { - g.type_definitions.write_string('\t${name} ') + g.type_definitions.write_string('\t${option_prefix}${name} ') } return } else if s.is_union { @@ -671,7 +672,7 @@ fn (mut g Gen) struct_decl(s ast.Struct, name string, is_anon bool) { if field_sym.info.is_anon { field_is_anon = true // Recursively generate code for this anon struct (this is the field's type) - g.struct_decl(field_sym.info, field_sym.cname, true) + g.struct_decl(field_sym.info, field_sym.cname, true, field.typ.has_flag(.option)) // Now the field's name g.type_definitions.writeln(' ${field_name}${size_suffix};') } diff --git a/vlib/v/parser/parse_type.v b/vlib/v/parser/parse_type.v index a1ddfb199..e17634d9c 100644 --- a/vlib/v/parser/parse_type.v +++ b/vlib/v/parser/parse_type.v @@ -525,7 +525,11 @@ fn (mut p Parser) parse_type() ast.Type { if p.tok.kind == .key_struct { p.anon_struct_decl = p.struct_decl(true) // Find the registered anon struct type, it was registered above in `p.struct_decl()` - return p.table.find_type_idx(p.anon_struct_decl.name) + mut typ := p.table.find_type_idx(p.anon_struct_decl.name) + if is_option { + typ = ast.new_type(typ).set_flag(.option) + } + return typ } language := p.parse_language() diff --git a/vlib/v/parser/struct.v b/vlib/v/parser/struct.v index abb368b0d..54ac81c05 100644 --- a/vlib/v/parser/struct.v +++ b/vlib/v/parser/struct.v @@ -14,6 +14,7 @@ fn (mut p Parser) struct_decl(is_anon bool) ast.StructDecl { start_pos := p.tok.pos() mut is_pub := p.tok.kind == .key_pub mut is_shared := p.tok.kind == .key_shared + is_option := is_anon && p.prev_tok.kind == .question if is_pub { p.next() } @@ -436,6 +437,7 @@ fn (mut p Parser) struct_decl(is_anon bool) ast.StructDecl { module_pos: module_pos language: language is_union: is_union + is_option: is_option attrs: if is_anon { []ast.Attr{} } else { attrs } // anon structs can't have attributes pre_comments: pre_comments end_comments: end_comments diff --git a/vlib/v/tests/options/option_anon_struct_test.v b/vlib/v/tests/options/option_anon_struct_test.v new file mode 100644 index 000000000..1d65b3545 --- /dev/null +++ b/vlib/v/tests/options/option_anon_struct_test.v @@ -0,0 +1,23 @@ +struct Foo { + a ?struct { + name string + } +} + +fn test_main() { + t := Foo{} + assert '${t}' == 'Foo{ + a: Option(none) +}' + + t2 := Foo{ + a: struct { + name: 'foo' + } + } + assert '${t2}' == "Foo{ + a: Option(struct { + name: 'foo' + }) +}" +} diff --git a/vlib/v/tests/options/option_none_to_anon_struct_test.v b/vlib/v/tests/options/option_none_to_anon_struct_test.v new file mode 100644 index 000000000..a6197ee72 --- /dev/null +++ b/vlib/v/tests/options/option_none_to_anon_struct_test.v @@ -0,0 +1,16 @@ +fn opt(params ?struct { name string surname string }) { + if params == none { + assert '${params}' == 'Option(none)' + } + if params != none { + assert '${params}' == "struct { + name: 'foo' + surname: 'bar' +}" + } +} + +fn test_main() { + opt(none) + opt(struct { name: 'foo', surname: 'bar' }) +} -- 2.39.5