From 17228e2121f47dd31e6d5845c98680234fc29c64 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Wed, 15 Apr 2026 01:44:22 +0300 Subject: [PATCH] checker: fix private C definition causing compile error if importing `os` earlier than others (fixes #15811) --- vlib/v/ast/table.v | 5 +++ vlib/v/checker/checker.v | 34 ++++++++++++++++--- vlib/v/checker/fn.v | 5 +++ vlib/v/tests/project_issue_15811/a/a.c.v | 6 ++++ vlib/v/tests/project_issue_15811/b/b.c.v | 18 ++++++++++ vlib/v/tests/project_issue_15811/main.v | 8 +++++ vlib/v/tests/project_issue_15811/v.mod | 3 ++ vlib/v/tests/project_issue_15811_check_test.v | 9 +++++ 8 files changed, 84 insertions(+), 4 deletions(-) create mode 100644 vlib/v/tests/project_issue_15811/a/a.c.v create mode 100644 vlib/v/tests/project_issue_15811/b/b.c.v create mode 100644 vlib/v/tests/project_issue_15811/main.v create mode 100644 vlib/v/tests/project_issue_15811/v.mod create mode 100644 vlib/v/tests/project_issue_15811_check_test.v diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index c88422855..506bd8a12 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -1142,6 +1142,11 @@ fn (mut t Table) rewrite_already_registered_symbol(typ TypeSymbol, existing_idx } return existing_idx } + // Keep concrete C type re-declarations so later modules can resolve their own + // symbol instead of inheriting the first module's private metadata. + if existing_symbol.language == .c && typ.language == .c { + return -2 + } // Override the already registered builtin types with the actual // v struct declarations in the vlib/builtin module sources: if (existing_idx >= string_type_idx && existing_idx <= map_type_idx) diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index e1f82d19a..d80b719b5 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -943,6 +943,7 @@ fn (mut c Checker) alias_type_decl(mut node ast.AliasTypeDecl) { if c.file.mod.name != 'builtin' && !node.name.starts_with('C.') { c.check_valid_pascal_case(node.name, 'type alias', node.pos) } + node.parent_type = c.preferred_c_symbol_type(node.parent_type) if c.pref.is_vls && c.pref.linfo.method == .definition { if c.vls_is_the_node(node.type_pos) { typ_str := c.table.type_to_str(node.parent_type) @@ -1079,6 +1080,30 @@ fn (mut c Checker) alias_type_decl(mut node ast.AliasTypeDecl) { } } +fn (c &Checker) preferred_c_symbol_type(typ ast.Type) ast.Type { + sym := c.table.sym(typ) + if sym.language != .c || sym.name == '' { + return typ + } + mut public_typ := ast.invalid_type + for i := c.table.type_symbols.len - 1; i >= 1; i-- { + candidate := c.table.type_symbols[i] + if candidate.language != .c || candidate.name != sym.name || candidate.kind == .placeholder { + continue + } + if candidate.mod == c.mod { + return ast.new_type(i).derive(typ) + } + if candidate.is_pub && public_typ == ast.invalid_type { + public_typ = ast.new_type(i).derive(typ) + } + } + if public_typ != ast.invalid_type { + return public_typ + } + return typ +} + fn (mut c Checker) check_alias_vs_element_type_of_parent(node ast.AliasTypeDecl, element_type_of_parent ast.Type, label string) { if node.typ.idx() != element_type_of_parent.idx() { @@ -8075,7 +8100,8 @@ fn (mut c Checker) ensure_generic_type_specify_type_names(typ ast.Type, pos toke } fn (mut c Checker) ensure_type_exists(typ ast.Type, pos token.Pos) bool { - if typ == 0 { + mut checked_typ := c.preferred_c_symbol_type(typ) + if checked_typ == 0 { c.error('unknown type', pos) return c.pref.is_vls } @@ -8088,9 +8114,9 @@ fn (mut c Checker) ensure_type_exists(typ ast.Type, pos token.Pos) bool { pos) return c.pref.is_vls } - sym := c.table.sym(typ) + sym := c.table.sym(checked_typ) if !c.is_builtin_mod && !sym.is_pub && sym.mod != c.mod && sym.mod != 'main' - && typ !in c.table.cur_concrete_types { + && checked_typ !in c.table.cur_concrete_types { if sym.kind == .function { fn_info := sym.info as ast.FnType // hack: recover fn mod from func name @@ -8199,7 +8225,7 @@ fn (mut c Checker) ensure_type_exists(typ ast.Type, pos token.Pos) bool { else {} } if sym.kind == .map { - if !c.ensure_supported_map_key_types(typ, pos) { + if !c.ensure_supported_map_key_types(checked_typ, pos) { return c.pref.is_vls } } diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index 8217e9334..cbceadb63 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -569,6 +569,10 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) { if node.language == .v { // Make sure all types are valid for mut param in node.params { + param.typ = c.preferred_c_symbol_type(param.typ) + if mut scoped_param := node.scope.find_var(param.name) { + scoped_param.typ = param.typ + } // handle vls go to definition for parameter types if c.pref.is_vls && c.pref.linfo.method == .definition { if c.vls_is_the_node(param.type_pos) { @@ -703,6 +707,7 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) { } } if node.return_type != ast.no_type { + node.return_type = c.preferred_c_symbol_type(node.return_type) if !c.ensure_type_exists(node.return_type, node.return_type_pos) { return } diff --git a/vlib/v/tests/project_issue_15811/a/a.c.v b/vlib/v/tests/project_issue_15811/a/a.c.v new file mode 100644 index 000000000..ddea660b2 --- /dev/null +++ b/vlib/v/tests/project_issue_15811/a/a.c.v @@ -0,0 +1,6 @@ +module a + +@[typedef] +struct C.dupe { + old int +} diff --git a/vlib/v/tests/project_issue_15811/b/b.c.v b/vlib/v/tests/project_issue_15811/b/b.c.v new file mode 100644 index 000000000..2c051a5b4 --- /dev/null +++ b/vlib/v/tests/project_issue_15811/b/b.c.v @@ -0,0 +1,18 @@ +module b + +pub fn take(x &C.dupe) int { + return x.new +} + +@[typedef] +pub struct C.dupe { + new int +} + +pub type Dupe = C.dupe + +pub fn make() &C.dupe { + return &C.dupe{ + new: 1 + } +} diff --git a/vlib/v/tests/project_issue_15811/main.v b/vlib/v/tests/project_issue_15811/main.v new file mode 100644 index 000000000..f5ed44533 --- /dev/null +++ b/vlib/v/tests/project_issue_15811/main.v @@ -0,0 +1,8 @@ +module main + +import a as _ +import b + +fn main() { + assert b.take(b.make()) == 1 +} diff --git a/vlib/v/tests/project_issue_15811/v.mod b/vlib/v/tests/project_issue_15811/v.mod new file mode 100644 index 000000000..e44dd0c1e --- /dev/null +++ b/vlib/v/tests/project_issue_15811/v.mod @@ -0,0 +1,3 @@ +Module { + name: 'project_issue_15811' +} diff --git a/vlib/v/tests/project_issue_15811_check_test.v b/vlib/v/tests/project_issue_15811_check_test.v new file mode 100644 index 000000000..0306d9801 --- /dev/null +++ b/vlib/v/tests/project_issue_15811_check_test.v @@ -0,0 +1,9 @@ +import os + +const vexe = os.quoted_path(@VEXE) +const issue_15811_project = os.join_path(os.dir(@FILE), 'project_issue_15811') + +fn test_private_c_redeclaration_order_checks_cleanly() { + res := os.execute('${vexe} -check ${os.quoted_path(issue_15811_project)}') + assert res.exit_code == 0, res.output +} -- 2.39.5