summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/ast.nim14
-rw-r--r--compiler/ccgstmts.nim142
-rw-r--r--compiler/ccgtypes.nim2
-rw-r--r--compiler/cgen.nim5
-rw-r--r--compiler/semstmts.nim14
-rw-r--r--lib/nimbase.h7
-rw-r--r--lib/system/excpt.nim2
-rw-r--r--tests/exception/tdont_overwrite_typename.nim1
-rw-r--r--tests/exception/texcas.nim1
-rw-r--r--tests/exception/tnestedreturn.nim1
10 files changed, 110 insertions, 79 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 0639ebf8e..02f5ae67e 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -1657,6 +1657,20 @@ proc toObject*(typ: PType): PType =
   if result.kind == tyRef:
     result = result.lastSon
 
+proc isException*(t: PType): bool =
+  # check if `y` is object type and it inherits from Exception
+  assert(t != nil)
+
+  if t.kind != tyObject: 
+    return false
+
+  var base = t
+  while base != nil:
+    if base.sym.magic == mException:
+      return true
+    base = base.lastSon
+  return false
+
 proc findUnresolvedStatic*(n: PNode): PNode =
   if n.kind == nkSym and n.typ.kind == tyStatic and n.typ.n == nil:
     return n
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index c085c82db..9c7bced33 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -779,91 +779,111 @@ proc genCase(p: BProc, t: PNode, d: var TLoc) =
     else:
       genOrdinalCase(p, t, d)
 
+
 proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
   # code to generate:
   #
-  # XXX: There should be a standard dispatch algorithm
-  # that's used both here and with multi-methods
-  #
   #   try
   #   {
   #      myDiv(4, 9);
-  #   } catch (NimException& exp) {
-  #      if (isObj(exp, EIO) {
-  #        ...
-  #      } else if (isObj(exp, ESystem) {
-  #        ...
-  #        finallyPart()
-  #        raise;
-  #      } else {
-  #        // general handler
-  #      }
-  #  }
-  #  finallyPart();
+  #   } catch (NimExceptionType1&) {
+  #      body
+  #      goto LA_END;
+  #   } catch (NimExceptionType2&) {
+  #      finallyPart()
+  #      raise;
+  #      goto LA_END;
+  #   } catch (NimExceptionType3&) {goto LA1;}
+  #   } catch (NimExceptionType4&) {goto LA1;}
+  #   } catch (NimExceptionType5&) {goto LA2;}
+  #   } catch (NimExceptionType6&) {goto LA2;}
+  #   catch(...) {
+  #     // general handler
+  #     goto LA_END;
+  #   }
+  #   {LA1:
+  #      labeled_branch_body_LA1
+  #      goto LA_END;
+  #   }
+  #   {LA2:
+  #      labeled_branch_body_LA2
+  #      finallyPart()
+  #      raise;
+  #      goto LA_END;
+  #   }
+  #   LA_END:  
+  #   finallyPart();
+ 
+  template genExceptBranchBody(body: PNode) {.dirty.} =
+    if optStackTrace in p.options:
+      linefmt(p, cpsStmts, "#setFrame((TFrame*)&FR_);$n") 
+    expr(p, body, d)
+    linefmt(p, cpsStmts, "#popCurrentException();$n")
+    linefmt(p, cpsStmts, "goto $1;$n", end_label)
+    
+
   if not isEmptyType(t.typ) and d.k == locNone:
     getTemp(p, t.typ, d)
   genLineDir(p, t)
-  let exc = getTempName(p.module)
-  if getCompilerProc("Exception") != nil:
-    discard cgsym(p.module, "Exception")
-  else:
-    discard cgsym(p.module, "E_Base")
+
+  discard cgsym(p.module, "Exception")
   add(p.nestedTryStmts, t)
   startBlock(p, "try {$n")
-  expr(p, t.sons[0], d)
-  let length = sonsLen(t)
-  endBlock(p, ropecg(p.module, "} catch (NimException& $1) {$n", [exc]))
-  if optStackTrace in p.options:
-    linefmt(p, cpsStmts, "#setFrame((TFrame*)&FR_);$n")
-  inc p.inExceptBlock
-  var i = 1
+  expr(p, t[0], d)
+  endBlock(p, ropecg(p.module, "}"))
+
+  let end_label = getLabel(p)
   var catchAllPresent = false
