generate default destructors

This commit is contained in:
Zahary Karadjov
2012-06-06 18:34:35 +03:00
parent 41a9a941ab
commit d10b524c9a
4 changed files with 107 additions and 16 deletions

View File

@@ -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}))

View File

@@ -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)

View File

@@ -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

View File

@@ -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)