memory fixes: 21% of V compiler leaks fixed

pull/4166/head
Alexander Medvednikov 2020-03-31 14:33:16 +02:00
parent 9b9c1cc834
commit 956bf23390
4 changed files with 181 additions and 122 deletions

View File

@ -73,7 +73,7 @@ pub fn cgen(files []ast.File, table &table.Table, pref &pref.Preferences) string
for file in files { for file in files {
g.file = file g.file = file
// println('\ncgen "$g.file.path" nr_stmts=$file.stmts.len') // println('\ncgen "$g.file.path" nr_stmts=$file.stmts.len')
building_v := g.file.path.contains('/vlib/') || g.file.path.contains('cmd/v') building_v := true && (g.file.path.contains('/vlib/') || g.file.path.contains('cmd/v'))
is_test := g.file.path.ends_with('.vv') || g.file.path.ends_with('_test.v') is_test := g.file.path.ends_with('.vv') || g.file.path.ends_with('_test.v')
if g.file.path.ends_with('_test.v') { if g.file.path.ends_with('_test.v') {
g.is_test = is_test g.is_test = is_test
@ -454,7 +454,7 @@ fn (g mut Gen) for_in(it ast.ForInStmt) {
g.write('; $i < ') g.write('; $i < ')
g.expr(it.high) g.expr(it.high)
g.writeln('; $i++) { ') g.writeln('; $i++) { ')
g.writeln('int $it.val_var = $i;') g.writeln('\tint $it.val_var = $i;')
g.stmts(it.stmts) g.stmts(it.stmts)
g.writeln('}') g.writeln('}')
} }
@ -467,7 +467,7 @@ fn (g mut Gen) for_in(it ast.ForInStmt) {
g.write('for (int $i = 0; $i < ') g.write('for (int $i = 0; $i < ')
g.expr(it.cond) g.expr(it.cond)
g.writeln('.len; $i++) {') g.writeln('.len; $i++) {')
g.write('$styp $it.val_var = (($styp*)') g.write('\t$styp $it.val_var = (($styp*)')
g.expr(it.cond) g.expr(it.cond)
g.writeln('.data)[$i];') g.writeln('.data)[$i];')
g.stmts(it.stmts) g.stmts(it.stmts)
@ -486,8 +486,8 @@ fn (g mut Gen) for_in(it ast.ForInStmt) {
g.expr(it.cond) g.expr(it.cond)
g.writeln(');') g.writeln(');')
g.writeln('for (int $idx = 0; $idx < ${keys_tmp}.len; $idx++) {') g.writeln('for (int $idx = 0; $idx < ${keys_tmp}.len; $idx++) {')
g.writeln('$key_styp $key = (($key_styp*)${keys_tmp}.data)[$idx];') g.writeln('\t$key_styp $key = (($key_styp*)${keys_tmp}.data)[$idx];')
g.write('$val_styp $it.val_var = (*($val_styp*)map_get3(') g.write('\t$val_styp $it.val_var = (*($val_styp*)map_get3(')
g.expr(it.cond) g.expr(it.cond)
g.writeln(', $key, &($val_styp[]){ $zero }));') g.writeln(', $key, &($val_styp[]){ $zero }));')
g.stmts(it.stmts) g.stmts(it.stmts)
@ -641,36 +641,28 @@ fn (g mut Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
} }
} }
else { else {
mut is_fixed_array_init := false
mut is_ident := false
right_sym := g.table.get_type_symbol(assign_stmt.right_types[i]) right_sym := g.table.get_type_symbol(assign_stmt.right_types[i])
mut is_fixed_array_init := false
match val { match val {
ast.ArrayInit { ast.ArrayInit {
is_fixed_array_init = right_sym.kind == .array_fixed is_fixed_array_init = right_sym.kind == .array_fixed
} }
ast.Ident {
is_ident = true
}
else {} else {}
} }
is_decl := assign_stmt.op == .decl_assign is_decl := assign_stmt.op == .decl_assign
g.write('/*assign_stmt*/')
if is_decl { if is_decl {
g.write('$styp ') g.write('$styp ')
} }
g.expr(ident) g.expr(ident)
if g.autofree && right_sym.kind == .array && is_ident { if g.autofree && right_sym.kind in [.array, .string] {
// `arr1 = arr2` => `arr1 = arr2.clone()` if g.gen_clone_assignment(val, right_sym, true) {
g.write(' = array_clone(&') g.writeln(';')
g.expr(val) // g.expr_var_name = ''
g.write(')') return
}
} }
else if g.autofree && right_sym.kind == .string && is_ident { if !is_fixed_array_init {
// `str1 = str2` => `str1 = str2.clone()`
g.write(' = string_clone(')
g.expr(val)
g.write(')')
}
else if !is_fixed_array_init {
g.write(' = ') g.write(' = ')
if !is_decl { if !is_decl {
g.expr_with_cast(val, assign_stmt.left_types[i], ident_var_info.typ) g.expr_with_cast(val, assign_stmt.left_types[i], ident_var_info.typ)
@ -692,6 +684,40 @@ fn (g mut Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
} }
} }
fn (g mut Gen) gen_clone_assignment(val ast.Expr, right_sym table.TypeSymbol, add_eq bool) bool {
mut is_ident := false
match val {
ast.Ident {
is_ident = true
}
ast.SelectorExpr {
is_ident = true
}
else {
return false
}
}
if g.autofree && right_sym.kind == .array && is_ident {
// `arr1 = arr2` => `arr1 = arr2.clone()`
if add_eq {
g.write('=')
}
g.write(' array_clone(&')
g.expr(val)
g.write(')')
}
else if g.autofree && right_sym.kind == .string && is_ident {
if add_eq {
g.write('=')
}
// `str1 = str2` => `str1 = str2.clone()`
g.write(' string_clone(')
g.expr(val)
g.write(')')
}
return true
}
fn (g mut Gen) gen_fn_decl(it ast.FnDecl) { fn (g mut Gen) gen_fn_decl(it ast.FnDecl) {
if it.is_c { if it.is_c {
// || it.no_body { // || it.no_body {
@ -1173,7 +1199,18 @@ fn (g mut Gen) assign_expr(node ast.AssignExpr) {
g.write(', ') g.write(', ')
} }
g.is_assign_lhs = false g.is_assign_lhs = false
g.expr_with_cast(node.val, node.right_type, node.left_type) right_sym := g.table.get_type_symbol(node.right_type)
// left_sym := g.table.get_type_symbol(node.left_type)
mut cloned := false
// !g.is_array_set
if g.autofree && right_sym.kind in [.array, .string] {
if g.gen_clone_assignment(node.val, right_sym, false) {
cloned = true
}
}
if !cloned {
g.expr_with_cast(node.val, node.right_type, node.left_type)
}
if g.is_array_set { if g.is_array_set {
g.write(' })') g.write(' })')
g.is_array_set = false g.is_array_set = false
@ -1397,7 +1434,7 @@ fn (g mut Gen) match_expr(node ast.MatchExpr) {
ast.Type { ast.Type {
it_type := g.typ(it.typ) it_type := g.typ(it.typ)
// g.writeln('$it_type* it = ($it_type*)${tmp}.obj; // ST it') // g.writeln('$it_type* it = ($it_type*)${tmp}.obj; // ST it')
g.write('$it_type* it = ($it_type*)') g.write('\t$it_type* it = ($it_type*)')
g.expr(node.cond) g.expr(node.cond)
g.writeln('.obj; // ST it') g.writeln('.obj; // ST it')
} }
@ -2194,53 +2231,64 @@ fn (g mut Gen) call_expr(node ast.CallExpr) {
g.write('$styp $tmp_opt = ') g.write('$styp $tmp_opt = ')
} }
if node.is_method { if node.is_method {
// TODO: there are still due to unchecked exprs (opt/some fn arg) g.method_call(node)
if node.left_type == 0 { }
verror('method receiver type is 0, this means there are some uchecked exprs') else {
g.fn_call(node)
}
if gen_or {
g.or_block(tmp_opt, node.or_block.stmts, node.return_type)
}
}
fn (g mut Gen) method_call(node ast.CallExpr) {
// TODO: there are still due to unchecked exprs (opt/some fn arg)
if node.left_type == 0 {
verror('method receiver type is 0, this means there are some uchecked exprs')
}
typ_sym := g.table.get_type_symbol(node.receiver_type)
// rec_sym := g.table.get_type_symbol(node.receiver_type)
mut receiver_name := typ_sym.name
if typ_sym.kind == .array && node.name == 'filter' {
g.gen_filter(node)
return
}
if typ_sym.kind == .array && node.name in
// TODO performance, detect `array` method differently
['repeat', 'sort_with_compare', 'free', 'push_many', 'trim',
//
'first', 'last', 'clone', 'reverse', 'slice'] {
// && rec_sym.name == 'array' {
// && rec_sym.name == 'array' && receiver_name.starts_with('array') {
// `array_byte_clone` => `array_clone`
receiver_name = 'array'
if node.name in ['last', 'first'] {
return_type_str := g.typ(node.return_type)
g.write('*($return_type_str*)')
} }
typ_sym := g.table.get_type_symbol(node.receiver_type) }
// rec_sym := g.table.get_type_symbol(node.receiver_type) name := '${receiver_name}_$node.name'.replace('.', '__')
mut receiver_name := typ_sym.name // if node.receiver_type != 0 {
if typ_sym.kind == .array && node.name == 'filter' { // g.write('/*${g.typ(node.receiver_type)}*/')
g.gen_filter(node) // g.write('/*expr_type=${g.typ(node.left_type)} rec type=${g.typ(node.receiver_type)}*/')
return // }
} g.write('${name}(')
if typ_sym.kind == .array && node.name in if table.type_is_ptr(node.receiver_type) && !table.type_is_ptr(node.left_type) {
// TODO performance, detect `array` method differently // The receiver is a reference, but the caller provided a value
['repeat', 'sort_with_compare', 'free', 'push_many', 'trim', // Add `&` automatically.
// // TODO same logic in call_args()
'first', 'last', 'clone', 'reverse', 'slice'] { g.write('&')
// && rec_sym.name == 'array' { }
// && rec_sym.name == 'array' && receiver_name.starts_with('array') { else if !table.type_is_ptr(node.receiver_type) && table.type_is_ptr(node.left_type) {
// `array_byte_clone` => `array_clone` g.write('/*rec*/*')
receiver_name = 'array' }
if node.name in ['last', 'first'] { g.expr(node.left)
return_type_str := g.typ(node.return_type) is_variadic := node.exp_arg_types.len > 0 && table.type_is_variadic(node.exp_arg_types[node.exp_arg_types.len - 1])
g.write('*($return_type_str*)') if node.args.len > 0 || is_variadic {
} g.write(', ')
} }
name := '${receiver_name}_$node.name'.replace('.', '__') // /////////
// if node.receiver_type != 0 { /*
// g.write('/*${g.typ(node.receiver_type)}*/')
// g.write('/*expr_type=${g.typ(node.left_type)} rec type=${g.typ(node.receiver_type)}*/')
// }
g.write('${name}(')
if table.type_is_ptr(node.receiver_type) && !table.type_is_ptr(node.left_type) {
// The receiver is a reference, but the caller provided a value
// Add `&` automatically.
// TODO same logic in call_args()
g.write('&')
}
else if !table.type_is_ptr(node.receiver_type) && table.type_is_ptr(node.left_type) {
g.write('/*rec*/*')
}
g.expr(node.left)
is_variadic := node.exp_arg_types.len > 0 && table.type_is_variadic(node.exp_arg_types[node.exp_arg_types.len - 1])
if node.args.len > 0 || is_variadic {
g.write(', ')
}
// /////////
/*
if name.contains('subkeys') { if name.contains('subkeys') {
println('call_args $name $node.arg_types.len') println('call_args $name $node.arg_types.len')
for t in node.arg_types { for t in node.arg_types {
@ -2250,23 +2298,27 @@ fn (g mut Gen) call_expr(node ast.CallExpr) {
println('') println('')
} }
*/ */
// /////// // ///////
g.call_args(node.args, node.exp_arg_types) g.call_args(node.args, node.exp_arg_types)
g.write(')') g.write(')')
// if node.or_block.stmts.len > 0 {
// g.or_block(node.or_block.stmts, node.return_type)
// }
}
fn (g mut Gen) fn_call(node ast.CallExpr) {
mut name := node.name
is_print := name == 'println'
if node.is_c {
// Skip "C."
g.is_c_call = true
name = name[2..].replace('.', '__')
} }
else { else {
mut name := node.name name = c_name(name)
is_print := name == 'println' }
if node.is_c { // Generate tmp vars for values that have to be freed.
// Skip "C." /*
g.is_c_call = true
name = name[2..].replace('.', '__')
}
else {
name = c_name(name)
}
// Generate tmp vars for values that have to be freed.
/*
mut tmps := []string mut tmps := []string
for arg in node.args { for arg in node.args {
if arg.typ == table.string_type_idx || is_print { if arg.typ == table.string_type_idx || is_print {
@ -2279,43 +2331,42 @@ fn (g mut Gen) call_expr(node ast.CallExpr) {
} }
*/ */
if is_print && node.args[0].typ != table.string_type_idx { if is_print && node.args[0].typ != table.string_type_idx {
typ := node.args[0].typ typ := node.args[0].typ
mut styp := g.typ(typ) mut styp := g.typ(typ)
sym := g.table.get_type_symbol(typ) sym := g.table.get_type_symbol(typ)
if !sym.has_method('str') && !(int(typ) in g.str_types) { if !sym.has_method('str') && !(int(typ) in g.str_types) {
// Generate an automatic str() method if this type doesn't have it already // Generate an automatic str() method if this type doesn't have it already
if table.type_is_ptr(typ) { if table.type_is_ptr(typ) {
styp = styp.replace('*', '') styp = styp.replace('*', '')
}
g.str_types << typ
g.definitions.writeln('string ${styp}_str($styp* x) { return tos3("TODO_str"); }')
}
if g.autofree && !table.type_is_optional(typ) {
tmp := g.new_tmp_var()
// tmps << tmp
g.write('string $tmp = ${styp}_str(')
g.expr(node.args[0].expr)
g.writeln('); println($tmp); string_free($tmp); //MEM2 $styp')
}
else {
// `println(int_str(10))`
// sym := g.table.get_type_symbol(node.args[0].typ)
g.write('println(${styp}_str(')
g.expr(node.args[0].expr)
g.write('))')
} }
g.str_types << typ
g.definitions.writeln('string ${styp}_str($styp* x) { return tos3("TODO_str"); }')
}
if g.autofree && !table.type_is_optional(typ) {
tmp := g.new_tmp_var()
// tmps << tmp
g.write('string $tmp = ${styp}_str(')
g.expr(node.args[0].expr)
g.writeln('); println($tmp); string_free($tmp); //MEM2 $styp')
} }
else { else {
g.write('${name}(') // `println(int_str(10))`
g.call_args(node.args, node.exp_arg_types) // sym := g.table.get_type_symbol(node.args[0].typ)
g.write(')') g.write('println(${styp}_str(')
g.expr(node.args[0].expr)
g.write('))')
} }
g.is_c_call = false
} }
if gen_or { else {
g.or_block(tmp_opt, node.or_block.stmts, node.return_type) g.write('${name}(')
g.call_args(node.args, node.exp_arg_types)
g.write(')')
} }
// if node.or_block.stmts.len > 0 {
// g.or_block(node.or_block.stmts, node.return_type)
// }
g.is_c_call = false
} }
// If user is accessing the return value eg. in assigment, pass the variable name. // If user is accessing the return value eg. in assigment, pass the variable name.

View File

@ -26,10 +26,10 @@ fn (p mut Parser) comp_if() ast.CompIf {
// `$if os {` for a different target, skip everything inside // `$if os {` for a different target, skip everything inside
// to avoid compilation errors (like including <windows.h> or calling WinAPI fns // to avoid compilation errors (like including <windows.h> or calling WinAPI fns
// on non-Windows systems) // on non-Windows systems)
if ((!is_not && os != p.pref.os) || (is_not && os == p.pref.os)) && !p.pref.output_cross_c { if false && ((!is_not && os != p.pref.os) || (is_not && os == p.pref.os)) && !p.pref.output_cross_c {
skip_os = true skip_os = true
p.check(.lcbr) p.check(.lcbr)
//p.warn('skipping $if $val os=$os p.pref.os=$p.pref.os') // p.warn('skipping $if $val os=$os p.pref.os=$p.pref.os')
mut stack := 1 mut stack := 1
for { for {
if p.tok.kind == .key_return { if p.tok.kind == .key_return {
@ -75,7 +75,7 @@ fn (p mut Parser) comp_if() ast.CompIf {
} }
const ( const (
todo_delete_me = pref.OS.linux // TODO import warning bug todo_delete_me = pref.OS.linux // TODO import warning bug
) )
fn os_from_string(os string) pref.OS { fn os_from_string(os string) pref.OS {

View File

@ -70,9 +70,10 @@ pub fn (p mut Parser) parse_multi_return_type() table.Type {
pub fn (p mut Parser) parse_fn_type(name string) table.Type { pub fn (p mut Parser) parse_fn_type(name string) table.Type {
// p.warn('parse fn') // p.warn('parse fn')
p.check(.key_fn) p.check(.key_fn)
args, is_variadic := p.fn_args() line_nr := p.tok.line_nr
args,is_variadic := p.fn_args()
mut return_type := table.void_type mut return_type := table.void_type
if p.tok.kind.is_start_of_type() { if p.tok.line_nr == line_nr && p.tok.kind.is_start_of_type() {
return_type = p.parse_type() return_type = p.parse_type()
} }
func := table.Fn{ func := table.Fn{
@ -96,7 +97,7 @@ pub fn (p mut Parser) parse_type() table.Type {
mut nr_muls := 0 mut nr_muls := 0
for p.tok.kind in [.and, .amp] { for p.tok.kind in [.and, .amp] {
if p.tok.kind == .and { if p.tok.kind == .and {
nr_muls+=2 nr_muls += 2
} }
else { else {
nr_muls++ nr_muls++

View File

@ -1520,6 +1520,13 @@ fn (p mut Parser) struct_decl() ast.StructDecl {
field_name := p.check_name() field_name := p.check_name()
// p.warn('field $field_name') // p.warn('field $field_name')
typ := p.parse_type() typ := p.parse_type()
/*
if name == '_net_module_s' {
s := p.table.get_type_symbol(typ)
println('XXXX' + s.str())
}
*/
// Default value // Default value
if p.tok.kind == .assign { if p.tok.kind == .assign {
p.next() p.next()