summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rwxr-xr-xcompiler/ast.nim3
-rwxr-xr-xcompiler/semstmts.nim72
-rwxr-xr-xcompiler/types.nim7
-rwxr-xr-xcompiler/wordrecg.nim7
4 files changed, 87 insertions, 2 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 10a3d8039..949c28c5f 100755
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -567,6 +567,9 @@ type
                               # for record types a nkRecord node
                               # for enum types a list of symbols
                               # else: unused
+    destructor*: PSym         # destructor. warning: nil here may not necessary
+                              # mean that there is no destructor.
+                              # see instantiateDestructor in types.nim
     owner*: PSym              # the 'owner' of the type
     sym*: PSym                # types have the sym associated with them
                               # it is used for converting types to strings
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 9c5efaf2d..f10c56880 100755
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -768,6 +768,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
@@ -856,6 +860,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}
@@ -893,11 +953,21 @@ proc SemStmt(c: PContext, n: PNode): PNode =
         return
       else:
         n.sons[i] = semStmt(c, n.sons[i])
-        if n.sons[i].kind in LastBlockStmts: 
+        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)
diff --git a/compiler/types.nim b/compiler/types.nim
index 15390bed7..633524eff 100755
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -1054,4 +1054,11 @@ proc getReturnType*(s: PSym): PType =
 proc getSize(typ: PType): biggestInt = 
   result = computeSize(typ)
   if result < 0: InternalError("getSize(" & $typ.kind & ')')
+
+proc instantiateDestructor*(typ: PType): bool =
+  # return true if the type already had a user-defined
+  # destructor or if the compiler generated a default
+  # member-wise one
+  if typ.destructor != nil: return true
+  return false
   
diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim
index 30a6b3c2c..af482966b 100755
--- a/compiler/wordrecg.nim
+++ b/compiler/wordrecg.nim
@@ -36,6 +36,9 @@ type
     wColon, wColonColon, wEquals, wDot, wDotDot,
     wStar, wMinus,
     wMagic, wThread, wFinal, wProfiler, wObjChecks,
+
+    wDestroy,
+    
     wImmediate, wImportCpp, wImportObjC,
     wImportCompilerProc,
     wImportc, wExportc, wIncompleteStruct,
@@ -110,7 +113,9 @@ const
 
     ":", "::", "=", ".", "..",
     "*", "-",
-    "magic", "thread", "final", "profiler", "objchecks", 
+    "magic", "thread", "final", "profiler", "objchecks",
+
+    "destroy",
     
     "immediate", "importcpp", "importobjc",
     "importcompilerproc", "importc", "exportc", "incompletestruct",