summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--changelog.md8
-rw-r--r--compiler/ast.nim4
-rw-r--r--compiler/ccgcalls.nim16
-rw-r--r--compiler/ccgexprs.nim126
-rw-r--r--compiler/ccgstmts.nim63
-rw-r--r--compiler/cgen.nim1
-rw-r--r--compiler/commands.nim4
-rw-r--r--compiler/condsyms.nim1
-rw-r--r--compiler/enumtostr.nim2
-rw-r--r--compiler/lineinfos.nim7
-rw-r--r--compiler/modulegraphs.nim1
-rw-r--r--compiler/options.nim11
-rw-r--r--compiler/semstmts.nim2
-rw-r--r--compiler/semtypes.nim3
-rw-r--r--compiler/sizealignoffsetimpl.nim2
-rw-r--r--doc/advopt.txt1
-rw-r--r--doc/manual.rst34
-rw-r--r--koch.nim2
-rw-r--r--lib/nimbase.h22
-rw-r--r--lib/system.nim5
-rw-r--r--lib/system/arithm.nim4
-rw-r--r--lib/system/chcks.nim18
-rw-r--r--lib/system/fatal.nim19
-rw-r--r--lib/system/gc.nim8
-rw-r--r--lib/system/gc_common.nim2
-rw-r--r--lib/system/gc_ms.nim6
-rw-r--r--lib/system/gc_regions.nim7
-rw-r--r--lib/system/integerops.nim132
-rw-r--r--lib/system/mm/boehm.nim4
-rw-r--r--lib/system/mm/none.nim4
-rw-r--r--lib/system/mmdisp.nim6
-rw-r--r--tests/assert/tassert_c.nim2
-rw-r--r--tests/destructor/tgotoexceptions7.nim52
-rw-r--r--tests/range/tsubrange2.nim2
-rw-r--r--tests/range/tsubrange3.nim2
35 files changed, 441 insertions, 142 deletions
diff --git a/changelog.md b/changelog.md
index 0fb99c6f4..fa753e7c4 100644
--- a/changelog.md
+++ b/changelog.md
@@ -161,6 +161,14 @@ echo f
 - The compiler now inferes "sink parameters". To disable this for a specific routine,
   annotate it with `.nosinks`. To disable it for a section of code, use
   `{.push sinkInference: off.}`...`{.pop.}`.
+- The compiler now supports a new switch `--panics:on` that turns runtime
+  errors like `IndexError` or `OverflowError` into fatal errors that **cannot**
+  be caught via Nim's `try` statement. `--panics:on` can improve the
+  runtime efficiency and code size of your program significantly.
+- The compiler now warns about inheriting directly from `system.Exception` as
+  this is **very bad** style. You should inherit from `ValueError`, `IOError`,
+  `OSError` or from a different specific exception type that inherits from
+  `CatchableError` and cannot be confused with a `Defect`.
 
 
 ## Bugfixes
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 62c301a43..55ad2ba4b 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -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]
 
diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim
index da5dd9b76..3e2cf2271 100644
--- a/compiler/ccgcalls.nim
+++ b/compiler/ccgcalls.nim
@@ -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)
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index 1bed4bc6c..4aad1a4f8 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -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:
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index 9c067a339..1c63ebaea 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -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, {})
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index 57424ec95..06507b6e6 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -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
diff --git a/compiler/commands.nim b/compiler/commands.nim
index 0c93654da..62d4d75dd 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -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:
diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim
index a84b0d878..da40dd2b7 100644
--- a/compiler/condsyms.nim
+++ b/compiler/condsyms.nim
@@ -113,3 +113,4 @@ proc initDefines*(symbols: StringTableRef) =
     defineSymbol("nimHasLibFFIEnabled")
 
   defineSymbol("nimHasSinkInference")
+  defineSymbol("nimNewIntegerOps")
diff --git a/compiler/enumtostr.nim b/compiler/enumtostr.nim
index 42d844609..614190ac3 100644
--- a/compiler/enumtostr.nim
+++ b/compiler/enumtostr.nim
@@ -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
diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim
index 5ac65201e..6eac5bf1b 100644
--- a/compiler/lineinfos.nim
+++ b/compiler/lineinfos.nim
@@ -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.}
 
diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim
index 3a6d28708..d3be4b3b6 100644
--- a/compiler/modulegraphs.nim
+++ b/compiler/modulegraphs.nim
@@ -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()
diff --git a/compiler/options.nim b/compiler/options.nim
index 42406272c..88251a42e 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -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.
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index b91e93025..cdb47d2a4 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -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
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index f05affc6b..e0be620c3 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -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:
diff --git a/compiler/sizealignoffsetimpl.nim b/compiler/sizealignoffsetimpl.nim
index ee88e276f..81d2f5faf 100644
--- a/compiler/sizealignoffsetimpl.nim
+++ b/compiler/sizealignoffsetimpl.nim
@@ -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")
diff --git a/doc/advopt.txt b/doc/advopt.txt
index 517edab5b..1ebec0f49 100644
--- a/doc/advopt.txt
+++ b/doc/advopt.txt
@@ -146,3 +146,4 @@ Advanced options:
                             see also https://nim-lang.github.io/Nim/estp.html
   --benchmarkVM:on|off      enable benchmarking of VM code with cpuTime()
   --sinkInference:on|off    en-/disable sink parameter inference (default: on)
+  --panics:on|off           turn panics into process terminations (default: off)
diff --git a/doc/manual.rst b/doc/manual.rst
index 516be24ea..3a0faed2e 100644
--- a/doc/manual.rst
+++ b/doc/manual.rst
@@ -105,13 +105,13 @@ identifier meanings, and in some cases expression values. An error detected
 during semantic analysis is called a `static error`:idx:. Errors described in
 this manual are static errors when not otherwise specified.
 
-A `checked runtime error`:idx: is an error that the implementation detects
+A `panic`:idx: is an error that the implementation detects
 and reports at runtime. The method for reporting such errors is via
 *raising exceptions* or *dying with a fatal error*. However, the implementation
 provides a means to disable these `runtime checks`:idx:. See the section
 pragmas_ for details.
 
-Whether a checked runtime error results in an exception or in a fatal error is
+Whether a panic results in an exception or in a fatal error is
 implementation specific. Thus the following program is invalid; even though the
 code purports to catch the `IndexError` from an out-of-bounds array access, the
 compiler may instead choose to allow the program to die with a fatal error.
@@ -124,6 +124,12 @@ compiler may instead choose to allow the program to die with a fatal error.
   except IndexError:
     echo "invalid index"
 
+The current implementation allows to switch between these different behaviors
+via ``--panics:on|off``. When panics are turned on, the program dies on a
+panic, if they are turned off the runtime errors are turned into
+exceptions. The benefit of ``--panics:on`` is that it produces smaller binary
+code and the compiler has more freedom to optimize the code.
+
 An `unchecked runtime error`:idx: is an error that is not guaranteed to be
 detected, and can cause the subsequent behavior of the computation to
 be arbitrary. Unchecked runtime errors cannot occur if only `safe`:idx:
@@ -869,9 +875,9 @@ Ordinal types have the following characteristics:
   the operation of functions as ``inc``, ``ord``, ``dec`` on ordinal types to
   be defined.
 - Ordinal values have a smallest possible value. Trying to count further
-  down than the smallest value gives a checked runtime or static error.
+  down than the smallest value produces a panic or a static error.
 - Ordinal values have a largest possible value. Trying to count further
-  than the largest value gives a checked runtime or static error.
+  than the largest value produces a panic or a static error.
 
 Integers, bool, characters and enumeration types (and subranges of these
 types) belong to ordinal types. For reasons of simplicity of implementation
@@ -982,7 +988,7 @@ lowest and highest value of the type. For example:
 to 5. ``PositiveFloat`` defines a subrange of all positive floating point values.
 NaN does not belong to any subrange of floating point types.
 Assigning any other value to a variable of type ``Subrange`` is a
