Skip to content

Commit 3324dd3

Browse files
authored
checker: fix autofree crash with option/result method chains (#26694)
* checker: fix autofree crash with option/result method chains * disable sanitizers tests for now
1 parent 5d6de17 commit 3324dd3

2 files changed

Lines changed: 49 additions & 1 deletion

File tree

vlib/v/checker/fn.v

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -817,11 +817,15 @@ fn (mut c Checker) call_expr(mut node ast.CallExpr) ast.Type {
817817
continue
818818
}
819819
if arg.expr in [ast.Ident, ast.StringLiteral, ast.SelectorExpr, ast.ComptimeSelector]
820-
|| (arg.expr is ast.CallExpr && arg.expr.or_block.kind != .absent) {
820+
|| autofree_expr_has_or_block_in_chain(arg.expr) {
821821
// Simple expressions like variables, string literals, selector expressions
822822
// (`x.field`) can't result in allocations and don't need to be assigned to
823823
// temporary vars.
824824
// Only expressions like `str + 'b'` need to be freed.
825+
// Expressions with result/option propagation in the call chain (e.g.
826+
// `get_str()!.to_upper()`) cannot be pre-generated safely by
827+
// autofree_call_pregen, because the or_block unwrapping internally calls
828+
// go_before_last_stmt() which garbles the output buffer.
825829
continue
826830
}
827831
if arg.expr is ast.CallExpr && arg.expr.name in ['json.encode', 'json.encode_pretty'] {
@@ -4161,3 +4165,19 @@ fn (mut c Checker) check_variadic_arg(arg_expr ast.Expr, typ ast.Type, expected_
41614165
arg_pos)
41624166
}
41634167
}
4168+
4169+
// autofree_expr_has_or_block_in_chain returns true if expr is a CallExpr whose call chain
4170+
// contains an or_block (i.e. result/option propagation via `!` or `?`). Used by autofree to
4171+
// skip tmp-var pre-generation for arguments like `get_str()!.to_upper()`, where the inner
4172+
// call's or_block would interfere with output-buffer positioning in autofree_call_pregen.
4173+
fn autofree_expr_has_or_block_in_chain(expr ast.Expr) bool {
4174+
if expr is ast.CallExpr {
4175+
if expr.or_block.kind != .absent {
4176+
return true
4177+
}
4178+
if expr.is_method {
4179+
return autofree_expr_has_or_block_in_chain(expr.left)
4180+
}
4181+
}
4182+
return false
4183+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// vtest vflags: -autofree
2+
// vtest build: !sanitize-address-gcc && !sanitize-address-clang
3+
4+
fn get_str_a() ?string {
5+
return 'hello world?'
6+
}
7+
8+
fn process_a(s string) string {
9+
return s.to_upper()
10+
}
11+
12+
fn get_str_b() !string {
13+
return 'hello world!'
14+
}
15+
16+
fn process_b(s string) string {
17+
return s.to_upper()
18+
}
19+
20+
fn test_autofree_chain_a() {
21+
result := process_a(get_str_a()?.to_upper())
22+
assert result == 'HELLO WORLD?'
23+
}
24+
25+
fn test_autofree_chain_b() {
26+
result := process_b(get_str_b()!.to_upper())
27+
assert result == 'HELLO WORLD!'
28+
}

0 commit comments

Comments
 (0)