all: allow defer use from other scope (#10284)

pull/10285/head^2
Louis Schmieder 2021-05-31 13:43:44 +02:00 committed by GitHub
parent eac1e25c5d
commit 38796521fa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 152 additions and 8 deletions

View File

@ -629,6 +629,7 @@ pub:
tok_kind token.Kind tok_kind token.Kind
pos token.Position pos token.Position
mut_pos token.Position mut_pos token.Position
comptime bool
pub mut: pub mut:
scope &Scope scope &Scope
obj ScopeObject obj ScopeObject
@ -983,6 +984,7 @@ pub:
stmts []Stmt stmts []Stmt
pos token.Position pos token.Position
pub mut: pub mut:
defer_vars []Ident
ifdef string ifdef string
idx_in_fn int = -1 // index in FnDecl.defer_stmts idx_in_fn int = -1 // index in FnDecl.defer_stmts
} }

View File

@ -3955,6 +3955,29 @@ fn (mut c Checker) stmt(node ast.Stmt) {
if c.locked_names.len != 0 || c.rlocked_names.len != 0 { if c.locked_names.len != 0 || c.rlocked_names.len != 0 {
c.error('defers are not allowed in lock statements', node.pos) 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) c.stmts(node.stmts)
} }
ast.EnumDecl { ast.EnumDecl {

View File

@ -174,6 +174,8 @@ mut:
obf_table map[string]string obf_table map[string]string
// main_fn_decl_node ast.FnDecl // main_fn_decl_node ast.FnDecl
expected_cast_type ast.Type // for match expr of sumtypes 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 { 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(';') g.writeln(';')
for i, lx in assign_stmt.left { for i, lx in assign_stmt.left {
mut is_auto_heap := false mut is_auto_heap := false
mut ident := ast.Ident{
scope: 0
}
if lx is ast.Ident { if lx is ast.Ident {
ident = lx
if lx.kind == .blank_ident { if lx.kind == .blank_ident {
continue continue
} }
@ -2256,7 +2262,11 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
is_auto_heap = lx.obj.is_auto_heap 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 { if assign_stmt.op == .decl_assign {
g.write('$styp ') g.write('$styp ')
if is_auto_heap { 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 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 is_fixed_array_init := false
mut has_val := false mut has_val := false
match val { 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) { fn (mut g Gen) gen_anon_fn_decl(mut node ast.AnonFn) {
if !node.has_gen { if !node.has_gen {
pos := g.out.len pos := g.out.len
g.anon_fn = true
g.stmt(node.decl) g.stmt(node.decl)
g.anon_fn = false
fn_body := g.out.after(pos) fn_body := g.out.after(pos)
g.out.go_back(fn_body.len) g.out.go_back(fn_body.len)
g.anon_fn_definitions << fn_body g.anon_fn_definitions << fn_body

View File

@ -103,6 +103,18 @@ fn (mut g Gen) gen_fn_decl(node &ast.FnDecl, skip bool) {
// || node.no_body { // || node.no_body {
return 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 // Skip [if xxx] if xxx is not defined
/* /*
for attr in node.attrs { for attr in node.attrs {
@ -265,6 +277,18 @@ fn (mut g Gen) gen_fn_decl(node &ast.FnDecl, skip bool) {
g.writeln(') {') g.writeln(') {')
for defer_stmt in node.defer_stmts { for defer_stmt in node.defer_stmts {
g.writeln('bool ${g.defer_flag_var(defer_stmt)} = false;') 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 { if is_live_wrap {
// The live function just calls its implementation dual, while ensuring // The live function just calls its implementation dual, while ensuring

View File

@ -6,7 +6,15 @@ module parser
import v.ast import v.ast
fn (mut p Parser) assign_stmt() ast.Stmt { fn (mut p Parser) assign_stmt() ast.Stmt {
mut defer_vars := p.defer_vars
p.defer_vars = []ast.Ident{}
exprs, comments := p.expr_list() 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) return p.partial_assign_stmt(exprs, comments)
} }

View File

@ -106,7 +106,9 @@ fn (mut p Parser) if_expr(is_comptime bool) ast.IfExpr {
prev_guard = true prev_guard = true
} else { } else {
prev_guard = false prev_guard = false
p.comp_if_cond = true
cond = p.expr(0) cond = p.expr(0)
p.comp_if_cond = false
} }
comments << p.eat_comments({}) comments << p.eat_comments({})
end_pos := p.prev_tok.position() end_pos := p.prev_tok.position()

View File

@ -77,6 +77,9 @@ mut:
inside_asm_template bool inside_asm_template bool
inside_asm bool inside_asm bool
global_labels []string global_labels []string
inside_defer bool
comp_if_cond bool
defer_vars []ast.Ident
} }
// for tests // for tests
@ -792,9 +795,13 @@ pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt {
.key_defer { .key_defer {
p.next() p.next()
spos := p.tok.position() spos := p.tok.position()
p.inside_defer = true
p.defer_vars = []ast.Ident{}
stmts := p.parse_block() stmts := p.parse_block()
p.inside_defer = false
return ast.DeferStmt{ return ast.DeferStmt{
stmts: stmts stmts: stmts
defer_vars: p.defer_vars.clone()
pos: spos.extend_with_last_line(p.tok.position(), p.prev_tok.line_nr) 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 // collect things upto hard boundaries
tok := p.tok tok := p.tok
mut pos := tok.position() mut pos := tok.position()
mut defer_vars := p.defer_vars
p.defer_vars = []ast.Ident{}
left, left_comments := p.expr_list() 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] left0 := left[0]
if tok.kind == .key_mut && p.tok.kind != .decl_assign { if tok.kind == .key_mut && p.tok.kind != .decl_assign {
return p.error('expecting `:=` (e.g. `mut x :=`)') 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{ return ast.Ident{
tok_kind: p.tok.kind tok_kind: p.tok.kind
name: '_' name: '_'
comptime: p.comp_if_cond
kind: .blank_ident kind: .blank_ident
pos: pos pos: pos
info: ast.IdentVar{ info: ast.IdentVar{
@ -1769,6 +1788,7 @@ pub fn (mut p Parser) parse_ident(language ast.Language) ast.Ident {
tok_kind: p.tok.kind tok_kind: p.tok.kind
kind: .unresolved kind: .unresolved
name: name name: name
comptime: p.comp_if_cond
language: language language: language
mod: p.mod mod: p.mod
pos: pos 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 } else if p.peek_tok.kind == .dot && p.peek_token(2).kind != .eof
&& p.peek_token(2).lit.len == 0 { && p.peek_token(2).lit.len == 0 {
// incomplete module selector must be handled by dot_expr instead // 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 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 same_line := p.tok.line_nr == p.peek_tok.line_nr
// `(` must be on same line as name token otherwise it's a ParExpr // `(` must be on same line as name token otherwise it's a ParExpr
if !same_line && p.peek_tok.kind == .lpar { 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 } else if p.peek_tok.kind == .lpar
|| (is_optional && p.peek_token(2).kind == .lpar) || p.is_generic_call() { || (is_optional && p.peek_token(2).kind == .lpar) || p.is_generic_call() {
// foo(), foo<int>() or type() cast // 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 // JS. function call with more than 1 dot
node = p.call_expr(language, mod) node = p.call_expr(language, mod)
} else { } 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 = '' p.expr_mod = ''
return node return node

View File

@ -33,7 +33,14 @@ pub fn (mut p Parser) check_expr(precedence int) ?ast.Expr {
// Prefix // Prefix
match p.tok.kind { match p.tok.kind {
.key_mut, .key_shared, .key_atomic, .key_static { .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 p.is_stmt_ident = is_stmt_ident
} }
.name, .question { .name, .question {

View File

@ -117,3 +117,28 @@ fn test_defer_order() {
assert i == 1 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]}'
}
}