diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 3f29fe51c4..0ad7448506 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -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 { diff --git a/vlib/v/checker/tests/cannot_cast_to_struct.out b/vlib/v/checker/tests/cannot_cast_to_struct.out index a9c103caf2..9fe4c10bde 100644 --- a/vlib/v/checker/tests/cannot_cast_to_struct.out +++ b/vlib/v/checker/tests/cannot_cast_to_struct.out @@ -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{}) diff --git a/vlib/v/checker/tests/struct_cast_to_struct_generic_err.out b/vlib/v/checker/tests/struct_cast_to_struct_generic_err.out index b87dc3beaa..62cb81ab11 100644 --- a/vlib/v/checker/tests/struct_cast_to_struct_generic_err.out +++ b/vlib/v/checker/tests/struct_cast_to_struct_generic_err.out @@ -1,6 +1,6 @@ -vlib/v/checker/tests/struct_cast_to_struct_generic_err.vv:11:7: error: cannot convert struct `Abc` 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{} 11 | _ := Xyz(abc) | ~~~~~~~~ - 12 | } \ No newline at end of file + 12 | } diff --git a/vlib/v/checker/tests/struct_cast_to_struct_mut_err_a.out b/vlib/v/checker/tests/struct_cast_to_struct_mut_err_a.out index e5e5b8478f..baafe70087 100644 --- a/vlib/v/checker/tests/struct_cast_to_struct_mut_err_a.out +++ b/vlib/v/checker/tests/struct_cast_to_struct_mut_err_a.out @@ -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 | } \ No newline at end of file + 13 | } diff --git a/vlib/v/checker/tests/struct_cast_to_struct_mut_err_b.out b/vlib/v/checker/tests/struct_cast_to_struct_mut_err_b.out index 766b1249aa..aa6f2324dc 100644 --- a/vlib/v/checker/tests/struct_cast_to_struct_mut_err_b.out +++ b/vlib/v/checker/tests/struct_cast_to_struct_mut_err_b.out @@ -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 | } \ No newline at end of file + 13 | } diff --git a/vlib/v/checker/tests/struct_cast_to_struct_pub_err_a.out b/vlib/v/checker/tests/struct_cast_to_struct_pub_err_a.out index 9ed7491fac..0a2778b2e8 100644 --- a/vlib/v/checker/tests/struct_cast_to_struct_pub_err_a.out +++ b/vlib/v/checker/tests/struct_cast_to_struct_pub_err_a.out @@ -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 | } \ No newline at end of file + 13 | } diff --git a/vlib/v/checker/tests/struct_cast_to_struct_pub_err_b.out b/vlib/v/checker/tests/struct_cast_to_struct_pub_err_b.out index f59dd7eb81..bbc7e3314b 100644 --- a/vlib/v/checker/tests/struct_cast_to_struct_pub_err_b.out +++ b/vlib/v/checker/tests/struct_cast_to_struct_pub_err_b.out @@ -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 | } \ No newline at end of file + 13 | } diff --git a/vlib/v/checker/tests/struct_init_update_struct_err.out b/vlib/v/checker/tests/struct_init_update_struct_err.out deleted file mode 100644 index 5d7956d276..0000000000 --- a/vlib/v/checker/tests/struct_init_update_struct_err.out +++ /dev/null @@ -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 | } \ No newline at end of file diff --git a/vlib/v/checker/tests/struct_init_update_struct_err.vv b/vlib/v/checker/tests/struct_init_update_struct_err.vv deleted file mode 100644 index 9b73579e6e..0000000000 --- a/vlib/v/checker/tests/struct_init_update_struct_err.vv +++ /dev/null @@ -1,15 +0,0 @@ -struct Foo { - name string - age int -} - -struct Foo2 {} - -fn main() { - f3 := Foo2{} - _ := Foo{ - ...f3 - name: 'f2' - } -} - diff --git a/vlib/v/checker/tests/struct_init_update_type_err.out b/vlib/v/checker/tests/struct_init_update_type_err.out index b8749f9e78..c0eab9e291 100644 --- a/vlib/v/checker/tests/struct_init_update_type_err.out +++ b/vlib/v/checker/tests/struct_init_update_type_err.out @@ -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 | } diff --git a/vlib/v/checker/tests/struct_init_update_type_err.vv b/vlib/v/checker/tests/struct_init_update_type_err.vv index 2bd876c786..6f8deebc91 100644 --- a/vlib/v/checker/tests/struct_init_update_type_err.vv +++ b/vlib/v/checker/tests/struct_init_update_type_err.vv @@ -24,3 +24,12 @@ fn main() { } } +struct Empty {} + +fn empty() { + e := Empty{} + _ = Foo{ + ...e + } +} + diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index 987c16447c..8f11ec316a 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -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) diff --git a/vlib/v/tests/struct_transmute_test.v b/vlib/v/tests/struct_transmute_test.v new file mode 100644 index 0000000000..a789aa968f --- /dev/null +++ b/vlib/v/tests/struct_transmute_test.v @@ -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} +}