all: allow defer use from other scope (#10284)
parent
eac1e25c5d
commit
38796521fa
|
@ -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,6 +984,7 @@ pub:
|
|||
stmts []Stmt
|
||||
pos token.Position
|
||||
pub mut:
|
||||
defer_vars []Ident
|
||||
ifdef string
|
||||
idx_in_fn int = -1 // index in FnDecl.defer_stmts
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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<int>() 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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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]}'
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue