From dd2002cc57d2029f2441ad52c3f10f3641bce278 Mon Sep 17 00:00:00 2001 From: yuyi Date: Fri, 23 Apr 2021 20:17:57 +0800 Subject: [PATCH] cgen: fix generics with multi generics struct receiver (#9853) --- vlib/v/ast/table.v | 2 +- vlib/v/ast/types.v | 1 + vlib/v/checker/check_types.v | 8 ++++-- vlib/v/checker/checker.v | 4 +-- vlib/v/gen/c/cgen.v | 4 +-- vlib/v/parser/struct.v | 1 + ...h_multiple_generics_struct_receiver_test.v | 28 +++++++++++++++++++ 7 files changed, 40 insertions(+), 8 deletions(-) create mode 100644 vlib/v/tests/generics_with_multiple_generics_struct_receiver_test.v diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index 17696d30f8..78fc2e9e54 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -1075,7 +1075,7 @@ pub fn (mut t Table) generic_struct_insts_to_concrete() { fields[i].typ = t_typ } } - parent_info.generic_types = [] + parent_info.is_generic = false parent_info.concrete_types = info.concrete_types.clone() parent_info.fields = fields parent_info.parent_type = new_type(info.parent_idx).set_flag(.generic) diff --git a/vlib/v/ast/types.v b/vlib/v/ast/types.v index a8081ee9d9..d3337094f0 100644 --- a/vlib/v/ast/types.v +++ b/vlib/v/ast/types.v @@ -722,6 +722,7 @@ pub mut: is_typedef bool // C. [typedef] is_union bool is_heap bool + is_generic bool generic_types []Type concrete_types []Type parent_type Type diff --git a/vlib/v/checker/check_types.v b/vlib/v/checker/check_types.v index cc63a40710..eb7aca18f8 100644 --- a/vlib/v/checker/check_types.v +++ b/vlib/v/checker/check_types.v @@ -486,13 +486,15 @@ pub fn (mut c Checker) infer_fn_generic_types(f ast.Fn, mut call_expr ast.CallEx mut typ := ast.void_type for i, param in f.params { mut to_set := ast.void_type - // resolve generic struct receiver (TODO: multi generic struct) + // resolve generic struct receiver if i == 0 && call_expr.is_method && param.typ.has_flag(.generic) { sym := c.table.get_type_symbol(call_expr.receiver_type) if sym.kind == .struct_ { info := sym.info as ast.Struct - if info.concrete_types.len > 0 { - typ = info.concrete_types[0] + receiver_generic_names := info.generic_types.map(c.table.get_type_symbol(it).name) + if gt_name in receiver_generic_names { + idx := receiver_generic_names.index(gt_name) + typ = info.concrete_types[idx] } } } diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index a2c6fcc636..7ac9fa1d00 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -1433,7 +1433,7 @@ fn (mut c Checker) check_map_and_filter(is_map bool, elem_typ ast.Type, call_exp fn (mut c Checker) check_return_generics_struct(return_type ast.Type, mut call_expr ast.CallExpr, generic_types []ast.Type) { rts := c.table.get_type_symbol(return_type) if rts.info is ast.Struct { - if rts.info.generic_types.len > 0 { + if rts.info.is_generic { mut nrt := '$rts.name<' mut c_nrt := '${rts.name}_T_' for i in 0 .. call_expr.generic_types.len { @@ -1462,7 +1462,7 @@ fn (mut c Checker) check_return_generics_struct(return_type ast.Type, mut call_e } } mut info := rts.info - info.generic_types = [] + info.is_generic = false info.concrete_types = generic_types.clone() info.parent_type = return_type info.fields = fields diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 90c37c7ba4..2d9cc57d17 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -693,7 +693,7 @@ fn (mut g Gen) cc_type(typ ast.Type, is_prefix_struct bool) string { mut styp := sym.cname // TODO: this needs to be removed; cgen shouldn't resolve generic types (job of checker) if mut sym.info is ast.Struct { - if sym.info.generic_types.len > 0 { + if sym.info.is_generic { mut sgtyps := '_T' for gt in sym.info.generic_types { gts := g.table.get_type_symbol(g.unwrap_generic(gt)) @@ -5438,7 +5438,7 @@ fn (mut g Gen) write_types(types []ast.TypeSymbol) { mut name := typ.cname match mut typ.info { ast.Struct { - if typ.info.generic_types.len > 0 { + if typ.info.is_generic { continue } if name.contains('_T_') { diff --git a/vlib/v/parser/struct.v b/vlib/v/parser/struct.v index 02939b3ad8..9d8081874b 100644 --- a/vlib/v/parser/struct.v +++ b/vlib/v/parser/struct.v @@ -299,6 +299,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl { is_typedef: attrs.contains('typedef') is_union: is_union is_heap: attrs.contains('heap') + is_generic: generic_types.len > 0 generic_types: generic_types attrs: attrs } diff --git a/vlib/v/tests/generics_with_multiple_generics_struct_receiver_test.v b/vlib/v/tests/generics_with_multiple_generics_struct_receiver_test.v new file mode 100644 index 0000000000..1307e2640d --- /dev/null +++ b/vlib/v/tests/generics_with_multiple_generics_struct_receiver_test.v @@ -0,0 +1,28 @@ +struct Foo { + a A + b B +} + +fn (num Foo) get_foo1() (A, B) { + return num.a, num.b +} + +fn (num Foo) get_foo2() (A, B) { + return num.a, num.b +} + +fn test_generics_with_multi_generics_struct_receiver() { + num := Foo{ + a: 3 + b: 'aaa' + } + a1, b1 := num.get_foo1() + println('$a1, $b1') + assert a1 == 3 + assert b1 == 'aaa' + + a2, b2 := num.get_foo2() + println('$a2, $b2') + assert a2 == 3 + assert b2 == 'aaa' +}