Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion vlib/v/ast/ast.v
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ pub struct Block {
pub:
is_unsafe bool
pos token.Pos
scope &Scope
pub mut:
stmts []Stmt
}
Expand Down Expand Up @@ -808,6 +809,7 @@ pub struct BranchStmt {
pub:
kind token.Kind
label string
scope &Scope
pos token.Pos
}

Expand Down Expand Up @@ -887,6 +889,7 @@ pub mut:
// function return statement
pub struct Return {
pub:
scope &Scope
pos token.Pos
comments []Comment
pub mut:
Expand Down Expand Up @@ -1548,7 +1551,8 @@ pub:
@[minify]
pub struct DeferStmt {
pub:
pos token.Pos
pos token.Pos
scope &Scope
pub mut:
stmts []Stmt
defer_vars []Ident
Expand Down
6 changes: 4 additions & 2 deletions vlib/v/checker/fn.v
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,8 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
node.stmts.last().pos
}
node.stmts << ast.Return{
pos: return_pos // node.pos
scope: node.scope
pos: return_pos // node.pos
}
}
}
Expand All @@ -512,7 +513,8 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
sym := c.table.sym(node.return_type)
if sym.kind == .void {
node.stmts << ast.Return{
pos: node.pos
scope: node.scope
pos: node.pos
}
}
}
Expand Down
1 change: 1 addition & 0 deletions vlib/v/checker/lambda_expr.v
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ pub fn (mut c Checker) lambda_expr(mut node ast.LambdaExpr, exp_typ ast.Type) as
}
} else {
stmts << ast.Return{
scope: node.scope
pos: node.pos
exprs: [node.expr]
}
Expand Down
75 changes: 34 additions & 41 deletions vlib/v/gen/c/cgen.v
Original file line number Diff line number Diff line change
Expand Up @@ -2549,6 +2549,11 @@ fn (mut g Gen) stmt(node ast.Stmt) {
}
}
g.stmts(node.stmts)
if g.pref.scoped_defer {
if node.scope != unsafe { nil } {
g.write_defer_stmts_when_needed(node.scope, false)
}
}
g.writeln('}')
if node.is_unsafe {
g.unsafe_level--
Expand Down Expand Up @@ -2655,7 +2660,9 @@ 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;')
if !g.pref.scoped_defer {
g.writeln('${g.defer_flag_var(defer_stmt)} = true;')
}
g.defer_stmts << defer_stmt
}
ast.EnumDecl {
Expand Down Expand Up @@ -2762,30 +2769,6 @@ fn (mut g Gen) stmt(node ast.Stmt) {
// `foo('a' + 'b')` => `tmp := 'a' + 'b'; foo(tmp); string_free(&tmp);`
}

fn (mut g Gen) write_defer_stmts() {
for i := g.defer_stmts.len - 1; i >= 0; i-- {
defer_stmt := g.defer_stmts[i]
if !g.pref.is_prod {
g.writeln('// Defer begin')
}
g.writeln('if (${g.defer_flag_var(defer_stmt)}) {')

// g.indent++
if defer_stmt.ifdef.len > 0 {
g.writeln(defer_stmt.ifdef)
g.stmts(defer_stmt.stmts)
g.writeln2('', '#endif')
} else {
g.stmts(defer_stmt.stmts)
}
// g.indent--
g.writeln('}')
if !g.pref.is_prod {
g.writeln('// Defer end')
}
}
}

struct SumtypeCastingFn {
fn_name string
got ast.Type
Expand Down Expand Up @@ -5036,6 +5019,9 @@ fn (mut g Gen) lock_expr(node ast.LockExpr) {
if node.is_expr {
g.writeln(';')
}
if g.pref.scoped_defer {
g.write_defer_stmts(node.scope, false)
}
g.writeln('}')
g.unlock_locks()
if node.is_expr {
Expand Down Expand Up @@ -6029,6 +6015,9 @@ fn (mut g Gen) branch_stmt(node ast.BranchStmt) {
}
else {}
}
if g.pref.scoped_defer {
g.write_defer_stmts(node.scope, false)
}
// continue or break
if g.is_autofree && !g.is_builtin_mod {
g.trace_autofree('// free before continue/break')
Expand Down Expand Up @@ -6081,7 +6070,7 @@ fn (mut g Gen) return_stmt(node ast.Return) {
&& !fn_ret_type.has_option_or_result()
mut has_semicolon := false
if exprs_len == 0 {
g.write_defer_stmts_when_needed()
g.write_defer_stmts_when_needed(node.scope, true)
if fn_return_is_option || fn_return_is_result {
styp := g.styp(fn_ret_type)
if g.is_autofree {
Expand Down Expand Up @@ -6109,10 +6098,10 @@ fn (mut g Gen) return_stmt(node ast.Return) {
g.write('${ret_typ} ${tmpvar} = ')
g.expr(expr0)
g.writeln(';')
g.write_defer_stmts_when_needed()
g.write_defer_stmts_when_needed(node.scope, true)
g.writeln('return ${tmpvar};')
} else {
g.write_defer_stmts_when_needed()
g.write_defer_stmts_when_needed(node.scope, true)
g.write('return ')
g.expr(expr0)
g.writeln(';')
Expand All @@ -6135,7 +6124,7 @@ fn (mut g Gen) return_stmt(node ast.Return) {
g.write('${ret_typ} ${test_error_var} = ')
g.gen_option_error(fn_ret_type, expr0)
g.writeln(';')
g.write_defer_stmts_when_needed()
g.write_defer_stmts_when_needed(node.scope, true)
g.gen_failing_return_error_for_test_fn(node, test_error_var)
return
}
Expand All @@ -6158,7 +6147,7 @@ fn (mut g Gen) return_stmt(node ast.Return) {
}
}
}
g.write_defer_stmts_when_needed()
g.write_defer_stmts_when_needed(node.scope, true)
g.writeln('return ${tmpvar};')
}
return
Expand All @@ -6174,7 +6163,7 @@ fn (mut g Gen) return_stmt(node ast.Return) {
g.write('${ret_typ} ${test_error_var} = ')
g.gen_result_error(fn_ret_type, expr0)
g.writeln(';')
g.write_defer_stmts_when_needed()
g.write_defer_stmts_when_needed(node.scope, true)
g.gen_failing_return_error_for_test_fn(node, test_error_var)
return
}
Expand All @@ -6186,7 +6175,7 @@ fn (mut g Gen) return_stmt(node ast.Return) {
g.gen_result_error(fn_ret_type, expr0)
g.writeln(';')
if use_tmp_var {
g.write_defer_stmts_when_needed()
g.write_defer_stmts_when_needed(node.scope, true)
g.writeln('return ${tmpvar};')
}
return
Expand All @@ -6199,7 +6188,7 @@ fn (mut g Gen) return_stmt(node ast.Return) {
g.write('${ret_typ} ${tmpvar} = ')
g.expr(expr0)
g.writeln(';')
g.write_defer_stmts_when_needed()
g.write_defer_stmts_when_needed(node.scope, true)
g.writeln('return ${tmpvar};')
return
}
Expand Down Expand Up @@ -6301,10 +6290,10 @@ fn (mut g Gen) return_stmt(node ast.Return) {
g.write('}')
if fn_return_is_option {
g.writeln(' }, (${option_name}*)(&${tmpvar}), sizeof(${styp}));')
g.write_defer_stmts_when_needed()
g.write_defer_stmts_when_needed(node.scope, true)
} else if fn_return_is_result {
g.writeln(' }, (${result_name}*)(&${tmpvar}), sizeof(${styp}));')
g.write_defer_stmts_when_needed()
g.write_defer_stmts_when_needed(node.scope, true)
}
// Make sure to add our unpacks
if multi_unpack != '' {
Expand All @@ -6318,7 +6307,7 @@ fn (mut g Gen) return_stmt(node ast.Return) {
if !has_semicolon {
g.writeln(';')
}
g.write_defer_stmts_when_needed()
g.write_defer_stmts_when_needed(node.scope, true)
g.writeln('return ${tmpvar};')
has_semicolon = true
} else if fn_return_is_option || fn_return_is_result {
Expand Down Expand Up @@ -6381,7 +6370,7 @@ fn (mut g Gen) return_stmt(node ast.Return) {
}
g.writeln(' }, (${option_name}*)(&${tmpvar}), sizeof(${styp}));')
}
g.write_defer_stmts_when_needed()
g.write_defer_stmts_when_needed(node.scope, true)
if g.is_autofree {
g.detect_used_var_on_return(expr0)
}
Expand Down Expand Up @@ -6429,7 +6418,7 @@ fn (mut g Gen) return_stmt(node ast.Return) {
}
g.writeln(' }, (${result_name}*)(&${tmpvar}), sizeof(${styp}));')
}
g.write_defer_stmts_when_needed()
g.write_defer_stmts_when_needed(node.scope, true)
if g.is_autofree {
g.detect_used_var_on_return(expr0)
}
Expand Down Expand Up @@ -6539,7 +6528,7 @@ fn (mut g Gen) return_stmt(node ast.Return) {
if use_tmp_var {
g.writeln(';')
has_semicolon = true
g.write_defer_stmts_when_needed()
g.write_defer_stmts_when_needed(node.scope, true)
if !g.is_builtin_mod {
g.autofree_scope_vars(node.pos.pos - 1, node.pos.line_nr, true)
}
Expand Down Expand Up @@ -7419,7 +7408,9 @@ fn (mut g Gen) or_block(var_name string, or_block ast.OrExpr, return_type ast.Ty
// `opt() or { return err }`
// Since we *do* return, first we have to ensure that
// the deferred statements are generated.
g.write_defer_stmts()
if or_block.scope != unsafe { nil } {
g.write_defer_stmts(or_block.scope, true)
}
// Now that option types are distinct we need a cast here
if g.fn_decl == unsafe { nil } || g.fn_decl.return_type == ast.void_type {
g.writeln('\treturn;')
Expand Down Expand Up @@ -7453,7 +7444,9 @@ fn (mut g Gen) or_block(var_name string, or_block ast.OrExpr, return_type ast.Ty
// `opt() or { return err }`
// Since we *do* return, first we have to ensure that
// the deferred statements are generated.
g.write_defer_stmts()
if or_block.scope != unsafe { nil } {
g.write_defer_stmts(or_block.scope, true)
}
// Now that option types are distinct we need a cast here
if g.fn_decl == unsafe { nil } || g.fn_decl.return_type == ast.void_type {
g.writeln('\treturn;')
Expand Down
63 changes: 63 additions & 0 deletions vlib/v/gen/c/defer.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module c

import v.ast

fn (g &Gen) defer_flag_var(stmt &ast.DeferStmt) string {
return '${g.last_fn_c_name}_defer_${stmt.idx_in_fn}'
}

@[inline]
fn is_same_scope(a &ast.Scope, b &ast.Scope) bool {
return a.start_pos == b.start_pos && a.end_pos == b.end_pos
}

fn (mut g Gen) write_defer_stmts(scope &ast.Scope, lookup bool) {
g.indent++
for i := g.defer_stmts.len - 1; i >= 0; i-- {
defer_stmt := g.defer_stmts[i]
if g.pref.scoped_defer {
if !((lookup && defer_stmt.scope.start_pos < scope.start_pos
&& defer_stmt.scope.end_pos > scope.end_pos)
|| is_same_scope(defer_stmt.scope, scope)) {
// generate only `defer`s from the current scope
continue
}
g.writeln('{ // defer begin')
if defer_stmt.ifdef.len > 0 {
g.writeln(defer_stmt.ifdef)
g.stmts(defer_stmt.stmts)
g.writeln2('', '#endif')
} else {
g.stmts(defer_stmt.stmts)
}
g.writeln('} // defer end')
} else {
g.writeln('if (${g.defer_flag_var(defer_stmt)}) { // defer begin')
if defer_stmt.ifdef.len > 0 {
g.writeln(defer_stmt.ifdef)
g.stmts(defer_stmt.stmts)
g.writeln2('', '#endif')
} else {
g.stmts(defer_stmt.stmts)
}
g.writeln('} // defer end')
}
}
g.indent--
}

fn (mut g Gen) write_defer_stmts_when_needed(scope &ast.Scope, lookup bool) {
// unlock all mutexes, in case we are in a lock statement. defers are not
// allowed in lock statements.
g.unlock_locks()
if g.defer_stmts.len > 0 {
g.write_defer_stmts(scope, lookup)
}
if g.defer_profile_code.len > 0 {
g.writeln2('', '\t// defer_profile_code')
g.writeln2(g.defer_profile_code, '')
}
}
Loading
Loading