-checked runtime error (or static error if it can be determined during
+panic (or a static error if it can be determined during
 semantic analysis). Assignments from the base type to one of its subrange types
 (and vice versa) are allowed.
 
@@ -1763,9 +1769,9 @@ Nil
 
 If a reference points to *nothing*, it has the value ``nil``. ``nil`` is also
 the default value for all ``ref`` and ``ptr`` types. Dereferencing ``nil``
-is an unrecoverable fatal runtime error. A dereferencing operation ``p[]``
-implies that ``p`` is not nil. This can be exploited by the implementation to
-optimize code like:
+is an unrecoverable fatal runtime error (and not a panic).
+A dereferencing operation ``p[]`` implies that ``p`` is not nil. This can be
+exploited by the implementation to optimize code like:
 
 
 .. code-block:: nim
@@ -4107,7 +4113,7 @@ branch always has to be ``void``:
 
 .. code-block:: nim
   from strutils import parseInt
-  
+
   let x = try: parseInt("133a")
           except: -1
           finally: echo "hi"
@@ -4242,9 +4248,11 @@ The exception tree is defined in the `system <system.html>`_ module.
 Every exception inherits from ``system.Exception``. Exceptions that indicate
 programming bugs inherit from ``system.Defect`` (which is a subtype of ``Exception``)
 and are stricly speaking not catchable as they can also be mapped to an operation
-that terminates the whole process. Exceptions that indicate any other runtime
-error that can be caught inherit from ``system.CatchableError``
-(which is a subtype of ``Exception``).
+that terminates the whole process. If panics are turned into exceptions, these
+exceptions inherit from `Defect`.
+
+Exceptions that indicate any other runtime error that can be caught inherit from
+``system.CatchableError`` (which is a subtype of ``Exception``).
 
 
 Imported exceptions
@@ -5667,7 +5675,7 @@ The ``include`` statement can be used outside of the top level, as such:
   # Module B
   proc main() =
     include A
-  
+
   main() # => Hello World!
 
 
diff --git a/koch.nim b/koch.nim
index c2f28f2e0..60d0e3544 100644
--- a/koch.nim
+++ b/koch.nim
@@ -475,7 +475,7 @@ proc hostInfo(): string =
 
 proc runCI(cmd: string) =
   doAssert cmd.len == 0, cmd # avoid silently ignoring
-  echo "runCI:", cmd
+  echo "runCI: ", cmd
   echo hostInfo()
   # note(@araq): Do not replace these commands with direct calls (eg boot())
   # as that would weaken our testing efforts.
diff --git a/lib/nimbase.h b/lib/nimbase.h
index 004ba170b..d25c4dad0 100644
--- a/lib/nimbase.h
+++ b/lib/nimbase.h
@@ -545,4 +545,26 @@ typedef int Nim_and_C_compiler_disagree_on_target_architecture[sizeof(NI) == siz
 #define NIM_CHECK_SIZE(typ, sz) \
   _Static_assert(sizeof(typ) == sz, "Nim & C disagree on type size")
 
+/* these exist to make the codegen logic simpler */
+#define nimModInt(a, b, res) (((*res) = (a) % (b)), 0)
+#define nimModInt64(a, b, res) (((*res) = (a) % (b)), 0)
+
+/* these exist because we cannot have .compilerProcs that are importc'ed
+   by a different name */
+
+#define nimAddInt64(a, b, res) __builtin_saddll_overflow(a, b, (long long int*)res)
+#define nimSubInt64(a, b, res) __builtin_ssubll_overflow(a, b, (long long int*)res)
+#define nimMulInt64(a, b, res) __builtin_smulll_overflow(a, b, (long long int*)res)
+
+#if NIM_INTBITS == 32
+  #define nimAddInt(a, b, res) __builtin_sadd_overflow(a, b, res)
+  #define nimSubInt(a, b, res) __builtin_ssub_overflow(a, b, res)
+  #define nimMulInt(a, b, res) __builtin_smul_overflow(a, b, res)
+#else
+  /* map it to the 'long long' variant */
+  #define nimAddInt(a, b, res) __builtin_saddll_overflow(a, b, (long long int*)res)
+  #define nimSubInt(a, b, res) __builtin_ssubll_overflow(a, b, (long long int*)res)
+  #define nimMulInt(a, b, res) __builtin_smulll_overflow(a, b, (long long int*)res)
+#endif
+
 #endif /* NIMBASE_H */
diff --git a/lib/system.nim b/lib/system.nim
index 5dff62806..602fbc1f1 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -2221,7 +2221,10 @@ when notJSnotNims:
 
   # we cannot compile this with stack tracing on
   # as it would recurse endlessly!
-  include "system/arithm"
+  when defined(nimNewIntegerOps):
+    include "system/integerops"
+  else:
+    include "system/arithm"
   {.pop.}
 
 
diff --git a/lib/system/arithm.nim b/lib/system/arithm.nim
index 16ac8affe..9e3ec79c4 100644
--- a/lib/system/arithm.nim
+++ b/lib/system/arithm.nim
@@ -409,13 +409,13 @@ when not declared(mulInt):
 # We avoid setting the FPU control word here for compatibility with libraries
 # written in other languages.
 
-proc raiseFloatInvalidOp {.noinline.} =
+proc raiseFloatInvalidOp {.compilerproc, noinline.} =
   sysFatal(FloatInvalidOpError, "FPU operation caused a NaN result")
 
 proc nanCheck(x: float64) {.compilerproc, inline.} =
   if x != x: raiseFloatInvalidOp()
 
-proc raiseFloatOverflow(x: float64) {.noinline.} =
+proc raiseFloatOverflow(x: float64) {.compilerproc, noinline.} =
   if x > 0.0:
     sysFatal(FloatOverflowError, "FPU operation caused an overflow")
   else:
diff --git a/lib/system/chcks.nim b/lib/system/chcks.nim
index 52642bbf9..117543fcd 100644
--- a/lib/system/chcks.nim
+++ b/lib/system/chcks.nim
@@ -28,6 +28,19 @@ proc raiseIndexError() {.compilerproc, noinline.} =
 proc raiseFieldError(f: string) {.compilerproc, noinline.} =
   sysFatal(FieldError, f)
 
+proc raiseRangeErrorI(i, a, b: BiggestInt) {.compilerproc, noinline.} =
+  sysFatal(RangeError, "value out of range: " & $i & " notin " & $a & " .. " & $b)
+
+proc raiseRangeErrorF(i, a, b: float) {.compilerproc, noinline.} =
+  sysFatal(RangeError, "value out of range: " & $i & " notin " & $a & " .. " & $b)
+
+proc raiseRangeErrorU(i, a, b: uint64) {.compilerproc, noinline.} =
+  # todo: better error reporting
+  sysFatal(RangeError, "value out of range")
+
+proc raiseObjectConversionError() {.compilerproc, noinline.} =
+  sysFatal(ObjectConversionError, "invalid object conversion")
+
 proc chckIndx(i, a, b: int): int =
   if i >= a and i <= b:
     return i
@@ -116,6 +129,5 @@ when not defined(nimV2):
     return true
 
 when defined(nimV2):
-  proc nimFieldDiscriminantCheckV2(oldDiscVal, newDiscVal: uint8) {.compilerproc.} =
-    if oldDiscVal != newDiscVal:
-      sysFatal(FieldError, "assignment to discriminant changes object branch")
+  proc raiseObjectCaseTransition() {.compilerproc.} =
+    sysFatal(FieldError, "assignment to discriminant changes object branch")
diff --git a/lib/system/fatal.nim b/lib/system/fatal.nim
index d68d06712..761e0dd69 100644
--- a/lib/system/fatal.nim
+++ b/lib/system/fatal.nim
@@ -24,7 +24,7 @@ when hostOS == "standalone":
     rawoutput(message)
     panic(arg)
 
-elif (defined(nimQuirky) or gotoBasedExceptions) and not defined(nimscript):
+elif (defined(nimQuirky) or defined(nimPanics)) and not defined(nimscript):
   import ansi_c
 
   proc name(t: typedesc): string {.magic: "TypeTrait".}
@@ -46,20 +46,9 @@ elif (defined(nimQuirky) or gotoBasedExceptions) and not defined(nimscript):
 
 else:
   proc sysFatal(exceptn: typedesc, message: string) {.inline, noreturn.} =
-    when declared(owned):
-      var e: owned(ref exceptn)
-    else:
-      var e: ref exceptn
-    new(e)
-    e.msg = message
-    raise e
+    raise (ref exceptn)(msg: message)
 
   proc sysFatal(exceptn: typedesc, message, arg: string) {.inline, noreturn.} =
-    when declared(owned):
-      var e: owned(ref exceptn)
-    else:
-      var e: ref exceptn
-    new(e)
-    e.msg = message & arg
-    raise e
+    raise (ref exceptn)(msg: message & arg)
+
 {.pop.}
diff --git a/lib/system/gc.nim b/lib/system/gc.nim
index 7be9f4b1f..15f316ccc 100644
--- a/lib/system/gc.nim
+++ b/lib/system/gc.nim
@@ -441,13 +441,15 @@ proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.} =
   zeroMem(result, size)
   when defined(memProfiler): nimProfile(size)
 
+{.push overflowChecks: on.}
 proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
   # `newObj` already uses locks, so no need for them here.
-  let size = addInt(mulInt(len, typ.base.size), GenericSeqSize)
+  let size = len * typ.base.size + GenericSeqSize
   result = newObj(typ, size)
   cast[PGenericSeq](result).len = len
   cast[PGenericSeq](result).reserved = len
   when defined(memProfiler): nimProfile(size)
+{.pop.}
 
 proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
   # generates a new object and sets its reference counter to 1
@@ -476,12 +478,14 @@ proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
   sysAssert(allocInv(gch.region), "newObjRC1 end")
   when defined(memProfiler): nimProfile(size)
 
+{.push overflowChecks: on.}
 proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
-  let size = addInt(mulInt(len, typ.base.size), GenericSeqSize)
+  let size = len * typ.base.size + GenericSeqSize
   result = newObjRC1(typ, size)
   cast[PGenericSeq](result).len = len
   cast[PGenericSeq](result).reserved = len
   when defined(memProfiler): nimProfile(size)
+{.pop.}
 
 proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
   collectCT(gch)
diff --git a/lib/system/gc_common.nim b/lib/system/gc_common.nim
index fe07766d9..ff2b6ad6a 100644
--- a/lib/system/gc_common.nim
+++ b/lib/system/gc_common.nim
@@ -79,7 +79,7 @@ template decTypeSize(cell, t) =
     if t.kind in {tyString, tySequence}:
       let cap = cast[PGenericSeq](cellToUsr(cell)).space
       let size = if t.kind == tyString: cap+1+GenericSeqSize
-                 else: addInt(mulInt(cap, t.base.size), GenericSeqSize)
+                 else: cap * t.base.size + GenericSeqSize
       atomicDec t.sizes, size+sizeof(Cell)
     else:
       atomicDec t.sizes, t.base.size+sizeof(Cell)
diff --git a/lib/system/gc_ms.nim b/lib/system/gc_ms.nim
index 271543445..a026b404b 100644
--- a/lib/system/gc_ms.nim
+++ b/lib/system/gc_ms.nim
@@ -305,20 +305,22 @@ proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
   when defined(memProfiler): nimProfile(size)
 
 when not defined(nimSeqsV2):
+  {.push overflowChecks: on.}
   proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
     # `newObj` already uses locks, so no need for them here.
-    let size = addInt(mulInt(len, typ.base.size), GenericSeqSize)
+    let size = len * typ.base.size + GenericSeqSize
     result = newObj(typ, size)
     cast[PGenericSeq](result).len = len
     cast[PGenericSeq](result).reserved = len
     when defined(memProfiler): nimProfile(size)
 
   proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
-    let size = addInt(mulInt(len, typ.base.size), GenericSeqSize)
+    let size = len * typ.base.size + GenericSeqSize
     result = newObj(typ, size)
     cast[PGenericSeq](result).len = len
     cast[PGenericSeq](result).reserved = len
     when defined(memProfiler): nimProfile(size)
+  {.pop.}
 
   proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
     collectCT(gch, newsize + sizeof(Cell))
diff --git a/lib/system/gc_regions.nim b/lib/system/gc_regions.nim
index 6b1640ab1..38365469e 100644
--- a/lib/system/gc_regions.nim
+++ b/lib/system/gc_regions.nim
@@ -334,20 +334,21 @@ proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl.} =
   result = rawNewObj(tlRegion, typ, size)
   when defined(memProfiler): nimProfile(size)
 
