From eaa108b9dc0ecf72b7b661eed6100b228e0f1e08 Mon Sep 17 00:00:00 2001 From: kbkpbot Date: Mon, 30 Mar 2026 08:44:41 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20C=20compilation=20error=20when=20using?= =?UTF-8?q?=20generics=20with=20multiple=20instantiat=E2=80=A6=20(#26786)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: C compilation error when using generics with multiple instantiations of struct field function pointer Fix for https://github.com/vlang/v/issues/26753 When calling a generic struct field function pointer inside a generic method, the expected argument types were not being properly resolved to concrete types. This caused incorrect (voidptr) casts in the generated C code. Changes: - fn.v: Properly unwrap generic field types for function pointer calls - fn.v: Handle field method calls when node.concrete_types is empty but inside a generic function (with proper generic type conversion using convert_generic_type) Added test case that verifies calling a struct field function pointer inside a generic method works correctly with multiple different type instantiations. * test: update new_generics_regression_test.v expected counts for new test Update expected test counts to account for the new test file: generic_struct_field_fn_with_multiple_instantiations_test.v The total count increases from 265 to 266, and passed count increases accordingly. Co-authored-by: Qwen-Coder --------- Co-authored-by: Qwen-Coder --- vlib/v/gen/c/fn.v | 29 +++++++++++++++++- .../v/generics/new_generics_regression_test.v | 4 +-- ...eld_fn_with_multiple_instantiations_test.v | 30 +++++++++++++++++++ 3 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 vlib/v/tests/generics/generic_struct_field_fn_with_multiple_instantiations_test.v diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index 54736ee8b..a4e864f6f 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -2035,7 +2035,7 @@ fn (mut g Gen) fn_call(node ast.CallExpr) { left_sym := g.table.sym(node.left_type) if node.is_field { if field := g.table.find_field_with_embeds(left_sym, node.name) { - fn_typ = field.typ + fn_typ = g.unwrap_generic(field.typ) } if node.is_unwrapped_fn_selector { fn_typ = fn_typ.clear_option_and_result() @@ -2576,6 +2576,33 @@ fn (mut g Gen) call_args(node ast.CallExpr) { } } } + if node.is_field && node.concrete_types.len == 0 && g.cur_fn != unsafe { nil } + && g.cur_fn.generic_names.len > 0 && g.cur_concrete_types.len > 0 { + left_sym := g.table.sym(node.left_type) + if field := g.table.find_field_with_embeds(left_sym, node.name) { + field_sym := g.table.sym(g.unwrap_generic(field.typ)) + if field_sym.kind == .function { + if field_sym.info is ast.FnType { + info := field_sym.info + for i in 0 .. expected_types.len { + if i < info.func.params.len { + mut param_typ := info.func.params[i].typ + if param_typ.has_flag(.generic) { + if utyp := g.table.convert_generic_type(param_typ, g.cur_fn.generic_names, + g.cur_concrete_types) + { + param_typ = utyp + } + } + if expected_types[i] != param_typ { + expected_types[i] = param_typ + } + } + } + } + } + } + } if node.is_method { left_sym := g.table.sym(node.left_type) if left_sym.info is ast.Struct && left_sym.info.generic_types.len > 0 diff --git a/vlib/v/generics/new_generics_regression_test.v b/vlib/v/generics/new_generics_regression_test.v index b4c77a619..31754bfe8 100644 --- a/vlib/v/generics/new_generics_regression_test.v +++ b/vlib/v/generics/new_generics_regression_test.v @@ -79,8 +79,8 @@ fn run_new_generic_solver_tests(root_label string, test_cmd string, expected_sum println('') } -const expected_summsvc_generics = 'Summary for all V _test.v files: 53 failed, 212 passed, 265 total.' -const expected_summary_generics = 'Summary for all V _test.v files: 52 failed, 213 passed, 265 total.' +const expected_summsvc_generics = 'Summary for all V _test.v files: 53 failed, 213 passed, 266 total.' +const expected_summary_generics = 'Summary for all V _test.v files: 52 failed, 214 passed, 266 total.' const expected_summsvc_vec = 'Summary for all V _test.v files: 3 failed, 3 total.' const expected_summary_vec = 'Summary for all V _test.v files: 3 failed, 3 total.' const expected_summsvc_flag = 'Summary for all V _test.v files: 14 failed, 5 passed, 19 total.' diff --git a/vlib/v/tests/generics/generic_struct_field_fn_with_multiple_instantiations_test.v b/vlib/v/tests/generics/generic_struct_field_fn_with_multiple_instantiations_test.v new file mode 100644 index 000000000..022ffa244 --- /dev/null +++ b/vlib/v/tests/generics/generic_struct_field_fn_with_multiple_instantiations_test.v @@ -0,0 +1,30 @@ +struct Foo[T] { +mut: + field_fn fn (T) T = unsafe { nil } +} + +fn (f &Foo[T]) call_field_fn(x T) T { + return f.field_fn(x) +} + +fn test_generic_struct_field_fn_with_multiple_instantiations() { + mut foo_int := Foo[int]{ + field_fn: fn (x int) int { + return x + 1 + } + } + mut foo_string := Foo[string]{ + field_fn: fn (x string) string { + return x + ' world' + } + } + + result1 := foo_int.call_field_fn(2) + result2 := foo_string.call_field_fn('joe') + + println(result1) + println(result2) + + assert result1 == 3 + assert result2 == 'joe world' +} -- 2.39.5