-  while (i < length) and (t.sons[i].kind == nkExceptBranch):
+  var labeled_branches: seq[tuple[label: Rope, body: PNode]] = @[] # generated after labels discovered
+
+  inc p.inExceptBlock
+  for i in 1..<t.len:
+    if t[i].kind != nkExceptBranch: break
+
     # bug #4230: avoid false sharing between branches:
     if d.k == locTemp and isEmptyType(t.typ): d.k = locNone
-    let blen = sonsLen(t.sons[i])
-    if i > 1: addf(p.s(cpsStmts), "else ", [])
-    if blen == 1:
+
+    if t[i].len == 1:
       # general except section:
       catchAllPresent = true
-      startBlock(p)
-      expr(p, t.sons[i].sons[0], d)
-      linefmt(p, cpsStmts, "#popCurrentException();$n")
-      endBlock(p)
-    else:
-      var orExpr: Rope = nil
-      for j in countup(0, blen - 2):
-        assert(t.sons[i].sons[j].kind == nkType)
-        if orExpr != nil: add(orExpr, "||")
-        appcg(p.module, orExpr,
-              "#isObj($1.exp->m_type, $2)",
-              [exc, genTypeInfo(p.module, t[i][j].typ, t[i][j].info)])
-      lineF(p, cpsStmts, "if ($1) ", [orExpr])
-      startBlock(p)
-      expr(p, t.sons[i].sons[blen-1], d)
-      linefmt(p, cpsStmts, "#popCurrentException();$n")
+      startBlock(p, "catch (...) {$n")
+      genExceptBranchBody(t[i][0])
       endBlock(p)
-    inc(i)
 
-  # reraise the exception if there was no catch all
-  # and none of the handlers matched
-  if not catchAllPresent:
-    if i > 1: lineF(p, cpsStmts, "else ", [])
-    startBlock(p)
-    var finallyBlock = t.lastSon
-    if finallyBlock.kind == nkFinally:
-      #expr(p, finallyBlock.sons[0], d)
-      genStmts(p, finallyBlock.sons[0])
+    elif t[i].len == 2:
+      startBlock(p, "catch ($1*) {$n", getTypeDesc(p.module, t[i][0].typ))
+      genExceptBranchBody(t[i][^1])
+      endBlock(p)
 
+    else:
+      # cpp can't catch multiple types in one statement so we need a label and goto
+      let label = getLabel(p)    
+      labeled_branches.add((label, t[i][^1]))
+      for j in 0..t[i].len-2:
+        assert(t[i][j].kind == nkType)
+        linefmt(p, cpsStmts, "catch ($1*) {goto $2;}$n",
+                           [getTypeDesc(p.module, t[i][j].typ), label])
+
+  if not catchAllPresent and t[^1].kind == nkFinally:
+    # finally requires catch all presence
+    startBlock(p, "catch (...) {$n")
+    genSimpleBlock(p, t[^1][0])
     line(p, cpsStmts, ~"throw;$n")
     endBlock(p)
 
-  lineF(p, cpsStmts, "}$n", []) # end of catch block
-  dec p.inExceptBlock
+  # generate labeled branches bodies
+  for label, body in labeled_branches.items():
+    startBlock(p)
+    fixLabel(p, label)
+    genExceptBranchBody(body)
+    endBlock(p)
+  fixLabel(p, end_label)
 
+  dec p.inExceptBlock
   discard pop(p.nestedTryStmts)
-  if (i < length) and (t.sons[i].kind == nkFinally):
-    genSimpleBlock(p, t.sons[i].sons[0])
+
+  if t[^1].kind == nkFinally:
+    genSimpleBlock(p, t[^1][0])
 
 proc genTry(p: BProc, t: PNode, d: var TLoc) =
   # code to generate:
diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim
index 3a001ebf7..6f9da56e3 100644
--- a/compiler/ccgtypes.nim
+++ b/compiler/ccgtypes.nim
@@ -569,6 +569,8 @@ proc getRecordDesc(m: BModule, typ: PType, name: Rope,
     elif m.compileToCpp:
       appcg(m, result, " : public $1 {$n",
                       [getTypeDescAux(m, typ.sons[0].skipTypes(skipPtrs), check)])
+      if typ.isException:
+        appcg(m, result, "virtual void raise() {throw this;}$n") # required for polymorphic exceptions
       hasField = true
     else:
       appcg(m, result, " {$n  $1 Sup;$n",
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index ed9e4b7a4..8b3da223f 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -260,6 +260,11 @@ proc rdCharLoc(a: TLoc): Rope =
 
 proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: TLoc,
                    takeAddr: bool) =
+  if p.module.compileToCpp and t.isException:
+    # init vtable in Exception object for polymorphic exceptions
+    includeHeader(p.module, "<new>")
+    linefmt(p, section, "new ($1) $2;$n", rdLoc(a), getTypeDesc(p.module, t))
+
   case analyseObjectWithTypeField(t)
   of frNone:
     discard
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index f0784021e..18b9a347b 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -737,16 +737,10 @@ proc semRaise(c: PContext, n: PNode): PNode =
       localError(n.info, errExprCannotBeRaised)
 
     # check if the given object inherits from Exception
-    var base = typ.lastSon
-    while true:
-      if base.sym.magic == mException:
-        break
-      if base.lastSon == nil:
-        localError(n.info,
-          "raised object of type $1 does not inherit from Exception",
-          [typeToString(typ)])
-        return
-      base = base.lastSon
+    if not typ.lastSon.isException():
+        localError(n.info, "raised object of type $1 does not inherit from Exception",
+                           [typeToString(typ)])
+
 
 proc addGenericParamListToScope(c: PContext, n: PNode) =
   if n.kind != nkGenericParams: illFormedAst(n)
diff --git a/lib/nimbase.h b/lib/nimbase.h
index 69699a2a4..a03407c4f 100644
--- a/lib/nimbase.h
+++ b/lib/nimbase.h
@@ -274,13 +274,6 @@ __clang__
 #  endif
 #  define NIM_BOOL bool
 #  define NIM_NIL 0
-struct NimException
-{
-  NimException(struct Exception* exp, const char* msg): exp(exp), msg(msg) {}
-
-  struct Exception* exp;
-  const char* msg;
-};
 #else
 #  ifdef bool
 #    define NIM_BOOL bool
diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim
index 3d75a8704..476582af2 100644
--- a/lib/system/excpt.nim
+++ b/lib/system/excpt.nim
@@ -320,7 +320,7 @@ proc raiseExceptionAux(e: ref Exception) =
       quitOrDebug()
     else:
       pushCurrentException(e)
-      {.emit: "throw NimException(`e`, `e`->name);".}
+      {.emit: "`e`->raise();".}
   else:
     if excHandler != nil:
       if not excHandler.hasRaiseAction or excHandler.raiseAction(e):
diff --git a/tests/exception/tdont_overwrite_typename.nim b/tests/exception/tdont_overwrite_typename.nim
index 147ccc001..6e3ff816f 100644
--- a/tests/exception/tdont_overwrite_typename.nim
+++ b/tests/exception/tdont_overwrite_typename.nim
@@ -1,4 +1,5 @@
 discard """
+  targets: "c cpp"
   output: '''Check passed
 Check passed'''
 """
diff --git a/tests/exception/texcas.nim b/tests/exception/texcas.nim
index fee45af3f..298aee707 100644
--- a/tests/exception/texcas.nim
+++ b/tests/exception/texcas.nim
@@ -1,4 +1,5 @@
 discard """
+  targets: "c cpp"
   output: '''Hello
 Hello
   '''
diff --git a/tests/exception/tnestedreturn.nim b/tests/exception/tnestedreturn.nim
index 1480764f1..bf26f4903 100644
--- a/tests/exception/tnestedreturn.nim
+++ b/tests/exception/tnestedreturn.nim
@@ -1,4 +1,5 @@
 discard """
+  targets: "c cpp"
   file: "tnestedreturn.nim"
   output: "A\nB\nC\n"
 """