mirror of
https://github.com/vlang/v.git
synced 2025-09-13 14:32:26 +03:00
all: add comptime support for traversing the method parameters with $for arg in method.params {
(#22229)
This commit is contained in:
parent
15bf8222b9
commit
1187e1367c
12 changed files with 100 additions and 26 deletions
|
@ -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
|
||||
|
@ -102,7 +102,7 @@ pub struct FunctionData {
|
|||
pub:
|
||||
name string
|
||||
attrs []string
|
||||
args []MethodArgs
|
||||
args []MethodParam
|
||||
return_type int
|
||||
typ int
|
||||
}
|
||||
|
|
|
@ -1250,6 +1250,7 @@ pub enum ComptimeForKind {
|
|||
attributes
|
||||
values
|
||||
variants
|
||||
params
|
||||
}
|
||||
|
||||
pub struct ComptimeFor {
|
||||
|
|
|
@ -896,5 +896,6 @@ pub fn (e ComptimeForKind) str() string {
|
|||
.attributes { return 'attributes' }
|
||||
.values { return 'values' }
|
||||
.variants { return 'variants' }
|
||||
.params { return 'params' }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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'))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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',
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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..])
|
||||
|
@ -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 {
|
||||
|
@ -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')
|
||||
|
@ -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}");')
|
||||
|
@ -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()
|
||||
|
@ -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')
|
||||
|
|
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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') {
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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` {
|
||||
|
|
|
@ -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
|
||||
|
@ -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{}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
30
vlib/v/tests/comptime/comptime_for_method_param_test.v
Normal file
30
vlib/v/tests/comptime/comptime_for_method_param_test.v
Normal file
|
@ -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'
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue