Skip to content

Commit

Permalink
ast, checker, cgen: fix generics interface signature methods call wit…
Browse files Browse the repository at this point in the history
…h embeded (fix #20113) (#20124)
  • Loading branch information
shove70 authored Dec 8, 2023
1 parent 4687f8c commit 4d1eb5f
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 7 deletions.
21 changes: 19 additions & 2 deletions vlib/v/ast/types.v
Original file line number Diff line number Diff line change
Expand Up @@ -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 []
}
8 changes: 8 additions & 0 deletions vlib/v/checker/fn.v
Original file line number Diff line number Diff line change
Expand Up @@ -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]
}
}
}
}
Expand Down Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions vlib/v/checker/interface.v
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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) {
Expand Down
6 changes: 4 additions & 2 deletions vlib/v/gen/c/cgen.v
Original file line number Diff line number Diff line change
Expand Up @@ -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* _')
Expand Down Expand Up @@ -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())
Expand Down
27 changes: 26 additions & 1 deletion vlib/v/tests/interface_embedding_call_test.v
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ interface Greeter {
greet() string
}

// for issue 16496
// for issue 16496, test the own methods.
interface Foo {
a_method()
}
Expand All @@ -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
}

0 comments on commit 4d1eb5f

Please sign in to comment.