+{.push overflowChecks: on.}
 proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
-  let size = roundup(addInt(mulInt(len, typ.base.size), GenericSeqSize),
-                     MemAlign)
+  let size = roundup(len * typ.base.size + GenericSeqSize, MemAlign)
   result = rawNewSeq(tlRegion, typ, size)
   zeroMem(result, size)
   cast[PGenericSeq](result).len = len
   cast[PGenericSeq](result).reserved = len
 
 proc newStr(typ: PNimType, len: int; init: bool): pointer {.compilerRtl.} =
-  let size = roundup(addInt(len, GenericSeqSize), MemAlign)
+  let size = roundup(len + GenericSeqSize, MemAlign)
   result = rawNewSeq(tlRegion, typ, size)
   if init: zeroMem(result, size)
   cast[PGenericSeq](result).len = 0
   cast[PGenericSeq](result).reserved = len
+{.pop.}
 
 proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
   result = rawNewObj(tlRegion, typ, size)
diff --git a/lib/system/integerops.nim b/lib/system/integerops.nim
new file mode 100644
index 000000000..dc0197f14
--- /dev/null
+++ b/lib/system/integerops.nim
@@ -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")
diff --git a/lib/system/mm/boehm.nim b/lib/system/mm/boehm.nim
index d02d52b52..a8b0e17b4 100644
--- a/lib/system/mm/boehm.nim
+++ b/lib/system/mm/boehm.nim
@@ -101,10 +101,12 @@ proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} =
   else: result = alloc(size)
   if typ.finalizer != nil:
     boehmRegisterFinalizer(result, boehmgc_finalizer, typ.finalizer, nil, nil)
