From 2d43fdb42a452048051221096f7a2d6311ef8d77 Mon Sep 17 00:00:00 2001 From: yuyi Date: Sat, 4 Dec 2021 16:19:19 +0800 Subject: [PATCH] ast, checker, cgen: fix nested struct embed error (fix #12659) (#12712) --- cmd/tools/vast/vast.v | 2 +- vlib/v/ast/ast.v | 14 +++++++------- vlib/v/ast/table.v | 13 +++++++------ vlib/v/checker/checker.v | 16 ++++++++-------- vlib/v/gen/c/cgen.v | 6 +++--- vlib/v/tests/nested_struct_embed_test.v | 20 ++++++++++++++++++++ 6 files changed, 46 insertions(+), 25 deletions(-) create mode 100644 vlib/v/tests/nested_struct_embed_test.v diff --git a/cmd/tools/vast/vast.v b/cmd/tools/vast/vast.v index 53c1673b3a..3b8990da0f 100644 --- a/cmd/tools/vast/vast.v +++ b/cmd/tools/vast/vast.v @@ -1345,7 +1345,7 @@ fn (t Tree) selector_expr(node ast.SelectorExpr) &Node { obj.add('typ', t.type_node(node.typ)) obj.add('name_type', t.type_node(node.name_type)) obj.add('gkind_field', t.enum_node(node.gkind_field)) - obj.add('from_embed_type', t.type_node(node.from_embed_type)) + obj.add('from_embed_types', t.array_node_type(node.from_embed_types)) obj.add('next_token', t.token_node(node.next_token)) obj.add('pos', t.position(node.pos)) obj.add('scope', t.number_node(int(node.scope))) diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 904554535c..68f88a581d 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -223,13 +223,13 @@ pub: mut_pos token.Position next_token token.Kind pub mut: - expr Expr // expr.field_name - expr_type Type // type of `Foo` in `Foo.bar` - typ Type // type of the entire thing (`Foo.bar`) - name_type Type // T in `T.name` or typeof in `typeof(expr).name` - gkind_field GenericKindField // `T.name` => ast.GenericKindField.name, `T.typ` => ast.GenericKindField.typ, or .unknown - scope &Scope - from_embed_type Type // holds the type of the embed that the method is called from + expr Expr // expr.field_name + expr_type Type // type of `Foo` in `Foo.bar` + typ Type // type of the entire thing (`Foo.bar`) + name_type Type // T in `T.name` or typeof in `typeof(expr).name` + gkind_field GenericKindField // `T.name` => ast.GenericKindField.name, `T.typ` => ast.GenericKindField.typ, or .unknown + scope &Scope + from_embed_types []Type // holds the type of the embed that the method is called from } // root_ident returns the origin ident where the selector started. diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index c874aac27d..cf81119918 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -503,22 +503,23 @@ pub fn (t &Table) find_field(s &TypeSymbol, name string) ?StructField { pub fn (t &Table) find_field_from_embeds_recursive(sym &TypeSymbol, field_name string) ?(StructField, []Type) { if sym.info is Struct { mut found_fields := []StructField{} - mut embeds_of_found_fields := [][]Type{} + mut embeds_of_found_fields := []Type{} for embed in sym.info.embeds { embed_sym := t.get_type_symbol(embed) if field := t.find_field(embed_sym, field_name) { found_fields << field - embeds_of_found_fields << [embed] + embeds_of_found_fields << embed } else { field, types := t.find_field_from_embeds_recursive(embed_sym, field_name) or { - StructField{}, []Type{} + continue } found_fields << field + embeds_of_found_fields << embed embeds_of_found_fields << types } } if found_fields.len == 1 { - return found_fields[0], embeds_of_found_fields[0] + return found_fields[0], embeds_of_found_fields } else if found_fields.len > 1 { return error('ambiguous field `$field_name`') } @@ -526,7 +527,7 @@ pub fn (t &Table) find_field_from_embeds_recursive(sym &TypeSymbol, field_name s for typ in sym.info.types { agg_sym := t.get_type_symbol(typ) field, embed_types := t.find_field_from_embeds_recursive(agg_sym, field_name) or { - return err + continue } if embed_types.len > 0 { return field, embed_types @@ -578,7 +579,7 @@ pub fn (t &Table) find_field_with_embeds(sym &TypeSymbol, field_name string) ?St } else { // look for embedded field first_err := err - field, _ := t.find_field_from_embeds(sym, field_name) or { return first_err } + field, _ := t.find_field_from_embeds_recursive(sym, field_name) or { return first_err } return field } } diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 30d5e90cd3..4836a1de78 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -3460,15 +3460,15 @@ pub fn (mut c Checker) selector_expr(mut node ast.SelectorExpr) ast.Type { } else { // look for embedded field has_field = true - mut embed_type := ast.Type(0) - field, embed_type = c.table.find_field_from_embeds(sym, field_name) or { + mut embed_types := []ast.Type{} + field, embed_types = c.table.find_field_from_embeds_recursive(sym, field_name) or { if err.msg != '' { c.error(err.msg, node.pos) } has_field = false - ast.StructField{}, ast.Type(0) + ast.StructField{}, []ast.Type{} } - node.from_embed_type = embed_type + node.from_embed_types = embed_types if sym.kind in [.aggregate, .sum_type] { unknown_field_msg = err.msg } @@ -3489,15 +3489,15 @@ pub fn (mut c Checker) selector_expr(mut node ast.SelectorExpr) ast.Type { } else { // look for embedded field has_field = true - mut embed_type := ast.Type(0) - field, embed_type = c.table.find_field_from_embeds(gs, field_name) or { + mut embed_types := []ast.Type{} + field, embed_types = c.table.find_field_from_embeds_recursive(gs, field_name) or { if err.msg != '' { c.error(err.msg, node.pos) } has_field = false - ast.StructField{}, ast.Type(0) + ast.StructField{}, []ast.Type{} } - node.from_embed_type = embed_type + node.from_embed_types = embed_types } } } diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 2fccd3cc77..c340d45d75 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -4226,8 +4226,8 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) { } // struct embedding if sym.info in [ast.Struct, ast.Aggregate] { - if node.from_embed_type != 0 { - embed_sym := g.table.get_type_symbol(node.from_embed_type) + for embed in node.from_embed_types { + embed_sym := g.table.get_type_symbol(embed) embed_name := embed_sym.embed_name() if node.expr_type.is_ptr() { g.write('->') @@ -4237,7 +4237,7 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) { g.write(embed_name) } } - if (node.expr_type.is_ptr() || sym.kind == .chan) && node.from_embed_type == 0 { + if (node.expr_type.is_ptr() || sym.kind == .chan) && node.from_embed_types.len == 0 { g.write('->') } else { // g.write('. /*typ= $it.expr_type */') // ${g.typ(it.expr_type)} /') diff --git a/vlib/v/tests/nested_struct_embed_test.v b/vlib/v/tests/nested_struct_embed_test.v new file mode 100644 index 0000000000..917629ce25 --- /dev/null +++ b/vlib/v/tests/nested_struct_embed_test.v @@ -0,0 +1,20 @@ +struct Foo { +mut: + x int +} + +struct Bar { + Foo +} + +struct Baz { + Bar +} + +fn test_nested_struct_embed() { + mut baz := Baz{} + baz.x = 3 + + println(baz.x) + assert baz.x == 3 +}