destructors: work in progress
This commit is contained in:
parent
1b695a67f8
commit
b407f083ba
8 changed files with 109 additions and 27 deletions
|
|
@ -93,7 +93,7 @@
|
||||||
|
|
||||||
import
|
import
|
||||||
intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
|
intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
|
||||||
strutils, options, dfa, lowerings
|
strutils, options, dfa, lowerings, rodread
|
||||||
|
|
||||||
const
|
const
|
||||||
InterestingSyms = {skVar, skResult, skLet}
|
InterestingSyms = {skVar, skResult, skLet}
|
||||||
|
|
@ -164,20 +164,44 @@ proc isHarmlessVar*(s: PSym; c: Con): bool =
|
||||||
template interestingSym(s: PSym): bool =
|
template interestingSym(s: PSym): bool =
|
||||||
s.owner == c.owner and s.kind in InterestingSyms and hasDestructor(s.typ)
|
s.owner == c.owner and s.kind in InterestingSyms and hasDestructor(s.typ)
|
||||||
|
|
||||||
|
proc patchHead(n: PNode; alreadyPatched: var IntSet) =
|
||||||
|
if n.kind in nkCallKinds and n[0].kind == nkSym and n.len > 1:
|
||||||
|
let s = n[0].sym
|
||||||
|
if sfFromGeneric in s.flags or true:
|
||||||
|
if s.name.s[0] == '=' and not containsOrIncl(alreadyPatched, s.id):
|
||||||
|
patchHead(s.getBody, alreadyPatched)
|
||||||
|
let t = n[1].typ.skipTypes({tyVar, tyGenericInst, tyAlias, tyInferred})
|
||||||
|
template patch(op, field) =
|
||||||
|
if s.name.s == op and field != nil: #and field != s:
|
||||||
|
n.sons[0].sym = field
|
||||||
|
patch "=sink", t.sink
|
||||||
|
patch "=", t.assignment
|
||||||
|
patch "=destroy", t.destructor
|
||||||
|
for x in n:
|
||||||
|
patchHead(x, alreadyPatched)
|
||||||
|
|
||||||
|
template patchHead(n: PNode) =
|
||||||
|
when false:
|
||||||
|
var alreadyPatched = initIntSet()
|
||||||
|
patchHead(n, alreadyPatched)
|
||||||
|
|
||||||
proc genSink(t: PType; dest: PNode): PNode =
|
proc genSink(t: PType; dest: PNode): PNode =
|
||||||
let t = t.skipTypes({tyGenericInst, tyAlias})
|
let t = t.skipTypes({tyGenericInst, tyAlias})
|
||||||
let op = if t.sink != nil: t.sink else: t.assignment
|
let op = if t.sink != nil: t.sink else: t.assignment
|
||||||
assert op != nil
|
assert op != nil
|
||||||
|
patchHead op.ast[bodyPos]
|
||||||
result = newTree(nkCall, newSymNode(op), newTree(nkHiddenAddr, dest))
|
result = newTree(nkCall, newSymNode(op), newTree(nkHiddenAddr, dest))
|
||||||
|
|
||||||
proc genCopy(t: PType; dest: PNode): PNode =
|
proc genCopy(t: PType; dest: PNode): PNode =
|
||||||
let t = t.skipTypes({tyGenericInst, tyAlias})
|
let t = t.skipTypes({tyGenericInst, tyAlias})
|
||||||
assert t.assignment != nil
|
assert t.assignment != nil
|
||||||
|
patchHead t.assignment.ast[bodyPos]
|
||||||
result = newTree(nkCall, newSymNode(t.assignment), newTree(nkHiddenAddr, dest))
|
result = newTree(nkCall, newSymNode(t.assignment), newTree(nkHiddenAddr, dest))
|
||||||
|
|
||||||
proc genDestroy(t: PType; dest: PNode): PNode =
|
proc genDestroy(t: PType; dest: PNode): PNode =
|
||||||
let t = t.skipTypes({tyGenericInst, tyAlias})
|
let t = t.skipTypes({tyGenericInst, tyAlias})
|
||||||
assert t.destructor != nil
|
assert t.destructor != nil
|
||||||
|
patchHead t.destructor.ast[bodyPos]
|
||||||
result = newTree(nkCall, newSymNode(t.destructor), newTree(nkHiddenAddr, dest))
|
result = newTree(nkCall, newSymNode(t.destructor), newTree(nkHiddenAddr, dest))
|
||||||
|
|
||||||
proc addTopVar(c: var Con; v: PNode) =
|
proc addTopVar(c: var Con; v: PNode) =
|
||||||
|
|
|
||||||
|
|
@ -522,6 +522,10 @@ proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
|
||||||
else:
|
else:
|
||||||
result = n
|
result = n
|
||||||
result = semStmt(c, result)
|
result = semStmt(c, result)
|
||||||
|
when false:
|
||||||
|
# Code generators are lazy now and can deal with undeclared procs, so these
|
||||||
|
# steps are not required anymore and actually harmful for the upcoming
|
||||||
|
# destructor support.
|
||||||
# BUGFIX: process newly generated generics here, not at the end!
|
# BUGFIX: process newly generated generics here, not at the end!
|
||||||
if c.lastGenericIdx < c.generics.len:
|
if c.lastGenericIdx < c.generics.len:
|
||||||
var a = newNodeI(nkStmtList, n.info)
|
var a = newNodeI(nkStmtList, n.info)
|
||||||
|
|
|
||||||
|
|
@ -268,10 +268,10 @@ proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||||
a.kind = kind
|
a.kind = kind
|
||||||
let body = newNodeI(nkStmtList, info)
|
let body = newNodeI(nkStmtList, info)
|
||||||
let procname = case kind
|
let procname = case kind
|
||||||
of attachedAsgn: getIdent":Asgn"
|
of attachedAsgn: getIdent"="
|
||||||
of attachedSink: getIdent":Sink"
|
of attachedSink: getIdent"=sink"
|
||||||
of attachedDeepCopy: getIdent":DeepCopy"
|
of attachedDeepCopy: getIdent"=deepcopy"
|
||||||
of attachedDestructor: getIdent":Destroy"
|
of attachedDestructor: getIdent"=destroy"
|
||||||
|
|
||||||
result = newSym(skProc, procname, typ.owner, info)
|
result = newSym(skProc, procname, typ.owner, info)
|
||||||
a.fn = result
|
a.fn = result
|
||||||
|
|
@ -314,6 +314,24 @@ proc overloadedAsgn(c: PContext; dest, src: PNode): PNode =
|
||||||
let a = getAsgnOrLiftBody(c, dest.typ, dest.info)
|
let a = getAsgnOrLiftBody(c, dest.typ, dest.info)
|
||||||
result = newAsgnCall(c, a, dest, src)
|
result = newAsgnCall(c, a, dest, src)
|
||||||
|
|
||||||
|
proc patchHead(n: PNode; alreadyPatched: var IntSet) =
|
||||||
|
when true:
|
||||||
|
if n.kind in nkCallKinds and n[0].kind == nkSym and n.len > 1:
|
||||||
|
let s = n[0].sym
|
||||||
|
if sfFromGeneric in s.flags:
|
||||||
|
if not containsOrIncl(alreadyPatched, s.id):
|
||||||
|
patchHead(s.getBody, alreadyPatched)
|
||||||
|
let t = n[1].typ.skipTypes({tyVar, tyGenericInst, tyAlias, tyInferred})
|
||||||
|
template patch(op, field) =
|
||||||
|
if s.name.s == op and field != nil and field != s:
|
||||||
|
n.sons[0].sym = field
|
||||||
|
|
||||||
|
patch "=sink", t.sink
|
||||||
|
patch "=", t.assignment
|
||||||
|
patch "=destroy", t.destructor
|
||||||
|
for x in n:
|
||||||
|
patchHead(x, alreadyPatched)
|
||||||
|
|
||||||
proc liftTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) =
|
proc liftTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) =
|
||||||
## In the semantic pass this is called in strategic places
|
## In the semantic pass this is called in strategic places
|
||||||
## to ensure we lift assignment, destructors and moves properly.
|
## to ensure we lift assignment, destructors and moves properly.
|
||||||
|
|
@ -321,6 +339,20 @@ proc liftTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) =
|
||||||
if not newDestructors or not hasDestructor(typ): return
|
if not newDestructors or not hasDestructor(typ): return
|
||||||
let typ = typ.skipTypes({tyGenericInst, tyAlias})
|
let typ = typ.skipTypes({tyGenericInst, tyAlias})
|
||||||
# we generate the destructor first so that other operators can depend on it:
|
# we generate the destructor first so that other operators can depend on it:
|
||||||
if typ.destructor == nil: liftBody(c, typ, attachedDestructor, info)
|
var changed = false
|
||||||
if typ.assignment == nil: liftBody(c, typ, attachedAsgn, info)
|
if typ.destructor != nil and typ.destructor.magic == mAsgn:
|
||||||
if typ.sink == nil: liftBody(c, typ, attachedSink, info)
|
typ.destructor = nil
|
||||||
|
if typ.destructor == nil:
|
||||||
|
liftBody(c, typ, attachedDestructor, info)
|
||||||
|
changed = true
|
||||||
|
if typ.assignment == nil:
|
||||||
|
liftBody(c, typ, attachedAsgn, info)
|
||||||
|
changed = true
|
||||||
|
if typ.sink == nil:
|
||||||
|
liftBody(c, typ, attachedSink, info)
|
||||||
|
changed = true
|
||||||
|
if changed:
|
||||||
|
var alreadyPatched = initIntSet()
|
||||||
|
patchHead(typ.destructor.getBody, alreadyPatched)
|
||||||
|
patchHead(typ.assignment.getBody, alreadyPatched)
|
||||||
|
patchHead(typ.sink.getBody, alreadyPatched)
|
||||||
|
|
|
||||||
|
|
@ -664,6 +664,19 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags): PNode =
|
||||||
of skMacro: result = semMacroExpr(c, result, orig, callee, flags)
|
of skMacro: result = semMacroExpr(c, result, orig, callee, flags)
|
||||||
of skTemplate: result = semTemplateExpr(c, result, callee, flags)
|
of skTemplate: result = semTemplateExpr(c, result, callee, flags)
|
||||||
else:
|
else:
|
||||||
|
when false:
|
||||||
|
if callee.name.s[0] == '=' and result.len > 1:
|
||||||
|
# careful, do not skip tyDistinct here:
|
||||||
|
let t = result[1].typ.skipTypes({tyVar, tyGenericInst, tyAlias, tyInferred})
|
||||||
|
|
||||||
|
proc patchHead(callee: PSym; name: string; field: PSym; result: PNode) =
|
||||||
|
if callee.name.s == name and field != nil:
|
||||||
|
result.sons[0].sym = field
|
||||||
|
|
||||||
|
patchHead(callee, "=destroy", t.destructor, result)
|
||||||
|
patchHead(callee, "=sink", t.sink, result)
|
||||||
|
patchHead(callee, "=", t.assignment, result)
|
||||||
|
|
||||||
semFinishOperands(c, result)
|
semFinishOperands(c, result)
|
||||||
activate(c, result)
|
activate(c, result)
|
||||||
fixAbstractType(c, result)
|
fixAbstractType(c, result)
|
||||||
|
|
|
||||||
|
|
@ -147,8 +147,9 @@ proc evalTypeTrait(traitCall: PNode, operand: PType, context: PSym): PNode =
|
||||||
of "stripGenericParams":
|
of "stripGenericParams":
|
||||||
result = uninstantiate(operand).toNode(traitCall.info)
|
result = uninstantiate(operand).toNode(traitCall.info)
|
||||||
of "supportsCopyMem":
|
of "supportsCopyMem":
|
||||||
let complexObj = containsGarbageCollectedRef(operand) or
|
let t = operand.skipTypes({tyVar, tyGenericInst, tyAlias, tyInferred})
|
||||||
hasDestructor(operand)
|
let complexObj = containsGarbageCollectedRef(t) or
|
||||||
|
hasDestructor(t)
|
||||||
result = newIntNodeT(ord(not complexObj), traitCall)
|
result = newIntNodeT(ord(not complexObj), traitCall)
|
||||||
else:
|
else:
|
||||||
localError(traitCall.info, "unknown trait")
|
localError(traitCall.info, "unknown trait")
|
||||||
|
|
@ -251,7 +252,11 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||||
result = semTypeOf(c, n.sons[1])
|
result = semTypeOf(c, n.sons[1])
|
||||||
of mArrGet: result = semArrGet(c, n, flags)
|
of mArrGet: result = semArrGet(c, n, flags)
|
||||||
of mArrPut: result = semArrPut(c, n, flags)
|
of mArrPut: result = semArrPut(c, n, flags)
|
||||||
of mAsgn: result = semAsgnOpr(c, n)
|
of mAsgn:
|
||||||
|
if n[0].sym.name.s == "=":
|
||||||
|
result = semAsgnOpr(c, n)
|
||||||
|
else:
|
||||||
|
result = n
|
||||||
of mIsPartOf: result = semIsPartOf(c, n, flags)
|
of mIsPartOf: result = semIsPartOf(c, n, flags)
|
||||||
of mTypeTrait: result = semTypeTraits(c, n)
|
of mTypeTrait: result = semTypeTraits(c, n)
|
||||||
of mAstToStr:
|
of mAstToStr:
|
||||||
|
|
|
||||||
|
|
@ -365,16 +365,19 @@ proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PTransNode =
|
||||||
# addr ( nkConv ( deref ( x ) ) ) --> nkConv(x)
|
# addr ( nkConv ( deref ( x ) ) ) --> nkConv(x)
|
||||||
n.sons[0].sons[0] = m.sons[0]
|
n.sons[0].sons[0] = m.sons[0]
|
||||||
result = PTransNode(n.sons[0])
|
result = PTransNode(n.sons[0])
|
||||||
|
PNode(result).typ = n.typ
|
||||||
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
|
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
|
||||||
var m = n.sons[0].sons[1]
|
var m = n.sons[0].sons[1]
|
||||||
if m.kind == a or m.kind == b:
|
if m.kind == a or m.kind == b:
|
||||||
# addr ( nkConv ( deref ( x ) ) ) --> nkConv(x)
|
# addr ( nkConv ( deref ( x ) ) ) --> nkConv(x)
|
||||||
n.sons[0].sons[1] = m.sons[0]
|
n.sons[0].sons[1] = m.sons[0]
|
||||||
result = PTransNode(n.sons[0])
|
result = PTransNode(n.sons[0])
|
||||||
|
PNode(result).typ = n.typ
|
||||||
else:
|
else:
|
||||||
if n.sons[0].kind == a or n.sons[0].kind == b:
|
if n.sons[0].kind == a or n.sons[0].kind == b:
|
||||||
# addr ( deref ( x )) --> x
|
# addr ( deref ( x )) --> x
|
||||||
result = PTransNode(n.sons[0].sons[0])
|
result = PTransNode(n.sons[0].sons[0])
|
||||||
|
PNode(result).typ = n.typ
|
||||||
|
|
||||||
proc generateThunk(prc: PNode, dest: PType): PNode =
|
proc generateThunk(prc: PNode, dest: PType): PNode =
|
||||||
## Converts 'prc' into '(thunk, nil)' so that it's compatible with
|
## Converts 'prc' into '(thunk, nil)' so that it's compatible with
|
||||||
|
|
|
||||||
|
|
@ -300,10 +300,10 @@ when defined(nimArrIdx):
|
||||||
x: S) {.noSideEffect, magic: "ArrPut".}
|
x: S) {.noSideEffect, magic: "ArrPut".}
|
||||||
proc `=`*[T](dest: var T; src: T) {.noSideEffect, magic: "Asgn".}
|
proc `=`*[T](dest: var T; src: T) {.noSideEffect, magic: "Asgn".}
|
||||||
when defined(nimNewRuntime):
|
when defined(nimNewRuntime):
|
||||||
proc `=destroy`*[T](x: var T) {.inline.} =
|
proc `=destroy`*[T](x: var T) {.inline, magic: "Asgn".} =
|
||||||
## generic `destructor`:idx: implementation that can be overriden.
|
## generic `destructor`:idx: implementation that can be overriden.
|
||||||
discard
|
discard
|
||||||
proc `=sink`*[T](x: var T; y: T) {.inline.} =
|
proc `=sink`*[T](x: var T; y: T) {.inline, magic: "Asgn".} =
|
||||||
## generic `sink`:idx: implementation that can be overriden.
|
## generic `sink`:idx: implementation that can be overriden.
|
||||||
shallowCopy(x, y)
|
shallowCopy(x, y)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ var
|
||||||
proc `=destroy`*[T](x: var opt[T]) =
|
proc `=destroy`*[T](x: var opt[T]) =
|
||||||
if x.data != nil:
|
if x.data != nil:
|
||||||
when not supportsCopyMem(T):
|
when not supportsCopyMem(T):
|
||||||
`=destroy`(x.data)
|
`=destroy`(x.data[])
|
||||||
dealloc(x.data)
|
dealloc(x.data)
|
||||||
inc deallocCount
|
inc deallocCount
|
||||||
x.data = nil
|
x.data = nil
|
||||||
|
|
@ -69,11 +69,12 @@ proc createTree(data: float): Tree =
|
||||||
result.data = data
|
result.data = data
|
||||||
|
|
||||||
proc insert(t: var opt[Tree]; newVal: float) =
|
proc insert(t: var opt[Tree]; newVal: float) =
|
||||||
if it ?= t:
|
#if it ?= t:
|
||||||
if it.data > newVal:
|
if t.data != nil:
|
||||||
insert(it.le, newVal)
|
if newVal < t.data[].data:
|
||||||
elif it.data < newVal:
|
insert(t.data[].le, newVal)
|
||||||
insert(it.ri, newVal)
|
elif t.data[].data < newVal:
|
||||||
|
insert(t.data[].ri, newVal)
|
||||||
else:
|
else:
|
||||||
discard "already in the tree"
|
discard "already in the tree"
|
||||||
else:
|
else:
|
||||||
Loading…
Add table
Add a link
Reference in a new issue