diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2201220828..ce2537d546 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -170,7 +170,7 @@ jobs: run: ./v -silent build-examples - name: Build examples with -autofree run: | - ./v -autofree -experimental -o tetris examples/tetris/tetris.v + ./v -autofree -o tetris examples/tetris/tetris.v - name: v doctor run: | ./v doctor @@ -182,7 +182,7 @@ jobs: run: | git clone --depth 1 https://github.com/vlang/ved cd ved && ../v -o ved . -# ../v -autofree . + ../v -autofree . - name: Build V UI examples run: | git clone --depth 1 https://github.com/vlang/ui @@ -538,6 +538,7 @@ jobs: git clone --depth 1 https://github.com/vlang/gitly cd gitly ../v . + ../v -autofree . cd .. websocket_autobahn: diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index b2f1095aa5..e1acd34b01 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -311,9 +311,6 @@ pub mut: generic_type table.Type // TODO array, to support multiple types generic_list_pos token.Position free_receiver bool // true if the receiver expression needs to be freed - // autofree_pregen string - // autofree_vars []AutofreeArgVar - // autofree_vars_ids []int } /* @@ -371,6 +368,8 @@ pub mut: pos token.Position is_used bool is_changed bool // to detect mutable vars that are never changed + is_or bool // `x := foo() or { ... }` + // (for setting the position after the or block for autofree) } // used for smartcasting only diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index 11d724b1ca..e12e262801 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -1079,7 +1079,7 @@ fn (mut g Gen) stmt(node ast.Stmt) { } ast.Module { // g.is_builtin_mod = node.name == 'builtin' - g.is_builtin_mod = node.name in ['builtin', 'os', 'strconv', 'strings'] + g.is_builtin_mod = node.name in ['builtin', 'os', 'strconv', 'strings', 'gg'] g.cur_mod = node.name } ast.Return { @@ -2070,7 +2070,7 @@ fn (mut g Gen) autofree_scope_vars(pos int, line_nr int, free_parent_scopes bool // TODO why can scope.pos be 0? (only outside fns?) return } - g.writeln('// autofree_scope_vars(pos=$pos scope.pos=$scope.start_pos scope.end_pos=$scope.end_pos)') + g.writeln('// autofree_scope_vars(pos=$pos line_nr=$line_nr scope.pos=$scope.start_pos scope.end_pos=$scope.end_pos)') // g.autofree_scope_vars2(scope, scope.end_pos) g.autofree_scope_vars2(scope, scope.start_pos, scope.end_pos, line_nr, free_parent_scopes) } @@ -2083,11 +2083,16 @@ fn (mut g Gen) autofree_scope_vars2(scope &ast.Scope, start_pos int, end_pos int for _, obj in scope.objects { match obj { ast.Var { - g.writeln('// var $obj.name pos=$obj.pos.pos') + g.writeln('// var "$obj.name" var.pos=$obj.pos.pos var.line_nr=$obj.pos.line_nr') if obj.name == g.returned_var_name { g.writeln('// skipping returned var') continue } + if obj.is_or { + // Skip vars inited with the `or {}`, since they are generated + // after the or block in C. + continue + } // if var.typ == 0 { // // TODO why 0? // continue @@ -2117,7 +2122,6 @@ fn (mut g Gen) autofree_scope_vars2(scope &ast.Scope, start_pos int, end_pos int // ``` // if !isnil(scope.parent) && line_nr > 0 { if free_parent_scopes && !isnil(scope.parent) { - // g.autofree_scope_vars2(scope.parent, end_pos) g.writeln('// af parent scope:') g.autofree_scope_vars2(scope.parent, start_pos, end_pos, line_nr, true) } @@ -3918,7 +3922,7 @@ fn (mut g Gen) return_statement(node ast.Return) { } else { if g.pref.autofree && !g.is_builtin_mod { g.writeln('// free before return (no values returned)') - g.autofree_scope_vars(node.pos.pos + 1, node.pos.line_nr, true) + g.autofree_scope_vars(node.pos.pos - 1, node.pos.line_nr, true) } g.writeln('return;') } diff --git a/vlib/v/gen/fn.v b/vlib/v/gen/fn.v index 69315d9dc0..a62168e035 100644 --- a/vlib/v/gen/fn.v +++ b/vlib/v/gen/fn.v @@ -423,7 +423,7 @@ 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*/*') } - if node.free_receiver && !g.inside_lambda { + if node.free_receiver && !g.inside_lambda && !g.is_builtin_mod { // The receiver expression needs to be freed, use the temp var. fn_name := node.name.replace('.', '_') arg_name := '_arg_expr_${fn_name}_0_$node.pos.pos' @@ -652,7 +652,7 @@ fn (mut g Gen) autofree_call_pregen(node ast.CallExpr) { } else { scope.register(ast.Var{ name: t - 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 + typ: table.string_type is_autofree_tmp: true }) s = 'string $t = ' @@ -662,10 +662,7 @@ fn (mut g Gen) autofree_call_pregen(node ast.CallExpr) { // 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 - // g.strs_to_free << t - // g.nr_vars_to_free++ - // g.strs_to_free << 'string_free(&$t);' + // This tmp arg var will be freed with the rest of the vars at the end of the scope. } } diff --git a/vlib/v/parser/assign.v b/vlib/v/parser/assign.v index f4983c7dac..2731bc6072 100644 --- a/vlib/v/parser/assign.v +++ b/vlib/v/parser/assign.v @@ -131,6 +131,7 @@ fn (mut p Parser) partial_assign_stmt(left []ast.Expr, left_comments []ast.Comme is_static = true } } + r0 := right[0] mut v := ast.Var{ name: lx.name expr: if left.len == right.len { right[i] } else { ast.Expr{} } @@ -138,6 +139,17 @@ fn (mut p Parser) partial_assign_stmt(left []ast.Expr, left_comments []ast.Comme is_mut: lx.is_mut || p.inside_for pos: lx.pos } + if p.pref.autofree { + if r0 is ast.CallExpr { + // Set correct variable position (after the or block) + // so that autofree doesn't free it in cgen before + // it's declared. (`Or` variables are declared after the or block). + if r0.or_block.pos.pos > 0 { + v.is_or = true + // v.pos = r0.or_block.pos. + } + } + } obj := ast.ScopeObject(v) lx.obj = obj p.scope.register(obj)