ast, checker, cgen: fix generics multiple type comptime call (fix #12777) (#12806)

pull/12814/head
yuyi 2021-12-13 01:42:40 +08:00 committed by GitHub
parent ab6e93394f
commit f407d6de02
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 50 additions and 10 deletions

View File

@ -966,11 +966,11 @@ fn (t Tree) comptime_call(node ast.ComptimeCall) &Node {
obj.add_terse('is_vweb', t.bool_node(node.is_vweb)) obj.add_terse('is_vweb', t.bool_node(node.is_vweb))
obj.add_terse('vweb_tmpl', t.string_node(node.vweb_tmpl.path)) obj.add_terse('vweb_tmpl', t.string_node(node.vweb_tmpl.path))
obj.add_terse('args_var', t.string_node(node.args_var)) obj.add_terse('args_var', t.string_node(node.args_var))
obj.add_terse('sym', t.string_node(node.sym.name))
obj.add_terse('has_parens', t.bool_node(node.has_parens)) obj.add_terse('has_parens', t.bool_node(node.has_parens))
obj.add_terse('is_embed', t.bool_node(node.is_embed)) obj.add_terse('is_embed', t.bool_node(node.is_embed))
obj.add_terse('embed_file', t.embed_file(node.embed_file)) obj.add_terse('embed_file', t.embed_file(node.embed_file))
obj.add('method_pos', t.position(node.method_pos)) obj.add('method_pos', t.position(node.method_pos))
obj.add_terse('left_type', t.type_node(node.left_type))
obj.add_terse('result_type', t.type_node(node.result_type)) obj.add_terse('result_type', t.type_node(node.result_type))
obj.add('scope', t.scope(node.scope)) obj.add('scope', t.scope(node.scope))
obj.add_terse('env_value', t.string_node(node.env_value)) obj.add_terse('env_value', t.string_node(node.env_value))

View File

@ -1559,7 +1559,7 @@ pub:
// //
is_pkgconfig bool is_pkgconfig bool
pub mut: pub mut:
sym TypeSymbol left_type Type
result_type Type result_type Type
env_value string env_value string
args []CallArg args []CallArg

View File

@ -10,7 +10,7 @@ import v.pkgconfig
fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) ast.Type { fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) ast.Type {
sym := c.table.get_type_symbol(c.unwrap_generic(c.expr(node.left))) sym := c.table.get_type_symbol(c.unwrap_generic(c.expr(node.left)))
node.sym = *sym node.left_type = c.expr(node.left)
if node.is_env { if node.is_env {
env_value := util.resolve_env_value("\$env('$node.args_var')", false) or { env_value := util.resolve_env_value("\$env('$node.args_var')", false) or {
c.error(err.msg, node.env_pos) c.error(err.msg, node.env_pos)
@ -91,7 +91,7 @@ fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) ast.Type {
} else { } else {
c.error('todo: not a string literal', node.method_pos) c.error('todo: not a string literal', node.method_pos)
} }
f := node.sym.find_method(method_name) or { f := sym.find_method(method_name) or {
c.error('could not find method `$method_name`', node.method_pos) c.error('could not find method `$method_name`', node.method_pos)
return ast.void_type return ast.void_type
} }

View File

@ -76,10 +76,11 @@ fn (mut g Gen) comptime_call(mut node ast.ComptimeCall) {
} }
return return
} }
g.trace_autofree('// \$method call. sym="$node.sym.name"') sym := g.table.get_type_symbol(g.unwrap_generic(node.left_type))
g.trace_autofree('// \$method call. sym="$sym.name"')
if node.method_name == 'method' { if node.method_name == 'method' {
// `app.$method()` // `app.$method()`
m := node.sym.find_method(g.comptime_for_method) or { return } m := sym.find_method(g.comptime_for_method) or { return }
/* /*
vals := m.attrs[0].split('/') vals := m.attrs[0].split('/')
args := vals.filter(it.starts_with(':')).map(it[1..]) args := vals.filter(it.starts_with(':')).map(it[1..])
@ -99,7 +100,7 @@ fn (mut g Gen) comptime_call(mut node ast.ComptimeCall) {
// check argument length and types // check argument length and types
if m.params.len - 1 != node.args.len && !expand_strs { if m.params.len - 1 != node.args.len && !expand_strs {
// do not generate anything if the argument lengths don't match // do not generate anything if the argument lengths don't match
g.writeln('/* skipping ${node.sym.name}.$m.name due to mismatched arguments list */') 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"));') // 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' + // eprintln('info: skipping ${node.sym.name}.$m.name due to mismatched arguments list\n' +
//'method.params: $m.params, args: $node.args\n\n') //'method.params: $m.params, args: $node.args\n\n')
@ -107,7 +108,7 @@ fn (mut g Gen) comptime_call(mut node ast.ComptimeCall) {
return return
} }
// TODO: check argument types // TODO: check argument types
g.write('${util.no_dots(node.sym.name)}_${g.comptime_for_method}(') g.write('${util.no_dots(sym.name)}_${g.comptime_for_method}(')
// try to see if we need to pass a pointer // try to see if we need to pass a pointer
if node.left is ast.Ident { if node.left is ast.Ident {
@ -153,7 +154,7 @@ fn (mut g Gen) comptime_call(mut node ast.ComptimeCall) {
return return
} }
mut j := 0 mut j := 0
for method in node.sym.methods { for method in sym.methods {
// if method.return_type != ast.void_type { // if method.return_type != ast.void_type {
if method.return_type != node.result_type { if method.return_type != node.result_type {
continue continue
@ -172,7 +173,7 @@ fn (mut g Gen) comptime_call(mut node ast.ComptimeCall) {
} }
g.write('if (string__eq($node.method_name, _SLIT("$method.name"))) ') g.write('if (string__eq($node.method_name, _SLIT("$method.name"))) ')
} }
g.write('${util.no_dots(node.sym.name)}_${method.name}($amp ') g.write('${util.no_dots(sym.name)}_${method.name}($amp ')
g.expr(node.left) g.expr(node.left)
g.writeln(');') g.writeln(');')
j++ j++

View File

@ -0,0 +1,39 @@
struct Foo {}
['/'; 'GET']
fn (mut f Foo) hello() string {
return @FN
}
struct Bar {}
['/'; 'GET']
fn (b &Bar) world() string {
return @FN
}
fn execute_methods<T>() string {
tmp := T{}
$for method in T.methods {
if method.attrs.len >= 2 {
fun_path := method.attrs[0]
fun_method := method.attrs[1]
if fun_path == '/' && fun_method == 'GET' {
ret := tmp.$method()
return ret
}
}
}
return ''
}
fn test_generics_multi_type_comptime_call() {
ret1 := execute_methods<Foo>()
println(ret1)
assert ret1 == 'hello'
ret2 := execute_methods<Bar>()
println(ret2)
assert ret2 == 'world'
}