From 0367f343b0598b117ae8fb6ad50b8f209bd50c5e Mon Sep 17 00:00:00 2001 From: GGRei Date: Sun, 25 Feb 2024 17:47:27 +0100 Subject: [PATCH] ast,checker,cgen: fix generics function with embedded structs, ensure correct link generation in cgen (#20900) --- vlib/v/ast/ast.v | 19 ++++---- vlib/v/checker/checker.v | 1 + vlib/v/gen/c/cgen.v | 48 ++++++++++++++----- ...nerics_method_with_embedded_structs_test.v | 46 ++++++++++++++++++ 4 files changed, 93 insertions(+), 21 deletions(-) create mode 100644 vlib/v/tests/generics_method_with_embedded_structs_test.v diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 82c2d33e17..d31cf7274b 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -282,15 +282,16 @@ pub: mut_pos token.Pos 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` - or_block OrExpr - gkind_field GenericKindField // `T.name` => ast.GenericKindField.name, `T.typ` => ast.GenericKindField.typ, or .unknown - scope &Scope = unsafe { nil } - from_embed_types []Type // holds the type of the embed that the method is called from - has_hidden_receiver bool + 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` + or_block OrExpr + gkind_field GenericKindField // `T.name` => ast.GenericKindField.name, `T.typ` => ast.GenericKindField.typ, or .unknown + scope &Scope = unsafe { nil } + from_embed_types []Type // holds the type of the embed that the method is called from + generic_from_embed_types [][]Type // holds the types of the embeds for each generic instance when the same generic method is called. + has_hidden_receiver bool } // root_ident returns the origin ident where the selector started. diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 1230b61415..d354d0263d 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -1583,6 +1583,7 @@ fn (mut c Checker) selector_expr(mut node ast.SelectorExpr) ast.Type { ast.StructField{}, []ast.Type{} } node.from_embed_types = embed_types + node.generic_from_embed_types << embed_types } } } diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index a6e103ce16..27ca5faa34 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -3889,20 +3889,26 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) { } // struct embedding if sym.info in [ast.Struct, ast.Aggregate] { - for i, embed in node.from_embed_types { - embed_sym := g.table.sym(embed) - embed_name := embed_sym.embed_name() - is_left_ptr := if i == 0 { - node.expr_type.is_ptr() + if node.generic_from_embed_types.len > 0 && sym.info is ast.Struct { + if sym.info.embeds.len > 0 { + mut is_find := false + for arr_val in node.generic_from_embed_types { + if arr_val.len > 0 { + if arr_val[0] == sym.info.embeds[0] { + g.write_selector_expr_embed_name(node, arr_val) + is_find = true + break + } + } + } + if !is_find { + g.write_selector_expr_embed_name(node, node.from_embed_types) + } } else { - node.from_embed_types[i - 1].is_ptr() + g.write_selector_expr_embed_name(node, node.from_embed_types) } - if is_left_ptr { - g.write('->') - } else { - g.write('.') - } - g.write(embed_name) + } else { + g.write_selector_expr_embed_name(node, node.from_embed_types) } } alias_to_ptr := sym.info is ast.Alias && sym.info.parent_type.is_ptr() @@ -3928,6 +3934,24 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) { } } +fn (mut g Gen) write_selector_expr_embed_name(node ast.SelectorExpr, embed_types []ast.Type) { + for i, embed in embed_types { + embed_sym := g.table.sym(embed) + embed_name := embed_sym.embed_name() + is_left_ptr := if i == 0 { + node.expr_type.is_ptr() + } else { + embed_types[i - 1].is_ptr() + } + if is_left_ptr { + g.write('->') + } else { + g.write('.') + } + g.write(embed_name) + } +} + // check_var_scope checks if the variable has its value known from the node position @[inline] fn (mut g Gen) check_var_scope(obj ast.Var, node_pos int) bool { diff --git a/vlib/v/tests/generics_method_with_embedded_structs_test.v b/vlib/v/tests/generics_method_with_embedded_structs_test.v new file mode 100644 index 0000000000..4c4c6e4fac --- /dev/null +++ b/vlib/v/tests/generics_method_with_embedded_structs_test.v @@ -0,0 +1,46 @@ +struct Base { + i int +} + +struct Base1 { + Base +} + +struct Base2 { + Base +} + +struct Struct1 { + Base1 +} + +struct Struct1O { + Struct1 +} + +struct Struct2 { + Base2 +} + +struct Struct2O { + Struct2 +} + +fn (b &Base) func[T]() T { + t := T{ + i: b.i + } + if t.i == 0 { + } + return t +} + +fn test_generic_fn_with_embedded_structs() { + s1 := Struct1{} + s2 := Struct2{} + res1 := s1.func[Struct1O]() + res2 := s2.func[Struct2O]() + + assert res1.i == 0 + assert res2.i == 0 +}