ast, checker, cgen: fix interface method with struct embed (#12783)

pull/12787/head
yuyi 2021-12-10 20:56:13 +08:00 committed by GitHub
parent b116170735
commit 9b4329d2f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 70 additions and 3 deletions

View File

@ -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')

View File

@ -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)

View File

@ -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

View File

@ -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
}