cgen: fix interface struct field initialization (#7340)

pull/7362/head
Ned Palacios 2020-12-16 17:03:28 +08:00 committed by GitHub
parent 7426544610
commit 604eab7742
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 156 additions and 2 deletions

View File

@ -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())

View File

@ -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) {

View File

@ -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'
}
*/