compiler: v -autofree can now compile itself

pull/4758/head
Delyan Angelov 2020-05-06 19:03:44 +03:00
parent 2b0f2be18b
commit f638caef39
9 changed files with 106 additions and 63 deletions

View File

@ -212,6 +212,12 @@ fn (a array) slice2(start, _end int, end_max bool) array {
return a.slice(start, end)
}
// array.clone_static returns an independent copy of a given array
// It should be used only in -autofree generated code.
fn (a array) clone_static() array {
return a.clone()
}
// array.clone returns an independent copy of a given array
pub fn (a &array) clone() array {
mut size := a.cap * a.element_size
@ -304,7 +310,7 @@ pub fn (a array) reverse() array {
// pub fn (a []int) free() {
[unsafe_fn]
pub fn (a array) free() {
pub fn (a &array) free() {
// if a.is_slice {
// return
// }

View File

@ -98,11 +98,17 @@ pub fn (a array) reverse() array {
return a
}
// array.clone_static returns an independent copy of a given array
// It should be used only in -autofree generated code.
fn (a array) clone_static() array {
return a.clone()
}
pub fn (a array) clone() array {
return a
}
pub fn (a array) free() {
pub fn (a &array) free() {
}
// "[ 'a', 'b', 'c' ]"

View File

@ -26,6 +26,11 @@ pub fn tos(s byteptr) string {
}
}
// string.clone_static returns an independent copy of a given array
// It should be used only in -autofree generated code.
fn (a string) clone_static() string {
return a.clone()
}
pub fn (a string) clone() string {
return a
@ -241,18 +246,9 @@ pub fn (c byte) is_letter() bool {
return (c >= `a` && c <= `z`) || (c >= `A` && c <= `Z`)
}
pub fn (s string) free() {
pub fn (s &string) free() {
}
/*
fn (arr []string) free() {
for s in arr {
s.free()
}
C.free(arr.data)
}
*/
// all_before('23:34:45.234', '.') == '23:34:45'
pub fn (s string) all_before(dot string) string {
pos := s.index(dot)

View File

@ -413,7 +413,7 @@ pub fn (m &map) keys() []string {
}
[unsafe_fn]
pub fn (m map) free() {
pub fn (m &map) free() {
free(m.metas)
for i := u32(0); i < m.key_values.size; i++ {
if m.key_values.keys[i].str == 0 {

View File

@ -104,6 +104,12 @@ pub fn tos3(s charptr) string {
}
}
// string.clone_static returns an independent copy of a given array
// It should be used only in -autofree generated code.
fn (a string) clone_static() string {
return a.clone()
}
pub fn (a string) clone() string {
mut b := string{
len: a.len
@ -1141,7 +1147,7 @@ pub fn (u ustring) at(idx int) string {
return u.substr(idx, idx + 1)
}
fn (u ustring) free() {
fn (u &ustring) free() {
u.runes.free()
}
@ -1165,19 +1171,10 @@ pub fn (c byte) is_letter() bool {
return (c >= `a` && c <= `z`) || (c >= `A` && c <= `Z`)
}
pub fn (s string) free() {
pub fn (s &string) free() {
free(s.str)
}
/*
fn (arr []string) free() {
for s in arr {
s.free()
}
C.free(arr.data)
}
*/
// all_before('23:34:45.234', '.') == '23:34:45'
pub fn (s string) all_before(dot string) string {
pos := s.index(dot) or {

View File

@ -278,6 +278,7 @@ pub:
name string
expr Expr
is_mut bool
is_arg bool // fn args should not be autofreed
mut:
typ table.Type
pos token.Position

View File

@ -134,12 +134,12 @@ pub fn cgen(files []ast.File, table &table.Table, pref &pref.Preferences) string
for file in files {
g.file = file
// println('\ncgen "$g.file.path" nr_stmts=$file.stmts.len')
building_v := true && (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')
if g.file.path.ends_with('_test.v') {
g.is_test = is_test
}
if g.file.path == '' || is_test || building_v || !g.pref.autofree {
if g.file.path == '' || is_test || !g.pref.autofree {
// cgen test or building V
// println('autofree=false')
g.autofree = false
@ -646,6 +646,7 @@ fn (mut g Gen) stmt(node ast.Stmt) {
}
ast.Return {
g.write_defer_stmts_when_needed()
g.write_autofree_stmts_when_needed(it)
g.return_statement(it)
}
ast.StructDecl {
@ -980,7 +981,7 @@ fn (mut g Gen) gen_clone_assignment(val ast.Expr, right_sym table.TypeSymbol, ad
if add_eq {
g.write('=')
}
g.write(' array_clone(&')
g.write(' array_clone_static(')
g.expr(val)
g.write(')')
} else if g.autofree && right_sym.kind == .string && is_ident {
@ -988,15 +989,16 @@ fn (mut g Gen) gen_clone_assignment(val ast.Expr, right_sym table.TypeSymbol, ad
g.write('=')
}
// `str1 = str2` => `str1 = str2.clone()`
g.write(' string_clone(')
g.write(' string_clone_static(')
g.expr(val)
g.write(')')
}
return true
}
fn (mut g Gen) free_scope_vars(pos int) {
println('free_scope_vars($pos)')
fn (mut g Gen) autofree_scope_vars(pos int) string {
// eprintln('> free_scope_vars($pos)')
mut freeing_code := ''
scope := g.file.scope.innermost(pos)
for _, obj in scope.objects {
match obj {
@ -1006,34 +1008,56 @@ fn (mut g Gen) free_scope_vars(pos int) {
// continue
// }
v := *it
println(v.name)
// println(v.typ)
sym := g.table.get_type_symbol(v.typ)
is_optional := v.typ.flag_is(.optional)
if sym.kind == .array && !is_optional {
g.writeln('\tarray_free($v.name); // autofreed')
}
if sym.kind == .string && !is_optional {
// Don't free simple string literals.
t := typeof(v.expr)
match v.expr {
ast.StringLiteral {
g.writeln('// str literal')
continue
}
else {
// NOTE/TODO: assign_stmt multi returns variables have no expr
// since the type comes from the called fns return type
g.writeln('// other ' + t)
continue
}
}
g.writeln('string_free($v.name); // autofreed')
if is_optional {
// TODO: free optionals
continue
}
freeing_code += g.autofree_variable(v)
}
else {}
}
}
return freeing_code
}
fn (g &Gen) autofree_variable(v ast.Var) string {
sym := g.table.get_type_symbol(v.typ)
// eprintln(' > var name: ${v.name:-20s} | is_arg: ${v.is_arg.str():6} | var type: ${int(v.typ):8} | type_name: ${sym.name:-33s}')
if sym.kind == .array {
return g.autofree_var_call('array_free', v)
}
if sym.kind == .string {
// Don't free simple string literals.
t := typeof(v.expr)
match v.expr {
ast.StringLiteral {
return '// str literal\n'
}
else {
// NOTE/TODO: assign_stmt multi returns variables have no expr
// since the type comes from the called fns return type
return '// other ' + t + '\n'
}
}
return g.autofree_var_call('string_free', v)
}
if sym.has_method('free') {
return g.autofree_var_call(c_name(sym.name) + '_free', v)
}
return ''
}
fn (g &Gen) autofree_var_call(free_fn_name string, v ast.Var) string {
if v.is_arg {
// fn args should not be autofreed
return ''
}
if v.typ.is_ptr() {
return '\t${free_fn_name}($v.name); // autofreed ptr var\n'
}else{
return '\t${free_fn_name}(&$v.name); // autofreed var\n'
}
}
fn (mut g Gen) expr(node ast.Expr) {
@ -2221,10 +2245,10 @@ fn (mut g Gen) write_init_function() {
g.writeln('void _vcleanup() {')
// g.writeln('puts("cleaning up...");')
if g.is_importing_os() {
g.writeln('free(_const_os__args.data);')
g.writeln('string_free(_const_os__wd_at_startup);')
g.writeln('array_free(&_const_os__args);')
g.writeln('string_free(&_const_os__wd_at_startup);')
}
g.writeln('free(_const_strconv__ftoa__powers_of_10.data);')
g.writeln('array_free(&_const_strconv__ftoa__powers_of_10);')
g.writeln('}')
}
}
@ -3215,7 +3239,7 @@ fn (mut g Gen) gen_str_default(sym table.TypeSymbol, styp, str_fn_name string) {
g.auto_str_funcs.writeln('\tstring tmp1 = string_add(tos3("${styp}("), tos3(${typename}_str((${convertor})it).str));')
}
g.auto_str_funcs.writeln('\tstring tmp2 = string_add(tmp1, tos3(")"));')
g.auto_str_funcs.writeln('\tstring_free(tmp1);')
g.auto_str_funcs.writeln('\tstring_free(&tmp1);')
g.auto_str_funcs.writeln('\treturn tmp2;')
g.auto_str_funcs.writeln('}')
}
@ -3324,7 +3348,7 @@ fn (mut g Gen) gen_str_for_array(info table.Array, styp, str_fn_name string) {
g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, x);')
if g.pref.autofree && info.elem_type != table.bool_type {
// no need to free "true"/"false" literals
g.auto_str_funcs.writeln('\t\tstring_free(x);')
g.auto_str_funcs.writeln('\t\tstring_free(&x);')
}
g.auto_str_funcs.writeln('\t\tif (i < a.len-1) {')
g.auto_str_funcs.writeln('\t\t\tstrings__Builder_write(&sb, tos3(", "));')

View File

@ -159,11 +159,6 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl) {
}
g.stmts(it.stmts)
// ////////////
if g.autofree {
// println('\n\ncalling free for fn $it.name')
g.free_scope_vars(it.body_pos.pos)
}
// /////////
if is_main {
if g.autofree {
g.writeln('\t_vcleanup();')
@ -173,6 +168,11 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl) {
}
}
g.write_defer_stmts_when_needed()
// /////////
if g.autofree {
// TODO: remove this, when g.write_autofree_stmts_when_needed works properly
g.writeln( g.autofree_scope_vars(it.body_pos.pos) )
}
if is_main {
g.writeln('\treturn 0;')
}
@ -183,6 +183,17 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl) {
}
}
fn (mut g Gen) write_autofree_stmts_when_needed(r ast.Return) {
// TODO: write_autofree_stmts_when_needed should account for the current local scope vars.
// TODO: write_autofree_stmts_when_needed should not free the returned variables.
// It may require rewriting g.return_statement to assign the expressions
// to temporary variables, then protecting *them* from autofreeing ...
g.writeln('/* autofreeings before return: -------')
//g.write( g.autofree_scope_vars(r.pos.pos) )
g.write( g.autofree_scope_vars(g.fn_decl.body_pos.pos) )
g.writeln('--------------------------------------------------- */')
}
fn (mut g Gen) write_defer_stmts_when_needed() {
if g.defer_stmts.len > 0 {
g.write_defer_stmts()
@ -440,7 +451,7 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
// tmps << tmp
g.write('string $tmp = ${str_fn_name}(')
g.expr(node.args[0].expr)
g.writeln('); ${print_method}($tmp); string_free($tmp); //MEM2 $styp')
g.writeln('); ${print_method}($tmp); string_free(&$tmp); //MEM2 $styp')
} else {
expr := node.args[0].expr
is_var := match expr {

View File

@ -175,6 +175,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
is_mut: arg.is_mut
pos: p.tok.position()
is_used: true
is_arg: true
})
// Do not allow `mut` with simple types
// TODO move to checker?
@ -280,6 +281,7 @@ fn (mut p Parser) anon_fn() ast.AnonFn {
typ: arg.typ
pos: p.tok.position()
is_used: true
is_arg: true
})
}
mut return_type := table.void_type