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
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)`

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 {
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 {

View File

@ -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

View File

@ -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

View File

@ -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)
}

View File

@ -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()

View File

@ -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

View File

@ -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 {

View File

@ -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]}'
}
}