From 006d3ab74aededae71901a467a4f5e00c82296b7 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Wed, 25 Mar 2026 16:42:24 +0300 Subject: [PATCH] parser: fix initializing anonymous struct (fixes #16207) --- vlib/v/checker/struct.v | 15 +++++++++++++ vlib/v/parser/expr.v | 22 ++++++++++++++++++- .../structs/anon_struct_local_init_test.v | 12 ++++++++++ 3 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 vlib/v/tests/structs/anon_struct_local_init_test.v diff --git a/vlib/v/checker/struct.v b/vlib/v/checker/struct.v index 7c8133efd..c48dff31b 100644 --- a/vlib/v/checker/struct.v +++ b/vlib/v/checker/struct.v @@ -694,6 +694,21 @@ fn (mut c Checker) struct_init(mut node ast.StructInit, is_field_zero_struct_ini return ast.void_type } } + if c.anon_struct_should_be_mut { + mut anon_type_sym := c.table.sym(node.typ) + if anon_type_sym.info is ast.Struct && anon_type_sym.info.is_anon { + mut anon_info := anon_type_sym.info as ast.Struct + mut anon_fields := []ast.StructField{cap: anon_info.fields.len} + for field in anon_info.fields { + anon_fields << ast.StructField{ + ...field + is_mut: true + } + } + anon_info.fields = anon_fields + anon_type_sym.info = anon_info + } + } // Make sure the first letter is capital, do not allow e.g. `x := string{}`, // but `x := T{}` is ok. if !c.is_builtin_mod && !c.inside_unsafe && type_sym.language == .v diff --git a/vlib/v/parser/expr.v b/vlib/v/parser/expr.v index b33622efc..3004125ed 100644 --- a/vlib/v/parser/expr.v +++ b/vlib/v/parser/expr.v @@ -449,7 +449,9 @@ fn (mut p Parser) check_expr(precedence int) !ast.Expr { } } else { // Anonymous struct - p.next() + if !p.is_explicit_anon_struct_init() { + p.next() + } return p.struct_init('', .anon, false) } } else if p.tok.kind == .key_type { @@ -483,6 +485,24 @@ fn (mut p Parser) check_expr(precedence int) !ast.Expr { return p.expr_with_left(node, precedence, is_stmt_ident) } +fn (p &Parser) is_explicit_anon_struct_init() bool { + mut n := 2 + mut curlies := 1 + for curlies > 0 { + tok := p.peek_token(n) + if tok.kind == .eof { + return false + } + if tok.kind == .lcbr { + curlies++ + } else if tok.kind == .rcbr { + curlies-- + } + n++ + } + return p.peek_token(n).kind == .lcbr +} + fn (mut p Parser) parse_typeof_expr(start_pos token.Pos) ast.Expr { p.next() // typeof if p.tok.kind == .lsbr { diff --git a/vlib/v/tests/structs/anon_struct_local_init_test.v b/vlib/v/tests/structs/anon_struct_local_init_test.v new file mode 100644 index 000000000..19976ba3f --- /dev/null +++ b/vlib/v/tests/structs/anon_struct_local_init_test.v @@ -0,0 +1,12 @@ +fn test_anon_struct_as_local_variable() { + mut s := struct { + foo string + bar int + }{} + assert s.foo == '' + assert s.bar == 0 + s.foo = 'foo' + s.bar = 1 + assert s.foo == 'foo' + assert s.bar == 1 +} -- 2.39.5