rewritten goto based exception handling; much cleaner implementation;… (#13677)
* rewritten goto based exception handling; much cleaner implementation; fixes #13668
This commit is contained in:
parent
b737bb4be0
commit
fe7b1dfb2a
4 changed files with 79 additions and 26 deletions
|
|
@ -1040,27 +1040,14 @@ proc genTryGoto(p: BProc; t: PNode; d: var TLoc) =
|
||||||
let lab = p.labels
|
let lab = p.labels
|
||||||
p.nestedTryStmts.add((fin, false, Natural lab))
|
p.nestedTryStmts.add((fin, false, Natural lab))
|
||||||
|
|
||||||
if not bodyCanRaise(p, t):
|
|
||||||
# optimize away the 'try' block:
|
|
||||||
expr(p, t[0], d)
|
|
||||||
linefmt(p, cpsStmts, "LA$1_: ;$n", [lab])
|
|
||||||
if fin != nil:
|
|
||||||
genStmts(p, fin[0])
|
|
||||||
discard pop(p.nestedTryStmts)
|
|
||||||
return
|
|
||||||
|
|
||||||
p.flags.incl nimErrorFlagAccessed
|
p.flags.incl nimErrorFlagAccessed
|
||||||
p.procSec(cpsLocals).add(ropecg(p.module, "NI oldNimErr$1_;$n", [lab]))
|
|
||||||
linefmt(p, cpsStmts, "oldNimErr$1_ = *nimErr_; *nimErr_ = 0;;$n", [lab])
|
|
||||||
|
|
||||||
expr(p, t[0], d)
|
expr(p, t[0], d)
|
||||||
|
|
||||||
if 1 < t.len and t[1].kind == nkExceptBranch:
|
if 1 < t.len and t[1].kind == nkExceptBranch:
|
||||||
startBlock(p, "if (NIM_UNLIKELY(*nimErr_)) {$n")
|
startBlock(p, "if (NIM_UNLIKELY(*nimErr_)) {$n")
|
||||||
else:
|
else:
|
||||||
startBlock(p)
|
startBlock(p)
|
||||||
# pretend we did handle the error for the safe execution of the sections:
|
linefmt(p, cpsStmts, "LA$1_:;$n", [lab])
|
||||||
linefmt(p, cpsStmts, "LA$1_: oldNimErr$1_ = *nimErr_; *nimErr_ = 0;$n", [lab])
|
|
||||||
|
|
||||||
p.nestedTryStmts[^1].inExcept = true
|
p.nestedTryStmts[^1].inExcept = true
|
||||||
var i = 1
|
var i = 1
|
||||||
|
|
@ -1077,7 +1064,7 @@ proc genTryGoto(p: BProc; t: PNode; d: var TLoc) =
|
||||||
if i > 1: lineF(p, cpsStmts, "else", [])
|
if i > 1: lineF(p, cpsStmts, "else", [])
|
||||||
startBlock(p)
|
startBlock(p)
|
||||||
# we handled the exception, remember this:
|
# we handled the exception, remember this:
|
||||||
linefmt(p, cpsStmts, "--oldNimErr$1_;$n", [lab])
|
linefmt(p, cpsStmts, "*nimErr_ = NIM_FALSE;$n", [])
|
||||||
expr(p, t[i][0], d)
|
expr(p, t[i][0], d)
|
||||||
else:
|
else:
|
||||||
var orExpr: Rope = nil
|
var orExpr: Rope = nil
|
||||||
|
|
@ -1094,7 +1081,7 @@ proc genTryGoto(p: BProc; t: PNode; d: var TLoc) =
|
||||||
if i > 1: line(p, cpsStmts, "else ")
|
if i > 1: line(p, cpsStmts, "else ")
|
||||||
startBlock(p, "if ($1) {$n", [orExpr])
|
startBlock(p, "if ($1) {$n", [orExpr])
|
||||||
# we handled the exception, remember this:
|
# we handled the exception, remember this:
|
||||||
linefmt(p, cpsStmts, "--oldNimErr$1_;$n", [lab])
|
linefmt(p, cpsStmts, "*nimErr_ = NIM_FALSE;$n", [])
|
||||||
expr(p, t[i][^1], d)
|
expr(p, t[i][^1], d)
|
||||||
|
|
||||||
linefmt(p, cpsStmts, "#popCurrentException();$n", [])
|
linefmt(p, cpsStmts, "#popCurrentException();$n", [])
|
||||||
|
|
@ -1105,7 +1092,6 @@ proc genTryGoto(p: BProc; t: PNode; d: var TLoc) =
|
||||||
discard pop(p.nestedTryStmts)
|
discard pop(p.nestedTryStmts)
|
||||||
endBlock(p)
|
endBlock(p)
|
||||||
|
|
||||||
#linefmt(p, cpsStmts, "LA$1_:;$n", [lab+1])
|
|
||||||
if i < t.len and t[i].kind == nkFinally:
|
if i < t.len and t[i].kind == nkFinally:
|
||||||
startBlock(p)
|
startBlock(p)
|
||||||
if not bodyCanRaise(p, t[i][0]):
|
if not bodyCanRaise(p, t[i][0]):
|
||||||
|
|
@ -1114,8 +1100,8 @@ proc genTryGoto(p: BProc; t: PNode; d: var TLoc) =
|
||||||
genStmts(p, t[i][0])
|
genStmts(p, t[i][0])
|
||||||
else:
|
else:
|
||||||
# pretend we did handle the error for the safe execution of the 'finally' section:
|
# pretend we did handle the error for the safe execution of the 'finally' section:
|
||||||
p.procSec(cpsLocals).add(ropecg(p.module, "NI oldNimErrFin$1_;$n", [lab]))
|
p.procSec(cpsLocals).add(ropecg(p.module, "NIM_BOOL oldNimErrFin$1_;$n", [lab]))
|
||||||
linefmt(p, cpsStmts, "oldNimErrFin$1_ = *nimErr_; *nimErr_ = 0;$n", [lab])
|
linefmt(p, cpsStmts, "oldNimErrFin$1_ = *nimErr_; *nimErr_ = NIM_FALSE;$n", [lab])
|
||||||
genStmts(p, t[i][0])
|
genStmts(p, t[i][0])
|
||||||
# this is correct for all these cases:
|
# this is correct for all these cases:
|
||||||
# 1. finally is run during ordinary control flow
|
# 1. finally is run during ordinary control flow
|
||||||
|
|
@ -1123,10 +1109,8 @@ proc genTryGoto(p: BProc; t: PNode; d: var TLoc) =
|
||||||
# error back to nil.
|
# error back to nil.
|
||||||
# 3. finally is run for exception handling code without any 'except'
|
# 3. finally is run for exception handling code without any 'except'
|
||||||
# handler present or only handlers that did not match.
|
# handler present or only handlers that did not match.
|
||||||
linefmt(p, cpsStmts, "*nimErr_ += oldNimErr$1_ + (*nimErr_ - oldNimErrFin$1_); oldNimErr$1_ = 0;$n", [lab])
|
linefmt(p, cpsStmts, "*nimErr_ = oldNimErrFin$1_;$n", [lab])
|
||||||
endBlock(p)
|
endBlock(p)
|
||||||
# restore the real error value:
|
|
||||||
linefmt(p, cpsStmts, "*nimErr_ += oldNimErr$1_;$n", [lab])
|
|
||||||
if p.prc != nil: raiseExit(p)
|
if p.prc != nil: raiseExit(p)
|
||||||
|
|
||||||
proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
|
proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
|
||||||
|
|
|
||||||
|
|
@ -975,7 +975,7 @@ proc genProcBody(p: BProc; procBody: PNode) =
|
||||||
genStmts(p, procBody) # modifies p.locals, p.init, etc.
|
genStmts(p, procBody) # modifies p.locals, p.init, etc.
|
||||||
if {nimErrorFlagAccessed, nimErrorFlagDeclared} * p.flags == {nimErrorFlagAccessed}:
|
if {nimErrorFlagAccessed, nimErrorFlagDeclared} * p.flags == {nimErrorFlagAccessed}:
|
||||||
p.flags.incl nimErrorFlagDeclared
|
p.flags.incl nimErrorFlagDeclared
|
||||||
p.blocks[0].sections[cpsLocals].add(ropecg(p.module, "NI* nimErr_;$n", []))
|
p.blocks[0].sections[cpsLocals].add(ropecg(p.module, "NIM_BOOL* nimErr_;$n", []))
|
||||||
p.blocks[0].sections[cpsInit].add(ropecg(p.module, "nimErr_ = #nimErrorFlag();$n", []))
|
p.blocks[0].sections[cpsInit].add(ropecg(p.module, "nimErr_ = #nimErrorFlag();$n", []))
|
||||||
|
|
||||||
proc genProcAux(m: BModule, prc: PSym) =
|
proc genProcAux(m: BModule, prc: PSym) =
|
||||||
|
|
|
||||||
|
|
@ -420,16 +420,16 @@ proc nimLeaveFinally() {.compilerRtl.} =
|
||||||
quit(1)
|
quit(1)
|
||||||
|
|
||||||
when gotoBasedExceptions:
|
when gotoBasedExceptions:
|
||||||
var nimInErrorMode {.threadvar.}: int
|
var nimInErrorMode {.threadvar.}: bool
|
||||||
|
|
||||||
proc nimErrorFlag(): ptr int {.compilerRtl, inl.} =
|
proc nimErrorFlag(): ptr bool {.compilerRtl, inl.} =
|
||||||
result = addr(nimInErrorMode)
|
result = addr(nimInErrorMode)
|
||||||
|
|
||||||
proc nimTestErrorFlag() {.compilerRtl.} =
|
proc nimTestErrorFlag() {.compilerRtl.} =
|
||||||
## This proc must be called before ``currException`` is destroyed.
|
## This proc must be called before ``currException`` is destroyed.
|
||||||
## It also must be called at the end of every thread to ensure no
|
## It also must be called at the end of every thread to ensure no
|
||||||
## error is swallowed.
|
## error is swallowed.
|
||||||
if currException != nil:
|
if nimInErrorMode and currException != nil:
|
||||||
reportUnhandledError(currException)
|
reportUnhandledError(currException)
|
||||||
currException = nil
|
currException = nil
|
||||||
quit(1)
|
quit(1)
|
||||||
|
|
|
||||||
69
tests/destructor/tgotoexceptions8.nim
Normal file
69
tests/destructor/tgotoexceptions8.nim
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
discard """
|
||||||
|
output: '''A
|
||||||
|
B
|
||||||
|
X
|
||||||
|
inner finally
|
||||||
|
Y
|
||||||
|
outer finally
|
||||||
|
msg1
|
||||||
|
msg2
|
||||||
|
finally2
|
||||||
|
finally1'''
|
||||||
|
cmd: "nim c --gc:arc $file"
|
||||||
|
"""
|
||||||
|
|
||||||
|
# bug #13668
|
||||||
|
|
||||||
|
proc main =
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
raise newException(IOError, "IOError")
|
||||||
|
|
||||||
|
except:
|
||||||
|
echo "A"
|
||||||
|
raise newException(CatchableError, "CatchableError")
|
||||||
|
|
||||||
|
except:
|
||||||
|
echo "B"
|
||||||
|
#discard
|
||||||
|
|
||||||
|
proc mainB =
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
raise newException(IOError, "IOError")
|
||||||
|
|
||||||
|
except:
|
||||||
|
echo "X"
|
||||||
|
raise newException(CatchableError, "CatchableError")
|
||||||
|
finally:
|
||||||
|
echo "inner finally"
|
||||||
|
|
||||||
|
except:
|
||||||
|
echo "Y"
|
||||||
|
#discard
|
||||||
|
finally:
|
||||||
|
echo "outer finally"
|
||||||
|
|
||||||
|
main()
|
||||||
|
mainB()
|
||||||
|
|
||||||
|
when true:
|
||||||
|
#bug 7204
|
||||||
|
proc nested_finally =
|
||||||
|
try:
|
||||||
|
raise newException(KeyError, "msg1")
|
||||||
|
except KeyError as ex:
|
||||||
|
echo ex.msg
|
||||||
|
try:
|
||||||
|
# pop exception
|
||||||
|
raise newException(ValueError, "msg2") # push: exception stack (1 entry)
|
||||||
|
except:
|
||||||
|
echo getCurrentExceptionMsg()
|
||||||
|
# pop exception (except)
|
||||||
|
finally:
|
||||||
|
echo "finally2"
|
||||||
|
# pop exception (except KeyError as ex)
|
||||||
|
finally:
|
||||||
|
echo "finally1"
|
||||||
|
|
||||||
|
nested_finally()
|
||||||
Loading…
Add table
Add a link
Reference in a new issue