From 405934fa01abb0c9ff45d8e551d923a3c5d19857 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Tue, 21 Apr 2026 15:01:07 +0300 Subject: [PATCH] checker: allow c declarations imported from an external module to be used for casting (fixes #26192) --- .gitignore | 2 ++ vlib/v/ast/ast.v | 1 + vlib/v/builder/builder.v | 18 ++++++++-- vlib/v/checker/fn.v | 33 +++++++++++++++++++ .../c_type_cast_imported_from_module.out | 1 + .../cmod/cmod.c.v | 5 +++ .../cmod/header.h | 1 + .../c_type_cast_imported_from_module/main.v | 7 ++++ vlib/v/gen/c/fn.v | 10 ++++++ 9 files changed, 75 insertions(+), 3 deletions(-) create mode 100644 vlib/v/checker/tests/modules/c_type_cast_imported_from_module.out create mode 100644 vlib/v/checker/tests/modules/c_type_cast_imported_from_module/cmod/cmod.c.v create mode 100644 vlib/v/checker/tests/modules/c_type_cast_imported_from_module/cmod/header.h create mode 100644 vlib/v/checker/tests/modules/c_type_cast_imported_from_module/main.v diff --git a/.gitignore b/.gitignore index 7f998a939..7a7b4addb 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,8 @@ !vlib/v/gen/c/testdata/nested_unary_minus_regression.vv !vlib/v/gen/c/testdata/nested_unary_minus_regression.out !vlib/v/tests/interfaces/interface_pointer_method_value_issue_22237_test.v +!vlib/v/checker/tests/modules/c_type_cast_imported_from_module/** +!vlib/v/checker/tests/modules/c_type_cast_imported_from_module.out # unignore c codegen testdata files !vlib/v/gen/c/testdata/*.vv diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 7ce5ff7c7..2d3e07aaf 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -913,6 +913,7 @@ pub mut: is_static_method bool // it is a static method call is_variadic bool is_c_variadic bool // it is a C variadic + is_c_type_cast bool // unresolved `C.Type(x)` reclassified by checker after imports are parsed args []CallArg expected_arg_types []Type comptime_ret_val bool diff --git a/vlib/v/builder/builder.v b/vlib/v/builder/builder.v index 52a8a36b3..962a70244 100644 --- a/vlib/v/builder/builder.v +++ b/vlib/v/builder/builder.v @@ -455,9 +455,21 @@ fn (mut b Builder) mark_imports_used_by_c_function_calls() { if used_c_calls.len == 0 { continue } - for c_fn_name, _ in used_c_calls { - c_fn := b.table.find_fn(c_fn_name) or { continue } - alias := import_alias_for_mod(file, c_fn.mod) or { continue } + for c_name, _ in used_c_calls { + if c_fn := b.table.find_fn(c_name) { + alias := import_alias_for_mod(file, c_fn.mod) or { continue } + register_used_import(mut file, alias) + continue + } + c_typ := b.table.find_type(c_name) + if c_typ == 0 { + continue + } + c_sym := b.table.sym(c_typ) + if c_sym.kind == .placeholder { + continue + } + alias := import_alias_for_mod(file, c_sym.mod) or { continue } register_used_import(mut file, alias) } } diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index a9e047377..3fa0483ff 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -1491,6 +1491,36 @@ fn (mut c Checker) builtin_args(mut node ast.CallExpr, fn_name string, func &ast */ } +fn (mut c Checker) try_resolve_c_type_cast_call(mut node ast.CallExpr) ?ast.Type { + if node.language != .c || !node.name.starts_with('C.') || node.args.len != 1 { + return none + } + c_name := node.name.all_after('C.') + if c_name.len == 0 || !c_name[0].is_capital() { + return none + } + to_type := c.table.find_type(node.name) + if to_type == 0 { + return none + } + to_sym := c.table.sym(to_type) + if to_sym.kind == .placeholder { + return none + } + mut cast_expr := ast.CastExpr{ + typ: to_type + typname: to_sym.name + expr: node.args[0].expr + pos: node.pos + } + typ := c.cast_expr(mut cast_expr) + node.is_c_type_cast = true + node.args[0].expr = cast_expr.expr + node.args[0].typ = cast_expr.expr_type + node.return_type = typ + return typ +} + fn (mut c Checker) needs_unwrap_generic_type(typ ast.Type) bool { if typ == 0 { return false @@ -2020,6 +2050,9 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast. } if !found { + if typ := c.try_resolve_c_type_cast_call(mut node) { + return typ + } if c.resolve_self_method_call(mut node) { return c.method_call(mut node, mut continue_check) } diff --git a/vlib/v/checker/tests/modules/c_type_cast_imported_from_module.out b/vlib/v/checker/tests/modules/c_type_cast_imported_from_module.out new file mode 100644 index 000000000..573541ac9 --- /dev/null +++ b/vlib/v/checker/tests/modules/c_type_cast_imported_from_module.out @@ -0,0 +1 @@ +0 diff --git a/vlib/v/checker/tests/modules/c_type_cast_imported_from_module/cmod/cmod.c.v b/vlib/v/checker/tests/modules/c_type_cast_imported_from_module/cmod/cmod.c.v new file mode 100644 index 000000000..869670fd8 --- /dev/null +++ b/vlib/v/checker/tests/modules/c_type_cast_imported_from_module/cmod/cmod.c.v @@ -0,0 +1,5 @@ +module cmod + +#include "header.h" + +pub type C.TEST = int diff --git a/vlib/v/checker/tests/modules/c_type_cast_imported_from_module/cmod/header.h b/vlib/v/checker/tests/modules/c_type_cast_imported_from_module/cmod/header.h new file mode 100644 index 000000000..db8bcf0e8 --- /dev/null +++ b/vlib/v/checker/tests/modules/c_type_cast_imported_from_module/cmod/header.h @@ -0,0 +1 @@ +typedef int TEST; diff --git a/vlib/v/checker/tests/modules/c_type_cast_imported_from_module/main.v b/vlib/v/checker/tests/modules/c_type_cast_imported_from_module/main.v new file mode 100644 index 000000000..5968d8a49 --- /dev/null +++ b/vlib/v/checker/tests/modules/c_type_cast_imported_from_module/main.v @@ -0,0 +1,7 @@ +module main + +import cmod + +fn main() { + println(C.TEST(0)) +} diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index 9bdb5e3ac..b444e2c67 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -1974,6 +1974,16 @@ fn (mut g Gen) call_expr(node ast.CallExpr) { if node.should_be_skipped { return } + if node.is_c_type_cast { + g.cast_expr(ast.CastExpr{ + typ: node.return_type + typname: g.table.sym(g.unwrap_generic(node.return_type)).name + expr: node.args[0].expr + expr_type: node.args[0].typ + pos: node.pos + }) + return + } is_shared := g.is_shared g.is_shared = false defer { -- 2.39.5