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)
}
mut has_decompose := false
mut nr_multi_values := 0
for i, mut call_arg in node.args {
if func.params.len == 0 {
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 {
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 {
func.params.last()
} else {
func.params[i]
func.params[param_i]
}
// 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
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)
}
}
has_decompose = call_arg.expr is ast.ArrayDecompose
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)
mut expected_type := param.typ
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 {
func.params.last()
} else {
func.params[n + i]
func.params[n + param_i]
}
c.check_expected_call_arg(curr_arg, c.unwrap_generic(multi_param.typ),
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)
continue out
}
}
nr_multi_values += arg_typ_sym.info.types.len - 1
continue
} else if param_typ_sym.info is ast.Struct && arg_typ_sym.info is ast.Struct
&& 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
}
*/
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]
&& !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)
c.error('cannot use `${got_typ_str}` as `${expected_typ_str}` in argument ${i + 1} to `${fn_name}`',
call_arg.pos)
c.error('cannot use `${got_typ_str}` as `${expected_typ_str}` in argument ${i +
nr_multi_values + 1} to `${fn_name}`', call_arg.pos)
}
// Warn about automatic (de)referencing, which will be removed soon.
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('')
}
}
} 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 {
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('')
} 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.fn_call_error_have_want(
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')
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() {
18 | my_func(my_func2())
19 | my_func3(my_func2(), 'foo')
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~
| ~~~~~~~~~~
20 | my_func4('foo', my_func2())
21 | my_func(my_func5())
Details: have ((int, f64), string)
want (int, int, string)
vlib/v/checker/tests/multi_return_err.vv:20:2: error: expected 3 arguments, but got 2
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)
18 | my_func(my_func2())
19 | my_func3(my_func2(), 'foo')
20 | my_func4('foo', my_func2())
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~
| ~~~~~~~~~~
21 | my_func(my_func5())
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
19 | my_func3(my_func2(), 'foo')
20 | my_func4('foo', my_func2())