From a88d0d603ff083365889823ef0f6ce45f14d68bf Mon Sep 17 00:00:00 2001 From: CreeperFace <165158232+dy-tea@users.noreply.github.com> Date: Fri, 28 Nov 2025 06:04:39 +0000 Subject: [PATCH] cgen,checker: improve handling of array decompose for dynamic arrays and array init (fix #25838) (#25843) --- vlib/v/checker/fn.v | 14 ++++++ .../array_init_decompose_extra_params.out | 20 ++++++++ .../array_init_decompose_extra_params.vv | 36 ++++++++++++++ vlib/v/gen/c/fn.v | 48 +++++++++++++++---- .../panic_array_decompose_extra_args.out | 8 ++++ .../inout/panic_array_decompose_extra_args.vv | 31 ++++++++++++ 6 files changed, 149 insertions(+), 8 deletions(-) create mode 100644 vlib/v/checker/tests/array_init_decompose_extra_params.out create mode 100644 vlib/v/checker/tests/array_init_decompose_extra_params.vv create mode 100644 vlib/v/slow_tests/inout/panic_array_decompose_extra_args.out create mode 100644 vlib/v/slow_tests/inout/panic_array_decompose_extra_args.vv diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index d22970324..00d298cc9 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -1524,6 +1524,20 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast. } } has_decompose = call_arg.expr is ast.ArrayDecompose + if !func.is_variadic && call_arg.expr is ast.ArrayDecompose { + if mut call_arg.expr is ast.ArrayDecompose { + if call_arg.expr.expr is ast.ArrayInit { + extra_params := func.params.len - i + array_init := call_arg.expr.expr as ast.ArrayInit + if array_init.exprs.len < extra_params { + elem_word := if array_init.exprs.len == 1 { 'element' } else { 'elements' } + verb_word := if extra_params == 1 { 'is' } else { 'are' } + c.error('array decompose has ${array_init.exprs.len} ${elem_word} but ${extra_params} ${verb_word} needed for `${func.name}`', + call_arg.pos) + } + } + } + } already_checked := node.language != .js && call_arg.expr is ast.CallExpr if func.is_variadic && param_i >= func.params.len - 1 { param_sym := c.table.sym(param.typ) diff --git a/vlib/v/checker/tests/array_init_decompose_extra_params.out b/vlib/v/checker/tests/array_init_decompose_extra_params.out new file mode 100644 index 000000000..959720ae8 --- /dev/null +++ b/vlib/v/checker/tests/array_init_decompose_extra_params.out @@ -0,0 +1,20 @@ +vlib/v/checker/tests/array_init_decompose_extra_params.vv:33:11: error: array decompose has 1 element but 2 are needed for `print_2` + 31 | print_3(...arr4) + 32 | + 33 | print_2(...['1']) + | ~~~~~~~~ + 34 | print_3(...['1', '2']) + 35 | print_4(...['1', '2', '3']) +vlib/v/checker/tests/array_init_decompose_extra_params.vv:34:11: error: array decompose has 2 elements but 3 are needed for `print_3` + 32 | + 33 | print_2(...['1']) + 34 | print_3(...['1', '2']) + | ~~~~~~~~~~~~~ + 35 | print_4(...['1', '2', '3']) + 36 | } +vlib/v/checker/tests/array_init_decompose_extra_params.vv:35:11: error: array decompose has 3 elements but 4 are needed for `print_4` + 33 | print_2(...['1']) + 34 | print_3(...['1', '2']) + 35 | print_4(...['1', '2', '3']) + | ~~~~~~~~~~~~~~~~~~ + 36 | } diff --git a/vlib/v/checker/tests/array_init_decompose_extra_params.vv b/vlib/v/checker/tests/array_init_decompose_extra_params.vv new file mode 100644 index 000000000..9b7778cda --- /dev/null +++ b/vlib/v/checker/tests/array_init_decompose_extra_params.vv @@ -0,0 +1,36 @@ +fn print_1(s string) { + dump(s) +} + +fn print_2(s string, t string) { + dump('${s} ${t}') +} + +fn print_3(s string, t string, u string) { + dump('${s} ${t} ${u}') +} + +fn print_4(s string, t string, u string, v string) { + dump('${s} ${t} ${u} ${v}') +} + +fn main() { + arr4 := ['Hello', 'World', 'V', '!'] + + print_1(...['Hello']) + print_2(...['Hello', 'World']) + print_3(...['Hello', 'World', '!']) + print_4(...['Hello', 'World', 'V', '!']) + print_4(...arr4) + + print_1(...['Hello', 'World', 'V', '!']) + print_1(...arr4) + print_2(...['Hello', 'World', 'V', '!']) + print_2(...arr4) + print_3(...['Hello', 'World', 'V', '!']) + print_3(...arr4) + + print_2(...['1']) + print_3(...['1', '2']) + print_4(...['1', '2', '3']) +} diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index 4af8d0a91..f10035b89 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -2483,15 +2483,47 @@ fn (mut g Gen) call_args(node ast.CallExpr) { } } else if arg.expr is ast.ArrayDecompose { mut d_count := 0 - for d_i in i .. expected_types.len { - g.write('*(${g.styp(expected_types[d_i])}*)builtin__array_get(') - g.expr(arg.expr) - g.write(', ${d_count})') - - if d_i < expected_types.len - 1 { - g.write(', ') + remaining_params := expected_types.len - i + if !arg.expr.expr_type.has_flag(.variadic) && remaining_params > 0 { + tmp_array := g.new_tmp_var() + line := g.go_before_last_stmt() + array_typ := g.styp(arg.expr.expr_type) + g.write('\t${array_typ} ${tmp_array} = ') + g.expr(arg.expr.expr) + g.writeln(';') + g.writeln('if (${tmp_array}.len < ${remaining_params}) {') + elem_word := if remaining_params == 1 { 'element is' } else { 'elements are' } + tmp_err_msg := g.new_tmp_var() + g.writeln('\tstring ${tmp_err_msg};') + g.writeln('\tif (${tmp_array}.len == 1) {') + g.writeln('\t\t${tmp_err_msg} = builtin__str_intp(2, _MOV((StrIntpData[]){') + g.writeln('\t\t\t{_S("array decompose: array has "), 0xfe07, {.d_i32 = ${tmp_array}.len}},') + g.writeln('\t\t\t{_S(" element but ${remaining_params} ${elem_word} needed"), 0, {.d_c = 0 }}}));') + g.writeln('\t} else {') + g.writeln('\t\t${tmp_err_msg} = builtin__str_intp(2, _MOV((StrIntpData[]){') + g.writeln('\t\t\t{_S("array decompose: array has "), 0xfe07, {.d_i32 = ${tmp_array}.len}},') + g.writeln('\t\t\t{_S(" elements but ${remaining_params} ${elem_word} needed"), 0, {.d_c = 0 }}}));') + g.writeln('\t}') + g.writeln('\tbuiltin___v_panic(${tmp_err_msg});') + g.writeln('}') + g.write(line.trim_left('\t')) + for d_i in i .. expected_types.len { + g.write('*(${g.styp(expected_types[d_i])}*)builtin__array_get(${tmp_array}, ${d_count})') + if d_i < expected_types.len - 1 { + g.write(', ') + } + d_count++ + } + } else { + for d_i in i .. expected_types.len { + g.write('*(${g.styp(expected_types[d_i])}*)builtin__array_get(') + g.expr(arg.expr) + g.write(', ${d_count})') + if d_i < expected_types.len - 1 { + g.write(', ') + } + d_count++ } - d_count++ } already_decomposed = true continue diff --git a/vlib/v/slow_tests/inout/panic_array_decompose_extra_args.out b/vlib/v/slow_tests/inout/panic_array_decompose_extra_args.out new file mode 100644 index 000000000..2ad4e4925 --- /dev/null +++ b/vlib/v/slow_tests/inout/panic_array_decompose_extra_args.out @@ -0,0 +1,8 @@ +[vlib/v/slow_tests/inout/panic_array_decompose_extra_args.vv:2] s: a +[vlib/v/slow_tests/inout/panic_array_decompose_extra_args.vv:2] s: a +[vlib/v/slow_tests/inout/panic_array_decompose_extra_args.vv:6] '$s $t': a b +[vlib/v/slow_tests/inout/panic_array_decompose_extra_args.vv:10] '$s $t $u': a b c +[vlib/v/slow_tests/inout/panic_array_decompose_extra_args.vv:2] s: b +[vlib/v/slow_tests/inout/panic_array_decompose_extra_args.vv:6] '$s $t': b c +================ V panic ================ +V panic: array decompose: array has 3 elements but 4 elements are needed diff --git a/vlib/v/slow_tests/inout/panic_array_decompose_extra_args.vv b/vlib/v/slow_tests/inout/panic_array_decompose_extra_args.vv new file mode 100644 index 000000000..a3b3ec6d4 --- /dev/null +++ b/vlib/v/slow_tests/inout/panic_array_decompose_extra_args.vv @@ -0,0 +1,31 @@ +fn print_1(s string) { + dump(s) +} + +fn print_2(s string, t string) { + dump('${s} ${t}') +} + +fn print_3(s string, t string, u string) { + dump('${s} ${t} ${u}') +} + +fn print_4(s string, t string, u string, v string) { + dump('${s} ${t} ${u} ${v}') +} + +fn main() { + array := ['a', 'b', 'c'] + + print_1(array[0]) + + print_1(...array) + print_2(...array) + print_3(...array) + + print_1(...array[1..2]) + print_2(...array[1..]) + + println('================ V panic ================') + print_4(...array) +} -- 2.39.5