From 4b818fa2beb82814b80adb254230897fd4390a28 Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Sun, 2 May 2021 03:00:47 +0300 Subject: [PATCH] v: implement interface embedding (#9935) --- vlib/v/ast/ast.v | 19 +- vlib/v/ast/table.v | 27 +- vlib/v/ast/types.v | 1 + vlib/v/checker/checker.v | 168 ++++++- .../interface_too_many_embedding_levels.out | 7 + .../interface_too_many_embedding_levels.vv | 431 ++++++++++++++++++ vlib/v/fmt/fmt.v | 5 + vlib/v/gen/c/cgen.v | 10 +- vlib/v/parser/struct.v | 26 +- vlib/v/pref/should_compile.v | 3 + .../interface_embedding_deep_nesting_test.v | 415 +++++++++++++++++ .../interface_embedding_recursive_test.v | 78 ++++ vlib/v/tests/interface_embedding_test.v | 56 +++ 13 files changed, 1217 insertions(+), 29 deletions(-) create mode 100644 vlib/v/checker/tests/interface_too_many_embedding_levels.out create mode 100644 vlib/v/checker/tests/interface_too_many_embedding_levels.vv create mode 100644 vlib/v/tests/interface_embedding_deep_nesting_test.v create mode 100644 vlib/v/tests/interface_embedding_recursive_test.v create mode 100644 vlib/v/tests/interface_embedding_test.v diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index d2b76192ed..8b57fcdc3a 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -260,18 +260,31 @@ pub: pos token.Position } +pub struct InterfaceEmbedding { +pub: + name string + typ Type + pos token.Position + comments []Comment +} + pub struct InterfaceDecl { pub: name string + typ Type name_pos token.Position language Language field_names []string is_pub bool - methods []FnDecl mut_pos int // mut: - fields []StructField pos token.Position pre_comments []Comment +pub mut: + methods []FnDecl + fields []StructField + // + ifaces []InterfaceEmbedding + are_ifaces_expanded bool } pub struct StructInitField { @@ -352,7 +365,6 @@ pub struct FnDecl { pub: name string mod string - params []Param is_deprecated bool is_pub bool is_variadic bool @@ -379,6 +391,7 @@ pub: attrs []Attr skip_gen bool // this function doesn't need to be generated (for example [if foo]) pub mut: + params []Param stmts []Stmt defer_stmts []DeferStmt return_type Type diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index beefe3d849..a470dbf737 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -19,6 +19,7 @@ pub mut: cflags []cflag.CFlag redefined_fns []string fn_generic_types map[string][][]Type // for generic functions + interfaces map[int]InterfaceDecl cmod_prefix string // needed for ast.type_to_str(Type) while vfmt; contains `os.` is_fmt bool used_fns map[string]bool // filled in by the checker, when pref.skip_unused = true; @@ -60,7 +61,6 @@ pub fn (t &Table) panic(message string) { pub struct Fn { pub: - params []Param return_type Type is_variadic bool language Language @@ -77,8 +77,12 @@ pub: mod string ctdefine string // compile time define. "myflag", when [if myflag] tag attrs []Attr + // + pos token.Position + return_type_pos token.Position pub mut: name string + params []Param source_fn voidptr // set in the checker, while processing fn declarations usages int } @@ -96,9 +100,24 @@ pub: name string is_mut bool is_auto_rec bool - typ Type type_pos token.Position is_hidden bool // interface first arg +pub mut: + typ Type +} + +pub fn (f Fn) new_method_with_receiver_type(new_type Type) Fn { + mut new_method := f + new_method.params = f.params.clone() + new_method.params[0].typ = new_type + return new_method +} + +pub fn (f FnDecl) new_method_with_receiver_type(new_type Type) FnDecl { + mut new_method := f + new_method.params = f.params.clone() + new_method.params[0].typ = new_type + return new_method } fn (p &Param) equals(o &Param) bool { @@ -213,6 +232,10 @@ pub fn (mut t Table) register_fn(new_fn Fn) { t.fns[new_fn.name] = new_fn } +pub fn (mut t Table) register_interface(idecl InterfaceDecl) { + t.interfaces[idecl.typ] = idecl +} + pub fn (mut t TypeSymbol) register_method(new_fn Fn) int { // returns a method index, stored in the ast.FnDecl // for faster lookup in the checker's fn_decl method diff --git a/vlib/v/ast/types.v b/vlib/v/ast/types.v index 6d958cd847..2cb9a5a849 100644 --- a/vlib/v/ast/types.v +++ b/vlib/v/ast/types.v @@ -734,6 +734,7 @@ pub mut: types []Type fields []StructField methods []Fn + ifaces []Type } pub struct Enum { diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index ebfc05a317..75bfc75367 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -360,25 +360,152 @@ pub fn (mut c Checker) sum_type_decl(node ast.SumTypeDecl) { } } -pub fn (mut c Checker) interface_decl(decl ast.InterfaceDecl) { - c.check_valid_pascal_case(decl.name, 'interface name', decl.pos) - for method in decl.methods { - if decl.language == .v { - c.check_valid_snake_case(method.name, 'method name', method.pos) - } - c.ensure_type_exists(method.return_type, method.return_type_pos) or { return } - for param in method.params { - c.ensure_type_exists(param.typ, param.pos) or { return } - } +pub fn (mut c Checker) expand_iface_embeds(idecl &ast.InterfaceDecl, level int, iface_embeds []ast.InterfaceEmbedding) []ast.InterfaceEmbedding { + // eprintln('> expand_iface_embeds: idecl.name: $idecl.name | level: $level | iface_embeds.len: $iface_embeds.len') + if level > 100 { + c.error('too many interface embedding levels: $level, for interface `$idecl.name`', + idecl.pos) + return [] } - for i, field in decl.fields { - if decl.language == .v { - c.check_valid_snake_case(field.name, 'field name', field.pos) + if iface_embeds.len == 0 { + return [] + } + mut res := map[int]ast.InterfaceEmbedding{} + mut ares := []ast.InterfaceEmbedding{} + for ie in iface_embeds { + if iface_decl := c.table.interfaces[ie.typ] { + mut list := iface_decl.ifaces + if !iface_decl.are_ifaces_expanded { + list = c.expand_iface_embeds(idecl, level + 1, iface_decl.ifaces) + c.table.interfaces[ie.typ].ifaces = list + c.table.interfaces[ie.typ].are_ifaces_expanded = true + } + for partial in list { + res[partial.typ] = partial + } } - c.ensure_type_exists(field.typ, field.pos) or { return } - for j in 0 .. i { - if field.name == decl.fields[j].name { - c.error('field name `$field.name` duplicate', field.pos) + res[ie.typ] = ie + } + for _, v in res { + ares << v + } + return ares +} + +pub fn (mut c Checker) interface_decl(mut decl ast.InterfaceDecl) { + c.check_valid_pascal_case(decl.name, 'interface name', decl.pos) + mut decl_sym := c.table.get_type_symbol(decl.typ) + if mut decl_sym.info is ast.Interface { + if decl.ifaces.len > 0 { + all_ifaces := c.expand_iface_embeds(decl, 0, decl.ifaces) + // eprintln('> decl.name: $decl.name | decl.ifaces.len: $decl.ifaces.len | all_ifaces: $all_ifaces.len') + decl.ifaces = all_ifaces + mut emnames := map[string]int{} + mut emnames_ds := map[string]bool{} + mut emnames_ds_info := map[string]bool{} + mut efnames := map[string]int{} + mut efnames_ds_info := map[string]bool{} + for i, m in decl.methods { + emnames[m.name] = i + emnames_ds[m.name] = true + emnames_ds_info[m.name] = true + } + for i, f in decl.fields { + efnames[f.name] = i + efnames_ds_info[f.name] = true + } + // + for iface in all_ifaces { + isym := c.table.get_type_symbol(iface.typ) + if isym.kind != .interface_ { + c.error('interface `$decl.name` tries to embed `$isym.name`, but `$isym.name` is not an interface, but `$isym.kind`', + iface.pos) + continue + } + for f in isym.info.fields { + if !efnames_ds_info[f.name] { + efnames_ds_info[f.name] = true + decl_sym.info.fields << f + } + } + for m in isym.info.methods { + if !emnames_ds_info[m.name] { + emnames_ds_info[m.name] = true + decl_sym.info.methods << m.new_method_with_receiver_type(decl.typ) + } + } + for m in isym.methods { + if !emnames_ds[m.name] { + emnames_ds[m.name] = true + decl_sym.methods << m.new_method_with_receiver_type(decl.typ) + } + } + if iface_decl := c.table.interfaces[iface.typ] { + for f in iface_decl.fields { + if f.name in efnames { + // already existing method name, check for conflicts + ifield := decl.fields[efnames[f.name]] + if field := c.table.find_field_with_embeds(isym, f.name) { + if ifield.typ != field.typ { + exp := c.table.type_to_str(ifield.typ) + got := c.table.type_to_str(field.typ) + c.error('embedded interface `$iface_decl.name` conflicts existing field: `$ifield.name`, expecting type: `$exp`, got type: `$got`', + ifield.pos) + } + } + } else { + efnames[f.name] = decl.fields.len + decl.fields << f + } + } + for m in iface_decl.methods { + if m.name in emnames { + // already existing field name, check for conflicts + imethod := decl.methods[emnames[m.name]] + if em_fn := decl_sym.find_method(imethod.name) { + if m_fn := isym.find_method(m.name) { + msg := c.table.is_same_method(m_fn, em_fn) + if msg.len > 0 { + em_sig := c.table.fn_signature(em_fn, skip_receiver: true) + m_sig := c.table.fn_signature(m_fn, skip_receiver: true) + c.error('embedded interface `$iface_decl.name` causes conflict: $msg, for interface method `$em_sig` vs `$m_sig`', + imethod.pos) + } + } + } + } else { + emnames[m.name] = decl.methods.len + mut new_method := m.new_method_with_receiver_type(decl.typ) + new_method.pos = iface.pos + decl.methods << new_method + } + } + } + } + } + for i, method in decl.methods { + if decl.language == .v { + c.check_valid_snake_case(method.name, 'method name', method.pos) + } + c.ensure_type_exists(method.return_type, method.return_type_pos) or { return } + for param in method.params { + c.ensure_type_exists(param.typ, param.pos) or { return } + } + for j in 0 .. i { + if method.name == decl.methods[j].name { + c.error('duplicate method name `$method.name`', method.pos) + } + } + } + for i, field in decl.fields { + if decl.language == .v { + c.check_valid_snake_case(field.name, 'field name', field.pos) + } + c.ensure_type_exists(field.typ, field.pos) or { return } + for j in 0 .. i { + if field.name == decl.fields[j].name { + c.error('field name `$field.name` duplicate', field.pos) + } } } } @@ -3660,7 +3787,7 @@ fn (mut c Checker) stmt(node ast.Stmt) { c.import_stmt(node) } ast.InterfaceDecl { - c.interface_decl(node) + c.interface_decl(mut node) } ast.Module { c.mod = node.name @@ -6319,6 +6446,11 @@ pub fn (mut c Checker) warn(s string, pos token.Position) { } pub fn (mut c Checker) error(message string, pos token.Position) { + $if checker_exit_on_first_error ? { + eprintln('\n\n>> checker error: $message, pos: $pos') + print_backtrace() + exit(1) + } if c.pref.translated && message.starts_with('mismatched types') { // TODO move this return diff --git a/vlib/v/checker/tests/interface_too_many_embedding_levels.out b/vlib/v/checker/tests/interface_too_many_embedding_levels.out new file mode 100644 index 0000000000..669fb012bd --- /dev/null +++ b/vlib/v/checker/tests/interface_too_many_embedding_levels.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/interface_too_many_embedding_levels.vv:9:1: error: too many interface embedding levels: 101, for interface `I103` + 7 | } + 8 | + 9 | interface I103 { + | ~~~~~~~~~~~~~~~~ + 10 | I102 + 11 | } diff --git a/vlib/v/checker/tests/interface_too_many_embedding_levels.vv b/vlib/v/checker/tests/interface_too_many_embedding_levels.vv new file mode 100644 index 0000000000..6975ef0652 --- /dev/null +++ b/vlib/v/checker/tests/interface_too_many_embedding_levels.vv @@ -0,0 +1,431 @@ +interface I1 { + I0 +} + +interface I2 { + I1 +} + +interface I103 { + I102 +} + +interface I102 { + I101 +} + +interface I101 { + I100 +} + +interface I3 { + I2 +} + +interface I4 { + I3 +} + +interface I5 { + I4 +} + +interface I6 { + I5 +} + +interface I7 { + I6 +} + +interface I8 { + I7 +} + +interface I9 { + I8 +} + +interface I10 { + I9 +} + +interface I11 { + I10 +} + +interface I12 { + I11 +} + +interface I13 { + I12 +} + +interface I14 { + I13 +} + +interface I15 { + I14 +} + +interface I16 { + I15 +} + +interface I17 { + I16 +} + +interface I18 { + I17 +} + +interface I19 { + I18 +} + +interface I20 { + I19 +} + +interface I21 { + I20 +} + +interface I22 { + I21 +} + +interface I23 { + I22 +} + +interface I24 { + I23 +} + +interface I25 { + I24 +} + +interface I26 { + I25 +} + +interface I27 { + I26 +} + +interface I28 { + I27 +} + +interface I29 { + I28 +} + +interface I30 { + I29 +} + +interface I31 { + I30 +} + +interface I32 { + I31 +} + +interface I33 { + I32 +} + +interface I34 { + I33 +} + +interface I35 { + I34 +} + +interface I36 { + I35 +} + +interface I37 { + I36 +} + +interface I38 { + I37 +} + +interface I39 { + I38 +} + +interface I40 { + I39 +} + +interface I41 { + I40 +} + +interface I42 { + I41 +} + +interface I43 { + I42 +} + +interface I44 { + I43 +} + +interface I45 { + I44 +} + +interface I46 { + I45 +} + +interface I47 { + I46 +} + +interface I48 { + I47 +} + +interface I49 { + I48 +} + +interface I50 { + I49 +} + +interface I51 { + I50 +} + +interface I52 { + I51 +} + +interface I53 { + I52 +} + +interface I54 { + I53 +} + +interface I55 { + I54 +} + +interface I56 { + I55 +} + +interface I57 { + I56 +} + +interface I58 { + I57 +} + +interface I59 { + I58 +} + +interface I60 { + I59 +} + +interface I61 { + I60 +} + +interface I62 { + I61 +} + +interface I63 { + I62 +} + +interface I64 { + I63 +} + +interface I65 { + I64 +} + +interface I66 { + I65 +} + +interface I67 { + I66 +} + +interface I68 { + I67 +} + +interface I69 { + I68 +} + +interface I70 { + I69 +} + +interface I71 { + I70 +} + +interface I72 { + I71 +} + +interface I73 { + I72 +} + +interface I74 { + I73 +} + +interface I75 { + I74 +} + +interface I76 { + I75 +} + +interface I77 { + I76 +} + +interface I78 { + I77 +} + +interface I79 { + I78 +} + +interface I80 { + I79 +} + +interface I81 { + I80 +} + +interface I82 { + I81 +} + +interface I83 { + I82 +} + +interface I84 { + I83 +} + +interface I85 { + I84 +} + +interface I86 { + I85 +} + +interface I87 { + I86 +} + +interface I88 { + I87 +} + +interface I89 { + I88 +} + +interface I90 { + I89 +} + +interface I91 { + I90 +} + +interface I92 { + I91 +} + +interface I93 { + I92 +} + +interface I94 { + I93 +} + +interface I95 { + I94 +} + +interface I96 { + I95 +} + +interface I97 { + I96 +} + +interface I98 { + I97 +} + +interface I99 { + I98 +} + +interface I100 { + I99 +} + +interface I0 { + m999() int +} + +struct Abc { + x int = 123 +} + +fn (s Abc) m999() int { + return 999 +} + +fn main() { + a := Abc{} + dump(a) + i := I103(a) + dump(i) + assert i.m999() == 999 +} diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index f064f77529..dc94e7fc41 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -1180,6 +1180,11 @@ pub fn (mut f Fmt) interface_decl(node ast.InterfaceDecl) { f.writeln('') } f.comments_after_last_field(node.pre_comments) + for iface in node.ifaces { + f.write('\t$iface.name') + f.comments(iface.comments, inline: true, has_nl: false, level: .indent) + f.writeln('') + } for i, field in node.fields { if i == node.mut_pos { f.writeln('mut:') diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index fcb9374f65..882849499d 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -6386,9 +6386,9 @@ $staticprefix inline $interface_name I_${cctype}_to_Interface_${interface_name}( // .speak = Cat_speak mut method_call := '${cctype}_$method.name' if !method.params[0].typ.is_ptr() { - // inline void Cat_speak_method_wrapper(Cat c) { return Cat_speak(*c); } - methods_wrapper.write_string('static inline ${g.typ(method.return_type)}') - methods_wrapper.write_string(' ${method_call}_method_wrapper(') + // inline void Cat_speak_Interface_Animal_method_wrapper(Cat c) { return Cat_speak(*c); } + iwpostfix := '_Interface_${interface_name}_method_wrapper' + methods_wrapper.write_string('static inline ${g.typ(method.return_type)} $method_call${iwpostfix}(') // params_start_pos := g.out.len mut params := method.params.clone() @@ -6406,8 +6406,8 @@ $staticprefix inline $interface_name I_${cctype}_to_Interface_${interface_name}( } methods_wrapper.writeln('${method_call}(*${fargs.join(', ')});') methods_wrapper.writeln('}') - // .speak = Cat_speak_method_wrapper - method_call += '_method_wrapper' + // .speak = Cat_speak_Interface_Animal_method_wrapper + method_call += iwpostfix } if g.pref.build_mode != .build_module { methods_struct.writeln('\t\t._method_${c_name(method.name)} = (void*) $method_call,') diff --git a/vlib/v/parser/struct.v b/vlib/v/parser/struct.v index 57d7a5247d..642b8bc8f9 100644 --- a/vlib/v/parser/struct.v +++ b/vlib/v/parser/struct.v @@ -483,7 +483,24 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl { mut methods := []ast.FnDecl{cap: 20} mut is_mut := false mut mut_pos := -1 + mut ifaces := []ast.InterfaceEmbedding{} for p.tok.kind != .rcbr && p.tok.kind != .eof { + if p.tok.kind == .name && p.tok.lit.len > 0 && p.tok.lit[0].is_capital() { + iface_pos := p.tok.position() + iface_name := p.tok.lit + iface_type := p.parse_type() + comments := p.eat_comments({}) + ifaces << ast.InterfaceEmbedding{ + name: iface_name + typ: iface_type + pos: iface_pos + comments: comments + } + if p.tok.kind == .rcbr { + break + } + continue + } if p.tok.kind == .key_mut { if is_mut { p.error_with_pos('redefinition of `mut` section', p.tok.position()) @@ -498,6 +515,7 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl { method_start_pos := p.tok.position() line_nr := p.tok.line_nr name := p.check_name() + if name == 'type_name' { p.error_with_pos('cannot override built-in method `type_name`', method_start_pos) return ast.InterfaceDecl{} @@ -545,6 +563,7 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl { tmethod := ast.Fn{ name: name params: args + pos: method.pos return_type: method.return_type is_variadic: is_variadic is_pub: true @@ -581,19 +600,24 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl { } } } + info.ifaces = ifaces.map(it.typ) ts.info = info p.top_level_statement_end() p.check(.rcbr) pos = pos.extend_with_last_line(p.prev_tok.position(), p.prev_tok.line_nr) - return ast.InterfaceDecl{ + res := ast.InterfaceDecl{ name: interface_name language: language + typ: typ fields: fields methods: methods + ifaces: ifaces is_pub: is_pub pos: pos pre_comments: pre_comments mut_pos: mut_pos name_pos: name_pos } + p.table.register_interface(res) + return res } diff --git a/vlib/v/pref/should_compile.v b/vlib/v/pref/should_compile.v index c9f5eb6932..9463582d0f 100644 --- a/vlib/v/pref/should_compile.v +++ b/vlib/v/pref/should_compile.v @@ -24,6 +24,9 @@ pub fn (prefs &Preferences) should_compile_filtered_files(dir string, files_ []s if prefs.backend != .js && !prefs.should_compile_asm(file) { continue } + if file.starts_with('.#') { + continue + } if file.contains('_d_') { if prefs.compile_defines_all.len == 0 { continue diff --git a/vlib/v/tests/interface_embedding_deep_nesting_test.v b/vlib/v/tests/interface_embedding_deep_nesting_test.v new file mode 100644 index 0000000000..66be70a270 --- /dev/null +++ b/vlib/v/tests/interface_embedding_deep_nesting_test.v @@ -0,0 +1,415 @@ +interface I99 { + I98 +} + +interface I1 { + I0 +} + +interface I2 { + I1 +} + +interface I3 { + I2 +} + +interface I4 { + I3 +} + +interface I5 { + I4 +} + +interface I6 { + I5 +} + +interface I7 { + I6 +} + +interface I8 { + I7 +} + +interface I9 { + I8 +} + +interface I10 { + I9 +} + +interface I11 { + I10 +} + +interface I12 { + I11 +} + +interface I13 { + I12 +} + +interface I14 { + I13 +} + +interface I15 { + I14 +} + +interface I16 { + I15 +} + +interface I17 { + I16 +} + +interface I18 { + I17 +} + +interface I19 { + I18 +} + +interface I20 { + I19 +} + +interface I21 { + I20 +} + +interface I22 { + I21 +} + +interface I23 { + I22 +} + +interface I24 { + I23 +} + +interface I25 { + I24 +} + +interface I26 { + I25 +} + +interface I27 { + I26 +} + +interface I28 { + I27 +} + +interface I29 { + I28 +} + +interface I30 { + I29 +} + +interface I31 { + I30 +} + +interface I32 { + I31 +} + +interface I33 { + I32 +} + +interface I34 { + I33 +} + +interface I35 { + I34 +} + +interface I36 { + I35 +} + +interface I37 { + I36 +} + +interface I38 { + I37 +} + +interface I39 { + I38 +} + +interface I40 { + I39 +} + +interface I41 { + I40 +} + +interface I42 { + I41 +} + +interface I43 { + I42 +} + +interface I44 { + I43 +} + +interface I45 { + I44 +} + +interface I46 { + I45 +} + +interface I47 { + I46 +} + +interface I48 { + I47 +} + +interface I49 { + I48 +} + +interface I50 { + I49 +} + +interface I51 { + I50 +} + +interface I52 { + I51 +} + +interface I53 { + I52 +} + +interface I54 { + I53 +} + +interface I55 { + I54 +} + +interface I56 { + I55 +} + +interface I57 { + I56 +} + +interface I58 { + I57 +} + +interface I59 { + I58 +} + +interface I60 { + I59 +} + +interface I61 { + I60 +} + +interface I62 { + I61 +} + +interface I63 { + I62 +} + +interface I64 { + I63 +} + +interface I65 { + I64 +} + +interface I66 { + I65 +} + +interface I67 { + I66 +} + +interface I68 { + I67 +} + +interface I69 { + I68 +} + +interface I70 { + I69 +} + +interface I71 { + I70 +} + +interface I72 { + I71 +} + +interface I73 { + I72 +} + +interface I74 { + I73 +} + +interface I75 { + I74 +} + +interface I76 { + I75 +} + +interface I77 { + I76 +} + +interface I78 { + I77 +} + +interface I79 { + I78 +} + +interface I80 { + I79 +} + +interface I81 { + I80 +} + +interface I82 { + I81 +} + +interface I83 { + I82 +} + +interface I84 { + I83 +} + +interface I85 { + I84 +} + +interface I86 { + I85 +} + +interface I87 { + I86 +} + +interface I88 { + I87 +} + +interface I89 { + I88 +} + +interface I90 { + I89 +} + +interface I91 { + I90 +} + +interface I92 { + I91 +} + +interface I93 { + I92 +} + +interface I94 { + I93 +} + +interface I95 { + I94 +} + +interface I96 { + I95 +} + +interface I97 { + I96 +} + +interface I98 { + I97 +} + +interface I0 { + m999() int +} + +struct Abc { + x int = 123 +} + +fn (s Abc) m999() int { + return 999 +} + +fn test_deep_nested_interface_embeddings() { + a := Abc{} + dump(a) + i := I99(a) + dump(i) + assert i.m999() == 999 +} diff --git a/vlib/v/tests/interface_embedding_recursive_test.v b/vlib/v/tests/interface_embedding_recursive_test.v new file mode 100644 index 0000000000..a0edb06684 --- /dev/null +++ b/vlib/v/tests/interface_embedding_recursive_test.v @@ -0,0 +1,78 @@ +// This test orders the interface definitions intentionally +// in such a way that interface `Re` is first, and `Fe` is +// last. The goal is testing that the embedding expansion +// works independently from the source order, and that both +// can be checked/compiled/used at the same time. +interface Re { + I1 + I2 + m_ie() int +} + +interface I1 { + I0 + m1() int +} + +interface I2 { + I0 + m2() int +} + +interface I0 { + m0() int +} + +interface Fe { + I1 + I2 + m_ie() int +} + +struct StructIE { + x int = 456 +} + +fn (s StructIE) m0() int { + println(@METHOD) + return 0 +} + +fn (s StructIE) m1() int { + println(@METHOD) + return 1 +} + +fn (s StructIE) m2() int { + println(@METHOD) + return 2 +} + +fn (s StructIE) m_ie() int { + println(@METHOD) + return 3 +} + +fn test_ie_recursive_forward() { + i := Fe(StructIE{}) + eprintln(i) + assert 0 == i.m0() + assert 1 == i.m1() + assert 2 == i.m2() + assert 3 == i.m_ie() + if i is StructIE { + assert i.x == 456 + } +} + +fn test_ie_recursive_backward() { + i := Re(StructIE{}) + eprintln(i) + assert 0 == i.m0() + assert 1 == i.m1() + assert 2 == i.m2() + assert 3 == i.m_ie() + if i is StructIE { + assert i.x == 456 + } +} diff --git a/vlib/v/tests/interface_embedding_test.v b/vlib/v/tests/interface_embedding_test.v new file mode 100644 index 0000000000..5f9052554e --- /dev/null +++ b/vlib/v/tests/interface_embedding_test.v @@ -0,0 +1,56 @@ +interface WalkerTalker { + // Abc + Walker // adsas + // zxczxc + Talker // xyzdef + // asdasdas + nspeeches int +} + +interface Talker { + nspeeches int + talk(msg string) +} + +interface Walker { + nsteps int + walk(newx int, newy int) +} + +struct Abc { +mut: + x int + y int + phrases []string + nsteps int = 1000 + nspeeches int = 1000 +} + +fn (mut s Abc) talk(msg string) { + s.phrases << msg + s.nspeeches++ +} + +fn (mut s Abc) walk(x int, y int) { + s.x = x + s.y = y + s.nsteps++ +} + +fn test_walker_talker() { + mut wt := WalkerTalker(Abc{ + x: 1 + y: 1 + phrases: ['hi'] + }) + wt.talk('my name is Wally') + wt.walk(100, 100) + if mut wt is Abc { + dump(wt) + assert wt.x == 100 + assert wt.y == 100 + assert wt.phrases.last().ends_with('Wally') + assert wt.nspeeches == 1001 + assert wt.nsteps == 1001 + } +}