autofree: handle more statements

pull/6640/head
Alexander Medvednikov 2020-10-18 00:48:06 +02:00
parent acc85be5ae
commit 55536bb364
4 changed files with 68 additions and 29 deletions

View File

@ -241,6 +241,8 @@ two ',
four!'] four!']
s = strings.join(' ') s = strings.join(' ')
assert s.contains('one') && s.contains('two ') && s.contains('four') assert s.contains('one') && s.contains('two ') && s.contains('four')
empty := []string{len:0}
assert empty.join('A') == ''
} }
fn test_clone() { fn test_clone() {
@ -906,7 +908,7 @@ fn test_sorter() {
i: 102 i: 102
} }
] ]
cmp := fn (a, b &Ka) int { cmp := fn (a &Ka, b &Ka) int {
return compare_strings(a.s, b.s) return compare_strings(a.s, b.s)
} }
arr.sort_with_compare(cmp) arr.sort_with_compare(cmp)

View File

@ -100,8 +100,8 @@ mut:
inside_vweb_tmpl bool inside_vweb_tmpl bool
inside_return bool inside_return bool
inside_or_block bool inside_or_block bool
strs_to_free0 []string // strings.Builder
strs_to_free []string // strings.Builder strs_to_free []string // strings.Builder
// strs_to_free0 []string // strings.Builder
inside_call bool inside_call bool
has_main bool has_main bool
inside_const bool inside_const bool
@ -111,6 +111,7 @@ mut:
match_sumtype_syms []table.TypeSymbol match_sumtype_syms []table.TypeSymbol
// tmp_arg_vars_to_free []string // tmp_arg_vars_to_free []string
// autofree_pregen map[string]string // autofree_pregen map[string]string
// autofree_pregen_buf strings.Builder
// autofree_tmp_vars []string // to avoid redefining the same tmp vars in a single function // autofree_tmp_vars []string // to avoid redefining the same tmp vars in a single function
called_fn_name string called_fn_name string
cur_mod string cur_mod string
@ -755,6 +756,9 @@ fn (mut g Gen) stmt(node ast.Stmt) {
defer { defer {
// If we have temporary string exprs to free after this statement, do it. e.g.: // If we have temporary string exprs to free after this statement, do it. e.g.:
// `foo('a' + 'b')` => `tmp := 'a' + 'b'; foo(tmp); string_free(&tmp);` // `foo('a' + 'b')` => `tmp := 'a' + 'b'; foo(tmp); string_free(&tmp);`
if g.pref.autofree {
g.autofree_call_postgen()
}
/* /*
if g.pref.autofree { // && !g.inside_or_block { if g.pref.autofree { // && !g.inside_or_block {
// TODO remove the inside_or_block hack. strings are not freed in or{} atm // TODO remove the inside_or_block hack. strings are not freed in or{} atm
@ -835,14 +839,14 @@ fn (mut g Gen) stmt(node ast.Stmt) {
} }
ast.ExprStmt { ast.ExprStmt {
g.write_v_source_line_info(node.pos) g.write_v_source_line_info(node.pos)
af := g.pref.autofree && node.expr is ast.CallExpr && !g.is_builtin_mod // af := g.pref.autofree && node.expr is ast.CallExpr && !g.is_builtin_mod
if af { // if af {
g.autofree_call_pregen(node.expr as ast.CallExpr) // g.autofree_call_pregen(node.expr as ast.CallExpr)
} // }
g.expr(node.expr) g.expr(node.expr)
if af { // if af {
g.autofree_call_postgen() // g.autofree_call_postgen()
} // }
if g.inside_ternary == 0 && !node.is_expr && !(node.expr is ast.IfExpr) { if g.inside_ternary == 0 && !node.is_expr && !(node.expr is ast.IfExpr) {
g.writeln(';') g.writeln(';')
} }
@ -1011,9 +1015,9 @@ fn (mut g Gen) stmt(node ast.Stmt) {
af := g.pref.autofree && node.exprs.len > 0 && node.exprs[0] is ast.CallExpr && !g.is_builtin_mod af := g.pref.autofree && node.exprs.len > 0 && node.exprs[0] is ast.CallExpr && !g.is_builtin_mod
if g.pref.autofree { if g.pref.autofree {
g.writeln('// ast.Return free') g.writeln('// ast.Return free')
if af { // if af {
g.autofree_call_pregen(node.exprs[0] as ast.CallExpr) // g.autofree_call_pregen(node.exprs[0] as ast.CallExpr)
} // }
// g.autofree_scope_vars(node.pos.pos) // g.autofree_scope_vars(node.pos.pos)
g.write_autofree_stmts_when_needed(node) g.write_autofree_stmts_when_needed(node)
} }
@ -1390,11 +1394,12 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
} }
} }
// Autofree tmp arg vars // Autofree tmp arg vars
first_right := assign_stmt.right[0] // first_right := assign_stmt.right[0]
af := g.pref.autofree && first_right is ast.CallExpr && !g.is_builtin_mod // af := g.pref.autofree && first_right is ast.CallExpr && !g.is_builtin_mod
if af { // if af {
g.autofree_call_pregen(first_right as ast.CallExpr) // g.autofree_call_pregen(first_right as ast.CallExpr)
} // }
//
// //
// Handle optionals. We need to declare a temp variable for them, that's why they are handled // Handle optionals. We need to declare a temp variable for them, that's why they are handled
// here, not in call_expr(). // here, not in call_expr().
@ -1422,9 +1427,9 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
gen_or = true gen_or = true
g.or_block(tmp_opt, call_expr.or_block, call_expr.return_type) g.or_block(tmp_opt, call_expr.or_block, call_expr.return_type)
g.writeln('/*=============ret*/') g.writeln('/*=============ret*/')
if af && is_optional { // if af && is_optional {
g.autofree_call_postgen() // g.autofree_call_postgen()
} // }
// return // return
} }
} }
@ -1987,7 +1992,15 @@ fn (mut g Gen) expr(node ast.Expr) {
// if g.fileis('1.strings') { // if g.fileis('1.strings') {
// println('before:' + node.autofree_pregen) // println('before:' + node.autofree_pregen)
// } // }
if g.pref.autofree { if g.pref.autofree && !g.is_builtin_mod && g.strs_to_free0.len == 0 {
// if len != 0, that means we are handling call expr inside call expr (arg)
// and it'll get messed up here, since it's handled recursively in autofree_call_pregen()
// so just skip it
g.autofree_call_pregen(node)
if g.strs_to_free0.len > 0 {
g.insert_before_stmt(g.strs_to_free0.join('\n'))
}
g.strs_to_free0 = []
// println('pos=$node.pos.pos') // println('pos=$node.pos.pos')
} }
// if g.pref.autofree && node.autofree_pregen != '' { // g.strs_to_free0.len != 0 { // if g.pref.autofree && node.autofree_pregen != '' { // g.strs_to_free0.len != 0 {

View File

@ -620,7 +620,7 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
} }
fn (mut g Gen) autofree_call_pregen(node ast.CallExpr) { fn (mut g Gen) autofree_call_pregen(node ast.CallExpr) {
g.writeln('// autofree_call()') // g.writeln('// autofree_call_pregen()')
// Create a temporary var before fn call for each argument in order to free it (only if it's a complex expression, // Create a temporary var before fn call for each argument in order to free it (only if it's a complex expression,
// like `foo(get_string())` or `foo(a + b)` // like `foo(get_string())` or `foo(a + b)`
mut free_tmp_arg_vars := g.autofree && g.pref.experimental && !g.is_builtin_mod && mut free_tmp_arg_vars := g.autofree && g.pref.experimental && !g.is_builtin_mod &&
@ -648,19 +648,22 @@ fn (mut g Gen) autofree_call_pregen(node ast.CallExpr) {
// g.called_fn_name = name // g.called_fn_name = name
// used := t in g.autofree_tmp_vars // used := t in g.autofree_tmp_vars
used := scope.known_var(t) used := scope.known_var(t)
if used { mut s := if used {
g.write('$t = ') '$t = '
} else { } else {
g.write('string $t = ')
scope.register(t, ast.Var{ scope.register(t, ast.Var{
name: t name: t
typ: table.string_type typ: table.string_type
is_arg: true // TODO hack so that it's not freed twice when out of scope. it's probably better to use one model is_arg: true // TODO hack so that it's not freed twice when out of scope. it's probably better to use one model
}) })
'string $t = '
// g.autofree_tmp_vars << t // g.autofree_tmp_vars << t
} }
g.expr(arg.expr) // g.expr(arg.expr)
g.writeln(';// new af pre') s += g.write_expr_to_string(arg.expr)
// g.writeln(';// new af pre')
s += ';// new af2 pre'
g.strs_to_free0 << s
// Now free the tmp arg vars right after the function call // Now free the tmp arg vars right after the function call
g.strs_to_free << t g.strs_to_free << t
// g.strs_to_free << 'string_free(&$t);' // g.strs_to_free << 'string_free(&$t);'

View File

@ -13,14 +13,14 @@ fn foo() {
// nums.free() // this should result in a double free and a CI error // nums.free() // this should result in a double free and a CI error
} }
fn handle_strings(s, p string) int { fn handle_strings(s string, p string) int {
return 0 return 0
} }
fn handle_int(n int) { fn handle_int(n int) {
} }
fn add_strings(a, b string) string { fn add_strings(a string, b string) string {
return a + b return a + b
} }
@ -120,6 +120,25 @@ fn optional_return() {
} }
} }
fn handle_string(s string) bool {
return true
}
fn if_cond() {
// handle_string('1' + '2')
if handle_string('1' + '2') {
// if '1' + '2' == '12' {
println('yes')
} else {
println('no')
}
}
fn addition_with_tmp_expr() {
x := 1 + handle_strings('a' + 'b', 'c')
println(x)
}
fn tt() { fn tt() {
// time.parse_rfc2822('1234') // time.parse_rfc2822('1234')
} }
@ -136,6 +155,8 @@ fn main() {
optional_str() optional_str()
optional_return() optional_return()
// str_replace() // str_replace()
if_cond()
addition_with_tmp_expr()
println('end') println('end')
} }