From 4d1eb5f2156b279b47b49316a84612199a9b025a Mon Sep 17 00:00:00 2001 From: shove Date: Sat, 9 Dec 2023 00:54:55 +0800 Subject: [PATCH] ast, checker, cgen: fix generics interface signature methods call with embeded (fix #20113) (#20124) --- vlib/v/ast/types.v | 21 +++++++++++++-- vlib/v/checker/fn.v | 8 ++++++ vlib/v/checker/interface.v | 4 +-- vlib/v/gen/c/cgen.v | 6 +++-- vlib/v/tests/interface_embedding_call_test.v | 27 +++++++++++++++++++- 5 files changed, 59 insertions(+), 7 deletions(-) diff --git a/vlib/v/ast/types.v b/vlib/v/ast/types.v index c87f071b87159f..1641130ef27ccb 100644 --- a/vlib/v/ast/types.v +++ b/vlib/v/ast/types.v @@ -1756,10 +1756,27 @@ pub fn (s &SumType) find_field(name string) ?StructField { } pub fn (i Interface) defines_method(name string) bool { - for mut method in unsafe { i.methods } { - if method.name == name { + if i.methods.any(it.name == name) { + return true + } + if i.parent_type.has_flag(.generic) { + parent_sym := global_table.sym(i.parent_type) + parent_info := parent_sym.info as Interface + if parent_info.methods.any(it.name == name) { return true } } return false } + +pub fn (i Interface) get_methods() []string { + if i.methods.len > 0 { + return i.methods.map(it.name) + } + if i.parent_type.has_flag(.generic) { + parent_sym := global_table.sym(i.parent_type) + parent_info := parent_sym.info as Interface + return parent_info.methods.map(it.name) + } + return [] +} diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index 99cfe7b971b05e..c66c98df670b41 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -1849,6 +1849,10 @@ fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type { method = m has_method = true is_generic = true + if left_sym.kind == .interface_ && m.from_embeded_type != 0 { + is_method_from_embed = true + node.from_embed_types = [m.from_embeded_type] + } } } } @@ -2346,6 +2350,10 @@ fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type { } else { node.receiver_type = method.params[0].typ } + if left_sym.kind == .interface_ && is_method_from_embed && method.return_type.has_flag(.generic) + && method.generic_names.len == 0 { + method.generic_names = c.table.get_generic_names((rec_sym.info as ast.Interface).generic_types) + } if method.generic_names.len != node.concrete_types.len { // no type arguments given in call, attempt implicit instantiation c.infer_fn_generic_types(method, mut node) diff --git a/vlib/v/checker/interface.v b/vlib/v/checker/interface.v index b6aadb306bc009..1d45171b1cad31 100644 --- a/vlib/v/checker/interface.v +++ b/vlib/v/checker/interface.v @@ -74,7 +74,7 @@ fn (mut c Checker) interface_decl(mut node ast.InterfaceDecl) { if embed_decl := c.table.interfaces[embed.typ] { for f in embed_decl.fields { if f.name in efnames { - // already existing method name, check for conflicts + // already existing field name, check for conflicts ifield := node.fields[efnames[f.name]] if field := c.table.find_field_with_embeds(isym, f.name) { if ifield.typ != field.typ { @@ -91,7 +91,7 @@ fn (mut c Checker) interface_decl(mut node ast.InterfaceDecl) { } for m in embed_decl.methods { if m.name in emnames { - // already existing field name, check for conflicts + // already existing method name, check for conflicts imethod := node.methods[emnames[m.name]] if em_fn := decl_sym.find_method(imethod.name) { if m_fn := isym.find_method(m.name) { diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 558f566a1fcb3a..b433465f759f84 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -6772,8 +6772,10 @@ fn (mut g Gen) interface_table() string { methods_struct_name := 'struct _${interface_name}_interface_methods' mut methods_struct_def := strings.new_builder(100) methods_struct_def.writeln('${methods_struct_name} {') + inter_methods := inter_info.get_methods() mut methodidx := map[string]int{} - for k, method in inter_info.methods { + for k, method_name in inter_methods { + method := isym.find_method_with_generic_parent(method_name) or { continue } methodidx[method.name] = k ret_styp := g.typ(method.return_type) methods_struct_def.write_string('\t${ret_styp} (*_method_${c_fn_name(method.name)})(void* _') @@ -7078,7 +7080,7 @@ static inline __shared__${interface_name} ${shared_fn_name}(__shared__${cctype}* } // add line return after interface index declarations sb.writeln('') - if inter_info.methods.len > 0 { + if inter_methods.len > 0 { sb.writeln(methods_wrapper.str()) sb.writeln(methods_struct_def.str()) sb.writeln(methods_struct.str()) diff --git a/vlib/v/tests/interface_embedding_call_test.v b/vlib/v/tests/interface_embedding_call_test.v index 8d919ef98efb0c..b4a7208d9d4750 100644 --- a/vlib/v/tests/interface_embedding_call_test.v +++ b/vlib/v/tests/interface_embedding_call_test.v @@ -23,7 +23,7 @@ interface Greeter { greet() string } -// for issue 16496 +// for issue 16496, test the own methods. interface Foo { a_method() } @@ -50,3 +50,28 @@ fn test_embedding_method_call_cgen() { bar := Bar(Derived{}) assert bar.bar_method() == 0 } + +// for issue 20113, test call the method signatures +interface IFoo[T] { + method(arg T) T +} + +interface IBar[T] { + IFoo +} + +interface IBaz[T] { + IBar +} + +struct DerivedStruct[T] { +} + +fn (d DerivedStruct[T]) method[T](arg T) T { + return arg +} + +fn main() { + a := IFoo[int](DerivedStruct[int]{}) + assert a.method(1) == 1 +}