From 2b53992c01ccbaa2bec71dfd007e3971c65bf4e1 Mon Sep 17 00:00:00 2001 From: spaceface Date: Mon, 1 Mar 2021 21:47:00 +0100 Subject: [PATCH] checker, cgen: allow implementing an interface with an embedded struct (#9042) --- vlib/v/checker/checker.v | 2 +- vlib/v/gen/c/cgen.v | 19 ++++++++++++++++--- vlib/v/table/table.v | 26 ++++++++++++++++++++++++++ vlib/v/tests/interface_test.v | 25 +++++++++++++++++++++++++ 4 files changed, 68 insertions(+), 4 deletions(-) diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 4f7694865e..12a6b6f0f6 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -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) diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index c01a2d6aa8..ce46b9b716 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -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) - cast_struct.writeln('\t\t.$cname = ($field_styp*)((char*)x + __offsetof($cctype, $cname)),') + 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 { diff --git a/vlib/v/table/table.v b/vlib/v/table/table.v index 5e2b1f025b..cc794ed979 100644 --- a/vlib/v/table/table.v +++ b/vlib/v/table/table.v @@ -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] diff --git a/vlib/v/tests/interface_test.v b/vlib/v/tests/interface_test.v index c85c549d09..18adc6489d 100644 --- a/vlib/v/tests/interface_test.v +++ b/vlib/v/tests/interface_test.v @@ -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 +}