summary refs log tree commit diff stats
path: root/tinyc/lib
diff options
context:
space:
mode:
Diffstat (limited to 'tinyc/lib')
0 files changed, 0 insertions, 0 deletions
id='n67' href='#n67'>67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
#
#
#           The Nim Compiler
#        (c) Copyright 2013 Andreas Rumpf
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

## 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
  destructorName = getIdent"destroy_"
  destructorParam = getIdent"this_"
  destructorPragma = newIdentNode(getIdent"destructor", unknownLineInfo())

proc instantiateDestructor(c: PContext, typ: PType): PType

proc doDestructorStuff(c: PContext, s: PSym, n: PNode) =
  var t = s.typ.sons[1].skipTypes({tyVar})
  if t.kind == tyGenericInvocation:
    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:
    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
      let destructableT = instantiateDestructor(c, t.sons[i])
      if destructableT != nil:
        n.sons[bodyPos].addSon(newNode(nkCall, t.sym.info, @[
            useSym(destructableT.destructor),
            n.sons[paramsPos][1][0]]))

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(destructableT.destructor),
      newNode(nkDotExpr, field.info, @[holder, useSym(field)])])

proc destroyCase(c: PContext, n: PNode, holder: PNode): PNode =
  var nonTrivialFields = 0
  result = newNode(nkCaseStmt, n.info, @[])
  # case x.kind
  result.addSon(newNode(nkDotExpr, n.info, @[holder, n.sons[0]]))
  for i in countup(1, n.len - 1):
    # of A, B:
    let ni = n[i]
    var caseBranch = newNode(ni.kind, ni.info, ni.sons[0..ni.len-2])

    let stmt = destroyFieldOrFields(c, ni.lastSon, holder)
    if stmt == nil:
      caseBranch.addSon(newNode(nkStmtList, ni.info, @[]))
    else:
      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) =
    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

  # 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())
  # call the destructods of all fields
  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): 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 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
    if typeHoldingUserDefinition.destructor notin
        [analyzingDestructor, destructorIsTrivial]:
      return typeHoldingUserDefinition
    else:
      return nil

  t = t.skipTypes({tyGenericInst})
  case t.kind
  of tySequence, tyArray, tyArrayConstr, tyOpenArray, tyVarargs:
    t.destructor = analyzingDestructor
    if instantiateDestructor(c, t.sons[0]) != nil:
      t.destructor = getCompilerProc"nimDestroyRange"
      return t
    else:
      return nil
  of tyTuple, tyObject:
    t.destructor = analyzingDestructor
    let generated = generateDestructor(c, t)
    if generated != nil:
      internalAssert t.sym != nil
      var i = t.sym.info
      let fullDef = newNode(nkProcDef, i, @[
        newIdentNode(destructorName, i),
        emptyNode,
        emptyNode,
        newNode(nkFormalParams, i, @[
          emptyNode,
          newNode(nkIdentDefs, i, @[
            newIdentNode(destructorParam, i),
            symNodeFromType(c, makeVarType(c, t), t.sym.info),
            emptyNode]),
          ]),
        newNode(nkPragma, i, @[destructorPragma]),
        emptyNode,
        generated
        ])
      let semantizedDef = semProc(c, fullDef)
      t.destructor = semantizedDef[namePos].sym
      return t
    else:
      t.destructor = destructorIsTrivial
      return nil
  else:
    return nil

proc createDestructorCall(c: PContext, s: PSym): PNode =
  let varTyp = s.typ
  if varTyp == nil or sfGlobal in s.flags: return
  let destructableT = instantiateDestructor(c, varTyp)
  if destructableT != nil:
    let call = semStmt(c, newNode(nkCall, s.info, @[
      useSym(destructableT.destructor), useSym(s)]))
    result = newNode(nkDefer, s.info, @[call])

proc insertDestructors(c: PContext,
                       varSection: PNode): tuple[outer, 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 or sfGlobal in varId.sym.flags: continue
    let destructableT = instantiateDestructor(c, varTyp)

    if destructableT != nil:
      var tryStmt = newNodeI(nkTryStmt, info)

      if j < totalVars - 1:
        var remainingVars = newNodeI(varSection.kind, info)
        remainingVars.sons = varSection.sons[(j+1)..varSection.len-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, @[
            useSym(destructableT.destructor),
            useSym(varId.sym)]))]))

      result.outer = newNodeI(nkStmtList, info)
      varSection.sons.setLen(j+1)
      result.outer.addSon(varSection)
      result.outer.addSon(tryStmt)

      return