summary refs log tree commit diff stats
path: root/compiler/semdestruct.nim
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/semdestruct.nim')
-rw-r--r--compiler/semdestruct.nim158
1 files changed, 91 insertions, 67 deletions
diff --git a/compiler/semdestruct.nim b/compiler/semdestruct.nim
index 797d8895e..791bef823 100644
--- a/compiler/semdestruct.nim
+++ b/compiler/semdestruct.nim
@@ -9,26 +9,39 @@
 
 ## This module implements destructors.
 
+# included from sem.nim
 
 # special marker values that indicates that we are
 # 1) AnalyzingDestructor: currently analyzing the type for destructor 
 # generation (needed for recursive types)
 # 2) DestructorIsTrivial: completed the analysis before and determined
 # that the type has a trivial destructor
-var AnalyzingDestructor, DestructorIsTrivial: PSym
-new(AnalyzingDestructor)
-new(DestructorIsTrivial)
+var analyzingDestructor, destructorIsTrivial: PSym
+new(analyzingDestructor)
+new(destructorIsTrivial)
 
 var
   destructorName = getIdent"destroy_"
   destructorParam = getIdent"this_"
-  destructorPragma = newIdentNode(getIdent"destructor", UnknownLineInfo())
+  destructorPragma = newIdentNode(getIdent"destructor", unknownLineInfo())
   rangeDestructorProc*: PSym
 
-proc instantiateDestructor(c: PContext, typ: PType): bool
+proc instantiateDestructor(c: PContext, typ: PType): PType
 
 proc doDestructorStuff(c: PContext, s: PSym, n: PNode) =
-  let t = s.typ.sons[1].skipTypes({tyVar})
+  var t = s.typ.sons[1].skipTypes({tyVar})
+  if t.kind == tyGenericInvokation:
+    for i in 1 .. <t.sonsLen:
+      if t.sons[i].kind != tyGenericParam:
+        localError(n.info, errDestructorNotGenericEnough)
+        return
+    t = t.base
+  elif t.kind == tyCompositeTypeClass:
+    t = t.base
+    if t.kind != tyGenericBody:
+      localError(n.info, errDestructorNotGenericEnough)
+      return
+  
   t.destructor = s
   # automatically insert calls to base classes' destructors
   if n.sons[bodyPos].kind != nkEmpty:
@@ -36,15 +49,19 @@ proc doDestructorStuff(c: PContext, s: PSym, n: PNode) =
       # when inheriting directly from object
       # there will be a single nil son
       if t.sons[i] == nil: continue
-      if instantiateDestructor(c, t.sons[i]):
+      let destructableT = instantiateDestructor(c, t.sons[i])
+      if destructableT != nil:
         n.sons[bodyPos].addSon(newNode(nkCall, t.sym.info, @[
-            useSym(t.sons[i].destructor),
+            useSym(destructableT.destructor),
             n.sons[paramsPos][1][0]]))
 
-proc destroyField(c: PContext, field: PSym, holder: PNode): PNode =
-  if instantiateDestructor(c, field.typ):
+proc destroyFieldOrFields(c: PContext, field: PNode, holder: PNode): PNode
+
+proc destroySym(c: PContext, field: PSym, holder: PNode): PNode =
+  let destructableT = instantiateDestructor(c, field.typ)
+  if destructableT != nil:
     result = newNode(nkCall, field.info, @[
-      useSym(field.typ.destructor),
+      useSym(destructableT.destructor),
       newNode(nkDotExpr, field.info, @[holder, useSym(field)])])
 
 proc destroyCase(c: PContext, n: PNode, holder: PNode): PNode =
@@ -55,78 +72,83 @@ proc destroyCase(c: PContext, n: PNode, holder: PNode): PNode =
   for i in countup(1, n.len - 1):
     # of A, B:
     var caseBranch = newNode(n[i].kind, n[i].info, n[i].sons[0 .. -2])
-    let recList = n[i].lastSon
-    var destroyRecList = newNode(nkStmtList, n[i].info, @[])
-    template addField(f: expr): stmt =
-      let stmt = destroyField(c, f, holder)
-      if stmt != nil:
-        destroyRecList.addSon(stmt)
-        inc nonTrivialFields
-        
-    case recList.kind
-    of nkSym:
-      addField(recList.sym)
-    of nkRecList:
-      for j in countup(0, recList.len - 1):
-        addField(recList[j].sym)
+    
+    let stmt = destroyFieldOrFields(c, n[i].lastSon, holder)
+    if stmt == nil:
+      caseBranch.addSon(newNode(nkStmtList, n[i].info, @[]))
     else:
-      internalAssert false
-      
-    caseBranch.addSon(destroyRecList)
+      caseBranch.addSon(stmt)
+      nonTrivialFields += stmt.len
+    
     result.addSon(caseBranch)
+  
   # maybe no fields were destroyed?
   if nonTrivialFields == 0:
     result = nil
