Skip to content

Commit

Permalink
all: add comptime support for traversing the method parameters with `…
Browse files Browse the repository at this point in the history
…$for arg in method.params {` (#22229)
  • Loading branch information
felipensp authored Sep 17, 2024
1 parent 15bf822 commit 1187e13
Show file tree
Hide file tree
Showing 12 changed files with 100 additions and 26 deletions.
6 changes: 3 additions & 3 deletions vlib/builtin/builtin.v
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ fn __print_assert_failure(i &VAssertMetaInfo) {
}
}

// MethodArgs holds type information for function and/or method arguments.
pub struct MethodArgs {
// MethodParam holds type information for function and/or method arguments.
pub struct MethodParam {
pub:
typ int
name string
Expand All @@ -102,7 +102,7 @@ pub struct FunctionData {
pub:
name string
attrs []string
args []MethodArgs
args []MethodParam
return_type int
typ int
}
Expand Down
1 change: 1 addition & 0 deletions vlib/v/ast/ast.v
Original file line number Diff line number Diff line change
Expand Up @@ -1250,6 +1250,7 @@ pub enum ComptimeForKind {
attributes
values
variants
params
}

pub struct ComptimeFor {
Expand Down
1 change: 1 addition & 0 deletions vlib/v/ast/str.v
Original file line number Diff line number Diff line change
Expand Up @@ -896,5 +896,6 @@ pub fn (e ComptimeForKind) str() string {
.attributes { return 'attributes' }
.values { return 'values' }
.variants { return 'variants' }
.params { return 'params' }
}
}
8 changes: 7 additions & 1 deletion vlib/v/checker/comptime.v
Original file line number Diff line number Diff line change
Expand Up @@ -318,13 +318,19 @@ fn (mut c Checker) comptime_for(mut node ast.ComptimeFor) {
for method in methods {
c.push_new_comptime_info()
c.comptime.inside_comptime_for = true
c.comptime.comptime_for_method = method.name
c.comptime.comptime_for_method = unsafe { &method }
c.comptime.comptime_for_method_var = node.val_var
c.comptime.comptime_for_method_ret_type = method.return_type
c.comptime.type_map['${node.val_var}.return_type'] = method.return_type
c.stmts(mut node.stmts)
c.pop_comptime_info()
}
} else if node.kind == .params {
c.push_new_comptime_info()
c.comptime.inside_comptime_for = true
c.comptime.comptime_for_method_param_var = node.val_var
c.stmts(mut node.stmts)
c.pop_comptime_info()
} else if node.kind == .variants {
if c.variant_data_type == 0 {
c.variant_data_type = ast.idx_to_type(c.table.find_type_idx('VariantData'))
Expand Down
4 changes: 2 additions & 2 deletions vlib/v/checker/if.v
Original file line number Diff line number Diff line change
Expand Up @@ -284,14 +284,14 @@ fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type {
is_comptime_type_is_expr = true
match branch.cond.op {
.eq {
skip_state = if c.comptime.comptime_for_method == right.val.str() {
skip_state = if c.comptime.comptime_for_method.name == right.val.str() {
ComptimeBranchSkipState.eval
} else {
ComptimeBranchSkipState.skip
}
}
.ne {
skip_state = if c.comptime.comptime_for_method == right.val.str() {
skip_state = if c.comptime.comptime_for_method.name == right.val.str() {
ComptimeBranchSkipState.skip
} else {
ComptimeBranchSkipState.eval
Expand Down
16 changes: 12 additions & 4 deletions vlib/v/comptime/comptimeinfo.v
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ pub fn (mut ct ComptimeInfo) get_comptime_var_type(node ast.Expr) ast.Type {
} else if node is ast.SelectorExpr && ct.is_comptime_selector_type(node) {
return ct.get_type_from_comptime_var(node.expr as ast.Ident)
} else if node is ast.ComptimeCall {
method_name := ct.comptime_for_method
method_name := ct.comptime_for_method.name
left_sym := ct.table.sym(ct.resolver.unwrap_generic(node.left_type))
f := left_sym.find_method(method_name) or {
ct.error('could not find method `${method_name}` on compile-time resolution',
Expand All @@ -114,6 +114,9 @@ pub fn (mut ct ComptimeInfo) get_type_from_comptime_var(var ast.Ident) ast.Type
ct.comptime_for_variant_var {
ct.type_map['${ct.comptime_for_variant_var}.typ']
}
ct.comptime_for_method_param_var {
ct.type_map['${ct.comptime_for_method_param_var}.typ']
}
else {
// field_var.typ from $for field
ct.comptime_for_field_type
Expand Down Expand Up @@ -152,7 +155,7 @@ pub fn (mut ct ComptimeInfo) is_comptime_selector_field_name(node ast.SelectorEx
pub fn (mut ct ComptimeInfo) is_comptime_selector_type(node ast.SelectorExpr) bool {
if ct.inside_comptime_for && node.expr is ast.Ident {
return
node.expr.name in [ct.comptime_for_enum_var, ct.comptime_for_variant_var, ct.comptime_for_field_var]
node.expr.name in [ct.comptime_for_enum_var, ct.comptime_for_variant_var, ct.comptime_for_field_var, ct.comptime_for_method_param_var]
&& node.field_name == 'typ'
}
return false
Expand Down Expand Up @@ -251,7 +254,7 @@ pub fn (mut ct ComptimeInfo) is_comptime_type(x ast.Type, y ast.ComptimeType) bo
}
}

// comptime_get_kind_var identifies the comptime variable kind (i.e. if it is about .values, .fields, .methods etc)
// comptime_get_kind_var identifies the comptime variable kind (i.e. if it is about .values, .fields, .methods, .args etc)
fn (mut ct ComptimeInfo) comptime_get_kind_var(var ast.Ident) ?ast.ComptimeForKind {
if ct.inside_comptime_for {
return none
Expand All @@ -273,6 +276,9 @@ fn (mut ct ComptimeInfo) comptime_get_kind_var(var ast.Ident) ?ast.ComptimeForKi
ct.comptime_for_attr_var {
return .attributes
}
ct.comptime_for_method_param_var {
return .params
}
else {
return none
}
Expand Down Expand Up @@ -339,6 +345,8 @@ pub mut:
comptime_for_attr_var string
// .methods
comptime_for_method_var string
comptime_for_method string
comptime_for_method &ast.Fn = unsafe { nil }
comptime_for_method_ret_type ast.Type
// .args
comptime_for_method_param_var string
}
36 changes: 28 additions & 8 deletions vlib/v/gen/c/comptime.v
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ fn (mut g Gen) comptime_call(mut node ast.ComptimeCall) {
g.trace_autofree('// \$method call. sym="${sym.name}"')
if node.method_name == 'method' {
// `app.$method()`
m := sym.find_method(g.comptime.comptime_for_method) or { return }
m := sym.find_method(g.comptime.comptime_for_method.name) or { return }
/*
vals := m.attrs[0].split('/')
args := vals.filter(it.starts_with(':')).map(it[1..])
Expand Down Expand Up @@ -199,7 +199,7 @@ fn (mut g Gen) comptime_call(mut node ast.ComptimeCall) {
g.write('(*(${g.base_type(m.return_type)}*)')
}
// TODO: check argument types
g.write('${util.no_dots(sym.name)}_${g.comptime.comptime_for_method}(')
g.write('${util.no_dots(sym.name)}_${g.comptime.comptime_for_method.name}(')

// try to see if we need to pass a pointer
if mut node.left is ast.Ident {
Expand Down Expand Up @@ -583,15 +583,15 @@ fn (mut g Gen) comptime_if_cond(cond ast.Expr, pkg_exist bool) (bool, bool) {
.eq, .ne {
// TODO: Implement `$if method.args.len == 1`
if cond.left is ast.SelectorExpr && (g.comptime.comptime_for_field_var.len > 0
|| g.comptime.comptime_for_method.len > 0) {
|| g.comptime.comptime_for_method != unsafe { nil }) {
if cond.right is ast.StringLiteral {
if cond.left.expr is ast.Ident && cond.left.field_name == 'name' {
if g.comptime.comptime_for_method_var.len > 0
&& cond.left.expr.name == g.comptime.comptime_for_method_var {
is_true := if cond.op == .eq {
g.comptime.comptime_for_method == cond.right.val
g.comptime.comptime_for_method.name == cond.right.val
} else {
g.comptime.comptime_for_method != cond.right.val
g.comptime.comptime_for_method.name != cond.right.val
}
if is_true {
g.write('1')
Expand Down Expand Up @@ -828,7 +828,7 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) {
}
}
}
g.comptime.comptime_for_method = method.name
g.comptime.comptime_for_method = unsafe { &method }
g.comptime.comptime_for_method_var = node.val_var
g.writeln('/* method ${i} */ {')
g.writeln('\t${node.val_var}.name = _SLIT("${method.name}");')
Expand All @@ -842,10 +842,10 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) {
}
if method.params.len < 2 {
// 0 or 1 (the receiver) args
g.writeln('\t${node.val_var}.args = __new_array_with_default(0, 0, sizeof(MethodArgs), 0);')
g.writeln('\t${node.val_var}.args = __new_array_with_default(0, 0, sizeof(MethodParam), 0);')
} else {
len := method.params.len - 1
g.write('\t${node.val_var}.args = new_array_from_c_array(${len}, ${len}, sizeof(MethodArgs), _MOV((MethodArgs[${len}]){')
g.write('\t${node.val_var}.args = new_array_from_c_array(${len}, ${len}, sizeof(MethodParam), _MOV((MethodParam[${len}]){')
// Skip receiver arg
for j, arg in method.params[1..] {
typ := arg.typ.idx()
Expand Down Expand Up @@ -1020,6 +1020,26 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) {
}
g.pop_comptime_info()
}
} else if node.kind == .params {
method := g.comptime.comptime_for_method

if method.params.len > 0 {
g.writeln('\tMethodParam ${node.val_var} = {0};')
}
g.push_new_comptime_info()
g.comptime.inside_comptime_for = true
g.comptime.comptime_for_method_param_var = node.val_var
for param in method.params[1..] {
g.comptime.type_map['${node.val_var}.typ'] = param.typ

g.writeln('/* method param ${i} */ {')
g.writeln('\t${node.val_var}.typ = ${int(param.typ)};')
g.writeln('\t${node.val_var}.name = _SLIT("${param.name}");')
g.stmts(node.stmts)
g.writeln('}')
i++
}
g.pop_comptime_info()
}
g.indent--
g.writeln('}// \$for')
Expand Down
10 changes: 5 additions & 5 deletions vlib/v/gen/c/fn.v
Original file line number Diff line number Diff line change
Expand Up @@ -1122,7 +1122,7 @@ fn (mut g Gen) gen_to_str_method_call(node ast.CallExpr) bool {
} else if left_node is ast.ComptimeCall {
if left_node.method_name == 'method' {
sym := g.table.sym(g.unwrap_generic(left_node.left_type))
if m := sym.find_method(g.comptime.comptime_for_method) {
if m := sym.find_method(g.comptime.comptime_for_method.name) {
rec_type = m.return_type
g.gen_expr_to_string(left_node, rec_type)
return true
Expand Down Expand Up @@ -1436,7 +1436,7 @@ fn (mut g Gen) resolve_comptime_args(func ast.Fn, mut node_ ast.CallExpr, concre
if call_arg.expr.method_name == 'method' {
sym := g.table.sym(g.unwrap_generic(call_arg.expr.left_type))
// `app.$method()`
if m := sym.find_method(g.comptime.comptime_for_method) {
if m := sym.find_method(g.comptime.comptime_for_method.name) {
comptime_args[k] = m.return_type
}
}
Expand Down Expand Up @@ -2043,7 +2043,7 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
// Handle `print(x)`
mut print_auto_str := false
if is_print && (node.args[0].typ != ast.string_type
|| g.comptime.comptime_for_method.len > 0
|| g.comptime.comptime_for_method != unsafe { nil }
|| g.comptime.is_comptime_var(node.args[0].expr)) {
g.inside_interface_deref = true
defer {
Expand All @@ -2059,7 +2059,7 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
if typ == 0 {
g.checker_bug('print arg.typ is 0', node.pos)
}
if typ != ast.string_type || g.comptime.comptime_for_method.len > 0 {
if typ != ast.string_type || g.comptime.comptime_for_method != unsafe { nil } {
expr := node.args[0].expr
typ_sym := g.table.sym(typ)
if typ_sym.kind == .interface_ && (typ_sym.info as ast.Interface).defines_method('str') {
Expand Down Expand Up @@ -2090,7 +2090,7 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
} else if expr is ast.ComptimeCall {
if expr.method_name == 'method' {
sym := g.table.sym(g.unwrap_generic(expr.left_type))
if m := sym.find_method(g.comptime.comptime_for_method) {
if m := sym.find_method(g.comptime.comptime_for_method.name) {
typ = m.return_type
}
}
Expand Down
2 changes: 1 addition & 1 deletion vlib/v/gen/c/str_intp.v
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ fn (mut g Gen) str_val(node ast.StringInterLiteral, i int, fmts []u8) {
fmt := fmts[i]
typ := g.unwrap_generic(node.expr_types[i])
typ_sym := g.table.sym(typ)
if typ == ast.string_type && g.comptime.comptime_for_method.len == 0 {
if typ == ast.string_type && g.comptime.comptime_for_method == unsafe { nil } {
if g.inside_vweb_tmpl {
g.write('${g.vweb_filter_fn_name}(')
if expr.is_auto_deref_var() && fmt != `p` {
Expand Down
10 changes: 9 additions & 1 deletion vlib/v/parser/comptime.v
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,14 @@ fn (mut p Parser) comptime_for() ast.ComptimeFor {
p.close_scope()
}
match for_val {
'params' {
p.scope.register(ast.Var{
name: val_var
typ: p.table.find_type_idx('MethodParam')
pos: var_pos
})
kind = .params
}
'methods' {
p.scope.register(ast.Var{
name: val_var
Expand Down Expand Up @@ -403,7 +411,7 @@ fn (mut p Parser) comptime_for() ast.ComptimeFor {
kind = .attributes
}
else {
p.error_with_pos('unknown kind `${for_val}`, available are: `methods`, `fields`, `values`, `variants` or `attributes`',
p.error_with_pos('unknown kind `${for_val}`, available are: `methods`, `fields`, `values`, `variants`, `attributes` or `params`',
p.prev_tok.pos())
return ast.ComptimeFor{}
}
Expand Down
2 changes: 1 addition & 1 deletion vlib/v/parser/tests/comptime_unknown_meta_kind_err.out
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
vlib/v/parser/tests/comptime_unknown_meta_kind_err.vv:7:20: error: unknown kind `abcde`, available are: `methods`, `fields`, `values`, `variants` or `attributes`
vlib/v/parser/tests/comptime_unknown_meta_kind_err.vv:7:20: error: unknown kind `abcde`, available are: `methods`, `fields`, `values`, `variants`, `attributes` or `params`
5 |
6 | fn test_main() {
7 | $for item in Test.abcde {
Expand Down
30 changes: 30 additions & 0 deletions vlib/v/tests/comptime/comptime_for_method_param_test.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
module main

struct Struct1 {
num f64
}

fn (mut s Struct1) do_thing(a f32, b voidptr) bool {
return false
}

fn register[T]() []string {
mut args := []string{}
$for method in T.methods {
$for arg in method.params {
$if arg.typ is f32 {
args << 'f32: ${arg.name} ${typeof(arg.typ).name}'
} $else $if arg.typ is voidptr {
args << '&void: ${arg.name} ${typeof(arg.typ).name}'
} $else {
}
}
}
return args
}

pub fn test_main() {
args := register[Struct1]()
assert args[0] == 'f32: a f32'
assert args[1] == '&void: b voidptr'
}

0 comments on commit 1187e13

Please sign in to comment.