From e861b73eaf7ddad36ec4633eda3a4c2cfe2efc15 Mon Sep 17 00:00:00 2001 From: kbkpbot Date: Mon, 24 Nov 2025 01:29:13 +0800 Subject: [PATCH] parser: fix the language support for a nested anonymous C.struct (fix #25807) (#25789) --- vlib/builtin/cfns.c.v | 19 +++++++++++++-- vlib/v/ast/types.v | 3 ++- vlib/v/gen/c/auto_str_methods.v | 6 ++++- vlib/v/gen/c/cgen.v | 10 ++++++-- vlib/v/parser/parser.v | 1 + vlib/v/parser/struct.v | 12 +++++++++- .../c_structs/cstruct_nested_anon_test.c.v | 23 +++++++++++++++++++ vlib/v/tests/c_structs/deep.h | 16 +++++++++++++ 8 files changed, 83 insertions(+), 7 deletions(-) create mode 100644 vlib/v/tests/c_structs/cstruct_nested_anon_test.c.v create mode 100644 vlib/v/tests/c_structs/deep.h diff --git a/vlib/builtin/cfns.c.v b/vlib/builtin/cfns.c.v index e6445cb6f..7821f68eb 100644 --- a/vlib/builtin/cfns.c.v +++ b/vlib/builtin/cfns.c.v @@ -552,8 +552,23 @@ fn C.sysconf(name int) int // C.SYSTEM_INFO contains information about the current computer system. This includes the architecture and type of the processor, the number of processors in the system, the page size, and other such information. @[typedef] pub struct C.SYSTEM_INFO { - dwNumberOfProcessors u32 - dwPageSize u32 + // DUMMYUNIONNAME union { + // dwOemId u32 + // DUMMYSTRUCTNAME struct { + // pub: + // wProcessorArchitecture u16 + // wReserved u16 + // } + //} + dwPageSize u32 + lpMinimumApplicationAddress voidptr + lpMaximumApplicationAddress voidptr + dwActiveProcessorMask &u32 = unsafe { nil } + dwNumberOfProcessors u32 + dwProcessorType u32 + dwAllocationGranularity u32 + wProcessorLevel u16 + wProcessorRevision u16 } fn C.GetSystemInfo(&C.SYSTEM_INFO) diff --git a/vlib/v/ast/types.v b/vlib/v/ast/types.v index 855f84940..4a2e51ba9 100644 --- a/vlib/v/ast/types.v +++ b/vlib/v/ast/types.v @@ -1091,7 +1091,8 @@ pub fn (t &TypeSymbol) is_array_fixed() bool { pub fn (t &TypeSymbol) is_c_struct() bool { if t.info is Struct { - return t.language == .c + // `C___VAnonStruct` need special handle, it need to create a new struct + return t.language == .c && !t.info.is_anon } else if t.info is Alias { return global_table.final_sym(t.info.parent_type).is_c_struct() } diff --git a/vlib/v/gen/c/auto_str_methods.v b/vlib/v/gen/c/auto_str_methods.v index f3cc76e50..dc0d7eedb 100644 --- a/vlib/v/gen/c/auto_str_methods.v +++ b/vlib/v/gen/c/auto_str_methods.v @@ -1188,7 +1188,11 @@ fn struct_auto_str_func(sym &ast.TypeSymbol, lang ast.Language, _field_type ast. if sym.kind == .struct { if sym.info is ast.Struct && sym.info.is_anon && !_field_type.has_flag(.option) && !_field_type.has_flag(.shared_f) { - typed_obj := '*(${sym.cname}*)&(${obj})' + typed_obj := if lang == .c { + '(${sym.cname}*)&(${obj})' + } else { + '*(${sym.cname}*)&(${obj})' + } return '${fn_name}(${typed_obj})', true } } diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 6ad1023a2..5bf2c4313 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -1723,7 +1723,9 @@ fn (mut g Gen) cc_type(typ ast.Type, is_prefix_struct bool) string { styp = styp[3..] if sym.kind == .struct { info := sym.info as ast.Struct - if !info.is_typedef { + if info.is_anon { + styp = 'C__' + styp + } else if !info.is_typedef { styp = 'struct ${styp}' } } @@ -6925,7 +6927,11 @@ fn (mut g Gen) write_types(symbols []&ast.TypeSymbol) { mut struct_names := map[string]bool{} for sym in symbols { if sym.name.starts_with('C.') { - continue + if sym.info is ast.Struct && sym.info.is_anon { + // For `C___VAnonStruct`, we need to create a new struct to make auto_str work. + } else { + continue + } } if sym.kind == .none && (!g.pref.skip_unused || g.table.used_features.used_none > 0) { g.type_definitions.writeln('struct none {') diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 0c0caaa8c..4af4838a9 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -30,6 +30,7 @@ mut: peek_tok token.Token language ast.Language fn_language ast.Language // .c for `fn C.abcd()` declarations + struct_language ast.Language // for `struct C.abcd{ embedded struct/union }` declarations expr_level int // prevent too deep recursions for pathological programs inside_vlib_file bool // true for all vlib/ files inside_test_file bool // when inside _test.v or _test.vv file diff --git a/vlib/v/parser/struct.v b/vlib/v/parser/struct.v index d2063528e..65506410f 100644 --- a/vlib/v/parser/struct.v +++ b/vlib/v/parser/struct.v @@ -31,8 +31,18 @@ fn (mut p Parser) struct_decl(is_anon bool) ast.StructDecl { } else { p.check(.key_union) } - language := p.parse_language() + mut language := p.parse_language() name_pos := p.tok.pos() + if p.inside_struct_field_decl && language == .v { + // anon struct/union language should keep the same language of outside + language = p.struct_language + } else { + old_struct_language := p.struct_language + p.struct_language = language + defer(fn) { + p.struct_language = old_struct_language + } + } p.check_for_impure_v(language, name_pos) if p.disallow_declarations_in_script_mode() { return ast.StructDecl{} diff --git a/vlib/v/tests/c_structs/cstruct_nested_anon_test.c.v b/vlib/v/tests/c_structs/cstruct_nested_anon_test.c.v new file mode 100644 index 000000000..a638884b6 --- /dev/null +++ b/vlib/v/tests/c_structs/cstruct_nested_anon_test.c.v @@ -0,0 +1,23 @@ +module main + +#insert "@VMODROOT/deep.h" + +struct C.DeepStruct { + A1 int + S1 struct { + A2 int + S2 struct { + A3 int + S3 struct { + A4 int + } + } + } +} + +fn test_cstruct_nested_anon() { + x := C.DeepStruct{} + y := C.DeepStruct{} + dump(x) + assert x == y +} diff --git a/vlib/v/tests/c_structs/deep.h b/vlib/v/tests/c_structs/deep.h new file mode 100644 index 000000000..33fbf26cf --- /dev/null +++ b/vlib/v/tests/c_structs/deep.h @@ -0,0 +1,16 @@ +#ifndef __DEEP_H__ +#define __DEEP_H__ + +struct DeepStruct{ + int A1; + struct { + int A2; + struct { + int A3; + struct { + int A4; + } S3; + } S2; + } S1; +}; +#endif -- 2.39.5