diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index c856e7a662..e58c69e144 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -629,6 +629,7 @@ pub: tok_kind token.Kind pos token.Position mut_pos token.Position + comptime bool pub mut: scope &Scope obj ScopeObject @@ -983,8 +984,9 @@ pub: stmts []Stmt pos token.Position pub mut: - ifdef string - idx_in_fn int = -1 // index in FnDecl.defer_stmts + defer_vars []Ident + ifdef string + idx_in_fn int = -1 // index in FnDecl.defer_stmts } // `(3+4)` diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index b8e6937dff..bdbe52a655 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -3955,6 +3955,29 @@ fn (mut c Checker) stmt(node ast.Stmt) { if c.locked_names.len != 0 || c.rlocked_names.len != 0 { c.error('defers are not allowed in lock statements', node.pos) } + for i, ident in node.defer_vars { + mut id := ident + if id.info is ast.IdentVar { + if id.comptime && (id.name in checker.valid_comp_if_compilers + || id.name in checker.valid_comp_if_os + || id.name in checker.valid_comp_if_other + || id.name in checker.valid_comp_if_platforms) { + node.defer_vars[i] = ast.Ident{ + scope: 0 + name: '' + } + continue + } + mut info := id.info as ast.IdentVar + typ := c.ident(mut id) + if typ == ast.error_type_idx { + continue + } + info.typ = typ + id.info = info + node.defer_vars[i] = id + } + } c.stmts(node.stmts) } ast.EnumDecl { diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 30ac14e712..94acb59007 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -174,6 +174,8 @@ mut: obf_table map[string]string // main_fn_decl_node ast.FnDecl expected_cast_type ast.Type // for match expr of sumtypes + defer_vars []string + anon_fn bool } pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string { @@ -2248,7 +2250,11 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { g.writeln(';') for i, lx in assign_stmt.left { mut is_auto_heap := false + mut ident := ast.Ident{ + scope: 0 + } if lx is ast.Ident { + ident = lx if lx.kind == .blank_ident { continue } @@ -2256,7 +2262,11 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { is_auto_heap = lx.obj.is_auto_heap } } - styp := g.typ(assign_stmt.left_types[i]) + styp := if ident.name in g.defer_vars { + '' + } else { + g.typ(assign_stmt.left_types[i]) + } if assign_stmt.op == .decl_assign { g.write('$styp ') if is_auto_heap { @@ -2403,7 +2413,7 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { is_auto_heap = left.obj.is_auto_heap } } - styp := g.typ(var_type) + styp := if ident.name in g.defer_vars { '' } else { g.typ(var_type) } mut is_fixed_array_init := false mut has_val := false match val { @@ -2946,7 +2956,9 @@ fn (mut g Gen) autofree_var_call(free_fn_name string, v ast.Var) { fn (mut g Gen) gen_anon_fn_decl(mut node ast.AnonFn) { if !node.has_gen { pos := g.out.len + g.anon_fn = true g.stmt(node.decl) + g.anon_fn = false fn_body := g.out.after(pos) g.out.go_back(fn_body.len) g.anon_fn_definitions << fn_body diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index bc23670931..14637092aa 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -103,6 +103,18 @@ fn (mut g Gen) gen_fn_decl(node &ast.FnDecl, skip bool) { // || node.no_body { return } + + tmp_defer_vars := g.defer_vars // must be here because of workflow + if !g.anon_fn { + g.defer_vars = []string{} + } else { + if node.defer_stmts.len > 0 { + g.defer_vars = []string{} + defer { + g.defer_vars = tmp_defer_vars + } + } + } // Skip [if xxx] if xxx is not defined /* for attr in node.attrs { @@ -265,6 +277,18 @@ fn (mut g Gen) gen_fn_decl(node &ast.FnDecl, skip bool) { g.writeln(') {') for defer_stmt in node.defer_stmts { g.writeln('bool ${g.defer_flag_var(defer_stmt)} = false;') + for var in defer_stmt.defer_vars { + if var.name in fargs || var.kind == .constant { + continue + } + if var.info is ast.IdentVar { + info := var.info + if var.name !in g.defer_vars { + g.defer_vars << var.name + g.writeln('${g.typ(info.typ)} $var.name;') + } + } + } } if is_live_wrap { // The live function just calls its implementation dual, while ensuring diff --git a/vlib/v/parser/assign.v b/vlib/v/parser/assign.v index 7e6d1ce32b..229de5f341 100644 --- a/vlib/v/parser/assign.v +++ b/vlib/v/parser/assign.v @@ -6,7 +6,15 @@ module parser import v.ast fn (mut p Parser) assign_stmt() ast.Stmt { + mut defer_vars := p.defer_vars + p.defer_vars = []ast.Ident{} + exprs, comments := p.expr_list() + + if !(p.inside_defer && p.tok.kind == .decl_assign) { + defer_vars << p.defer_vars + } + p.defer_vars = defer_vars return p.partial_assign_stmt(exprs, comments) } diff --git a/vlib/v/parser/if_match.v b/vlib/v/parser/if_match.v index 199a07e736..513b4bbe6d 100644 --- a/vlib/v/parser/if_match.v +++ b/vlib/v/parser/if_match.v @@ -106,7 +106,9 @@ fn (mut p Parser) if_expr(is_comptime bool) ast.IfExpr { prev_guard = true } else { prev_guard = false + p.comp_if_cond = true cond = p.expr(0) + p.comp_if_cond = false } comments << p.eat_comments({}) end_pos := p.prev_tok.position() diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 50d80081c9..d935896542 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -77,6 +77,9 @@ mut: inside_asm_template bool inside_asm bool global_labels []string + inside_defer bool + comp_if_cond bool + defer_vars []ast.Ident } // for tests @@ -792,9 +795,13 @@ pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt { .key_defer { p.next() spos := p.tok.position() + p.inside_defer = true + p.defer_vars = []ast.Ident{} stmts := p.parse_block() + p.inside_defer = false return ast.DeferStmt{ stmts: stmts + defer_vars: p.defer_vars.clone() pos: spos.extend_with_last_line(p.tok.position(), p.prev_tok.line_nr) } } @@ -1689,7 +1696,18 @@ fn (mut p Parser) parse_multi_expr(is_top_level bool) ast.Stmt { // collect things upto hard boundaries tok := p.tok mut pos := tok.position() + + mut defer_vars := p.defer_vars + p.defer_vars = []ast.Ident{} + left, left_comments := p.expr_list() + + if !(p.inside_defer && p.tok.kind == .decl_assign) { + defer_vars << p.defer_vars + } + + p.defer_vars = defer_vars + left0 := left[0] if tok.kind == .key_mut && p.tok.kind != .decl_assign { return p.error('expecting `:=` (e.g. `mut x :=`)') @@ -1750,6 +1768,7 @@ pub fn (mut p Parser) parse_ident(language ast.Language) ast.Ident { return ast.Ident{ tok_kind: p.tok.kind name: '_' + comptime: p.comp_if_cond kind: .blank_ident pos: pos info: ast.IdentVar{ @@ -1769,6 +1788,7 @@ pub fn (mut p Parser) parse_ident(language ast.Language) ast.Ident { tok_kind: p.tok.kind kind: .unresolved name: name + comptime: p.comp_if_cond language: language mod: p.mod pos: pos @@ -1954,7 +1974,14 @@ pub fn (mut p Parser) name_expr() ast.Expr { } else if p.peek_tok.kind == .dot && p.peek_token(2).kind != .eof && p.peek_token(2).lit.len == 0 { // incomplete module selector must be handled by dot_expr instead - node = p.parse_ident(language) + ident := p.parse_ident(language) + node = ident + if p.inside_defer { + if p.defer_vars.filter(it.name == ident.name + && it.mod == ident.mod).len == 0 && ident.name != 'err' { + p.defer_vars << ident + } + } return node } } @@ -1975,7 +2002,14 @@ pub fn (mut p Parser) name_expr() ast.Expr { same_line := p.tok.line_nr == p.peek_tok.line_nr // `(` must be on same line as name token otherwise it's a ParExpr if !same_line && p.peek_tok.kind == .lpar { - node = p.parse_ident(language) + ident := p.parse_ident(language) + node = ident + if p.inside_defer { + if p.defer_vars.filter(it.name == ident.name && it.mod == ident.mod).len == 0 + && ident.name != 'err' { + p.defer_vars << ident + } + } } else if p.peek_tok.kind == .lpar || (is_optional && p.peek_token(2).kind == .lpar) || p.is_generic_call() { // foo(), foo() or type() cast @@ -2087,7 +2121,14 @@ pub fn (mut p Parser) name_expr() ast.Expr { // JS. function call with more than 1 dot node = p.call_expr(language, mod) } else { - node = p.parse_ident(language) + ident := p.parse_ident(language) + node = ident + if p.inside_defer { + if p.defer_vars.filter(it.name == ident.name && it.mod == ident.mod).len == 0 + && ident.name != 'err' { + p.defer_vars << ident + } + } } p.expr_mod = '' return node diff --git a/vlib/v/parser/pratt.v b/vlib/v/parser/pratt.v index 1c623ceddb..1221b287bc 100644 --- a/vlib/v/parser/pratt.v +++ b/vlib/v/parser/pratt.v @@ -33,7 +33,14 @@ pub fn (mut p Parser) check_expr(precedence int) ?ast.Expr { // Prefix match p.tok.kind { .key_mut, .key_shared, .key_atomic, .key_static { - node = p.parse_ident(ast.Language.v) + ident := p.parse_ident(ast.Language.v) + node = ident + if p.inside_defer { + if p.defer_vars.filter(it.name == ident.name && it.mod == ident.mod).len == 0 + && ident.name != 'err' { + p.defer_vars << ident + } + } p.is_stmt_ident = is_stmt_ident } .name, .question { diff --git a/vlib/v/tests/defer_test.v b/vlib/v/tests/defer_test.v index 8cdc73db84..0a2be26913 100644 --- a/vlib/v/tests/defer_test.v +++ b/vlib/v/tests/defer_test.v @@ -117,3 +117,28 @@ fn test_defer_order() { assert i == 1 } } + +fn test_defer_access() { + if true { + mut i := 0 + defer { + i++ + assert i == 1 + } + } +} + +fn test_defer_arrays() { + mut ia := []int{} + defer { + ia << 1 + } +} + +fn test_defer_str_interpol() { + mut t := []string{} + defer { + t << 'test' + t << '${t[0]}' + } +}