checker: fix multi return arg passing checking (fix #25167) (fix #25180) (#25177)

This commit is contained in:
Felipe Pena 2025-08-29 01:43:19 -03:00 committed by GitHub
parent 9e1273ae71
commit 1ba45123ec
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 61 additions and 17 deletions

View file

@ -1472,6 +1472,7 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast.
node.pos) node.pos)
} }
mut has_decompose := false mut has_decompose := false
mut nr_multi_values := 0
for i, mut call_arg in node.args { for i, mut call_arg in node.args {
if func.params.len == 0 { if func.params.len == 0 {
continue continue
@ -1482,21 +1483,22 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast.
if !func.is_variadic && has_decompose { if !func.is_variadic && has_decompose {
c.error('cannot have parameter after array decompose', node.pos) c.error('cannot have parameter after array decompose', node.pos)
} }
param_i := i + nr_multi_values
param := if func.is_variadic && i >= func.params.len - 1 { param := if func.is_variadic && i >= func.params.len - 1 {
func.params.last() func.params.last()
} else { } else {
func.params[i] func.params[param_i]
} }
// registers if the arg must be passed by ref to disable auto deref args // registers if the arg must be passed by ref to disable auto deref args
call_arg.should_be_ptr = param.typ.is_ptr() && !param.is_mut call_arg.should_be_ptr = param.typ.is_ptr() && !param.is_mut
if func.is_variadic && call_arg.expr is ast.ArrayDecompose { if func.is_variadic && call_arg.expr is ast.ArrayDecompose {
if i > func.params.len - 1 { if param_i > func.params.len - 1 {
c.error('too many arguments in call to `${func.name}`', node.pos) c.error('too many arguments in call to `${func.name}`', node.pos)
} }
} }
has_decompose = call_arg.expr is ast.ArrayDecompose has_decompose = call_arg.expr is ast.ArrayDecompose
already_checked := node.language != .js && call_arg.expr is ast.CallExpr already_checked := node.language != .js && call_arg.expr is ast.CallExpr
if func.is_variadic && i >= func.params.len - 1 { if func.is_variadic && param_i >= func.params.len - 1 {
param_sym := c.table.sym(param.typ) param_sym := c.table.sym(param.typ)
mut expected_type := param.typ mut expected_type := param.typ
if param_sym.info is ast.Array { if param_sym.info is ast.Array {
@ -1688,15 +1690,16 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast.
multi_param := if func.is_variadic && i >= func.params.len - 1 { multi_param := if func.is_variadic && i >= func.params.len - 1 {
func.params.last() func.params.last()
} else { } else {
func.params[n + i] func.params[n + param_i]
} }
c.check_expected_call_arg(curr_arg, c.unwrap_generic(multi_param.typ), c.check_expected_call_arg(curr_arg, c.unwrap_generic(multi_param.typ),
node.language, call_arg) or { node.language, call_arg) or {
c.error('${err.msg()} in argument ${i + n + 1} to `${fn_name}` from ${c.table.type_to_str(arg_typ)}', c.error('${err.msg()} in argument ${param_i + n + 1} to `${fn_name}` from ${c.table.type_to_str(arg_typ)}',
call_arg.pos) call_arg.pos)
continue out continue out
} }
} }
nr_multi_values += arg_typ_sym.info.types.len - 1
continue continue
} else if param_typ_sym.info is ast.Struct && arg_typ_sym.info is ast.Struct } else if param_typ_sym.info is ast.Struct && arg_typ_sym.info is ast.Struct
&& param_typ_sym.info.is_anon { && param_typ_sym.info.is_anon {
@ -1788,13 +1791,14 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast.
continue continue
} }
*/ */
c.error('${err.msg()} in argument ${i + 1} to `${fn_name}`', call_arg.pos) c.error('${err.msg()} in argument ${i + nr_multi_values + 1} to `${fn_name}`',
call_arg.pos)
} }
if final_param_sym.kind == .struct && arg_typ !in [ast.voidptr_type, ast.nil_type] if final_param_sym.kind == .struct && arg_typ !in [ast.voidptr_type, ast.nil_type]
&& !c.check_multiple_ptr_match(arg_typ, param.typ, param, call_arg) { && !c.check_multiple_ptr_match(arg_typ, param.typ, param, call_arg) {
got_typ_str, expected_typ_str := c.get_string_names_of(arg_typ, param.typ) got_typ_str, expected_typ_str := c.get_string_names_of(arg_typ, param.typ)
c.error('cannot use `${got_typ_str}` as `${expected_typ_str}` in argument ${i + 1} to `${fn_name}`', c.error('cannot use `${got_typ_str}` as `${expected_typ_str}` in argument ${i +
call_arg.pos) nr_multi_values + 1} to `${fn_name}`', call_arg.pos)
} }
// Warn about automatic (de)referencing, which will be removed soon. // Warn about automatic (de)referencing, which will be removed soon.
if func.language != .c && !c.inside_unsafe && !(call_arg.is_mut && param.is_mut) { if func.language != .c && !c.inside_unsafe && !(call_arg.is_mut && param.is_mut) {
@ -2924,6 +2928,17 @@ fn (mut c Checker) check_expected_arg_count(mut node ast.CallExpr, f &ast.Fn) !
return error('') return error('')
} }
} }
} else if node.args.len > 1 && node.args.any(it.expr is ast.CallExpr
&& it.expr.nr_ret_values > 1) {
mut check_args := 0
for arg in node.args {
if arg.expr is ast.CallExpr && arg.expr.nr_ret_values > 0 {
check_args += arg.expr.nr_ret_values
} else {
check_args += 1
}
}
nr_args = check_args
} }
if min_required_params < 0 { if min_required_params < 0 {
min_required_params = 0 min_required_params = 0
@ -2966,7 +2981,7 @@ fn (mut c Checker) check_expected_arg_count(mut node ast.CallExpr, f &ast.Fn) !
) )
return error('') return error('')
} else if !f.is_variadic && nr_args > nr_params { } else if !f.is_variadic && nr_args > nr_params {
unexpected_args_pos := node.args[min_required_params].pos.extend(node.args.last().pos) unexpected_args_pos := node.args[int_min(min_required_params, node.args.len - 1)].pos.extend(node.args.last().pos)
// c.error('3expected ${min_required_params} arguments, but got ${nr_args}', unexpected_args_pos) // c.error('3expected ${min_required_params} arguments, but got ${nr_args}', unexpected_args_pos)
c.fn_call_error_have_want( c.fn_call_error_have_want(
nr_params: min_required_params nr_params: min_required_params

View file

@ -0,0 +1,18 @@
vlib/v/checker/tests/multi_return_arg_missing_err.vv:12:14: error: expected 2 arguments, but got 3
10 |
11 | fn main() {
12 | expect_2(1, returning_2())
| ~~~~~~~~~~~~~
13 | expect_2(returning_2(), 1)
14 | expect_4(returning_2(), returning_2())
Details: have (int literal, (int, int))
want (int, int)
vlib/v/checker/tests/multi_return_arg_missing_err.vv:13:26: error: expected 2 arguments, but got 3
11 | fn main() {
12 | expect_2(1, returning_2())
13 | expect_2(returning_2(), 1)
| ^
14 | expect_4(returning_2(), returning_2())
15 | }
Details: have ((int, int), int literal)
want (int, int)

View file

@ -0,0 +1,15 @@
fn expect_2(a int, b int) {
}
fn expect_4(a int, b int, c int, d int) {
}
fn returning_2() (int, int) {
return 1, 1
}
fn main() {
expect_2(1, returning_2())
expect_2(returning_2(), 1)
expect_4(returning_2(), returning_2())
}

View file

@ -5,24 +5,20 @@ vlib/v/checker/tests/multi_return_err.vv:18:10: error: cannot use `f64` as `int`
| ~~~~~~~~~~ | ~~~~~~~~~~
19 | my_func3(my_func2(), 'foo') 19 | my_func3(my_func2(), 'foo')
20 | my_func4('foo', my_func2()) 20 | my_func4('foo', my_func2())
vlib/v/checker/tests/multi_return_err.vv:19:2: error: expected 3 arguments, but got 2 vlib/v/checker/tests/multi_return_err.vv:19:11: error: cannot use `f64` as `int` in argument 2 to `my_func3` from (int, f64)
17 | fn main() { 17 | fn main() {
18 | my_func(my_func2()) 18 | my_func(my_func2())
19 | my_func3(my_func2(), 'foo') 19 | my_func3(my_func2(), 'foo')
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ~~~~~~~~~~
20 | my_func4('foo', my_func2()) 20 | my_func4('foo', my_func2())
21 | my_func(my_func5()) 21 | my_func(my_func5())
Details: have ((int, f64), string) vlib/v/checker/tests/multi_return_err.vv:20:18: error: cannot use `f64` as `int` in argument 3 to `my_func4` from (int, f64)
want (int, int, string)
vlib/v/checker/tests/multi_return_err.vv:20:2: error: expected 3 arguments, but got 2
18 | my_func(my_func2()) 18 | my_func(my_func2())
19 | my_func3(my_func2(), 'foo') 19 | my_func3(my_func2(), 'foo')
20 | my_func4('foo', my_func2()) 20 | my_func4('foo', my_func2())
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ~~~~~~~~~~
21 | my_func(my_func5()) 21 | my_func(my_func5())
22 | my_func(my_func6()) 22 | my_func(my_func6())
Details: have (string, (int, f64))
want (string, int, int)
vlib/v/checker/tests/multi_return_err.vv:21:2: error: expected 2 arguments, but got 1 vlib/v/checker/tests/multi_return_err.vv:21:2: error: expected 2 arguments, but got 1
19 | my_func3(my_func2(), 'foo') 19 | my_func3(my_func2(), 'foo')
20 | my_func4('foo', my_func2()) 20 | my_func4('foo', my_func2())