--exception:goto switch for deterministic exception handling (#12977)
This implements "deterministic" exception handling for Nim based on goto instead of setjmp. This means raising an exception is much cheaper than in C++'s table based implementations. Supports hard realtime systems. Default for --gc:arc and the C target because it's generally a good idea and arc is all about deterministic behavior. Note: This implies that fatal runtime traps are not catchable anymore! This needs to be documented.
This commit is contained in:
parent
8a63caca07
commit
c3344862b0
23 changed files with 640 additions and 183 deletions
117
tests/destructor/tgotoexceptions.nim
Executable file
117
tests/destructor/tgotoexceptions.nim
Executable file
|
|
@ -0,0 +1,117 @@
|
|||
discard """
|
||||
output: '''
|
||||
msg1
|
||||
msg2
|
||||
finally2
|
||||
finally1
|
||||
begin
|
||||
one iteration!
|
||||
caught!
|
||||
except1
|
||||
finally1
|
||||
caught! 2
|
||||
BEFORE
|
||||
FINALLY
|
||||
BEFORE
|
||||
EXCEPT
|
||||
FINALLY
|
||||
RECOVER
|
||||
BEFORE
|
||||
EXCEPT: IOError: hi
|
||||
FINALLY
|
||||
'''
|
||||
cmd: "nim c --gc:arc --exceptions:goto $file"
|
||||
"""
|
||||
|
||||
#bug 7204
|
||||
proc nested_finally =
|
||||
try:
|
||||
raise newException(KeyError, "msg1")
|
||||
except KeyError as ex:
|
||||
echo ex.msg
|
||||
try:
|
||||
raise newException(ValueError, "msg2")
|
||||
except:
|
||||
echo getCurrentExceptionMsg()
|
||||
finally:
|
||||
echo "finally2"
|
||||
finally:
|
||||
echo "finally1"
|
||||
|
||||
nested_finally()
|
||||
|
||||
proc doraise =
|
||||
raise newException(ValueError, "gah")
|
||||
|
||||
proc main =
|
||||
while true:
|
||||
try:
|
||||
echo "begin"
|
||||
doraise()
|
||||
finally:
|
||||
echo "one ", "iteration!"
|
||||
|
||||
try:
|
||||
main()
|
||||
except:
|
||||
echo "caught!"
|
||||
|
||||
when true:
|
||||
proc p =
|
||||
try:
|
||||
raise newException(Exception, "Hello")
|
||||
except:
|
||||
echo "except1"
|
||||
raise
|
||||
finally:
|
||||
echo "finally1"
|
||||
|
||||
try:
|
||||
p()
|
||||
except:
|
||||
echo "caught! 2"
|
||||
|
||||
|
||||
proc noException =
|
||||
try:
|
||||
echo "BEFORE"
|
||||
|
||||
except:
|
||||
echo "EXCEPT"
|
||||
raise
|
||||
|
||||
finally:
|
||||
echo "FINALLY"
|
||||
|
||||
try: noException()
|
||||
except: echo "RECOVER"
|
||||
|
||||
proc reraise_in_except =
|
||||
try:
|
||||
echo "BEFORE"
|
||||
raise newException(IOError, "")
|
||||
|
||||
except IOError:
|
||||
echo "EXCEPT"
|
||||
raise
|
||||
|
||||
finally:
|
||||
echo "FINALLY"
|
||||
|
||||
try: reraise_in_except()
|
||||
except: echo "RECOVER"
|
||||
|
||||
proc return_in_except =
|
||||
try:
|
||||
echo "BEFORE"
|
||||
raise newException(IOError, "hi")
|
||||
|
||||
except:
|
||||
echo "EXCEPT: ", getCurrentException().name, ": ", getCurrentExceptionMsg()
|
||||
return
|
||||
|
||||
finally:
|
||||
echo "FINALLY"
|
||||
|
||||
try: return_in_except()
|
||||
except: echo "RECOVER"
|
||||
104
tests/destructor/tgotoexceptions2.nim
Normal file
104
tests/destructor/tgotoexceptions2.nim
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
discard """
|
||||
cmd: "nim c --gc:arc --exceptions:goto $file"
|
||||
output: '''
|
||||
B1
|
||||
B2
|
||||
catch
|
||||
A1
|
||||
1
|
||||
B1
|
||||
B2
|
||||
catch
|
||||
A1
|
||||
A2
|
||||
0
|
||||
B1
|
||||
B2
|
||||
A1
|
||||
1
|
||||
B1
|
||||
B2
|
||||
A1
|
||||
A2
|
||||
3
|
||||
A
|
||||
B
|
||||
C
|
||||
'''
|
||||
"""
|
||||
|
||||
# More thorough test of return-in-finaly
|
||||
|
||||
var raiseEx = true
|
||||
var returnA = true
|
||||
var returnB = false
|
||||
|
||||
proc main: int =
|
||||
try: #A
|
||||
try: #B
|
||||
if raiseEx:
|
||||
raise newException(OSError, "")
|
||||
return 3
|
||||
finally: #B
|
||||
echo "B1"
|
||||
if returnB:
|
||||
return 2
|
||||
echo "B2"
|
||||
except OSError: #A
|
||||
echo "catch"
|
||||
finally: #A
|
||||
echo "A1"
|
||||
if returnA:
|
||||
return 1
|
||||
echo "A2"
|
||||
|
||||
for x in [true, false]:
|
||||
for y in [true, false]:
|
||||
# echo "raiseEx: " & $x
|
||||
# echo "returnA: " & $y
|
||||
# echo "returnB: " & $z
|
||||
# in the original test returnB was set to true too and
|
||||
# this leads to swallowing the OSError exception. This is
|
||||
# somewhat compatible with Python but it's non-sense, 'finally'
|
||||
# should not be allowed to swallow exceptions. The goto based
|
||||
# implementation does something sane so we don't "correct" its
|
||||
# behavior just to be compatible with v1.
|
||||
raiseEx = x
|
||||
returnA = y
|
||||
echo main()
|
||||
|
||||
# Various tests of return nested in double try/except statements
|
||||
|
||||
proc test1() =
|
||||
|
||||
defer: echo "A"
|
||||
|
||||
try:
|
||||
raise newException(OSError, "Problem")
|
||||
except OSError:
|
||||
return
|
||||
|
||||
test1()
|
||||
|
||||
|
||||
proc test2() =
|
||||
|
||||
defer: echo "B"
|
||||
|
||||
try:
|
||||
return
|
||||
except OSError:
|
||||
discard
|
||||
|
||||
test2()
|
||||
|
||||
proc test3() =
|
||||
try:
|
||||
try:
|
||||
raise newException(OSError, "Problem")
|
||||
except OSError:
|
||||
return
|
||||
finally:
|
||||
echo "C"
|
||||
|
||||
test3()
|
||||
7
tests/destructor/tgotoexceptions3.nim
Normal file
7
tests/destructor/tgotoexceptions3.nim
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
discard """
|
||||
cmd: "nim c --gc:arc --exceptions:goto $file"
|
||||
outputsub: "Error: unhandled exception: Problem [OSError]"
|
||||
exitcode: "1"
|
||||
"""
|
||||
|
||||
raise newException(OSError, "Problem")
|
||||
Loading…
Add table
Add a link
Reference in a new issue