From 212b4fa089a0f284e2348eb146bd0e4ce9e615d3 Mon Sep 17 00:00:00 2001 From: yuyi Date: Mon, 26 Apr 2021 15:56:08 +0800 Subject: [PATCH] parser: implement infering generic type parameters from receiver types (fix #5862) (#9870) --- vlib/v/ast/str.v | 26 ++++++++++++++----- vlib/v/parser/fn.v | 13 +++++----- ...ceiver_method_has_no_generic_names_err.out | 7 ----- ...eceiver_method_has_no_generic_names_err.vv | 13 ---------- ...ceiver_method_has_no_generic_names_err.out | 7 ----- ...eceiver_method_has_no_generic_names_err.vv | 12 --------- .../generics_call_with_reference_arg_test.v | 2 +- .../generics_method_on_receiver_types_test.v | 13 ++++++++++ .../tests/generics_with_embed_generics_test.v | 2 +- ...erics_with_generics_struct_receiver_test.v | 2 +- ...h_multiple_generics_struct_receiver_test.v | 2 +- 11 files changed, 43 insertions(+), 56 deletions(-) delete mode 100644 vlib/v/parser/tests/generic_multiple_receiver_method_has_no_generic_names_err.out delete mode 100644 vlib/v/parser/tests/generic_multiple_receiver_method_has_no_generic_names_err.vv delete mode 100644 vlib/v/parser/tests/generic_receiver_method_has_no_generic_names_err.out delete mode 100644 vlib/v/parser/tests/generic_receiver_method_has_no_generic_names_err.vv create mode 100644 vlib/v/tests/generics_method_on_receiver_types_test.v diff --git a/vlib/v/ast/str.v b/vlib/v/ast/str.v index 2f0bee7373..dfcfa7aff1 100644 --- a/vlib/v/ast/str.v +++ b/vlib/v/ast/str.v @@ -62,16 +62,28 @@ pub fn (node &FnDecl) stringify(t &Table, cur_mod string, m2a map[string]string) if name in ['+', '-', '*', '/', '%', '<', '>', '==', '!=', '>=', '<='] { f.write_string(' ') } + mut add_para_types := true if node.generic_names.len > 0 { - f.write_string('<') - for i, gname in node.generic_names { - is_last := i == node.generic_names.len - 1 - f.write_string(gname) - if !is_last { - f.write_string(', ') + if node.is_method { + sym := t.get_type_symbol(node.params[0].typ) + if sym.info is Struct { + generic_names := sym.info.generic_types.map(t.get_type_symbol(it).name) + if generic_names == node.generic_names { + add_para_types = false + } } } - f.write_string('>') + if add_para_types { + f.write_string('<') + for i, gname in node.generic_names { + is_last := i == node.generic_names.len - 1 + f.write_string(gname) + if !is_last { + f.write_string(', ') + } + } + f.write_string('>') + } } f.write_string('(') for i, arg in node.params { diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index 6843968c19..5bf6329dc5 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -273,12 +273,13 @@ fn (mut p Parser) fn_decl() ast.FnDecl { } } // - generic_names := p.parse_generic_names() - // check generic receiver method has no generic names - if is_method && rec.typ.has_flag(.generic) && generic_names.len == 0 - && p.table.get_type_symbol(rec.typ).kind != .any { - p.error_with_pos('generic receiver method `$name` should add generic names, e.g. $name', - name_pos) + mut generic_names := p.parse_generic_names() + // generic names can be infer with receiver's generic names + if is_method && rec.typ.has_flag(.generic) && generic_names.len == 0 { + sym := p.table.get_type_symbol(rec.typ) + if sym.info is ast.Struct { + generic_names = sym.info.generic_types.map(p.table.get_type_symbol(it).name) + } } // Args args2, are_args_type_only, is_variadic := p.fn_args() diff --git a/vlib/v/parser/tests/generic_multiple_receiver_method_has_no_generic_names_err.out b/vlib/v/parser/tests/generic_multiple_receiver_method_has_no_generic_names_err.out deleted file mode 100644 index 146771c8da..0000000000 --- a/vlib/v/parser/tests/generic_multiple_receiver_method_has_no_generic_names_err.out +++ /dev/null @@ -1,7 +0,0 @@ -vlib/v/parser/tests/generic_multiple_receiver_method_has_no_generic_names_err.vv:6:20: error: generic receiver method `poll` should add generic names, e.g. poll - 4 | } - 5 | - 6 | fn (q Queue) poll() A { - | ~~~~ - 7 | return q.buffer[0] - 8 | } diff --git a/vlib/v/parser/tests/generic_multiple_receiver_method_has_no_generic_names_err.vv b/vlib/v/parser/tests/generic_multiple_receiver_method_has_no_generic_names_err.vv deleted file mode 100644 index 29c0ef8edf..0000000000 --- a/vlib/v/parser/tests/generic_multiple_receiver_method_has_no_generic_names_err.vv +++ /dev/null @@ -1,13 +0,0 @@ -struct Queue{ - buffer []A - size B -} - -fn (q Queue) poll() A { - return q.buffer[0] -} - -fn main() { - q := Queue{} - println(q) -} diff --git a/vlib/v/parser/tests/generic_receiver_method_has_no_generic_names_err.out b/vlib/v/parser/tests/generic_receiver_method_has_no_generic_names_err.out deleted file mode 100644 index 3682d147f5..0000000000 --- a/vlib/v/parser/tests/generic_receiver_method_has_no_generic_names_err.out +++ /dev/null @@ -1,7 +0,0 @@ -vlib/v/parser/tests/generic_receiver_method_has_no_generic_names_err.vv:5:17: error: generic receiver method `poll` should add generic names, e.g. poll - 3 | } - 4 | - 5 | fn (q Queue) poll() T { - | ~~~~ - 6 | return q.buffer[0] - 7 | } diff --git a/vlib/v/parser/tests/generic_receiver_method_has_no_generic_names_err.vv b/vlib/v/parser/tests/generic_receiver_method_has_no_generic_names_err.vv deleted file mode 100644 index cb2eeb20f2..0000000000 --- a/vlib/v/parser/tests/generic_receiver_method_has_no_generic_names_err.vv +++ /dev/null @@ -1,12 +0,0 @@ -struct Queue{ - buffer []T -} - -fn (q Queue) poll() T { - return q.buffer[0] -} - -fn main() { - q := Queue{} - println(q) -} diff --git a/vlib/v/tests/generics_call_with_reference_arg_test.v b/vlib/v/tests/generics_call_with_reference_arg_test.v index 5ad195bef6..c80543b196 100644 --- a/vlib/v/tests/generics_call_with_reference_arg_test.v +++ b/vlib/v/tests/generics_call_with_reference_arg_test.v @@ -4,7 +4,7 @@ mut: buffer []&T } -fn (mut s MyStruct) add(e &T) bool { +fn (mut s MyStruct) add(e &T) bool { s.buffer[0] = e return true } diff --git a/vlib/v/tests/generics_method_on_receiver_types_test.v b/vlib/v/tests/generics_method_on_receiver_types_test.v new file mode 100644 index 0000000000..da0a1c17ec --- /dev/null +++ b/vlib/v/tests/generics_method_on_receiver_types_test.v @@ -0,0 +1,13 @@ +struct Abc { + value T +} + +fn (s Abc) get_value() T { + return s.value +} + +fn test_generics_method_on_receiver_types() { + s := Abc{'hello'} + println(s.get_value()) + assert s.get_value() == 'hello' +} diff --git a/vlib/v/tests/generics_with_embed_generics_test.v b/vlib/v/tests/generics_with_embed_generics_test.v index 2ddbed730e..3ccc0db162 100644 --- a/vlib/v/tests/generics_with_embed_generics_test.v +++ b/vlib/v/tests/generics_with_embed_generics_test.v @@ -18,7 +18,7 @@ fn group_new(val ...T) Group { return g } -fn (mut it Group) next() ?T { +fn (mut it Group) next() ?T { if it.index >= it.len { return none } diff --git a/vlib/v/tests/generics_with_generics_struct_receiver_test.v b/vlib/v/tests/generics_with_generics_struct_receiver_test.v index d3fe13920b..ddedd391a3 100644 --- a/vlib/v/tests/generics_with_generics_struct_receiver_test.v +++ b/vlib/v/tests/generics_with_generics_struct_receiver_test.v @@ -2,7 +2,7 @@ struct Num { num T } -fn (num Num) is_autom() bool { +fn (num Num) is_autom() bool { return true } 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 index 1307e2640d..1e8417e9dd 100644 --- a/vlib/v/tests/generics_with_multiple_generics_struct_receiver_test.v +++ b/vlib/v/tests/generics_with_multiple_generics_struct_receiver_test.v @@ -3,7 +3,7 @@ struct Foo { b B } -fn (num Foo) get_foo1() (A, B) { +fn (num Foo) get_foo1() (A, B) { return num.a, num.b }