parent
427490a845
commit
16c1a90857
12 changed files with 267 additions and 42 deletions
|
|
@ -37,6 +37,10 @@
|
||||||
the use of `static[T]` types.
|
the use of `static[T]` types.
|
||||||
(#6415)
|
(#6415)
|
||||||
|
|
||||||
|
- Native C++ exceptions can now be imported with `importcpp` pragma.
|
||||||
|
Imported exceptions can be raised and caught just like Nim exceptions.
|
||||||
|
More details in language manual.
|
||||||
|
|
||||||
### Tool changes
|
### Tool changes
|
||||||
|
|
||||||
- ``jsondoc2`` has been renamed ``jsondoc``, similar to how ``doc2`` was renamed
|
- ``jsondoc2`` has been renamed ``jsondoc``, similar to how ``doc2`` was renamed
|
||||||
|
|
|
||||||
|
|
@ -1674,6 +1674,19 @@ proc isException*(t: PType): bool =
|
||||||
base = base.lastSon
|
base = base.lastSon
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
proc isImportedException*(t: PType): bool =
|
||||||
|
assert(t != nil)
|
||||||
|
if optNoCppExceptions in gGlobalOptions:
|
||||||
|
return false
|
||||||
|
|
||||||
|
let base = t.skipTypes({tyAlias, tyPtr, tyDistinct, tyGenericInst})
|
||||||
|
|
||||||
|
if base.sym != nil and sfCompileToCpp in base.sym.flags:
|
||||||
|
result = true
|
||||||
|
|
||||||
|
proc isInfixAs*(n: PNode): bool =
|
||||||
|
return n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.id == getIdent("as").id
|
||||||
|
|
||||||
proc findUnresolvedStatic*(n: PNode): PNode =
|
proc findUnresolvedStatic*(n: PNode): PNode =
|
||||||
if n.kind == nkSym and n.typ.kind == tyStatic and n.typ.n == nil:
|
if n.kind == nkSym and n.typ.kind == tyStatic and n.typ.n == nil:
|
||||||
return n
|
return n
|
||||||
|
|
|
||||||
|
|
@ -577,13 +577,16 @@ proc genRaiseStmt(p: BProc, t: PNode) =
|
||||||
# we must execute it before reraising
|
# we must execute it before reraising
|
||||||
var finallyBlock = p.nestedTryStmts[^1].n[^1]
|
var finallyBlock = p.nestedTryStmts[^1].n[^1]
|
||||||
if finallyBlock.kind == nkFinally:
|
if finallyBlock.kind == nkFinally:
|
||||||
genSimpleBlock(p, finallyBlock.sons[0])
|
genSimpleBlock(p, finallyBlock[0])
|
||||||
if t.sons[0].kind != nkEmpty:
|
if t[0].kind != nkEmpty:
|
||||||
var a: TLoc
|
var a: TLoc
|
||||||
initLocExpr(p, t.sons[0], a)
|
initLocExprSingleUse(p, t[0], a)
|
||||||
var e = rdLoc(a)
|
var e = rdLoc(a)
|
||||||
var typ = skipTypes(t.sons[0].typ, abstractPtrs)
|
var typ = skipTypes(t[0].typ, abstractPtrs)
|
||||||
genLineDir(p, t)
|
genLineDir(p, t)
|
||||||
|
if isImportedException(typ):
|
||||||
|
lineF(p, cpsStmts, "throw $1;$n", [e])
|
||||||
|
else:
|
||||||
lineCg(p, cpsStmts, "#raiseException((#Exception*)$1, $2);$n",
|
lineCg(p, cpsStmts, "#raiseException((#Exception*)$1, $2);$n",
|
||||||
[e, makeCString(typ.sym.name.s)])
|
[e, makeCString(typ.sym.name.s)])
|
||||||
else:
|
else:
|
||||||
|
|
@ -804,14 +807,11 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
|
||||||
if optStackTrace in p.options:
|
if optStackTrace in p.options:
|
||||||
linefmt(p, cpsStmts, "#setFrame((TFrame*)&FR_);$n")
|
linefmt(p, cpsStmts, "#setFrame((TFrame*)&FR_);$n")
|
||||||
expr(p, body, d)
|
expr(p, body, d)
|
||||||
linefmt(p, cpsStmts, "#popCurrentException();$n")
|
|
||||||
|
|
||||||
if not isEmptyType(t.typ) and d.k == locNone:
|
if not isEmptyType(t.typ) and d.k == locNone:
|
||||||
getTemp(p, t.typ, d)
|
getTemp(p, t.typ, d)
|
||||||
genLineDir(p, t)
|
genLineDir(p, t)
|
||||||
|
discard cgsym(p.module, "popCurrentExceptionEx")
|
||||||
let end_label = getLabel(p)
|
|
||||||
discard cgsym(p.module, "Exception")
|
|
||||||
add(p.nestedTryStmts, (t, false))
|
add(p.nestedTryStmts, (t, false))
|
||||||
startBlock(p, "try {$n")
|
startBlock(p, "try {$n")
|
||||||
expr(p, t[0], d)
|
expr(p, t[0], d)
|
||||||
|
|
@ -834,8 +834,12 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
|
||||||
endBlock(p)
|
endBlock(p)
|
||||||
else:
|
else:
|
||||||
for j in 0..t[i].len-2:
|
for j in 0..t[i].len-2:
|
||||||
assert(t[i][j].kind == nkType)
|
if t[i][j].isInfixAs():
|
||||||
startBlock(p, "catch ($1*) {$n", getTypeDesc(p.module, t[i][j].typ))
|
let exvar = t[i][j][2] # ex1 in `except ExceptType as ex1:`
|
||||||
|
fillLoc(exvar.sym.loc, locTemp, exvar, mangleLocalName(p, exvar.sym), OnUnknown)
|
||||||
|
startBlock(p, "catch ($1& $2) {$n", getTypeDesc(p.module, t[i][j][1].typ), rdLoc(exvar.sym.loc))
|
||||||
|
else:
|
||||||
|
startBlock(p, "catch ($1&) {$n", getTypeDesc(p.module, t[i][j].typ))
|
||||||
genExceptBranchBody(t[i][^1]) # exception handler body will duplicated for every type
|
genExceptBranchBody(t[i][^1]) # exception handler body will duplicated for every type
|
||||||
endBlock(p)
|
endBlock(p)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@
|
||||||
import sighashes
|
import sighashes
|
||||||
from lowerings import createObj
|
from lowerings import createObj
|
||||||
|
|
||||||
|
proc genProcHeader(m: BModule, prc: PSym): Rope
|
||||||
|
|
||||||
proc isKeyword(w: PIdent): bool =
|
proc isKeyword(w: PIdent): bool =
|
||||||
# Nim and C++ share some keywords
|
# Nim and C++ share some keywords
|
||||||
# it's more efficient to test the whole Nim keywords range
|
# it's more efficient to test the whole Nim keywords range
|
||||||
|
|
@ -573,7 +575,15 @@ proc getRecordDesc(m: BModule, typ: PType, name: Rope,
|
||||||
appcg(m, result, " : public $1 {$n",
|
appcg(m, result, " : public $1 {$n",
|
||||||
[getTypeDescAux(m, typ.sons[0].skipTypes(skipPtrs), check)])
|
[getTypeDescAux(m, typ.sons[0].skipTypes(skipPtrs), check)])
|
||||||
if typ.isException:
|
if typ.isException:
|
||||||
appcg(m, result, "virtual void raise() {throw this;}$n") # required for polymorphic exceptions
|
appcg(m, result, "virtual void raise() {throw *this;}$n") # required for polymorphic exceptions
|
||||||
|
if typ.sym.magic == mException:
|
||||||
|
# Add cleanup destructor to Exception base class
|
||||||
|
appcg(m, result, "~$1() {if(this->raise_id) popCurrentExceptionEx(this->raise_id);}$n", [name])
|
||||||
|
# hack: forward declare popCurrentExceptionEx() on top of type description,
|
||||||
|
# proper request to generate popCurrentExceptionEx not possible for 2 reasons:
|
||||||
|
# generated function will be below declared Exception type and circular dependency
|
||||||
|
# between Exception and popCurrentExceptionEx function
|
||||||
|
result = genProcHeader(m, magicsys.getCompilerProc("popCurrentExceptionEx")) & ";" & rnl & result
|
||||||
hasField = true
|
hasField = true
|
||||||
else:
|
else:
|
||||||
appcg(m, result, " {$n $1 Sup;$n",
|
appcg(m, result, " {$n $1 Sup;$n",
|
||||||
|
|
|
||||||
|
|
@ -456,5 +456,3 @@ proc pickSym*(c: PContext, n: PNode; kinds: set[TSymKind];
|
||||||
else: return nil # ambiguous
|
else: return nil # ambiguous
|
||||||
a = nextOverloadIter(o, c, n)
|
a = nextOverloadIter(o, c, n)
|
||||||
|
|
||||||
proc isInfixAs*(n: PNode): bool =
|
|
||||||
return n.kind == nkInfix and considerQuotedIdent(n[0]).s == "as"
|
|
||||||
|
|
|
||||||
|
|
@ -359,9 +359,12 @@ proc trackTryStmt(tracked: PEffects, n: PNode) =
|
||||||
catchesAll(tracked)
|
catchesAll(tracked)
|
||||||
else:
|
else:
|
||||||
for j in countup(0, blen - 2):
|
for j in countup(0, blen - 2):
|
||||||
|
if b.sons[j].isInfixAs():
|
||||||
|
assert(b.sons[j][1].kind == nkType)
|
||||||
|
catches(tracked, b.sons[j][1].typ)
|
||||||
|
else:
|
||||||
assert(b.sons[j].kind == nkType)
|
assert(b.sons[j].kind == nkType)
|
||||||
catches(tracked, b.sons[j].typ)
|
catches(tracked, b.sons[j].typ)
|
||||||
|
|
||||||
setLen(tracked.init, oldState)
|
setLen(tracked.init, oldState)
|
||||||
track(tracked, b.sons[blen-1])
|
track(tracked, b.sons[blen-1])
|
||||||
for i in oldState..<tracked.init.len:
|
for i in oldState..<tracked.init.len:
|
||||||
|
|
|
||||||
|
|
@ -257,13 +257,19 @@ proc semCase(c: PContext, n: PNode): PNode =
|
||||||
proc semTry(c: PContext, n: PNode): PNode =
|
proc semTry(c: PContext, n: PNode): PNode =
|
||||||
|
|
||||||
var check = initIntSet()
|
var check = initIntSet()
|
||||||
template semExceptBranchType(typeNode: PNode): PNode =
|
template semExceptBranchType(typeNode: PNode): bool =
|
||||||
|
# returns true if exception type is imported type
|
||||||
let typ = semTypeNode(c, typeNode, nil).toObject()
|
let typ = semTypeNode(c, typeNode, nil).toObject()
|
||||||
if typ.kind != tyObject:
|
var is_imported = false
|
||||||
|
if isImportedException(typ):
|
||||||
|
is_imported = true
|
||||||
|
elif not isException(typ):
|
||||||
localError(typeNode.info, errExprCannotBeRaised)
|
localError(typeNode.info, errExprCannotBeRaised)
|
||||||
|
|
||||||
if containsOrIncl(check, typ.id):
|
if containsOrIncl(check, typ.id):
|
||||||
localError(typeNode.info, errExceptionAlreadyHandled)
|
localError(typeNode.info, errExceptionAlreadyHandled)
|
||||||
newNodeIT(nkType, typeNode.info, typ)
|
typeNode = newNodeIT(nkType, typeNode.info, typ)
|
||||||
|
is_imported
|
||||||
|
|
||||||
result = n
|
result = n
|
||||||
inc c.p.inTryStmt
|
inc c.p.inTryStmt
|
||||||
|
|
@ -286,18 +292,24 @@ proc semTry(c: PContext, n: PNode): PNode =
|
||||||
|
|
||||||
if a.len == 2 and a[0].isInfixAs():
|
if a.len == 2 and a[0].isInfixAs():
|
||||||
# support ``except Exception as ex: body``
|
# support ``except Exception as ex: body``
|
||||||
a[0][1] = semExceptBranchType(a[0][1])
|
let is_imported = semExceptBranchType(a[0][1])
|
||||||
|
|
||||||
let symbol = newSymG(skLet, a[0][2], c)
|
let symbol = newSymG(skLet, a[0][2], c)
|
||||||
symbol.typ = a[0][1].typ.toRef()
|
symbol.typ = if is_imported: a[0][1].typ
|
||||||
|
else: a[0][1].typ.toRef()
|
||||||
addDecl(c, symbol)
|
addDecl(c, symbol)
|
||||||
# Overwrite symbol in AST with the symbol in the symbol table.
|
# Overwrite symbol in AST with the symbol in the symbol table.
|
||||||
a[0][2] = newSymNode(symbol, a[0][2].info)
|
a[0][2] = newSymNode(symbol, a[0][2].info)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# support ``except KeyError, ValueError, ... : body``
|
# support ``except KeyError, ValueError, ... : body``
|
||||||
|
var is_native, is_imported: bool
|
||||||
for j in 0..a.len-2:
|
for j in 0..a.len-2:
|
||||||
a[j] = semExceptBranchType(a[j])
|
let tmp = semExceptBranchType(a[j])
|
||||||
|
if tmp: is_imported = true
|
||||||
|
else: is_native = true
|
||||||
|
|
||||||
|
if is_native and is_imported:
|
||||||
|
localError(a[0].info, "Mix of imported and native exception types is not allowed in one except branch")
|
||||||
|
|
||||||
elif a.kind != nkFinally:
|
elif a.kind != nkFinally:
|
||||||
illFormedAst(n)
|
illFormedAst(n)
|
||||||
|
|
@ -731,14 +743,17 @@ proc semFor(c: PContext, n: PNode): PNode =
|
||||||
proc semRaise(c: PContext, n: PNode): PNode =
|
proc semRaise(c: PContext, n: PNode): PNode =
|
||||||
result = n
|
result = n
|
||||||
checkSonsLen(n, 1)
|
checkSonsLen(n, 1)
|
||||||
if n.sons[0].kind != nkEmpty:
|
if n[0].kind != nkEmpty:
|
||||||
n.sons[0] = semExprWithType(c, n.sons[0])
|
|
||||||
var typ = n.sons[0].typ
|
n[0] = semExprWithType(c, n[0])
|
||||||
|
let typ = n[0].typ
|
||||||
|
|
||||||
|
if not isImportedException(typ):
|
||||||
|
|
||||||
if typ.kind != tyRef or typ.lastSon.kind != tyObject:
|
if typ.kind != tyRef or typ.lastSon.kind != tyObject:
|
||||||
localError(n.info, errExprCannotBeRaised)
|
localError(n.info, errExprCannotBeRaised)
|
||||||
|
|
||||||
# check if the given object inherits from Exception
|
if not isException(typ.lastSon):
|
||||||
if not typ.lastSon.isException():
|
|
||||||
localError(n.info, "raised object of type $1 does not inherit from Exception",
|
localError(n.info, "raised object of type $1 does not inherit from Exception",
|
||||||
[typeToString(typ)])
|
[typeToString(typ)])
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -714,7 +714,7 @@ proc transformCall(c: PTransf, n: PNode): PTransNode =
|
||||||
|
|
||||||
proc transformExceptBranch(c: PTransf, n: PNode): PTransNode =
|
proc transformExceptBranch(c: PTransf, n: PNode): PTransNode =
|
||||||
result = transformSons(c, n)
|
result = transformSons(c, n)
|
||||||
if n[0].isInfixAs():
|
if n[0].isInfixAs() and (not isImportedException(n[0][1].typ)):
|
||||||
let excTypeNode = n[0][1]
|
let excTypeNode = n[0][1]
|
||||||
let actions = newTransNode(nkStmtListExpr, n[1], 2)
|
let actions = newTransNode(nkStmtListExpr, n[1], 2)
|
||||||
# Generating `let exc = (excType)(getCurrentException())`
|
# Generating `let exc = (excType)(getCurrentException())`
|
||||||
|
|
|
||||||
|
|
@ -156,3 +156,25 @@ Exception hierarchy
|
||||||
The exception tree is defined in the `system <system.html>`_ module:
|
The exception tree is defined in the `system <system.html>`_ module:
|
||||||
|
|
||||||
.. include:: ../exception_hierarchy_fragment.txt
|
.. include:: ../exception_hierarchy_fragment.txt
|
||||||
|
|
||||||
|
|
||||||
|
Imported exceptions
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
It is possible to raise/catch imported C++ exceptions. Types imported using
|
||||||
|
`importcpp` can be raised or caught. Exceptions are raised by value and
|
||||||
|
caught by reference. Example:
|
||||||
|
|
||||||
|
.. code-block:: nim
|
||||||
|
|
||||||
|
type
|
||||||
|
std_exception {.importcpp: "std::exception", header: "<exception>".} = object
|
||||||
|
|
||||||
|
proc what(s: std_exception): cstring {.importcpp: "((char *)#.what())".}
|
||||||
|
|
||||||
|
try:
|
||||||
|
raise std_exception()
|
||||||
|
except std_exception as ex:
|
||||||
|
echo ex.what()
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -483,6 +483,7 @@ type
|
||||||
trace: string
|
trace: string
|
||||||
else:
|
else:
|
||||||
trace: seq[StackTraceEntry]
|
trace: seq[StackTraceEntry]
|
||||||
|
raise_id: uint # set when exception is raised
|
||||||
up: ref Exception # used for stacking exceptions. Not exported!
|
up: ref Exception # used for stacking exceptions. Not exported!
|
||||||
|
|
||||||
SystemError* = object of Exception ## \
|
SystemError* = object of Exception ## \
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,12 @@ proc showErrorMessage(data: cstring) {.gcsafe.} =
|
||||||
else:
|
else:
|
||||||
writeToStdErr(data)
|
writeToStdErr(data)
|
||||||
|
|
||||||
|
proc quitOrDebug() {.inline.} =
|
||||||
|
when not defined(endb):
|
||||||
|
quit(1)
|
||||||
|
else:
|
||||||
|
endbStep() # call the debugger
|
||||||
|
|
||||||
proc chckIndx(i, a, b: int): int {.inline, compilerproc, benign.}
|
proc chckIndx(i, a, b: int): int {.inline, compilerproc, benign.}
|
||||||
proc chckRange(i, a, b: int): int {.inline, compilerproc, benign.}
|
proc chckRange(i, a, b: int): int {.inline, compilerproc, benign.}
|
||||||
proc chckRangeF(x, a, b: float): float {.inline, compilerproc, benign.}
|
proc chckRangeF(x, a, b: float): float {.inline, compilerproc, benign.}
|
||||||
|
|
@ -50,6 +56,8 @@ var
|
||||||
# list of exception handlers
|
# list of exception handlers
|
||||||
# a global variable for the root of all try blocks
|
# a global variable for the root of all try blocks
|
||||||
currException {.threadvar.}: ref Exception
|
currException {.threadvar.}: ref Exception
|
||||||
|
raise_counter {.threadvar.}: uint
|
||||||
|
|
||||||
gcFramePtr {.threadvar.}: GcFrame
|
gcFramePtr {.threadvar.}: GcFrame
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
@ -108,6 +116,21 @@ proc pushCurrentException(e: ref Exception) {.compilerRtl, inl.} =
|
||||||
proc popCurrentException {.compilerRtl, inl.} =
|
proc popCurrentException {.compilerRtl, inl.} =
|
||||||
currException = currException.up
|
currException = currException.up
|
||||||
|
|
||||||
|
proc popCurrentExceptionEx(id: uint) {.compilerRtl.} =
|
||||||
|
# in cpp backend exceptions can pop-up in the different order they were raised, example #5628
|
||||||
|
if currException.raise_id == id:
|
||||||
|
currException = currException.up
|
||||||
|
else:
|
||||||
|
var cur = currException.up
|
||||||
|
var prev = currException
|
||||||
|
while cur != nil and cur.raise_id != id:
|
||||||
|
prev = cur
|
||||||
|
cur = cur.up
|
||||||
|
if cur == nil:
|
||||||
|
showErrorMessage("popCurrentExceptionEx() exception was not found in the exception stack. Aborting...")
|
||||||
|
quitOrDebug()
|
||||||
|
prev.up = cur.up
|
||||||
|
|
||||||
# some platforms have native support for stack traces:
|
# some platforms have native support for stack traces:
|
||||||
const
|
const
|
||||||
nativeStackTraceSupported* = (defined(macosx) or defined(linux)) and
|
nativeStackTraceSupported* = (defined(macosx) or defined(linux)) and
|
||||||
|
|
@ -291,12 +314,6 @@ when hasSomeStackTrace:
|
||||||
else:
|
else:
|
||||||
proc stackTraceAvailable*(): bool = result = false
|
proc stackTraceAvailable*(): bool = result = false
|
||||||
|
|
||||||
proc quitOrDebug() {.inline.} =
|
|
||||||
when not defined(endb):
|
|
||||||
quit(1)
|
|
||||||
else:
|
|
||||||
endbStep() # call the debugger
|
|
||||||
|
|
||||||
var onUnhandledException*: (proc (errorMsg: string) {.
|
var onUnhandledException*: (proc (errorMsg: string) {.
|
||||||
nimcall.}) ## set this error \
|
nimcall.}) ## set this error \
|
||||||
## handler to override the existing behaviour on an unhandled exception.
|
## handler to override the existing behaviour on an unhandled exception.
|
||||||
|
|
@ -320,6 +337,10 @@ proc raiseExceptionAux(e: ref Exception) =
|
||||||
quitOrDebug()
|
quitOrDebug()
|
||||||
else:
|
else:
|
||||||
pushCurrentException(e)
|
pushCurrentException(e)
|
||||||
|
raise_counter.inc
|
||||||
|
if raise_counter == 0:
|
||||||
|
raise_counter.inc # skip zero at overflow
|
||||||
|
e.raise_id = raise_counter
|
||||||
{.emit: "`e`->raise();".}
|
{.emit: "`e`->raise();".}
|
||||||
else:
|
else:
|
||||||
if excHandler != nil:
|
if excHandler != nil:
|
||||||
|
|
|
||||||
134
tests/exception/tcpp_imported_exc.nim
Normal file
134
tests/exception/tcpp_imported_exc.nim
Normal file
|
|
@ -0,0 +1,134 @@
|
||||||
|
discard """
|
||||||
|
targets: "cpp"
|
||||||
|
output: '''caught as std::exception
|
||||||
|
expected
|
||||||
|
finally1
|
||||||
|
finally2
|
||||||
|
finally2
|
||||||
|
2
|
||||||
|
expected
|
||||||
|
finally 1
|
||||||
|
finally 2
|
||||||
|
expected
|
||||||
|
cpp exception caught
|
||||||
|
'''
|
||||||
|
"""
|
||||||
|
|
||||||
|
type
|
||||||
|
std_exception* {.importcpp: "std::exception", header: "<exception>".} = object
|
||||||
|
std_runtime_error* {.importcpp: "std::runtime_error", header: "<stdexcept>".} = object
|
||||||
|
std_string* {.importcpp: "std::string", header: "<string>".} = object
|
||||||
|
|
||||||
|
proc constructStdString(s: cstring): std_string {.importcpp: "std::string(@)", constructor, header: "<string>".}
|
||||||
|
|
||||||
|
proc constructRuntimeError(s: stdstring): std_runtime_error {.importcpp: "std::runtime_error(@)", constructor.}
|
||||||
|
|
||||||
|
proc what(ex: std_runtime_error): cstring {.importcpp: "((char *)#.what())".}
|
||||||
|
|
||||||
|
proc myexception =
|
||||||
|
raise constructRuntimeError(constructStdString("cpp_exception"))
|
||||||
|
|
||||||
|
try:
|
||||||
|
myexception() # raise std::runtime_error
|
||||||
|
except std_exception:
|
||||||
|
echo "caught as std::exception"
|
||||||
|
try:
|
||||||
|
raise constructStdString("x")
|
||||||
|
except std_exception:
|
||||||
|
echo "should not happen"
|
||||||
|
except:
|
||||||
|
echo "expected"
|
||||||
|
|
||||||
|
doAssert(getCurrentException() == nil)
|
||||||
|
|
||||||
|
proc earlyReturn =
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
myexception()
|
||||||
|
finally:
|
||||||
|
echo "finally1"
|
||||||
|
except:
|
||||||
|
return
|
||||||
|
finally:
|
||||||
|
echo "finally2"
|
||||||
|
|
||||||
|
earlyReturn()
|
||||||
|
doAssert(getCurrentException() == nil)
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
block blk1:
|
||||||
|
try:
|
||||||
|
raise newException(ValueError, "mmm")
|
||||||
|
except:
|
||||||
|
break blk1
|
||||||
|
except:
|
||||||
|
echo "should not happen"
|
||||||
|
finally:
|
||||||
|
echo "finally2"
|
||||||
|
|
||||||
|
doAssert(getCurrentException() == nil)
|
||||||
|
|
||||||
|
#--------------------------------------
|
||||||
|
|
||||||
|
# raise by pointer and also generic type
|
||||||
|
|
||||||
|
type
|
||||||
|
std_vector {.importcpp"std::vector", header"<vector>".} [T] = object
|
||||||
|
|
||||||
|
proc newVector[T](len: int): ptr std_vector[T] {.importcpp: "new std::vector<'1>(@)".}
|
||||||
|
proc deleteVector[T](v: ptr std_vector[T]) {.importcpp: "delete @; @ = NIM_NIL;".}
|
||||||
|
proc len[T](v: std_vector[T]): uint {.importcpp: "size".}
|
||||||
|
|
||||||
|
var v = newVector[int](2)
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
raise v
|
||||||
|
except ptr std_vector[int] as ex:
|
||||||
|
echo len(ex[])
|
||||||
|
raise newException(ValueError, "msg5")
|
||||||
|
except:
|
||||||
|
echo "should not happen"
|
||||||
|
finally:
|
||||||
|
deleteVector(v)
|
||||||
|
except:
|
||||||
|
echo "expected"
|
||||||
|
|
||||||
|
doAssert(v == nil)
|
||||||
|
doAssert(getCurrentException() == nil)
|
||||||
|
|
||||||
|
#--------------------------------------
|
||||||
|
|
||||||
|
# mix of Nim and imported exceptions
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
raise newException(KeyError, "msg1")
|
||||||
|
except KeyError:
|
||||||
|
raise newException(ValueError, "msg2")
|
||||||
|
except:
|
||||||
|
echo "should not happen"
|
||||||
|
finally:
|
||||||
|
echo "finally 1"
|
||||||
|
except:
|
||||||
|
doAssert(getCurrentExceptionMsg() == "msg2")
|
||||||
|
raise constructStdString("std::string")
|
||||||
|
finally:
|
||||||
|
echo "finally 2"
|
||||||
|
except:
|
||||||
|
echo "expected"
|
||||||
|
|
||||||
|
|
||||||
|
doAssert(getCurrentException() == nil)
|
||||||
|
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
myexception()
|
||||||
|
except std_runtime_error as ex:
|
||||||
|
echo "cpp exception caught"
|
||||||
|
raise newException(ValueError, "rewritten " & $ex.what())
|
||||||
|
except:
|
||||||
|
doAssert(getCurrentExceptionMsg() == "rewritten cpp_exception")
|
||||||
|
|
||||||
|
doAssert(getCurrentException() == nil)
|
||||||
Loading…
Add table
Add a link
Reference in a new issue