From c53757848195bf0852a5651c2531b2a359bb2810 Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Mon, 1 Feb 2021 14:18:03 +0000 Subject: [PATCH] checker: ensure `expr` is an lvalue with `Struct{...expr` (#8489) --- vlib/v/checker/checker.v | 13 +++++--- .../tests/struct_init_update_type_err.out | 31 ++++++++++++++++--- .../tests/struct_init_update_type_err.vv | 17 ++++++++-- 3 files changed, 49 insertions(+), 12 deletions(-) diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index c5429fe32a..fd74e50ab1 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -680,12 +680,17 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) table.Type { if struct_init.has_update_expr { update_type := c.expr(struct_init.update_expr) struct_init.update_expr_type = update_type - update_sym := c.table.get_type_symbol(update_type) - sym := c.table.get_type_symbol(struct_init.typ) - if update_sym.kind != .struct_ { - c.error('expected struct `$sym.name`, found `$update_sym.name`', struct_init.update_expr.position()) + if c.table.type_kind(update_type) != .struct_ { + 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() { + // 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()) } } return struct_init.typ 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 c75fb0abdb..b8749f9e78 100644 --- a/vlib/v/checker/tests/struct_init_update_type_err.out +++ b/vlib/v/checker/tests/struct_init_update_type_err.out @@ -1,7 +1,28 @@ -vlib/v/checker/tests/struct_init_update_type_err.vv:11:6: error: expected struct `Foo`, found `int` - 9 | f3 := 2 +vlib/v/checker/tests/struct_init_update_type_err.vv:11:6: error: expected struct, found `int` + 9 | i := 2 10 | _ := Foo{ - 11 | ...f3 - | ~~ + 11 | ...i + | ^ 12 | name: 'f2' - 13 | } \ No newline at end of file + 13 | } +vlib/v/checker/tests/struct_init_update_type_err.vv:16:6: error: expected struct, found `&int` + 14 | p := &i + 15 | _ = Foo{ + 16 | ...p + | ^ + 17 | } + 18 | f2 := Foo2{} +vlib/v/checker/tests/struct_init_update_type_err.vv:20:6: error: expected struct `Foo`, found struct `Foo2` + 18 | f2 := Foo2{} + 19 | _ = Foo{ + 20 | ...f2 + | ~~ + 21 | } + 22 | _ = Foo{ +vlib/v/checker/tests/struct_init_update_type_err.vv:23:6: error: expression is not an lvalue + 21 | } + 22 | _ = Foo{ + 23 | ...Foo{} + | ~~~~~ + 24 | } + 25 | } 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 a7988d9da7..2bd876c786 100644 --- a/vlib/v/checker/tests/struct_init_update_type_err.vv +++ b/vlib/v/checker/tests/struct_init_update_type_err.vv @@ -3,13 +3,24 @@ struct Foo { age int } -struct Foo2 {} +struct Foo2 {b bool} fn main() { - f3 := 2 + i := 2 _ := Foo{ - ...f3 + ...i name: 'f2' } + p := &i + _ = Foo{ + ...p + } + f2 := Foo2{} + _ = Foo{ + ...f2 + } + _ = Foo{ + ...Foo{} + } }