destructors: work in progress

This commit is contained in:
Araq 2017-10-24 09:40:28 +02:00
commit b407f083ba
8 changed files with 109 additions and 27 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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