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
commit a6682de004
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
35 changed files with 441 additions and 142 deletions

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