diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 252d9a2e22..cf7499a1c0 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -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)` diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 4a6bb9e83c..455c12c654 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -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 { diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index d0c5e35bca..6462298127 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -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') } } diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index 48576c25e6..4162fb5d0a 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -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() diff --git a/vlib/v/tests/defer_test.v b/vlib/v/tests/defer_test.v index a6e69af3fc..32e85ea6df 100644 --- a/vlib/v/tests/defer_test.v +++ b/vlib/v/tests/defer_test.v @@ -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 +}