From 130d189fce29acf8693c0e2a708a2a0e199c88e7 Mon Sep 17 00:00:00 2001 From: Tim Basel Date: Fri, 17 Dec 2021 14:32:31 +0100 Subject: [PATCH] cgen: fix overwriting methods of embedded structs + empty struct for interfaces (#12876) --- vlib/v/gen/c/cgen.v | 13 ++-- .../v/tests/interface_struct_embedding_test.v | 67 +++++++++++++++++++ 2 files changed, 75 insertions(+), 5 deletions(-) create mode 100644 vlib/v/tests/interface_struct_embedding_test.v diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index cfef4a0357..9c154ec623 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -6034,8 +6034,13 @@ fn (mut g Gen) struct_init(node ast.StructInit) { typ: embed fields: init_fields_to_embed } + inside_cast_in_heap := g.inside_cast_in_heap + g.inside_cast_in_heap = 0 // prevent use of pointers in child structs + g.write('.$embed_name = ') g.struct_init(default_init) + + g.inside_cast_in_heap = inside_cast_in_heap // restore value for further struct inits if is_multiline { g.writeln(',') } else { @@ -7262,9 +7267,7 @@ fn (mut g Gen) interface_table() string { // i.e. cctype is always just Cat, not Cat_ptr: cctype := g.cc_type(st, true) $if debug_interface_table ? { - eprintln( - '>> interface name: $isym.name | concrete type: $st.debug() | st symname: ' + - st_sym.name) + eprintln('>> interface name: $isym.name | concrete type: $st.debug() | st symname: $st_sym.name') } // Speaker_Cat_index = 0 interface_index_name := '_${interface_name}_${cctype}_index' @@ -7323,6 +7326,7 @@ static inline $interface_name I_${cctype}_to_Interface_${interface_name}($cctype } } mut methods := st_sym.methods + method_names := methods.map(it.name) match st_sym.info { ast.Struct, ast.Interface, ast.SumType { if st_sym.info.parent_type.has_flag(.generic) { @@ -7341,7 +7345,6 @@ static inline $interface_name I_${cctype}_to_Interface_${interface_name}($cctype if st_sym.info is ast.Struct { for embed in st_sym.info.embeds { embed_sym := g.table.get_type_symbol(embed) - method_names := methods.map(it.name) for embed_method in embed_sym.methods { if embed_method.name !in method_names { methods << embed_method @@ -7396,7 +7399,7 @@ static inline $interface_name I_${cctype}_to_Interface_${interface_name}($cctype _, embed_types := g.table.find_method_from_embeds(st_sym, method.name) or { ast.Fn{}, []ast.Type{} } - if embed_types.len > 0 { + if embed_types.len > 0 && method.name !in method_names { 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]}') diff --git a/vlib/v/tests/interface_struct_embedding_test.v b/vlib/v/tests/interface_struct_embedding_test.v new file mode 100644 index 0000000000..ac1f544d92 --- /dev/null +++ b/vlib/v/tests/interface_struct_embedding_test.v @@ -0,0 +1,67 @@ +module main + +interface Getter { + get() string +} + +struct Struct { + msg string +} + +fn (s Struct) get() string { + return s.msg +} + +struct EmbeddingStruct { + Struct +} + +fn (s EmbeddingStruct) get() string { + return 'embedded: $s.msg' +} + +fn test_struct_embedding() { + s1 := EmbeddingStruct{ + msg: '1' + } + getter1 := Getter(s1) + s2 := &EmbeddingStruct{ + msg: '2' + } + getter2 := Getter(s2) + getter3 := Getter(EmbeddingStruct{ + msg: '3' + }) + getter4 := Getter(&EmbeddingStruct{ + msg: '4' + }) + + assert getter1.get() == 'embedded: 1' + assert getter2.get() == 'embedded: 2' + assert getter3.get() == 'embedded: 3' + assert getter4.get() == 'embedded: 4' +} + +struct EmptyStruct {} + +fn (s EmptyStruct) get() string { + return 'empty' +} + +struct EmbeddingEmptyStruct { + EmptyStruct +} + +fn test_empty_struct_embedding() { + s1 := EmbeddingEmptyStruct{} + getter1 := Getter(s1) + s2 := &EmbeddingEmptyStruct{} + getter2 := Getter(s2) + getter3 := Getter(EmbeddingEmptyStruct{}) + getter4 := Getter(&EmbeddingEmptyStruct{}) + + assert getter1.get() == 'empty' + assert getter2.get() == 'empty' + assert getter3.get() == 'empty' + assert getter4.get() == 'empty' +}