From ca198ace7d187e545bed1d462d4f0bd1d29a11d0 Mon Sep 17 00:00:00 2001 From: Felipe Pena Date: Fri, 24 Mar 2023 12:36:37 -0300 Subject: [PATCH] cgen: fix nested fn call with result/option propagation (#17738) --- vlib/v/gen/c/cgen.v | 2 ++ vlib/v/gen/c/fn.v | 41 ++++++++++++++++++++---- vlib/v/parser/parser.v | 18 +++++++++++ vlib/v/tests/call_on_anon_test.v | 55 ++++++++++++++++++++++++++++++++ 4 files changed, 110 insertions(+), 6 deletions(-) create mode 100644 vlib/v/tests/call_on_anon_test.v diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 6f8241d492..bb79c3bac5 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -136,6 +136,7 @@ mut: inside_struct_init bool inside_or_block bool inside_call bool + inside_curry_call bool // inside foo()()!, foo()()?, foo()() inside_for_c_stmt bool inside_comptime_for_field bool inside_cast_in_heap int // inside cast to interface type in heap (resolve recursive calls) @@ -144,6 +145,7 @@ mut: inside_lambda bool inside_for_in_any_cond bool inside_cinit bool + last_tmp_call_var []string loop_depth int ternary_names map[string]string ternary_level_names map[string][]string diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index 8e32578610..2db16737b1 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -677,8 +677,29 @@ fn (mut g Gen) call_expr(node ast.CallExpr) { g.is_fn_index_call = true g.expr(node.left) g.is_fn_index_call = false - } else if node.left is ast.CallExpr && node.name == '' { - g.expr(node.left) + } else if !g.inside_curry_call && node.left is ast.CallExpr && node.name == '' { + if node.or_block.kind == .absent { + g.expr(node.left) + } else { + old_inside_curry_call := g.inside_curry_call + g.inside_curry_call = true + ret_typ := node.return_type + + line := g.go_before_stmt(0) + g.empty_line = true + + tmp_res := g.new_tmp_var() + g.write('${g.typ(ret_typ)} ${tmp_res} = ') + + g.last_tmp_call_var << tmp_res + g.expr(node.left) + g.expr(node) + + g.inside_curry_call = old_inside_curry_call + g.write(line) + g.write('*(${g.base_type(ret_typ)}*)${tmp_res}.data') + return + } } old_inside_call := g.inside_call g.inside_call = true @@ -689,7 +710,7 @@ fn (mut g Gen) call_expr(node ast.CallExpr) { && g.pref.gc_mode in [.boehm_full, .boehm_incr, .boehm_full_opt, .boehm_incr_opt] gen_or := node.or_block.kind != .absent // && !g.is_autofree is_gen_or_and_assign_rhs := gen_or && !g.discard_or_result - mut cur_line := if is_gen_or_and_assign_rhs || gen_keep_alive { // && !g.is_autofree { + mut cur_line := if !g.inside_curry_call && (is_gen_or_and_assign_rhs || gen_keep_alive) { // && !g.is_autofree { // `x := foo() or { ...}` // cut everything that has been generated to prepend option variable creation line := g.go_before_stmt(0) @@ -699,7 +720,15 @@ fn (mut g Gen) call_expr(node ast.CallExpr) { '' } // g.write('/*EE line="$cur_line"*/') - tmp_opt := if gen_or || gen_keep_alive { g.new_tmp_var() } else { '' } + tmp_opt := if gen_or || gen_keep_alive { + if g.inside_curry_call && g.last_tmp_call_var.len > 0 { + g.last_tmp_call_var.pop() + } else { + g.new_tmp_var() + } + } else { + '' + } if gen_or || gen_keep_alive { mut ret_typ := node.return_type if g.table.sym(ret_typ).kind == .alias { @@ -717,7 +746,7 @@ fn (mut g Gen) call_expr(node ast.CallExpr) { g.writeln('if (${g.infix_left_var_name}) {') g.indent++ g.write('${tmp_opt} = ') - } else { + } else if !g.inside_curry_call { g.write('${styp} ${tmp_opt} = ') if node.left is ast.AnonFn { g.expr(node.left) @@ -752,7 +781,7 @@ fn (mut g Gen) call_expr(node ast.CallExpr) { } if unwrapped_typ == ast.void_type { g.write('\n ${cur_line}') - } else { + } else if !g.inside_curry_call { if !g.inside_const_opt_or_res { g.write('\n ${cur_line} (*(${unwrapped_styp}*)${tmp_opt}.data)') } else { diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 5cd07bd876..56041e75ac 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -2633,11 +2633,29 @@ pub fn (mut p Parser) name_expr() ast.Expr { pos := p.tok.pos() args := p.call_args() p.check(.rpar) + + mut or_kind := ast.OrKind.absent + mut or_stmts := []ast.Stmt{} + mut or_pos := p.tok.pos() + if p.tok.kind in [.not, .question] { + or_kind = if p.tok.kind == .not { .propagate_result } else { .propagate_option } + p.next() + } + if p.tok.kind == .key_orelse { + // `foo() or {}`` + or_kind = .block + or_stmts, or_pos = p.or_block(.with_err_var) + } node = ast.CallExpr{ left: node args: args pos: pos scope: p.scope + or_block: ast.OrExpr{ + stmts: or_stmts + kind: or_kind + pos: or_pos + } } } } diff --git a/vlib/v/tests/call_on_anon_test.v b/vlib/v/tests/call_on_anon_test.v new file mode 100644 index 0000000000..799056d807 --- /dev/null +++ b/vlib/v/tests/call_on_anon_test.v @@ -0,0 +1,55 @@ +fn f1(s string) fn (string) !string { + return fn [s] (str string) !string { + return s + str + } +} + +fn f2(s string) fn (string) ?string { + return fn [s] (str string) ?string { + return s + str + } +} + +fn f3(s string) fn (string) string { + return fn [s] (str string) string { + return s + str + } +} + +fn f4(s string) fn (string) ?string { + return fn (str string) ?string { + return none + } +} + +fn f5(s string) fn (string) !string { + return fn (str string) !string { + return error('test') + } +} + +fn test_call_nested_anon() { + println(main.f1('V')('Lang')!) + s1 := main.f1('V')('Lang')! + println(s1) + s2 := main.f1('V')('Lang') or { 'ErrLang' } + println(s2) + s3 := main.f2('V')('Lang')? + println(s3) + s4 := main.f2('V')('Lang') or { 'NoneLang' } + println(s4) + s := main.f3('V')('Lang') + println(s) + assert s == 'VLang' + assert s1 == 'VLang' + assert s2 == 'VLang' + assert s3 == 'VLang' + assert s4 == 'VLang' + + s5 := main.f4('V')('Lang') or { 'Lang++' } + println(s5) + assert s5 == 'Lang++' + s6 := main.f5('V')('Lang') or { '${err}' } + println(s6) + assert s6 == 'test' +}