summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorZahary Karadjov <zahary@gmail.com>2012-06-06 18:34:35 +0300
committerZahary Karadjov <zahary@gmail.com>2012-06-06 19:11:52 +0300
commitd10b524c9a52c1d13ca175ac9781c85fad22b0f7 (patch)
treef8ce522ecb4cd01eadeb9d77905a5350696cf084
parent41a9a941ab627acb413f067a38406d7908dbd364 (diff)
downloadNim-d10b524c9a52c1d13ca175ac9781c85fad22b0f7.tar.gz
generate default destructors
-rwxr-xr-xcompiler/semdata.nim6
-rwxr-xr-xcompiler/semstmts.nim106
-rwxr-xr-xcompiler/types.nim7
-rwxr-xr-xlib/system/assign.nim4
4 files changed, 107 insertions, 16 deletions
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index 81e45f71c..c28c8c7a1 100755
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -212,7 +212,11 @@ proc markUsed*(n: PNode, s: PSym) =
   if {sfDeprecated, sfError} * s.flags != {}:
     if sfDeprecated in s.flags: Message(n.info, warnDeprecated, s.name.s)
     if sfError in s.flags: LocalError(n.info, errWrongSymbolX, s.name.s)
-  
+
+proc useSym*(sym: PSym): PNode =
+  result = newSymNode(sym)
+  markUsed(result, sym)
+
 proc illFormedAst*(n: PNode) = 
   GlobalError(n.info, errIllFormedAstX, renderTree(n, {renderNoComments}))
 
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 82f43e787..3d00d495d 100755
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -681,7 +681,9 @@ proc semLambda(c: PContext, n: PNode): PNode =
   closeScope(c.tab)           # close scope for parameters
   popOwner()
   result.typ = s.typ
-  
+ 
+proc instantiateDestructor*(c: PContext, typ: PType): bool
+
 proc semProcAux(c: PContext, n: PNode, kind: TSymKind, 
                 validPragmas: TSpecialWords): PNode = 
   result = n
@@ -743,6 +745,19 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
     popOwner()
     pushOwner(s)
   s.options = gOptions
+  if result.sons[namePos].sym.name.id == ord(wDestroy) and s.typ.sons.len == 2:
+    let t = s.typ.sons[1].skipTypes({tyVar})
+    t.destructor = s
+    # automatically insert calls to base classes' destructors
+    if n.sons[bodyPos].kind != nkEmpty:
+      for i in countup(0, t.sonsLen - 1):
+        # when inheriting directly from object
+        # there will be a single nil son
+        if t.sons[i] == nil: continue
+        if instantiateDestructor(c, t.sons[i]):
+          n.sons[bodyPos].addSon(newNode(nkCall, t.sym.info, @[
+              useSym(t.sons[i].destructor),
+              n.sons[paramsPos][1][0]]))
   if n.sons[bodyPos].kind != nkEmpty: 
     # for DLL generation it is annoying to check for sfImportc!
     if sfBorrow in s.flags: 
@@ -772,10 +787,6 @@ 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
@@ -864,6 +875,85 @@ proc semStaticStmt(c: PContext, n: PNode): PNode =
   if result.isNil:
     LocalError(n.info, errCannotInterpretNodeX, renderTree(n))
 