+{.push overflowChecks: on.}
 proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.} =
-  result = newObj(typ, addInt(mulInt(len, typ.base.size), GenericSeqSize))
+  result = newObj(typ, len * typ.base.size + GenericSeqSize)
   cast[PGenericSeq](result).len = len
   cast[PGenericSeq](result).reserved = len
+{.pop.}
 
 proc growObj(old: pointer, newsize: int): pointer =
   result = realloc(old, newsize)
diff --git a/lib/system/mm/none.nim b/lib/system/mm/none.nim
index 3572b062c..0079daac3 100644
--- a/lib/system/mm/none.nim
+++ b/lib/system/mm/none.nim
@@ -19,10 +19,12 @@ proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} =
 proc newObjNoInit(typ: PNimType, size: int): pointer =
   result = alloc(size)
 
+{.push overflowChecks: on.}
 proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.} =
-  result = newObj(typ, addInt(mulInt(len, typ.base.size), GenericSeqSize))
+  result = newObj(typ, len * typ.base.size + GenericSeqSize)
   cast[PGenericSeq](result).len = len
   cast[PGenericSeq](result).reserved = len
+{.pop.}
 
 proc growObj(old: pointer, newsize: int): pointer =
   result = realloc(old, newsize)
diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim
index d2d0d576f..2c3394df9 100644
--- a/lib/system/mmdisp.nim
+++ b/lib/system/mmdisp.nim
@@ -102,18 +102,20 @@ else:
     include "system/gc"
 
 when not declared(nimNewSeqOfCap) and not defined(nimSeqsV2):
