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:
parent
14b2354b7d
commit
a6682de004
35 changed files with 441 additions and 142 deletions
|
|
@ -161,6 +161,14 @@ echo f
|
||||||
- The compiler now inferes "sink parameters". To disable this for a specific routine,
|
- 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
|
annotate it with `.nosinks`. To disable it for a section of code, use
|
||||||
`{.push sinkInference: off.}`...`{.pop.}`.
|
`{.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
|
## Bugfixes
|
||||||
|
|
|
||||||
|
|
@ -229,7 +229,7 @@ type
|
||||||
TNodeKinds* = set[TNodeKind]
|
TNodeKinds* = set[TNodeKind]
|
||||||
|
|
||||||
type
|
type
|
||||||
TSymFlag* = enum # 41 flags!
|
TSymFlag* = enum # 42 flags!
|
||||||
sfUsed, # read access of sym (for warnings) or simply used
|
sfUsed, # read access of sym (for warnings) or simply used
|
||||||
sfExported, # symbol is exported from module
|
sfExported, # symbol is exported from module
|
||||||
sfFromGeneric, # symbol is instantiation of a generic; this is needed
|
sfFromGeneric, # symbol is instantiation of a generic; this is needed
|
||||||
|
|
@ -289,6 +289,8 @@ type
|
||||||
sfTemplateParam # symbol is a template parameter
|
sfTemplateParam # symbol is a template parameter
|
||||||
sfCursor # variable/field is a cursor, see RFC 177 for details
|
sfCursor # variable/field is a cursor, see RFC 177 for details
|
||||||
sfInjectDestructors # whether the proc needs the 'injectdestructors' transformation
|
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]
|
TSymFlags* = set[TSymFlag]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -557,6 +557,18 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
|
||||||
pl.add(~"];$n")
|
pl.add(~"];$n")
|
||||||
line(p, cpsStmts, pl)
|
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) =
|
proc genCall(p: BProc, e: PNode, d: var TLoc) =
|
||||||
if e[0].typ.skipTypes({tyGenericInst, tyAlias, tySink, tyOwned}).callConv == ccClosure:
|
if e[0].typ.skipTypes({tyGenericInst, tyAlias, tySink, tyOwned}).callConv == ccClosure:
|
||||||
genClosureCall(p, nil, e, d)
|
genClosureCall(p, nil, e, d)
|
||||||
|
|
@ -567,7 +579,7 @@ proc genCall(p: BProc, e: PNode, d: var TLoc) =
|
||||||
else:
|
else:
|
||||||
genPrefixCall(p, nil, e, d)
|
genPrefixCall(p, nil, e, d)
|
||||||
postStmtActions(p)
|
postStmtActions(p)
|
||||||
if p.config.exc == excGoto and canRaise(e[0]):
|
if p.config.exc == excGoto and canRaiseDisp(p, e[0]):
|
||||||
raiseExit(p)
|
raiseExit(p)
|
||||||
|
|
||||||
proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
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:
|
else:
|
||||||
genPrefixCall(p, le, ri, d)
|
genPrefixCall(p, le, ri, d)
|
||||||
postStmtActions(p)
|
postStmtActions(p)
|
||||||
if p.config.exc == excGoto and canRaise(ri[0]):
|
if p.config.exc == excGoto and canRaiseDisp(p, ri[0]):
|
||||||
raiseExit(p)
|
raiseExit(p)
|
||||||
|
|
|
||||||
|
|
@ -506,23 +506,25 @@ template binaryArithOverflowRaw(p: BProc, t: PType, a, b: TLoc;
|
||||||
else: getTypeDesc(p.module, t)
|
else: getTypeDesc(p.module, t)
|
||||||
var result = getTempName(p.module)
|
var result = getTempName(p.module)
|
||||||
linefmt(p, cpsLocals, "$1 $2;$n", [storage, result])
|
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}:
|
if size < p.config.target.intSize or t.kind in {tyRange, tyEnum}:
|
||||||
linefmt(p, cpsStmts, "if ($1 < $2 || $1 > $3) #raiseOverflow();$n",
|
linefmt(p, cpsStmts, "if ($1 < $2 || $1 > $3){ #raiseOverflow(); $4}$n",
|
||||||
[result, intLiteral(firstOrd(p.config, t)), intLiteral(lastOrd(p.config, t))])
|
[result, intLiteral(firstOrd(p.config, t)), intLiteral(lastOrd(p.config, t)),
|
||||||
|
raiseInstr(p)])
|
||||||
result
|
result
|
||||||
|
|
||||||
proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
|
proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
|
||||||
const
|
const
|
||||||
prc: array[mAddI..mPred, string] = [
|
prc: array[mAddI..mPred, string] = [
|
||||||
"addInt", "subInt",
|
"nimAddInt", "nimSubInt",
|
||||||
"mulInt", "divInt", "modInt",
|
"nimMulInt", "nimDivInt", "nimModInt",
|
||||||
"addInt", "subInt"
|
"nimAddInt", "nimSubInt"
|
||||||
]
|
]
|
||||||
prc64: array[mAddI..mPred, string] = [
|
prc64: array[mAddI..mPred, string] = [
|
||||||
"addInt64", "subInt64",
|
"nimAddInt64", "nimSubInt64",
|
||||||
"mulInt64", "divInt64", "modInt64",
|
"nimMulInt64", "nimDivInt64", "nimModInt64",
|
||||||
"addInt64", "subInt64"
|
"nimAddInt64", "nimSubInt64"
|
||||||
]
|
]
|
||||||
opr: array[mAddI..mPred, string] = ["+", "-", "*", "/", "%", "+", "-"]
|
opr: array[mAddI..mPred, string] = ["+", "-", "*", "/", "%", "+", "-"]
|
||||||
var a, b: TLoc
|
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)]
|
let res = "($1)($2 $3 $4)" % [getTypeDesc(p.module, e.typ), rdLoc(a), rope(opr[m]), rdLoc(b)]
|
||||||
putIntoDest(p, d, e, res)
|
putIntoDest(p, d, e, res)
|
||||||
else:
|
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,
|
let res = binaryArithOverflowRaw(p, t, a, b,
|
||||||
if t.kind == tyInt64: prc64[m] else: prc[m])
|
if t.kind == tyInt64: prc64[m] else: prc[m])
|
||||||
putIntoDest(p, d, e, "($#)($#)" % [getTypeDesc(p.module, e.typ), res])
|
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)
|
initLocExpr(p, e[1], a)
|
||||||
t = skipTypes(e.typ, abstractRange)
|
t = skipTypes(e.typ, abstractRange)
|
||||||
if optOverflowCheck in p.options:
|
if optOverflowCheck in p.options:
|
||||||
linefmt(p, cpsStmts, "if ($1 == $2) #raiseOverflow();$n",
|
linefmt(p, cpsStmts, "if ($1 == $2){ #raiseOverflow(); $3}$n",
|
||||||
[rdLoc(a), intLiteral(firstOrd(p.config, t))])
|
[rdLoc(a), intLiteral(firstOrd(p.config, t)), raiseInstr(p)])
|
||||||
case m
|
case m
|
||||||
of mUnaryMinusI:
|
of mUnaryMinusI:
|
||||||
putIntoDest(p, d, e, "((NI$2)-($1))" % [rdLoc(a), rope(getSize(p.config, t) * 8)])
|
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))
|
let strLit = genStringLiteral(p.module, newStrNode(nkStrLit, msg))
|
||||||
if op.magic == mNot:
|
if op.magic == mNot:
|
||||||
linefmt(p, cpsStmts,
|
linefmt(p, cpsStmts,
|
||||||
"if ($1) #raiseFieldError($2);$n",
|
"if ($1){ #raiseFieldError($2); $3}$n",
|
||||||
[rdLoc(test), strLit])
|
[rdLoc(test), strLit, raiseInstr(p)])
|
||||||
else:
|
else:
|
||||||
linefmt(p, cpsStmts,
|
linefmt(p, cpsStmts,
|
||||||
"if (!($1)) #raiseFieldError($2);$n",
|
"if (!($1)){ #raiseFieldError($2); $3}$n",
|
||||||
[rdLoc(test), strLit])
|
[rdLoc(test), strLit, raiseInstr(p)])
|
||||||
|
|
||||||
proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) =
|
proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) =
|
||||||
if optFieldCheck in p.options:
|
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
|
# semantic pass has already checked for const index expressions
|
||||||
if firstOrd(p.config, ty) == 0:
|
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)):
|
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",
|
linefmt(p, cpsStmts, "if ((NU)($1) > (NU)($2)){ #raiseIndexError2($1, $2); $3}$n",
|
||||||
[rdCharLoc(b), intLiteral(lastOrd(p.config, ty))])
|
[rdCharLoc(b), intLiteral(lastOrd(p.config, ty)), raiseInstr(p)])
|
||||||
else:
|
else:
|
||||||
linefmt(p, cpsStmts, "if ($1 < $2 || $1 > $3) #raiseIndexError3($1, $2, $3);$n",
|
linefmt(p, cpsStmts, "if ($1 < $2 || $1 > $3){ #raiseIndexError3($1, $2, $3); $4}$n",
|
||||||
[rdCharLoc(b), first, intLiteral(lastOrd(p.config, ty))])
|
[rdCharLoc(b), first, intLiteral(lastOrd(p.config, ty)), raiseInstr(p)])
|
||||||
else:
|
else:
|
||||||
let idx = getOrdValue(y)
|
let idx = getOrdValue(y)
|
||||||
if idx < firstOrd(p.config, ty) or idx > lastOrd(p.config, ty):
|
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:
|
of tyOpenArray, tyVarargs:
|
||||||
linefmt(p, cpsStmts,
|
linefmt(p, cpsStmts,
|
||||||
"if ($2-$1 != -1 && " &
|
"if ($2-$1 != -1 && " &
|
||||||
"((NU)($1) >= (NU)($3Len_0) || (NU)($2) >= (NU)($3Len_0))) #raiseIndexError();$n",
|
"((NU)($1) >= (NU)($3Len_0) || (NU)($2) >= (NU)($3Len_0))){ #raiseIndexError(); $4}$n",
|
||||||
[rdLoc(a), rdLoc(b), rdLoc(arr)])
|
[rdLoc(a), rdLoc(b), rdLoc(arr), raiseInstr(p)])
|
||||||
of tyArray:
|
of tyArray:
|
||||||
let first = intLiteral(firstOrd(p.config, ty))
|
let first = intLiteral(firstOrd(p.config, ty))
|
||||||
linefmt(p, cpsStmts,
|
linefmt(p, cpsStmts,
|
||||||
"if ($2-$1 != -1 && " &
|
"if ($2-$1 != -1 && " &
|
||||||
"($2-$1 < -1 || $1 < $3 || $1 > $4 || $2 < $3 || $2 > $4)) #raiseIndexError();$n",
|
"($2-$1 < -1 || $1 < $3 || $1 > $4 || $2 < $3 || $2 > $4)){ #raiseIndexError(); $5}$n",
|
||||||
[rdCharLoc(a), rdCharLoc(b), first, intLiteral(lastOrd(p.config, ty))])
|
[rdCharLoc(a), rdCharLoc(b), first, intLiteral(lastOrd(p.config, ty)), raiseInstr(p)])
|
||||||
of tySequence, tyString:
|
of tySequence, tyString:
|
||||||
linefmt(p, cpsStmts,
|
linefmt(p, cpsStmts,
|
||||||
"if ($2-$1 != -1 && " &
|
"if ($2-$1 != -1 && " &
|
||||||
"((NU)($1) >= (NU)$3 || (NU)($2) >= (NU)$3)) #raiseIndexError();$n",
|
"((NU)($1) >= (NU)$3 || (NU)($2) >= (NU)$3)){ #raiseIndexError(); $4}$n",
|
||||||
[rdLoc(a), rdLoc(b), lenExpr(p, arr)])
|
[rdLoc(a), rdLoc(b), lenExpr(p, arr), raiseInstr(p)])
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
proc genOpenArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) =
|
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, x, a)
|
||||||
initLocExpr(p, y, b) # emit range check:
|
initLocExpr(p, y, b) # emit range check:
|
||||||
if optBoundsCheck in p.options:
|
if optBoundsCheck in p.options:
|
||||||
linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)($2Len_0)) #raiseIndexError2($1,$2Len_0-1);$n",
|
linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)($2Len_0)){ #raiseIndexError2($1,$2Len_0-1); $3}$n",
|
||||||
[rdLoc(b), rdLoc(a)]) # BUGFIX: ``>=`` and not ``>``!
|
[rdLoc(b), rdLoc(a), raiseInstr(p)]) # BUGFIX: ``>=`` and not ``>``!
|
||||||
inheritLocation(d, a)
|
inheritLocation(d, a)
|
||||||
putIntoDest(p, d, n,
|
putIntoDest(p, d, n,
|
||||||
ropecg(p.module, "$1[$2]", [rdLoc(a), rdCharLoc(b)]), a.storage)
|
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 optBoundsCheck in p.options:
|
||||||
if ty.kind == tyString and (not defined(nimNoZeroTerminator) or optLaxStrings in p.options):
|
if ty.kind == tyString and (not defined(nimNoZeroTerminator) or optLaxStrings in p.options):
|
||||||
linefmt(p, cpsStmts,
|
linefmt(p, cpsStmts,
|
||||||
"if ((NU)($1) > (NU)$2) #raiseIndexError2($1,$2);$n",
|
"if ((NU)($1) > (NU)$2){ #raiseIndexError2($1,$2); $3}$n",
|
||||||
[rdLoc(b), lenExpr(p, a)])
|
[rdLoc(b), lenExpr(p, a), raiseInstr(p)])
|
||||||
else:
|
else:
|
||||||
linefmt(p, cpsStmts,
|
linefmt(p, cpsStmts,
|
||||||
"if ((NU)($1) >= (NU)$2) #raiseIndexError2($1,$2-1);$n",
|
"if ((NU)($1) >= (NU)$2){ #raiseIndexError2($1,$2-1); $3}$n",
|
||||||
[rdLoc(b), lenExpr(p, a)])
|
[rdLoc(b), lenExpr(p, a), raiseInstr(p)])
|
||||||
if d.k == locNone: d.storage = OnHeap
|
if d.k == locNone: d.storage = OnHeap
|
||||||
if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}:
|
if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}:
|
||||||
a.r = ropecg(p.module, "(*$1)", [a.r])
|
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:
|
# C code; plus it's the right thing to do for closures:
|
||||||
genSomeCast(p, e, d)
|
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 a: TLoc
|
||||||
var dest = skipTypes(n.typ, abstractVar)
|
var dest = skipTypes(n.typ, abstractVar)
|
||||||
|
initLocExpr(p, n[0], a)
|
||||||
if optRangeCheck notin p.options or (dest.kind in {tyUInt..tyUInt64} and
|
if optRangeCheck notin p.options or (dest.kind in {tyUInt..tyUInt64} and
|
||||||
checkUnsignedConversions notin p.config.legacyFeatures):
|
checkUnsignedConversions notin p.config.legacyFeatures):
|
||||||
initLocExpr(p, n[0], a)
|
discard "no need to generate a check because it was disabled"
|
||||||
|
else:
|
||||||
|
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))" %
|
putIntoDest(p, d, n, "(($1) ($2))" %
|
||||||
[getTypeDesc(p.module, dest), rdCharLoc(a)], a.storage)
|
[getTypeDesc(p.module, dest), rdCharLoc(a)], a.storage)
|
||||||
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)
|
|
||||||
|
|
||||||
proc genConv(p: BProc, e: PNode, d: var TLoc) =
|
proc genConv(p: BProc, e: PNode, d: var TLoc) =
|
||||||
let destType = e.typ.skipTypes({tyVar, tyLent, tyGenericInst, tyAlias, tySink})
|
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),
|
[opr[m], rdLoc(a), rdLoc(b),
|
||||||
getSimpleTypeDesc(p.module, e[1].typ)]))
|
getSimpleTypeDesc(p.module, e[1].typ)]))
|
||||||
if optNaNCheck in p.options:
|
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:
|
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:
|
else:
|
||||||
binaryArith(p, e, d, m)
|
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 mSwap: genSwap(p, e, d)
|
||||||
of mInc, mDec:
|
of mInc, mDec:
|
||||||
const opr: array[mInc..mDec, string] = ["+=", "-="]
|
const opr: array[mInc..mDec, string] = ["+=", "-="]
|
||||||
const fun64: array[mInc..mDec, string] = ["addInt64",
|
const fun64: array[mInc..mDec, string] = ["nimAddInt64", "nimSubInt64"]
|
||||||
"subInt64"]
|
const fun: array[mInc..mDec, string] = ["nimAddInt","nimSubInt"]
|
||||||
const fun: array[mInc..mDec, string] = ["addInt",
|
|
||||||
"subInt"]
|
|
||||||
let underlying = skipTypes(e[1].typ, {tyGenericInst, tyAlias, tySink, tyVar, tyLent, tyRange})
|
let underlying = skipTypes(e[1].typ, {tyGenericInst, tyAlias, tySink, tyVar, tyLent, tyRange})
|
||||||
if optOverflowCheck notin p.options or underlying.kind in {tyUInt..tyUInt64}:
|
if optOverflowCheck notin p.options or underlying.kind in {tyUInt..tyUInt64}:
|
||||||
binaryStmt(p, e, d, opr[op])
|
binaryStmt(p, e, d, opr[op])
|
||||||
|
|
@ -2432,11 +2450,11 @@ proc upConv(p: BProc, n: PNode, d: var TLoc) =
|
||||||
else:
|
else:
|
||||||
genTypeInfo(p.module, dest, n.info)
|
genTypeInfo(p.module, dest, n.info)
|
||||||
if nilCheck != nil:
|
if nilCheck != nil:
|
||||||
linefmt(p, cpsStmts, "if ($1) #chckObj($2, $3);$n",
|
linefmt(p, cpsStmts, "if ($1 && !#isObj($2, $3)){ #raiseObjectConversionError(); $4}$n",
|
||||||
[nilCheck, r, checkFor])
|
[nilCheck, r, checkFor, raiseInstr(p)])
|
||||||
else:
|
else:
|
||||||
linefmt(p, cpsStmts, "#chckObj($1, $2);$n",
|
linefmt(p, cpsStmts, "if (!#isObj($1, $2)){ #raiseObjectConversionError(); $3}$n",
|
||||||
[r, checkFor])
|
[r, checkFor, raiseInstr(p)])
|
||||||
if n[0].typ.kind != tyObject:
|
if n[0].typ.kind != tyObject:
|
||||||
putIntoDest(p, d, n,
|
putIntoDest(p, d, n,
|
||||||
"(($1) ($2))" % [getTypeDesc(p.module, n.typ), rdLoc(a)], a.storage)
|
"(($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)
|
expr(p, n[1][0], d)
|
||||||
of nkObjDownConv: downConv(p, n, d)
|
of nkObjDownConv: downConv(p, n, d)
|
||||||
of nkObjUpConv: upConv(p, n, d)
|
of nkObjUpConv: upConv(p, n, d)
|
||||||
of nkChckRangeF: genRangeChck(p, n, d, "chckRangeF")
|
of nkChckRangeF: genRangeChck(p, n, d)
|
||||||
of nkChckRange64: genRangeChck(p, n, d, "chckRange64")
|
of nkChckRange64: genRangeChck(p, n, d)
|
||||||
of nkChckRange: genRangeChck(p, n, d, "chckRange")
|
of nkChckRange: genRangeChck(p, n, d)
|
||||||
of nkStringToCString: convStrToCStr(p, n, d)
|
of nkStringToCString: convStrToCStr(p, n, d)
|
||||||
of nkCStringToString: convCStrToStr(p, n, d)
|
of nkCStringToString: convCStrToStr(p, n, d)
|
||||||
of nkLambdaKinds:
|
of nkLambdaKinds:
|
||||||
|
|
|
||||||
|
|
@ -706,6 +706,22 @@ proc finallyActions(p: BProc) =
|
||||||
if finallyBlock != nil:
|
if finallyBlock != nil:
|
||||||
genSimpleBlock(p, finallyBlock[0])
|
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) =
|
proc genRaiseStmt(p: BProc, t: PNode) =
|
||||||
if p.config.exc == excCpp:
|
if p.config.exc == excCpp:
|
||||||
discard cgsym(p.module, "popCurrentExceptionEx")
|
discard cgsym(p.module, "popCurrentExceptionEx")
|
||||||
|
|
@ -733,18 +749,9 @@ proc genRaiseStmt(p: BProc, t: PNode) =
|
||||||
line(p, cpsStmts, ~"throw;$n")
|
line(p, cpsStmts, ~"throw;$n")
|
||||||
else:
|
else:
|
||||||
linefmt(p, cpsStmts, "#reraiseException();$n", [])
|
linefmt(p, cpsStmts, "#reraiseException();$n", [])
|
||||||
if p.config.exc == excGoto:
|
let gotoInstr = raiseInstr(p)
|
||||||
let L = p.nestedTryStmts.len
|
if gotoInstr != nil:
|
||||||
if L == 0:
|
line(p, cpsStmts, gotoInstr)
|
||||||
p.flags.incl beforeRetNeeded
|
|
||||||
# easy case, simply goto 'ret':
|
|
||||||
lineCg(p, cpsStmts, "goto BeforeRet_;$n", [])
|
|
||||||
else:
|
|
||||||
# raise inside an 'except' must go to the finally block,
|
|
||||||
# raise outside an 'except' block must go to the 'except' list.
|
|
||||||
lineCg(p, cpsStmts, "goto LA$1_;$n",
|
|
||||||
[p.nestedTryStmts[L-1].label])
|
|
||||||
# + ord(p.nestedTryStmts[L-1].inExcept)])
|
|
||||||
|
|
||||||
template genCaseGenericBranch(p: BProc, b: PNode, e: TLoc,
|
template genCaseGenericBranch(p: BProc, b: PNode, e: TLoc,
|
||||||
rangeFormat, eqFormat: FormatStr, labl: TLabel) =
|
rangeFormat, eqFormat: FormatStr, labl: TLabel) =
|
||||||
|
|
@ -1009,14 +1016,14 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
|
||||||
|
|
||||||
genSimpleBlock(p, t[^1][0])
|
genSimpleBlock(p, t[^1][0])
|
||||||
|
|
||||||
proc bodyCanRaise(n: PNode): bool =
|
proc bodyCanRaise(p: BProc; n: PNode): bool =
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkCallKinds:
|
of nkCallKinds:
|
||||||
result = canRaise(n[0])
|
result = canRaiseDisp(p, n[0])
|
||||||
if not result:
|
if not result:
|
||||||
# also check the arguments:
|
# also check the arguments:
|
||||||
for i in 1 ..< n.len:
|
for i in 1 ..< n.len:
|
||||||
if bodyCanRaise(n[i]): return true
|
if bodyCanRaise(p, n[i]): return true
|
||||||
of nkRaiseStmt:
|
of nkRaiseStmt:
|
||||||
result = true
|
result = true
|
||||||
of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef,
|
of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef,
|
||||||
|
|
@ -1024,22 +1031,24 @@ proc bodyCanRaise(n: PNode): bool =
|
||||||
result = false
|
result = false
|
||||||
else:
|
else:
|
||||||
for i in 0 ..< safeLen(n):
|
for i in 0 ..< safeLen(n):
|
||||||
if bodyCanRaise(n[i]): return true
|
if bodyCanRaise(p, n[i]): return true
|
||||||
result = false
|
result = false
|
||||||
|
|
||||||
proc genTryGoto(p: BProc; t: PNode; d: var TLoc) =
|
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:
|
# optimize away the 'try' block:
|
||||||
expr(p, t[0], d)
|
expr(p, t[0], d)
|
||||||
if t.len > 1 and t[^1].kind == nkFinally:
|
linefmt(p, cpsStmts, "LA$1_: ;$n", [lab])
|
||||||
genStmts(p, t[^1][0])
|
if fin != nil:
|
||||||
|
genStmts(p, fin[0])
|
||||||
|
discard pop(p.nestedTryStmts)
|
||||||
return
|
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.flags.incl nimErrorFlagAccessed
|
||||||
p.procSec(cpsLocals).add(ropecg(p.module, "NI oldNimErr$1_;$n", [lab]))
|
p.procSec(cpsLocals).add(ropecg(p.module, "NI oldNimErr$1_;$n", [lab]))
|
||||||
linefmt(p, cpsStmts, "oldNimErr$1_ = *nimErr_; *nimErr_ = 0;;$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])
|
#linefmt(p, cpsStmts, "LA$1_:;$n", [lab+1])
|
||||||
if i < t.len and t[i].kind == nkFinally:
|
if i < t.len and t[i].kind == nkFinally:
|
||||||
startBlock(p)
|
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
|
# 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:
|
# exception and so we help the C optimizer by not mutating nimErr_ pointlessly:
|
||||||
genStmts(p, t[i][0])
|
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[0], t, field, oldVal)
|
||||||
genCaseObjDiscMapping(p, e[1], t, field, newVal)
|
genCaseObjDiscMapping(p, e[1], t, field, newVal)
|
||||||
lineCg(p, cpsStmts,
|
lineCg(p, cpsStmts,
|
||||||
"#nimFieldDiscriminantCheckV2($1, $2);$n",
|
"if ($1 != $2) { #raiseObjectCaseTransition(); $3}$n",
|
||||||
[rdLoc(oldVal), rdLoc(newVal)])
|
[rdLoc(oldVal), rdLoc(newVal), raiseInstr(p)])
|
||||||
else:
|
else:
|
||||||
genDiscriminantCheck(p, a, tmp, dotExpr[0].typ, field)
|
genDiscriminantCheck(p, a, tmp, dotExpr[0].typ, field)
|
||||||
genAssignment(p, a, tmp, {})
|
genAssignment(p, a, tmp, {})
|
||||||
|
|
|
||||||
|
|
@ -276,6 +276,7 @@ proc postStmtActions(p: BProc) {.inline.} =
|
||||||
proc accessThreadLocalVar(p: BProc, s: PSym)
|
proc accessThreadLocalVar(p: BProc, s: PSym)
|
||||||
proc emulatedThreadVars(conf: ConfigRef): bool {.inline.}
|
proc emulatedThreadVars(conf: ConfigRef): bool {.inline.}
|
||||||
proc genProc(m: BModule, prc: PSym)
|
proc genProc(m: BModule, prc: PSym)
|
||||||
|
proc raiseInstr(p: BProc): Rope
|
||||||
|
|
||||||
template compileToCpp(m: BModule): untyped =
|
template compileToCpp(m: BModule): untyped =
|
||||||
m.config.cmd == cmdCompileToCpp or sfCompileToCpp in m.module.flags
|
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)
|
processOnOffSwitchG(conf, {optBenchmarkVM}, arg, pass, info)
|
||||||
of "sinkinference":
|
of "sinkinference":
|
||||||
processOnOffSwitch(conf, {optSinkInference}, arg, pass, info)
|
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 -)
|
of "": # comes from "-" in for example: `nim c -r -` (gets stripped from -)
|
||||||
handleStdinInput(conf)
|
handleStdinInput(conf)
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -113,3 +113,4 @@ proc initDefines*(symbols: StringTableRef) =
|
||||||
defineSymbol("nimHasLibFFIEnabled")
|
defineSymbol("nimHasLibFFIEnabled")
|
||||||
|
|
||||||
defineSymbol("nimHasSinkInference")
|
defineSymbol("nimHasSinkInference")
|
||||||
|
defineSymbol("nimNewIntegerOps")
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ proc genEnumToStrProc*(t: PType; info: TLineInfo; g: ModuleGraph): PSym =
|
||||||
n[resultPos] = newSymNode(res)
|
n[resultPos] = newSymNode(res)
|
||||||
result.ast = n
|
result.ast = n
|
||||||
incl result.flags, sfFromGeneric
|
incl result.flags, sfFromGeneric
|
||||||
|
incl result.flags, sfAlwaysReturn
|
||||||
|
|
||||||
proc searchObjCaseImpl(obj: PNode; field: PSym): PNode =
|
proc searchObjCaseImpl(obj: PNode; field: PSym): PNode =
|
||||||
case obj.kind
|
case obj.kind
|
||||||
|
|
@ -101,3 +102,4 @@ proc genCaseObjDiscMapping*(t: PType; field: PSym; info: TLineInfo; g: ModuleGra
|
||||||
n[resultPos] = newSymNode(res)
|
n[resultPos] = newSymNode(res)
|
||||||
result.ast = n
|
result.ast = n
|
||||||
incl result.flags, sfFromGeneric
|
incl result.flags, sfFromGeneric
|
||||||
|
incl result.flags, sfAlwaysReturn
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ type
|
||||||
warnTypelessParam,
|
warnTypelessParam,
|
||||||
warnUseBase, warnWriteToForeignHeap, warnUnsafeCode,
|
warnUseBase, warnWriteToForeignHeap, warnUnsafeCode,
|
||||||
warnUnusedImportX,
|
warnUnusedImportX,
|
||||||
|
warnInheritFromException,
|
||||||
warnEachIdentIsTuple,
|
warnEachIdentIsTuple,
|
||||||
warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2,
|
warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2,
|
||||||
warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed,
|
warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed,
|
||||||
|
|
@ -81,6 +82,7 @@ const
|
||||||
warnWriteToForeignHeap: "write to foreign heap",
|
warnWriteToForeignHeap: "write to foreign heap",
|
||||||
warnUnsafeCode: "unsafe code: '$1'",
|
warnUnsafeCode: "unsafe code: '$1'",
|
||||||
warnUnusedImportX: "imported and not used: '$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",
|
warnEachIdentIsTuple: "each identifier is a tuple",
|
||||||
warnProveInit: "Cannot prove that '$1' is initialized. This will become a compile time error in the future.",
|
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",
|
warnProveField: "cannot prove that field '$1' is accessible",
|
||||||
|
|
@ -138,7 +140,8 @@ const
|
||||||
"LanguageXNotSupported", "FieldXNotSupported",
|
"LanguageXNotSupported", "FieldXNotSupported",
|
||||||
"CommentXIgnored",
|
"CommentXIgnored",
|
||||||
"TypelessParam", "UseBase", "WriteToForeignHeap",
|
"TypelessParam", "UseBase", "WriteToForeignHeap",
|
||||||
"UnsafeCode", "UnusedImport", "EachIdentIsTuple",
|
"UnsafeCode", "UnusedImport", "InheritFromException",
|
||||||
|
"EachIdentIsTuple",
|
||||||
"ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit",
|
"ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit",
|
||||||
"GcMem", "Destructor", "LockLevel", "ResultShadowed",
|
"GcMem", "Destructor", "LockLevel", "ResultShadowed",
|
||||||
"Spacing", "CaseTransition", "CycleCreated", "User"]
|
"Spacing", "CaseTransition", "CycleCreated", "User"]
|
||||||
|
|
@ -227,7 +230,7 @@ type
|
||||||
TErrorOutputs* = set[TErrorOutput]
|
TErrorOutputs* = set[TErrorOutput]
|
||||||
|
|
||||||
ERecoverableError* = object of ValueError
|
ERecoverableError* = object of ValueError
|
||||||
ESuggestDone* = object of Exception
|
ESuggestDone* = object of ValueError
|
||||||
|
|
||||||
proc `==`*(a, b: FileIndex): bool {.borrow.}
|
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 =
|
proc createMagic*(g: ModuleGraph; name: string, m: TMagic): PSym =
|
||||||
result = newSym(skProc, getIdent(g.cache, name), nil, unknownLineInfo, {})
|
result = newSym(skProc, getIdent(g.cache, name), nil, unknownLineInfo, {})
|
||||||
result.magic = m
|
result.magic = m
|
||||||
|
result.flags = {sfAlwaysReturn}
|
||||||
|
|
||||||
proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph =
|
proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph =
|
||||||
result = ModuleGraph()
|
result = ModuleGraph()
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,7 @@ type # please make sure we have under 32 options
|
||||||
optNimV019
|
optNimV019
|
||||||
optBenchmarkVM # Enables cpuTime() in the VM
|
optBenchmarkVM # Enables cpuTime() in the VM
|
||||||
optProduceAsm # produce assembler code
|
optProduceAsm # produce assembler code
|
||||||
|
optPanics # turn panics (sysFatal) into a process termination
|
||||||
|
|
||||||
TGlobalOptions* = set[TGlobalOption]
|
TGlobalOptions* = set[TGlobalOption]
|
||||||
|
|
||||||
|
|
@ -509,6 +510,7 @@ proc getOutFile*(conf: ConfigRef; filename: RelativeFile, ext: string): Absolute
|
||||||
|
|
||||||
proc absOutFile*(conf: ConfigRef): AbsoluteFile =
|
proc absOutFile*(conf: ConfigRef): AbsoluteFile =
|
||||||
result = conf.outDir / conf.outFile
|
result = conf.outDir / conf.outFile
|
||||||
|
when defined(posix):
|
||||||
if dirExists(result.string):
|
if dirExists(result.string):
|
||||||
result.string.add ".out"
|
result.string.add ".out"
|
||||||
|
|
||||||
|
|
@ -516,6 +518,7 @@ proc prepareToWriteOutput*(conf: ConfigRef): AbsoluteFile =
|
||||||
## Create the output directory and returns a full path to the output file
|
## Create the output directory and returns a full path to the output file
|
||||||
createDir conf.outDir
|
createDir conf.outDir
|
||||||
result = conf.outDir / conf.outFile
|
result = conf.outDir / conf.outFile
|
||||||
|
when defined(posix):
|
||||||
if dirExists(result.string):
|
if dirExists(result.string):
|
||||||
result.string.add ".out"
|
result.string.add ".out"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1283,7 +1283,7 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
|
||||||
while x.kind in {nkStmtList, nkStmtListExpr} and x.len > 0:
|
while x.kind in {nkStmtList, nkStmtListExpr} and x.len > 0:
|
||||||
x = x.lastSon
|
x = x.lastSon
|
||||||
if x.kind notin {nkObjectTy, nkDistinctTy, nkEnumTy, nkEmpty} and
|
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:
|
# type aliases are hard:
|
||||||
var t = semTypeNode(c, x, nil)
|
var t = semTypeNode(c, x, nil)
|
||||||
assert t != 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
|
# specialized object, there will be second check after instantiation
|
||||||
# located in semGeneric.
|
# located in semGeneric.
|
||||||
if concreteBase.kind == tyObject:
|
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)
|
addInheritedFields(c, check, pos, concreteBase)
|
||||||
else:
|
else:
|
||||||
if concreteBase.kind != tyError:
|
if concreteBase.kind != tyError:
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ const
|
||||||
szUncomputedSize* = -1
|
szUncomputedSize* = -1
|
||||||
szTooBigSize* = -4
|
szTooBigSize* = -4
|
||||||
|
|
||||||
type IllegalTypeRecursionError = object of Exception
|
type IllegalTypeRecursionError = object of ValueError
|
||||||
|
|
||||||
proc raiseIllegalTypeRecursion() =
|
proc raiseIllegalTypeRecursion() =
|
||||||
raise newException(IllegalTypeRecursionError, "illegal type recursion")
|
raise newException(IllegalTypeRecursionError, "illegal type recursion")
|
||||||
|
|
|
||||||
|
|
@ -146,3 +146,4 @@ Advanced options:
|
||||||
see also https://nim-lang.github.io/Nim/estp.html
|
see also https://nim-lang.github.io/Nim/estp.html
|
||||||
--benchmarkVM:on|off enable benchmarking of VM code with cpuTime()
|
--benchmarkVM:on|off enable benchmarking of VM code with cpuTime()
|
||||||
--sinkInference:on|off en-/disable sink parameter inference (default: on)
|
--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
|
during semantic analysis is called a `static error`:idx:. Errors described in
|
||||||
this manual are static errors when not otherwise specified.
|
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
|
and reports at runtime. The method for reporting such errors is via
|
||||||
*raising exceptions* or *dying with a fatal error*. However, the implementation
|
*raising exceptions* or *dying with a fatal error*. However, the implementation
|
||||||
provides a means to disable these `runtime checks`:idx:. See the section
|
provides a means to disable these `runtime checks`:idx:. See the section
|
||||||
pragmas_ for details.
|
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
|
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
|
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.
|
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:
|
except IndexError:
|
||||||
echo "invalid index"
|
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
|
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
|
detected, and can cause the subsequent behavior of the computation to
|
||||||
be arbitrary. Unchecked runtime errors cannot occur if only `safe`:idx:
|
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
|
the operation of functions as ``inc``, ``ord``, ``dec`` on ordinal types to
|
||||||
be defined.
|
be defined.
|
||||||
- Ordinal values have a smallest possible value. Trying to count further
|
- 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
|
- 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
|
Integers, bool, characters and enumeration types (and subranges of these
|
||||||
types) belong to ordinal types. For reasons of simplicity of implementation
|
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.
|
to 5. ``PositiveFloat`` defines a subrange of all positive floating point values.
|
||||||
NaN does not belong to any subrange of floating point types.
|
NaN does not belong to any subrange of floating point types.
|
||||||
Assigning any other value to a variable of type ``Subrange`` is a
|
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
|
semantic analysis). Assignments from the base type to one of its subrange types
|
||||||
(and vice versa) are allowed.
|
(and vice versa) are allowed.
|
||||||
|
|
||||||
|
|
@ -1763,9 +1769,9 @@ Nil
|
||||||
|
|
||||||
If a reference points to *nothing*, it has the value ``nil``. ``nil`` is also
|
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``
|
the default value for all ``ref`` and ``ptr`` types. Dereferencing ``nil``
|
||||||
is an unrecoverable fatal runtime error. A dereferencing operation ``p[]``
|
is an unrecoverable fatal runtime error (and not a panic).
|
||||||
implies that ``p`` is not nil. This can be exploited by the implementation to
|
A dereferencing operation ``p[]`` implies that ``p`` is not nil. This can be
|
||||||
optimize code like:
|
exploited by the implementation to optimize code like:
|
||||||
|
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
@ -4242,9 +4248,11 @@ The exception tree is defined in the `system <system.html>`_ module.
|
||||||
Every exception inherits from ``system.Exception``. Exceptions that indicate
|
Every exception inherits from ``system.Exception``. Exceptions that indicate
|
||||||
programming bugs inherit from ``system.Defect`` (which is a subtype of ``Exception``)
|
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
|
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
|
that terminates the whole process. If panics are turned into exceptions, these
|
||||||
error that can be caught inherit from ``system.CatchableError``
|
exceptions inherit from `Defect`.
|
||||||
(which is a subtype of ``Exception``).
|
|
||||||
|
Exceptions that indicate any other runtime error that can be caught inherit from
|
||||||
|
``system.CatchableError`` (which is a subtype of ``Exception``).
|
||||||
|
|
||||||
|
|
||||||
Imported exceptions
|
Imported exceptions
|
||||||
|
|
|
||||||
2
koch.nim
2
koch.nim
|
|
@ -475,7 +475,7 @@ proc hostInfo(): string =
|
||||||
|
|
||||||
proc runCI(cmd: string) =
|
proc runCI(cmd: string) =
|
||||||
doAssert cmd.len == 0, cmd # avoid silently ignoring
|
doAssert cmd.len == 0, cmd # avoid silently ignoring
|
||||||
echo "runCI:", cmd
|
echo "runCI: ", cmd
|
||||||
echo hostInfo()
|
echo hostInfo()
|
||||||
# note(@araq): Do not replace these commands with direct calls (eg boot())
|
# note(@araq): Do not replace these commands with direct calls (eg boot())
|
||||||
# as that would weaken our testing efforts.
|
# 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) \
|
#define NIM_CHECK_SIZE(typ, sz) \
|
||||||
_Static_assert(sizeof(typ) == sz, "Nim & C disagree on type size")
|
_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 */
|
#endif /* NIMBASE_H */
|
||||||
|
|
|
||||||
|
|
@ -2221,6 +2221,9 @@ when notJSnotNims:
|
||||||
|
|
||||||
# we cannot compile this with stack tracing on
|
# we cannot compile this with stack tracing on
|
||||||
# as it would recurse endlessly!
|
# as it would recurse endlessly!
|
||||||
|
when defined(nimNewIntegerOps):
|
||||||
|
include "system/integerops"
|
||||||
|
else:
|
||||||
include "system/arithm"
|
include "system/arithm"
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -409,13 +409,13 @@ when not declared(mulInt):
|
||||||
# We avoid setting the FPU control word here for compatibility with libraries
|
# We avoid setting the FPU control word here for compatibility with libraries
|
||||||
# written in other languages.
|
# written in other languages.
|
||||||
|
|
||||||
proc raiseFloatInvalidOp {.noinline.} =
|
proc raiseFloatInvalidOp {.compilerproc, noinline.} =
|
||||||
sysFatal(FloatInvalidOpError, "FPU operation caused a NaN result")
|
sysFatal(FloatInvalidOpError, "FPU operation caused a NaN result")
|
||||||
|
|
||||||
proc nanCheck(x: float64) {.compilerproc, inline.} =
|
proc nanCheck(x: float64) {.compilerproc, inline.} =
|
||||||
if x != x: raiseFloatInvalidOp()
|
if x != x: raiseFloatInvalidOp()
|
||||||
|
|
||||||
proc raiseFloatOverflow(x: float64) {.noinline.} =
|
proc raiseFloatOverflow(x: float64) {.compilerproc, noinline.} =
|
||||||
if x > 0.0:
|
if x > 0.0:
|
||||||
sysFatal(FloatOverflowError, "FPU operation caused an overflow")
|
sysFatal(FloatOverflowError, "FPU operation caused an overflow")
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,19 @@ proc raiseIndexError() {.compilerproc, noinline.} =
|
||||||
proc raiseFieldError(f: string) {.compilerproc, noinline.} =
|
proc raiseFieldError(f: string) {.compilerproc, noinline.} =
|
||||||
sysFatal(FieldError, f)
|
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 =
|
proc chckIndx(i, a, b: int): int =
|
||||||
if i >= a and i <= b:
|
if i >= a and i <= b:
|
||||||
return i
|
return i
|
||||||
|
|
@ -116,6 +129,5 @@ when not defined(nimV2):
|
||||||
return true
|
return true
|
||||||
|
|
||||||
when defined(nimV2):
|
when defined(nimV2):
|
||||||
proc nimFieldDiscriminantCheckV2(oldDiscVal, newDiscVal: uint8) {.compilerproc.} =
|
proc raiseObjectCaseTransition() {.compilerproc.} =
|
||||||
if oldDiscVal != newDiscVal:
|
|
||||||
sysFatal(FieldError, "assignment to discriminant changes object branch")
|
sysFatal(FieldError, "assignment to discriminant changes object branch")
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ when hostOS == "standalone":
|
||||||
rawoutput(message)
|
rawoutput(message)
|
||||||
panic(arg)
|
panic(arg)
|
||||||
|
|
||||||
elif (defined(nimQuirky) or gotoBasedExceptions) and not defined(nimscript):
|
elif (defined(nimQuirky) or defined(nimPanics)) and not defined(nimscript):
|
||||||
import ansi_c
|
import ansi_c
|
||||||
|
|
||||||
proc name(t: typedesc): string {.magic: "TypeTrait".}
|
proc name(t: typedesc): string {.magic: "TypeTrait".}
|
||||||
|
|
@ -46,20 +46,9 @@ elif (defined(nimQuirky) or gotoBasedExceptions) and not defined(nimscript):
|
||||||
|
|
||||||
else:
|
else:
|
||||||
proc sysFatal(exceptn: typedesc, message: string) {.inline, noreturn.} =
|
proc sysFatal(exceptn: typedesc, message: string) {.inline, noreturn.} =
|
||||||
when declared(owned):
|
raise (ref exceptn)(msg: message)
|
||||||
var e: owned(ref exceptn)
|
|
||||||
else:
|
|
||||||
var e: ref exceptn
|
|
||||||
new(e)
|
|
||||||
e.msg = message
|
|
||||||
raise e
|
|
||||||
|
|
||||||
proc sysFatal(exceptn: typedesc, message, arg: string) {.inline, noreturn.} =
|
proc sysFatal(exceptn: typedesc, message, arg: string) {.inline, noreturn.} =
|
||||||
when declared(owned):
|
raise (ref exceptn)(msg: message & arg)
|
||||||
var e: owned(ref exceptn)
|
|
||||||
else:
|
|
||||||
var e: ref exceptn
|
|
||||||
new(e)
|
|
||||||
e.msg = message & arg
|
|
||||||
raise e
|
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
|
||||||
|
|
@ -441,13 +441,15 @@ proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.} =
|
||||||
zeroMem(result, size)
|
zeroMem(result, size)
|
||||||
when defined(memProfiler): nimProfile(size)
|
when defined(memProfiler): nimProfile(size)
|
||||||
|
|
||||||
|
{.push overflowChecks: on.}
|
||||||
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
|
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
|
||||||
# `newObj` already uses locks, so no need for them here.
|
# `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)
|
result = newObj(typ, size)
|
||||||
cast[PGenericSeq](result).len = len
|
cast[PGenericSeq](result).len = len
|
||||||
cast[PGenericSeq](result).reserved = len
|
cast[PGenericSeq](result).reserved = len
|
||||||
when defined(memProfiler): nimProfile(size)
|
when defined(memProfiler): nimProfile(size)
|
||||||
|
{.pop.}
|
||||||
|
|
||||||
proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
|
proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
|
||||||
# generates a new object and sets its reference counter to 1
|
# 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")
|
sysAssert(allocInv(gch.region), "newObjRC1 end")
|
||||||
when defined(memProfiler): nimProfile(size)
|
when defined(memProfiler): nimProfile(size)
|
||||||
|
|
||||||
|
{.push overflowChecks: on.}
|
||||||
proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
|
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)
|
result = newObjRC1(typ, size)
|
||||||
cast[PGenericSeq](result).len = len
|
cast[PGenericSeq](result).len = len
|
||||||
cast[PGenericSeq](result).reserved = len
|
cast[PGenericSeq](result).reserved = len
|
||||||
when defined(memProfiler): nimProfile(size)
|
when defined(memProfiler): nimProfile(size)
|
||||||
|
{.pop.}
|
||||||
|
|
||||||
proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
|
proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
|
||||||
collectCT(gch)
|
collectCT(gch)
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ template decTypeSize(cell, t) =
|
||||||
if t.kind in {tyString, tySequence}:
|
if t.kind in {tyString, tySequence}:
|
||||||
let cap = cast[PGenericSeq](cellToUsr(cell)).space
|
let cap = cast[PGenericSeq](cellToUsr(cell)).space
|
||||||
let size = if t.kind == tyString: cap+1+GenericSeqSize
|
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)
|
atomicDec t.sizes, size+sizeof(Cell)
|
||||||
else:
|
else:
|
||||||
atomicDec t.sizes, t.base.size+sizeof(Cell)
|
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 defined(memProfiler): nimProfile(size)
|
||||||
|
|
||||||
when not defined(nimSeqsV2):
|
when not defined(nimSeqsV2):
|
||||||
|
{.push overflowChecks: on.}
|
||||||
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
|
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
|
||||||
# `newObj` already uses locks, so no need for them here.
|
# `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)
|
result = newObj(typ, size)
|
||||||
cast[PGenericSeq](result).len = len
|
cast[PGenericSeq](result).len = len
|
||||||
cast[PGenericSeq](result).reserved = len
|
cast[PGenericSeq](result).reserved = len
|
||||||
when defined(memProfiler): nimProfile(size)
|
when defined(memProfiler): nimProfile(size)
|
||||||
|
|
||||||
proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
|
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)
|
result = newObj(typ, size)
|
||||||
cast[PGenericSeq](result).len = len
|
cast[PGenericSeq](result).len = len
|
||||||
cast[PGenericSeq](result).reserved = len
|
cast[PGenericSeq](result).reserved = len
|
||||||
when defined(memProfiler): nimProfile(size)
|
when defined(memProfiler): nimProfile(size)
|
||||||
|
{.pop.}
|
||||||
|
|
||||||
proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
|
proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
|
||||||
collectCT(gch, newsize + sizeof(Cell))
|
collectCT(gch, newsize + sizeof(Cell))
|
||||||
|
|
|
||||||
|
|
@ -334,20 +334,21 @@ proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl.} =
|
||||||
result = rawNewObj(tlRegion, typ, size)
|
result = rawNewObj(tlRegion, typ, size)
|
||||||
when defined(memProfiler): nimProfile(size)
|
when defined(memProfiler): nimProfile(size)
|
||||||
|
|
||||||
|
{.push overflowChecks: on.}
|
||||||
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
|
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
|
||||||
let size = roundup(addInt(mulInt(len, typ.base.size), GenericSeqSize),
|
let size = roundup(len * typ.base.size + GenericSeqSize, MemAlign)
|
||||||
MemAlign)
|
|
||||||
result = rawNewSeq(tlRegion, typ, size)
|
result = rawNewSeq(tlRegion, typ, size)
|
||||||
zeroMem(result, size)
|
zeroMem(result, size)
|
||||||
cast[PGenericSeq](result).len = len
|
cast[PGenericSeq](result).len = len
|
||||||
cast[PGenericSeq](result).reserved = len
|
cast[PGenericSeq](result).reserved = len
|
||||||
|
|
||||||
proc newStr(typ: PNimType, len: int; init: bool): pointer {.compilerRtl.} =
|
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)
|
result = rawNewSeq(tlRegion, typ, size)
|
||||||
if init: zeroMem(result, size)
|
if init: zeroMem(result, size)
|
||||||
cast[PGenericSeq](result).len = 0
|
cast[PGenericSeq](result).len = 0
|
||||||
cast[PGenericSeq](result).reserved = len
|
cast[PGenericSeq](result).reserved = len
|
||||||
|
{.pop.}
|
||||||
|
|
||||||
proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
|
proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
|
||||||
result = rawNewObj(tlRegion, typ, size)
|
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)
|
else: result = alloc(size)
|
||||||
if typ.finalizer != nil:
|
if typ.finalizer != nil:
|
||||||
boehmRegisterFinalizer(result, boehmgc_finalizer, typ.finalizer, nil, nil)
|
boehmRegisterFinalizer(result, boehmgc_finalizer, typ.finalizer, nil, nil)
|
||||||
|
{.push overflowChecks: on.}
|
||||||
proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.} =
|
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).len = len
|
||||||
cast[PGenericSeq](result).reserved = len
|
cast[PGenericSeq](result).reserved = len
|
||||||
|
{.pop.}
|
||||||
|
|
||||||
proc growObj(old: pointer, newsize: int): pointer =
|
proc growObj(old: pointer, newsize: int): pointer =
|
||||||
result = realloc(old, newsize)
|
result = realloc(old, newsize)
|
||||||
|
|
|
||||||
|
|
@ -19,10 +19,12 @@ proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} =
|
||||||
proc newObjNoInit(typ: PNimType, size: int): pointer =
|
proc newObjNoInit(typ: PNimType, size: int): pointer =
|
||||||
result = alloc(size)
|
result = alloc(size)
|
||||||
|
|
||||||
|
{.push overflowChecks: on.}
|
||||||
proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.} =
|
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).len = len
|
||||||
cast[PGenericSeq](result).reserved = len
|
cast[PGenericSeq](result).reserved = len
|
||||||
|
{.pop.}
|
||||||
|
|
||||||
proc growObj(old: pointer, newsize: int): pointer =
|
proc growObj(old: pointer, newsize: int): pointer =
|
||||||
result = realloc(old, newsize)
|
result = realloc(old, newsize)
|
||||||
|
|
|
||||||
|
|
@ -102,18 +102,20 @@ else:
|
||||||
include "system/gc"
|
include "system/gc"
|
||||||
|
|
||||||
when not declared(nimNewSeqOfCap) and not defined(nimSeqsV2):
|
when not declared(nimNewSeqOfCap) and not defined(nimSeqsV2):
|
||||||
|
{.push overflowChecks: on.}
|
||||||
proc nimNewSeqOfCap(typ: PNimType, cap: int): pointer {.compilerproc.} =
|
proc nimNewSeqOfCap(typ: PNimType, cap: int): pointer {.compilerproc.} =
|
||||||
when defined(gcRegions):
|
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)
|
result = newStr(typ, s, ntfNoRefs notin typ.base.flags)
|
||||||
else:
|
else:
|
||||||
let s = addInt(mulInt(cap, typ.base.size), GenericSeqSize)
|
let s = cap * typ.base.size + GenericSeqSize
|
||||||
when declared(newObjNoInit):
|
when declared(newObjNoInit):
|
||||||
result = if ntfNoRefs in typ.base.flags: newObjNoInit(typ, s) else: newObj(typ, s)
|
result = if ntfNoRefs in typ.base.flags: newObjNoInit(typ, s) else: newObj(typ, s)
|
||||||
else:
|
else:
|
||||||
result = newObj(typ, s)
|
result = newObj(typ, s)
|
||||||
cast[PGenericSeq](result).len = 0
|
cast[PGenericSeq](result).len = 0
|
||||||
cast[PGenericSeq](result).reserved = cap
|
cast[PGenericSeq](result).reserved = cap
|
||||||
|
{.pop.}
|
||||||
|
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ tassert_c.nim(35) tassert_c
|
||||||
tassert_c.nim(34) foo
|
tassert_c.nim(34) foo
|
||||||
assertions.nim(29) failedAssertImpl
|
assertions.nim(29) failedAssertImpl
|
||||||
assertions.nim(22) raiseAssert
|
assertions.nim(22) raiseAssert
|
||||||
fatal.nim(55) sysFatal"""
|
fatal.nim(49) sysFatal"""
|
||||||
|
|
||||||
proc tmatch(x, p: string): bool =
|
proc tmatch(x, p: string): bool =
|
||||||
var i = 0
|
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 """
|
discard """
|
||||||
outputsub: "value out of range: 50 [RangeError]"
|
outputsub: "value out of range: 50 notin 0 .. 40 [RangeError]"
|
||||||
exitcode: "1"
|
exitcode: "1"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
discard """
|
discard """
|
||||||
outputsub: "value out of range: 50 [RangeError]"
|
outputsub: "value out of range: 50 notin 0 .. 40 [RangeError]"
|
||||||
exitcode: "1"
|
exitcode: "1"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue