From 4b3ce79e84ace1224c64b091d2379e2298a4a159 Mon Sep 17 00:00:00 2001 From: Enzo Baldisserri Date: Mon, 4 May 2020 00:14:59 +0200 Subject: [PATCH] interface: support arguments in methods and simplify --- vlib/v/gen/cgen.v | 188 ++++++++++++++++++++-------------- vlib/v/gen/fn.v | 54 ++++------ vlib/v/parser/struct.v | 5 +- vlib/v/table/atypes.v | 33 ++++-- vlib/v/table/table.v | 61 +++-------- vlib/v/tests/interface_test.v | 13 ++- 6 files changed, 183 insertions(+), 171 deletions(-) diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index c7cf86e984..33a18a3828 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -103,14 +103,6 @@ const ( ) pub fn cgen(files []ast.File, table &table.Table, pref &pref.Preferences) string { - if true { // if - x := 10 // line - // sep - y := 20 - _ = x - _ = y - } else { - } // println('start cgen2') mut g := Gen{ out: strings.new_builder(1000) @@ -291,8 +283,8 @@ pub fn (mut g Gen) write_typeof_functions() { } // V type to C type -pub fn (mut g Gen) typ(t table.Type) string { - mut styp := g.base_typ(t) +fn (mut g Gen) typ(t table.Type) string { + mut styp := g.base_type(t) if t.flag_is(.optional) { // Register an optional styp = 'Option_' + styp @@ -309,13 +301,20 @@ pub fn (mut g Gen) typ(t table.Type) string { return styp } -pub fn (mut g Gen) base_typ(t table.Type) string { +fn (g &Gen) base_type(t table.Type) string { + mut styp := g.cc_type(t) nr_muls := t.nr_muls() - sym := g.table.get_type_symbol(t) - mut styp := sym.name.replace('.', '__') if nr_muls > 0 { styp += strings.repeat(`*`, nr_muls) } + return styp +} + +// cc_type returns the Cleaned Concrete Type name, *without ptr*, +// i.e. it's always just Cat, not Cat_ptr: +fn (g &Gen) cc_type(t table.Type) string { + sym := g.table.get_type_symbol(t) + mut styp := sym.name.replace('.', '__') if styp.starts_with('C__') { styp = styp[3..] if sym.kind == .struct_ { @@ -330,6 +329,12 @@ pub fn (mut g Gen) base_typ(t table.Type) string { // pub fn (mut g Gen) write_typedef_types() { + g.typedefs.writeln(' +typedef struct { + void* _object; + int _interface_idx; +} _Interface; +') for typ in g.table.types { match typ.kind { .alias { @@ -342,6 +347,9 @@ pub fn (mut g Gen) write_typedef_types() { styp := typ.name.replace('.', '__') g.definitions.writeln('typedef array $styp;') } + .interface_ { + g.definitions.writeln('typedef _Interface ${c_name(typ.name)};') + } .map { styp := typ.name.replace('.', '__') g.definitions.writeln('typedef map $styp;') @@ -842,7 +850,7 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { } g.expr(ident) if is_optional { - mr_base_styp := g.base_typ(return_type) + mr_base_styp := g.base_type(return_type) g.writeln(' = (*(${mr_base_styp}*)${mr_var_name}.data).arg$i;') } else { g.writeln(' = ${mr_var_name}.arg$i;') @@ -1185,7 +1193,6 @@ fn (mut g Gen) expr(node ast.Expr) { } ast.SelectorExpr { g.expr(it.expr) - // if it.expr_type.nr_muls() > 0 { if it.expr_type.is_ptr() { g.write('->') } else { @@ -1650,7 +1657,7 @@ fn (mut g Gen) ident(node ast.Ident) { // `println(x)` => `println(*(int*)x.data)` if ident_var.is_optional && !(g.is_assign_lhs && g.right_is_opt) { g.write('/*opt*/') - styp := g.base_typ(ident_var.typ) + styp := g.base_type(ident_var.typ) g.write('(*($styp*)${name}.data)') return } @@ -1908,7 +1915,7 @@ fn (mut g Gen) return_statement(node ast.Return) { // mr_info := typ_sym.info as table.MultiReturn mut styp := '' if fn_return_is_optional { // && !node.types[0].flag_is(.optional) && node.types[0] != - styp = g.base_typ(g.fn_decl.return_type) + styp = g.base_type(g.fn_decl.return_type) g.write('opt_ok(&($styp/*X*/[]) { ') } else { styp = g.typ(g.fn_decl.return_type) @@ -1947,7 +1954,7 @@ fn (mut g Gen) return_statement(node ast.Return) { else {} } if !is_none && !is_error { - styp := g.base_typ(g.fn_decl.return_type) + styp := g.base_type(g.fn_decl.return_type) g.write('/*:)$return_sym.name*/opt_ok(&($styp[]) { ') if !g.fn_decl.return_type.is_ptr() && node.types[0].is_ptr() { // Automatic Dereference for optional @@ -2277,13 +2284,6 @@ fn (mut g Gen) write_types(types []table.TypeSymbol) { g.definitions.writeln('typedef $fixed $styp [$len];') // } } - table.Interface { - g.definitions.writeln('//interface') - g.definitions.writeln('typedef struct {') - g.definitions.writeln('\tvoid* _object;') - g.definitions.writeln('\tint _interface_idx;') - g.definitions.writeln('} $name;') - } else {} } } @@ -2608,7 +2608,7 @@ fn (mut g Gen) insert_before(s string) { // to access its fields (`.ok`, `.error` etc) // `os.cp(...)` => `Option bool tmp = os__cp(...); if (!tmp.ok) { ... }` fn (mut g Gen) or_block(var_name string, stmts []ast.Stmt, return_type table.Type) { - mr_styp := g.base_typ(return_type) + mr_styp := g.base_type(return_type) g.writeln(';') // or') g.writeln('if (!${var_name}.ok) {') g.writeln('\tstring err = ${var_name}.v_error;') @@ -3375,59 +3375,100 @@ fn (g Gen) type_to_fmt(typ table.Type) string { } // Generates interface table and interface indexes -// TODO remove all `replace()` -fn (v &Gen) interface_table() string { +fn (g &Gen) interface_table() string { mut sb := strings.new_builder(100) - for _, t in v.table.types { - if t.kind != .interface_ { + for ityp in g.table.types { + if ityp.kind != .interface_ { continue } - info := t.info as table.Interface + inter_info := ityp.info as table.Interface + if inter_info.types.len == 0 { + continue + } + sb.writeln('// NR interfaced types= $inter_info.types.len') // interface_name is for example Speaker - interface_name := t.name.replace('.', '__') - mut methods := '' - mut generated_casting_functions := '' - sb.writeln('// NR gen_types= $info.gen_types.len') - for i, gen_type in info.gen_types { - // ptr_ctype can be for example Cat OR Cat_ptr: - ptr_ctype := gen_type.replace('*', '_ptr').replace('.', '__') + interface_name := c_name(ityp.name) + // generate a struct that references interface methods + methods_struct_name := 'struct _${interface_name}_interface_methods' + mut methods_typ_def := strings.new_builder(100) + mut methods_struct_def := strings.new_builder(100) + methods_struct_def.writeln('$methods_struct_name {') + for method in ityp.methods { + typ_name := '_${interface_name}_${method.name}_fn' + ret_styp := g.typ(method.return_type) + methods_typ_def.write('typedef $ret_styp (*$typ_name)(void* _') + // the first param is the receiver, it's handled by `void*` above + for i in 1 .. method.args.len { + arg := method.args[i] + methods_typ_def.write(', ${g.typ(arg.typ)} $arg.name') + } + // TODO g.fn_args(method.args[1..], method.is_variadic) + methods_typ_def.writeln(');') + methods_struct_def.writeln('\t$typ_name ${c_name(method.name)};') + } + methods_struct_def.writeln('};') + // generate an array of the interface methods for the structs using the interface + // as well as case functions from the struct to the interface + mut methods_struct := strings.new_builder(100) + methods_struct.writeln('$methods_struct_name ${interface_name}_name_table[$inter_info.types.len] = {') + mut cast_functions := strings.new_builder(100) + cast_functions.write('// Casting functions for interface "${interface_name}"') + mut methods_wrapper := strings.new_builder(100) + methods_wrapper.writeln('// Methods wrapper for interface "${interface_name}"') + for i, st in inter_info.types { // cctype is the Cleaned Concrete Type name, *without ptr*, // i.e. cctype is always just Cat, not Cat_ptr: - cctype := gen_type.replace('*', '').replace('.', '__') + cctype := g.cc_type(st) // Speaker_Cat_index = 0 - interface_index_name := '_${interface_name}_${ptr_ctype}_index' - generated_casting_functions += ' -${interface_name} I_${cctype}_to_${interface_name}(${cctype}* x) { - return (${interface_name}){ - ._object = (void*) memdup(x, sizeof(${cctype})), - ._interface_idx = ${interface_index_name} }; -} -' - methods += '{\n' - for j, method in t.methods { - // Cat_speak - methods += ' (void*) ${cctype}_${method.name}' - if j < t.methods.len - 1 { - methods += ', \n' + interface_index_name := '_${interface_name}_${cctype}_index' + cast_functions.writeln(' +_Interface I_${cctype}_to_Interface(${cctype}* x) { + return (_Interface) { + ._object = (void*) memdup(x, sizeof(${cctype})), + ._interface_idx = ${interface_index_name} + }; +}') + methods_struct.writeln('\t{') + for method in ityp.methods { + // .speak = Cat_speak + mut method_call := '${cctype}_${method.name}' + if !method.args[0].typ.is_ptr() { + // inline void Cat_speak_method_wrapper(Cat c) { return Cat_speak(*c); } + methods_wrapper.write('static inline ${g.typ(method.return_type)}') + methods_wrapper.write(' ${method_call}_method_wrapper(') + methods_wrapper.write('${cctype}* ${method.args[0].name}') + // TODO g.fn_args + for j in 1 .. method.args.len { + arg := method.args[j] + methods_wrapper.write(', ${g.typ(arg.typ)} $arg.name') + } + methods_wrapper.writeln(') {') + methods_wrapper.write('\t') + if method.return_type != table.void_type { + methods_wrapper.write('return ') + } + methods_wrapper.write('${method_call}(*${method.args[0].name}') + for j in 1 .. method.args.len { + methods_wrapper.write(', ${method.args[j].name}') + } + methods_wrapper.writeln(');') + methods_wrapper.writeln('}') + // .speak = Cat_speak_method_wrapper + method_call += '_method_wrapper' } + methods_struct.writeln('\t\t.${c_name(method.name)} = $method_call,') } - methods += '\n},\n\n' + methods_struct.writeln('\t},') sb.writeln('int ${interface_index_name} = $i;') } - if info.gen_types.len > 0 { - // methods = '{TCCSKIP(0)}' - // } - sb.writeln('void* (* ${interface_name}_name_table[][$t.methods.len]) = ' + '{ \n $methods \n }; ') - } else { - // The line below is needed so that C compilation succeeds, - // even if no interface methods are called. - // See https://github.com/zenith391/vgtk3/issues/7 - sb.writeln('void* (* ${interface_name}_name_table[][1]) = ' + '{ {NULL} }; ') - } - if generated_casting_functions.len > 0 { - sb.writeln('// Casting functions for interface "${interface_name}" :') - sb.writeln(generated_casting_functions) - } + methods_struct.writeln('};') + // add line return after interface index declarations + sb.writeln('') + sb.writeln(methods_wrapper.str()) + sb.writeln(methods_typ_def.str()) + sb.writeln(methods_struct_def.str()) + sb.writeln(methods_struct.str()) + sb.writeln(cast_functions.str()) } return sb.str() } @@ -3471,11 +3512,6 @@ fn (mut g Gen) array_init(it ast.ArrayInit) { if it.is_interface { // sym := g.table.get_type_symbol(it.interface_types[i]) // isym := g.table.get_type_symbol(it.interface_type) - /* - interface_styp := g.typ(it.interface_type) - styp := g.typ(it.interface_types[i]) - g.write('I_${styp}_to_${interface_styp}(') - */ g.interface_call(it.interface_types[i], it.interface_type) } g.expr(expr) @@ -3490,9 +3526,9 @@ fn (mut g Gen) array_init(it ast.ArrayInit) { // `ui.foo(button)` => // `ui__foo(I_ui__Button_to_ui__Widget(` ... fn (g &Gen) interface_call(typ, interface_type table.Type) { - interface_styp := g.typ(interface_type).replace('*', '') - styp := g.typ(typ).replace('*', '') - g.write('I_${styp}_to_${interface_styp}(') + interface_styp := g.cc_type(interface_type) + styp := g.cc_type(typ) + g.write('/* $interface_styp */ I_${styp}_to_Interface(') if !typ.is_ptr() { g.write('&') } diff --git a/vlib/v/gen/fn.v b/vlib/v/gen/fn.v index d579968a33..5bed0118f4 100644 --- a/vlib/v/gen/fn.v +++ b/vlib/v/gen/fn.v @@ -50,8 +50,7 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl) { } } else { mut name := it.name - c := name[0] - if c in [`+`, `-`, `*`, `/`, `%`] { + if name[0] in [`+`, `-`, `*`, `/`, `%`] { name = util.replace_op(name) } if it.is_method { @@ -279,28 +278,20 @@ fn (mut g Gen) method_call(node ast.CallExpr) { if node.left_type == 0 { verror('method receiver type is 0, this means there are some uchecked exprs') } + mut receiver_type_name := g.cc_type(node.receiver_type) typ_sym := g.table.get_type_symbol(node.receiver_type) - // mut receiver_type_name := g.typ(node.receiver_type) - mut receiver_type_name := typ_sym.name.replace('.', '__') // TODO g.typ() ? if typ_sym.kind == .interface_ { - // Find the index of the method - mut idx := -1 - for i, method in typ_sym.methods { - if method.name == node.name { - idx = i - } - } - if idx == -1 { - verror('method_call: cannot find interface method index') - } - sret_type := g.typ(node.return_type) - g.writeln('// interface method call') - // `((void (*)())(Speaker_name_table[s._interface_idx][1]))(s._object);` - g.write('(($sret_type (*)())(${receiver_type_name}_name_table[') + // Speaker_name_table[s._interface_idx].speak(s._object) + g.write('${c_name(receiver_type_name)}_name_table[') g.expr(node.left) - g.write('._interface_idx][$idx]))(') + g.write('._interface_idx].${node.name}(') g.expr(node.left) - g.write('._object)') + g.write('._object') + if node.args.len > 0 { + g.write(', ') + g.call_args(node.args, node.expected_arg_types) + } + g.write(')') return } if typ_sym.kind == .array && node.name == 'map' { @@ -509,9 +500,8 @@ fn (mut g Gen) call_args(args []ast.CallArg, expected_types []table.Type) { is_variadic := expected_types.len > 0 && expected_types[expected_types.len - 1].flag_is(.variadic) is_forwarding_varg := args.len > 0 && args[args.len - 1].typ.flag_is(.variadic) gen_vargs := is_variadic && !is_forwarding_varg - mut arg_no := 0 - for arg in args { - if gen_vargs && arg_no == expected_types.len - 1 { + for i, arg in args { + if gen_vargs && i == expected_types.len - 1 { break } // if arg.typ.name.starts_with('I') { @@ -519,42 +509,42 @@ fn (mut g Gen) call_args(args []ast.CallArg, expected_types []table.Type) { mut is_interface := false // some c fn definitions dont have args (cfns.v) or are not updated in checker // when these are fixed we wont need this check - if arg_no < expected_types.len { - if expected_types[arg_no] != 0 { + if i < expected_types.len { + if expected_types[i] != 0 { // Cast a type to interface // `foo(dog)` => `foo(I_Dog_to_Animal(dog))` - exp_sym := g.table.get_type_symbol(expected_types[arg_no]) + exp_sym := g.table.get_type_symbol(expected_types[i]) // exp_styp := g.typ(expected_types[arg_no]) // g.table.get_type_symbol(expected_types[arg_no]) // styp := g.typ(arg.typ) // g.table.get_type_symbol(arg.typ) if exp_sym.kind == .interface_ { - g.interface_call(arg.typ, expected_types[arg_no]) + g.interface_call(arg.typ, expected_types[i]) // g.write('/*Z*/I_${styp}_to_${exp_styp}(') is_interface = true } } - g.ref_or_deref_arg(arg, expected_types[arg_no]) + g.ref_or_deref_arg(arg, expected_types[i]) } else { g.expr(arg.expr) } if is_interface { g.write(')') } - if arg_no < args.len - 1 || gen_vargs { + if i < args.len - 1 || gen_vargs { g.write(', ') } - arg_no++ } + arg_nr := expected_types.len - 1 if gen_vargs { varg_type := expected_types[expected_types.len - 1] struct_name := 'varg_' + g.typ(varg_type).replace('*', '_ptr') - variadic_count := args.len - arg_no + variadic_count := args.len - arg_nr varg_type_str := int(varg_type).str() if variadic_count > g.variadic_args[varg_type_str] { g.variadic_args[varg_type_str] = variadic_count } g.write('($struct_name){.len=$variadic_count,.args={') if variadic_count > 0 { - for j in arg_no .. args.len { + for j in arg_nr .. args.len { g.ref_or_deref_arg(args[j], varg_type) if j < args.len - 1 { g.write(', ') diff --git a/vlib/v/parser/struct.v b/vlib/v/parser/struct.v index 950a23723b..00a372fb6b 100644 --- a/vlib/v/parser/struct.v +++ b/vlib/v/parser/struct.v @@ -135,7 +135,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl { fields << table.Field{ name: field_name typ: typ - default_expr: ast.ex2fe( default_expr ) + default_expr: ast.ex2fe(default_expr) has_default_expr: has_default_expr is_pub: is_field_pub is_mut: is_field_mut @@ -265,8 +265,7 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl { kind: .interface_ name: interface_name info: table.Interface{ - gen_types: [] - foo: 'foo' + types: [] } } typ := table.new_type(p.table.register_type_symbol(t)) diff --git a/vlib/v/table/atypes.v b/vlib/v/table/atypes.v index 9da97f3384..225a3cd1f5 100644 --- a/vlib/v/table/atypes.v +++ b/vlib/v/table/atypes.v @@ -180,23 +180,23 @@ pub const ( ) pub const ( - integer_type_idxs = [i8_type_idx, i16_type_idx, int_type_idx, i64_type_idx, byte_type_idx, + integer_type_idxs = [i8_type_idx, i16_type_idx, int_type_idx, i64_type_idx, byte_type_idx, u16_type_idx, u32_type_idx, u64_type_idx ] - signed_integer_type_idxs = [i8_type_idx, i16_type_idx, int_type_idx, i64_type_idx] + signed_integer_type_idxs = [i8_type_idx, i16_type_idx, int_type_idx, i64_type_idx] unsigned_integer_type_idxs = [byte_type_idx, u16_type_idx, u32_type_idx, u64_type_idx] - float_type_idxs = [f32_type_idx, f64_type_idx] - number_type_idxs = [i8_type_idx, i16_type_idx, int_type_idx, i64_type_idx, byte_type_idx, + float_type_idxs = [f32_type_idx, f64_type_idx] + number_type_idxs = [i8_type_idx, i16_type_idx, int_type_idx, i64_type_idx, byte_type_idx, u16_type_idx, u32_type_idx, u64_type_idx, f32_type_idx, f64_type_idx ] - pointer_type_idxs = [voidptr_type_idx, byteptr_type_idx, charptr_type_idx] - string_type_idxs = [string_type_idx, ustring_type_idx] + pointer_type_idxs = [voidptr_type_idx, byteptr_type_idx, charptr_type_idx] + string_type_idxs = [string_type_idx, ustring_type_idx] ) pub const ( @@ -530,8 +530,7 @@ pub mut: pub struct Interface { mut: - gen_types []string - foo string + types []Type } pub struct Enum { @@ -547,7 +546,7 @@ pub: // NB: FExpr here is a actually an ast.Expr . // It should always be used by casting to ast.Expr, using ast.fe2ex()/ast.ex2fe() // That hack is needed to break an import cycle between v.ast and v.table . -type FExpr = voidptr | byteptr +type FExpr = byteptr | voidptr pub struct Field { pub: @@ -634,6 +633,22 @@ pub fn (table &Table) type_to_str(t Type) string { return res } +pub fn (t &TypeSymbol) has_method(name string) bool { + t.find_method(name) or { + return false + } + return true +} + +pub fn (t &TypeSymbol) find_method(name string) ?Fn { + for method in t.methods { + if method.name == name { + return method + } + } + return none +} + pub fn (s Struct) find_field(name string) ?Field { for field in s.fields { if field.name == name { diff --git a/vlib/v/table/table.v b/vlib/v/table/table.v index d25e91c9b3..d629591a96 100644 --- a/vlib/v/table/table.v +++ b/vlib/v/table/table.v @@ -96,41 +96,6 @@ pub fn (mut t TypeSymbol) register_method(new_fn Fn) { t.methods << new_fn } -pub fn (t &TypeSymbol) has_method(name string) bool { - t.find_method(name) or { - return false - } - return true -} - -pub fn (t &TypeSymbol) find_method(name string) ?Fn { - for method in t.methods { - if method.name == name { - return method - } - } - return none -} - -pub fn (s &TypeSymbol) has_field(name string) bool { - s.find_field(name) or { - return false - } - return true -} - -pub fn (s &TypeSymbol) find_field(name string) ?Field { - match s.info { - Struct { for field in it.fields { - if field.name == name { - return field - } - } } - else {} - } - return none -} - pub fn (t &Table) type_has_method(s &TypeSymbol, name string) bool { // println('type_has_method($s.name, $name) types.len=$t.types.len s.parent_idx=$s.parent_idx') if _ := t.type_find_method(s, name) { @@ -168,8 +133,11 @@ pub fn (t &Table) struct_find_field(s &TypeSymbol, name string) ?Field { // println('struct_find_field($s.name, $name) types.len=$t.types.len s.parent_idx=$s.parent_idx') mut ts := s for { - if field := ts.find_field(name) { - return field + if ts.info is Struct { + struct_info := ts.info as Struct + if field := struct_info.find_field(name) { + return field + } } if ts.parent_idx == 0 { break @@ -179,6 +147,15 @@ pub fn (t &Table) struct_find_field(s &TypeSymbol, name string) ?Field { return none } +pub fn (t &Table) interface_add_type(inter mut Interface, typ Type) bool { + // TODO Verify `typ` implements `inter` + typ_sym := t.get_type_symbol(typ) + if typ !in inter.types && typ_sym.kind != .interface_ { + inter.types << typ + } + return true +} + [inline] pub fn (t &Table) find_type_idx(name string) int { return t.type_idxs[name] @@ -491,15 +468,7 @@ pub fn (t &Table) check(got, expected Type) bool { // Handle expected interface if exp_type_sym.kind == .interface_ { mut info := exp_type_sym.info as Interface - // println('gen_types before') - // println(info.gen_types) - if got_type_sym.name !in info.gen_types && got_type_sym.kind != .interface_ { - // TODO `got` should never be an interface? - info.gen_types << got_type_sym.name - } - // println('adding gen_type $got_type_sym.name') - // println(info.gen_types) - return true + return t.interface_add_type(info, got) } // Handle expected interface array /* diff --git a/vlib/v/tests/interface_test.v b/vlib/v/tests/interface_test.v index 7107cb580d..342e539bc4 100644 --- a/vlib/v/tests/interface_test.v +++ b/vlib/v/tests/interface_test.v @@ -11,15 +11,18 @@ fn (d Cat) name() string { return 'Cat' } -fn (d Cat) speak() { +fn (d Cat) speak(s string) { + assert s == 'Hi !' println('meow') } -fn (d Dog) speak() { +fn (d Dog) speak(s string) { + assert s == 'Hi !' println('woof') } fn (d Dog) name() string { + assert d.breed == 'Labrador Retriever' return 'Dog' } @@ -31,7 +34,7 @@ fn test_todo() { fn perform_speak(s Speaker) { - s.speak() + s.speak('Hi !') assert true name := s.name() assert name == 'Dog' || name == 'Cat' @@ -42,7 +45,7 @@ fn perform_speak(s Speaker) { } fn test_perform_speak() { - dog := Dog{} + dog := Dog{breed: 'Labrador Retriever'} perform_speak(dog) cat := Cat{} perform_speak(cat) @@ -88,7 +91,7 @@ struct Foo { interface Speaker { name() string - speak() + speak(s string) }