This commit is contained in:
Zahary Karadjov
2014-01-02 17:55:18 +02:00
parent 8e0941576f
commit 02533c260b
5 changed files with 126 additions and 37 deletions

View File

@@ -432,6 +432,8 @@ proc debugTree(n: PNode, indent: int, maxRecDepth: int): PRope =
proc debug(n: PSym) =
if n == nil:
writeln(stdout, "null")
elif n.kind == skUnknown:
writeln(stdout, "skUnknown")
else:
#writeln(stdout, ropeToStr(symToYaml(n, 0, 1)))
writeln(stdout, ropeToStr(ropef("$1_$2: $3, $4", [

View File

@@ -93,6 +93,7 @@ type
errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile,
errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitely,
errOnlyACallOpCanBeDelegator, errUsingNoSymbol,
errDestructorNotGenericEnough,
errXExpectsTwoArguments,
errXExpectsObjectTypes, errXcanNeverBeOfThisSubtype, errTooManyIterations,
@@ -322,6 +323,8 @@ const
errInstantiateXExplicitely: "instantiate '$1' explicitely",
errOnlyACallOpCanBeDelegator: "only a call operator can be a delegator",
errUsingNoSymbol: "'$1' is not a variable, constant or a proc name",
errDestructorNotGenericEnough: "Destructor signarue is too specific. " &
"A destructor must be associated will all instantiations of a generic type",
errXExpectsTwoArguments: "\'$1\' expects two arguments",
errXExpectsObjectTypes: "\'$1\' expects object types",
errXcanNeverBeOfThisSubtype: "\'$1\' can never be of this subtype",

View File

@@ -9,6 +9,7 @@
## This module implements destructors.
# included from sem.nim
# special marker values that indicates that we are
# 1) AnalyzingDestructor: currently analyzing the type for destructor
@@ -25,10 +26,22 @@ var
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,17 @@ 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):
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 =
@@ -105,26 +120,35 @@ proc generateDestructor(c: PContext, t: PType): PNode =
# 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
let generated = generateDestructor(c, t)
@@ -139,21 +163,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
return nil
else:
return false
return nil
proc insertDestructors(c: PContext,
varSection: PNode): tuple[outer, inner: PNode] =
@@ -179,9 +203,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 +224,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)

View File

@@ -86,7 +86,7 @@ include semdestruct
proc semDestructorCheck(c: PContext, n: PNode, flags: TExprFlags) {.inline.} =
if efAllowDestructor notin flags and n.kind in nkCallKinds+{nkObjConstr}:
if instantiateDestructor(c, n.typ):
if instantiateDestructor(c, n.typ) != nil:
localError(n.info, errGenerated,
"usage of a type with a destructor in a non destructible context")
# This still breaks too many things:

View File

@@ -1,26 +1,84 @@
discard """
output: '''some text
Destructor called!'''
output: '''----
myobj constructed
myobj destructed
----
mygeneric1 constructed
mygeneric1 destroyed
----
mygeneric2 constructed
mygeneric2 destroyed
myobj destroyed
----
mygeneric3 constructed
mygeneric1 destroyed
'''
"""
type
TMyObj = object
x, y: int
p: pointer
TMyGeneric1[T] = object
x: T
TMyGeneric2[A, B] = object
x: A
y: B
TMyGeneric3[A, B, C] = object
x: A
y: B
z: C
proc destruct(o: var TMyObj) {.destructor.} =
if o.p != nil: dealloc o.p
echo "Destructor called!"
echo "myobj destroyed"
proc destroy(o: var TMyGeneric1) {.destructor.} =
echo "mygeneric1 destroyed"
proc destroy[A, B](o: var TMyGeneric2[A, B]) {.destructor.} =
echo "mygeneric2 destroyed"
proc open: TMyObj =
# allow for superfluous ()
result = (TMyObj(x: 1, y: 2, p: alloc(3)))
proc `$`(x: TMyObj): string = $x.y
proc main() =
proc myobj() =
var x = open()
echo "some text"
echo "myobj constructed"
proc mygeneric1() =
var x = TMyGeneric1[int](x: 10)
echo "mygeneric1 constructed"
proc mygeneric2[T](val: T) =
var
a = open()
b = TMyGeneric2[int, T](x: 10, y: val)
c = TMyGeneric3[int, int, string](x: 10, y: 20, z: "test")
echo "mygeneric2 constructed"
proc mygeneric3 =
var x = TMyGeneric3[int, string, TMyGeneric1[int]](
x: 10, y: "test", z: TMyGeneric1[int](x: 10))
echo "mygeneric3 constructed"
echo "----"
myobj()
echo "----"
mygeneric1()
echo "----"
mygeneric2[int](10)
echo "----"
mygeneric3()
main()