checker: allow `Struct{...expr}` where `expr` is another struct type (#8495)
parent
17746561f2
commit
79e9084f7b
vlib/v
checker
tests
gen
|
@ -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 {
|
||||
|
|
|
@ -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{})
|
||||
|
|
|
@ -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 | }
|
||||
|
|
|
@ -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 | }
|
||||
|
|
|
@ -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 | }
|
||||
|
|
|
@ -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 | }
|
||||
|
|
|
@ -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 | }
|
||||
|
|
|
@ -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 | }
|
|
@ -1,15 +0,0 @@
|
|||
struct Foo {
|
||||
name string
|
||||
age int
|
||||
}
|
||||
|
||||
struct Foo2 {}
|
||||
|
||||
fn main() {
|
||||
f3 := Foo2{}
|
||||
_ := Foo{
|
||||
...f3
|
||||
name: 'f2'
|
||||
}
|
||||
}
|
||||
|
|
@ -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 | }
|
||||
|
|
|
@ -24,3 +24,12 @@ fn main() {
|
|||
}
|
||||
}
|
||||
|
||||
struct Empty {}
|
||||
|
||||
fn empty() {
|
||||
e := Empty{}
|
||||
_ = Foo{
|
||||
...e
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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}
|
||||
}
|
Loading…
Reference in New Issue