autofree: handle method chains

pull/6666/head
Alexander Medvednikov 2020-10-22 03:51:25 +02:00
parent ab137e4164
commit 66b8462d7a
4 changed files with 37 additions and 7 deletions

View File

@ -288,6 +288,7 @@ pub mut:
return_type table.Type return_type table.Type
should_be_skipped bool should_be_skipped bool
generic_type table.Type // TODO array, to support multiple types 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_pregen string
// autofree_vars []AutofreeArgVar // autofree_vars []AutofreeArgVar
// autofree_vars_ids []int // autofree_vars_ids []int

View File

@ -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. // 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) } 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 && 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) call_expr.args.len > 0 && !call_expr.args[0].typ.has_flag(.optional)
if free_tmp_arg_vars { 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 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 return typ
} }

View File

@ -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' { } else if !node.receiver_type.is_ptr() && node.left_type.is_ptr() && node.name != 'str' {
g.write('/*rec*/*') 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 - is_variadic := node.expected_arg_types.len > 0 && node.expected_arg_types[node.expected_arg_types.len -
1].has_flag(.variadic) 1].has_flag(.variadic)
if node.args.len > 0 || is_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 free_tmp_arg_vars = false // set the flag to true only if we have at least one arg to free
g.tmp_count2++ g.tmp_count2++
mut scope := g.file.scope.innermost(node.pos.pos) 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 { if !arg.is_tmp_autofree {
continue continue
} }
@ -725,7 +740,7 @@ fn (mut g Gen) call_args(node ast.CallExpr) {
// Use these variables here. // Use these variables here.
fn_name := node.name.replace('.', '_') fn_name := node.name.replace('.', '_')
// name := '_tt${g.tmp_count2}_arg_expr_${fn_name}_$i' // 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) g.write('/*af arg*/' + name)
} }
} else { } else {
@ -736,7 +751,7 @@ fn (mut g Gen) call_args(node ast.CallExpr) {
// TODO copypasta, move to an inline fn // TODO copypasta, move to an inline fn
fn_name := node.name.replace('.', '_') fn_name := node.name.replace('.', '_')
// name := '_tt${g.tmp_count2}_arg_expr_${fn_name}_$i' // 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) g.write('/*af arg2*/' + name)
} else { } else {
g.expr(arg.expr) g.expr(arg.expr)

View File

@ -3,7 +3,7 @@ fn return_array(array_arg []string) []int { // array argument must not be freed
return s return s
} }
fn foo() { fn simple() {
nums := [1, 2, 3] // local array must be freed nums := [1, 2, 3] // local array must be freed
println(nums) println(nums)
nums_copy := nums // array assignments call .clone() 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() { fn reassign_str() {
mut s := 'a' + 'b' mut s := 'a' + 'b'
s = 'x' + 'y' // 'a' + 'b' must be freed before the re-assignment s = 'x' + 'y' // 'a' + 'b' must be freed before the re-assignment
@ -149,7 +158,7 @@ fn tt() {
fn main() { fn main() {
println('start') println('start')
foo() simple()
str_tmp_expr() str_tmp_expr()
str_tmp_expr_advanced() str_tmp_expr_advanced()
str_tmp_expr_advanced_var_decl() str_tmp_expr_advanced_var_decl()
@ -159,6 +168,7 @@ fn main() {
optional_str() optional_str()
optional_return() optional_return()
str_replace() str_replace()
str_replace2()
if_cond() if_cond()
addition_with_tmp_expr() addition_with_tmp_expr()
println('end') println('end')