diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 169330b9ec..1cc88502e5 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -288,6 +288,7 @@ pub mut: return_type table.Type should_be_skipped bool generic_type table.Type // TODO array, to support multiple types + free_receiver bool // true if the receiver expression needs to be freed // autofree_pregen string // autofree_vars []AutofreeArgVar // autofree_vars_ids []int diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index ee9dbc85cf..ea6e80814c 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -952,7 +952,7 @@ pub fn (mut c Checker) call_expr(mut call_expr ast.CallExpr) table.Type { */ // Now call `call_method` or `call_fn` for specific checks. typ := if call_expr.is_method { c.call_method(mut call_expr) } else { c.call_fn(mut call_expr) } - // autofree + // autofree: mark args that have to be freed (after saving them in tmp exprs) free_tmp_arg_vars := c.pref.autofree && c.pref.experimental && !c.is_builtin_mod && call_expr.args.len > 0 && !call_expr.args[0].typ.has_flag(.optional) if free_tmp_arg_vars { @@ -965,6 +965,10 @@ pub fn (mut c Checker) call_expr(mut call_expr ast.CallExpr) table.Type { } call_expr.args[i].is_tmp_autofree = true } + if call_expr.receiver_type == table.string_type && !(call_expr.left is ast.Ident || + call_expr.left is ast.StringLiteral) { + call_expr.free_receiver = true + } } return typ } diff --git a/vlib/v/gen/fn.v b/vlib/v/gen/fn.v index 5c7215974c..5e7fec1444 100644 --- a/vlib/v/gen/fn.v +++ b/vlib/v/gen/fn.v @@ -443,7 +443,14 @@ fn (mut g Gen) method_call(node ast.CallExpr) { } else if !node.receiver_type.is_ptr() && node.left_type.is_ptr() && node.name != 'str' { g.write('/*rec*/*') } - g.expr(node.left) + if node.free_receiver { + // The receiver expression needs to be freed, use the temp var. + fn_name := node.name.replace('.', '_') + arg_name := '_arg_expr_${fn_name}_0' + g.write('/*af receiver arg*/' + arg_name) + } else { + g.expr(node.left) + } is_variadic := node.expected_arg_types.len > 0 && node.expected_arg_types[node.expected_arg_types.len - 1].has_flag(.variadic) if node.args.len > 0 || is_variadic { @@ -631,7 +638,15 @@ fn (mut g Gen) autofree_call_pregen(node ast.CallExpr) { free_tmp_arg_vars = false // set the flag to true only if we have at least one arg to free g.tmp_count2++ mut scope := g.file.scope.innermost(node.pos.pos) - for i, arg in node.args { + // prepend the receiver for now (TODO turn the receiver into a CallArg everywhere?) + mut args := [ast.CallArg{ + typ: node.receiver_type + expr: node.left + is_tmp_autofree: node.free_receiver + }] + args << node.args + // for i, arg in node.args { + for i, arg in args { if !arg.is_tmp_autofree { continue } @@ -725,7 +740,7 @@ fn (mut g Gen) call_args(node ast.CallExpr) { // Use these variables here. fn_name := node.name.replace('.', '_') // name := '_tt${g.tmp_count2}_arg_expr_${fn_name}_$i' - name := '_arg_expr_${fn_name}_$i' + name := '_arg_expr_${fn_name}_${i + 1}' g.write('/*af arg*/' + name) } } else { @@ -736,7 +751,7 @@ fn (mut g Gen) call_args(node ast.CallExpr) { // TODO copypasta, move to an inline fn fn_name := node.name.replace('.', '_') // name := '_tt${g.tmp_count2}_arg_expr_${fn_name}_$i' - name := '_arg_expr_${fn_name}_$i' + name := '_arg_expr_${fn_name}_${i + 1}' g.write('/*af arg2*/' + name) } else { g.expr(arg.expr) diff --git a/vlib/v/tests/valgrind/1.strings_and_arrays.v b/vlib/v/tests/valgrind/1.strings_and_arrays.v index 71bd76aa94..1cfb278f7e 100644 --- a/vlib/v/tests/valgrind/1.strings_and_arrays.v +++ b/vlib/v/tests/valgrind/1.strings_and_arrays.v @@ -3,7 +3,7 @@ fn return_array(array_arg []string) []int { // array argument must not be freed return s } -fn foo() { +fn simple() { nums := [1, 2, 3] // local array must be freed println(nums) nums_copy := nums // array assignments call .clone() @@ -73,6 +73,15 @@ fn str_replace() { */ } +fn fooo(s string) { +} + +fn str_replace2() { + mut s := 'hello world' + s = s.replace('hello', 'hi').replace('world', 'planet') + println(s) +} + fn reassign_str() { mut s := 'a' + 'b' s = 'x' + 'y' // 'a' + 'b' must be freed before the re-assignment @@ -149,7 +158,7 @@ fn tt() { fn main() { println('start') - foo() + simple() str_tmp_expr() str_tmp_expr_advanced() str_tmp_expr_advanced_var_decl() @@ -159,6 +168,7 @@ fn main() { optional_str() optional_return() str_replace() + str_replace2() if_cond() addition_with_tmp_expr() println('end')