From 506b64303e97ecc36fd4634cd2bb05a20ecd98b5 Mon Sep 17 00:00:00 2001 From: Felipe Pena <felipensp@gmail.com> Date: Tue, 4 Mar 2025 09:06:47 -0300 Subject: [PATCH 1/4] implement --- vlib/v/checker/struct.v | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/vlib/v/checker/struct.v b/vlib/v/checker/struct.v index 4ca2edb277beff..bc433d2d1fd8d9 100644 --- a/vlib/v/checker/struct.v +++ b/vlib/v/checker/struct.v @@ -534,7 +534,7 @@ fn (mut c Checker) struct_init(mut node ast.StructInit, is_field_zero_struct_ini && c.table.cur_concrete_types.len == 0 { pos := type_sym.name.last_index_u8(`.`) first_letter := type_sym.name[pos + 1] - if !first_letter.is_capital() + if !first_letter.is_capital() && type_sym.kind != .none && (type_sym.kind != .struct || !(type_sym.info is ast.Struct && type_sym.info.is_anon)) && type_sym.kind != .placeholder { c.error('cannot initialize builtin type `${type_sym.name}`', node.pos) @@ -850,6 +850,34 @@ or use an explicit `unsafe{ a[..] }`, if you do not want a copy of the slice.', inited_fields) } } + .none { + // var := struct { name: "" } + mut init_fields := []ast.StructField{} + for init_field in node.init_fields { + mut expr := unsafe { init_field } + init_fields << ast.StructField{ + name: init_field.name + typ: c.expr(mut expr.expr) + } + } + c.table.anon_struct_counter++ + name := '_VAnonStruct${c.table.anon_struct_counter}' + sym_struct := ast.TypeSymbol{ + kind: .struct + language: .v + name: name + cname: util.no_dots(name) + mod: c.mod + info: ast.Struct{ + is_anon: true + fields: init_fields + } + is_pub: true + } + ret := c.table.register_sym(sym_struct) + c.table.register_anon_struct(name, ret) + node.typ = c.table.find_type_idx(name) + } else {} } if node.has_update_expr { From 9c7d6639be9f5f5696f4333c7bd59bee3b54a29e Mon Sep 17 00:00:00 2001 From: Felipe Pena <felipensp@gmail.com> Date: Tue, 4 Mar 2025 17:33:35 -0300 Subject: [PATCH 2/4] implement --- vlib/v/checker/assign.v | 6 ++++++ vlib/v/checker/checker.v | 1 + vlib/v/checker/fn.v | 5 +++++ vlib/v/checker/struct.v | 19 +++++++++++++++++-- vlib/v/gen/c/fn.v | 2 ++ 5 files changed, 31 insertions(+), 2 deletions(-) diff --git a/vlib/v/checker/assign.v b/vlib/v/checker/assign.v index 89c1c165f9d535..6bbbc20cb0d876 100644 --- a/vlib/v/checker/assign.v +++ b/vlib/v/checker/assign.v @@ -199,6 +199,12 @@ fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) { } c.inside_decl_rhs = is_decl mut expr := node.right[i] + if left is ast.Ident && left.is_mut() && expr is ast.StructInit && expr.is_anon { + c.anon_struct_should_be_mut = true + defer { + c.anon_struct_should_be_mut = false + } + } right_type := c.expr(mut expr) c.inside_decl_rhs = false c.inside_ref_lit = old_inside_ref_lit diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index dfdea9de31a550..4a8f7fa212de8f 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -89,6 +89,7 @@ pub mut: inside_fn_arg bool // `a`, `b` in `a.f(b)` inside_ct_attr bool // true inside `[if expr]` inside_x_is_type bool // true inside the Type expression of `if x is Type {` + anon_struct_should_be_mut bool // true when `mut var := struct { ... }` is used inside_generic_struct_init bool inside_integer_literal_cast bool // true inside `int(123)` cur_struct_generic_types []ast.Type diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index 1205fc20803fe3..30f1f933393157 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -1654,6 +1654,11 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast. } } continue + } else if param_typ_sym.info is ast.Struct && arg_typ_sym.info is ast.Struct + && param_typ_sym.info.is_anon { + if c.is_anon_struct_compatible(param_typ_sym.info, arg_typ_sym.info) { + continue + } } if c.pref.translated || c.file.is_translated { // in case of variadic make sure to use array elem type for checks diff --git a/vlib/v/checker/struct.v b/vlib/v/checker/struct.v index bc433d2d1fd8d9..645103c566ef1c 100644 --- a/vlib/v/checker/struct.v +++ b/vlib/v/checker/struct.v @@ -856,8 +856,9 @@ or use an explicit `unsafe{ a[..] }`, if you do not want a copy of the slice.', for init_field in node.init_fields { mut expr := unsafe { init_field } init_fields << ast.StructField{ - name: init_field.name - typ: c.expr(mut expr.expr) + name: init_field.name + typ: c.expr(mut expr.expr) + is_mut: c.anon_struct_should_be_mut } } c.table.anon_struct_counter++ @@ -1168,3 +1169,17 @@ fn (mut c Checker) check_ref_fields_initialized_note(struct_sym &ast.TypeSymbol, } } } + +fn (mut c Checker) is_anon_struct_compatible(s1 ast.Struct, s2 ast.Struct) bool { + if !(s1.is_anon && s2.is_anon && s1.fields.len == s2.fields.len) { + return false + } + mut is_compatible := true + for k, field in s1.fields { + if !c.check_basic(field.typ, s2.fields[k].typ) { + is_compatible = false + break + } + } + return is_compatible +} diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index 00c87e7acf1a43..c2a2a8cc94ad79 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -2760,6 +2760,8 @@ fn (mut g Gen) ref_or_deref_arg(arg ast.CallArg, expected_type ast.Type, lang as g.expr_with_cast(arg.expr, arg_typ, expected_type) g.write('.data') return + } else if arg_sym.info is ast.Struct && arg_sym.info.is_anon { + g.write('*(${g.cc_type(expected_type, false)}*)&') } // check if the argument must be dereferenced or not g.arg_no_auto_deref = is_smartcast && !arg_is_ptr && !exp_is_ptr && arg.should_be_ptr From 217ee0b253b8e12da6ff49ebe0041ea090976cf9 Mon Sep 17 00:00:00 2001 From: Felipe Pena <felipensp@gmail.com> Date: Tue, 4 Mar 2025 18:41:18 -0300 Subject: [PATCH 3/4] fix --- vlib/v/gen/c/fn.v | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index c2a2a8cc94ad79..029a2a192cd520 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -2760,7 +2760,8 @@ fn (mut g Gen) ref_or_deref_arg(arg ast.CallArg, expected_type ast.Type, lang as g.expr_with_cast(arg.expr, arg_typ, expected_type) g.write('.data') return - } else if arg_sym.info is ast.Struct && arg_sym.info.is_anon { + } else if arg.expr is ast.Ident && arg_sym.info is ast.Struct && arg_sym.info.is_anon { + // make anon struct struct compatible with another anon struct declaration g.write('*(${g.cc_type(expected_type, false)}*)&') } // check if the argument must be dereferenced or not From 53833238e0514817fd7ee3b13083a4d5340c5060 Mon Sep 17 00:00:00 2001 From: Felipe Pena <felipensp@gmail.com> Date: Tue, 4 Mar 2025 19:13:58 -0300 Subject: [PATCH 4/4] add test --- .../structs/anon_struct_assign_expr_test.v | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 vlib/v/tests/structs/anon_struct_assign_expr_test.v diff --git a/vlib/v/tests/structs/anon_struct_assign_expr_test.v b/vlib/v/tests/structs/anon_struct_assign_expr_test.v new file mode 100644 index 00000000000000..b861f178998b16 --- /dev/null +++ b/vlib/v/tests/structs/anon_struct_assign_expr_test.v @@ -0,0 +1,25 @@ +fn t() int { + return 123 +} + +fn r(a struct { name string age int }) { + assert '${a}' == "struct { + name: 'Foo' + age: 123 +}" +} + +fn test_main() { + mut a := struct { + name: 'Foo' + age: t() + } + dump(a) + r(a) + r(struct { name: 'Foo', age: t() }) + a.age = 2 + assert '${a}' == "struct { + name: 'Foo' + age: 2 +}" +}