+# special marker that indicates that we've already tried
+# to generate a destructor for some type, but it turned out
+# to be trivial
+var DestructorIsTrivial: PSym
+new(DestructorIsTrivial)
+
+var
+  destructorParam = getIdent"this_"
+  rangeDestructorProc: PSym
+
+proc generateDestructor(c: PContext, t: PType): PNode =
+  ## generate a destructor for a user-defined object ot tuple type
+  ## returns nil if the destructor turns out to be trivial
+  
+  template addLine(e: expr): stmt =
+    if result == nil: result = newNode(nkStmtList)
+    result.addSon(e)
+
+  internalAssert t.n.kind == nkRecList
+  # call the destructods of all fields
+  for s in countup(0, t.n.sons.len - 1):
+    internalAssert t.n.sons[s].kind == nkSym
+    let field = t.n.sons[s].sym
+    if instantiateDestructor(c, field.typ):
+      addLine(newNode(nkCall, field.info, @[
+        useSym(field.typ.destructor),
+        newNode(nkDotExpr, field.info, @[
+          newIdentNode(destructorParam, t.sym.info),
+          useSym(field)
+          ])
+        ]))
+  # base classes' destructors will be automatically called by
+  # semProcAux for both auto-generated and user-defined destructors
+
+proc instantiateDestructor*(c: PContext, typ: PType): bool =
+  # returns true if the type already had a user-defined
+  # destructor or if the compiler generated a default
+  # member-wise one
+  var t = skipTypes(typ, {tyConst, tyMutable})
+  
+  if t.destructor != nil:
+    return t.destructor != DestructorIsTrivial
+  
+  case t.kind
+  of tySequence, tyArray, tyArrayConstr, tyOpenArray:
+    if instantiateDestructor(c, t.sons[0]):
+      if rangeDestructorProc == nil:
+        rangeDestructorProc = SymtabGet(c.tab, getIdent"nimDestroyRange")
+      t.destructor = rangeDestructorProc
+      return true
+    else:
+      return false
+  of tyTuple, tyObject:
+    let generated = generateDestructor(c, t)
+    if generated != nil:
+      internalAssert t.sym != nil
+      var i = t.sym.info
+      let fullDef = newNode(nkProcDef, i, @[
+        newIdentNode(getIdent"destroy", i),
+        emptyNode,
+        newNode(nkFormalParams, i, @[
+          emptyNode,
+          newNode(nkIdentDefs, i, @[
+            newIdentNode(destructorParam, i),
+            useSym(t.sym),
+            emptyNode]),
+          ]),
+        emptyNode,
+        generated
+        ])
+      discard semProc(c, fullDef)
+      internalAssert t.destructor != nil
+      return true
+    else:
+      t.destructor = DestructorIsTrivial
+      return false
+  else:
+    return false
+
 proc insertDestructors(c: PContext, varSection: PNode):
   tuple[outer: PNode, inner: PNode] =
   # Accepts a var or let section.
@@ -889,7 +979,7 @@ proc insertDestructors(c: PContext, varSection: PNode):
       varTyp = varId.sym.typ
       info = varId.info
 
-    if varTyp != nil and instantiateDestructor(varTyp):
+    if varTyp != nil and instantiateDestructor(c, varTyp):
       var tryStmt = newNodeI(nkTryStmt, info)
 
       if j < totalVars - 1:
@@ -910,8 +1000,8 @@ proc insertDestructors(c: PContext, varSection: PNode):
       tryStmt.addSon(
         newNode(nkFinally, info, @[
           semStmt(c, newNode(nkCall, info, @[
-            semSym(c, varId, varTyp.destructor, {}),
-            semSym(c, varId, varId.sym, {})]))]))
+            useSym(varTyp.destructor),
+            useSym(varId.sym)]))]))
 
       result.outer = newNodeI(nkStmtList, info)
       varSection.sons.setLen(j+1)
diff --git a/compiler/types.nim b/compiler/types.nim
index 2f201b9de..ecc250a5a 100755
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -1062,10 +1062,3 @@ 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/lib/system/assign.nim b/lib/system/assign.nim
index 59c44a6cc..f29dc547c 100755
--- a/lib/system/assign.nim
+++ b/lib/system/assign.nim
@@ -139,6 +139,10 @@ proc objectInit(dest: Pointer, typ: PNimType) =
   
 # ---------------------- assign zero -----------------------------------------
 
+proc nimDestroyRange*[T](r: T) =
+  # internal proc used for destroying sequences and arrays
+  for i in countup(0, r.len - 1): destroy(r[i])
+
 proc genericReset(dest: Pointer, mt: PNimType) {.compilerProc.}
 proc genericResetAux(dest: Pointer, n: ptr TNimNode) =
   var d = cast[TAddress](dest)