From 6d399c511635dc9fe7c7439e67ccb3a15f433da7 Mon Sep 17 00:00:00 2001 From: yuyi Date: Fri, 19 Aug 2022 00:39:41 +0800 Subject: [PATCH] checker: fix struct init with update expr (fix #9472) (#15460) --- vlib/v/checker/struct.v | 5 --- .../tests/struct_init_update_type_err.out | 7 ---- vlib/v/gen/c/struct.v | 23 +++++++++++-- vlib/v/tests/struct_init_with_update_test.v | 32 +++++++++++++++++++ 4 files changed, 53 insertions(+), 14 deletions(-) create mode 100644 vlib/v/tests/struct_init_with_update_test.v diff --git a/vlib/v/checker/struct.v b/vlib/v/checker/struct.v index 6b7447336e..44f6dd6879 100644 --- a/vlib/v/checker/struct.v +++ b/vlib/v/checker/struct.v @@ -563,11 +563,6 @@ pub fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type { node.update_expr.pos()) } } - if !node.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', node.update_expr.pos()) - } } return node.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 c0eab9e291..26ab0bf8b3 100644 --- a/vlib/v/checker/tests/struct_init_update_type_err.out +++ b/vlib/v/checker/tests/struct_init_update_type_err.out @@ -19,13 +19,6 @@ vlib/v/checker/tests/struct_init_update_type_err.vv:20:6: error: struct `Foo2` i | ~~ 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 | } 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{ diff --git a/vlib/v/gen/c/struct.v b/vlib/v/gen/c/struct.v index 40c9be0be0..babd6cbe3b 100644 --- a/vlib/v/gen/c/struct.v +++ b/vlib/v/gen/c/struct.v @@ -8,6 +8,20 @@ import v.ast const skip_struct_init = ['struct stat', 'struct addrinfo'] fn (mut g Gen) struct_init(node ast.StructInit) { + mut is_update_tmp_var := false + mut tmp_update_var := '' + if node.has_update_expr && !node.update_expr.is_lvalue() { + tmp_update_var = g.new_tmp_var() + is_update_tmp_var = true + s := g.go_before_stmt(0) + styp := g.typ(node.update_expr_type) + g.empty_line = true + g.write('$styp $tmp_update_var = ') + g.expr(node.update_expr) + g.writeln(';') + g.empty_line = false + g.write(s) + } styp := g.typ(node.typ) mut shared_styp := '' // only needed for shared x := St{... if styp in c.skip_struct_init { @@ -167,8 +181,8 @@ fn (mut g Gen) struct_init(node ast.StructInit) { // unions thould have exactly one explicit initializer continue } + field_name := c_name(field.name) if field.typ.has_flag(.optional) { - field_name := c_name(field.name) g.write('.$field_name = {EMPTY_STRUCT_INITIALIZATION},') initialized = true continue @@ -177,7 +191,12 @@ fn (mut g Gen) struct_init(node ast.StructInit) { continue } if node.has_update_expr { - g.expr(node.update_expr) + g.write('.$field_name = ') + if is_update_tmp_var { + g.write(tmp_update_var) + } else { + g.expr(node.update_expr) + } if node.update_expr_type.is_ptr() { g.write('->') } else { diff --git a/vlib/v/tests/struct_init_with_update_test.v b/vlib/v/tests/struct_init_with_update_test.v new file mode 100644 index 0000000000..c2c398e046 --- /dev/null +++ b/vlib/v/tests/struct_init_with_update_test.v @@ -0,0 +1,32 @@ +module main + +struct Author { + username string + name string + pass string + height int + age int +} + +fn cool_author() Author { + return Author{ + username: 'Terisback' + name: 'Bob' + pass: '123456' + height: 175 + age: 18 + } +} + +fn test_struct_init_with_update_expr() { + mut o := Author{ + ...cool_author() + age: 21 + } + println(o) + assert o.username == 'Terisback' + assert o.name == 'Bob' + assert o.pass == '123456' + assert o.height == 175 + assert o.age == 21 +}