diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 81abf3e21d..f2225f0021 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -1614,8 +1614,10 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type { return table.string_type } ast.AnonFn { + keep_ret_type := c.fn_return_type c.fn_return_type = it.decl.return_type c.stmts(it.decl.stmts) + c.fn_return_type = keep_ret_type return it.typ } else { diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index 958338dd3e..20700e1f43 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -358,7 +358,10 @@ typedef struct { .function { info := typ.info as table.FnType func := info.func - if !info.has_decl && !info.is_anon { + sym := g.table.get_type_symbol(func.return_type) + is_multi := sym.kind == .multi_return + is_fn_sig := func.name == '' + if !info.has_decl && (!info.is_anon || is_fn_sig) && !is_multi { fn_name := if func.is_c { func.name.replace('.', '__') } else if info.is_anon { @@ -556,6 +559,7 @@ fn (mut g Gen) stmt(node ast.Stmt) { println('build module `$g.module_built` fn `$it.name`') } } + keep_fn_decl := g.fn_decl g.fn_decl = it // &it if it.name == 'main' { // just remember `it`; main code will be generated in finish() @@ -563,7 +567,7 @@ fn (mut g Gen) stmt(node ast.Stmt) { } else { g.gen_fn_decl(it) } - g.fn_decl = 0 + g.fn_decl = keep_fn_decl if skip { g.out.go_back_to(pos) } @@ -820,15 +824,22 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { if assign_stmt.is_static { g.write('static ') } - if assign_stmt.left.len > assign_stmt.right.len { + mut return_type := table.void_type + if assign_stmt.right[0] is ast.CallExpr { + it := assign_stmt.right[0] as ast.CallExpr + return_type = it.return_type + } + mut is_multi := false + // json_test failed w/o this check + if return_type != 0 { + sym := g.table.get_type_symbol(return_type) + // the left vs. right is ugly and should be removed + is_multi = sym.kind == .multi_return || assign_stmt.left.len > assign_stmt.right.len || + assign_stmt.left.len > 1 + } + if is_multi { // multi return mut or_stmts := []ast.Stmt{} - mut return_type := table.void_type - if assign_stmt.right[0] is ast.CallExpr { - it := assign_stmt.right[0] as ast.CallExpr - or_stmts = it.or_block.stmts - return_type = it.return_type - } is_optional := return_type.flag_is(.optional) mr_var_name := 'mr_$assign_stmt.pos.pos' mr_styp := g.typ(return_type) @@ -865,7 +876,7 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { styp := g.typ(ident_var_info.typ) mut is_call := false mut or_stmts := []ast.Stmt{} - mut return_type := table.void_type + blank_assign := ident.kind == .blank_ident match val { ast.CallExpr { is_call = true @@ -874,6 +885,9 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { } ast.AnonFn { // TODO: no buffer fiddling + if blank_assign { + g.write('{') + } ret_styp := g.typ(it.decl.return_type) g.write('$ret_styp (*$ident.name) (') def_pos := g.definitions.len @@ -882,13 +896,16 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { g.write(') = ') g.expr(*it) g.writeln(';') + if blank_assign { + g.write('}') + } continue } else {} } gen_or := is_call && return_type.flag_is(.optional) g.is_assign_rhs = true - if ident.kind == .blank_ident { + if blank_assign { if is_call { g.expr(val) } else { @@ -1909,8 +1926,13 @@ fn (mut g Gen) return_statement(node ast.Return) { return } fn_return_is_optional := g.fn_decl.return_type.flag_is(.optional) - // multiple returns - if node.exprs.len > 1 { + + // got to do a correct check for multireturn + sym := g.table.get_type_symbol(g.fn_decl.return_type) + fn_return_is_multi := sym.kind == .multi_return + + // optional multi not supported + if fn_return_is_multi && !fn_return_is_optional { g.write(' ') // typ_sym := g.table.get_type_symbol(g.fn_decl.return_type) // mr_info := typ_sym.info as table.MultiReturn @@ -1933,7 +1955,7 @@ fn (mut g Gen) return_statement(node ast.Return) { if fn_return_is_optional { g.write(' }, sizeof($styp))') } - } else if node.exprs.len == 1 { + } else if node.exprs.len >= 1 { // normal return g.write(' ') return_sym := g.table.get_type_symbol(node.types[0]) diff --git a/vlib/v/table/table.v b/vlib/v/table/table.v index f2685a6b09..7a7d6bd808 100644 --- a/vlib/v/table/table.v +++ b/vlib/v/table/table.v @@ -369,11 +369,12 @@ pub fn (mut t Table) find_or_register_multi_return(mr_typs []Type) int { pub fn (mut t Table) find_or_register_fn_type(f Fn, is_anon, has_decl bool) int { name := if f.name.len == 0 { 'anon_fn_$f.signature()' } else { f.name } + anon := f.name.len == 0 || is_anon return t.register_type_symbol(TypeSymbol{ kind: .function name: name info: FnType{ - is_anon: f.name.len == 0 || is_anon + is_anon: anon has_decl: has_decl func: f } diff --git a/vlib/v/tests/fn_test.v b/vlib/v/tests/fn_test.v index 790c4a28f7..902991285e 100644 --- a/vlib/v/tests/fn_test.v +++ b/vlib/v/tests/fn_test.v @@ -120,6 +120,58 @@ fn high_fn_multi_return(a int, b fn (c []int, d []string) ([]int, []string)) { } +fn high_fn_return_single_anon() (fn(int)f32) { + _ := 1 + correct := fn(n int)f32 { + return n * n + } + return correct +} +fn high_fn_return_multi_anons() (fn(int)f32, fn(int)string) { + // parsing trap + _ := fn(n int)byte { + return 0x00 + } + correct_second := fn(n int)string { + return '$n' + } + correct_first := fn(n int)f32 { + return n * n + } + // parsing trap + _ := fn(n int)[]int { + return [n] + } + return correct_first, correct_second +} +fn high_fn_return_named_fn() (fn(int)int) { + return sqr +} +fn test_high_fn_ret_anons() { + param := 13 + func_sqr1 := high_fn_return_single_anon() + assert func_sqr1(param) == param * param + + func_sqr2, func_repr := high_fn_return_multi_anons() + assert func_sqr2(param) == (param * param) + assert func_repr(param) == '$param' + + top_lvl_sqr := high_fn_return_named_fn() + assert top_lvl_sqr(param) == param * param +} + +fn high_fn_applier(arg int, func fn(a int)string) string { + return func(arg) +} +fn test_high_fn_applier() { + arg := 13 + expect := '$arg $arg' + func := fn (arg int) string { + return '$arg $arg' + } + assert expect == high_fn_applier(arg, func) +} + fn sqr(x int) int { return x * x }