--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
|
|
@ -1763,7 +1763,7 @@ proc toObject*(typ: PType): PType =
|
||||||
proc isImportedException*(t: PType; conf: ConfigRef): bool =
|
proc isImportedException*(t: PType; conf: ConfigRef): bool =
|
||||||
assert t != nil
|
assert t != nil
|
||||||
|
|
||||||
if optNoCppExceptions in conf.globalOptions:
|
if conf.exc != excCpp:
|
||||||
return false
|
return false
|
||||||
|
|
||||||
let base = t.skipTypes({tyAlias, tyPtr, tyDistinct, tyGenericInst})
|
let base = t.skipTypes({tyAlias, tyPtr, tyDistinct, tyGenericInst})
|
||||||
|
|
@ -1834,7 +1834,7 @@ template assignment*(t: PType): PSym = t.attachedOps[attachedAsgn]
|
||||||
template asink*(t: PType): PSym = t.attachedOps[attachedSink]
|
template asink*(t: PType): PSym = t.attachedOps[attachedSink]
|
||||||
|
|
||||||
const magicsThatCanRaise = {
|
const magicsThatCanRaise = {
|
||||||
mNone, mSlurp, mStaticExec, mParseExprToAst, mParseStmtToAst}
|
mNone, mSlurp, mStaticExec, mParseExprToAst, mParseStmtToAst, mEcho}
|
||||||
|
|
||||||
proc canRaiseConservative*(fn: PNode): bool =
|
proc canRaiseConservative*(fn: PNode): bool =
|
||||||
if fn.kind == nkSym and fn.sym.magic notin magicsThatCanRaise:
|
if fn.kind == nkSym and fn.sym.magic notin magicsThatCanRaise:
|
||||||
|
|
@ -1844,9 +1844,12 @@ proc canRaiseConservative*(fn: PNode): bool =
|
||||||
|
|
||||||
proc canRaise*(fn: PNode): bool =
|
proc canRaise*(fn: PNode): bool =
|
||||||
if fn.kind == nkSym and (fn.sym.magic notin magicsThatCanRaise or
|
if fn.kind == nkSym and (fn.sym.magic notin magicsThatCanRaise or
|
||||||
{sfImportc, sfInfixCall} * fn.sym.flags == {sfImportc}):
|
{sfImportc, sfInfixCall} * fn.sym.flags == {sfImportc} or
|
||||||
|
sfGeneratedOp in fn.sym.flags):
|
||||||
result = false
|
result = false
|
||||||
|
elif fn.kind == nkSym and fn.sym.magic == mEcho:
|
||||||
|
result = true
|
||||||
else:
|
else:
|
||||||
result = fn.typ != nil and ((fn.typ.n[0].len < effectListLen) or
|
result = fn.typ != nil and fn.typ.n != nil and ((fn.typ.n[0].len < effectListLen) or
|
||||||
(fn.typ.n[0][exceptionEffects] != nil and
|
(fn.typ.n[0][exceptionEffects] != nil and
|
||||||
fn.typ.n[0][exceptionEffects].safeLen > 0))
|
fn.typ.n[0][exceptionEffects].safeLen > 0))
|
||||||
|
|
|
||||||
|
|
@ -567,6 +567,8 @@ proc genCall(p: BProc, e: PNode, d: var TLoc) =
|
||||||
else:
|
else:
|
||||||
genPrefixCall(p, nil, e, d)
|
genPrefixCall(p, nil, e, d)
|
||||||
postStmtActions(p)
|
postStmtActions(p)
|
||||||
|
if p.config.exc == excGoto and canRaise(e[0]):
|
||||||
|
raiseExit(p)
|
||||||
|
|
||||||
proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||||
if ri[0].typ.skipTypes({tyGenericInst, tyAlias, tySink, tyOwned}).callConv == ccClosure:
|
if ri[0].typ.skipTypes({tyGenericInst, tyAlias, tySink, tyOwned}).callConv == ccClosure:
|
||||||
|
|
@ -578,3 +580,5 @@ proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||||
else:
|
else:
|
||||||
genPrefixCall(p, le, ri, d)
|
genPrefixCall(p, le, ri, d)
|
||||||
postStmtActions(p)
|
postStmtActions(p)
|
||||||
|
if p.config.exc == excGoto and canRaise(ri[0]):
|
||||||
|
raiseExit(p)
|
||||||
|
|
|
||||||
|
|
@ -2675,10 +2675,13 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
|
||||||
line(p, cpsStmts, "(void)(" & a.r & ");\L")
|
line(p, cpsStmts, "(void)(" & a.r & ");\L")
|
||||||
of nkAsmStmt: genAsmStmt(p, n)
|
of nkAsmStmt: genAsmStmt(p, n)
|
||||||
of nkTryStmt, nkHiddenTryStmt:
|
of nkTryStmt, nkHiddenTryStmt:
|
||||||
if p.module.compileToCpp and optNoCppExceptions notin p.config.globalOptions:
|
case p.config.exc
|
||||||
|
of excGoto:
|
||||||
|
genTryGoto(p, n, d)
|
||||||
|
of excCpp:
|
||||||
genTryCpp(p, n, d)
|
genTryCpp(p, n, d)
|
||||||
else:
|
else:
|
||||||
genTry(p, n, d)
|
genTrySetjmp(p, n, d)
|
||||||
of nkRaiseStmt: genRaiseStmt(p, n)
|
of nkRaiseStmt: genRaiseStmt(p, n)
|
||||||
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
|
||||||
|
|
|
||||||
|
|
@ -196,13 +196,13 @@ proc genState(p: BProc, n: PNode) =
|
||||||
|
|
||||||
proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
|
proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
|
||||||
# Called by return and break stmts.
|
# Called by return and break stmts.
|
||||||
# Deals with issues faced when jumping out of try/except/finally stmts,
|
# Deals with issues faced when jumping out of try/except/finally stmts.
|
||||||
|
|
||||||
var stack = newSeq[tuple[fin: PNode, inExcept: bool]](0)
|
var stack = newSeq[tuple[fin: PNode, inExcept: bool, label: Natural]](0)
|
||||||
|
|
||||||
for i in 1..howManyTrys:
|
for i in 1..howManyTrys:
|
||||||
let tryStmt = p.nestedTryStmts.pop
|
let tryStmt = p.nestedTryStmts.pop
|
||||||
if not p.module.compileToCpp or optNoCppExceptions in p.config.globalOptions:
|
if p.config.exc == excSetjmp:
|
||||||
# Pop safe points generated by try
|
# Pop safe points generated by try
|
||||||
if not tryStmt.inExcept:
|
if not tryStmt.inExcept:
|
||||||
linefmt(p, cpsStmts, "#popSafePoint();$n", [])
|
linefmt(p, cpsStmts, "#popSafePoint();$n", [])
|
||||||
|
|
@ -221,10 +221,10 @@ proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
|
||||||
for i in countdown(howManyTrys-1, 0):
|
for i in countdown(howManyTrys-1, 0):
|
||||||
p.nestedTryStmts.add(stack[i])
|
p.nestedTryStmts.add(stack[i])
|
||||||
|
|
||||||
if not p.module.compileToCpp or optNoCppExceptions in p.config.globalOptions:
|
if p.config.exc != excCpp:
|
||||||
# Pop exceptions that was handled by the
|
# Pop exceptions that was handled by the
|
||||||
# except-blocks we are in
|
# except-blocks we are in
|
||||||
if not p.noSafePoints:
|
if noSafePoints notin p.flags:
|
||||||
for i in countdown(howManyExcepts-1, 0):
|
for i in countdown(howManyExcepts-1, 0):
|
||||||
linefmt(p, cpsStmts, "#popCurrentException();$n", [])
|
linefmt(p, cpsStmts, "#popCurrentException();$n", [])
|
||||||
|
|
||||||
|
|
@ -237,7 +237,7 @@ proc genGotoState(p: BProc, n: PNode) =
|
||||||
var a: TLoc
|
var a: TLoc
|
||||||
initLocExpr(p, n[0], a)
|
initLocExpr(p, n[0], a)
|
||||||
lineF(p, cpsStmts, "switch ($1) {$n", [rdLoc(a)])
|
lineF(p, cpsStmts, "switch ($1) {$n", [rdLoc(a)])
|
||||||
p.beforeRetNeeded = true
|
p.flags.incl beforeRetNeeded
|
||||||
lineF(p, cpsStmts, "case -1:$n", [])
|
lineF(p, cpsStmts, "case -1:$n", [])
|
||||||
blockLeaveActions(p,
|
blockLeaveActions(p,
|
||||||
howManyTrys = p.nestedTryStmts.len,
|
howManyTrys = p.nestedTryStmts.len,
|
||||||
|
|
@ -454,13 +454,13 @@ proc genIf(p: BProc, n: PNode, d: var TLoc) =
|
||||||
|
|
||||||
proc genReturnStmt(p: BProc, t: PNode) =
|
proc genReturnStmt(p: BProc, t: PNode) =
|
||||||
if nfPreventCg in t.flags: return
|
if nfPreventCg in t.flags: return
|
||||||
p.beforeRetNeeded = true
|
p.flags.incl beforeRetNeeded
|
||||||
genLineDir(p, t)
|
genLineDir(p, t)
|
||||||
if (t[0].kind != nkEmpty): genStmts(p, t[0])
|
if (t[0].kind != nkEmpty): genStmts(p, t[0])
|
||||||
blockLeaveActions(p,
|
blockLeaveActions(p,
|
||||||
howManyTrys = p.nestedTryStmts.len,
|
howManyTrys = p.nestedTryStmts.len,
|
||||||
howManyExcepts = p.inExceptBlockLen)
|
howManyExcepts = p.inExceptBlockLen)
|
||||||
if (p.finallySafePoints.len > 0) and not p.noSafePoints:
|
if (p.finallySafePoints.len > 0) and noSafePoints notin p.flags:
|
||||||
# If we're in a finally block, and we came here by exception
|
# If we're in a finally block, and we came here by exception
|
||||||
# consume it before we return.
|
# consume it before we return.
|
||||||
var safePoint = p.finallySafePoints[^1]
|
var safePoint = p.finallySafePoints[^1]
|
||||||
|
|
@ -687,15 +687,27 @@ proc genBreakStmt(p: BProc, t: PNode) =
|
||||||
genLineDir(p, t)
|
genLineDir(p, t)
|
||||||
lineF(p, cpsStmts, "goto $1;$n", [label])
|
lineF(p, cpsStmts, "goto $1;$n", [label])
|
||||||
|
|
||||||
|
proc raiseExit(p: BProc) =
|
||||||
|
assert p.config.exc == excGoto
|
||||||
|
p.flags.incl nimErrorFlagAccessed
|
||||||
|
if p.nestedTryStmts.len == 0:
|
||||||
|
p.flags.incl beforeRetNeeded
|
||||||
|
# easy case, simply goto 'ret':
|
||||||
|
lineCg(p, cpsStmts, "if (NIM_UNLIKELY(*nimErr_)) goto BeforeRet_;$n", [])
|
||||||
|
else:
|
||||||
|
lineCg(p, cpsStmts, "if (NIM_UNLIKELY(*nimErr_)) goto LA$1_;$n",
|
||||||
|
[p.nestedTryStmts[^1].label])
|
||||||
|
|
||||||
proc genRaiseStmt(p: BProc, t: PNode) =
|
proc genRaiseStmt(p: BProc, t: PNode) =
|
||||||
if p.module.compileToCpp:
|
if p.config.exc != excGoto:
|
||||||
discard cgsym(p.module, "popCurrentExceptionEx")
|
if p.config.exc == excCpp:
|
||||||
if p.nestedTryStmts.len > 0 and p.nestedTryStmts[^1].inExcept:
|
discard cgsym(p.module, "popCurrentExceptionEx")
|
||||||
# if the current try stmt have a finally block,
|
if p.nestedTryStmts.len > 0 and p.nestedTryStmts[^1].inExcept:
|
||||||
# we must execute it before reraising
|
# if the current try stmt have a finally block,
|
||||||
let finallyBlock = p.nestedTryStmts[^1].fin
|
# we must execute it before reraising
|
||||||
if finallyBlock != nil:
|
let finallyBlock = p.nestedTryStmts[^1].fin
|
||||||
genSimpleBlock(p, finallyBlock[0])
|
if finallyBlock != nil:
|
||||||
|
genSimpleBlock(p, finallyBlock[0])
|
||||||
if t[0].kind != nkEmpty:
|
if t[0].kind != nkEmpty:
|
||||||
var a: TLoc
|
var a: TLoc
|
||||||
initLocExprSingleUse(p, t[0], a)
|
initLocExprSingleUse(p, t[0], a)
|
||||||
|
|
@ -714,10 +726,22 @@ proc genRaiseStmt(p: BProc, t: PNode) =
|
||||||
else:
|
else:
|
||||||
genLineDir(p, t)
|
genLineDir(p, t)
|
||||||
# reraise the last exception:
|
# reraise the last exception:
|
||||||
if p.module.compileToCpp and optNoCppExceptions notin p.config.globalOptions:
|
if p.config.exc == excCpp:
|
||||||
line(p, cpsStmts, ~"throw;$n")
|
line(p, cpsStmts, ~"throw;$n")
|
||||||
else:
|
else:
|
||||||
linefmt(p, cpsStmts, "#reraiseException();$n", [])
|
linefmt(p, cpsStmts, "#reraiseException();$n", [])
|
||||||
|
if p.config.exc == excGoto:
|
||||||
|
let L = p.nestedTryStmts.len
|
||||||
|
if L == 0:
|
||||||
|
p.flags.incl beforeRetNeeded
|
||||||
|
# easy case, simply goto 'ret':
|
||||||
|
lineCg(p, cpsStmts, "goto BeforeRet_;$n", [])
|
||||||
|
else:
|
||||||
|
# raise inside an 'except' must go to the finally block,
|
||||||
|
# raise outside an 'except' block must go to the 'except' list.
|
||||||
|
lineCg(p, cpsStmts, "goto LA$1_;$n",
|
||||||
|
[p.nestedTryStmts[L-1].label])
|
||||||
|
# + ord(p.nestedTryStmts[L-1].inExcept)])
|
||||||
|
|
||||||
template genCaseGenericBranch(p: BProc, b: PNode, e: TLoc,
|
template genCaseGenericBranch(p: BProc, b: PNode, e: TLoc,
|
||||||
rangeFormat, eqFormat: FormatStr, labl: TLabel) =
|
rangeFormat, eqFormat: FormatStr, labl: TLabel) =
|
||||||
|
|
@ -906,8 +930,8 @@ proc genCase(p: BProc, t: PNode, d: var TLoc) =
|
||||||
|
|
||||||
proc genRestoreFrameAfterException(p: BProc) =
|
proc genRestoreFrameAfterException(p: BProc) =
|
||||||
if optStackTrace in p.module.config.options:
|
if optStackTrace in p.module.config.options:
|
||||||
if not p.hasCurFramePointer:
|
if hasCurFramePointer notin p.flags:
|
||||||
p.hasCurFramePointer = true
|
p.flags.incl hasCurFramePointer
|
||||||
p.procSec(cpsLocals).add(ropecg(p.module, "\tTFrame* _nimCurFrame;$n", []))
|
p.procSec(cpsLocals).add(ropecg(p.module, "\tTFrame* _nimCurFrame;$n", []))
|
||||||
p.procSec(cpsInit).add(ropecg(p.module, "\t_nimCurFrame = #getFrame();$n", []))
|
p.procSec(cpsInit).add(ropecg(p.module, "\t_nimCurFrame = #getFrame();$n", []))
|
||||||
linefmt(p, cpsStmts, "#setFrame(_nimCurFrame);$n", [])
|
linefmt(p, cpsStmts, "#setFrame(_nimCurFrame);$n", [])
|
||||||
|
|
@ -938,7 +962,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
|
||||||
genLineDir(p, t)
|
genLineDir(p, t)
|
||||||
discard cgsym(p.module, "popCurrentExceptionEx")
|
discard cgsym(p.module, "popCurrentExceptionEx")
|
||||||
let fin = if t[^1].kind == nkFinally: t[^1] else: nil
|
let fin = if t[^1].kind == nkFinally: t[^1] else: nil
|
||||||
p.nestedTryStmts.add((fin, false))
|
p.nestedTryStmts.add((fin, false, 0.Natural))
|
||||||
startBlock(p, "try {$n")
|
startBlock(p, "try {$n")
|
||||||
expr(p, t[0], d)
|
expr(p, t[0], d)
|
||||||
endBlock(p)
|
endBlock(p)
|
||||||
|
|
@ -982,7 +1006,116 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
|
||||||
|
|
||||||
genSimpleBlock(p, t[^1][0])
|
genSimpleBlock(p, t[^1][0])
|
||||||
|
|
||||||
proc genTry(p: BProc, t: PNode, d: var TLoc) =
|
proc bodyCanRaise(n: PNode): bool =
|
||||||
|
case n.kind
|
||||||
|
of nkCallKinds:
|
||||||
|
result = canRaise(n[0])
|
||||||
|
if not result:
|
||||||
|
# also check the arguments:
|
||||||
|
for i in 1 ..< n.len:
|
||||||
|
if bodyCanRaise(n[i]): return true
|
||||||
|
of nkRaiseStmt:
|
||||||
|
result = true
|
||||||
|
of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef,
|
||||||
|
nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef:
|
||||||
|
result = false
|
||||||
|
else:
|
||||||
|
for i in 0 ..< safeLen(n):
|
||||||
|
if bodyCanRaise(n[i]): return true
|
||||||
|
result = false
|
||||||
|
|
||||||
|
proc genTryGoto(p: BProc; t: PNode; d: var TLoc) =
|
||||||
|
if not bodyCanRaise(t):
|
||||||
|
# optimize away the 'try' block:
|
||||||
|
expr(p, t[0], d)
|
||||||
|
if t.len > 1 and t[^1].kind == nkFinally:
|
||||||
|
genStmts(p, t[^1][0])
|
||||||
|
return
|
||||||
|
|
||||||
|
let fin = if t[^1].kind == nkFinally: t[^1] else: nil
|
||||||
|
inc p.labels, 2
|
||||||
|
let lab = p.labels-1
|
||||||
|
p.nestedTryStmts.add((fin, false, Natural lab))
|
||||||
|
|
||||||
|
p.flags.incl nimErrorFlagAccessed
|
||||||
|
linefmt(p, cpsStmts, "NI oldNimErr$1_ = *nimErr_; *nimErr_ = 0;;$n", [lab])
|
||||||
|
|
||||||
|
expr(p, t[0], d)
|
||||||
|
|
||||||
|
if 1 < t.len and t[1].kind == nkExceptBranch:
|
||||||
|
startBlock(p, "if (NIM_UNLIKELY(*nimErr_)) {$n")
|
||||||
|
else:
|
||||||
|
startBlock(p)
|
||||||
|
# pretend we did handle the error for the safe execution of the sections:
|
||||||
|
linefmt(p, cpsStmts, "LA$1_: oldNimErr$1_ = *nimErr_; *nimErr_ = 0;$n", [lab])
|
||||||
|
|
||||||
|
p.nestedTryStmts[^1].inExcept = true
|
||||||
|
var i = 1
|
||||||
|
while (i < t.len) and (t[i].kind == nkExceptBranch):
|
||||||
|
|
||||||
|
inc p.labels
|
||||||
|
let nextExcept = p.labels
|
||||||
|
p.nestedTryStmts[^1].label = nextExcept
|
||||||
|
|
||||||
|
# bug #4230: avoid false sharing between branches:
|
||||||
|
if d.k == locTemp and isEmptyType(t.typ): d.k = locNone
|
||||||
|
if t[i].len == 1:
|
||||||
|
# general except section:
|
||||||
|
if i > 1: lineF(p, cpsStmts, "else", [])
|
||||||
|
startBlock(p)
|
||||||
|
# we handled the exception, remember this:
|
||||||
|
linefmt(p, cpsStmts, "--oldNimErr$1_;$n", [lab])
|
||||||
|
expr(p, t[i][0], d)
|
||||||
|
else:
|
||||||
|
var orExpr: Rope = nil
|
||||||
|
for j in 0..<t[i].len - 1:
|
||||||
|
assert(t[i][j].kind == nkType)
|
||||||
|
if orExpr != nil: orExpr.add("||")
|
||||||
|
let checkFor = if optTinyRtti in p.config.globalOptions:
|
||||||
|
genTypeInfo2Name(p.module, t[i][j].typ)
|
||||||
|
else:
|
||||||
|
genTypeInfo(p.module, t[i][j].typ, t[i][j].info)
|
||||||
|
let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type"
|
||||||
|
appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor])
|
||||||
|
|
||||||
|
if i > 1: line(p, cpsStmts, "else ")
|
||||||
|
startBlock(p, "if ($1) {$n", [orExpr])
|
||||||
|
# we handled the exception, remember this:
|
||||||
|
linefmt(p, cpsStmts, "--oldNimErr$1_;$n", [lab])
|
||||||
|
expr(p, t[i][^1], d)
|
||||||
|
|
||||||
|
linefmt(p, cpsStmts, "#popCurrentException();$n", [])
|
||||||
|
linefmt(p, cpsStmts, "LA$1_:;$n", [nextExcept])
|
||||||
|
endBlock(p)
|
||||||
|
|
||||||
|
inc(i)
|
||||||
|
discard pop(p.nestedTryStmts)
|
||||||
|
endBlock(p)
|
||||||
|
|
||||||
|
#linefmt(p, cpsStmts, "LA$1_:;$n", [lab+1])
|
||||||
|
if i < t.len and t[i].kind == nkFinally:
|
||||||
|
startBlock(p)
|
||||||
|
if not bodyCanRaise(t[i][0]):
|
||||||
|
# this is an important optimization; most destroy blocks are detected not to raise an
|
||||||
|
# exception and so we help the C optimizer by not mutating nimErr_ pointlessly:
|
||||||
|
genStmts(p, t[i][0])
|
||||||
|
else:
|
||||||
|
# pretend we did handle the error for the safe execution of the 'finally' section:
|
||||||
|
linefmt(p, cpsStmts, "NI oldNimErrFin$1_ = *nimErr_; *nimErr_ = 0;$n", [lab])
|
||||||
|
genStmts(p, t[i][0])
|
||||||
|
# this is correct for all these cases:
|
||||||
|
# 1. finally is run during ordinary control flow
|
||||||
|
# 2. finally is run after 'except' block handling: these however set the
|
||||||
|
# error back to nil.
|
||||||
|
# 3. finally is run for exception handling code without any 'except'
|
||||||
|
# handler present or only handlers that did not match.
|
||||||
|
linefmt(p, cpsStmts, "*nimErr_ += oldNimErr$1_ + (*nimErr_ - oldNimErrFin$1_); oldNimErr$1_ = 0;$n", [lab])
|
||||||
|
raiseExit(p)
|
||||||
|
endBlock(p)
|
||||||
|
# restore the real error value:
|
||||||
|
linefmt(p, cpsStmts, "*nimErr_ += oldNimErr$1_;$n", [lab])
|
||||||
|
|
||||||
|
proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
|
||||||
# code to generate:
|
# code to generate:
|
||||||
#
|
#
|
||||||
# XXX: There should be a standard dispatch algorithm
|
# XXX: There should be a standard dispatch algorithm
|
||||||
|
|
@ -1013,12 +1146,12 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
|
||||||
#
|
#
|
||||||
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)
|
||||||
let quirkyExceptions = isDefined(p.config, "nimQuirky") or
|
let quirkyExceptions = p.config.exc == excQuirky or
|
||||||
(t.kind == nkHiddenTryStmt and sfSystemModule in p.module.module.flags)
|
(t.kind == nkHiddenTryStmt and sfSystemModule in p.module.module.flags)
|
||||||
if not quirkyExceptions:
|
if not quirkyExceptions:
|
||||||
p.module.includeHeader("<setjmp.h>")
|
p.module.includeHeader("<setjmp.h>")
|
||||||
else:
|
else:
|
||||||
p.noSafePoints = true
|
p.flags.incl noSafePoints
|
||||||
genLineDir(p, t)
|
genLineDir(p, t)
|
||||||
discard cgsym(p.module, "Exception")
|
discard cgsym(p.module, "Exception")
|
||||||
var safePoint: Rope
|
var safePoint: Rope
|
||||||
|
|
@ -1036,7 +1169,7 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
|
||||||
linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", [safePoint])
|
linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", [safePoint])
|
||||||
startBlock(p, "if ($1.status == 0) {$n", [safePoint])
|
startBlock(p, "if ($1.status == 0) {$n", [safePoint])
|
||||||
let fin = if t[^1].kind == nkFinally: t[^1] else: nil
|
let fin = if t[^1].kind == nkFinally: t[^1] else: nil
|
||||||
p.nestedTryStmts.add((fin, quirkyExceptions))
|
p.nestedTryStmts.add((fin, quirkyExceptions, 0.Natural))
|
||||||
expr(p, t[0], d)
|
expr(p, t[0], d)
|
||||||
if not quirkyExceptions:
|
if not quirkyExceptions:
|
||||||
linefmt(p, cpsStmts, "#popSafePoint();$n", [])
|
linefmt(p, cpsStmts, "#popSafePoint();$n", [])
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,8 @@ proc emulatedThreadVars(conf: ConfigRef): bool =
|
||||||
result = {optThreads, optTlsEmulation} <= conf.globalOptions
|
result = {optThreads, optTlsEmulation} <= conf.globalOptions
|
||||||
|
|
||||||
proc accessThreadLocalVar(p: BProc, s: PSym) =
|
proc accessThreadLocalVar(p: BProc, s: PSym) =
|
||||||
if emulatedThreadVars(p.config) and not p.threadVarAccessed:
|
if emulatedThreadVars(p.config) and threadVarAccessed notin p.flags:
|
||||||
p.threadVarAccessed = true
|
p.flags.incl threadVarAccessed
|
||||||
incl p.module.flags, usesThreadVars
|
incl p.module.flags, usesThreadVars
|
||||||
p.procSec(cpsLocals).addf("\tNimThreadVars* NimTV_;$n", [])
|
p.procSec(cpsLocals).addf("\tNimThreadVars* NimTV_;$n", [])
|
||||||
p.procSec(cpsInit).add(
|
p.procSec(cpsInit).add(
|
||||||
|
|
|
||||||
|
|
@ -481,14 +481,6 @@ proc getIntTemp(p: BProc, result: var TLoc) =
|
||||||
result.lode = lodeTyp getSysType(p.module.g.graph, unknownLineInfo(), tyInt)
|
result.lode = lodeTyp getSysType(p.module.g.graph, unknownLineInfo(), tyInt)
|
||||||
result.flags = {}
|
result.flags = {}
|
||||||
|
|
||||||
proc initGCFrame(p: BProc): Rope =
|
|
||||||
if p.gcFrameId > 0: result = "struct {$1} GCFRAME_;$n" % [p.gcFrameType]
|
|
||||||
|
|
||||||
proc deinitGCFrame(p: BProc): Rope =
|
|
||||||
if p.gcFrameId > 0:
|
|
||||||
result = ropecg(p.module,
|
|
||||||
"if (((NU)&GCFRAME_) < 4096) #nimGCFrame(&GCFRAME_);$n", [])
|
|
||||||
|
|
||||||
proc localVarDecl(p: BProc; n: PNode): Rope =
|
proc localVarDecl(p: BProc; n: PNode): Rope =
|
||||||
let s = n.sym
|
let s = n.sym
|
||||||
if s.loc.k == locNone:
|
if s.loc.k == locNone:
|
||||||
|
|
@ -599,6 +591,7 @@ proc putLocIntoDest(p: BProc, d: var TLoc, s: TLoc)
|
||||||
proc intLiteral(i: BiggestInt): Rope
|
proc intLiteral(i: BiggestInt): Rope
|
||||||
proc genLiteral(p: BProc, n: PNode): Rope
|
proc genLiteral(p: BProc, n: PNode): Rope
|
||||||
proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope
|
proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope
|
||||||
|
proc raiseExit(p: BProc)
|
||||||
|
|
||||||
proc initLocExpr(p: BProc, e: PNode, result: var TLoc) =
|
proc initLocExpr(p: BProc, e: PNode, result: var TLoc) =
|
||||||
initLoc(result, locNone, e, OnUnknown)
|
initLoc(result, locNone, e, OnUnknown)
|
||||||
|
|
@ -636,13 +629,7 @@ proc initFrame(p: BProc, procname, filename: Rope): Rope =
|
||||||
appcg(p.module, p.module.s[cfsFrameDefines], frameDefines, ["#"])
|
appcg(p.module, p.module.s[cfsFrameDefines], frameDefines, ["#"])
|
||||||
|
|
||||||
discard cgsym(p.module, "nimFrame")
|
discard cgsym(p.module, "nimFrame")
|
||||||
if p.maxFrameLen > 0:
|
result = ropecg(p.module, "\tnimfr_($1, $2);$n", [procname, filename])
|
||||||
discard cgsym(p.module, "VarSlot")
|
|
||||||
result = ropecg(p.module, "\tnimfrs_($1, $2, $3, $4);$n",
|
|
||||||
[procname, filename, p.maxFrameLen,
|
|
||||||
p.blocks[0].frameLen])
|
|
||||||
else:
|
|
||||||
result = ropecg(p.module, "\tnimfr_($1, $2);$n", [procname, filename])
|
|
||||||
|
|
||||||
proc initFrameNoDebug(p: BProc; frame, procname, filename: Rope; line: int): Rope =
|
proc initFrameNoDebug(p: BProc; frame, procname, filename: Rope; line: int): Rope =
|
||||||
discard cgsym(p.module, "nimFrame")
|
discard cgsym(p.module, "nimFrame")
|
||||||
|
|
@ -979,6 +966,13 @@ proc getProcTypeCast(m: BModule, prc: PSym): Rope =
|
||||||
genProcParams(m, prc.typ, rettype, params, check)
|
genProcParams(m, prc.typ, rettype, params, check)
|
||||||
result = "$1(*)$2" % [rettype, params]
|
result = "$1(*)$2" % [rettype, params]
|
||||||
|
|
||||||
|
proc genProcBody(p: BProc; procBody: PNode) =
|
||||||
|
genStmts(p, procBody) # modifies p.locals, p.init, etc.
|
||||||
|
if {nimErrorFlagAccessed, nimErrorFlagDeclared} * p.flags == {nimErrorFlagAccessed}:
|
||||||
|
p.flags.incl nimErrorFlagDeclared
|
||||||
|
p.blocks[0].sections[cpsLocals].add(ropecg(p.module, "NI* nimErr_;$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) =
|
||||||
var p = newProc(prc, m)
|
var p = newProc(prc, m)
|
||||||
var header = genProcHeader(m, prc)
|
var header = genProcHeader(m, prc)
|
||||||
|
|
@ -1029,7 +1023,8 @@ proc genProcAux(m: BModule, prc: PSym) =
|
||||||
if param.typ.isCompileTimeOnly: continue
|
if param.typ.isCompileTimeOnly: continue
|
||||||
assignParam(p, param, prc.typ[0])
|
assignParam(p, param, prc.typ[0])
|
||||||
closureSetup(p, prc)
|
closureSetup(p, prc)
|
||||||
genStmts(p, procBody) # modifies p.locals, p.init, etc.
|
genProcBody(p, procBody)
|
||||||
|
|
||||||
var generatedProc: Rope
|
var generatedProc: Rope
|
||||||
generatedProc.genCLineDir prc.info, m.config
|
generatedProc.genCLineDir prc.info, m.config
|
||||||
if sfNoReturn in prc.flags:
|
if sfNoReturn in prc.flags:
|
||||||
|
|
@ -1046,8 +1041,7 @@ proc genProcAux(m: BModule, prc: PSym) =
|
||||||
# This fixes the use of methods and also the case when 2 functions within the same module
|
# This fixes the use of methods and also the case when 2 functions within the same module
|
||||||
# call each other using directly the "_actual" versions (an optimization) - see issue #11608
|
# call each other using directly the "_actual" versions (an optimization) - see issue #11608
|
||||||
m.s[cfsProcHeaders].addf("$1;\n", [header])
|
m.s[cfsProcHeaders].addf("$1;\n", [header])
|
||||||
generatedProc.add ropecg(p.module, "$1 {", [header])
|
generatedProc.add ropecg(p.module, "$1 {$n", [header])
|
||||||
generatedProc.add(initGCFrame(p))
|
|
||||||
if optStackTrace in prc.options:
|
if optStackTrace in prc.options:
|
||||||
generatedProc.add(p.s(cpsLocals))
|
generatedProc.add(p.s(cpsLocals))
|
||||||
var procname = makeCString(prc.name.s)
|
var procname = makeCString(prc.name.s)
|
||||||
|
|
@ -1057,11 +1051,12 @@ proc genProcAux(m: BModule, prc: PSym) =
|
||||||
if optProfiler in prc.options:
|
if optProfiler in prc.options:
|
||||||
# invoke at proc entry for recursion:
|
# invoke at proc entry for recursion:
|
||||||
appcg(p, cpsInit, "\t#nimProfile();$n", [])
|
appcg(p, cpsInit, "\t#nimProfile();$n", [])
|
||||||
if p.beforeRetNeeded: generatedProc.add("{")
|
# this pair of {} is required for C++ (C++ is weird with its
|
||||||
|
# control flow integrity checks):
|
||||||
|
if beforeRetNeeded in p.flags: generatedProc.add("{")
|
||||||
generatedProc.add(p.s(cpsInit))
|
generatedProc.add(p.s(cpsInit))
|
||||||
generatedProc.add(p.s(cpsStmts))
|
generatedProc.add(p.s(cpsStmts))
|
||||||
if p.beforeRetNeeded: generatedProc.add(~"\t}BeforeRet_: ;$n")
|
if beforeRetNeeded in p.flags: generatedProc.add(~"\t}BeforeRet_: ;$n")
|
||||||
generatedProc.add(deinitGCFrame(p))
|
|
||||||
if optStackTrace in prc.options: generatedProc.add(deinitFrame(p))
|
if optStackTrace in prc.options: generatedProc.add(deinitFrame(p))
|
||||||
generatedProc.add(returnStmt)
|
generatedProc.add(returnStmt)
|
||||||
generatedProc.add(~"}$N")
|
generatedProc.add(~"}$N")
|
||||||
|
|
@ -1645,10 +1640,6 @@ proc genInitCode(m: BModule) =
|
||||||
# add new scope for following code, because old vcc compiler need variable
|
# add new scope for following code, because old vcc compiler need variable
|
||||||
# be defined at the top of the block
|
# be defined at the top of the block
|
||||||
prc.addf("{$N", [])
|
prc.addf("{$N", [])
|
||||||
if m.initProc.gcFrameId > 0:
|
|
||||||
moduleInitRequired = true
|
|
||||||
prc.add(initGCFrame(m.initProc))
|
|
||||||
|
|
||||||
writeSection(initProc, cpsLocals)
|
writeSection(initProc, cpsLocals)
|
||||||
|
|
||||||
if m.initProc.s(cpsInit).len > 0 or m.initProc.s(cpsStmts).len > 0:
|
if m.initProc.s(cpsInit).len > 0 or m.initProc.s(cpsStmts).len > 0:
|
||||||
|
|
@ -1666,12 +1657,11 @@ proc genInitCode(m: BModule) =
|
||||||
writeSection(initProc, cpsInit, m.hcrOn)
|
writeSection(initProc, cpsInit, m.hcrOn)
|
||||||
writeSection(initProc, cpsStmts)
|
writeSection(initProc, cpsStmts)
|
||||||
|
|
||||||
|
if beforeRetNeeded in m.initProc.flags:
|
||||||
|
prc.add(~"\tBeforeRet_: ;$n")
|
||||||
if optStackTrace in m.initProc.options and preventStackTrace notin m.flags:
|
if optStackTrace in m.initProc.options and preventStackTrace notin m.flags:
|
||||||
prc.add(deinitFrame(m.initProc))
|
prc.add(deinitFrame(m.initProc))
|
||||||
|
|
||||||
if m.initProc.gcFrameId > 0:
|
|
||||||
moduleInitRequired = true
|
|
||||||
prc.add(deinitGCFrame(m.initProc))
|
|
||||||
prc.addf("}$N", [])
|
prc.addf("}$N", [])
|
||||||
|
|
||||||
prc.addf("}$N$N", [])
|
prc.addf("}$N$N", [])
|
||||||
|
|
@ -1894,7 +1884,7 @@ proc myProcess(b: PPassContext, n: PNode): PNode =
|
||||||
if m.hcrOn:
|
if m.hcrOn:
|
||||||
addHcrInitGuards(m.initProc, transformedN, m.inHcrInitGuard)
|
addHcrInitGuards(m.initProc, transformedN, m.inHcrInitGuard)
|
||||||
else:
|
else:
|
||||||
genStmts(m.initProc, transformedN)
|
genProcBody(m.initProc, transformedN)
|
||||||
|
|
||||||
proc shouldRecompile(m: BModule; code: Rope, cfile: Cfile): bool =
|
proc shouldRecompile(m: BModule; code: Rope, cfile: Cfile): bool =
|
||||||
if optForceFullMake notin m.config.globalOptions:
|
if optForceFullMake notin m.config.globalOptions:
|
||||||
|
|
@ -1996,6 +1986,10 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
|
||||||
if b == nil: return
|
if b == nil: return
|
||||||
var m = BModule(b)
|
var m = BModule(b)
|
||||||
if sfMainModule in m.module.flags:
|
if sfMainModule in m.module.flags:
|
||||||
|
let testForError = getCompilerProc(graph, "nimTestErrorFlag")
|
||||||
|
if testForError != nil and graph.config.exc == excGoto:
|
||||||
|
n.add newTree(nkCall, testForError.newSymNode)
|
||||||
|
|
||||||
for i in countdown(high(graph.globalDestructors), 0):
|
for i in countdown(high(graph.globalDestructors), 0):
|
||||||
n.add graph.globalDestructors[i]
|
n.add graph.globalDestructors[i]
|
||||||
if passes.skipCodegen(m.config, n): return
|
if passes.skipCodegen(m.config, n): return
|
||||||
|
|
@ -2005,7 +1999,7 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
|
||||||
# XXX emit the dispatchers into its own .c file?
|
# XXX emit the dispatchers into its own .c file?
|
||||||
if n != nil:
|
if n != nil:
|
||||||
m.initProc.options = initProcOptions(m)
|
m.initProc.options = initProcOptions(m)
|
||||||
genStmts(m.initProc, n)
|
genProcBody(m.initProc, n)
|
||||||
|
|
||||||
if m.hcrOn:
|
if m.hcrOn:
|
||||||
# make sure this is pulled in (meaning hcrGetGlobal() is called for it during init)
|
# make sure this is pulled in (meaning hcrGetGlobal() is called for it during init)
|
||||||
|
|
|
||||||
|
|
@ -64,16 +64,20 @@ type
|
||||||
nestedExceptStmts*: int16 # how many except statements is it nested into
|
nestedExceptStmts*: int16 # how many except statements is it nested into
|
||||||
frameLen*: int16
|
frameLen*: int16
|
||||||
|
|
||||||
|
TCProcFlag* = enum
|
||||||
|
beforeRetNeeded,
|
||||||
|
threadVarAccessed,
|
||||||
|
hasCurFramePointer,
|
||||||
|
noSafePoints,
|
||||||
|
nimErrorFlagAccessed,
|
||||||
|
nimErrorFlagDeclared
|
||||||
|
|
||||||
TCProc = object # represents C proc that is currently generated
|
TCProc = object # represents C proc that is currently generated
|
||||||
prc*: PSym # the Nim proc that this C proc belongs to
|
prc*: PSym # the Nim proc that this C proc belongs to
|
||||||
beforeRetNeeded*: bool # true iff 'BeforeRet' label for proc is needed
|
flags*: set[TCProcFlag]
|
||||||
threadVarAccessed*: bool # true if the proc already accessed some threadvar
|
|
||||||
hasCurFramePointer*: bool # true if _nimCurFrame var needed to recover after
|
|
||||||
# exception is generated
|
|
||||||
noSafePoints*: bool # the proc doesn't use safe points in exception handling
|
|
||||||
lastLineInfo*: TLineInfo # to avoid generating excessive 'nimln' statements
|
lastLineInfo*: TLineInfo # to avoid generating excessive 'nimln' statements
|
||||||
currLineInfo*: TLineInfo # AST codegen will make this superfluous
|
currLineInfo*: TLineInfo # AST codegen will make this superfluous
|
||||||
nestedTryStmts*: seq[tuple[fin: PNode, inExcept: bool]]
|
nestedTryStmts*: seq[tuple[fin: PNode, inExcept: bool, label: Natural]]
|
||||||
# in how many nested try statements we are
|
# in how many nested try statements we are
|
||||||
# (the vars must be volatile then)
|
# (the vars must be volatile then)
|
||||||
# bool is true when are in the except part of a try block
|
# bool is true when are in the except part of a try block
|
||||||
|
|
@ -86,14 +90,11 @@ type
|
||||||
options*: TOptions # options that should be used for code
|
options*: TOptions # options that should be used for code
|
||||||
# generation; this is the same as prc.options
|
# generation; this is the same as prc.options
|
||||||
# unless prc == nil
|
# unless prc == nil
|
||||||
maxFrameLen*: int # max length of frame descriptor
|
|
||||||
module*: BModule # used to prevent excessive parameter passing
|
module*: BModule # used to prevent excessive parameter passing
|
||||||
withinLoop*: int # > 0 if we are within a loop
|
withinLoop*: int # > 0 if we are within a loop
|
||||||
splitDecls*: int # > 0 if we are in some context for C++ that
|
splitDecls*: int # > 0 if we are in some context for C++ that
|
||||||
# requires 'T x = T()' to become 'T x; x = T()'
|
# requires 'T x = T()' to become 'T x; x = T()'
|
||||||
# (yes, C++ is weird like that)
|
# (yes, C++ is weird like that)
|
||||||
gcFrameId*: Natural # for the GC stack marking
|
|
||||||
gcFrameType*: Rope # the struct {} we put the GC markers into
|
|
||||||
sigConflicts*: CountTable[string]
|
sigConflicts*: CountTable[string]
|
||||||
|
|
||||||
TTypeSeq* = seq[PType]
|
TTypeSeq* = seq[PType]
|
||||||
|
|
|
||||||
|
|
@ -219,6 +219,7 @@ const
|
||||||
errNoneBoehmRefcExpectedButXFound = "'none', 'boehm' or 'refc' expected, but '$1' found"
|
errNoneBoehmRefcExpectedButXFound = "'none', 'boehm' or 'refc' expected, but '$1' found"
|
||||||
errNoneSpeedOrSizeExpectedButXFound = "'none', 'speed' or 'size' expected, but '$1' found"
|
errNoneSpeedOrSizeExpectedButXFound = "'none', 'speed' or 'size' expected, but '$1' found"
|
||||||
errGuiConsoleOrLibExpectedButXFound = "'gui', 'console' or 'lib' expected, but '$1' found"
|
errGuiConsoleOrLibExpectedButXFound = "'gui', 'console' or 'lib' expected, but '$1' found"
|
||||||
|
errInvalidExceptionSystem = "'goto', 'setjump', 'cpp' or 'quirky' expected, but '$1' found"
|
||||||
|
|
||||||
proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo): bool =
|
proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo): bool =
|
||||||
case switch.normalize
|
case switch.normalize
|
||||||
|
|
@ -254,6 +255,13 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo
|
||||||
else: localError(conf, info, errGuiConsoleOrLibExpectedButXFound % arg)
|
else: localError(conf, info, errGuiConsoleOrLibExpectedButXFound % arg)
|
||||||
of "dynliboverride":
|
of "dynliboverride":
|
||||||
result = isDynlibOverride(conf, arg)
|
result = isDynlibOverride(conf, arg)
|
||||||
|
of "exceptions":
|
||||||
|
case arg.normalize
|
||||||
|
of "cpp": result = conf.exc == excCpp
|
||||||
|
of "setjmp": result = conf.exc == excSetjmp
|
||||||
|
of "quirky": result = conf.exc == excQuirky
|
||||||
|
of "goto": result = conf.exc == excGoto
|
||||||
|
else: localError(conf, info, errInvalidExceptionSystem % arg)
|
||||||
else: invalidCmdLineOption(conf, passCmd1, switch, info)
|
else: invalidCmdLineOption(conf, passCmd1, switch, info)
|
||||||
|
|
||||||
proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool =
|
proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool =
|
||||||
|
|
@ -411,8 +419,12 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||||
expectArg(conf, switch, arg, pass, info)
|
expectArg(conf, switch, arg, pass, info)
|
||||||
if {':', '='} in arg:
|
if {':', '='} in arg:
|
||||||
splitSwitch(conf, arg, key, val, pass, info)
|
splitSwitch(conf, arg, key, val, pass, info)
|
||||||
|
if cmpIgnoreStyle(key, "nimQuirky") == 0:
|
||||||
|
conf.exc = excQuirky
|
||||||
defineSymbol(conf.symbols, key, val)
|
defineSymbol(conf.symbols, key, val)
|
||||||
else:
|
else:
|
||||||
|
if cmpIgnoreStyle(arg, "nimQuirky") == 0:
|
||||||
|
conf.exc = excQuirky
|
||||||
defineSymbol(conf.symbols, arg)
|
defineSymbol(conf.symbols, arg)
|
||||||
of "undef", "u":
|
of "undef", "u":
|
||||||
expectArg(conf, switch, arg, pass, info)
|
expectArg(conf, switch, arg, pass, info)
|
||||||
|
|
@ -457,6 +469,9 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||||
defineSymbol(conf.symbols, "gcmarkandsweep")
|
defineSymbol(conf.symbols, "gcmarkandsweep")
|
||||||
of "destructors", "arc":
|
of "destructors", "arc":
|
||||||
conf.selectedGC = gcArc
|
conf.selectedGC = gcArc
|
||||||
|
when true:
|
||||||
|
if conf.cmd != cmdCompileToCpp:
|
||||||
|
conf.exc = excGoto
|
||||||
defineSymbol(conf.symbols, "gcdestructors")
|
defineSymbol(conf.symbols, "gcdestructors")
|
||||||
defineSymbol(conf.symbols, "gcarc")
|
defineSymbol(conf.symbols, "gcarc")
|
||||||
incl conf.globalOptions, optSeqDestructors
|
incl conf.globalOptions, optSeqDestructors
|
||||||
|
|
@ -466,6 +481,9 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||||
defineSymbol(conf.symbols, "nimV2")
|
defineSymbol(conf.symbols, "nimV2")
|
||||||
of "orc":
|
of "orc":
|
||||||
conf.selectedGC = gcOrc
|
conf.selectedGC = gcOrc
|
||||||
|
when true:
|
||||||
|
if conf.cmd != cmdCompileToCpp:
|
||||||
|
conf.exc = excGoto
|
||||||
defineSymbol(conf.symbols, "gcdestructors")
|
defineSymbol(conf.symbols, "gcdestructors")
|
||||||
defineSymbol(conf.symbols, "gcorc")
|
defineSymbol(conf.symbols, "gcorc")
|
||||||
incl conf.globalOptions, optSeqDestructors
|
incl conf.globalOptions, optSeqDestructors
|
||||||
|
|
@ -775,8 +793,15 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||||
localError(conf, info, "unknown obsolete feature")
|
localError(conf, info, "unknown obsolete feature")
|
||||||
of "nocppexceptions":
|
of "nocppexceptions":
|
||||||
expectNoArg(conf, switch, arg, pass, info)
|
expectNoArg(conf, switch, arg, pass, info)
|
||||||
incl(conf.globalOptions, optNoCppExceptions)
|
conf.exc = low(ExceptionSystem)
|
||||||
defineSymbol(conf.symbols, "noCppExceptions")
|
defineSymbol(conf.symbols, "noCppExceptions")
|
||||||
|
of "exceptions":
|
||||||
|
case arg.normalize
|
||||||
|
of "cpp": conf.exc = excCpp
|
||||||
|
of "setjmp": conf.exc = excSetjmp
|
||||||
|
of "quirky": conf.exc = excQuirky
|
||||||
|
of "goto": conf.exc = excGoto
|
||||||
|
else: localError(conf, info, errInvalidExceptionSystem % arg)
|
||||||
of "cppdefine":
|
of "cppdefine":
|
||||||
expectArg(conf, switch, arg, pass, info)
|
expectArg(conf, switch, arg, pass, info)
|
||||||
if conf != nil:
|
if conf != nil:
|
||||||
|
|
|
||||||
|
|
@ -102,3 +102,4 @@ proc initDefines*(symbols: StringTableRef) =
|
||||||
defineSymbol("nimnomagic64")
|
defineSymbol("nimnomagic64")
|
||||||
defineSymbol("nimNewShiftOps")
|
defineSymbol("nimNewShiftOps")
|
||||||
defineSymbol("nimHasCursor")
|
defineSymbol("nimHasCursor")
|
||||||
|
defineSymbol("nimHasExceptionsQuery")
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,7 @@ proc commandJsonScript(graph: ModuleGraph) =
|
||||||
when not defined(leanCompiler):
|
when not defined(leanCompiler):
|
||||||
proc commandCompileToJS(graph: ModuleGraph) =
|
proc commandCompileToJS(graph: ModuleGraph) =
|
||||||
let conf = graph.config
|
let conf = graph.config
|
||||||
|
conf.exc = excCpp
|
||||||
|
|
||||||
if conf.outDir.isEmpty:
|
if conf.outDir.isEmpty:
|
||||||
conf.outDir = conf.projectPath
|
conf.outDir = conf.projectPath
|
||||||
|
|
@ -188,6 +189,7 @@ proc mainCommand*(graph: ModuleGraph) =
|
||||||
commandCompileToC(graph)
|
commandCompileToC(graph)
|
||||||
of "cpp", "compiletocpp":
|
of "cpp", "compiletocpp":
|
||||||
conf.cmd = cmdCompileToCpp
|
conf.cmd = cmdCompileToCpp
|
||||||
|
conf.exc = excCpp
|
||||||
defineSymbol(graph.config.symbols, "cpp")
|
defineSymbol(graph.config.symbols, "cpp")
|
||||||
commandCompileToC(graph)
|
commandCompileToC(graph)
|
||||||
of "objc", "compiletooc":
|
of "objc", "compiletooc":
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,6 @@ type # please make sure we have under 32 options
|
||||||
# also: generate header file
|
# also: generate header file
|
||||||
optIdeDebug # idetools: debug mode
|
optIdeDebug # idetools: debug mode
|
||||||
optIdeTerse # idetools: use terse descriptions
|
optIdeTerse # idetools: use terse descriptions
|
||||||
optNoCppExceptions # use C exception handling even with CPP
|
|
||||||
optExcessiveStackTrace # fully qualified module filenames
|
optExcessiveStackTrace # fully qualified module filenames
|
||||||
optShowAllMismatches # show all overloading resolution candidates
|
optShowAllMismatches # show all overloading resolution candidates
|
||||||
optWholeProject # for 'doc2': output any dependency
|
optWholeProject # for 'doc2': output any dependency
|
||||||
|
|
@ -159,6 +158,12 @@ type
|
||||||
ccNone, ccGcc, ccNintendoSwitch, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc,
|
ccNone, ccGcc, ccNintendoSwitch, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc,
|
||||||
ccTcc, ccPcc, ccUcc, ccIcl, ccIcc, ccClangCl
|
ccTcc, ccPcc, ccUcc, ccIcl, ccIcc, ccClangCl
|
||||||
|
|
||||||
|
ExceptionSystem* = enum
|
||||||
|
excSetjmp, # setjmp based exception handling
|
||||||
|
excCpp, # use C++'s native exception handling
|
||||||
|
excGoto, # exception handling based on goto (should become the new default for C)
|
||||||
|
excQuirky # quirky exception handling
|
||||||
|
|
||||||
CfileFlag* {.pure.} = enum
|
CfileFlag* {.pure.} = enum
|
||||||
Cached, ## no need to recompile this time
|
Cached, ## no need to recompile this time
|
||||||
External ## file was introduced via .compile pragma
|
External ## file was introduced via .compile pragma
|
||||||
|
|
@ -204,6 +209,7 @@ type
|
||||||
exitcode*: int8
|
exitcode*: int8
|
||||||
cmd*: TCommands # the command
|
cmd*: TCommands # the command
|
||||||
selectedGC*: TGCMode # the selected GC (+)
|
selectedGC*: TGCMode # the selected GC (+)
|
||||||
|
exc*: ExceptionSystem
|
||||||
verbosity*: int # how verbose the compiler is
|
verbosity*: int # how verbose the compiler is
|
||||||
numberOfProcessors*: int # number of processors
|
numberOfProcessors*: int # number of processors
|
||||||
evalExpr*: string # expression for idetools --eval
|
evalExpr*: string # expression for idetools --eval
|
||||||
|
|
|
||||||
|
|
@ -880,7 +880,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||||
of wNoreturn:
|
of wNoreturn:
|
||||||
noVal(c, it)
|
noVal(c, it)
|
||||||
# Disable the 'noreturn' annotation when in the "Quirky Exceptions" mode!
|
# Disable the 'noreturn' annotation when in the "Quirky Exceptions" mode!
|
||||||
if not isDefined(c.config, "nimQuirky"):
|
if c.config.exc notin {excQuirky, excGoto}:
|
||||||
incl(sym.flags, sfNoReturn)
|
incl(sym.flags, sfNoReturn)
|
||||||
if sym.typ[0] != nil:
|
if sym.typ[0] != nil:
|
||||||
localError(c.config, sym.ast[paramsPos][0].info,
|
localError(c.config, sym.ast[paramsPos][0].info,
|
||||||
|
|
|
||||||
|
|
@ -167,9 +167,7 @@ proc guardDotAccess(a: PEffects; n: PNode) =
|
||||||
guardGlobal(a, n, g)
|
guardGlobal(a, n, g)
|
||||||
|
|
||||||
proc makeVolatile(a: PEffects; s: PSym) {.inline.} =
|
proc makeVolatile(a: PEffects; s: PSym) {.inline.} =
|
||||||
template compileToCpp(a): untyped =
|
if a.inTryStmt > 0 and a.config.exc == excSetjmp:
|
||||||
a.config.cmd == cmdCompileToCpp or sfCompileToCpp in getModule(a.owner).flags
|
|
||||||
if a.inTryStmt > 0 and not compileToCpp(a):
|
|
||||||
incl(s.flags, sfVolatile)
|
incl(s.flags, sfVolatile)
|
||||||
|
|
||||||
proc initVar(a: PEffects, n: PNode; volatileCheck: bool) =
|
proc initVar(a: PEffects, n: PNode; volatileCheck: bool) =
|
||||||
|
|
|
||||||
|
|
@ -98,12 +98,13 @@ Advanced options:
|
||||||
--skipProjCfg:on|off do not read the project's configuration file
|
--skipProjCfg:on|off do not read the project's configuration file
|
||||||
--gc:refc|markAndSweep|boehm|go|none|regions
|
--gc:refc|markAndSweep|boehm|go|none|regions
|
||||||
select the GC to use; default is 'refc'
|
select the GC to use; default is 'refc'
|
||||||
|
--exceptions:setjmp|cpp|goto|quirky
|
||||||
|
select the exception handling implementation
|
||||||
--index:on|off turn index file generation on|off
|
--index:on|off turn index file generation on|off
|
||||||
--putenv:key=value set an environment variable
|
--putenv:key=value set an environment variable
|
||||||
--NimblePath:PATH add a path for Nimble support
|
--NimblePath:PATH add a path for Nimble support
|
||||||
--noNimblePath deactivate the Nimble path
|
--noNimblePath deactivate the Nimble path
|
||||||
--clearNimblePath empty the list of Nimble package search paths
|
--clearNimblePath empty the list of Nimble package search paths
|
||||||
--noCppExceptions use default exception handling with C++ backend
|
|
||||||
--cppCompileToNamespace:namespace
|
--cppCompileToNamespace:namespace
|
||||||
use the provided namespace for the generated C++ code,
|
use the provided namespace for the generated C++ code,
|
||||||
if no namespace is provided "Nim" will be used
|
if no namespace is provided "Nim" will be used
|
||||||
|
|
|
||||||
|
|
@ -3105,7 +3105,7 @@ when not defined(js):
|
||||||
when defined(nimV2):
|
when defined(nimV2):
|
||||||
type
|
type
|
||||||
TNimNode {.compilerproc.} = object # to keep the code generator simple
|
TNimNode {.compilerproc.} = object # to keep the code generator simple
|
||||||
DestructorProc = proc (p: pointer) {.nimcall, benign.}
|
DestructorProc = proc (p: pointer) {.nimcall, benign, raises: [].}
|
||||||
TNimType {.compilerproc.} = object
|
TNimType {.compilerproc.} = object
|
||||||
destructor: pointer
|
destructor: pointer
|
||||||
size: int
|
size: int
|
||||||
|
|
@ -3120,6 +3120,12 @@ when not defined(js):
|
||||||
|
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
||||||
|
when not defined(js) and not defined(nimscript):
|
||||||
|
proc writeStackTrace*() {.tags: [], gcsafe, raises: [].}
|
||||||
|
## Writes the current stack trace to ``stderr``. This is only works
|
||||||
|
## for debug builds. Since it's usually used for debugging, this
|
||||||
|
## is proclaimed to have no IO effect!
|
||||||
|
|
||||||
when not declared(sysFatal):
|
when not declared(sysFatal):
|
||||||
include "system/fatal"
|
include "system/fatal"
|
||||||
|
|
||||||
|
|
@ -3693,10 +3699,6 @@ when not defined(JS): #and not defined(nimscript):
|
||||||
proc unsetControlCHook*()
|
proc unsetControlCHook*()
|
||||||
## Reverts a call to setControlCHook.
|
## Reverts a call to setControlCHook.
|
||||||
|
|
||||||
proc writeStackTrace*() {.tags: [], gcsafe.}
|
|
||||||
## Writes the current stack trace to ``stderr``. This is only works
|
|
||||||
## for debug builds. Since it's usually used for debugging, this
|
|
||||||
## is proclaimed to have no IO effect!
|
|
||||||
when hostOS != "standalone":
|
when hostOS != "standalone":
|
||||||
proc getStackTrace*(): string {.gcsafe.}
|
proc getStackTrace*(): string {.gcsafe.}
|
||||||
## Gets the current stack trace. This only works for debug builds.
|
## Gets the current stack trace. This only works for debug builds.
|
||||||
|
|
|
||||||
|
|
@ -27,19 +27,21 @@ else:
|
||||||
proc writeToStdErr(msg: cstring) =
|
proc writeToStdErr(msg: cstring) =
|
||||||
discard MessageBoxA(nil, msg, nil, 0)
|
discard MessageBoxA(nil, msg, nil, 0)
|
||||||
|
|
||||||
proc showErrorMessage(data: cstring) {.gcsafe.} =
|
proc showErrorMessage(data: cstring) {.gcsafe, raises: [].} =
|
||||||
|
var toWrite = true
|
||||||
if errorMessageWriter != nil:
|
if errorMessageWriter != nil:
|
||||||
errorMessageWriter($data)
|
try:
|
||||||
else:
|
errorMessageWriter($data)
|
||||||
|
toWrite = false
|
||||||
|
except:
|
||||||
|
discard
|
||||||
|
if toWrite:
|
||||||
when defined(genode):
|
when defined(genode):
|
||||||
# stderr not available by default, use the LOG session
|
# stderr not available by default, use the LOG session
|
||||||
echo data
|
echo data
|
||||||
else:
|
else:
|
||||||
writeToStdErr(data)
|
writeToStdErr(data)
|
||||||
|
|
||||||
proc quitOrDebug() {.inline.} =
|
|
||||||
quit(1)
|
|
||||||
|
|
||||||
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.}
|
||||||
|
|
@ -57,10 +59,12 @@ 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
|
||||||
raiseCounter {.threadvar.}: uint
|
|
||||||
|
|
||||||
gcFramePtr {.threadvar.}: GcFrame
|
gcFramePtr {.threadvar.}: GcFrame
|
||||||
|
|
||||||
|
when defined(cpp) and not defined(noCppExceptions):
|
||||||
|
var
|
||||||
|
raiseCounter {.threadvar.}: uint
|
||||||
|
|
||||||
type
|
type
|
||||||
FrameState = tuple[gcFramePtr: GcFrame, framePtr: PFrame,
|
FrameState = tuple[gcFramePtr: GcFrame, framePtr: PFrame,
|
||||||
excHandler: PSafePoint, currException: ref Exception]
|
excHandler: PSafePoint, currException: ref Exception]
|
||||||
|
|
@ -130,7 +134,7 @@ proc popCurrentExceptionEx(id: uint) {.compilerRtl.} =
|
||||||
cur = cur.up
|
cur = cur.up
|
||||||
if cur == nil:
|
if cur == nil:
|
||||||
showErrorMessage("popCurrentExceptionEx() exception was not found in the exception stack. Aborting...")
|
showErrorMessage("popCurrentExceptionEx() exception was not found in the exception stack. Aborting...")
|
||||||
quitOrDebug()
|
quit(1)
|
||||||
prev.up = cur.up
|
prev.up = cur.up
|
||||||
|
|
||||||
proc closureIterSetupExc(e: ref Exception) {.compilerproc, inline.} =
|
proc closureIterSetupExc(e: ref Exception) {.compilerproc, inline.} =
|
||||||
|
|
@ -347,57 +351,87 @@ var onUnhandledException*: (proc (errorMsg: string) {.
|
||||||
## The default is to write a stacktrace to ``stderr`` and then call ``quit(1)``.
|
## The default is to write a stacktrace to ``stderr`` and then call ``quit(1)``.
|
||||||
## Unstable API.
|
## Unstable API.
|
||||||
|
|
||||||
template unhandled(buf, body) =
|
proc reportUnhandledError(e: ref Exception) {.nodestroy.} =
|
||||||
if onUnhandledException != nil:
|
when hasSomeStackTrace:
|
||||||
onUnhandledException($buf)
|
var buf = newStringOfCap(2000)
|
||||||
|
if e.trace.len == 0:
|
||||||
|
rawWriteStackTrace(buf)
|
||||||
|
else:
|
||||||
|
var trace = $e.trace
|
||||||
|
add(buf, trace)
|
||||||
|
`=destroy`(trace)
|
||||||
|
add(buf, "Error: unhandled exception: ")
|
||||||
|
add(buf, e.msg)
|
||||||
|
add(buf, " [")
|
||||||
|
add(buf, $e.name)
|
||||||
|
add(buf, "]\n")
|
||||||
|
|
||||||
|
if onUnhandledException != nil:
|
||||||
|
onUnhandledException(buf)
|
||||||
|
else:
|
||||||
|
showErrorMessage(buf)
|
||||||
|
`=destroy`(buf)
|
||||||
else:
|
else:
|
||||||
body
|
# ugly, but avoids heap allocations :-)
|
||||||
|
template xadd(buf, s, slen) =
|
||||||
|
if L + slen < high(buf):
|
||||||
|
copyMem(addr(buf[L]), cstring(s), slen)
|
||||||
|
inc L, slen
|
||||||
|
template add(buf, s) =
|
||||||
|
xadd(buf, s, s.len)
|
||||||
|
var buf: array[0..2000, char]
|
||||||
|
var L = 0
|
||||||
|
if e.trace.len != 0:
|
||||||
|
var trace = $e.trace
|
||||||
|
add(buf, trace)
|
||||||
|
`=destroy`(trace)
|
||||||
|
add(buf, "Error: unhandled exception: ")
|
||||||
|
add(buf, e.msg)
|
||||||
|
add(buf, " [")
|
||||||
|
xadd(buf, e.name, e.name.len)
|
||||||
|
add(buf, "]\n")
|
||||||
|
when defined(nimNoArrayToCstringConversion):
|
||||||
|
template tbuf(): untyped = addr buf
|
||||||
|
else:
|
||||||
|
template tbuf(): untyped = buf
|
||||||
|
|
||||||
|
if onUnhandledException != nil:
|
||||||
|
onUnhandledException($tbuf())
|
||||||
|
else:
|
||||||
|
showErrorMessage(tbuf())
|
||||||
|
|
||||||
proc nimLeaveFinally() {.compilerRtl.} =
|
proc nimLeaveFinally() {.compilerRtl.} =
|
||||||
when defined(cpp) and not defined(noCppExceptions):
|
when defined(cpp) and not defined(noCppExceptions):
|
||||||
{.emit: "throw;".}
|
{.emit: "throw;".}
|
||||||
else:
|
else:
|
||||||
template e: untyped = currException
|
|
||||||
if excHandler != nil:
|
if excHandler != nil:
|
||||||
c_longjmp(excHandler.context, 1)
|
c_longjmp(excHandler.context, 1)
|
||||||
else:
|
else:
|
||||||
when hasSomeStackTrace:
|
reportUnhandledError(currException)
|
||||||
var buf = newStringOfCap(2000)
|
quit(1)
|
||||||
if e.trace.len == 0: rawWriteStackTrace(buf)
|
|
||||||
else: add(buf, $e.trace)
|
when gotoBasedExceptions:
|
||||||
add(buf, "Error: unhandled exception: ")
|
var nimInErrorMode {.threadvar.}: int
|
||||||
add(buf, e.msg)
|
|
||||||
add(buf, " [")
|
proc nimErrorFlag(): ptr int {.compilerRtl, inl.} =
|
||||||
add(buf, $e.name)
|
result = addr(nimInErrorMode)
|
||||||
add(buf, "]\n")
|
|
||||||
unhandled(buf):
|
proc nimTestErrorFlag() {.compilerRtl.} =
|
||||||
showErrorMessage(buf)
|
## This proc must be called before ``currException`` is destroyed.
|
||||||
quitOrDebug()
|
## It also must be called at the end of every thread to ensure no
|
||||||
`=destroy`(buf)
|
## error is swallowed.
|
||||||
else:
|
if currException != nil:
|
||||||
# ugly, but avoids heap allocations :-)
|
reportUnhandledError(currException)
|
||||||
template xadd(buf, s, slen) =
|
currException = nil
|
||||||
if L + slen < high(buf):
|
quit(1)
|
||||||
copyMem(addr(buf[L]), cstring(s), slen)
|
|
||||||
inc L, slen
|
addQuitProc(proc () {.noconv.} =
|
||||||
template add(buf, s) =
|
if currException != nil:
|
||||||
xadd(buf, s, s.len)
|
reportUnhandledError(currException)
|
||||||
var buf: array[0..2000, char]
|
# emulate: ``programResult = 1`` via abort() and a nop signal handler.
|
||||||
var L = 0
|
c_signal(SIGABRT, (proc (sign: cint) {.noconv, benign.} = discard))
|
||||||
if e.trace.len != 0:
|
c_abort()
|
||||||
add(buf, $e.trace) # gc allocation
|
)
|
||||||
add(buf, "Error: unhandled exception: ")
|
|
||||||
add(buf, e.msg)
|
|
||||||
add(buf, " [")
|
|
||||||
xadd(buf, e.name, e.name.len)
|
|
||||||
add(buf, "]\n")
|
|
||||||
when defined(nimNoArrayToCstringConversion):
|
|
||||||
template tbuf(): untyped = addr buf
|
|
||||||
else:
|
|
||||||
template tbuf(): untyped = buf
|
|
||||||
unhandled(tbuf()):
|
|
||||||
showErrorMessage(tbuf())
|
|
||||||
quitOrDebug()
|
|
||||||
|
|
||||||
proc raiseExceptionAux(e: sink(ref Exception)) {.nodestroy.} =
|
proc raiseExceptionAux(e: sink(ref Exception)) {.nodestroy.} =
|
||||||
if localRaiseHook != nil:
|
if localRaiseHook != nil:
|
||||||
|
|
@ -414,50 +448,19 @@ proc raiseExceptionAux(e: sink(ref Exception)) {.nodestroy.} =
|
||||||
raiseCounter.inc # skip zero at overflow
|
raiseCounter.inc # skip zero at overflow
|
||||||
e.raiseId = raiseCounter
|
e.raiseId = raiseCounter
|
||||||
{.emit: "`e`->raise();".}
|
{.emit: "`e`->raise();".}
|
||||||
elif defined(nimQuirky):
|
elif defined(nimQuirky) or gotoBasedExceptions:
|
||||||
pushCurrentException(e)
|
# XXX This check should likely also be done in the setjmp case below.
|
||||||
|
if e != currException:
|
||||||
|
pushCurrentException(e)
|
||||||
|
when gotoBasedExceptions:
|
||||||
|
inc nimInErrorMode
|
||||||
else:
|
else:
|
||||||
if excHandler != nil:
|
if excHandler != nil:
|
||||||
pushCurrentException(e)
|
pushCurrentException(e)
|
||||||
c_longjmp(excHandler.context, 1)
|
c_longjmp(excHandler.context, 1)
|
||||||
else:
|
else:
|
||||||
when hasSomeStackTrace:
|
reportUnhandledError(e)
|
||||||
var buf = newStringOfCap(2000)
|
quit(1)
|
||||||
if e.trace.len == 0: rawWriteStackTrace(buf)
|
|
||||||
else: add(buf, $e.trace)
|
|
||||||
add(buf, "Error: unhandled exception: ")
|
|
||||||
add(buf, e.msg)
|
|
||||||
add(buf, " [")
|
|
||||||
add(buf, $e.name)
|
|
||||||
add(buf, "]\n")
|
|
||||||
unhandled(buf):
|
|
||||||
showErrorMessage(buf)
|
|
||||||
quitOrDebug()
|
|
||||||
`=destroy`(buf)
|
|
||||||
else:
|
|
||||||
# ugly, but avoids heap allocations :-)
|
|
||||||
template xadd(buf, s, slen) =
|
|
||||||
if L + slen < high(buf):
|
|
||||||
copyMem(addr(buf[L]), cstring(s), slen)
|
|
||||||
inc L, slen
|
|
||||||
template add(buf, s) =
|
|
||||||
xadd(buf, s, s.len)
|
|
||||||
var buf: array[0..2000, char]
|
|
||||||
var L = 0
|
|
||||||
if e.trace.len != 0:
|
|
||||||
add(buf, $e.trace) # gc allocation
|
|
||||||
add(buf, "Error: unhandled exception: ")
|
|
||||||
add(buf, e.msg)
|
|
||||||
add(buf, " [")
|
|
||||||
xadd(buf, e.name, e.name.len)
|
|
||||||
add(buf, "]\n")
|
|
||||||
when defined(nimNoArrayToCstringConversion):
|
|
||||||
template tbuf(): untyped = addr buf
|
|
||||||
else:
|
|
||||||
template tbuf(): untyped = buf
|
|
||||||
unhandled(tbuf()):
|
|
||||||
showErrorMessage(tbuf())
|
|
||||||
quitOrDebug()
|
|
||||||
|
|
||||||
proc raiseExceptionEx(e: sink(ref Exception), ename, procname, filename: cstring,
|
proc raiseExceptionEx(e: sink(ref Exception), ename, procname, filename: cstring,
|
||||||
line: int) {.compilerRtl, nodestroy.} =
|
line: int) {.compilerRtl, nodestroy.} =
|
||||||
|
|
@ -484,15 +487,18 @@ proc reraiseException() {.compilerRtl.} =
|
||||||
if currException == nil:
|
if currException == nil:
|
||||||
sysFatal(ReraiseError, "no exception to reraise")
|
sysFatal(ReraiseError, "no exception to reraise")
|
||||||
else:
|
else:
|
||||||
raiseExceptionAux(currException)
|
when gotoBasedExceptions:
|
||||||
|
inc nimInErrorMode
|
||||||
|
else:
|
||||||
|
raiseExceptionAux(currException)
|
||||||
|
|
||||||
proc writeStackTrace() =
|
proc writeStackTrace() =
|
||||||
when hasSomeStackTrace:
|
when hasSomeStackTrace:
|
||||||
var s = ""
|
var s = ""
|
||||||
rawWriteStackTrace(s)
|
rawWriteStackTrace(s)
|
||||||
cast[proc (s: cstring) {.noSideEffect, tags: [], nimcall.}](showErrorMessage)(s)
|
cast[proc (s: cstring) {.noSideEffect, tags: [], nimcall, raises: [].}](showErrorMessage)(s)
|
||||||
else:
|
else:
|
||||||
cast[proc (s: cstring) {.noSideEffect, tags: [], nimcall.}](showErrorMessage)("No stack traceback available\n")
|
cast[proc (s: cstring) {.noSideEffect, tags: [], nimcall, raises: [].}](showErrorMessage)("No stack traceback available\n")
|
||||||
|
|
||||||
proc getStackTrace(): string =
|
proc getStackTrace(): string =
|
||||||
when hasSomeStackTrace:
|
when hasSomeStackTrace:
|
||||||
|
|
@ -529,9 +535,9 @@ proc callDepthLimitReached() {.noinline.} =
|
||||||
$nimCallDepthLimit & " function calls). You can change it with " &
|
$nimCallDepthLimit & " function calls). You can change it with " &
|
||||||
"-d:nimCallDepthLimit=<int> but really try to avoid deep " &
|
"-d:nimCallDepthLimit=<int> but really try to avoid deep " &
|
||||||
"recursions instead.\n")
|
"recursions instead.\n")
|
||||||
quitOrDebug()
|
quit(1)
|
||||||
|
|
||||||
proc nimFrame(s: PFrame) {.compilerRtl, inl.} =
|
proc nimFrame(s: PFrame) {.compilerRtl, inl, raises: [].} =
|
||||||
s.calldepth = if framePtr == nil: 0 else: framePtr.calldepth+1
|
s.calldepth = if framePtr == nil: 0 else: framePtr.calldepth+1
|
||||||
s.prev = framePtr
|
s.prev = framePtr
|
||||||
framePtr = s
|
framePtr = s
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,19 @@
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Nim's Runtime Library
|
||||||
|
# (c) Copyright 2019 Andreas Rumpf
|
||||||
|
#
|
||||||
|
# See the file "copying.txt", included in this
|
||||||
|
# distribution, for details about the copyright.
|
||||||
|
#
|
||||||
|
|
||||||
{.push profiler: off.}
|
{.push profiler: off.}
|
||||||
|
|
||||||
|
when defined(nimHasExceptionsQuery):
|
||||||
|
const gotoBasedExceptions = compileOption("exceptions", "goto")
|
||||||
|
else:
|
||||||
|
const gotoBasedExceptions = false
|
||||||
|
|
||||||
when hostOS == "standalone":
|
when hostOS == "standalone":
|
||||||
include "$projectpath/panicoverride"
|
include "$projectpath/panicoverride"
|
||||||
|
|
||||||
|
|
@ -9,19 +24,20 @@ when hostOS == "standalone":
|
||||||
rawoutput(message)
|
rawoutput(message)
|
||||||
panic(arg)
|
panic(arg)
|
||||||
|
|
||||||
elif defined(nimQuirky) and not defined(nimscript):
|
elif (defined(nimQuirky) or gotoBasedExceptions) and not defined(nimscript):
|
||||||
import ansi_c
|
import ansi_c
|
||||||
|
|
||||||
proc name(t: typedesc): string {.magic: "TypeTrait".}
|
proc name(t: typedesc): string {.magic: "TypeTrait".}
|
||||||
|
|
||||||
proc sysFatal(exceptn: typedesc, message, arg: string) {.inline, noreturn.} =
|
proc sysFatal(exceptn: typedesc, message, arg: string) {.inline, noreturn.} =
|
||||||
|
writeStackTrace()
|
||||||
var buf = newStringOfCap(200)
|
var buf = newStringOfCap(200)
|
||||||
add(buf, "Error: unhandled exception: ")
|
add(buf, "Error: unhandled exception: ")
|
||||||
add(buf, message)
|
add(buf, message)
|
||||||
add(buf, arg)
|
add(buf, arg)
|
||||||
add(buf, " [")
|
add(buf, " [")
|
||||||
add(buf, name exceptn)
|
add(buf, name exceptn)
|
||||||
add(buf, "]")
|
add(buf, "]\n")
|
||||||
cstderr.rawWrite buf
|
cstderr.rawWrite buf
|
||||||
quit 1
|
quit 1
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,7 @@ proc nimRawDispose(p: pointer) {.compilerRtl.} =
|
||||||
template dispose*[T](x: owned(ref T)) = nimRawDispose(cast[pointer](x))
|
template dispose*[T](x: owned(ref T)) = nimRawDispose(cast[pointer](x))
|
||||||
#proc dispose*(x: pointer) = nimRawDispose(x)
|
#proc dispose*(x: pointer) = nimRawDispose(x)
|
||||||
|
|
||||||
proc nimDestroyAndDispose(p: pointer) {.compilerRtl.} =
|
proc nimDestroyAndDispose(p: pointer) {.compilerRtl, raises: [].} =
|
||||||
let d = cast[ptr PNimType](p)[].destructor
|
let d = cast[ptr PNimType](p)[].destructor
|
||||||
if d != nil: cast[DestructorProc](d)(p)
|
if d != nil: cast[DestructorProc](d)(p)
|
||||||
when false:
|
when false:
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ tassert_c.nim(35) tassert_c
|
||||||
tassert_c.nim(34) foo
|
tassert_c.nim(34) foo
|
||||||
assertions.nim(27) failedAssertImpl
|
assertions.nim(27) failedAssertImpl
|
||||||
assertions.nim(20) raiseAssert
|
assertions.nim(20) raiseAssert
|
||||||
fatal.nim(39) sysFatal"""
|
fatal.nim(55) sysFatal"""
|
||||||
|
|
||||||
proc tmatch(x, p: string): bool =
|
proc tmatch(x, p: string): bool =
|
||||||
var i = 0
|
var i = 0
|
||||||
|
|
|
||||||
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")
|
||||||
|
|
@ -1,5 +1,39 @@
|
||||||
discard """
|
discard """
|
||||||
output: "B1\nA1\n1\nB1\nB2\ncatch\nA1\n1\nB1\nA1\nA2\n2\nB1\nB2\ncatch\nA1\nA2\n0\nB1\nA1\n1\nB1\nB2\nA1\n1\nB1\nA1\nA2\n2\nB1\nB2\nA1\nA2\n3"
|
output: '''
|
||||||
|
B1
|
||||||
|
A1
|
||||||
|
1
|
||||||
|
B1
|
||||||
|
B2
|
||||||
|
catch
|
||||||
|
A1
|
||||||
|
1
|
||||||
|
B1
|
||||||
|
A1
|
||||||
|
A2
|
||||||
|
2
|
||||||
|
B1
|
||||||
|
B2
|
||||||
|
catch
|
||||||
|
A1
|
||||||
|
A2
|
||||||
|
0
|
||||||
|
B1
|
||||||
|
A1
|
||||||
|
1
|
||||||
|
B1
|
||||||
|
B2
|
||||||
|
A1
|
||||||
|
1
|
||||||
|
B1
|
||||||
|
A1
|
||||||
|
A2
|
||||||
|
2
|
||||||
|
B1
|
||||||
|
B2
|
||||||
|
A1
|
||||||
|
A2
|
||||||
|
3'''
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# More thorough test of return-in-finaly
|
# More thorough test of return-in-finaly
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue