Skip to content

Commit 4927890

Browse files
authored
autofree: fix support for match exprs with nested if exprs (#26599)
1 parent 95f0fa9 commit 4927890

4 files changed

Lines changed: 118 additions & 4 deletions

File tree

vlib/v/gen/c/cgen.v

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ mut:
164164
inside_global_decl bool
165165
inside_interface_deref bool
166166
inside_assign_fn_var bool
167+
outer_tmp_var string // tmp var from outer context (e.g. from stmts_with_tmp_var) to be used by nested if/match expressions
167168
last_tmp_call_var []string
168169
last_if_option_type ast.Type // stores the expected if type on nested if expr
169170
loop_depth int
@@ -2356,6 +2357,7 @@ fn (mut g Gen) stmts_with_tmp_var(stmts []ast.Stmt, tmp_var string) bool {
23562357
g.set_current_pos_as_last_stmt_pos()
23572358
g.skip_stmt_pos = true
23582359
mut is_noreturn := false
2360+
mut is_if_expr_with_tmp := false
23592361
if stmt in [ast.Return, ast.BranchStmt] {
23602362
is_noreturn = true
23612363
} else if stmt is ast.ExprStmt {
@@ -2364,15 +2366,24 @@ fn (mut g Gen) stmts_with_tmp_var(stmts []ast.Stmt, tmp_var string) bool {
23642366
is_array_fixed_init = true
23652367
ret_type = stmt.expr.typ
23662368
}
2369+
if stmt.expr is ast.IfExpr && g.is_autofree && !g.inside_if_option
2370+
&& !g.inside_if_result {
2371+
is_if_expr_with_tmp = true
2372+
g.outer_tmp_var = tmp_var
2373+
}
23672374
}
2368-
if !is_noreturn {
2375+
if !is_noreturn && !is_if_expr_with_tmp {
23692376
if is_array_fixed_init {
23702377
g.write('memcpy(${tmp_var}, (${g.styp(ret_type)})')
23712378
} else {
23722379
g.write('${tmp_var} = ')
23732380
}
23742381
}
23752382
g.stmt(stmt)
2383+
// Reset outer_tmp_var after processing
2384+
if is_if_expr_with_tmp {
2385+
g.outer_tmp_var = ''
2386+
}
23762387
if is_array_fixed_init {
23772388
lines := g.go_before_last_stmt().trim_right('; \n')
23782389
g.writeln('${lines}, sizeof(${tmp_var}));')

vlib/v/gen/c/if.v

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -181,15 +181,24 @@ fn (mut g Gen) needs_conds_order(node ast.IfExpr) bool {
181181
}
182182

183183
fn (mut g Gen) if_expr(node ast.IfExpr) {
184+
use_outer_tmp := g.outer_tmp_var != ''
185+
saved_outer_tmp_var := g.outer_tmp_var
186+
if use_outer_tmp {
187+
g.outer_tmp_var = ''
188+
}
189+
184190
// For simple if expressions we can use C's `?:`
185191
// `if x > 0 { 1 } else { 2 }` => `(x > 0)? (1) : (2)`
186192
// For if expressions with multiple statements or another if expression inside, it's much
187193
// easier to use a temp var, than do C tricks with commas, introduce special vars etc
188194
// (as it used to be done).
189195
// Always use this in -autofree, since ?: can have tmp expressions that have to be freed.
190-
needs_tmp_var := g.inside_if_option || g.need_tmp_var_in_if(node)
196+
needs_tmp_var := g.inside_if_option || g.need_tmp_var_in_if(node) || use_outer_tmp
191197
needs_conds_order := g.needs_conds_order(node)
192-
tmp := if g.inside_if_option || (node.typ != ast.void_type && needs_tmp_var) {
198+
tmp := if use_outer_tmp {
199+
// Use the tmp var from outer context (e.g. from stmts_with_tmp_var)
200+
saved_outer_tmp_var
201+
} else if g.inside_if_option || (node.typ != ast.void_type && needs_tmp_var) {
193202
g.new_tmp_var()
194203
} else {
195204
''
@@ -267,7 +276,8 @@ fn (mut g Gen) if_expr(node ast.IfExpr) {
267276
}
268277
cur_line = g.go_before_last_stmt()
269278
g.empty_line = true
270-
if tmp != '' {
279+
if tmp != '' && !use_outer_tmp {
280+
// Only declare the tmp var if it's not from outer context
271281
if node.typ == ast.void_type && g.last_if_option_type != 0 {
272282
// nested if on return stmt
273283
g.write2(g.styp(g.unwrap_generic(g.last_if_option_type)), ' ')
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// vtest vflags: -autofree
2+
// vtest build: !sanitize-address-gcc && !sanitize-address-clang
3+
4+
type Result = Success | Failure
5+
6+
struct Success {
7+
value int
8+
}
9+
10+
struct Failure {
11+
msg string
12+
}
13+
14+
fn process(r Result) Result {
15+
return match r {
16+
Success {
17+
if r.value > 10 {
18+
Success{
19+
value: r.value * 2
20+
}
21+
} else {
22+
r
23+
}
24+
}
25+
Failure {
26+
r
27+
}
28+
}
29+
}
30+
31+
fn test_autofree_match_with_nested_if() {
32+
r := process(Success{
33+
value: 15
34+
})
35+
assert r is Success
36+
assert (r as Success).value == 30
37+
}
38+
39+
fn test_autofree_match_with_nested_if_else() {
40+
r := process(Success{
41+
value: 5
42+
})
43+
assert r is Success
44+
assert (r as Success).value == 5
45+
}
46+
47+
fn test_autofree_match_error_branch() {
48+
r := process(Failure{
49+
msg: 'test error'
50+
})
51+
assert r is Failure
52+
assert (r as Failure).msg == 'test error'
53+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// vtest vflags: -autofree
2+
// vtest build: !sanitize-address-gcc && !sanitize-address-clang
3+
4+
type Tree[T] = Empty | Node[T]
5+
6+
struct Empty {}
7+
8+
struct Node[T] {
9+
value T
10+
left Tree[T]
11+
right Tree[T]
12+
}
13+
14+
fn (tree Tree[T]) delete[T](x T) Tree[T] {
15+
return match tree {
16+
Empty {
17+
tree
18+
}
19+
Node[T] {
20+
if tree.left !is Empty && tree.right !is Empty {
21+
if x < tree.value {
22+
Node[T]{
23+
...tree
24+
left: tree.left.delete(x)
25+
}
26+
} else {
27+
tree
28+
}
29+
} else {
30+
tree
31+
}
32+
}
33+
}
34+
}
35+
36+
fn test_autofree_match() {
37+
mut tree := Tree[int](Empty{})
38+
tree = tree.delete(5)
39+
assert tree is Empty
40+
}

0 commit comments

Comments
 (0)