cgen: fix autofree used vars on return (fix #25196) (#25198)

This commit is contained in:
Felipe Pena 2025-08-30 17:39:34 -03:00 committed by GitHub
parent cb2756e39c
commit 9fb8aae2d7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 53 additions and 10 deletions

View file

@ -62,7 +62,7 @@ fn (mut g Gen) autofree_scope_vars2(scope &ast.Scope, start_pos int, end_pos int
match obj {
ast.Var {
g.trace_autofree('// var "${obj.name}" var.pos=${obj.pos.pos} var.line_nr=${obj.pos.line_nr}')
if obj.name == g.returned_var_name {
if obj.name in g.returned_var_names {
g.print_autofree_var(obj, 'returned from function')
g.trace_autofree('// skipping returned var')
continue
@ -247,3 +247,17 @@ fn (mut g Gen) autofree_var_call(free_fn_name string, v ast.Var) {
}
g.autofree_scope_stmts << af.str()
}
fn (mut g Gen) detect_used_var_on_return(expr ast.Expr) {
match expr {
ast.Ident {
g.returned_var_names[expr.name] = true
}
ast.StructInit {
for field_expr in expr.init_fields {
g.detect_used_var_on_return(field_expr.expr)
}
}
else {}
}
}

View file

@ -231,11 +231,11 @@ mut:
// where an aggregate (at least two types) is generated
// sum type deref needs to know which index to deref because unions take care of the correct field
aggregate_type_idx int
arg_no_auto_deref bool // smartcast must not be dereferenced
branch_parent_pos int // used in BranchStmt (continue/break) for autofree stop position
returned_var_name string // to detect that a var doesn't need to be freed since it's being returned
infix_left_var_name string // a && if expr
curr_var_name []string // curr var name on assignment
arg_no_auto_deref bool // smartcast must not be dereferenced
branch_parent_pos int // used in BranchStmt (continue/break) for autofree stop position
returned_var_names map[string]bool // to detect that vars doesn't need to be freed since it's being returned
infix_left_var_name string // a && if expr
curr_var_name []string // curr var name on assignment
called_fn_name string
timers &util.Timers = util.get_timers()
force_main_console bool // true when @[console] used on fn main()
@ -6241,6 +6241,9 @@ fn (mut g Gen) return_stmt(node ast.Return) {
g.writeln(' }, (${option_name}*)(&${tmpvar}), sizeof(${styp}));')
}
g.write_defer_stmts_when_needed()
if g.is_autofree {
g.detect_used_var_on_return(expr0)
}
g.autofree_scope_vars(node.pos.pos - 1, node.pos.line_nr, true)
g.writeln('return ${tmpvar};')
return
@ -6286,6 +6289,9 @@ fn (mut g Gen) return_stmt(node ast.Return) {
g.writeln(' }, (${result_name}*)(&${tmpvar}), sizeof(${styp}));')
}
g.write_defer_stmts_when_needed()
if g.is_autofree {
g.detect_used_var_on_return(expr0)
}
g.autofree_scope_vars(node.pos.pos - 1, node.pos.line_nr, true)
g.writeln('return ${tmpvar};')
return
@ -6294,9 +6300,7 @@ fn (mut g Gen) return_stmt(node ast.Return) {
// set free_parent_scopes to true, since all variables defined in parent
// scopes need to be freed before the return
if g.is_autofree {
if expr0 is ast.Ident {
g.returned_var_name = expr0.name
}
g.detect_used_var_on_return(expr0)
if !use_tmp_var && !g.is_builtin_mod {
use_tmp_var = expr0 is ast.CallExpr
}

View file

@ -196,7 +196,7 @@ fn (mut g Gen) gen_fn_decl(node &ast.FnDecl, skip bool) {
}
*/
g.returned_var_name = ''
g.returned_var_names.clear()
old_g_autofree := g.is_autofree
if node.is_manualfree {
g.is_autofree = false

View file

@ -0,0 +1,9 @@
_result_toml__scanner__Scanner_ptr toml__scanner__new_scanner(toml__scanner__Config config) {
_result_toml__scanner__Scanner_ptr _t3 = {0};
_result_ok(&(toml__scanner__Scanner*[]) { s }, (_result*)(&_t3), sizeof(toml__scanner__Scanner*));
return _t3;
}
toml__ast__Quoted toml__parser__Parser_quoted(toml__parser__Parser* p) {
return ((toml__ast__Quoted){.text = string_clone_static(lit),.pos = toml__token__Token_pos(&p->tok),.is_multiline = is_multiline,.quote = quote,});
}

View file

15
vlib/v/gen/c/testdata/autofree_toml.vv vendored Normal file
View file

@ -0,0 +1,15 @@
// vtest vflags: -autofree
import toml
import os
fn main() {
config_fname := 'config.toml'
tab_title := 'test tab title'
if !os.exists(config_fname) {
mut f := os.create(config_fname) or { panic(err) }
f.writeln('tab_title = "${tab_title}"') or { panic(err) }
f.close()
}
doc := toml.parse_file(config_fname) or { panic(err) }
assert doc.value('tab_title').string() == tab_title
}

View file

@ -398,6 +398,7 @@ fn advanced_options() {
s2 := parse_header1('foo:bar') or { return }
_ := s.len + s2.len // avoid warning for unused variables
// TODO: fix -autofree, so that it adds this free automatically:
unsafe { s.free() }
unsafe { s2.free() }
}