123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187 |
- #
- #
- # 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, c.graph.usageSym),
- 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, c.graph.usageSym),
- newNode(nkDotExpr, field.info, @[holder, useSym(field, c.graph.usageSym)])])
- 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 = typ.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, tyAlias})
- case t.kind
- of tySequence, tyArray, 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, c.graph.usageSym),
- useSym(s, c.graph.usageSym)]))
- result = newNode(nkDefer, s.info, @[call])
|