From 9e8f69efbae14402ca57b93f97c8737ed12279c1 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Wed, 25 Mar 2026 16:42:16 +0300 Subject: [PATCH] cgen: fix not finding structure defined in a separate file (fixes #15724) --- vlib/v/gen/c/cgen.v | 22 +++++++++++++++++++ vlib/v/tests/project_issue_15724/main.v | 10 +++++++++ .../project_compiles_test.v | 5 +++++ vlib/v/tests/project_issue_15724/v.mod | 0 vlib/v/tests/project_issue_15724/z_decl.v | 5 +++++ 5 files changed, 42 insertions(+) create mode 100644 vlib/v/tests/project_issue_15724/main.v create mode 100644 vlib/v/tests/project_issue_15724/project_compiles_test.v create mode 100644 vlib/v/tests/project_issue_15724/v.mod create mode 100644 vlib/v/tests/project_issue_15724/z_decl.v diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 8007f9752..25a6f029e 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -8776,12 +8776,30 @@ fn (mut g Gen) ensure_fixed_array_option_definition(elem_type ast.Type) bool { return true } +fn (g &Gen) should_emit_private_c_struct(sym &ast.TypeSymbol, info ast.Struct) bool { + if sym.language != .c || sym.kind != .struct || info.is_anon || info.is_typedef { + return false + } + if !sym.name.all_after('C.').starts_with('_') { + return false + } + if info.name_pos.file_idx < 0 || int(info.name_pos.file_idx) >= g.table.filelist.len { + return false + } + decl_path := g.table.filelist[info.name_pos.file_idx] + return !decl_path.ends_with('.c.v') +} + 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.') { 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 if sym.info is ast.Struct && g.should_emit_private_c_struct(sym, sym.info) { + // Private C tags like `C._gpgme_key` are often only forward-declared in headers. + // When they are defined in a plain `.v` file, cgen needs to emit the backing + // struct body instead of assuming the C headers will provide it. } else { continue } @@ -8793,6 +8811,10 @@ fn (mut g Gen) write_types(symbols []&ast.TypeSymbol) { g.typedefs.writeln('typedef struct none none;') } mut name := sym.scoped_cname() + if sym.name.starts_with('C.') && sym.info is ast.Struct + && g.should_emit_private_c_struct(sym, sym.info) { + name = sym.name.all_after('C.') + } if g.pref.skip_unused && g.table.used_features.used_maps == 0 { if name in ['map', 'mapnode', 'SortedMap', 'MapMode', 'DenseArray'] { continue diff --git a/vlib/v/tests/project_issue_15724/main.v b/vlib/v/tests/project_issue_15724/main.v new file mode 100644 index 000000000..e0be177fa --- /dev/null +++ b/vlib/v/tests/project_issue_15724/main.v @@ -0,0 +1,10 @@ +module main + +fn get_key_refcount() u32 { + key := &C._gpgme_key(unsafe { nil }) + return key._refs +} + +fn main() { + assert get_key_refcount() == 0 +} diff --git a/vlib/v/tests/project_issue_15724/project_compiles_test.v b/vlib/v/tests/project_issue_15724/project_compiles_test.v new file mode 100644 index 000000000..edb1e414d --- /dev/null +++ b/vlib/v/tests/project_issue_15724/project_compiles_test.v @@ -0,0 +1,5 @@ +module main + +fn test_compiles() { + assert true +} diff --git a/vlib/v/tests/project_issue_15724/v.mod b/vlib/v/tests/project_issue_15724/v.mod new file mode 100644 index 000000000..e69de29bb diff --git a/vlib/v/tests/project_issue_15724/z_decl.v b/vlib/v/tests/project_issue_15724/z_decl.v new file mode 100644 index 000000000..7823d348b --- /dev/null +++ b/vlib/v/tests/project_issue_15724/z_decl.v @@ -0,0 +1,5 @@ +module main + +struct C._gpgme_key { + _refs u32 +} -- 2.39.5