From 78998a09e4761bf0df6ffade708720a959a390fd Mon Sep 17 00:00:00 2001 From: yuyi Date: Sun, 4 Sep 2022 19:03:24 +0800 Subject: [PATCH] cgen: fix go call anon fn with closure (#15656) --- vlib/v/gen/c/assign.v | 3 +- vlib/v/gen/c/auto_eq_methods.v | 3 +- vlib/v/gen/c/fn.v | 87 ++++++++++++++----- vlib/v/gen/c/utils.v | 12 +-- .../tests/go_call_anon_fn_with_closure_test.v | 27 ++++++ 5 files changed, 102 insertions(+), 30 deletions(-) create mode 100644 vlib/v/tests/go_call_anon_fn_with_closure_test.v diff --git a/vlib/v/gen/c/assign.v b/vlib/v/gen/c/assign.v index 635da4e73..450c236f0 100644 --- a/vlib/v/gen/c/assign.v +++ b/vlib/v/gen/c/assign.v @@ -155,7 +155,8 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) { } // if it's a decl assign (`:=`) or a blank assignment `_ =`/`_ :=` then generate `void (*ident) (args) =` if (is_decl || blank_assign) && left is ast.Ident { - sig := g.fn_var_signature(val.decl.return_type, val.decl.params, ident.name) + sig := g.fn_var_signature(val.decl.return_type, val.decl.params.map(it.typ), + ident.name) g.write(sig + ' = ') } else { g.is_assign_lhs = true diff --git a/vlib/v/gen/c/auto_eq_methods.v b/vlib/v/gen/c/auto_eq_methods.v index 37182a305..56a47e4ae 100644 --- a/vlib/v/gen/c/auto_eq_methods.v +++ b/vlib/v/gen/c/auto_eq_methods.v @@ -344,7 +344,8 @@ fn (mut g Gen) gen_map_equality_fn(left_type ast.Type) string { kind := g.table.type_kind(value.typ) if kind == .function { info := value.sym.info as ast.FnType - sig := g.fn_var_signature(info.func.return_type, info.func.params, 'v') + sig := g.fn_var_signature(info.func.return_type, info.func.params.map(it.typ), + 'v') fn_builder.writeln('\t\t$sig = *(voidptr*)map_get(&a, k, &(voidptr[]){ 0 });') } else { fn_builder.writeln('\t\t$ptr_value_styp v = *($ptr_value_styp*)map_get(&a, k, &($ptr_value_styp[]){ 0 });') diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index 2906205c7..51395438e 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -491,7 +491,7 @@ fn (mut g Gen) gen_anon_fn_decl(mut node ast.AnonFn) { for var in node.inherited_vars { var_sym := g.table.sym(var.typ) if var_sym.info is ast.FnType { - sig := g.fn_var_signature(var_sym.info.func.return_type, var_sym.info.func.params, + sig := g.fn_var_signature(var_sym.info.func.return_type, var_sym.info.func.params.map(it.typ), var.name) builder.writeln('\t' + sig + ';') } else { @@ -1693,8 +1693,10 @@ fn (mut g Gen) go_expr(node ast.GoExpr) { mut handle := '' tmp := g.new_tmp_var() mut expr := node.call_expr - mut name := expr.name // util.no_dots(expr.name) - // TODO: fn call is duplicated. merge with fn_call(). + mut name := expr.name + mut use_tmp_fn_var := false + tmp_fn := g.new_tmp_var() + for i, concrete_type in expr.concrete_types { if concrete_type != ast.void_type && concrete_type != 0 { // Using _T_ to differentiate between get and get_string @@ -1709,10 +1711,17 @@ fn (mut g Gen) go_expr(node ast.GoExpr) { receiver_sym := g.table.sym(expr.receiver_type) name = receiver_sym.name + '_' + name } else if mut expr.left is ast.AnonFn { - g.gen_anon_fn_decl(mut expr.left) - name = expr.left.decl.name - } else if expr.is_fn_var { - name = g.table.sym(expr.fn_var_type).name + if expr.left.inherited_vars.len > 0 { + fn_var := g.fn_var_signature(expr.left.decl.return_type, expr.left.decl.params.map(it.typ), + tmp_fn) + g.write('\t$fn_var = ') + g.gen_anon_fn(mut expr.left) + g.writeln(';') + use_tmp_fn_var = true + } else { + g.gen_anon_fn_decl(mut expr.left) + name = expr.left.decl.name + } } name = util.no_dots(name) if g.pref.obfuscate && g.cur_mod.name == 'main' && name.starts_with('main__') { @@ -1732,6 +1741,10 @@ fn (mut g Gen) go_expr(node ast.GoExpr) { wrapper_fn_name := name + '_thread_wrapper' arg_tmp_var := 'arg_' + tmp g.writeln('$wrapper_struct_name *$arg_tmp_var = malloc(sizeof(thread_arg_$name));') + fn_name := if use_tmp_fn_var { tmp_fn } else { name } + if !(expr.is_method && g.table.sym(expr.receiver_type).kind == .interface_) { + g.writeln('$arg_tmp_var->fn = $fn_name;') + } if expr.is_method { g.write('$arg_tmp_var->arg0 = ') g.expr(expr.left) @@ -1841,24 +1854,54 @@ fn (mut g Gen) go_expr(node ast.GoExpr) { } if should_register { g.type_definitions.writeln('\ntypedef struct $wrapper_struct_name {') + mut fn_var := '' + if node.call_expr.is_fn_var { + fn_sym := g.table.sym(node.call_expr.fn_var_type) + info := fn_sym.info as ast.FnType + fn_var = g.fn_var_signature(info.func.return_type, info.func.params.map(it.typ), + 'fn') + } else if node.call_expr.left is ast.AnonFn { + f := node.call_expr.left.decl + fn_var = g.fn_var_signature(f.return_type, f.params.map(it.typ), 'fn') + } else { + if node.call_expr.is_method { + rec_sym := g.table.sym(node.call_expr.receiver_type) + if f := g.table.find_method(rec_sym, node.call_expr.name) { + mut muttable := unsafe { &ast.Table(g.table) } + return_type := muttable.resolve_generic_to_concrete(f.return_type, + f.generic_names, node.call_expr.concrete_types) or { f.return_type } + mut arg_types := f.params.map(it.typ) + arg_types = arg_types.map(muttable.resolve_generic_to_concrete(it, + f.generic_names, node.call_expr.concrete_types) or { it }) + fn_var = g.fn_var_signature(return_type, arg_types, 'fn') + } + } else { + if f := g.table.find_fn(node.call_expr.name) { + mut muttable := unsafe { &ast.Table(g.table) } + return_type := muttable.resolve_generic_to_concrete(f.return_type, + f.generic_names, node.call_expr.concrete_types) or { f.return_type } + mut arg_types := f.params.map(it.typ) + arg_types = arg_types.map(muttable.resolve_generic_to_concrete(it, + f.generic_names, node.call_expr.concrete_types) or { it }) + fn_var = g.fn_var_signature(return_type, arg_types, 'fn') + } + } + } + g.type_definitions.writeln('\t$fn_var;') if expr.is_method { styp := g.typ(expr.receiver_type) g.type_definitions.writeln('\t$styp arg0;') } need_return_ptr := g.pref.os == .windows && node.call_expr.return_type != ast.void_type - if expr.args.len == 0 && !need_return_ptr { - g.type_definitions.writeln('EMPTY_STRUCT_DECLARATION;') - } else { - for i, arg in expr.args { - arg_sym := g.table.sym(arg.typ) - if arg_sym.info is ast.FnType { - sig := g.fn_var_signature(arg_sym.info.func.return_type, arg_sym.info.func.params, - 'arg${i + 1}') - g.type_definitions.writeln('\t' + sig + ';') - } else { - styp := g.typ(arg.typ) - g.type_definitions.writeln('\t$styp arg${i + 1};') - } + for i, arg in expr.args { + arg_sym := g.table.sym(arg.typ) + if arg_sym.info is ast.FnType { + sig := g.fn_var_signature(arg_sym.info.func.return_type, arg_sym.info.func.params.map(it.typ), + 'arg${i + 1}') + g.type_definitions.writeln('\t' + sig + ';') + } else { + styp := g.typ(arg.typ) + g.type_definitions.writeln('\t$styp arg${i + 1};') } } if need_return_ptr { @@ -1893,14 +1936,14 @@ fn (mut g Gen) go_expr(node ast.GoExpr) { g.gowrappers.write_string('arg->arg0') g.gowrappers.write_string('${dot}_object') } else { - g.gowrappers.write_string('${name}(') + g.gowrappers.write_string('arg->fn(') g.gowrappers.write_string('arg->arg0') } if expr.args.len > 0 { g.gowrappers.write_string(', ') } } else { - g.gowrappers.write_string('${name}(') + g.gowrappers.write_string('arg->fn(') } if expr.args.len > 0 { mut has_cast := false diff --git a/vlib/v/gen/c/utils.v b/vlib/v/gen/c/utils.v index 7582fafc2..07a9ca520 100644 --- a/vlib/v/gen/c/utils.v +++ b/vlib/v/gen/c/utils.v @@ -63,20 +63,20 @@ fn (mut g Gen) unwrap(typ ast.Type) Type { } // generate function variable definition, e.g. `void (*var_name) (int, string)` -fn (mut g Gen) fn_var_signature(return_type ast.Type, params []ast.Param, var_name string) string { +fn (mut g Gen) fn_var_signature(return_type ast.Type, arg_types []ast.Type, var_name string) string { ret_styp := g.typ(return_type) mut sig := '$ret_styp (*$var_name) (' - for j, arg in params { - arg_sym := g.table.sym(arg.typ) + for j, arg_typ in arg_types { + arg_sym := g.table.sym(arg_typ) if arg_sym.info is ast.FnType { func := arg_sym.info.func - arg_sig := g.fn_var_signature(func.return_type, func.params, '') + arg_sig := g.fn_var_signature(func.return_type, func.params.map(it.typ), '') sig += arg_sig } else { - arg_styp := g.typ(arg.typ) + arg_styp := g.typ(arg_typ) sig += arg_styp } - if j < params.len - 1 { + if j < arg_types.len - 1 { sig += ', ' } } diff --git a/vlib/v/tests/go_call_anon_fn_with_closure_test.v b/vlib/v/tests/go_call_anon_fn_with_closure_test.v new file mode 100644 index 000000000..4cdde80d5 --- /dev/null +++ b/vlib/v/tests/go_call_anon_fn_with_closure_test.v @@ -0,0 +1,27 @@ +fn test_go_call_anon_fn_with_closure1() { + a := 1 + + b := fn [a] () int { + println(a) + return a + } + + g := go b() + ret := g.wait() + assert ret == 1 +} + +fn test_go_call_anon_fn_with_closure2() { + m := { + 'key1': 1 + 'key2': 2 + } + + h := go fn [m] () int { + println(m['key2']) + return m['key2'] + }() + + ret := h.wait() + assert ret == 2 +} -- 2.30.2