diff --git a/vlib/v/gen/c/autofree.v b/vlib/v/gen/c/autofree.v index 69c82f2437..a4f88ae12a 100644 --- a/vlib/v/gen/c/autofree.v +++ b/vlib/v/gen/c/autofree.v @@ -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 {} + } +} diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 79cd19c325..ad2b3110c0 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -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 } diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index 781341e150..34a0835e70 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -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 diff --git a/vlib/v/gen/c/testdata/autofree_toml.c.must_have b/vlib/v/gen/c/testdata/autofree_toml.c.must_have new file mode 100644 index 0000000000..0f0d705c8c --- /dev/null +++ b/vlib/v/gen/c/testdata/autofree_toml.c.must_have @@ -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,}); +} + diff --git a/vlib/v/gen/c/testdata/autofree_toml.out b/vlib/v/gen/c/testdata/autofree_toml.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/vlib/v/gen/c/testdata/autofree_toml.vv b/vlib/v/gen/c/testdata/autofree_toml.vv new file mode 100644 index 0000000000..42272f8c0d --- /dev/null +++ b/vlib/v/gen/c/testdata/autofree_toml.vv @@ -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 +} diff --git a/vlib/v/slow_tests/valgrind/1.strings_and_arrays.v b/vlib/v/slow_tests/valgrind/1.strings_and_arrays.v index 4741ae4fbf..7b48049607 100644 --- a/vlib/v/slow_tests/valgrind/1.strings_and_arrays.v +++ b/vlib/v/slow_tests/valgrind/1.strings_and_arrays.v @@ -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() } }