checker, cgen: allow implementing an interface with an embedded struct (#9042)

pull/9053/head
spaceface 2021-03-01 21:47:00 +01:00 committed by GitHub
parent 65900e55e3
commit 2b53992c01
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 68 additions and 4 deletions

View File

@ -2060,7 +2060,7 @@ fn (mut c Checker) type_implements(typ table.Type, inter_typ table.Type, pos tok
}
if mut inter_sym.info is table.Interface {
for ifield in inter_sym.info.fields {
if field := typ_sym.find_field(ifield.name) {
if field := c.table.find_field_with_embeds(typ_sym, ifield.name) {
if ifield.typ != field.typ {
exp := c.table.type_to_str(ifield.typ)
got := c.table.type_to_str(field.typ)

View File

@ -6164,13 +6164,14 @@ fn (mut g Gen) interface_table() string {
iinidx_minimum_base := 1000 // NB: NOT 0, to avoid map entries set to 0 later, so `if already_generated_mwrappers[name] > 0 {` works.
mut current_iinidx := iinidx_minimum_base
for st in inter_info.types {
st_sym := g.table.get_type_symbol(st)
// cctype is the Cleaned Concrete Type name, *without ptr*,
// i.e. cctype is always just Cat, not Cat_ptr:
cctype := g.cc_type(st, true)
$if debug_interface_table ? {
eprintln(
'>> interface name: $ityp.name | concrete type: $st.debug() | st symname: ' +
g.table.get_type_symbol(st).name)
st_sym.name)
}
// Speaker_Cat_index = 0
interface_index_name := '_${interface_name}_${cctype}_index'
@ -6189,7 +6190,20 @@ fn (mut g Gen) interface_table() string {
for field in inter_info.fields {
cname := c_name(field.name)
field_styp := g.typ(field.typ)
if _ := st_sym.find_field(field.name) {
cast_struct.writeln('\t\t.$cname = ($field_styp*)((char*)x + __offsetof($cctype, $cname)),')
} else {
// the field is embedded in another struct
cast_struct.write_string('\t\t.$cname = ($field_styp*)((char*)x')
for embed_type in st_sym.struct_info().embeds {
embed_sym := g.table.get_type_symbol(embed_type)
if _ := embed_sym.find_field(field.name) {
cast_struct.write_string(' + __offsetof($cctype, $embed_sym.embed_name()) + __offsetof($embed_sym.cname, $cname)')
break
}
}
cast_struct.writeln('),')
}
}
cast_struct.write_string('\t}')
cast_struct_str := cast_struct.str()
@ -6208,7 +6222,6 @@ $staticprefix $interface_name* I_${cctype}_to_Interface_${interface_name}_ptr($c
if g.pref.build_mode != .build_module {
methods_struct.writeln('\t{')
}
st_sym := g.table.get_type_symbol(st)
mut method := table.Fn{}
for _, m in ityp.methods {
for mm in st_sym.methods {

View File

@ -296,6 +296,32 @@ pub fn (t &Table) find_field(s &TypeSymbol, name string) ?Field {
return none
}
// search for a given field, looking through embedded fields
pub fn (t &Table) find_field_with_embeds(sym &TypeSymbol, field_name string) ?Field {
if f := t.find_field(sym, field_name) {
return f
} else {
// look for embedded field
if sym.info is Struct {
mut found_fields := []Field{}
mut embed_of_found_fields := []Type{}
for embed in sym.info.embeds {
embed_sym := t.get_type_symbol(embed)
if f := t.find_field(embed_sym, field_name) {
found_fields << f
embed_of_found_fields << embed
}
}
if found_fields.len == 1 {
return found_fields[0]
} else if found_fields.len > 1 {
return error('ambiguous field `$field_name`')
}
}
return err
}
}
[inline]
pub fn (t &Table) find_type_idx(name string) int {
return t.type_idxs[name]

View File

@ -305,3 +305,28 @@ fn animal_match(a Animal) {
}
}
*/
interface II {
mut:
my_field int
}
struct AA {
BB
}
struct BB {
pad [10]byte
mut:
my_field int
}
fn main() {
mut aa := AA{}
mut ii := II(aa)
assert ii.my_field == 0
aa.my_field = 123
assert ii.my_field == 123
ii.my_field = 1234
assert aa.my_field == 1234
}