mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
cgen, checker: allow array decompose on non-variadic func call (e.g call(...arr)) (#17284)
This commit is contained in:
parent
d907ceb50f
commit
04e00a46d4
@ -965,11 +965,14 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast.
|
||||
c.error('too many arguments in call to `${func.name}` (non-js backend: ${c.pref.backend})',
|
||||
node.pos)
|
||||
}
|
||||
mut has_decompose := false
|
||||
for i, mut call_arg in node.args {
|
||||
if func.params.len == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if !func.is_variadic && has_decompose {
|
||||
c.error('cannot have parameter after array decompose', node.pos)
|
||||
}
|
||||
param := if func.is_variadic && i >= func.params.len - 1 {
|
||||
func.params.last()
|
||||
} else {
|
||||
@ -980,6 +983,7 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast.
|
||||
c.error('too many arguments in call to `${func.name}`', node.pos)
|
||||
}
|
||||
}
|
||||
has_decompose = call_arg.expr is ast.ArrayDecompose
|
||||
if func.is_variadic && i >= func.params.len - 1 {
|
||||
param_sym := c.table.sym(param.typ)
|
||||
mut expected_type := param.typ
|
||||
@ -2048,6 +2052,12 @@ fn (mut c Checker) check_expected_arg_count(mut node ast.CallExpr, f &ast.Fn) !
|
||||
}
|
||||
if f.is_variadic {
|
||||
min_required_params--
|
||||
} else {
|
||||
has_decompose := node.args.filter(it.expr is ast.ArrayDecompose).len > 0
|
||||
if has_decompose {
|
||||
// if call(...args) is present
|
||||
min_required_params = nr_args
|
||||
}
|
||||
}
|
||||
if min_required_params < 0 {
|
||||
min_required_params = 0
|
||||
|
6
vlib/v/checker/tests/arraydecompose_arg2_err.out
Normal file
6
vlib/v/checker/tests/arraydecompose_arg2_err.out
Normal file
@ -0,0 +1,6 @@
|
||||
vlib/v/checker/tests/arraydecompose_arg2_err.vv:7:12: error: cannot use `f64` as `int` in argument 1 to `test_arr`
|
||||
5 | fn main() {
|
||||
6 | var := []f64{}
|
||||
7 | test_arr(...var)
|
||||
| ~~~~~~
|
||||
8 | }
|
8
vlib/v/checker/tests/arraydecompose_arg2_err.vv
Normal file
8
vlib/v/checker/tests/arraydecompose_arg2_err.vv
Normal file
@ -0,0 +1,8 @@
|
||||
fn test_arr(i int) string {
|
||||
return '${i}'
|
||||
}
|
||||
|
||||
fn main() {
|
||||
var := []f64{}
|
||||
test_arr(...var)
|
||||
}
|
6
vlib/v/checker/tests/arraydecompose_arg_err.out
Normal file
6
vlib/v/checker/tests/arraydecompose_arg_err.out
Normal file
@ -0,0 +1,6 @@
|
||||
vlib/v/checker/tests/arraydecompose_arg_err.vv:7:2: error: cannot have parameter after array decompose
|
||||
5 | fn test_main() {
|
||||
6 | var := [0.0]
|
||||
7 | test_arr(1, ...var, 'a')
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
8 | }
|
8
vlib/v/checker/tests/arraydecompose_arg_err.vv
Normal file
8
vlib/v/checker/tests/arraydecompose_arg_err.vv
Normal file
@ -0,0 +1,8 @@
|
||||
fn test_arr(i int, f f64, s string) string {
|
||||
return '${i} : ${f}'
|
||||
}
|
||||
|
||||
fn test_main() {
|
||||
var := [0.0]
|
||||
test_arr(1, ...var, 'a')
|
||||
}
|
@ -1906,7 +1906,11 @@ pub fn (mut f Fmt) comptime_call(node ast.ComptimeCall) {
|
||||
inner_args := if node.args_var != '' {
|
||||
node.args_var
|
||||
} else {
|
||||
node.args.map(it.str()).join(', ')
|
||||
node.args.map(if it.expr is ast.ArrayDecompose {
|
||||
'...${it.expr.expr.str()}'
|
||||
} else {
|
||||
it.str()
|
||||
}).join(', ')
|
||||
}
|
||||
method_expr := if node.has_parens {
|
||||
'(${node.method_name}(${inner_args}))'
|
||||
|
@ -141,20 +141,24 @@ fn (mut g Gen) comptime_call(mut node ast.ComptimeCall) {
|
||||
} else {
|
||||
false
|
||||
}
|
||||
mut has_decompose := !m.is_variadic
|
||||
&& node.args.filter(it.expr is ast.ArrayDecompose).len > 0
|
||||
// check argument length and types
|
||||
if m.params.len - 1 != node.args.len && !expand_strs {
|
||||
if g.inside_call {
|
||||
g.error('expected ${m.params.len - 1} arguments to method ${sym.name}.${m.name}, but got ${node.args.len}',
|
||||
node.pos)
|
||||
} else {
|
||||
// do not generate anything if the argument lengths don't match
|
||||
g.writeln('/* skipping ${sym.name}.${m.name} due to mismatched arguments list */')
|
||||
// g.writeln('println(_SLIT("skipping ${node.sym.name}.$m.name due to mismatched arguments list"));')
|
||||
// eprintln('info: skipping ${node.sym.name}.$m.name due to mismatched arguments list\n' +
|
||||
//'method.params: $m.params, args: $node.args\n\n')
|
||||
// verror('expected ${m.params.len-1} arguments to method ${node.sym.name}.$m.name, but got $node.args.len')
|
||||
if !has_decompose {
|
||||
// do not generate anything if the argument lengths don't match
|
||||
g.writeln('/* skipping ${sym.name}.${m.name} due to mismatched arguments list */')
|
||||
// g.writeln('println(_SLIT("skipping ${node.sym.name}.$m.name due to mismatched arguments list"));')
|
||||
// eprintln('info: skipping ${node.sym.name}.$m.name due to mismatched arguments list\n' +
|
||||
//'method.params: $m.params, args: $node.args\n\n')
|
||||
// verror('expected ${m.params.len-1} arguments to method ${node.sym.name}.$m.name, but got $node.args.len')
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if !g.inside_call && (m.return_type.has_flag(.option) || m.return_type.has_flag(.result)) {
|
||||
@ -181,7 +185,21 @@ fn (mut g Gen) comptime_call(mut node ast.ComptimeCall) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if i - 1 < node.args.len - 1 {
|
||||
if (i - 1 <= node.args.len - 1) && has_decompose
|
||||
&& node.args[i - 1].expr is ast.ArrayDecompose {
|
||||
mut d_count := 0
|
||||
for d_i in i .. m.params.len {
|
||||
g.write('*(${g.typ(m.params[i].typ)}*)array_get(')
|
||||
g.expr(node.args[i - 1].expr)
|
||||
g.write(', ${d_count})')
|
||||
|
||||
if d_i < m.params.len - 1 {
|
||||
g.write(', ')
|
||||
}
|
||||
d_count++
|
||||
}
|
||||
break
|
||||
} else if i - 1 < node.args.len - 1 {
|
||||
g.expr(node.args[i - 1].expr)
|
||||
g.write(', ')
|
||||
} else if !expand_strs && i == node.args.len {
|
||||
|
@ -1759,6 +1759,19 @@ 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.typ(expected_types[d_i])}*)array_get(')
|
||||
g.expr(arg.expr)
|
||||
g.write(', ${d_count})')
|
||||
|
||||
if d_i < expected_types.len - 1 {
|
||||
g.write(', ')
|
||||
}
|
||||
d_count++
|
||||
}
|
||||
continue
|
||||
}
|
||||
use_tmp_var_autofree := g.is_autofree && arg.typ == ast.string_type && arg.is_tmp_autofree
|
||||
&& !g.inside_const && !g.is_builtin_mod
|
||||
|
137
vlib/v/tests/arraydecompose_nonvariadic_test.v
Normal file
137
vlib/v/tests/arraydecompose_nonvariadic_test.v
Normal file
@ -0,0 +1,137 @@
|
||||
type Any = f64 | int | string
|
||||
|
||||
struct Test {}
|
||||
|
||||
struct Test2 {}
|
||||
|
||||
struct Test3 {}
|
||||
|
||||
fn (t Test) test(args ...Any) {
|
||||
println('called with ${args}')
|
||||
}
|
||||
|
||||
fn (t Test2) test_str(arg string) {
|
||||
println('called with ${arg}')
|
||||
}
|
||||
|
||||
fn (t Test2) test_int(arg int, arg2 int) {
|
||||
println('called with ${arg}, ${arg2}')
|
||||
}
|
||||
|
||||
fn (t Test3) test_int(arg int, arg2 int) {
|
||||
println('called with ${arg}, ${arg2}')
|
||||
}
|
||||
|
||||
fn foo_any(i Any, k Any, j Any) string {
|
||||
return '${i} : ${k} : ${j}'
|
||||
}
|
||||
|
||||
fn foo(i int, k int) string {
|
||||
return '${i} : ${k}'
|
||||
}
|
||||
|
||||
fn bar(i f64, k f64, j f64) string {
|
||||
return '${i} : ${k} : ${j}'
|
||||
}
|
||||
|
||||
fn baz(s string) string {
|
||||
return s
|
||||
}
|
||||
|
||||
fn f_arr(i int, f f64) string {
|
||||
return '${i} : ${f}'
|
||||
}
|
||||
|
||||
fn f_var(s string, args ...string) string {
|
||||
return '${s} [ ${args.map(it).join(',')} ]'
|
||||
}
|
||||
|
||||
fn varargs[T](args ...T) string {
|
||||
assert args.len > 0
|
||||
return args.map(it.str()).join(' : ')
|
||||
}
|
||||
|
||||
fn call[T](func_name string, args ...T) string {
|
||||
return match func_name {
|
||||
'foo' { foo(...args) }
|
||||
'bar' { bar(...args) }
|
||||
'baz' { baz(...args) }
|
||||
'varargs' { varargs(...args) }
|
||||
else { '' }
|
||||
}
|
||||
}
|
||||
|
||||
fn call_any(func_name string, args ...Any) string {
|
||||
return match func_name {
|
||||
'foo_any' { foo_any(...args) }
|
||||
else { '' }
|
||||
}
|
||||
}
|
||||
|
||||
fn comptime_call[T](instance T, method_name string, args ...Any) bool {
|
||||
$for method in T.methods {
|
||||
if method.name == method_name {
|
||||
instance.$method(...args)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fn comptime_call_vargs[T, R](instance T, method_name string, args ...R) bool {
|
||||
$for method in T.methods {
|
||||
if method.name == method_name {
|
||||
instance.$method(...args)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fn comptime_call_vargs2[T, R](instance T, method_name string, args ...R) bool {
|
||||
$for method in T.methods {
|
||||
if method.name == method_name {
|
||||
instance.$method(200, ...args)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fn test_main() {
|
||||
assert call('foo', 10, 100) == '10 : 100'
|
||||
assert call('bar', 1.1, 1.2, 1.3) == '1.1 : 1.2 : 1.3'
|
||||
assert call('baz', 'test') == 'test'
|
||||
assert call_any('foo_any', 10, 1.2, 'test') == "Any(10) : Any(1.2) : Any('test')"
|
||||
assert call[Any]('varargs', 10, 1.2, 'test') == "Any(10) : Any(1.2) : Any('test')"
|
||||
|
||||
a := []int{len: 2, init: 50}
|
||||
assert foo(...a) == '50 : 50'
|
||||
|
||||
b := []f64{len: 3, init: 1.2}
|
||||
assert bar(...b) == '1.2 : 1.2 : 1.2'
|
||||
|
||||
mut c := []Any{}
|
||||
c << 10
|
||||
c << 1.2
|
||||
c << 'test'
|
||||
assert varargs(...c) == "Any(10) : Any(1.2) : Any('test')"
|
||||
|
||||
var := [0.0]
|
||||
assert f_arr(1, ...var) == '1 : 0.0'
|
||||
|
||||
var2 := ['a', 'b', 'c']
|
||||
assert f_var('foo', ...var2) == 'foo [ a,b,c ]'
|
||||
}
|
||||
|
||||
fn test_comptime() {
|
||||
var := Test{}
|
||||
assert comptime_call(var, 'test', 1, 2.3, '')
|
||||
|
||||
var2 := Test2{}
|
||||
assert comptime_call_vargs(var2, 'test_int', 1, 100)
|
||||
assert comptime_call_vargs(var2, 'test_str', 'foo')
|
||||
|
||||
var3 := Test3{}
|
||||
assert comptime_call_vargs2(var3, 'test_int', 100)
|
||||
}
|
Loading…
Reference in New Issue
Block a user