From 4cf14a8ba384a60f1a26673f0f70297786c53c63 Mon Sep 17 00:00:00 2001 From: Carlos Esquerdo Bernat Date: Fri, 13 Feb 2026 11:19:12 +0100 Subject: [PATCH] autofree: remove duplicate `free()` calls after a `return` statement, triggered by error string interpolation (#26592) --- vlib/v/gen/c/cgen.v | 7 +++++-- .../valgrind/autofree_or_block_string_interp.v | 17 +++++++++++++++++ vlib/v/slow_tests/valgrind/valgrind_test.v | 1 + 3 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 vlib/v/slow_tests/valgrind/autofree_or_block_string_interp.v diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 831eadf05..ee6ef0a0f 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -2232,19 +2232,22 @@ fn is_noreturn_callexpr(expr ast.Expr) bool { } // stmts_with_tmp_var is used in `if` or `match` branches. -// It returns true, if the last statement was a `return` +// It returns true, if the last statement was a `return` or `branch` fn (mut g Gen) stmts_with_tmp_var(stmts []ast.Stmt, tmp_var string) bool { g.indent++ if g.inside_ternary > 0 { g.write('(') } expected_cast_type := g.expected_cast_type + // Track if last statement was return or branch (for autofree, both skip scope cleanup) mut last_stmt_was_return := false for i, stmt in stmts { if i == stmts.len - 1 { g.expected_cast_type = expected_cast_type if stmt is ast.Return { last_stmt_was_return = true + } else if stmt is ast.BranchStmt { + last_stmt_was_return = true } } if i == stmts.len - 1 && tmp_var != '' { @@ -2402,7 +2405,7 @@ fn (mut g Gen) stmts_with_tmp_var(stmts []ast.Stmt, tmp_var string) bool { if g.inside_ternary > 0 { g.write2('', ')') } - if g.is_autofree && !g.inside_vweb_tmpl && stmts.len > 0 { + if g.is_autofree && !g.inside_vweb_tmpl && stmts.len > 0 && !last_stmt_was_return { // use the first stmt to get the scope stmt := stmts[0] if stmt !is ast.FnDecl && g.inside_ternary == 0 { diff --git a/vlib/v/slow_tests/valgrind/autofree_or_block_string_interp.v b/vlib/v/slow_tests/valgrind/autofree_or_block_string_interp.v new file mode 100644 index 000000000..ff1996d2a --- /dev/null +++ b/vlib/v/slow_tests/valgrind/autofree_or_block_string_interp.v @@ -0,0 +1,17 @@ +// vtest vflags: -autofree +// Test that autofree doesn't generate duplicate free statements +// after return in or blocks with string interpolation + +fn main() { + x := 'test' + _ := somefn() or { + msg := 'fail' + println('Error: ${msg}') + return + } + println('Success') +} + +fn somefn() !int { + return error('test') +} diff --git a/vlib/v/slow_tests/valgrind/valgrind_test.v b/vlib/v/slow_tests/valgrind/valgrind_test.v index da0250d01..0d4990613 100644 --- a/vlib/v/slow_tests/valgrind/valgrind_test.v +++ b/vlib/v/slow_tests/valgrind/valgrind_test.v @@ -26,6 +26,7 @@ const skip_compile_files = [ ] const skip_valgrind_files = [ + 'vlib/v/slow_tests/valgrind/autofree_or_block_string_interp.v', 'vlib/v/slow_tests/valgrind/struct_field.v', 'vlib/v/slow_tests/valgrind/fn_returning_string_param.v', 'vlib/v/slow_tests/valgrind/fn_with_return_should_free_local_vars.v', -- 2.39.5