+  {.push overflowChecks: on.}
   proc nimNewSeqOfCap(typ: PNimType, cap: int): pointer {.compilerproc.} =
     when defined(gcRegions):
-      let s = mulInt(cap, typ.base.size)  # newStr already adds GenericSeqSize
+      let s = cap * typ.base.size  # newStr already adds GenericSeqSize
       result = newStr(typ, s, ntfNoRefs notin typ.base.flags)
     else:
-      let s = addInt(mulInt(cap, typ.base.size), GenericSeqSize)
+      let s = cap * typ.base.size + GenericSeqSize
       when declared(newObjNoInit):
         result = if ntfNoRefs in typ.base.flags: newObjNoInit(typ, s) else: newObj(typ, s)
       else:
         result = newObj(typ, s)
       cast[PGenericSeq](result).len = 0
       cast[PGenericSeq](result).reserved = cap
+  {.pop.}
 
 {.pop.}
 
diff --git a/tests/assert/tassert_c.nim b/tests/assert/tassert_c.nim
index 98b859c1c..e9e6b5192 100644
--- a/tests/assert/tassert_c.nim
+++ b/tests/assert/tassert_c.nim
@@ -8,7 +8,7 @@ tassert_c.nim(35)        tassert_c
 tassert_c.nim(34)        foo
 assertions.nim(29)       failedAssertImpl
 assertions.nim(22)       raiseAssert
-fatal.nim(55)            sysFatal"""
+fatal.nim(49)            sysFatal"""
 
 proc tmatch(x, p: string): bool =
   var i = 0
diff --git a/tests/destructor/tgotoexceptions7.nim b/tests/destructor/tgotoexceptions7.nim
new file mode 100644
index 000000000..30fe28e7c
--- /dev/null
+++ b/tests/destructor/tgotoexceptions7.nim
@@ -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)
diff --git a/tests/range/tsubrange2.nim b/tests/range/tsubrange2.nim
index e0fb71c5f..1b90b52c7 100644
--- a/tests/range/tsubrange2.nim
+++ b/tests/range/tsubrange2.nim
@@ -1,5 +1,5 @@
 discard """
-  outputsub: "value out of range: 50 [RangeError]"
+  outputsub: "value out of range: 50 notin 0 .. 40 [RangeError]"
   exitcode: "1"
 """
 
diff --git a/tests/range/tsubrange3.nim b/tests/range/tsubrange3.nim
index d3aae87df..ba9e6a143 100644
--- a/tests/range/tsubrange3.nim
+++ b/tests/range/tsubrange3.nim
@@ -1,5 +1,5 @@
 discard """
-  outputsub: "value out of range: 50 [RangeError]"
+  outputsub: "value out of range: 50 notin 0 .. 40 [RangeError]"
   exitcode: "1"
 """