From bf6a2f80ef4d44384c4af7185f8168973e5bfbf2 Mon Sep 17 00:00:00 2001 From: yuyi Date: Sat, 10 Apr 2021 10:00:53 +0800 Subject: [PATCH] checker: fix generics with generic struct receiver (#9658) --- vlib/v/ast/table.v | 2 + vlib/v/ast/types.v | 14 ++++--- vlib/v/checker/check_types.v | 10 +++++ vlib/v/checker/checker.v | 41 +++++++++++-------- ...erics_with_generics_struct_receiver_test.v | 13 ++++++ 5 files changed, 58 insertions(+), 22 deletions(-) create mode 100644 vlib/v/tests/generics_with_generics_struct_receiver_test.v diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index 3211f74762..fd322f239f 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -1108,7 +1108,9 @@ pub fn (mut t Table) generic_struct_insts_to_concrete() { fields[i] = field } parent_info.generic_types = [] + parent_info.concrete_types = info.generic_types.clone() parent_info.fields = fields + parent_info.parent_type = new_type(info.parent_idx).set_flag(.generic) typ.is_public = true typ.kind = .struct_ typ.info = parent_info diff --git a/vlib/v/ast/types.v b/vlib/v/ast/types.v index 503076188b..a65906e19c 100644 --- a/vlib/v/ast/types.v +++ b/vlib/v/ast/types.v @@ -715,12 +715,14 @@ pub struct Struct { pub: attrs []Attr pub mut: - embeds []Type - fields []StructField - is_typedef bool // C. [typedef] - is_union bool - is_heap bool - generic_types []Type + embeds []Type + fields []StructField + is_typedef bool // C. [typedef] + is_union bool + is_heap bool + generic_types []Type + concrete_types []Type + parent_type Type } // instantiation of a generic struct diff --git a/vlib/v/checker/check_types.v b/vlib/v/checker/check_types.v index 4223fbdf24..5271c0df58 100644 --- a/vlib/v/checker/check_types.v +++ b/vlib/v/checker/check_types.v @@ -486,6 +486,16 @@ pub fn (mut c Checker) infer_fn_types(f ast.Fn, mut call_expr ast.CallExpr) { 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) + 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] + } + } + } arg_i := if i != 0 && call_expr.is_method { i - 1 } else { i } if call_expr.args.len <= arg_i { break diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 826ed65c70..41af16f87c 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -1464,7 +1464,7 @@ fn (mut c Checker) check_return_generics_struct(return_type ast.Type, mut call_e pub fn (mut c Checker) method_call(mut call_expr ast.CallExpr) ast.Type { left_type := c.expr(call_expr.left) c.expected_type = left_type - is_generic := left_type.has_flag(.generic) + mut is_generic := left_type.has_flag(.generic) call_expr.left_type = left_type // Set default values for .return_type & .receiver_type too, // or there will be hard to diagnose 0 type panics in cgen. @@ -1533,22 +1533,31 @@ pub fn (mut c Checker) method_call(mut call_expr ast.CallExpr) ast.Type { } else { // can this logic be moved to ast.type_find_method() so it can be used from anywhere if left_type_sym.info is ast.Struct { - mut found_methods := []ast.Fn{} - mut embed_of_found_methods := []ast.Type{} - for embed in left_type_sym.info.embeds { - embed_sym := c.table.get_type_symbol(embed) - if m := c.table.type_find_method(embed_sym, method_name) { - found_methods << m - embed_of_found_methods << embed + if left_type_sym.info.parent_type != 0 { + type_sym := c.table.get_type_symbol(left_type_sym.info.parent_type) + if m := c.table.type_find_method(type_sym, method_name) { + method = m + has_method = true + is_generic = true + } + } else { + mut found_methods := []ast.Fn{} + mut embed_of_found_methods := []ast.Type{} + for embed in left_type_sym.info.embeds { + embed_sym := c.table.get_type_symbol(embed) + if m := c.table.type_find_method(embed_sym, method_name) { + found_methods << m + embed_of_found_methods << embed + } + } + if found_methods.len == 1 { + method = found_methods[0] + has_method = true + is_method_from_embed = true + call_expr.from_embed_type = embed_of_found_methods[0] + } else if found_methods.len > 1 { + c.error('ambiguous method `$method_name`', call_expr.pos) } - } - if found_methods.len == 1 { - method = found_methods[0] - has_method = true - is_method_from_embed = true - call_expr.from_embed_type = embed_of_found_methods[0] - } else if found_methods.len > 1 { - c.error('ambiguous method `$method_name`', call_expr.pos) } } if left_type_sym.kind == .aggregate { diff --git a/vlib/v/tests/generics_with_generics_struct_receiver_test.v b/vlib/v/tests/generics_with_generics_struct_receiver_test.v new file mode 100644 index 0000000000..d3fe13920b --- /dev/null +++ b/vlib/v/tests/generics_with_generics_struct_receiver_test.v @@ -0,0 +1,13 @@ +struct Num { + num T +} + +fn (num Num) is_autom() bool { + return true +} + +fn test_generics_with_generic_struct_receiver() { + num := Num{3} + println(num.is_autom()) + assert num.is_autom() +}