summary refs log tree commit diff stats
path: root/compiler/semstmts.nim
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/semstmts.nim')
-rwxr-xr-xcompiler/semstmts.nim106
1 files changed, 98 insertions, 8 deletions
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 202b7416a..82f43e787 100755
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -27,7 +27,7 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
     case it.kind
     of nkElifBranch, nkElifExpr: 
       checkSonsLen(it, 2)
-      var e = semAndEvalConstExpr(c, it.sons[0])
+      var e = semConstExpr(c, it.sons[0])
       if e.kind != nkIntLit: InternalError(n.info, "semWhen")
       if e.intVal != 0 and result == nil:
         setResult(it.sons[1]) 
@@ -772,6 +772,10 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
       incl(s.flags, sfForward)
     elif sfBorrow in s.flags: semBorrow(c, n, s)
   sideEffectsCheck(c, s)
+  if result.sons[namePos].sym.name.id == ord(wDestroy):
+    if s.typ.sons.len == 2:
+      let typ = s.typ.sons[1].skipTypes({tyVar})
+      typ.destructor = s
   if s.typ.callConv == ccClosure and s.owner.kind == skModule:
     localError(s.info, errXCannotBeClosure, s.name.s)
   closeScope(c.tab)           # close scope for parameters
@@ -860,6 +864,62 @@ proc semStaticStmt(c: PContext, n: PNode): PNode =
   if result.isNil:
     LocalError(n.info, errCannotInterpretNodeX, renderTree(n))
 
+proc insertDestructors(c: PContext, varSection: PNode):
+  tuple[outer: PNode, inner: PNode] =
+  # Accepts a var or let section.
+  #
+  # When a var section has variables with destructors
+  # the var section is split up and finally blocks are inserted
+  # immediately after all "destructable" vars
+  #
+  # In case there were no destrucable variables, the proc returns
+  # (nil, nil) and the enclosing stmt-list requires no modifications.
+  #
+  # Otherwise, after the try blocks are created, the rest of the enclosing
+  # stmt-list should be inserted in the most `inner` such block (corresponding
+  # to the last variable).
+  #
+  # `outer` is a statement list that should replace the original var section.
+  # It will include the new truncated var section followed by the outermost
+  # try block.
+  let totalVars = varSection.sonsLen
+  for j in countup(0, totalVars - 1):
+    let
+      varId = varSection[j][0]
+      varTyp = varId.sym.typ
+      info = varId.info
+
+    if varTyp != nil and instantiateDestructor(varTyp):
+      var tryStmt = newNodeI(nkTryStmt, info)
+
+      if j < totalVars - 1:
+        var remainingVars = newNodeI(varSection.kind, info)
+        remainingVars.sons = varSection.sons[(j+1)..(-1)]
+        let (outer, inner) = insertDestructors(c, remainingVars)
+        if outer != nil:
+          tryStmt.addSon(outer)
+          result.inner = inner
+        else:
+          result.inner = newNodeI(nkStmtList, info)
+          result.inner.addSon(remainingVars)
+          tryStmt.addSon(result.inner)
+      else:
+        result.inner = newNodeI(nkStmtList, info)
+        tryStmt.addSon(result.inner)
+
+      tryStmt.addSon(
+        newNode(nkFinally, info, @[
+          semStmt(c, newNode(nkCall, info, @[
+            semSym(c, varId, varTyp.destructor, {}),
+            semSym(c, varId, varId.sym, {})]))]))
+
+      result.outer = newNodeI(nkStmtList, info)
+      varSection.sons.setLen(j+1)
+      result.outer.addSon(varSection)
+      result.outer.addSon(tryStmt)
+
+      return
+
 proc SemStmt(c: PContext, n: PNode): PNode = 
   const                       # must be last statements in a block:
     LastBlockStmts = {nkRaiseStmt, nkReturnStmt, nkBreakStmt, nkContinueStmt}
@@ -875,13 +935,43 @@ proc SemStmt(c: PContext, n: PNode): PNode =
   of nkBlockStmt: result = semBlock(c, n)
   of nkStmtList: 
     var length = sonsLen(n)
-    for i in countup(0, length - 1): 
-      n.sons[i] = semStmt(c, n.sons[i])
-      if n.sons[i].kind in LastBlockStmts: 
-        for j in countup(i + 1, length - 1): 
-          case n.sons[j].kind
-          of nkPragma, nkCommentStmt, nkNilLit, nkEmpty: nil
-          else: localError(n.sons[j].info, errStmtInvalidAfterReturn)
+    for i in countup(0, length - 1):
+      case n.sons[i].kind
+      of nkFinally, nkExceptBranch:
+        # stand-alone finally and except blocks are
+        # transformed into regular try blocks:
+        #
+        # var f = fopen("somefile") | var f = fopen("somefile")
+        # finally: fcsole(f)        | try:
+        # ...                       |   ...
+        #                           | finally:
+        #                           |   fclose(f)
+        var tryStmt = newNodeI(nkTryStmt, n.sons[i].info)
+        var body = newNodeI(nkStmtList, n.sons[i].info)
+        if i < n.sonsLen - 1:
+          body.sons = n.sons[(i+1)..(-1)]
+        tryStmt.addSon(body)
+        tryStmt.addSon(n.sons[i])
+        n.sons[i] = semTry(c, tryStmt)
+        n.sons.setLen(i+1)
+        return
+      else:
+        n.sons[i] = semStmt(c, n.sons[i])
+        case n.sons[i].kind
+        of nkVarSection, nkLetSection:
+          let (outer, inner) = insertDestructors(c, n.sons[i])
+          if outer != nil:
+            n.sons[i] = outer
+            for j in countup(i+1, length-1):
+              inner.addSon(SemStmt(c, n.sons[j]))
+            n.sons.setLen(i+1)
+            return
+        of LastBlockStmts: 
+          for j in countup(i + 1, length - 1): 
+            case n.sons[j].kind
+            of nkPragma, nkCommentStmt, nkNilLit, nkEmpty: nil
+            else: localError(n.sons[j].info, errStmtInvalidAfterReturn)
+        else: nil
   of nkRaiseStmt: result = semRaise(c, n)
   of nkVarSection: result = semVarOrLet(c, n, skVar)
   of nkLetSection: result = semVarOrLet(c, n, skLet)