From 2b041128ad1d2e400aa60412eb30e0413f00c420 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Tue, 21 Apr 2026 06:00:28 +0300 Subject: [PATCH] checker: fix passing fixed array [n]t to generic function expecting []t (fixes #26899) --- vlib/v/checker/check_types.v | 7 +++ vlib/v/checker/fn.v | 59 +++++++++++++++++++++++- vlib/v/checker/tests/fn_args.out | 12 ++--- vlib/v/checker/tests/fn_args.vv | 2 +- vlib/v/tests/fixed_array_on_array_test.v | 25 ++++++++++ 5 files changed, 96 insertions(+), 9 deletions(-) diff --git a/vlib/v/checker/check_types.v b/vlib/v/checker/check_types.v index 678f902f1..e3daaef11 100644 --- a/vlib/v/checker/check_types.v +++ b/vlib/v/checker/check_types.v @@ -1205,6 +1205,10 @@ fn (mut c Checker) infer_composite_generic_type(gt_name string, generic_typ ast. return c.infer_composite_generic_type(gt_name, param_sym.info.elem_type, arg_sym.info.elem_type) } + if arg_sym.info is ast.ArrayFixed { + return c.infer_composite_generic_type(gt_name, param_sym.info.elem_type, + arg_sym.info.elem_type) + } } ast.ArrayFixed { if arg_sym.info is ast.ArrayFixed && param_sym.info.size == arg_sym.info.size { @@ -1718,6 +1722,9 @@ fn (mut c Checker) infer_fn_generic_types(func &ast.Fn, mut node ast.CallExpr) { break } } + } else if arg_sym.info is ast.ArrayFixed && param_sym.info is ast.Array { + typ = c.infer_composite_generic_type(gt_name, param_sym.info.elem_type, + arg_sym.info.elem_type) } else if arg_sym.info is ast.ArrayFixed && param_sym.info is ast.ArrayFixed { mut arg_elem_typ, mut param_elem_typ := arg_sym.info.elem_type, param_sym.info.elem_type mut arg_elem_sym, mut param_elem_sym := c.table.sym(arg_elem_typ), c.table.sym(param_elem_typ) diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index 3fa0483ff..20ddd69a0 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -2315,6 +2315,7 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast. } node.args[i].expr = call_arg.expr node.args[i].typ = arg_typ + call_arg.typ = arg_typ if c.comptime.comptime_for_field_var != '' { if mut call_arg.expr is ast.Ident && call_arg.expr.obj is ast.Var { node.args[i].typ = call_arg.expr.obj.typ @@ -2362,7 +2363,7 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast. node.args[i].typ = param.typ arg_typ = param.typ } - arg_typ_sym := c.table.sym(arg_typ) + mut arg_typ_sym := c.table.sym(arg_typ) if param.typ.has_flag(.generic) { if arg_typ_sym.kind == .none && !param.typ.has_flag(.option) { c.error('cannot use `none` as generic argument', call_arg.pos) @@ -2419,6 +2420,18 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast. c.fail_if_unreadable(call_arg.expr, arg_typ, 'argument') } } + array_param_typ := if func.generic_names.len > 0 && concrete_types.len > 0 { + c.table.convert_generic_param_type(param, func.generic_names, concrete_types) or { + param.typ + } + } else { + param.typ + } + arg_typ = c.lower_fixed_array_call_arg_to_array(mut call_arg, array_param_typ, + node.language) + node.args[i] = call_arg + node.args[i].typ = arg_typ + arg_typ_sym = c.table.sym(arg_typ) mut final_param_sym := unsafe { param_typ_sym } mut final_param_typ := param.typ if func.is_variadic && param_typ_sym.info is ast.Array { @@ -2718,7 +2731,7 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast. c.expected_type = param.typ } already_checked := node.language != .js && call_arg.expr is ast.CallExpr - typ := c.check_expr_option_or_result_call(call_arg.expr, if already_checked + mut typ := c.check_expr_option_or_result_call(call_arg.expr, if already_checked && mut call_arg.expr is ast.CallExpr { call_arg.expr.return_type } else { @@ -2729,6 +2742,11 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast. if unwrap_typ := c.table.convert_generic_param_type(param, func.generic_names, concrete_types) { + call_arg.typ = typ + typ = c.lower_fixed_array_call_arg_to_array(mut call_arg, unwrap_typ, + node.language) + node.args[i].expr = call_arg.expr + node.args[i].typ = typ utyp := c.unwrap_generic(typ) unwrap_sym := c.table.sym(unwrap_typ) if unwrap_sym.kind == .interface { @@ -2964,6 +2982,39 @@ fn (mut c Checker) is_optional_array_arg_compatible(got ast.Type, expected ast.T return c.check_types(got_value_type.clear_flag(.option), expected_value_type) } +fn (mut c Checker) fixed_array_arg_as_array_type(got ast.Type, expected ast.Type) ast.Type { + if expected.has_flag(.variadic) { + return ast.no_type + } + got_sym := c.table.final_sym(c.unwrap_generic(got).clear_option_and_result()) + expected_sym := + c.table.final_sym(c.unwrap_generic(expected).clear_option_and_result().set_nr_muls(0)) + if got_sym.kind != .array_fixed || expected_sym.kind != .array { + return ast.no_type + } + return ast.new_type(c.table.find_or_register_array(got_sym.array_fixed_info().elem_type)) +} + +fn (mut c Checker) lower_fixed_array_call_arg_to_array(mut arg ast.CallArg, expected ast.Type, language ast.Language) ast.Type { + array_typ := c.fixed_array_arg_as_array_type(arg.typ, expected) + if array_typ == ast.no_type { + return arg.typ + } + expected_array_typ := c.unwrap_generic(expected).clear_option_and_result().set_nr_muls(0) + c.check_expected_call_arg(array_typ, expected_array_typ, language, arg) or { return arg.typ } + original_expr := arg.expr + arg.expr = ast.IndexExpr{ + pos: original_expr.pos() + left: original_expr + left_type: arg.typ + index: ast.RangeExpr{ + pos: original_expr.pos() + } + } + arg.typ = c.expr(mut arg.expr) + return arg.typ +} + fn (mut c Checker) method_call(mut node ast.CallExpr, mut continue_check &bool) ast.Type { // `(if true { 'foo.bar' } else { 'foo.bar.baz' }).all_after('foo.')` node.concrete_types = node.raw_concrete_types.clone() @@ -3576,6 +3627,7 @@ fn (mut c Checker) method_call(mut node ast.CallExpr, mut continue_check &bool) mut got_arg_typ := c.check_expr_option_or_result_call(arg.expr, c.expr(mut arg.expr)) node.args[i].typ = got_arg_typ + arg.typ = got_arg_typ if no_type_promotion { if got_arg_typ != exp_arg_typ { c.error('cannot use `${c.table.sym(got_arg_typ).name}` as argument for `${method.name}` (`${exp_arg_sym.name}` expected)', @@ -3694,6 +3746,9 @@ fn (mut c Checker) method_call(mut node ast.CallExpr, mut continue_check &bool) } } } + got_arg_typ = c.lower_fixed_array_call_arg_to_array(mut arg, exp_arg_typ, node.language) + node.args[i] = arg + node.args[i].typ = got_arg_typ // Handle expected interface if final_arg_sym.kind == .interface { if c.type_implements_with_mut_receiver(got_arg_typ, final_arg_typ, arg.expr.pos(), diff --git a/vlib/v/checker/tests/fn_args.out b/vlib/v/checker/tests/fn_args.out index 319cc8eda..f73d65bf7 100644 --- a/vlib/v/checker/tests/fn_args.out +++ b/vlib/v/checker/tests/fn_args.out @@ -29,25 +29,25 @@ vlib/v/checker/tests/fn_args.vv:9:6: error: cannot use `&int` as `u8` in argumen 8 | v := 4 9 | uu8(&v) | ~~ - 10 | arr([5]!) + 10 | arr([5.0]!) 11 | fun(fn (i &int) {}) -vlib/v/checker/tests/fn_args.vv:10:6: error: cannot use `[1]int` as `[]int` in argument 1 to `arr` +vlib/v/checker/tests/fn_args.vv:10:6: error: cannot use `[1]f64` as `[]int` in argument 1 to `arr` 8 | v := 4 9 | uu8(&v) - 10 | arr([5]!) - | ~~~~ + 10 | arr([5.0]!) + | ~~~~~~ 11 | fun(fn (i &int) {}) 12 | fun(fn (ii ...int) {}) vlib/v/checker/tests/fn_args.vv:11:6: error: cannot use `fn (&int)` as `fn (int)` in argument 1 to `fun` 9 | uu8(&v) - 10 | arr([5]!) + 10 | arr([5.0]!) 11 | fun(fn (i &int) {}) | ~~~~~~~~~~~~~~ 12 | fun(fn (ii ...int) {}) 13 | } Details: expected argument 1 to be NOT a pointer, but the passed argument 1 is a pointer vlib/v/checker/tests/fn_args.vv:12:6: error: cannot use `fn (...int)` as `fn (int)` in argument 1 to `fun` - 10 | arr([5]!) + 10 | arr([5.0]!) 11 | fun(fn (i &int) {}) 12 | fun(fn (ii ...int) {}) | ~~~~~~~~~~~~~~~~~ diff --git a/vlib/v/checker/tests/fn_args.vv b/vlib/v/checker/tests/fn_args.vv index 71760cf59..f1bde8759 100644 --- a/vlib/v/checker/tests/fn_args.vv +++ b/vlib/v/checker/tests/fn_args.vv @@ -7,7 +7,7 @@ fn fun(a fn (int)) {} fn basic() { v := 4 uu8(&v) - arr([5]!) + arr([5.0]!) fun(fn (i &int) {}) fun(fn (ii ...int) {}) } diff --git a/vlib/v/tests/fixed_array_on_array_test.v b/vlib/v/tests/fixed_array_on_array_test.v index 2d2a7ec2d..5d87e2406 100644 --- a/vlib/v/tests/fixed_array_on_array_test.v +++ b/vlib/v/tests/fixed_array_on_array_test.v @@ -2,7 +2,32 @@ fn separate_wires(coo_adj_wires [][2]u32, id u64) [][2]u32 { return [][2]u32{len: coo_adj_wires.len, init: coo_adj_wires[index]} } +fn fixed_array_len(mut arr []int) int { + return arr.len +} + +fn fixed_array_generic_len[T](mut arr []T) int { + return arr.len +} + +fn fixed_array_generic_dispatch[T](mut val T) int { + $if T is $array_fixed { + return fixed_array_generic_len(mut val) + } + return -1 +} + fn test_main() { t := separate_wires([[u32(1), 2]!], 0) assert t.str() == '[[1, 2]]' } + +fn test_fixed_array_can_be_passed_to_array_param() { + mut fixed := [3]int{} + assert fixed_array_len(mut fixed) == 3 +} + +fn test_fixed_array_can_be_passed_to_generic_array_param() { + mut fixed := [3]int{} + assert fixed_array_generic_dispatch(mut fixed) == 3 +} -- 2.39.5