gen: fix bug where unreached defer is executed (#8594)

pull/8613/head
zakuro 2021-02-07 01:40:39 +09:00 committed by GitHub
parent de9813233f
commit fe9d062b41
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 49 additions and 5 deletions

View File

@ -333,6 +333,7 @@ pub:
skip_gen bool // this function doesn't need to be generated (for example [if foo])
pub mut:
stmts []Stmt
defer_stmts []DeferStmt
return_type table.Type
has_return bool
comments []Comment // comments *after* the header, but *before* `{`; used for InterfaceDecl
@ -891,7 +892,8 @@ pub:
stmts []Stmt
pos token.Position
pub mut:
ifdef string
ifdef string
idx_in_fn int = -1 // index in FnDecl.defer_stmts
}
// `(3+4)`

View File

@ -3130,6 +3130,10 @@ fn (mut c Checker) stmt(node ast.Stmt) {
c.inside_const = false
}
ast.DeferStmt {
if node.idx_in_fn < 0 {
node.idx_in_fn = c.cur_fn.defer_stmts.len
c.cur_fn.defer_stmts << &node
}
c.stmts(node.stmts)
}
ast.EnumDecl {

View File

@ -994,6 +994,7 @@ fn (mut g Gen) stmt(node ast.Stmt) {
ast.DeferStmt {
mut defer_stmt := node
defer_stmt.ifdef = g.defer_ifdef
g.writeln('${g.defer_flag_var(defer_stmt)} = true;')
g.defer_stmts << defer_stmt
}
ast.EnumDecl {
@ -1280,6 +1281,8 @@ fn (mut g Gen) stmt(node ast.Stmt) {
fn (mut g Gen) write_defer_stmts() {
for defer_stmt in g.defer_stmts {
g.writeln('// Defer begin')
g.writeln('if (${g.defer_flag_var(defer_stmt)} == true) {')
g.indent++
if defer_stmt.ifdef.len > 0 {
g.writeln(defer_stmt.ifdef)
g.stmts(defer_stmt.stmts)
@ -1290,6 +1293,8 @@ fn (mut g Gen) write_defer_stmts() {
g.stmts(defer_stmt.stmts)
g.indent++
}
g.indent--
g.writeln('}')
g.writeln('// Defer end')
}
}

View File

@ -65,6 +65,10 @@ fn (mut g Gen) gen_fn_decl(node ast.FnDecl, skip bool) {
g.cur_generic_types = []
return
}
cur_fn_save := g.cur_fn
defer {
g.cur_fn = cur_fn_save
}
g.cur_fn = node
fn_start_pos := g.out.len
g.write_v_source_line_info(node.pos)
@ -131,6 +135,10 @@ fn (mut g Gen) gen_fn_decl(node ast.FnDecl, skip bool) {
if is_live_wrap {
impl_fn_name = 'impl_live_$name'
}
last_fn_c_name_save := g.last_fn_c_name
defer {
g.last_fn_c_name = last_fn_c_name_save
}
g.last_fn_c_name = impl_fn_name
//
if is_live_wrap {
@ -179,6 +187,9 @@ fn (mut g Gen) gen_fn_decl(node ast.FnDecl, skip bool) {
}
g.definitions.writeln(');')
g.writeln(') {')
for defer_stmt in node.defer_stmts {
g.writeln('bool ${g.defer_flag_var(defer_stmt)} = false;')
}
if is_live_wrap {
// The live function just calls its implementation dual, while ensuring
// that the call is wrapped by the mutex lock & unlock calls.
@ -258,6 +269,10 @@ fn (mut g Gen) gen_fn_decl(node ast.FnDecl, skip bool) {
}
}
fn (g &Gen) defer_flag_var(stmt &ast.DeferStmt) string {
return '${g.last_fn_c_name}_defer_$stmt.idx_in_fn'
}
fn (mut g Gen) write_defer_stmts_when_needed() {
if g.defer_stmts.len > 0 {
g.write_defer_stmts()

View File

@ -27,8 +27,7 @@ fn set_num(i int, mut n Num) {
println('Hi')
if i < 5 {
return
}
else {
} else {
n.val++
}
}
@ -60,15 +59,18 @@ fn test_defer_early_exit() {
fn test_defer_option() {
mut ok := Num{0}
set_num_opt(mut ok) or {}
set_num_opt(mut ok) or { }
assert ok.val == 1
}
fn test_defer_with_anon_fn() {
mut f := &Num{val: 110}
mut f := &Num{
val: 110
}
defer {
assert f.add(1) == 111
}
go fn () {
defer {
println('deferred 1')
@ -83,3 +85,19 @@ fn test_defer_with_anon_fn() {
x()
return
}
fn set_num_if(mut n Num, v int, cond bool) {
if cond {
defer {
n.val = v
}
}
}
fn test_defer_with_if() {
mut n := Num{0}
set_num_if(mut n, 10, true)
assert n.val == 10
set_num_if(mut n, 20, false)
assert n.val == 10
}