bugfix: finally blocks were not executed when the except block is exited by raise or return

This commit is contained in:
Zahary Karadjov 2012-06-03 18:00:45 +03:00
commit 3ce400bb00
3 changed files with 87 additions and 13 deletions

View file

@ -205,15 +205,19 @@ proc blockLeaveActions(p: BProc, howMany: int) =
stack[i-1] = p.nestedTryStmts[L-i] stack[i-1] = p.nestedTryStmts[L-i]
setLen(p.nestedTryStmts, L-howMany) setLen(p.nestedTryStmts, L-howMany)
var alreadyPoppedCnt = p.inExceptBlock
for tryStmt in items(stack): for tryStmt in items(stack):
appcg(p, cpsStmts, "#popSafePoint();$n", []) if alreadyPoppedCnt > 0:
dec alreadyPoppedCnt
else:
appcg(p, cpsStmts, "#popSafePoint();$n", [])
var finallyStmt = lastSon(tryStmt) var finallyStmt = lastSon(tryStmt)
if finallyStmt.kind == nkFinally: if finallyStmt.kind == nkFinally:
genStmts(p, finallyStmt.sons[0]) genStmts(p, finallyStmt.sons[0])
# push old elements again: # push old elements again:
for i in countdown(howMany-1, 0): for i in countdown(howMany-1, 0):
p.nestedTryStmts.add(stack[i]) p.nestedTryStmts.add(stack[i])
for i in countdown(p.popCurrExc-1, 0): for i in countdown(p.inExceptBlock-1, 0):
appcg(p, cpsStmts, "#popCurrentException();$n", []) appcg(p, cpsStmts, "#popCurrentException();$n", [])
proc genReturnStmt(p: BProc, t: PNode) = proc genReturnStmt(p: BProc, t: PNode) =
@ -310,7 +314,13 @@ proc getRaiseFrmt(p: BProc): string =
#else: #else:
result = "#raiseException((#E_Base*)$1, $2);$n" result = "#raiseException((#E_Base*)$1, $2);$n"
proc genRaiseStmt(p: BProc, t: PNode) = proc genRaiseStmt(p: BProc, t: PNode) =
if p.inExceptBlock > 0:
# if the current try stmt have a finally block,
# we must execute it before reraising
var finallyBlock = p.nestedTryStmts[p.nestedTryStmts.len - 1].lastSon
if finallyBlock.kind == nkFinally:
genStmts(p, finallyBlock.sons[0])
if t.sons[0].kind != nkEmpty: if t.sons[0].kind != nkEmpty:
var a: TLoc var a: TLoc
InitLocExpr(p, t.sons[0], a) InitLocExpr(p, t.sons[0], a)
@ -628,7 +638,7 @@ proc genTryStmt(p: BProc, t: PNode) =
add(p.nestedTryStmts, t) add(p.nestedTryStmts, t)
genStmts(p, t.sons[0]) genStmts(p, t.sons[0])
endBlock(p, ropecg(p.module, "#popSafePoint();$n } else {$n#popSafePoint();$n")) endBlock(p, ropecg(p.module, "#popSafePoint();$n } else {$n#popSafePoint();$n"))
discard pop(p.nestedTryStmts) inc p.inExceptBlock
var i = 1 var i = 1
while (i < length) and (t.sons[i].kind == nkExceptBranch): while (i < length) and (t.sons[i].kind == nkExceptBranch):
var blen = sonsLen(t.sons[i]) var blen = sonsLen(t.sons[i])
@ -637,13 +647,10 @@ proc genTryStmt(p: BProc, t: PNode) =
if i > 1: appf(p.s(cpsStmts), "else") if i > 1: appf(p.s(cpsStmts), "else")
startBlock(p) startBlock(p)
appcg(p, cpsStmts, "$1.status = 0;$n", [safePoint]) appcg(p, cpsStmts, "$1.status = 0;$n", [safePoint])
inc p.popCurrExc
genStmts(p, t.sons[i].sons[0]) genStmts(p, t.sons[i].sons[0])
dec p.popCurrExc
appcg(p, cpsStmts, "#popCurrentException();$n", []) appcg(p, cpsStmts, "#popCurrentException();$n", [])
endBlock(p) endBlock(p)
else: else:
inc p.popCurrExc
var orExpr: PRope = nil var orExpr: PRope = nil
for j in countup(0, blen - 2): for j in countup(0, blen - 2):
assert(t.sons[i].sons[j].kind == nkType) assert(t.sons[i].sons[j].kind == nkType)
@ -655,9 +662,10 @@ proc genTryStmt(p: BProc, t: PNode) =
startBlock(p, "if ($1) {$n", [orExpr]) startBlock(p, "if ($1) {$n", [orExpr])
appcg(p, cpsStmts, "$1.status = 0;$n", [safePoint]) appcg(p, cpsStmts, "$1.status = 0;$n", [safePoint])
genStmts(p, t.sons[i].sons[blen-1]) genStmts(p, t.sons[i].sons[blen-1])
dec p.popCurrExc
endBlock(p, ropecg(p.module, "#popCurrentException();}$n")) endBlock(p, ropecg(p.module, "#popCurrentException();}$n"))
inc(i) inc(i)
dec p.inExceptBlock
discard pop(p.nestedTryStmts)
appf(p.s(cpsStmts), "}$n") # end of else block appf(p.s(cpsStmts), "}$n") # end of else block
if i < length and t.sons[i].kind == nkFinally: if i < length and t.sons[i].kind == nkFinally:
genSimpleBlock(p, t.sons[i].sons[0]) genSimpleBlock(p, t.sons[i].sons[0])
@ -820,9 +828,8 @@ proc genStmts(p: BProc, t: PNode) =
initLocExpr(p, t.sons[0], a) initLocExpr(p, t.sons[0], a)
of nkAsmStmt: genAsmStmt(p, t) of nkAsmStmt: genAsmStmt(p, t)
of nkTryStmt: of nkTryStmt:
#if gCmd == cmdCompileToCpp: genTryStmtCpp(p, t) if gCmd == cmdCompileToCpp: genTryStmtCpp(p, t)
#else: else: genTryStmt(p, t)
genTryStmt(p, t)
of nkRaiseStmt: genRaiseStmt(p, t) of nkRaiseStmt: genRaiseStmt(p, t)
of nkTypeSection: of nkTypeSection:
# we have to emit the type information for object types here to support # we have to emit the type information for object types here to support

