fixed #597
This commit is contained in:
parent
8e0941576f
commit
02533c260b
5 changed files with 126 additions and 37 deletions
|
|
@ -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", [
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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()
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue