summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
authorAudun Wilhelmsen <skyfex@gmail.com>2014-02-23 00:23:06 +0100
committerAudun Wilhelmsen <skyfex@gmail.com>2014-02-23 00:23:06 +0100
commit739b4f214b62f55f5fcfc8c71a246c385146fca8 (patch)
treede9c1e84d0ff945fe79e6f0d473fd0b2c97066ae /compiler
parentef379d0a10e800982d4a10ad623d2426d68e830d (diff)
downloadNim-739b4f214b62f55f5fcfc8c71a246c385146fca8.tar.gz
Fixed #688 : return in except statments. Also fixed return in finally statements.
Diffstat (limited to 'compiler')
-rw-r--r--compiler/ccgstmts.nim41
-rw-r--r--compiler/cgendata.nim7
2 files changed, 30 insertions, 18 deletions
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index 443d845f6..4576a54b5 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -263,33 +263,33 @@ proc genIf(p: BProc, n: PNode, d: var TLoc) =
 
 
 proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) = 
-  # This is called by return and break stmts.
-  # When jumping out of try/except/finally stmts,
-  # we need to pop safe points from try statements,
-  # execute finally-stmts, and pop exceptions
-  # from except stmts
+  # Called by return and break stmts.
+  # Deals with issues faced when jumping out of try/except/finally stmts,
 
-  let L = p.nestedTryStmts.len
-
-  # danger of endless recursion! we workaround this here by a temp stack
   var stack: seq[PNode]
-  newSeq(stack, howManyTrys)
-  for i in countup(1, howManyTrys): 
-    stack[i-1] = p.nestedTryStmts[L-i]
-  setLen(p.nestedTryStmts, L-howManyTrys)
+  newSeq(stack, 0)
   
   var alreadyPoppedCnt = p.inExceptBlock
-  for tryStmt in items(stack):
+  for i in countup(1, howManyTrys):
+
     if gCmd != cmdCompileToCpp:
+      # Pop safe points generated by try
       if alreadyPoppedCnt > 0:
         dec alreadyPoppedCnt
       else:
         linefmt(p, cpsStmts, "#popSafePoint();$n")
-    # Find finally-stmts for this try-stmt
-    # and generate a copy of the finally stmts here
+
+    # Pop this try-stmt of the list of nested trys
+    # so we don't infinite recurse on it in the next step.
+    var tryStmt = p.nestedTryStmts.pop
+    stack.add(tryStmt)
+
+    # Find finally-stmt for this try-stmt
+    # and generate a copy of its sons
     var finallyStmt = lastSon(tryStmt)
     if finallyStmt.kind == nkFinally: 
       genStmts(p, finallyStmt.sons[0])
+
   # push old elements again:
   for i in countdown(howManyTrys-1, 0): 
     p.nestedTryStmts.add(stack[i])
@@ -304,7 +304,14 @@ proc genReturnStmt(p: BProc, t: PNode) =
   p.beforeRetNeeded = true
   genLineDir(p, t)
   if (t.sons[0].kind != nkEmpty): genStmts(p, t.sons[0])
-  blockLeaveActions(p, min(1, p.nestedTryStmts.len), p.inExceptBlock)
+  blockLeaveActions(p, 
+    howManyTrys    = p.nestedTryStmts.len,
+    howManyExcepts = p.inExceptBlock)
+  if (p.finallySafePoints.len > 0):
+    # If we're in a finally block, and we came here by exception
+    # consume it before we return.
+    var safePoint = p.finallySafePoints[p.finallySafePoints.len-1]
+    linefmt(p, cpsStmts, "if ($1.status != 0) #popCurrentException();$n", safePoint)    
   lineFF(p, cpsStmts, "goto BeforeRet;$n", "br label %BeforeRet$n", [])
 
 proc genComputedGoto(p: BProc; n: PNode) =
@@ -843,7 +850,9 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
   discard pop(p.nestedTryStmts)
   endBlock(p) # end of else block
   if i < length and t.sons[i].kind == nkFinally:
+    p.finallySafePoints.add(safePoint)
     exprBlock(p, t.sons[i].sons[0], d)
+    discard pop(p.finallySafePoints)
   linefmt(p, cpsStmts, "if ($1.status != 0) #reraiseException();$n", safePoint)
 
 proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): PRope =
diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim
index 71479abdd..0df7bb6dc 100644
--- a/compiler/cgendata.nim
+++ b/compiler/cgendata.nim
@@ -65,11 +65,13 @@ type
     prc*: PSym                # the Nimrod proc that this C proc belongs to
     beforeRetNeeded*: bool    # true iff 'BeforeRet' label for proc is needed
     threadVarAccessed*: bool  # true if the proc already accessed some threadvar
-    nestedTryStmts*: seq[PNode] # in how many nested try statements we are
-                                # (the vars must be volatile then)
+    nestedTryStmts*: seq[PNode]   # in how many nested try statements we are
+                                  # (the vars must be volatile then)
     inExceptBlock*: int       # are we currently inside an except block?
                               # leaving such scopes by raise or by return must
                               # execute any applicable finally blocks
+    finallySafePoints*: seq[PRope]  # For correctly cleaning up exceptions when
+                                    # using return in finally statements
     labels*: Natural          # for generating unique labels in the C proc
     blocks*: seq[TBlock]      # nested blocks
     breakIdx*: int            # the block that will be exited
@@ -142,6 +144,7 @@ proc newProc*(prc: PSym, module: BModule): BProc =
   else: result.options = gOptions
   newSeq(result.blocks, 1)
   result.nestedTryStmts = @[]
+  result.finallySafePoints = @[]
 
 iterator cgenModules*: var BModule =
   for i in 0..high(gModules):