View file

@ -62,8 +62,9 @@ type
ThreadVarAccessed*: bool # true if the proc already accessed some threadvar ThreadVarAccessed*: bool # true if the proc already accessed some threadvar
nestedTryStmts*: seq[PNode] # in how many nested try statements we are nestedTryStmts*: seq[PNode] # in how many nested try statements we are
# (the vars must be volatile then) # (the vars must be volatile then)
popCurrExc*: Natural # how often to emit 'popCurrentException()' inExceptBlock*: int # are we currently inside an except block?
# before 'break'|'return' # leaving such scopes by raise or by return must
# execute any applicable finally blocks
labels*: Natural # for generating unique labels in the C proc labels*: Natural # for generating unique labels in the C proc
blocks*: seq[TBlock] # nested blocks blocks*: seq[TBlock] # nested blocks
breakIdx*: int # the block that will be exited breakIdx*: int # the block that will be exited

66
tests/run/texceptions.nim Normal file
View file

@ -0,0 +1,66 @@
discard """
output: '''
BEFORE
FINALLY
BEFORE
EXCEPT
FINALLY
RECOVER
BEFORE
EXCEPT
FINALLY
'''
"""
echo ""
proc no_expcetion =
try:
echo "BEFORE"
except:
echo "EXCEPT"
raise
finally:
echo "FINALLY"
try: no_expcetion()
except: echo "RECOVER"
echo ""
proc reraise_in_except =
try:
echo "BEFORE"
raise newException(EIO, "")
except:
echo "EXCEPT"
raise
finally:
echo "FINALLY"
try: reraise_in_except()
except: echo "RECOVER"
echo ""
proc return_in_except =
try:
echo "BEFORE"
raise newException(EIO, "")
except:
echo "EXCEPT"
return
finally:
echo "FINALLY"
try: return_in_except()
except: echo "RECOVER"