From 604eab774233ada58eb0694c7b959a86c0724fb6 Mon Sep 17 00:00:00 2001 From: Ned Palacios Date: Wed, 16 Dec 2020 17:03:28 +0800 Subject: [PATCH] cgen: fix interface struct field initialization (#7340) --- vlib/v/checker/checker.v | 7 +- vlib/v/gen/cgen.v | 34 ++++++++ vlib/v/tests/interface_struct_test.v | 117 +++++++++++++++++++++++++++ 3 files changed, 156 insertions(+), 2 deletions(-) create mode 100644 vlib/v/tests/interface_struct_test.v diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 0e03464f5d..188fff8509 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -566,10 +566,13 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) table.Type { } } inited_fields << field_name + field_type_sym := c.table.get_type_symbol(info_field.typ) c.expected_type = info_field.typ expr_type := c.expr(field.expr) expr_type_sym := c.table.get_type_symbol(expr_type) - if expr_type != table.void_type && expr_type_sym.kind != .placeholder { + if field_type_sym.kind == .interface_ { + c.type_implements(expr_type, info_field.typ, field.pos) + } else if expr_type != table.void_type && expr_type_sym.kind != .placeholder { c.check_expected(expr_type, info_field.typ) or { c.error('cannot assign to field `$info_field.name`: $err', field.pos) } @@ -2263,7 +2266,7 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) { } else {} } - if !is_blank_ident && right_sym.kind != .placeholder { + if !is_blank_ident && right_sym.kind != .placeholder && left_sym.kind != .interface_ { // Dual sides check (compatibility check) c.check_expected(right_type_unwrapped, left_type_unwrapped) or { c.error('cannot assign to `$left`: $err', right.position()) diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index 1e17a1883a..098a2e86be 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -1734,6 +1734,7 @@ 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] @@ -1742,6 +1743,19 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { mut ident := ast.Ident{ 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() @@ -1799,6 +1813,9 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { } right_sym := g.table.get_type_symbol(val_type) g.is_assign_lhs = true + if is_interface && right_sym.kind == .interface_ { + is_interface = false + } if val_type.has_flag(.optional) { g.right_is_opt = true } @@ -1939,7 +1956,13 @@ 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(')') + } } } } @@ -4340,8 +4363,11 @@ 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.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) { @@ -4349,11 +4375,17 @@ 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(',') @@ -5706,6 +5738,8 @@ fn (mut g Gen) interface_table() string { already_generated_mwrappers[interface_index_name] = current_iinidx current_iinidx++ // eprintln('>>> current_iinidx: ${current_iinidx-iinidx_minimum_base} | interface_index_name: $interface_index_name') + sb.writeln('_Interface I_${cctype}_to_Interface_${interface_name}($cctype* x);') + sb.writeln('_Interface* I_${cctype}_to_Interface_${interface_name}_ptr($cctype* x);') cast_functions.writeln(' _Interface I_${cctype}_to_Interface_${interface_name}($cctype* x) { return (_Interface) { diff --git a/vlib/v/tests/interface_struct_test.v b/vlib/v/tests/interface_struct_test.v new file mode 100644 index 0000000000..7af23cae0f --- /dev/null +++ b/vlib/v/tests/interface_struct_test.v @@ -0,0 +1,117 @@ +interface Speaker { + say_hello() string + speak(msg string) +} + +struct Boss { + name string +} + +fn (b Boss) say_hello() string { + return "Hello, My name is $b.name and I\'m the bawz" +} + +fn (b Boss) speak(msg string) { + println(msg) +} + +struct Cat { + name string + breed string +} + +fn (c Cat) say_hello() string { + return 'Meow meow $c.name the $c.breed meow' +} + +fn (c Cat) speak(msg string) { + println('Meow $msg') +} + +struct Baz { +mut: + sp Speaker +} + +fn test_interface_struct() { + bz1 := Baz{ + sp: Boss{ + name: 'Richard' + } + } + assert bz1.sp.say_hello() == "Hello, My name is Richard and I\'m the bawz" + print('Test Boss inside Baz struct: ') + bz1.sp.speak('Hello world!') + bz2 := Baz{ + sp: Cat{ + name: 'Grungy' + breed: 'Persian Cat' + } + } + assert bz2.sp.say_hello() == 'Meow meow Grungy the Persian Cat meow' + print('Test Cat inside Baz struct: ') + bz2.sp.speak('Hello world!') +} + +fn test_interface_mut_struct() { + mut mbaz := Baz{ + sp: Boss{ + name: 'Derek' + } + } + assert mbaz.sp.say_hello() == "Hello, My name is Derek and I\'m the bawz" + mbaz.sp = Cat{ + name: 'Dog' + breed: 'Not a dog' + } + assert mbaz.sp.say_hello() == 'Meow meow Dog the Not a dog meow' +} + +fn test_interface_struct_from_array() { + bazs := [ + Baz{ + sp: Cat{ + name: 'Kitty' + breed: 'Catty Koo' + } + }, + Baz{ + sp: Boss{ + name: 'Bob' + } + }, + ] + assert bazs[0].sp.say_hello() == 'Meow meow Kitty the Catty Koo meow' + assert bazs[1].sp.say_hello() == "Hello, My name is Bob and I\'m the bawz" +} + +/* +// TODO: fix this too; currently with V 0.1.30 7426544 produces: `V panic: as cast: cannot cast 200 to 197` +fn test_interface_struct_from_mut_array() { + mut bazs := [ + Baz{ + sp: Cat{ + name: 'Kitty' + breed: 'Catty Koo' + } + }, + Baz{ + sp: Boss{ + name: 'Bob' + } + } + ] + + bazs[0].sp = Boss{ + name: 'Ross' + } + + bazs[1].sp = Cat{ + name: 'Doggy' + breed: 'Doggy Doo' + } + + assert bazs[0].sp.say_hello() == 'Hello, My name is Ross and I\'m the bawz' + assert bazs[1].sp.say_hello() == 'Meow meow Doggy the Doggy Doo meow' +} +*/