diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 8ba67c90db..f38dd598ba 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -1184,6 +1184,7 @@ pub mut: sym table.TypeSymbol result_type table.Type env_value string + args []CallArg } pub struct None { diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 6f7c8275f7..f03878c428 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -4072,15 +4072,9 @@ fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) table.Type { return rtyp } if node.method_name == 'method' { - if node.args_var.len > 0 { - v := node.scope.find_var(node.args_var) or { - c.error('unknown identifier `$node.args_var`', node.method_pos) - return table.void_type - } - s := c.table.type_to_str(c.expr(v.expr)) - if s != '[]string' { - c.error('expected `[]string`, not s', node.method_pos) - } + for i, arg in node.args { + // check each arg expression + node.args[i].typ = c.expr(arg.expr) } // assume string for now return table.string_type diff --git a/vlib/v/checker/tests/comptime_call_method.out b/vlib/v/checker/tests/comptime_call_method.out index 981d74a281..de71203461 100644 --- a/vlib/v/checker/tests/comptime_call_method.out +++ b/vlib/v/checker/tests/comptime_call_method.out @@ -1,14 +1,7 @@ -vlib/v/checker/tests/comptime_call_method.vv:10:7: error: unknown identifier `wrong` +vlib/v/checker/tests/comptime_call_method.vv:10:14: error: undefined ident: `wrong` 8 | s1 := S1{} 9 | $for method in S1.methods { 10 | s1.$method(wrong) - | ~~~~~~ - 11 | arg := 7 - 12 | s1.$method(arg) -vlib/v/checker/tests/comptime_call_method.vv:12:7: error: expected `[]string`, not s - 10 | s1.$method(wrong) - 11 | arg := 7 - 12 | s1.$method(arg) - | ~~~~~~ - 13 | } - 14 | } + | ~~~~~ + 11 | } + 12 | } diff --git a/vlib/v/checker/tests/comptime_call_method.vv b/vlib/v/checker/tests/comptime_call_method.vv index f1fb66f5f7..2c217890b5 100644 --- a/vlib/v/checker/tests/comptime_call_method.vv +++ b/vlib/v/checker/tests/comptime_call_method.vv @@ -8,7 +8,5 @@ fn test_methods_arg() { s1 := S1{} $for method in S1.methods { s1.$method(wrong) - arg := 7 - s1.$method(arg) } } diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index a76b103951..66ff8f0c1c 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -986,10 +986,15 @@ pub fn (mut f Fmt) comptime_call(node ast.ComptimeCall) { } else if node.is_env { f.write("\$env('$node.args_var')") } else { - method_expr := if node.has_parens { - '(${node.method_name}($node.args_var))' + inner_args := if node.args_var != '' { + node.args_var } else { - '${node.method_name}($node.args_var)' + node.args.map(it.str()).join(', ') + } + method_expr := if node.has_parens { + '(${node.method_name}($inner_args))' + } else { + '${node.method_name}($inner_args)' } f.write('${node.left}.$$method_expr') } diff --git a/vlib/v/fmt/tests/comptime_keep.vv b/vlib/v/fmt/tests/comptime_keep.vv index 59f8b0c438..987e513dd7 100644 --- a/vlib/v/fmt/tests/comptime_keep.vv +++ b/vlib/v/fmt/tests/comptime_keep.vv @@ -67,12 +67,9 @@ fn (mut a App) my_method(p string) Result { } fn handle_conn(mut app T) { - mut vars := []string{cap: 123} - vars << 'abc' - vars << 'def' $for method in T.methods { $if method.return_type is Result { - app.$method(vars) + app.$method('abc', 'def') } } } diff --git a/vlib/v/gen/c/comptime.v b/vlib/v/gen/c/comptime.v index 2d33e646db..f1634392f2 100644 --- a/vlib/v/gen/c/comptime.v +++ b/vlib/v/gen/c/comptime.v @@ -76,7 +76,34 @@ fn (mut g Gen) comptime_call(node ast.ComptimeCall) { for val in vals { } */ + expand_strs := if node.args.len > 0 && m.params.len - 1 >= node.args.len { + arg := node.args[node.args.len - 1] + param := m.params[node.args.len] + + arg.expr is ast.Ident && g.table.type_to_str(arg.typ) == '[]string' + && g.table.type_to_str(param.typ) != '[]string' + } else { + false + } + // check argument length and types + if m.params.len - 1 != node.args.len && !expand_strs { + // do not generate anything if the argument lengths don't match + g.writeln('/* skipping ${node.sym.name}.$m.name due to mismatched arguments list */') + // verror('expected ${m.params.len-1} arguments to method ${node.sym.name}.$m.name, but got $node.args.len') + return + } + // TODO: check argument types g.write('${util.no_dots(node.sym.name)}_${g.comp_for_method}(') + + // try to see if we need to pass a pointer + if node.left is ast.Ident { + scope := g.file.scope.innermost(node.pos.pos) + if v := scope.find_var(node.left.name) { + if m.params[0].typ.is_ptr() && !v.typ.is_ptr() { + g.write('&') + } + } + } g.expr(node.left) if m.params.len > 1 { g.write(', ') @@ -87,15 +114,25 @@ fn (mut g Gen) comptime_call(node ast.ComptimeCall) { continue } } - if m.params[i].typ.is_int() || m.params[i].typ.idx() == table.bool_type_idx { - // Gets the type name and cast the string to the type with the string_ function - type_name := g.table.types[int(m.params[i].typ)].str() - g.write('string_${type_name}(((string*)${node.args_var}.data) [${i - 1}])') - } else { - g.write('((string*)${node.args_var}.data) [${i - 1}] ') - } - if i < m.params.len - 1 { + if i - 1 < node.args.len - 1 { + g.expr(node.args[i - 1].expr) g.write(', ') + } else if !expand_strs && i == node.args.len { + g.expr(node.args[i - 1].expr) + break + } else { + // last argument; try to expand if it's []string + idx := i - node.args.len + if m.params[i].typ.is_int() || m.params[i].typ.idx() == table.bool_type_idx { + // Gets the type name and cast the string to the type with the string_ function + type_name := g.table.types[int(m.params[i].typ)].str() + g.write('string_${type_name}(((string*)${node.args[node.args.len - 1]}.data) [$idx])') + } else { + g.write('((string*)${node.args[node.args.len - 1]}.data) [$idx] ') + } + if i < m.params.len - 1 { + g.write(', ') + } } } g.write(' ); // vweb action call with args') diff --git a/vlib/v/parser/comptime.v b/vlib/v/parser/comptime.v index 26b7ca9c0d..d2adb61283 100644 --- a/vlib/v/parser/comptime.v +++ b/vlib/v/parser/comptime.v @@ -305,12 +305,7 @@ fn (mut p Parser) comptime_selector(left ast.Expr) ast.Expr { p.mark_var_as_used(method_name) // `app.$action()` (`action` is a string) p.check(.lpar) - mut args_var := '' - if p.tok.kind == .name { - args_var = p.tok.lit - p.mark_var_as_used(args_var) - p.next() - } + args := p.call_args() p.check(.rpar) if p.tok.kind == .key_orelse { p.check(.key_orelse) @@ -321,7 +316,8 @@ fn (mut p Parser) comptime_selector(left ast.Expr) ast.Expr { method_name: method_name method_pos: method_pos scope: p.scope - args_var: args_var + args_var: '' + args: args pos: start_pos.extend(p.prev_tok.position()) } } diff --git a/vlib/v/tests/comptime_method_args_test.v b/vlib/v/tests/comptime_method_args_test.v index 5cec5fe050..47520abaef 100644 --- a/vlib/v/tests/comptime_method_args_test.v +++ b/vlib/v/tests/comptime_method_args_test.v @@ -1,14 +1,52 @@ -struct TestStruct {} +struct TestStruct { +mut: + one_arg_called bool + two_args_called bool + three_args_called bool +} -fn (t TestStruct) test(arg1 string, arg2 string, arg3 string) {} +fn (mut t TestStruct) one_arg(a1 string) { + t.one_arg_called = true +} +fn (mut t TestStruct) two_args(a2 string, b2 int) { + t.two_args_called = true +} +fn (mut t TestStruct) three_args(a3 string, b3 int, c3 []string) { + t.three_args_called = true +} fn test_comptime_method_names() { + mut num_methods := 0 $for method in TestStruct.methods { - if method.name == 'test' { - args := method.args - assert args[0].name == 'arg1' - assert args[1].name == 'arg2' - assert args[2].name == 'arg3' + if method.name == 'one_arg' { + assert method.args[0].name == 'a1' + num_methods++ + } else if method.name == 'two_args' { + assert method.args[0].name == 'a2' + assert method.args[1].name == 'b2' + num_methods++ + } else if method.name == 'three_args' { + assert method.args[0].name == 'a3' + assert method.args[1].name == 'b3' + assert method.args[2].name == 'c3' + num_methods++ } } + assert num_methods == 3 +} + +fn test_comptime_call_method() { + mut t := TestStruct{} + $for method in TestStruct.methods { + if method.name == 'one_arg' { + t.$method('one') + } else if method.name == 'two_args' { + t.$method('two', 2) + } else if method.name == 'three_args' { + t.$method('three', 3, ['th' 'ree']) + } + } + assert t.one_arg_called + assert t.two_args_called + assert t.three_args_called } diff --git a/vlib/vweb/vweb.v b/vlib/vweb/vweb.v index a3ae34d944..00e5ea803d 100644 --- a/vlib/vweb/vweb.v +++ b/vlib/vweb/vweb.v @@ -379,12 +379,12 @@ fn handle_conn(mut conn net.TcpConn, mut app T) { // should be called first. if !route_path.contains('/:') && url_words == route_words { // We found a match - app.$method(method_args) + app.$method() return } if url_words.len == 0 && route_words == ['index'] && method.name == 'index' { - app.$method(method_args) + app.$method() return }