From c62439de342ce9c6fd66eaa8938639042b4e50c8 Mon Sep 17 00:00:00 2001 From: CreeperFace <165158232+dy-tea@users.noreply.github.com> Date: Mon, 6 Oct 2025 08:37:49 +0100 Subject: [PATCH] checker: always use ... prefix when passing array to vararg (#25428) --- vlib/v/checker/fn.v | 72 +++++++++++-------- .../tests/sumtype_array_to_variadic.out | 7 ++ .../tests/sumtype_array_to_variadic.vv | 38 ++++++++++ .../fns/call_args_variadic_sumtype_test.v | 2 +- vlib/v/tests/fns/variadic_sumtype_test.v | 5 ++ 5 files changed, 93 insertions(+), 31 deletions(-) create mode 100644 vlib/v/checker/tests/sumtype_array_to_variadic.out create mode 100644 vlib/v/checker/tests/sumtype_array_to_variadic.vv diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index e33b6150a..d0632dea0 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -1528,21 +1528,9 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast. c.expr(mut call_arg.expr) } if i == args_len - 1 { - if c.table.sym(typ).kind == .array && call_arg.expr !is ast.ArrayDecompose - && c.table.sym(expected_type).kind !in [.sum_type, .interface] - && !param.typ.has_flag(.generic) && expected_type != typ { - styp := c.table.type_to_str(typ) - elem_styp := c.table.type_to_str(expected_type) - c.error('to pass `${call_arg.expr}` (${styp}) to `${func.name}` (which accepts type `...${elem_styp}`), use `...${call_arg.expr}`', - node.pos) - } else if call_arg.expr is ast.ArrayDecompose - && c.table.sym(expected_type).kind == .sum_type - && expected_type.idx() != typ.idx() { - expected_type_str := c.table.type_to_str(expected_type) - got_type_str := c.table.type_to_str(typ) - c.error('cannot use `...${got_type_str}` as `...${expected_type_str}` in argument ${ - i + 1} to `${fn_name}`', call_arg.pos) - } + c.check_variadic_arg(call_arg.expr, typ, expected_type, param.typ, i + 1, + func.name, func.is_method, func.is_variadic, args_len == 1 && i == 0, + node.pos, call_arg.pos) } } else { c.expected_type = param.typ @@ -2553,21 +2541,9 @@ fn (mut c Checker) method_call(mut node ast.CallExpr, mut continue_check &bool) } typ := c.expr(mut arg.expr) if i == node.args.len - 1 { - if c.table.sym(typ).kind == .array && arg.expr !is ast.ArrayDecompose - && c.table.sym(expected_type).kind !in [.sum_type, .interface] - && !param.typ.has_flag(.generic) && expected_type != typ { - styp := c.table.type_to_str(typ) - elem_styp := c.table.type_to_str(expected_type) - c.error('to pass `${arg.expr}` (${styp}) to `${method.name}` (which accepts type `...${elem_styp}`), use `...${arg.expr}`', - node.pos) - } else if arg.expr is ast.ArrayDecompose - && c.table.sym(expected_type).kind == .sum_type - && expected_type.idx() != typ.idx() { - expected_type_str := c.table.type_to_str(expected_type) - got_type_str := c.table.type_to_str(typ) - c.error('cannot use `...${got_type_str}` as `...${expected_type_str}` in argument ${ - i + 1} to `${method_name}`', arg.pos) - } + c.check_variadic_arg(arg.expr, typ, expected_type, param.typ, i + 1, method.name, + true, method.is_variadic, node.args.len == 1 && i == 0, node.pos, + arg.pos) } } else { c.expected_type = param.typ @@ -3991,3 +3967,39 @@ fn (mut c Checker) has_veb_context(typ ast.Type) bool { } return false } + +fn (mut c Checker) check_variadic_arg(arg_expr ast.Expr, typ ast.Type, expected_type ast.Type, param_typ ast.Type, + arg_num int, fn_name string, is_method bool, fn_is_variadic bool, is_single_array_arg bool, + call_pos token.Pos, arg_pos token.Pos) { + kind := c.table.sym(typ).kind + is_decompose := arg_expr is ast.ArrayDecompose + mut allow_variadic_pass := false + if arg_expr is ast.Ident && !is_method { + ident := arg_expr as ast.Ident + if ident.obj is ast.Var { + var_obj := ident.obj as ast.Var + if var_obj.is_arg && c.table.cur_fn != unsafe { nil } && c.table.cur_fn.is_variadic + && c.table.cur_fn.params.len > 0 && ident.name == c.table.cur_fn.params.last().name + && fn_is_variadic { + cur_fn_param_sym := c.table.sym(c.table.cur_fn.params.last().typ) + if cur_fn_param_sym.info is ast.Array { + allow_variadic_pass = cur_fn_param_sym.info.elem_type == expected_type + } + } + } + } + styp := c.table.type_to_str(typ) + elem_styp := c.table.type_to_str(expected_type) + expected_kind := c.table.sym(expected_type).kind + sum_type_needs_spread := expected_kind == .sum_type && is_single_array_arg + if kind == .array && !is_decompose && !allow_variadic_pass + && (expected_kind !in [.sum_type, .interface] || sum_type_needs_spread) + && !param_typ.has_flag(.generic) && expected_type != typ { + c.error('to pass `${arg_expr}` (${styp}) to `${fn_name}` (which accepts type `...${elem_styp}`), use `...${arg_expr}`', + call_pos) + } else if is_decompose && !param_typ.has_flag(.generic) && expected_kind != .interface + && expected_type.idx() != typ.idx() && typ != ast.void_type { + c.error('cannot use `...${styp}` as `...${elem_styp}` in argument ${arg_num} to `${fn_name}`', + arg_pos) + } +} diff --git a/vlib/v/checker/tests/sumtype_array_to_variadic.out b/vlib/v/checker/tests/sumtype_array_to_variadic.out new file mode 100644 index 000000000..d487fa13a --- /dev/null +++ b/vlib/v/checker/tests/sumtype_array_to_variadic.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/sumtype_array_to_variadic.vv:32:9: error: to pass `kv_pairs` ([]Value) to `Map__static__create` (which accepts type `...Value`), use `...kv_pairs` + 30 | } + 31 | assert kv_pairs.len % 2 == 0 + 32 | return Map.create(kv_pairs) + | ~~~~~~~~~~~~~~~~~~~~ + 33 | } + 34 | diff --git a/vlib/v/checker/tests/sumtype_array_to_variadic.vv b/vlib/v/checker/tests/sumtype_array_to_variadic.vv new file mode 100644 index 000000000..711374ae7 --- /dev/null +++ b/vlib/v/checker/tests/sumtype_array_to_variadic.vv @@ -0,0 +1,38 @@ +type Nil = u8 + +const c_nil = Nil(`\0`) + +pub type Value = Nil | int | string | []Value | Map + +pub fn (v Value) string() string { + return '${v.str()}' +} + +pub struct Map { +mut: + m map[string]Value +} + +pub fn Map.create(values ...Value) Map { + assert values.len > 1 + assert values.len % 2 == 0 + mut m := Map{} + for i := 0; i < values.len; i += 2 { + m.m[values[i].string()] = values[i + 1] + } + return m +} + +pub fn build_map(index_from int, index_to int) Map { + mut kv_pairs := []Value{len: index_to - index_from, init: Value(c_nil)} + for i := index_from; i < index_to; i++ { + kv_pairs[i - index_from] = Value(i) + } + assert kv_pairs.len % 2 == 0 + return Map.create(kv_pairs) +} + +fn main() { + m := build_map(2, 4) + _ = m +} diff --git a/vlib/v/tests/fns/call_args_variadic_sumtype_test.v b/vlib/v/tests/fns/call_args_variadic_sumtype_test.v index 4895872a2..4de44ec20 100644 --- a/vlib/v/tests/fns/call_args_variadic_sumtype_test.v +++ b/vlib/v/tests/fns/call_args_variadic_sumtype_test.v @@ -20,5 +20,5 @@ fn test_main() { } mut animals := []Animal{} animals << cat - print_names(animals) + print_names(...animals) } diff --git a/vlib/v/tests/fns/variadic_sumtype_test.v b/vlib/v/tests/fns/variadic_sumtype_test.v index 5d9c1332d..79589f921 100644 --- a/vlib/v/tests/fns/variadic_sumtype_test.v +++ b/vlib/v/tests/fns/variadic_sumtype_test.v @@ -14,6 +14,11 @@ fn div(params ...Either) int { return tag(params) } +fn div2(params ...Either) int { + return tag(...params) +} + fn test_main() { assert dump(div('foo', 1, 2)) == 3 + assert dump(div2(6, -2, 'hello', 10)) == 14 } -- 2.39.5