This commit is contained in:
Zahary Karadjov 2014-01-02 17:55:18 +02:00
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) = proc debug(n: PSym) =
if n == nil: if n == nil:
writeln(stdout, "null") writeln(stdout, "null")
elif n.kind == skUnknown:
writeln(stdout, "skUnknown")
else: else:
#writeln(stdout, ropeToStr(symToYaml(n, 0, 1))) #writeln(stdout, ropeToStr(symToYaml(n, 0, 1)))
writeln(stdout, ropeToStr(ropef("$1_$2: $3, $4", [ writeln(stdout, ropeToStr(ropef("$1_$2: $3, $4", [

View file

@ -93,6 +93,7 @@ type
errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile, errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile,
errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitely, errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitely,
errOnlyACallOpCanBeDelegator, errUsingNoSymbol, errOnlyACallOpCanBeDelegator, errUsingNoSymbol,
errDestructorNotGenericEnough,
errXExpectsTwoArguments, errXExpectsTwoArguments,
errXExpectsObjectTypes, errXcanNeverBeOfThisSubtype, errTooManyIterations, errXExpectsObjectTypes, errXcanNeverBeOfThisSubtype, errTooManyIterations,
@ -322,6 +323,8 @@ const
errInstantiateXExplicitely: "instantiate '$1' explicitely", errInstantiateXExplicitely: "instantiate '$1' explicitely",
errOnlyACallOpCanBeDelegator: "only a call operator can be a delegator", errOnlyACallOpCanBeDelegator: "only a call operator can be a delegator",
errUsingNoSymbol: "'$1' is not a variable, constant or a proc name", 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", errXExpectsTwoArguments: "\'$1\' expects two arguments",
errXExpectsObjectTypes: "\'$1\' expects object types", errXExpectsObjectTypes: "\'$1\' expects object types",
errXcanNeverBeOfThisSubtype: "\'$1\' can never be of this subtype", errXcanNeverBeOfThisSubtype: "\'$1\' can never be of this subtype",

View file

@ -9,6 +9,7 @@
## This module implements destructors. ## This module implements destructors.
# included from sem.nim
# special marker values that indicates that we are # special marker values that indicates that we are
# 1) AnalyzingDestructor: currently analyzing the type for destructor # 1) AnalyzingDestructor: currently analyzing the type for destructor
@ -25,10 +26,22 @@ var
destructorPragma = newIdentNode(getIdent"destructor", unknownLineInfo()) destructorPragma = newIdentNode(getIdent"destructor", unknownLineInfo())
rangeDestructorProc*: PSym rangeDestructorProc*: PSym
proc instantiateDestructor(c: PContext, typ: PType): bool proc instantiateDestructor(c: PContext, typ: PType): PType
proc doDestructorStuff(c: PContext, s: PSym, n: PNode) = 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 t.destructor = s
# automatically insert calls to base classes' destructors # automatically insert calls to base classes' destructors
if n.sons[bodyPos].kind != nkEmpty: if n.sons[bodyPos].kind != nkEmpty:
@ -36,15 +49,17 @@ proc doDestructorStuff(c: PContext, s: PSym, n: PNode) =
# when inheriting directly from object # when inheriting directly from object
# there will be a single nil son # there will be a single nil son
if t.sons[i] == nil: continue 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, @[ n.sons[bodyPos].addSon(newNode(nkCall, t.sym.info, @[
useSym(t.sons[i].destructor), useSym(destructableT.destructor),
n.sons[paramsPos][1][0]])) n.sons[paramsPos][1][0]]))
proc destroyField(c: PContext, field: PSym, holder: PNode): PNode = 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, @[ result = newNode(nkCall, field.info, @[
useSym(field.typ.destructor), useSym(destructableT.destructor),
newNode(nkDotExpr, field.info, @[holder, useSym(field)])]) newNode(nkDotExpr, field.info, @[holder, useSym(field)])])
proc destroyCase(c: PContext, n: PNode, holder: PNode): PNode = 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 # base classes' destructors will be automatically called by
# semProcAux for both auto-generated and user-defined destructors # semProcAux for both auto-generated and user-defined destructors
proc instantiateDestructor(c: PContext, typ: PType): bool = proc instantiateDestructor(c: PContext, typ: PType): PType =
# returns true if the type already had a user-defined # returns nil if a variable of type `typ` doesn't require a
# destructor or if the compiler generated a default # destructor. Otherwise, returns the type, which holds the
# member-wise one # destructor that must be used for the varialbe.
var t = skipTypes(typ, {tyConst, tyMutable}) # 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 # XXX: This is not entirely correct for recursive types, but we need
# it temporarily to hide the "destroy is already defined" problem # 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 case t.kind
of tySequence, tyArray, tyArrayConstr, tyOpenArray, tyVarargs: of tySequence, tyArray, tyArrayConstr, tyOpenArray, tyVarargs:
if instantiateDestructor(c, t.sons[0]): if instantiateDestructor(c, t.sons[0]) != nil:
if rangeDestructorProc == nil: if rangeDestructorProc == nil:
rangeDestructorProc = searchInScopes(c, getIdent"nimDestroyRange") rangeDestructorProc = searchInScopes(c, getIdent"nimDestroyRange")
t.destructor = rangeDestructorProc t.destructor = rangeDestructorProc
return true return t
else: else:
return false return nil
of tyTuple, tyObject: of tyTuple, tyObject:
t.destructor = analyzingDestructor t.destructor = analyzingDestructor
let generated = generateDestructor(c, t) let generated = generateDestructor(c, t)
@ -139,21 +163,21 @@ proc instantiateDestructor(c: PContext, typ: PType): bool =
emptyNode, emptyNode,
newNode(nkIdentDefs, i, @[ newNode(nkIdentDefs, i, @[
newIdentNode(destructorParam, i), newIdentNode(destructorParam, i),
useSym(t.sym), symNodeFromType(c, makeVarType(c, t), t.sym.info),
emptyNode]), emptyNode]),
]), ]),
newNode(nkPragma, i, @[destructorPragma]), newNode(nkPragma, i, @[destructorPragma]),
emptyNode, emptyNode,
generated generated
]) ])
discard semProc(c, fullDef) let semantizedDef = semProc(c, fullDef)
internalAssert t.destructor != nil t.destructor = semantizedDef[namePos].sym
return true return t
else: else:
t.destructor = destructorIsTrivial t.destructor = destructorIsTrivial
return false return nil
else: else:
return false return nil
proc insertDestructors(c: PContext, proc insertDestructors(c: PContext,
varSection: PNode): tuple[outer, inner: PNode] = varSection: PNode): tuple[outer, inner: PNode] =
@ -180,8 +204,10 @@ proc insertDestructors(c: PContext,
varTyp = varId.sym.typ varTyp = varId.sym.typ
info = varId.info info = varId.info
if varTyp != nil and instantiateDestructor(c, varTyp) and if varTyp == nil or sfGlobal in varId.sym.flags: continue
sfGlobal notin varId.sym.flags: let destructableT = instantiateDestructor(c, varTyp)
if destructableT != nil:
var tryStmt = newNodeI(nkTryStmt, info) var tryStmt = newNodeI(nkTryStmt, info)
if j < totalVars - 1: if j < totalVars - 1:
@ -202,7 +228,7 @@ proc insertDestructors(c: PContext,
tryStmt.addSon( tryStmt.addSon(
newNode(nkFinally, info, @[ newNode(nkFinally, info, @[
semStmt(c, newNode(nkCall, info, @[ semStmt(c, newNode(nkCall, info, @[
useSym(varTyp.destructor), useSym(destructableT.destructor),
useSym(varId.sym)]))])) useSym(varId.sym)]))]))
result.outer = newNodeI(nkStmtList, info) result.outer = newNodeI(nkStmtList, info)

View file

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

View file

@ -1,6 +1,18 @@
discard """ discard """
output: '''some text output: '''----
Destructor called!''' myobj constructed
myobj destructed
----
mygeneric1 constructed
mygeneric1 destroyed
----
mygeneric2 constructed
mygeneric2 destroyed
myobj destroyed
----
mygeneric3 constructed
mygeneric1 destroyed
'''
""" """
type type
@ -8,19 +20,65 @@ type
x, y: int x, y: int
p: pointer 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.} = proc destruct(o: var TMyObj) {.destructor.} =
if o.p != nil: dealloc o.p 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 = proc open: TMyObj =
# allow for superfluous () # allow for superfluous ()
result = (TMyObj(x: 1, y: 2, p: alloc(3))) result = (TMyObj(x: 1, y: 2, p: alloc(3)))
proc `$`(x: TMyObj): string = $x.y proc `$`(x: TMyObj): string = $x.y
proc main() = proc myobj() =
var x = open() 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()