mirror of
https://github.com/nim-lang/Nim.git
synced 2026-01-22 20:40:44 +00:00
catchable defects (#13626)
* allow defects to be caught even for --exceptions:goto (WIP) * implemented the new --panics:on|off switch; refs https://github.com/nim-lang/RFCs/issues/180 * new implementation for integer overflow checking * produce a warning if a user-defined exception type inherits from Exception directly * applied Timothee's suggestions; improved the documentation and replace the term 'checked runtime check' by 'panic' * fixes #13627 * don't inherit from Exception directly
This commit is contained in:
@@ -161,6 +161,14 @@ echo f
|
||||
- The compiler now inferes "sink parameters". To disable this for a specific routine,
|
||||
annotate it with `.nosinks`. To disable it for a section of code, use
|
||||
`{.push sinkInference: off.}`...`{.pop.}`.
|
||||
- The compiler now supports a new switch `--panics:on` that turns runtime
|
||||
errors like `IndexError` or `OverflowError` into fatal errors that **cannot**
|
||||
be caught via Nim's `try` statement. `--panics:on` can improve the
|
||||
runtime efficiency and code size of your program significantly.
|
||||
- The compiler now warns about inheriting directly from `system.Exception` as
|
||||
this is **very bad** style. You should inherit from `ValueError`, `IOError`,
|
||||
`OSError` or from a different specific exception type that inherits from
|
||||
`CatchableError` and cannot be confused with a `Defect`.
|
||||
|
||||
|
||||
## Bugfixes
|
||||
|
||||
@@ -229,7 +229,7 @@ type
|
||||
TNodeKinds* = set[TNodeKind]
|
||||
|
||||
type
|
||||
TSymFlag* = enum # 41 flags!
|
||||
TSymFlag* = enum # 42 flags!
|
||||
sfUsed, # read access of sym (for warnings) or simply used
|
||||
sfExported, # symbol is exported from module
|
||||
sfFromGeneric, # symbol is instantiation of a generic; this is needed
|
||||
@@ -289,6 +289,8 @@ type
|
||||
sfTemplateParam # symbol is a template parameter
|
||||
sfCursor # variable/field is a cursor, see RFC 177 for details
|
||||
sfInjectDestructors # whether the proc needs the 'injectdestructors' transformation
|
||||
sfAlwaysReturn # proc can never raise an exception, not even OverflowError
|
||||
# or out-of-memory
|
||||
|
||||
TSymFlags* = set[TSymFlag]
|
||||
|
||||
|
||||
@@ -557,6 +557,18 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
|
||||
pl.add(~"];$n")
|
||||
line(p, cpsStmts, pl)
|
||||
|
||||
proc canRaiseDisp(p: BProc; n: PNode): bool =
|
||||
# we assume things like sysFatal cannot raise themselves
|
||||
if n.kind == nkSym and sfAlwaysReturn in n.sym.flags:
|
||||
result = false
|
||||
elif optPanics in p.config.globalOptions or
|
||||
(n.kind == nkSym and sfSystemModule in getModule(n.sym).flags):
|
||||
# we know we can be strict:
|
||||
result = canRaise(n)
|
||||
else:
|
||||
# we have to be *very* conservative:
|
||||
result = canRaiseConservative(n)
|
||||
|
||||
proc genCall(p: BProc, e: PNode, d: var TLoc) =
|
||||
if e[0].typ.skipTypes({tyGenericInst, tyAlias, tySink, tyOwned}).callConv == ccClosure:
|
||||
genClosureCall(p, nil, e, d)
|
||||
@@ -567,7 +579,7 @@ proc genCall(p: BProc, e: PNode, d: var TLoc) =
|
||||
else:
|
||||
genPrefixCall(p, nil, e, d)
|
||||
postStmtActions(p)
|
||||
if p.config.exc == excGoto and canRaise(e[0]):
|
||||
if p.config.exc == excGoto and canRaiseDisp(p, e[0]):
|
||||
raiseExit(p)
|
||||
|
||||
proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
@@ -580,5 +592,5 @@ proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
else:
|
||||
genPrefixCall(p, le, ri, d)
|
||||
postStmtActions(p)
|
||||
if p.config.exc == excGoto and canRaise(ri[0]):
|
||||
if p.config.exc == excGoto and canRaiseDisp(p, ri[0]):
|
||||
raiseExit(p)
|
||||
|
||||
@@ -506,23 +506,25 @@ template binaryArithOverflowRaw(p: BProc, t: PType, a, b: TLoc;
|
||||
else: getTypeDesc(p.module, t)
|
||||
var result = getTempName(p.module)
|
||||
linefmt(p, cpsLocals, "$1 $2;$n", [storage, result])
|
||||
lineCg(p, cpsStmts, "$1 = #$2($3, $4);$n", [result, cpname, rdCharLoc(a), rdCharLoc(b)])
|
||||
lineCg(p, cpsStmts, "if (#$2($3, $4, &$1)) { #raiseOverflow(); $5};$n",
|
||||
[result, cpname, rdCharLoc(a), rdCharLoc(b), raiseInstr(p)])
|
||||
if size < p.config.target.intSize or t.kind in {tyRange, tyEnum}:
|
||||
linefmt(p, cpsStmts, "if ($1 < $2 || $1 > $3) #raiseOverflow();$n",
|
||||
[result, intLiteral(firstOrd(p.config, t)), intLiteral(lastOrd(p.config, t))])
|
||||
linefmt(p, cpsStmts, "if ($1 < $2 || $1 > $3){ #raiseOverflow(); $4}$n",
|
||||
[result, intLiteral(firstOrd(p.config, t)), intLiteral(lastOrd(p.config, t)),
|
||||
raiseInstr(p)])
|
||||
result
|
||||
|
||||
proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
|
||||
const
|
||||
prc: array[mAddI..mPred, string] = [
|
||||
"addInt", "subInt",
|
||||
"mulInt", "divInt", "modInt",
|
||||
"addInt", "subInt"
|
||||
"nimAddInt", "nimSubInt",
|
||||
"nimMulInt", "nimDivInt", "nimModInt",
|
||||
"nimAddInt", "nimSubInt"
|
||||
]
|
||||
prc64: array[mAddI..mPred, string] = [
|
||||
"addInt64", "subInt64",
|
||||
"mulInt64", "divInt64", "modInt64",
|
||||
"addInt64", "subInt64"
|
||||
"nimAddInt64", "nimSubInt64",
|
||||
"nimMulInt64", "nimDivInt64", "nimModInt64",
|
||||
"nimAddInt64", "nimSubInt64"
|
||||
]
|
||||
opr: array[mAddI..mPred, string] = ["+", "-", "*", "/", "%", "+", "-"]
|
||||
var a, b: TLoc
|
||||
@@ -537,6 +539,12 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
|
||||
let res = "($1)($2 $3 $4)" % [getTypeDesc(p.module, e.typ), rdLoc(a), rope(opr[m]), rdLoc(b)]
|
||||
putIntoDest(p, d, e, res)
|
||||
else:
|
||||
# we handle div by zero here so that we know that the compilerproc's
|
||||
# result is only for overflows.
|
||||
if m in {mDivI, mModI}:
|
||||
linefmt(p, cpsStmts, "if ($1 == 0){ #raiseDivByZero(); $2}$n",
|
||||
[rdLoc(b), raiseInstr(p)])
|
||||
|
||||
let res = binaryArithOverflowRaw(p, t, a, b,
|
||||
if t.kind == tyInt64: prc64[m] else: prc[m])
|
||||
putIntoDest(p, d, e, "($#)($#)" % [getTypeDesc(p.module, e.typ), res])
|
||||
@@ -549,8 +557,8 @@ proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
|
||||
initLocExpr(p, e[1], a)
|
||||
t = skipTypes(e.typ, abstractRange)
|
||||
if optOverflowCheck in p.options:
|
||||
linefmt(p, cpsStmts, "if ($1 == $2) #raiseOverflow();$n",
|
||||
[rdLoc(a), intLiteral(firstOrd(p.config, t))])
|
||||
linefmt(p, cpsStmts, "if ($1 == $2){ #raiseOverflow(); $3}$n",
|
||||
[rdLoc(a), intLiteral(firstOrd(p.config, t)), raiseInstr(p)])
|
||||
case m
|
||||
of mUnaryMinusI:
|
||||
putIntoDest(p, d, e, "((NI$2)-($1))" % [rdLoc(a), rope(getSize(p.config, t) * 8)])
|
||||
@@ -817,12 +825,12 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) =
|
||||
let strLit = genStringLiteral(p.module, newStrNode(nkStrLit, msg))
|
||||
if op.magic == mNot:
|
||||
linefmt(p, cpsStmts,
|
||||
"if ($1) #raiseFieldError($2);$n",
|
||||
[rdLoc(test), strLit])
|
||||
"if ($1){ #raiseFieldError($2); $3}$n",
|
||||
[rdLoc(test), strLit, raiseInstr(p)])
|
||||
else:
|
||||
linefmt(p, cpsStmts,
|
||||
"if (!($1)) #raiseFieldError($2);$n",
|
||||
[rdLoc(test), strLit])
|
||||
"if (!($1)){ #raiseFieldError($2); $3}$n",
|
||||
[rdLoc(test), strLit, raiseInstr(p)])
|
||||
|
||||
proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) =
|
||||
if optFieldCheck in p.options:
|
||||
@@ -861,11 +869,11 @@ proc genArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) =
|
||||
# semantic pass has already checked for const index expressions
|
||||
if firstOrd(p.config, ty) == 0:
|
||||
if (firstOrd(p.config, b.t) < firstOrd(p.config, ty)) or (lastOrd(p.config, b.t) > lastOrd(p.config, ty)):
|
||||
linefmt(p, cpsStmts, "if ((NU)($1) > (NU)($2)) #raiseIndexError2($1, $2);$n",
|
||||
[rdCharLoc(b), intLiteral(lastOrd(p.config, ty))])
|
||||
linefmt(p, cpsStmts, "if ((NU)($1) > (NU)($2)){ #raiseIndexError2($1, $2); $3}$n",
|
||||
[rdCharLoc(b), intLiteral(lastOrd(p.config, ty)), raiseInstr(p)])
|
||||
else:
|
||||
linefmt(p, cpsStmts, "if ($1 < $2 || $1 > $3) #raiseIndexError3($1, $2, $3);$n",
|
||||
[rdCharLoc(b), first, intLiteral(lastOrd(p.config, ty))])
|
||||
linefmt(p, cpsStmts, "if ($1 < $2 || $1 > $3){ #raiseIndexError3($1, $2, $3); $4}$n",
|
||||
[rdCharLoc(b), first, intLiteral(lastOrd(p.config, ty)), raiseInstr(p)])
|
||||
else:
|
||||
let idx = getOrdValue(y)
|
||||
if idx < firstOrd(p.config, ty) or idx > lastOrd(p.config, ty):
|
||||
@@ -888,19 +896,19 @@ proc genBoundsCheck(p: BProc; arr, a, b: TLoc) =
|
||||
of tyOpenArray, tyVarargs:
|
||||
linefmt(p, cpsStmts,
|
||||
"if ($2-$1 != -1 && " &
|
||||
"((NU)($1) >= (NU)($3Len_0) || (NU)($2) >= (NU)($3Len_0))) #raiseIndexError();$n",
|
||||
[rdLoc(a), rdLoc(b), rdLoc(arr)])
|
||||
"((NU)($1) >= (NU)($3Len_0) || (NU)($2) >= (NU)($3Len_0))){ #raiseIndexError(); $4}$n",
|
||||
[rdLoc(a), rdLoc(b), rdLoc(arr), raiseInstr(p)])
|
||||
of tyArray:
|
||||
let first = intLiteral(firstOrd(p.config, ty))
|
||||
linefmt(p, cpsStmts,
|
||||
"if ($2-$1 != -1 && " &
|
||||
"($2-$1 < -1 || $1 < $3 || $1 > $4 || $2 < $3 || $2 > $4)) #raiseIndexError();$n",
|
||||
[rdCharLoc(a), rdCharLoc(b), first, intLiteral(lastOrd(p.config, ty))])
|
||||
"($2-$1 < -1 || $1 < $3 || $1 > $4 || $2 < $3 || $2 > $4)){ #raiseIndexError(); $5}$n",
|
||||
[rdCharLoc(a), rdCharLoc(b), first, intLiteral(lastOrd(p.config, ty)), raiseInstr(p)])
|
||||
of tySequence, tyString:
|
||||
linefmt(p, cpsStmts,
|
||||
"if ($2-$1 != -1 && " &
|
||||
"((NU)($1) >= (NU)$3 || (NU)($2) >= (NU)$3)) #raiseIndexError();$n",
|
||||
[rdLoc(a), rdLoc(b), lenExpr(p, arr)])
|
||||
"((NU)($1) >= (NU)$3 || (NU)($2) >= (NU)$3)){ #raiseIndexError(); $4}$n",
|
||||
[rdLoc(a), rdLoc(b), lenExpr(p, arr), raiseInstr(p)])
|
||||
else: discard
|
||||
|
||||
proc genOpenArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) =
|
||||
@@ -908,8 +916,8 @@ proc genOpenArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) =
|
||||
initLocExpr(p, x, a)
|
||||
initLocExpr(p, y, b) # emit range check:
|
||||
if optBoundsCheck in p.options:
|
||||
linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)($2Len_0)) #raiseIndexError2($1,$2Len_0-1);$n",
|
||||
[rdLoc(b), rdLoc(a)]) # BUGFIX: ``>=`` and not ``>``!
|
||||
linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)($2Len_0)){ #raiseIndexError2($1,$2Len_0-1); $3}$n",
|
||||
[rdLoc(b), rdLoc(a), raiseInstr(p)]) # BUGFIX: ``>=`` and not ``>``!
|
||||
inheritLocation(d, a)
|
||||
putIntoDest(p, d, n,
|
||||
ropecg(p.module, "$1[$2]", [rdLoc(a), rdCharLoc(b)]), a.storage)
|
||||
@@ -924,12 +932,12 @@ proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) =
|
||||
if optBoundsCheck in p.options:
|
||||
if ty.kind == tyString and (not defined(nimNoZeroTerminator) or optLaxStrings in p.options):
|
||||
linefmt(p, cpsStmts,
|
||||
"if ((NU)($1) > (NU)$2) #raiseIndexError2($1,$2);$n",
|
||||
[rdLoc(b), lenExpr(p, a)])
|
||||
"if ((NU)($1) > (NU)$2){ #raiseIndexError2($1,$2); $3}$n",
|
||||
[rdLoc(b), lenExpr(p, a), raiseInstr(p)])
|
||||
else:
|
||||
linefmt(p, cpsStmts,
|
||||
"if ((NU)($1) >= (NU)$2) #raiseIndexError2($1,$2-1);$n",
|
||||
[rdLoc(b), lenExpr(p, a)])
|
||||
"if ((NU)($1) >= (NU)$2){ #raiseIndexError2($1,$2-1); $3}$n",
|
||||
[rdLoc(b), lenExpr(p, a), raiseInstr(p)])
|
||||
if d.k == locNone: d.storage = OnHeap
|
||||
if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}:
|
||||
a.r = ropecg(p.module, "(*$1)", [a.r])
|
||||
@@ -1938,21 +1946,33 @@ proc genCast(p: BProc, e: PNode, d: var TLoc) =
|
||||
# C code; plus it's the right thing to do for closures:
|
||||
genSomeCast(p, e, d)
|
||||
|
||||
proc genRangeChck(p: BProc, n: PNode, d: var TLoc, magic: string) =
|
||||
proc genRangeChck(p: BProc, n: PNode, d: var TLoc) =
|
||||
var a: TLoc
|
||||
var dest = skipTypes(n.typ, abstractVar)
|
||||
initLocExpr(p, n[0], a)
|
||||
if optRangeCheck notin p.options or (dest.kind in {tyUInt..tyUInt64} and
|
||||
checkUnsignedConversions notin p.config.legacyFeatures):
|
||||
initLocExpr(p, n[0], a)
|
||||
putIntoDest(p, d, n, "(($1) ($2))" %
|
||||
[getTypeDesc(p.module, dest), rdCharLoc(a)], a.storage)
|
||||
discard "no need to generate a check because it was disabled"
|
||||
else:
|
||||
let mm = if dest.kind in {tyUInt32, tyUInt64, tyUInt}: "chckRangeU" else: magic
|
||||
initLocExpr(p, n[0], a)
|
||||
putIntoDest(p, d, lodeTyp dest, ropecg(p.module, "(($1)#$5($2, $3, $4))", [
|
||||
getTypeDesc(p.module, dest), rdCharLoc(a),
|
||||
genLiteral(p, n[1], dest), genLiteral(p, n[2], dest),
|
||||
mm]), a.storage)
|
||||
let raiser =
|
||||
case skipTypes(n.typ, abstractVarRange).kind
|
||||
of tyUInt..tyUInt64, tyChar: "raiseRangeErrorU"
|
||||
of tyFloat..tyFloat128: "raiseRangeErrorF"
|
||||
else: "raiseRangeErrorI"
|
||||
discard cgsym(p.module, raiser)
|
||||
# This seems to be bug-compatible with Nim version 1 but what we
|
||||
# should really do here is to check if uint64Value < high(int)
|
||||
let boundaryCast =
|
||||
if n[0].typ.skipTypes(abstractVarRange).kind in {tyUInt, tyUInt32, tyUInt64}:
|
||||
"(NI64)"
|
||||
else:
|
||||
""
|
||||
# emit range check:
|
||||
linefmt(p, cpsStmts, "if ($6($1) < $2 || $6($1) > $3){ $4($1, $2, $3); $5}$n",
|
||||
[rdCharLoc(a), genLiteral(p, n[1], dest), genLiteral(p, n[2], dest),
|
||||
raiser, raiseInstr(p), boundaryCast])
|
||||
putIntoDest(p, d, n, "(($1) ($2))" %
|
||||
[getTypeDesc(p.module, dest), rdCharLoc(a)], a.storage)
|
||||
|
||||
proc genConv(p: BProc, e: PNode, d: var TLoc) =
|
||||
let destType = e.typ.skipTypes({tyVar, tyLent, tyGenericInst, tyAlias, tySink})
|
||||
@@ -2004,9 +2024,9 @@ proc binaryFloatArith(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
|
||||
[opr[m], rdLoc(a), rdLoc(b),
|
||||
getSimpleTypeDesc(p.module, e[1].typ)]))
|
||||
if optNaNCheck in p.options:
|
||||
linefmt(p, cpsStmts, "#nanCheck($1);$n", [rdLoc(d)])
|
||||
linefmt(p, cpsStmts, "if ($1 != $1){ #raiseFloatInvalidOp(); $2}$n", [rdLoc(d), raiseInstr(p)])
|
||||
if optInfCheck in p.options:
|
||||
linefmt(p, cpsStmts, "#infCheck($1);$n", [rdLoc(d)])
|
||||
linefmt(p, cpsStmts, "if ($1 != 0.0 && $1*0.5 == $1) { #raiseFloatOverflow($1); $2}$n", [rdLoc(d), raiseInstr(p)])
|
||||
else:
|
||||
binaryArith(p, e, d, m)
|
||||
|
||||
@@ -2122,10 +2142,8 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
of mSwap: genSwap(p, e, d)
|
||||
of mInc, mDec:
|
||||
const opr: array[mInc..mDec, string] = ["+=", "-="]
|
||||
const fun64: array[mInc..mDec, string] = ["addInt64",
|
||||
"subInt64"]
|
||||
const fun: array[mInc..mDec, string] = ["addInt",
|
||||
"subInt"]
|
||||
const fun64: array[mInc..mDec, string] = ["nimAddInt64", "nimSubInt64"]
|
||||
const fun: array[mInc..mDec, string] = ["nimAddInt","nimSubInt"]
|
||||
let underlying = skipTypes(e[1].typ, {tyGenericInst, tyAlias, tySink, tyVar, tyLent, tyRange})
|
||||
if optOverflowCheck notin p.options or underlying.kind in {tyUInt..tyUInt64}:
|
||||
binaryStmt(p, e, d, opr[op])
|
||||
@@ -2432,11 +2450,11 @@ proc upConv(p: BProc, n: PNode, d: var TLoc) =
|
||||
else:
|
||||
genTypeInfo(p.module, dest, n.info)
|
||||
if nilCheck != nil:
|
||||
linefmt(p, cpsStmts, "if ($1) #chckObj($2, $3);$n",
|
||||
[nilCheck, r, checkFor])
|
||||
linefmt(p, cpsStmts, "if ($1 && !#isObj($2, $3)){ #raiseObjectConversionError(); $4}$n",
|
||||
[nilCheck, r, checkFor, raiseInstr(p)])
|
||||
else:
|
||||
linefmt(p, cpsStmts, "#chckObj($1, $2);$n",
|
||||
[r, checkFor])
|
||||
linefmt(p, cpsStmts, "if (!#isObj($1, $2)){ #raiseObjectConversionError(); $3}$n",
|
||||
[r, checkFor, raiseInstr(p)])
|
||||
if n[0].typ.kind != tyObject:
|
||||
putIntoDest(p, d, n,
|
||||
"(($1) ($2))" % [getTypeDesc(p.module, n.typ), rdLoc(a)], a.storage)
|
||||
@@ -2629,9 +2647,9 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
|
||||
expr(p, n[1][0], d)
|
||||
of nkObjDownConv: downConv(p, n, d)
|
||||
of nkObjUpConv: upConv(p, n, d)
|
||||
of nkChckRangeF: genRangeChck(p, n, d, "chckRangeF")
|
||||
of nkChckRange64: genRangeChck(p, n, d, "chckRange64")
|
||||
of nkChckRange: genRangeChck(p, n, d, "chckRange")
|
||||
of nkChckRangeF: genRangeChck(p, n, d)
|
||||
of nkChckRange64: genRangeChck(p, n, d)
|
||||
of nkChckRange: genRangeChck(p, n, d)
|
||||
of nkStringToCString: convStrToCStr(p, n, d)
|
||||
of nkCStringToString: convCStrToStr(p, n, d)
|
||||
of nkLambdaKinds:
|
||||
|
||||
@@ -706,6 +706,22 @@ proc finallyActions(p: BProc) =
|
||||
if finallyBlock != nil:
|
||||
genSimpleBlock(p, finallyBlock[0])
|
||||
|
||||
proc raiseInstr(p: BProc): Rope =
|
||||
if p.config.exc == excGoto:
|
||||
let L = p.nestedTryStmts.len
|
||||
if L == 0:
|
||||
p.flags.incl beforeRetNeeded
|
||||
# easy case, simply goto 'ret':
|
||||
result = ropecg(p.module, "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.
|
||||
result = ropecg(p.module, "goto LA$1_;$n",
|
||||
[p.nestedTryStmts[L-1].label])
|
||||
# + ord(p.nestedTryStmts[L-1].inExcept)])
|
||||
else:
|
||||
result = nil
|
||||
|
||||
proc genRaiseStmt(p: BProc, t: PNode) =
|
||||
if p.config.exc == excCpp:
|
||||
discard cgsym(p.module, "popCurrentExceptionEx")
|
||||
@@ -733,18 +749,9 @@ proc genRaiseStmt(p: BProc, t: PNode) =
|
||||
line(p, cpsStmts, ~"throw;$n")
|
||||
else:
|
||||
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)])
|
||||
let gotoInstr = raiseInstr(p)
|
||||
if gotoInstr != nil:
|
||||
line(p, cpsStmts, gotoInstr)
|
||||
|
||||
template genCaseGenericBranch(p: BProc, b: PNode, e: TLoc,
|
||||
rangeFormat, eqFormat: FormatStr, labl: TLabel) =
|
||||
@@ -1009,14 +1016,14 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
|
||||
|
||||
genSimpleBlock(p, t[^1][0])
|
||||
|
||||
proc bodyCanRaise(n: PNode): bool =
|
||||
proc bodyCanRaise(p: BProc; n: PNode): bool =
|
||||
case n.kind
|
||||
of nkCallKinds:
|
||||
result = canRaise(n[0])
|
||||
result = canRaiseDisp(p, n[0])
|
||||
if not result:
|
||||
# also check the arguments:
|
||||
for i in 1 ..< n.len:
|
||||
if bodyCanRaise(n[i]): return true
|
||||
if bodyCanRaise(p, n[i]): return true
|
||||
of nkRaiseStmt:
|
||||
result = true
|
||||
of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef,
|
||||
@@ -1024,22 +1031,24 @@ proc bodyCanRaise(n: PNode): bool =
|
||||
result = false
|
||||
else:
|
||||
for i in 0 ..< safeLen(n):
|
||||
if bodyCanRaise(n[i]): return true
|
||||
if bodyCanRaise(p, n[i]): return true
|
||||
result = false
|
||||
|
||||
proc genTryGoto(p: BProc; t: PNode; d: var TLoc) =
|
||||
if not bodyCanRaise(t):
|
||||
let fin = if t[^1].kind == nkFinally: t[^1] else: nil
|
||||
inc p.labels
|
||||
let lab = p.labels
|
||||
p.nestedTryStmts.add((fin, false, Natural lab))
|
||||
|
||||
if not bodyCanRaise(p, 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])
|
||||
linefmt(p, cpsStmts, "LA$1_: ;$n", [lab])
|
||||
if fin != nil:
|
||||
genStmts(p, fin[0])
|
||||
discard pop(p.nestedTryStmts)
|
||||
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
|
||||
p.procSec(cpsLocals).add(ropecg(p.module, "NI oldNimErr$1_;$n", [lab]))
|
||||
linefmt(p, cpsStmts, "oldNimErr$1_ = *nimErr_; *nimErr_ = 0;;$n", [lab])
|
||||
@@ -1099,7 +1108,7 @@ proc genTryGoto(p: BProc; t: PNode; d: var TLoc) =
|
||||
#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]):
|
||||
if not bodyCanRaise(p, 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])
|
||||
@@ -1380,8 +1389,8 @@ proc asgnFieldDiscriminant(p: BProc, e: PNode) =
|
||||
genCaseObjDiscMapping(p, e[0], t, field, oldVal)
|
||||
genCaseObjDiscMapping(p, e[1], t, field, newVal)
|
||||
lineCg(p, cpsStmts,
|
||||
"#nimFieldDiscriminantCheckV2($1, $2);$n",
|
||||
[rdLoc(oldVal), rdLoc(newVal)])
|
||||
"if ($1 != $2) { #raiseObjectCaseTransition(); $3}$n",
|
||||
[rdLoc(oldVal), rdLoc(newVal), raiseInstr(p)])
|
||||
else:
|
||||
genDiscriminantCheck(p, a, tmp, dotExpr[0].typ, field)
|
||||
genAssignment(p, a, tmp, {})
|
||||
|
||||
@@ -276,6 +276,7 @@ proc postStmtActions(p: BProc) {.inline.} =
|
||||
proc accessThreadLocalVar(p: BProc, s: PSym)
|
||||
proc emulatedThreadVars(conf: ConfigRef): bool {.inline.}
|
||||
proc genProc(m: BModule, prc: PSym)
|
||||
proc raiseInstr(p: BProc): Rope
|
||||
|
||||
template compileToCpp(m: BModule): untyped =
|
||||
m.config.cmd == cmdCompileToCpp or sfCompileToCpp in m.module.flags
|
||||
|
||||
@@ -881,6 +881,10 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
processOnOffSwitchG(conf, {optBenchmarkVM}, arg, pass, info)
|
||||
of "sinkinference":
|
||||
processOnOffSwitch(conf, {optSinkInference}, arg, pass, info)
|
||||
of "panics":
|
||||
processOnOffSwitchG(conf, {optPanics}, arg, pass, info)
|
||||
if optPanics in conf.globalOptions:
|
||||
defineSymbol(conf.symbols, "nimPanics")
|
||||
of "": # comes from "-" in for example: `nim c -r -` (gets stripped from -)
|
||||
handleStdinInput(conf)
|
||||
else:
|
||||
|
||||
@@ -113,3 +113,4 @@ proc initDefines*(symbols: StringTableRef) =
|
||||
defineSymbol("nimHasLibFFIEnabled")
|
||||
|
||||
defineSymbol("nimHasSinkInference")
|
||||
defineSymbol("nimNewIntegerOps")
|
||||
|
||||
@@ -40,6 +40,7 @@ proc genEnumToStrProc*(t: PType; info: TLineInfo; g: ModuleGraph): PSym =
|
||||
n[resultPos] = newSymNode(res)
|
||||
result.ast = n
|
||||
incl result.flags, sfFromGeneric
|
||||
incl result.flags, sfAlwaysReturn
|
||||
|
||||
proc searchObjCaseImpl(obj: PNode; field: PSym): PNode =
|
||||
case obj.kind
|
||||
@@ -101,3 +102,4 @@ proc genCaseObjDiscMapping*(t: PType; field: PSym; info: TLineInfo; g: ModuleGra
|
||||
n[resultPos] = newSymNode(res)
|
||||
result.ast = n
|
||||
incl result.flags, sfFromGeneric
|
||||
incl result.flags, sfAlwaysReturn
|
||||
|
||||
@@ -34,6 +34,7 @@ type
|
||||
warnTypelessParam,
|
||||
warnUseBase, warnWriteToForeignHeap, warnUnsafeCode,
|
||||
warnUnusedImportX,
|
||||
warnInheritFromException,
|
||||
warnEachIdentIsTuple,
|
||||
warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2,
|
||||
warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed,
|
||||
@@ -81,6 +82,7 @@ const
|
||||
warnWriteToForeignHeap: "write to foreign heap",
|
||||
warnUnsafeCode: "unsafe code: '$1'",
|
||||
warnUnusedImportX: "imported and not used: '$1'",
|
||||
warnInheritFromException: "inherit from a more precise exception type like ValueError, IOError or OSError",
|
||||
warnEachIdentIsTuple: "each identifier is a tuple",
|
||||
warnProveInit: "Cannot prove that '$1' is initialized. This will become a compile time error in the future.",
|
||||
warnProveField: "cannot prove that field '$1' is accessible",
|
||||
@@ -138,7 +140,8 @@ const
|
||||
"LanguageXNotSupported", "FieldXNotSupported",
|
||||
"CommentXIgnored",
|
||||
"TypelessParam", "UseBase", "WriteToForeignHeap",
|
||||
"UnsafeCode", "UnusedImport", "EachIdentIsTuple",
|
||||
"UnsafeCode", "UnusedImport", "InheritFromException",
|
||||
"EachIdentIsTuple",
|
||||
"ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit",
|
||||
"GcMem", "Destructor", "LockLevel", "ResultShadowed",
|
||||
"Spacing", "CaseTransition", "CycleCreated", "User"]
|
||||
@@ -227,7 +230,7 @@ type
|
||||
TErrorOutputs* = set[TErrorOutput]
|
||||
|
||||
ERecoverableError* = object of ValueError
|
||||
ESuggestDone* = object of Exception
|
||||
ESuggestDone* = object of ValueError
|
||||
|
||||
proc `==`*(a, b: FileIndex): bool {.borrow.}
|
||||
|
||||
|
||||
@@ -165,6 +165,7 @@ proc stopCompile*(g: ModuleGraph): bool {.inline.} =
|
||||
proc createMagic*(g: ModuleGraph; name: string, m: TMagic): PSym =
|
||||
result = newSym(skProc, getIdent(g.cache, name), nil, unknownLineInfo, {})
|
||||
result.magic = m
|
||||
result.flags = {sfAlwaysReturn}
|
||||
|
||||
proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph =
|
||||
result = ModuleGraph()
|
||||
|
||||
@@ -90,6 +90,7 @@ type # please make sure we have under 32 options
|
||||
optNimV019
|
||||
optBenchmarkVM # Enables cpuTime() in the VM
|
||||
optProduceAsm # produce assembler code
|
||||
optPanics # turn panics (sysFatal) into a process termination
|
||||
|
||||
TGlobalOptions* = set[TGlobalOption]
|
||||
|
||||
@@ -509,15 +510,17 @@ proc getOutFile*(conf: ConfigRef; filename: RelativeFile, ext: string): Absolute
|
||||
|
||||
proc absOutFile*(conf: ConfigRef): AbsoluteFile =
|
||||
result = conf.outDir / conf.outFile
|
||||
if dirExists(result.string):
|
||||
result.string.add ".out"
|
||||
when defined(posix):
|
||||
if dirExists(result.string):
|
||||
result.string.add ".out"
|
||||
|
||||
proc prepareToWriteOutput*(conf: ConfigRef): AbsoluteFile =
|
||||
## Create the output directory and returns a full path to the output file
|
||||
createDir conf.outDir
|
||||
result = conf.outDir / conf.outFile
|
||||
if dirExists(result.string):
|
||||
result.string.add ".out"
|
||||
when defined(posix):
|
||||
if dirExists(result.string):
|
||||
result.string.add ".out"
|
||||
|
||||
proc getPrefixDir*(conf: ConfigRef): AbsoluteDir =
|
||||
## Gets the prefix dir, usually the parent directory where the binary resides.
|
||||
|
||||
@@ -1283,7 +1283,7 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
|
||||
while x.kind in {nkStmtList, nkStmtListExpr} and x.len > 0:
|
||||
x = x.lastSon
|
||||
if x.kind notin {nkObjectTy, nkDistinctTy, nkEnumTy, nkEmpty} and
|
||||
s.typ.kind notin {tyObject, tyEnum}:
|
||||
s.typ.skipTypes(abstractPtrs-{tyAlias}).kind notin {tyObject, tyEnum}:
|
||||
# type aliases are hard:
|
||||
var t = semTypeNode(c, x, nil)
|
||||
assert t != nil
|
||||
|
||||
@@ -847,6 +847,9 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType; isInheritable: bool): PTy
|
||||
# specialized object, there will be second check after instantiation
|
||||
# located in semGeneric.
|
||||
if concreteBase.kind == tyObject:
|
||||
if concreteBase.sym != nil and concreteBase.sym.magic == mException and
|
||||
sfSystemModule notin c.module.flags:
|
||||
message(c.config, n.info, warnInheritFromException, "")
|
||||
addInheritedFields(c, check, pos, concreteBase)
|
||||
else:
|
||||
if concreteBase.kind != tyError:
|
||||
|
||||
@@ -23,7 +23,7 @@ const
|
||||
szUncomputedSize* = -1
|
||||
szTooBigSize* = -4
|
||||
|
||||
type IllegalTypeRecursionError = object of Exception
|
||||
type IllegalTypeRecursionError = object of ValueError
|
||||
|
||||
proc raiseIllegalTypeRecursion() =
|
||||
raise newException(IllegalTypeRecursionError, "illegal type recursion")
|
||||
|
||||
@@ -146,3 +146,4 @@ Advanced options:
|
||||
see also https://nim-lang.github.io/Nim/estp.html
|
||||
--benchmarkVM:on|off enable benchmarking of VM code with cpuTime()
|
||||
--sinkInference:on|off en-/disable sink parameter inference (default: on)
|
||||
--panics:on|off turn panics into process terminations (default: off)
|
||||
|
||||
@@ -105,13 +105,13 @@ identifier meanings, and in some cases expression values. An error detected
|
||||
during semantic analysis is called a `static error`:idx:. Errors described in
|
||||
this manual are static errors when not otherwise specified.
|
||||
|
||||
A `checked runtime error`:idx: is an error that the implementation detects
|
||||
A `panic`:idx: is an error that the implementation detects
|
||||
and reports at runtime. The method for reporting such errors is via
|
||||
*raising exceptions* or *dying with a fatal error*. However, the implementation
|
||||
provides a means to disable these `runtime checks`:idx:. See the section
|
||||
pragmas_ for details.
|
||||
|
||||
Whether a checked runtime error results in an exception or in a fatal error is
|
||||
Whether a panic results in an exception or in a fatal error is
|
||||
implementation specific. Thus the following program is invalid; even though the
|
||||
code purports to catch the `IndexError` from an out-of-bounds array access, the
|
||||
compiler may instead choose to allow the program to die with a fatal error.
|
||||
@@ -124,6 +124,12 @@ compiler may instead choose to allow the program to die with a fatal error.
|
||||
except IndexError:
|
||||
echo "invalid index"
|
||||
|
||||
The current implementation allows to switch between these different behaviors
|
||||
via ``--panics:on|off``. When panics are turned on, the program dies on a
|
||||
panic, if they are turned off the runtime errors are turned into
|
||||
exceptions. The benefit of ``--panics:on`` is that it produces smaller binary
|
||||
code and the compiler has more freedom to optimize the code.
|
||||
|
||||
An `unchecked runtime error`:idx: is an error that is not guaranteed to be
|
||||
detected, and can cause the subsequent behavior of the computation to
|
||||
be arbitrary. Unchecked runtime errors cannot occur if only `safe`:idx:
|
||||
@@ -869,9 +875,9 @@ Ordinal types have the following characteristics:
|
||||
the operation of functions as ``inc``, ``ord``, ``dec`` on ordinal types to
|
||||
be defined.
|
||||
- Ordinal values have a smallest possible value. Trying to count further
|
||||
down than the smallest value gives a checked runtime or static error.
|
||||
down than the smallest value produces a panic or a static error.
|
||||
- Ordinal values have a largest possible value. Trying to count further
|
||||
than the largest value gives a checked runtime or static error.
|
||||
than the largest value produces a panic or a static error.
|
||||
|
||||
Integers, bool, characters and enumeration types (and subranges of these
|
||||
types) belong to ordinal types. For reasons of simplicity of implementation
|
||||
@@ -982,7 +988,7 @@ lowest and highest value of the type. For example:
|
||||
to 5. ``PositiveFloat`` defines a subrange of all positive floating point values.
|
||||
NaN does not belong to any subrange of floating point types.
|
||||
Assigning any other value to a variable of type ``Subrange`` is a
|
||||
checked runtime error (or static error if it can be determined during
|
||||
panic (or a static error if it can be determined during
|
||||
semantic analysis). Assignments from the base type to one of its subrange types
|
||||
(and vice versa) are allowed.
|
||||
|
||||
@@ -1763,9 +1769,9 @@ Nil
|
||||
|
||||
If a reference points to *nothing*, it has the value ``nil``. ``nil`` is also
|
||||
the default value for all ``ref`` and ``ptr`` types. Dereferencing ``nil``
|
||||
is an unrecoverable fatal runtime error. A dereferencing operation ``p[]``
|
||||
implies that ``p`` is not nil. This can be exploited by the implementation to
|
||||
optimize code like:
|
||||
is an unrecoverable fatal runtime error (and not a panic).
|
||||
A dereferencing operation ``p[]`` implies that ``p`` is not nil. This can be
|
||||
exploited by the implementation to optimize code like:
|
||||
|
||||
|
||||
.. code-block:: nim
|
||||
@@ -4107,7 +4113,7 @@ branch always has to be ``void``:
|
||||
|
||||
.. code-block:: nim
|
||||
from strutils import parseInt
|
||||
|
||||
|
||||
let x = try: parseInt("133a")
|
||||
except: -1
|
||||
finally: echo "hi"
|
||||
@@ -4242,9 +4248,11 @@ The exception tree is defined in the `system <system.html>`_ module.
|
||||
Every exception inherits from ``system.Exception``. Exceptions that indicate
|
||||
programming bugs inherit from ``system.Defect`` (which is a subtype of ``Exception``)
|
||||
and are stricly speaking not catchable as they can also be mapped to an operation
|
||||
that terminates the whole process. Exceptions that indicate any other runtime
|
||||
error that can be caught inherit from ``system.CatchableError``
|
||||
(which is a subtype of ``Exception``).
|
||||
that terminates the whole process. If panics are turned into exceptions, these
|
||||
exceptions inherit from `Defect`.
|
||||
|
||||
Exceptions that indicate any other runtime error that can be caught inherit from
|
||||
``system.CatchableError`` (which is a subtype of ``Exception``).
|
||||
|
||||
|
||||
Imported exceptions
|
||||
@@ -5667,7 +5675,7 @@ The ``include`` statement can be used outside of the top level, as such:
|
||||
# Module B
|
||||
proc main() =
|
||||
include A
|
||||
|
||||
|
||||
main() # => Hello World!
|
||||
|
||||
|
||||
|
||||
2
koch.nim
2
koch.nim
@@ -475,7 +475,7 @@ proc hostInfo(): string =
|
||||
|
||||
proc runCI(cmd: string) =
|
||||
doAssert cmd.len == 0, cmd # avoid silently ignoring
|
||||
echo "runCI:", cmd
|
||||
echo "runCI: ", cmd
|
||||
echo hostInfo()
|
||||
# note(@araq): Do not replace these commands with direct calls (eg boot())
|
||||
# as that would weaken our testing efforts.
|
||||
|
||||
@@ -545,4 +545,26 @@ typedef int Nim_and_C_compiler_disagree_on_target_architecture[sizeof(NI) == siz
|
||||
#define NIM_CHECK_SIZE(typ, sz) \
|
||||
_Static_assert(sizeof(typ) == sz, "Nim & C disagree on type size")
|
||||
|
||||
/* these exist to make the codegen logic simpler */
|
||||
#define nimModInt(a, b, res) (((*res) = (a) % (b)), 0)
|
||||
#define nimModInt64(a, b, res) (((*res) = (a) % (b)), 0)
|
||||
|
||||
/* these exist because we cannot have .compilerProcs that are importc'ed
|
||||
by a different name */
|
||||
|
||||
#define nimAddInt64(a, b, res) __builtin_saddll_overflow(a, b, (long long int*)res)
|
||||
#define nimSubInt64(a, b, res) __builtin_ssubll_overflow(a, b, (long long int*)res)
|
||||
#define nimMulInt64(a, b, res) __builtin_smulll_overflow(a, b, (long long int*)res)
|
||||
|
||||
#if NIM_INTBITS == 32
|
||||
#define nimAddInt(a, b, res) __builtin_sadd_overflow(a, b, res)
|
||||
#define nimSubInt(a, b, res) __builtin_ssub_overflow(a, b, res)
|
||||
#define nimMulInt(a, b, res) __builtin_smul_overflow(a, b, res)
|
||||
#else
|
||||
/* map it to the 'long long' variant */
|
||||
#define nimAddInt(a, b, res) __builtin_saddll_overflow(a, b, (long long int*)res)
|
||||
#define nimSubInt(a, b, res) __builtin_ssubll_overflow(a, b, (long long int*)res)
|
||||
#define nimMulInt(a, b, res) __builtin_smulll_overflow(a, b, (long long int*)res)
|
||||
#endif
|
||||
|
||||
#endif /* NIMBASE_H */
|
||||
|
||||
@@ -2221,7 +2221,10 @@ when notJSnotNims:
|
||||
|
||||
# we cannot compile this with stack tracing on
|
||||
# as it would recurse endlessly!
|
||||
include "system/arithm"
|
||||
when defined(nimNewIntegerOps):
|
||||
include "system/integerops"
|
||||
else:
|
||||
include "system/arithm"
|
||||
{.pop.}
|
||||
|
||||
|
||||
|
||||
@@ -409,13 +409,13 @@ when not declared(mulInt):
|
||||
# We avoid setting the FPU control word here for compatibility with libraries
|
||||
# written in other languages.
|
||||
|
||||
proc raiseFloatInvalidOp {.noinline.} =
|
||||
proc raiseFloatInvalidOp {.compilerproc, noinline.} =
|
||||
sysFatal(FloatInvalidOpError, "FPU operation caused a NaN result")
|
||||
|
||||
proc nanCheck(x: float64) {.compilerproc, inline.} =
|
||||
if x != x: raiseFloatInvalidOp()
|
||||
|
||||
proc raiseFloatOverflow(x: float64) {.noinline.} =
|
||||
proc raiseFloatOverflow(x: float64) {.compilerproc, noinline.} =
|
||||
if x > 0.0:
|
||||
sysFatal(FloatOverflowError, "FPU operation caused an overflow")
|
||||
else:
|
||||
|
||||
@@ -28,6 +28,19 @@ proc raiseIndexError() {.compilerproc, noinline.} =
|
||||
proc raiseFieldError(f: string) {.compilerproc, noinline.} =
|
||||
sysFatal(FieldError, f)
|
||||
|
||||
proc raiseRangeErrorI(i, a, b: BiggestInt) {.compilerproc, noinline.} =
|
||||
sysFatal(RangeError, "value out of range: " & $i & " notin " & $a & " .. " & $b)
|
||||
|
||||
proc raiseRangeErrorF(i, a, b: float) {.compilerproc, noinline.} =
|
||||
sysFatal(RangeError, "value out of range: " & $i & " notin " & $a & " .. " & $b)
|
||||
|
||||
proc raiseRangeErrorU(i, a, b: uint64) {.compilerproc, noinline.} =
|
||||
# todo: better error reporting
|
||||
sysFatal(RangeError, "value out of range")
|
||||
|
||||
proc raiseObjectConversionError() {.compilerproc, noinline.} =
|
||||
sysFatal(ObjectConversionError, "invalid object conversion")
|
||||
|
||||
proc chckIndx(i, a, b: int): int =
|
||||
if i >= a and i <= b:
|
||||
return i
|
||||
@@ -116,6 +129,5 @@ when not defined(nimV2):
|
||||
return true
|
||||
|
||||
when defined(nimV2):
|
||||
proc nimFieldDiscriminantCheckV2(oldDiscVal, newDiscVal: uint8) {.compilerproc.} =
|
||||
if oldDiscVal != newDiscVal:
|
||||
sysFatal(FieldError, "assignment to discriminant changes object branch")
|
||||
proc raiseObjectCaseTransition() {.compilerproc.} =
|
||||
sysFatal(FieldError, "assignment to discriminant changes object branch")
|
||||
|
||||
@@ -24,7 +24,7 @@ when hostOS == "standalone":
|
||||
rawoutput(message)
|
||||
panic(arg)
|
||||
|
||||
elif (defined(nimQuirky) or gotoBasedExceptions) and not defined(nimscript):
|
||||
elif (defined(nimQuirky) or defined(nimPanics)) and not defined(nimscript):
|
||||
import ansi_c
|
||||
|
||||
proc name(t: typedesc): string {.magic: "TypeTrait".}
|
||||
@@ -46,20 +46,9 @@ elif (defined(nimQuirky) or gotoBasedExceptions) and not defined(nimscript):
|
||||
|
||||
else:
|
||||
proc sysFatal(exceptn: typedesc, message: string) {.inline, noreturn.} =
|
||||
when declared(owned):
|
||||
var e: owned(ref exceptn)
|
||||
else:
|
||||
var e: ref exceptn
|
||||
new(e)
|
||||
e.msg = message
|
||||
raise e
|
||||
raise (ref exceptn)(msg: message)
|
||||
|
||||
proc sysFatal(exceptn: typedesc, message, arg: string) {.inline, noreturn.} =
|
||||
when declared(owned):
|
||||
var e: owned(ref exceptn)
|
||||
else:
|
||||
var e: ref exceptn
|
||||
new(e)
|
||||
e.msg = message & arg
|
||||
raise e
|
||||
raise (ref exceptn)(msg: message & arg)
|
||||
|
||||
{.pop.}
|
||||
|
||||
@@ -441,13 +441,15 @@ proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.} =
|
||||
zeroMem(result, size)
|
||||
when defined(memProfiler): nimProfile(size)
|
||||
|
||||
{.push overflowChecks: on.}
|
||||
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
|
||||
# `newObj` already uses locks, so no need for them here.
|
||||
let size = addInt(mulInt(len, typ.base.size), GenericSeqSize)
|
||||
let size = len * typ.base.size + GenericSeqSize
|
||||
result = newObj(typ, size)
|
||||
cast[PGenericSeq](result).len = len
|
||||
cast[PGenericSeq](result).reserved = len
|
||||
when defined(memProfiler): nimProfile(size)
|
||||
{.pop.}
|
||||
|
||||
proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
|
||||
# generates a new object and sets its reference counter to 1
|
||||
@@ -476,12 +478,14 @@ proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
|
||||
sysAssert(allocInv(gch.region), "newObjRC1 end")
|
||||
when defined(memProfiler): nimProfile(size)
|
||||
|
||||
{.push overflowChecks: on.}
|
||||
proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
|
||||
let size = addInt(mulInt(len, typ.base.size), GenericSeqSize)
|
||||
let size = len * typ.base.size + GenericSeqSize
|
||||
result = newObjRC1(typ, size)
|
||||
cast[PGenericSeq](result).len = len
|
||||
cast[PGenericSeq](result).reserved = len
|
||||
when defined(memProfiler): nimProfile(size)
|
||||
{.pop.}
|
||||
|
||||
proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
|
||||
collectCT(gch)
|
||||
|
||||
@@ -79,7 +79,7 @@ template decTypeSize(cell, t) =
|
||||
if t.kind in {tyString, tySequence}:
|
||||
let cap = cast[PGenericSeq](cellToUsr(cell)).space
|
||||
let size = if t.kind == tyString: cap+1+GenericSeqSize
|
||||
else: addInt(mulInt(cap, t.base.size), GenericSeqSize)
|
||||
else: cap * t.base.size + GenericSeqSize
|
||||
atomicDec t.sizes, size+sizeof(Cell)
|
||||
else:
|
||||
atomicDec t.sizes, t.base.size+sizeof(Cell)
|
||||
|
||||
@@ -305,20 +305,22 @@ proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
|
||||
when defined(memProfiler): nimProfile(size)
|
||||
|
||||
when not defined(nimSeqsV2):
|
||||
{.push overflowChecks: on.}
|
||||
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
|
||||
# `newObj` already uses locks, so no need for them here.
|
||||
let size = addInt(mulInt(len, typ.base.size), GenericSeqSize)
|
||||
let size = len * typ.base.size + GenericSeqSize
|
||||
result = newObj(typ, size)
|
||||
cast[PGenericSeq](result).len = len
|
||||
cast[PGenericSeq](result).reserved = len
|
||||
when defined(memProfiler): nimProfile(size)
|
||||
|
||||
proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
|
||||
let size = addInt(mulInt(len, typ.base.size), GenericSeqSize)
|
||||
let size = len * typ.base.size + GenericSeqSize
|
||||
result = newObj(typ, size)
|
||||
cast[PGenericSeq](result).len = len
|
||||
cast[PGenericSeq](result).reserved = len
|
||||
when defined(memProfiler): nimProfile(size)
|
||||
{.pop.}
|
||||
|
||||
proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
|
||||
collectCT(gch, newsize + sizeof(Cell))
|
||||
|
||||
@@ -334,20 +334,21 @@ proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl.} =
|
||||
result = rawNewObj(tlRegion, typ, size)
|
||||
when defined(memProfiler): nimProfile(size)
|
||||
|
||||
{.push overflowChecks: on.}
|
||||
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
|
||||
let size = roundup(addInt(mulInt(len, typ.base.size), GenericSeqSize),
|
||||
MemAlign)
|
||||
let size = roundup(len * typ.base.size + GenericSeqSize, MemAlign)
|
||||
result = rawNewSeq(tlRegion, typ, size)
|
||||
zeroMem(result, size)
|
||||
cast[PGenericSeq](result).len = len
|
||||
cast[PGenericSeq](result).reserved = len
|
||||
|
||||
proc newStr(typ: PNimType, len: int; init: bool): pointer {.compilerRtl.} =
|
||||
let size = roundup(addInt(len, GenericSeqSize), MemAlign)
|
||||
let size = roundup(len + GenericSeqSize, MemAlign)
|
||||
result = rawNewSeq(tlRegion, typ, size)
|
||||
if init: zeroMem(result, size)
|
||||
cast[PGenericSeq](result).len = 0
|
||||
cast[PGenericSeq](result).reserved = len
|
||||
{.pop.}
|
||||
|
||||
proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
|
||||
result = rawNewObj(tlRegion, typ, size)
|
||||
|
||||
132
lib/system/integerops.nim
Normal file
132
lib/system/integerops.nim
Normal file
@@ -0,0 +1,132 @@
|
||||
#
|
||||
#
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2020 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
# Integer arithmetic with overflow checking. Uses
|
||||
# intrinsics or inline assembler.
|
||||
|
||||
proc raiseOverflow {.compilerproc, noinline.} =
|
||||
# a single proc to reduce code size to a minimum
|
||||
sysFatal(OverflowError, "over- or underflow")
|
||||
|
||||
proc raiseDivByZero {.compilerproc, noinline.} =
|
||||
sysFatal(DivByZeroError, "division by zero")
|
||||
|
||||
{.pragma: nimbaseH, importc, nodecl, noSideEffect, compilerproc.}
|
||||
|
||||
when defined(gcc) or defined(clang):
|
||||
# take the #define from nimbase.h
|
||||
|
||||
proc nimAddInt(a, b: int, res: ptr int): bool {.nimbaseH.}
|
||||
proc nimSubInt(a, b: int, res: ptr int): bool {.nimbaseH.}
|
||||
proc nimMulInt(a, b: int, res: ptr int): bool {.nimbaseH.}
|
||||
|
||||
proc nimAddInt64(a, b: int64; res: ptr int64): bool {.nimbaseH.}
|
||||
proc nimSubInt64(a, b: int64; res: ptr int64): bool {.nimbaseH.}
|
||||
proc nimMulInt64(a, b: int64; res: ptr int64): bool {.nimbaseH.}
|
||||
|
||||
# unary minus and 'abs' not required here anymore and are directly handled
|
||||
# in the code generator.
|
||||
# 'nimModInt' does exist in nimbase.h without check as we moved the
|
||||
# check for 0 to the codgen.
|
||||
proc nimModInt(a, b: int; res: ptr int): bool {.nimbaseH.}
|
||||
|
||||
proc nimModInt64(a, b: int64; res: ptr int64): bool {.nimbaseH.}
|
||||
|
||||
# Platform independent versions.
|
||||
|
||||
template addImplFallback(name, T, U) {.dirty.} =
|
||||
when not declared(name):
|
||||
proc name(a, b: T; res: ptr T): bool {.compilerproc, inline.} =
|
||||
let r = cast[T](cast[U](a) + cast[U](b))
|
||||
if (r xor a) >= T(0) or (r xor b) >= T(0):
|
||||
res[] = r
|
||||
else:
|
||||
result = true
|
||||
|
||||
addImplFallback(nimAddInt, int, uint)
|
||||
addImplFallback(nimAddInt64, int64, uint64)
|
||||
|
||||
template subImplFallback(name, T, U) {.dirty.} =
|
||||
when not declared(name):
|
||||
proc name(a, b: T; res: ptr T): bool {.compilerproc, inline.} =
|
||||
let r = cast[T](cast[U](a) - cast[U](b))
|
||||
if (r xor a) >= 0 or (r xor not b) >= 0:
|
||||
res[] = r
|
||||
else:
|
||||
result = true
|
||||
|
||||
subImplFallback(nimSubInt, int, uint)
|
||||
subImplFallback(nimSubInt64, int64, uint64)
|
||||
|
||||
template mulImplFallback(name, T, U, conv) {.dirty.} =
|
||||
#
|
||||
# This code has been inspired by Python's source code.
|
||||
# The native int product x*y is either exactly right or *way* off, being
|
||||
# just the last n bits of the true product, where n is the number of bits
|
||||
# in an int (the delivered product is the true product plus i*2**n for
|
||||
# some integer i).
|
||||
#
|
||||
# The native float64 product x*y is subject to three
|
||||
# rounding errors: on a sizeof(int)==8 box, each cast to double can lose
|
||||
# info, and even on a sizeof(int)==4 box, the multiplication can lose info.
|
||||
# But, unlike the native int product, it's not in *range* trouble: even
|
||||
# if sizeof(int)==32 (256-bit ints), the product easily fits in the
|
||||
# dynamic range of a float64. So the leading 50 (or so) bits of the float64
|
||||
# product are correct.
|
||||
#
|
||||
# We check these two ways against each other, and declare victory if
|
||||
# they're approximately the same. Else, because the native int product is
|
||||
# the only one that can lose catastrophic amounts of information, it's the
|
||||
# native int product that must have overflowed.
|
||||
#
|
||||
when not declared(name):
|
||||
proc name(a, b: T; res: ptr T): bool {.compilerproc, inline.} =
|
||||
let r = cast[T](cast[U](a) * cast[U](b))
|
||||
let floatProd = conv(a) * conv(b)
|
||||
let resAsFloat = conv(r)
|
||||
# Fast path for normal case: small multiplicands, and no info
|
||||
# is lost in either method.
|
||||
if resAsFloat == floatProd:
|
||||
res[] = r
|
||||
else:
|
||||
# Somebody somewhere lost info. Close enough, or way off? Note
|
||||
# that a != 0 and b != 0 (else resAsFloat == floatProd == 0).
|
||||
# The difference either is or isn't significant compared to the
|
||||
# true value (of which floatProd is a good approximation).
|
||||
|
||||
# abs(diff)/abs(prod) <= 1/32 iff
|
||||
# 32 * abs(diff) <= abs(prod) -- 5 good bits is "close enough"
|
||||
if 32.0 * abs(resAsFloat - floatProd) <= abs(floatProd):
|
||||
res[] = r
|
||||
else:
|
||||
result = true
|
||||
|
||||
mulImplFallback(nimMulInt, int, uint, toFloat)
|
||||
mulImplFallback(nimMulInt64, int64, uint64, toBiggestFloat)
|
||||
|
||||
|
||||
template divImplFallback(name, T) {.dirty.} =
|
||||
proc name(a, b: T; res: ptr T): bool {.compilerproc, inline.} =
|
||||
# we moved the b == 0 case out into the codegen.
|
||||
if a == low(T) and b == T(-1):
|
||||
result = true
|
||||
else:
|
||||
res[] = a div b
|
||||
|
||||
divImplFallback(nimDivInt, int)
|
||||
divImplFallback(nimDivInt64, int64)
|
||||
|
||||
proc raiseFloatInvalidOp {.compilerproc, noinline.} =
|
||||
sysFatal(FloatInvalidOpError, "FPU operation caused a NaN result")
|
||||
|
||||
proc raiseFloatOverflow(x: float64) {.compilerproc, noinline.} =
|
||||
if x > 0.0:
|
||||
sysFatal(FloatOverflowError, "FPU operation caused an overflow")
|
||||
else:
|
||||
sysFatal(FloatUnderflowError, "FPU operations caused an underflow")
|
||||
@@ -101,10 +101,12 @@ proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} =
|
||||
else: result = alloc(size)
|
||||
if typ.finalizer != nil:
|
||||
boehmRegisterFinalizer(result, boehmgc_finalizer, typ.finalizer, nil, nil)
|
||||
{.push overflowChecks: on.}
|
||||
proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.} =
|
||||
result = newObj(typ, addInt(mulInt(len, typ.base.size), GenericSeqSize))
|
||||
result = newObj(typ, len * typ.base.size + GenericSeqSize)
|
||||
cast[PGenericSeq](result).len = len
|
||||
cast[PGenericSeq](result).reserved = len
|
||||
{.pop.}
|
||||
|
||||
proc growObj(old: pointer, newsize: int): pointer =
|
||||
result = realloc(old, newsize)
|
||||
|
||||
@@ -19,10 +19,12 @@ proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} =
|
||||
proc newObjNoInit(typ: PNimType, size: int): pointer =
|
||||
result = alloc(size)
|
||||
|
||||
{.push overflowChecks: on.}
|
||||
proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.} =
|
||||
result = newObj(typ, addInt(mulInt(len, typ.base.size), GenericSeqSize))
|
||||
result = newObj(typ, len * typ.base.size + GenericSeqSize)
|
||||
cast[PGenericSeq](result).len = len
|
||||
cast[PGenericSeq](result).reserved = len
|
||||
{.pop.}
|
||||
|
||||
proc growObj(old: pointer, newsize: int): pointer =
|
||||
result = realloc(old, newsize)
|
||||
|
||||
@@ -102,18 +102,20 @@ else:
|
||||
include "system/gc"
|
||||
|
||||
when not declared(nimNewSeqOfCap) and not defined(nimSeqsV2):
|
||||
{.push overflowChecks: on.}
|
||||
proc nimNewSeqOfCap(typ: PNimType, cap: int): pointer {.compilerproc.} =
|
||||
when defined(gcRegions):
|
||||
let s = mulInt(cap, typ.base.size) # newStr already adds GenericSeqSize
|
||||
let s = cap * typ.base.size # newStr already adds GenericSeqSize
|
||||
result = newStr(typ, s, ntfNoRefs notin typ.base.flags)
|
||||
else:
|
||||
let s = addInt(mulInt(cap, typ.base.size), GenericSeqSize)
|
||||
let s = cap * typ.base.size + GenericSeqSize
|
||||
when declared(newObjNoInit):
|
||||
result = if ntfNoRefs in typ.base.flags: newObjNoInit(typ, s) else: newObj(typ, s)
|
||||
else:
|
||||
result = newObj(typ, s)
|
||||
cast[PGenericSeq](result).len = 0
|
||||
cast[PGenericSeq](result).reserved = cap
|
||||
{.pop.}
|
||||
|
||||
{.pop.}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ tassert_c.nim(35) tassert_c
|
||||
tassert_c.nim(34) foo
|
||||
assertions.nim(29) failedAssertImpl
|
||||
assertions.nim(22) raiseAssert
|
||||
fatal.nim(55) sysFatal"""
|
||||
fatal.nim(49) sysFatal"""
|
||||
|
||||
proc tmatch(x, p: string): bool =
|
||||
var i = 0
|
||||
|
||||
52
tests/destructor/tgotoexceptions7.nim
Normal file
52
tests/destructor/tgotoexceptions7.nim
Normal file
@@ -0,0 +1,52 @@
|
||||
discard """
|
||||
cmd: "nim c --gc:arc --exceptions:goto --panics:off $file"
|
||||
output: '''field error prevented
|
||||
prevented!
|
||||
caught
|
||||
AssertionError
|
||||
900'''
|
||||
"""
|
||||
|
||||
type
|
||||
E = enum
|
||||
kindA, kindB
|
||||
Obj = object
|
||||
case kind: E
|
||||
of kindA: s: string
|
||||
of kindB: i: int
|
||||
|
||||
ObjA = ref object of RootObj
|
||||
ObjB = ref object of ObjA
|
||||
|
||||
proc takeRange(x: range[0..4]) = discard
|
||||
|
||||
proc bplease(x: ObjB) = discard
|
||||
|
||||
proc helper = doAssert(false)
|
||||
|
||||
proc main(i: int) =
|
||||
var obj = Obj(kind: kindA, s: "abc")
|
||||
try:
|
||||
obj.kind = kindB
|
||||
except FieldError:
|
||||
echo "field error prevented"
|
||||
|
||||
try:
|
||||
var objA = ObjA()
|
||||
bplease(ObjB(objA))
|
||||
except ObjectConversionError:
|
||||
echo "prevented!"
|
||||
|
||||
try:
|
||||
takeRange(i)
|
||||
except RangeError:
|
||||
echo "caught"
|
||||
|
||||
try:
|
||||
helper()
|
||||
except AssertionError:
|
||||
echo "AssertionError"
|
||||
|
||||
echo i * i
|
||||
|
||||
main(30)
|
||||
@@ -1,5 +1,5 @@
|
||||
discard """
|
||||
outputsub: "value out of range: 50 [RangeError]"
|
||||
outputsub: "value out of range: 50 notin 0 .. 40 [RangeError]"
|
||||
exitcode: "1"
|
||||
"""
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
discard """
|
||||
outputsub: "value out of range: 50 [RangeError]"
|
||||
outputsub: "value out of range: 50 notin 0 .. 40 [RangeError]"
|
||||
exitcode: "1"
|
||||
"""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user