checker: allow `Struct{...expr}` where `expr` is another struct type (#8495)

pull/8499/head
Nick Treleaven 2021-02-01 19:08:25 +00:00 committed by GitHub
parent 17746561f2
commit 79e9084f7b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 88 additions and 40 deletions

View File

@ -542,6 +542,7 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) table.Type {
match type_sym.kind {
.placeholder {
c.error('unknown struct: $type_sym.name', struct_init.pos)
return table.void_type
}
// string & array are also structs but .kind of string/array
.struct_, .string, .array, .alias {
@ -684,10 +685,17 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) table.Type {
s := c.table.type_to_str(update_type)
c.error('expected struct, found `$s`', struct_init.update_expr.position())
} else if update_type != struct_init.typ {
sym := c.table.get_type_symbol(struct_init.typ)
update_sym := c.table.get_type_symbol(update_type)
c.error('expected struct `$sym.name`, found struct `$update_sym.name`', struct_init.update_expr.position())
} else if !struct_init.update_expr.is_lvalue() {
from_sym := c.table.get_type_symbol(update_type)
to_sym := c.table.get_type_symbol(struct_init.typ)
from_info := from_sym.info as table.Struct
to_info := to_sym.info as table.Struct
// TODO this check is too strict
if !c.check_struct_signature(from_info, to_info) {
c.error('struct `$from_sym.name` is not compatible with struct `$to_sym.name`',
struct_init.update_expr.position())
}
}
if !struct_init.update_expr.is_lvalue() {
// cgen will repeat `update_expr` for each field
// so enforce an lvalue for efficiency
c.error('expression is not an lvalue', struct_init.update_expr.position())
@ -3653,6 +3661,8 @@ pub fn (mut c Checker) cast_expr(mut node ast.CastExpr) table.Type {
&& !(to_type_sym.info as table.Struct).is_typedef {
// For now we ignore C typedef because of `C.Window(C.None)` in vlib/clipboard
if from_type_sym.kind == .struct_ && !node.expr_type.is_ptr() {
c.warn('casting to struct is deprecated, use e.g. `Struct{...expr}` instead',
node.pos)
from_type_info := from_type_sym.info as table.Struct
to_type_info := to_type_sym.info as table.Struct
if !c.check_struct_signature(from_type_info, to_type_info) {
@ -5274,9 +5284,10 @@ pub fn (mut c Checker) error(message string, pos token.Position) {
c.warn_or_error(msg, pos, false)
}
// check_struct_signature checks if both structs has the same signature / fields for casting
// check `to` has all fields of `from`
fn (c Checker) check_struct_signature(from table.Struct, to table.Struct) bool {
if from.fields.len != to.fields.len {
// Note: `to` can have extra fields
if from.fields.len == 0 {
return false
}
for _, field in from.fields {

View File

@ -1,4 +1,4 @@
vlib/v/checker/tests/cannot_cast_to_struct.vv:10:7: error: cannot convert struct `Abc` to struct `Test`
vlib/v/checker/tests/cannot_cast_to_struct.vv:10:7: error: casting to struct is deprecated, use e.g. `Struct{...expr}` instead
8 |
9 | fn main() {
10 | _ := Test(Abc{})

View File

@ -1,6 +1,6 @@
vlib/v/checker/tests/struct_cast_to_struct_generic_err.vv:11:7: error: cannot convert struct `Abc<int>` to struct `Xyz`
vlib/v/checker/tests/struct_cast_to_struct_generic_err.vv:11:7: error: casting to struct is deprecated, use e.g. `Struct{...expr}` instead
9 | fn main() {
10 | abc := Abc<int>{}
11 | _ := Xyz(abc)
| ~~~~~~~~
12 | }
12 | }

View File

@ -1,6 +1,6 @@
vlib/v/checker/tests/struct_cast_to_struct_mut_err_a.vv:12:7: error: cannot convert struct `Abc` to struct `Xyz`
vlib/v/checker/tests/struct_cast_to_struct_mut_err_a.vv:12:7: error: casting to struct is deprecated, use e.g. `Struct{...expr}` instead
10 | fn main() {
11 | abc := Abc{}
12 | _ := Xyz(abc)
| ~~~~~~~~
13 | }
13 | }

View File

@ -1,6 +1,6 @@
vlib/v/checker/tests/struct_cast_to_struct_mut_err_b.vv:12:7: error: cannot convert struct `Abc` to struct `Xyz`
vlib/v/checker/tests/struct_cast_to_struct_mut_err_b.vv:12:7: error: casting to struct is deprecated, use e.g. `Struct{...expr}` instead
10 | fn main() {
11 | abc := Abc{}
12 | _ := Xyz(abc)
| ~~~~~~~~
13 | }
13 | }

View File

@ -1,6 +1,6 @@
vlib/v/checker/tests/struct_cast_to_struct_pub_err_a.vv:12:7: error: cannot convert struct `Abc` to struct `Xyz`
vlib/v/checker/tests/struct_cast_to_struct_pub_err_a.vv:12:7: error: casting to struct is deprecated, use e.g. `Struct{...expr}` instead
10 | fn main() {
11 | abc := Abc{}
12 | _ := Xyz(abc)
| ~~~~~~~~
13 | }
13 | }

View File

@ -1,6 +1,6 @@
vlib/v/checker/tests/struct_cast_to_struct_pub_err_b.vv:12:7: error: cannot convert struct `Abc` to struct `Xyz`
vlib/v/checker/tests/struct_cast_to_struct_pub_err_b.vv:12:7: error: casting to struct is deprecated, use e.g. `Struct{...expr}` instead
10 | fn main() {
11 | abc := Abc{}
12 | _ := Xyz(abc)
| ~~~~~~~~
13 | }
13 | }

View File

@ -1,7 +0,0 @@
vlib/v/checker/tests/struct_init_update_struct_err.vv:11:6: error: expected struct `Foo`, found struct `Foo2`
9 | f3 := Foo2{}
10 | _ := Foo{
11 | ...f3
| ~~
12 | name: 'f2'
13 | }

View File

@ -1,15 +0,0 @@
struct Foo {
name string
age int
}
struct Foo2 {}
fn main() {
f3 := Foo2{}
_ := Foo{
...f3
name: 'f2'
}
}

View File

@ -12,7 +12,7 @@ vlib/v/checker/tests/struct_init_update_type_err.vv:16:6: error: expected struct
| ^
17 | }
18 | f2 := Foo2{}
vlib/v/checker/tests/struct_init_update_type_err.vv:20:6: error: expected struct `Foo`, found struct `Foo2`
vlib/v/checker/tests/struct_init_update_type_err.vv:20:6: error: struct `Foo2` is not compatible with struct `Foo`
18 | f2 := Foo2{}
19 | _ = Foo{
20 | ...f2
@ -26,3 +26,10 @@ vlib/v/checker/tests/struct_init_update_type_err.vv:23:6: error: expression is n
| ~~~~~
24 | }
25 | }
vlib/v/checker/tests/struct_init_update_type_err.vv:32:6: error: struct `Empty` is not compatible with struct `Foo`
30 | e := Empty{}
31 | _ = Foo{
32 | ...e
| ^
33 | }
34 | }

View File

@ -24,3 +24,12 @@ fn main() {
}
}
struct Empty {}
fn empty() {
e := Empty{}
_ = Foo{
...e
}
}

View File

@ -2570,6 +2570,7 @@ fn (mut g Gen) expr(node ast.Expr) {
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 {
// deprecated, replaced by Struct{...exr}
styp := g.typ(node.typ)
g.write('*(($styp *)(&')
g.expr(node.expr)

View File

@ -0,0 +1,42 @@
struct Foo {
age int
name string
}
// different order
struct Bar {
name string
age int
}
fn test_order() {
f := Foo{
age: 4
name: 'f'
}
b := Bar{
...f
}
assert b == Bar{'f',4}
b2 := Bar{
...f
name: 'b2'
}
assert b2.name == 'b2'
assert b2.age == 4
}
struct Qux {
name string
age int
extra bool
}
fn test_extra() {
f := Foo{4,'f'}
q := Qux{
...f
extra: true
}
assert q == Qux{'f', 4, true}
}