diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index bee00752a0..c80cd32fbb 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -412,6 +412,18 @@ pub fn (t &Table) find_method_from_embeds(sym &TypeSymbol, method_name string) ? return none } +// find_method_with_embeds searches for a given method, also looking through embedded fields +pub fn (t &Table) find_method_with_embeds(sym &TypeSymbol, method_name string) ?Fn { + if func := t.find_method(sym, method_name) { + return func + } else { + // look for embedded field + first_err := err + func, _ := t.find_method_from_embeds(sym, method_name) or { return first_err } + return func + } +} + fn (t &Table) register_aggregate_field(mut sym TypeSymbol, name string) ?StructField { if sym.kind != .aggregate { t.panic('Unexpected type symbol: $sym.kind') diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 7cf904cccf..b1fde88516 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -3232,7 +3232,7 @@ fn (mut c Checker) type_implements(typ ast.Type, interface_type ast.Type, pos to if utyp != ast.voidptr_type { // Verify methods for imethod in imethods { - method := typ_sym.find_method(imethod.name) or { + method := c.table.find_method_with_embeds(typ_sym, imethod.name) or { typ_sym.find_method_with_generic_parent(imethod.name) or { c.error("`$styp` doesn't implement method `$imethod.name` of interface `$inter_sym.name`", pos) diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index c4c90c5e45..10c9f2062c 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -7310,6 +7310,12 @@ static inline $interface_name I_${cctype}_to_Interface_${interface_name}($cctype } else {} } + if st_sym.info is ast.Struct { + for embed in st_sym.info.embeds { + embed_sym := g.table.get_type_symbol(embed) + methods << embed_sym.methods + } + } for method in methods { mut name := method.name if inter_info.parent_type.has_flag(.generic) { @@ -7345,7 +7351,7 @@ static inline $interface_name I_${cctype}_to_Interface_${interface_name}($cctype // hack to mutate typ params[0] = ast.Param{ ...params[0] - typ: params[0].typ.set_nr_muls(1) + typ: st.set_nr_muls(1) } fargs, _, _ := g.fn_args(params, voidptr(0)) methods_wrapper.write_string(g.out.cut_last(g.out.len - params_start_pos)) @@ -7354,7 +7360,21 @@ static inline $interface_name I_${cctype}_to_Interface_${interface_name}($cctype if method.return_type != ast.void_type { methods_wrapper.write_string('return ') } - methods_wrapper.writeln('${method_call}(*${fargs.join(', ')});') + _, embed_types := g.table.find_method_from_embeds(st_sym, method.name) or { + ast.Fn{}, []ast.Type{} + } + if embed_types.len > 0 { + embed_sym := g.table.get_type_symbol(embed_types.last()) + method_name := '${embed_sym.cname}_$method.name' + methods_wrapper.write_string('${method_name}(${fargs[0]}') + for embed in embed_types { + esym := g.table.get_type_symbol(embed) + methods_wrapper.write_string('->$esym.embed_name()') + } + methods_wrapper.writeln('${fargs[1..].join(', ')});') + } else { + methods_wrapper.writeln('${method_call}(*${fargs.join(', ')});') + } methods_wrapper.writeln('}') // .speak = Cat_speak_Interface_Animal_method_wrapper method_call += iwpostfix diff --git a/vlib/v/tests/interface_method_with_struct_embed_test.v b/vlib/v/tests/interface_method_with_struct_embed_test.v new file mode 100644 index 0000000000..d18cc9aa44 --- /dev/null +++ b/vlib/v/tests/interface_method_with_struct_embed_test.v @@ -0,0 +1,35 @@ +struct Foo { + x int +} + +fn (f Foo) get() int { + return f.x +} + +struct Bar { + Foo +} + +interface Getter { + get() int +} + +fn test_interface_method_with_struct_embed() { + mut getter := []Getter{} + + foo := Foo{22} + getter << foo + + bar := Bar{Foo{11}} + getter << bar + + assert getter.len == 2 + + println(foo) + println(getter[0]) + assert getter[0].get() == 22 + + println(bar) + println(getter[1]) + assert getter[1].get() == 11 +}