- 
+
+proc destroyFieldOrFields(c: PContext, field: PNode, holder: PNode): PNode =
+  template maybeAddLine(e: expr): stmt =
+    let stmt = e
+    if stmt != nil:
+      if result == nil: result = newNode(nkStmtList)
+      result.addSon(stmt)
+
+  case field.kind
+  of nkRecCase:
+    maybeAddLine destroyCase(c, field, holder)
+  of nkSym:
+    maybeAddLine destroySym(c, field.sym, holder)
+  of nkRecList:
+    for son in field:
+      maybeAddLine destroyFieldOrFields(c, son, holder)
+  else:
+    internalAssert false
+
 proc generateDestructor(c: PContext, t: PType): PNode =
   ## generate a destructor for a user-defined object or 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)
-
   # XXX: This may be true for some C-imported types such as
   # Tposix_spawnattr
   if t.n == nil or t.n.sons == nil: return
   internalAssert t.n.kind == nkRecList
-  let destructedObj = newIdentNode(destructorParam, UnknownLineInfo())
+  let destructedObj = newIdentNode(destructorParam, unknownLineInfo())
   # call the destructods of all fields
-  for s in countup(0, t.n.sons.len - 1):
-    case t.n.sons[s].kind
-    of nkRecCase:
-      let stmt = destroyCase(c, t.n.sons[s], destructedObj)
-      if stmt != nil: addLine(stmt)
-    of nkSym:
-      let stmt = destroyField(c, t.n.sons[s].sym, destructedObj)
-      if stmt != nil: addLine(stmt)
-    else:
-      internalAssert false
+  result = destroyFieldOrFields(c, t.n, destructedObj)
   # 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})
+proc instantiateDestructor(c: PContext, typ: PType): PType =
+  # returns nil if a variable of type `typ` doesn't require a
+  # destructor. Otherwise, returns the type, which holds the 
+  # destructor that must be used for the varialbe.
+  # The destructor is either user-defined or automatically
+  # generated by the compiler in a member-wise fashion.
+  var t = skipTypes(typ, {tyConst, tyMutable}).skipGenericAlias
+  let typeHoldingUserDefinition = if t.kind == tyGenericInst: t.base
+                                  else: t
   
-  if t.destructor != nil:
+  if typeHoldingUserDefinition.destructor != nil:
     # XXX: This is not entirely correct for recursive types, but we need
     # it temporarily to hide the "destroy is already defined" problem
-    return t.destructor notin [AnalyzingDestructor, DestructorIsTrivial]
+    if typeHoldingUserDefinition.destructor notin
+        [analyzingDestructor, destructorIsTrivial]:
+      return typeHoldingUserDefinition
+    else:
+      return nil
   
+  t = t.skipTypes({tyGenericInst})
   case t.kind
   of tySequence, tyArray, tyArrayConstr, tyOpenArray, tyVarargs:
-    if instantiateDestructor(c, t.sons[0]):
+    if instantiateDestructor(c, t.sons[0]) != nil:
       if rangeDestructorProc == nil:
         rangeDestructorProc = searchInScopes(c, getIdent"nimDestroyRange")
       t.destructor = rangeDestructorProc
-      return true
+      return t
     else:
-      return false
+      return nil
   of tyTuple, tyObject:
-    t.destructor = AnalyzingDestructor
+    t.destructor = analyzingDestructor
     let generated = generateDestructor(c, t)
     if generated != nil:
       internalAssert t.sym != nil
@@ -139,21 +161,21 @@ proc instantiateDestructor(c: PContext, typ: PType): bool =
           emptyNode,
           newNode(nkIdentDefs, i, @[
             newIdentNode(destructorParam, i),
-            useSym(t.sym),
+            symNodeFromType(c, makeVarType(c, t), t.sym.info),
             emptyNode]),
           ]),
         newNode(nkPragma, i, @[destructorPragma]),
         emptyNode,
         generated
         ])
-      discard semProc(c, fullDef)
-      internalAssert t.destructor != nil
-      return true
+      let semantizedDef = semProc(c, fullDef)
+      t.destructor = semantizedDef[namePos].sym
+      return t
     else:
-      t.destructor = DestructorIsTrivial
-      return false
+      t.destructor = destructorIsTrivial
+      return nil
   else:
-    return false
+    return nil
 
 proc insertDestructors(c: PContext,
                        varSection: PNode): tuple[outer, inner: PNode] =
@@ -179,9 +201,11 @@ proc insertDestructors(c: PContext,
       varId = varSection[j][0]
       varTyp = varId.sym.typ
       info = varId.info
-
-    if varTyp != nil and instantiateDestructor(c, varTyp) and 
-        sfGlobal notin varId.sym.flags:
+    
+    if varTyp == nil or sfGlobal in varId.sym.flags: continue
+    let destructableT = instantiateDestructor(c, varTyp)
+    
+    if destructableT != nil:
       var tryStmt = newNodeI(nkTryStmt, info)
 
       if j < totalVars - 1:
@@ -198,11 +222,11 @@ proc insertDestructors(c: PContext,
       else:
         result.inner = newNodeI(nkStmtList, info)
         tryStmt.addSon(result.inner)
-
+    
       tryStmt.addSon(
         newNode(nkFinally, info, @[
           semStmt(c, newNode(nkCall, info, @[
-            useSym(varTyp.destructor),
+            useSym(destructableT.destructor),
             useSym(varId.sym)]))]))
 
       result.outer = newNodeI(nkStmtList, info)