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:
Andreas Rumpf
2020-03-12 23:44:33 +01:00
committed by GitHub
parent 14b2354b7d
commit a6682de004
35 changed files with 441 additions and 142 deletions

View File

@@ -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

View File

@@ -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]

View File

@@ -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)

View File

@@ -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:

View File

@@ -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, {})

View File

@@ -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

View File

@@ -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:

View File

@@ -113,3 +113,4 @@ proc initDefines*(symbols: StringTableRef) =
defineSymbol("nimHasLibFFIEnabled")
defineSymbol("nimHasSinkInference")
defineSymbol("nimNewIntegerOps")

View File

@@ -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

View File

@@ -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.}

View File

@@ -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()

View File

@@ -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.

View File

@@ -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

View File

@@ -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:

View File

@@ -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")

View File

@@ -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)

View File

@@ -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!

View File

@@ -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.

View File

@@ -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 */

View File

@@ -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.}

View File

@@ -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:

View File

@@ -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")

View File

@@ -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.}

View File

@@ -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)

View File

@@ -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)

View File

@@ -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))

View File

@@ -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
View 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")

View File

@@ -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)

View File

@@ -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)

View File

@@ -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.}

View File

@@ -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

View 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)

View File

@@ -1,5 +1,5 @@
discard """
outputsub: "value out of range: 50 [RangeError]"
outputsub: "value out of range: 50 notin 0 .. 40 [RangeError]"
exitcode: "1"
"""

View File

@@ -1,5 +1,5 @@
discard """
outputsub: "value out of range: 50 [RangeError]"
outputsub: "value out of range: 50 notin 0 .. 40 [RangeError]"
exitcode: "1"
"""