1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

cgen: fix nested fn call with result/option propagation (#17738)

This commit is contained in:
Felipe Pena 2023-03-24 12:36:37 -03:00 committed by GitHub
parent dc11f1fe05
commit ca198ace7d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 110 additions and 6 deletions

View File

@ -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

View File

@ -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 {

View File

@ -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
}
}
}
}

View File

@ -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'
}