From effa3188dd532ee7565f6783707ebdf2b03eb5ee Mon Sep 17 00:00:00 2001 From: spaceface Date: Sun, 31 Jan 2021 16:24:33 -0100 Subject: [PATCH] cgen: fix type casts to interfaces (#8476) --- vlib/v/ast/ast.v | 8 +-- vlib/v/checker/checker.v | 2 - vlib/v/gen/array.v | 8 --- vlib/v/gen/cgen.v | 91 +++++++-------------------- vlib/v/gen/fn.v | 25 +------- vlib/v/tests/cast_to_interface_test.v | 17 +++++ 6 files changed, 44 insertions(+), 107 deletions(-) create mode 100644 vlib/v/tests/cast_to_interface_test.v diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index f8f787b62a..c1f1390ec6 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -939,11 +939,9 @@ pub: has_cap bool has_default bool pub mut: - expr_types []table.Type // [Dog, Cat] // also used for interface_types - is_interface bool // array of interfaces e.g. `[]Animal` `[Dog{}, Cat{}]` - interface_type table.Type // Animal - elem_type table.Type // element type - typ table.Type // array type + expr_types []table.Type // [Dog, Cat] // also used for interface_types + elem_type table.Type // element type + typ table.Type // array type } pub struct ArrayDecompose { diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index ee41ee6144..b28ea4965f 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -2832,8 +2832,6 @@ pub fn (mut c Checker) array_init(mut array_init ast.ArrayInit) table.Type { if c.table.get_type_symbol(expected_value_type).kind == .interface_ { // Array of interfaces? (`[dog, cat]`) Save the interface type (`Animal`) expecting_interface_array = true - array_init.interface_type = expected_value_type - array_init.is_interface = true } } // expecting_interface_array := c.expected_type != 0 && diff --git a/vlib/v/gen/array.v b/vlib/v/gen/array.v index 91ebe19c11..42db6d9920 100644 --- a/vlib/v/gen/array.v +++ b/vlib/v/gen/array.v @@ -114,15 +114,7 @@ fn (mut g Gen) array_init(it ast.ArrayInit) { g.write('\t\t') } for i, expr in it.exprs { - if it.is_interface { - // sym := g.table.get_type_symbol(it.expr_types[i]) - // isym := g.table.get_type_symbol(it.interface_type) - g.interface_call(it.expr_types[i], it.interface_type) - } g.expr_with_cast(expr, it.expr_types[i], it.elem_type) - if it.is_interface { - g.write(')') - } if i != len - 1 { g.write(', ') } diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index 9793e64258..9cc1b0398b 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -1449,9 +1449,26 @@ fn (mut g Gen) for_in(it ast.ForInStmt) { } } -// use instead of expr() when you need to cast to union sum type (can add other casts also) +// use instead of expr() when you need to cast to a different type fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw table.Type, expected_type table.Type) { got_type := g.table.mktyp(got_type_raw) + exp_sym := g.table.get_type_symbol(expected_type) + if exp_sym.kind == .interface_ && got_type_raw.idx() != expected_type.idx() + && !expected_type.has_flag(.optional) { + got_styp := g.cc_type(got_type) + exp_styp := g.cc_type(expected_type) + g.write('I_${got_styp}_to_Interface_$exp_styp') + if expected_type.is_ptr() { + g.write('_ptr') + } + g.write('(') + if !got_type.is_ptr() { + g.write('&') + } + g.expr(expr) + g.write(')') + return + } // cast to sum type if expected_type != table.void_type { expected_is_ptr := expected_type.is_ptr() @@ -1878,7 +1895,6 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { } // `a := 1` | `a,b := 1,2` for i, left in assign_stmt.left { - mut is_interface := false mut var_type := assign_stmt.left_types[i] mut val_type := assign_stmt.right_types[i] val := assign_stmt.right[i] @@ -1888,18 +1904,6 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { scope: 0 } left_sym := g.table.get_type_symbol(var_type) - if left_sym.kind == .interface_ { - if left is ast.SelectorExpr { - ident = left.root_ident() - if ident.obj is ast.Var { - idobj := ident.obj as ast.Var - root_type_sym := g.table.get_type_symbol(idobj.typ) - if root_type_sym.kind == .struct_ { - is_interface = true - } - } - } - } if left is ast.Ident { ident = left // id_info := ident.var_info() @@ -1959,9 +1963,6 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { is_fixed_array_copy := right_sym.kind == .array_fixed && val is ast.Ident g.is_assign_lhs = true g.assign_op = assign_stmt.op - if is_interface && right_sym.kind == .interface_ { - is_interface = false - } if val_type.has_flag(.optional) { g.right_is_opt = true } @@ -2143,13 +2144,7 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { if assign_stmt.has_cross_var { g.gen_cross_tmp_variable(assign_stmt.left, val) } else { - if is_interface { - g.interface_call(val_type, var_type) - } g.expr_with_cast(val, val_type, var_type) - if is_interface { - g.write(')') - } } } } @@ -2571,7 +2566,7 @@ fn (mut g Gen) expr(node ast.Expr) { g.expr(node.arg) } g.write(')') - } else if sym.kind == .sum_type { + } else if sym.kind in [.sum_type, .interface_] { g.expr_with_cast(node.expr, node.expr_type, node.typ) } else if sym.kind == .struct_ && !node.typ.is_ptr() && !(sym.info as table.Struct).is_typedef { @@ -3303,10 +3298,6 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) { } else { g.write(', _MOV(($elem_type_str[]){ ') } - is_interface := elem_sym.kind == .interface_ && node.right_type != info.elem_type - if elem_sym.kind == .interface_ && node.right_type != info.elem_type { - g.interface_call(node.right_type, info.elem_type) - } // if g.autofree needs_clone := info.elem_type == table.string_type && !g.is_builtin_mod if needs_clone { @@ -3316,9 +3307,6 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) { if needs_clone { g.write(')') } - if is_interface { - g.write(')') - } g.write(' }))') } } else if node.op == .arrow { @@ -4508,11 +4496,8 @@ fn (mut g Gen) return_statement(node ast.Return) { g.writeln('$opt_type $opt_tmp;') g.write('opt_ok2(&($styp[]) { ') if !g.fn_decl.return_type.is_ptr() && node.types[0].is_ptr() { - // Automatic Dereference for optional - g.write('*') - // Fixes returning a mutable receiver with interface as return type - if node.exprs[0] is ast.Ident && !g.is_amp { - g.write('&') + if !(node.exprs[0] is ast.Ident && !g.is_amp) { + g.write('*') } } for i, expr in node.exprs { @@ -4544,14 +4529,7 @@ fn (mut g Gen) return_statement(node ast.Return) { } else { g.write('return ') } - cast_interface := sym.kind == .interface_ && node.types[0] != g.fn_decl.return_type - if cast_interface { - g.interface_call(node.types[0], g.fn_decl.return_type) - } g.expr_with_cast(node.exprs[0], node.types[0], g.fn_decl.return_type) - if cast_interface { - g.write(')') - } if free { expr := node.exprs[0] if expr is ast.Ident { @@ -4830,11 +4808,8 @@ fn (mut g Gen) struct_init(struct_init ast.StructInit) { continue } g.write('.$field_name = ') - expected_field_type_sym := g.table.get_type_symbol(sfield.expected_type) field_type_sym := g.table.get_type_symbol(sfield.typ) mut cloned := false - is_interface := expected_field_type_sym.kind == .interface_ - && field_type_sym.kind != .interface_ if g.is_autofree && !sfield.typ.is_ptr() && field_type_sym.kind in [.array, .string] { g.write('/*clone1*/') if g.gen_clone_assignment(sfield.expr, field_type_sym, false) { @@ -4842,17 +4817,11 @@ fn (mut g Gen) struct_init(struct_init ast.StructInit) { } } if !cloned { - if is_interface { - g.interface_call(sfield.typ, sfield.expected_type) - } if sfield.expected_type.is_ptr() && !(sfield.typ.is_ptr() || sfield.typ.is_pointer())&& !sfield.typ.is_number() { g.write('/* autoref */&') } g.expr_with_cast(sfield.expr, sfield.typ, sfield.expected_type) - if is_interface { - g.write(')') - } } if is_multiline { g.writeln(',') @@ -5209,9 +5178,6 @@ fn (g &Gen) sort_structs(typesa []table.TypeSymbol) []table.TypeSymbol { } } table.Struct { - // if info.is_interface { - // continue - // } for embed in t.info.embeds { dep := g.table.get_type_symbol(embed).name // skip if not in types list or already in deps @@ -6105,21 +6071,6 @@ $staticprefix $interface_name* I_${cctype}_to_Interface_${interface_name}_ptr($c return sb.str() } -// `ui.foo(button)` => -// `ui__foo(I_ui__Button_to_ui__Widget(` ... -fn (mut g Gen) interface_call(typ table.Type, interface_type table.Type) { - interface_styp := g.cc_type(interface_type) - styp := g.cc_type(typ) - mut cast_fn_name := 'I_${styp}_to_Interface_$interface_styp' - if interface_type.is_ptr() { - cast_fn_name += '_ptr' - } - g.write('${cast_fn_name}(') - if !typ.is_ptr() { - g.write('&') - } -} - fn (mut g Gen) panic_debug_info(pos token.Position) (int, string, string, string) { paline := pos.line_nr + 1 if isnil(g.fn_decl) { diff --git a/vlib/v/gen/fn.v b/vlib/v/gen/fn.v index 39ed634b39..8feb8c64ee 100644 --- a/vlib/v/gen/fn.v +++ b/vlib/v/gen/fn.v @@ -832,26 +832,10 @@ fn (mut g Gen) call_args(node ast.CallExpr) { use_tmp_var_autofree := g.is_autofree && arg.typ == table.string_type && arg.is_tmp_autofree && !g.inside_const&& !g.is_builtin_mod // g.write('/* af=$arg.is_tmp_autofree */') - 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 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[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) - // NB: the second check avoids casting the interface into itself - // aka avoid 'I__Speaker_to_Interface_Speaker' thing for example - if exp_sym.kind == .interface_ && expected_types[i] != arg.typ { - g.interface_call(arg.typ, expected_types[i]) - is_interface = true - } - } - if is_interface { - g.expr(arg.expr) - } else if use_tmp_var_autofree { + if use_tmp_var_autofree { if arg.is_tmp_autofree { // && !g.is_js_call { // We saved expressions in temp variables so that they can be freed later. // `foo(str + str2) => x := str + str2; foo(x); x.free()` @@ -876,9 +860,6 @@ fn (mut g Gen) call_args(node ast.CallExpr) { g.expr(arg.expr) } } - if is_interface { - g.write(')') - } if i < args.len - 1 || is_variadic { g.write(', ') } @@ -945,8 +926,8 @@ fn (mut g Gen) ref_or_deref_arg(arg ast.CallArg, expected_type table.Type) { } else { expected_type } - is_sum_type := g.table.get_type_symbol(expected_deref_type).kind == .sum_type - if !((arg_typ_sym.kind == .function) || is_sum_type) { + deref_sym := g.table.get_type_symbol(expected_deref_type) + if !((arg_typ_sym.kind == .function) || deref_sym.kind in [.sum_type, .interface_]) { g.write('(voidptr)&/*qq*/') } } diff --git a/vlib/v/tests/cast_to_interface_test.v b/vlib/v/tests/cast_to_interface_test.v new file mode 100644 index 0000000000..09ab0a8bb4 --- /dev/null +++ b/vlib/v/tests/cast_to_interface_test.v @@ -0,0 +1,17 @@ +struct Cat { + x int = 123 +} + +interface Adoptable { +} + +fn test_casting_to_interface() { + cat := Cat{} + a := Adoptable(cat) + if a is Cat { + assert typeof(a).name == '&Cat' + assert a.x == 123 + return + } + assert false +}