diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index cd5fc2f11e..7c2a15ce3c 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -138,6 +138,9 @@ import strutils, options, dfa, lowerings, tables, modulegraphs, msgs, lineinfos, parampatterns, sighashes +const + InterestingSyms = {skVar, skResult, skLet, skForVar, skTemp} + type Con = object owner: PSym @@ -214,6 +217,43 @@ proc isLastRead(n: PNode; c: var Con): bool = dbg: echo "ugh ", c.otherRead.isNil, " ", result + when false: + let s = n.sym + var pcs: seq[int] = @[instr+1] + var takenGotos: IntSet + var takenForks = initIntSet() + while pcs.len > 0: + var pc = pcs.pop + + takenGotos = initIntSet() + while pc < c.g.len: + case c.g[pc].kind + of def: + if c.g[pc].sym == s: + # the path lead to a redefinition of 's' --> abandon it. + break + inc pc + of use: + if c.g[pc].sym == s: + c.otherRead = c.g[pc].n + return false + inc pc + of goto: + # we must leave endless loops eventually: + if not takenGotos.containsOrIncl(pc): + pc = pc + c.g[pc].dest + else: + inc pc + of fork: + # we follow the next instruction but push the dest onto our "work" stack: + if not takenForks.containsOrIncl(pc): + pcs.add pc + c.g[pc].dest + inc pc + of InstrKind.join: + inc pc + #echo c.graph.config $ n.info, " last read here!" + return true + proc initialized(code: ControlFlowGraph; pc: int, init, uninit: var IntSet; comesFrom: int): int = ## Computes the set of definitely initialized variables accross all code paths @@ -250,6 +290,9 @@ proc initialized(code: ControlFlowGraph; pc: int, inc pc return pc +template interestingSym(s: PSym): bool = + s.owner == c.owner and s.kind in InterestingSyms and hasDestructor(s.typ) + template isUnpackedTuple(s: PSym): bool = ## we move out all elements of unpacked tuples, ## hence unpacked tuples themselves don't need to be destroyed @@ -310,8 +353,8 @@ proc canBeMoved(t: PType): bool {.inline.} = let t = t.skipTypes({tyGenericInst, tyAlias, tySink}) result = t.kind != tyRef and t.attachedOps[attachedSink] != nil -proc genSink(c: Con; dest, ri: PNode): PNode = - let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink}) +proc genSink(c: Con; t: PType; dest, ri: PNode): PNode = + let t = t.skipTypes({tyGenericInst, tyAlias, tySink}) let k = if t.attachedOps[attachedSink] != nil: attachedSink else: attachedAsgn if t.attachedOps[k] != nil: @@ -322,20 +365,20 @@ proc genSink(c: Con; dest, ri: PNode): PNode = # we generate a fast assignment in this case: result = newTree(nkFastAsgn, dest) -proc genCopyNoCheck(c: Con; dest, ri: PNode): PNode = - let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink}) - result = genOp(c, t, attachedAsgn, dest, ri) - -proc genCopy(c: var Con; dest, ri: PNode): PNode = - let t = dest.typ +proc genCopy(c: var Con; t: PType; dest, ri: PNode): PNode = if tfHasOwned in t.flags: # try to improve the error message here: if c.otherRead == nil: discard isLastRead(ri, c) checkForErrorPragma(c, t, ri, "=") - genCopyNoCheck(c, dest, ri) + let t = t.skipTypes({tyGenericInst, tyAlias, tySink}) + result = genOp(c, t, attachedAsgn, dest, ri) -proc genDestroy(c: Con; dest: PNode): PNode = - let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink}) +proc genCopyNoCheck(c: Con; t: PType; dest, ri: PNode): PNode = + let t = t.skipTypes({tyGenericInst, tyAlias, tySink}) + result = genOp(c, t, attachedAsgn, dest, ri) + +proc genDestroy(c: Con; t: PType; dest: PNode): PNode = + let t = t.skipTypes({tyGenericInst, tyAlias, tySink}) result = genOp(c, t, attachedDestructor, dest, nil) proc addTopVar(c: var Con; v: PNode) = @@ -347,10 +390,20 @@ proc getTemp(c: var Con; typ: PType; info: TLineInfo): PNode = result = newSymNode(sym) c.addTopVar(result) -proc genWasMoved(n: PNode; c: var Con): PNode = +proc p(n: PNode; c: var Con): PNode + +template recurse(n, dest) = + for i in 0.. 0 and n.typ != nil and isDangerousSeq(n.typ): @@ -396,151 +467,19 @@ proc containsConstSeq(n: PNode): bool = of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv: result = containsConstSeq(n[1]) of nkObjConstr, nkClosure: - for i in 1.. 0: - ri = genDefaultCall(v.typ, c, v.info) - if ri.kind != nkEmpty: - let r = moveOrCopy(v, ri, c) - result.add r - else: - result.add keepVar(n, it, c) - of nkCallKinds: - let parameters = n[0].typ - let L = if parameters != nil: parameters.len else: 0 - for i in 1.. 0 and isDangerousSeq(ri.typ): - result = genCopy(c, dest, ri) + result = genCopy(c, dest.typ, dest, ri) else: - result = genSink(c, dest, ri) - result.add pExpr(ri, c) - of nkObjConstr, nkTupleConstr, nkClosure, nkCharLit..nkNilLit: - result = genSink(c, dest, ri) - result.add pExpr(ri, c) + result = genSink(c, dest.typ, dest, ri) + let ri2 = copyTree(ri) + for i in 0.. 0: + ri = genDefaultCall(v.typ, c, v.info) + if ri.kind != nkEmpty: + let r = moveOrCopy(v, ri, c) + result.add r + else: + result.add keepVar(n, it, c) + of nkCallKinds: + let parameters = n[0].typ + let L = if parameters != nil: parameters.len else: 0 + for i in 1 ..< n.len: + n.sons[i] = pArg(n[i], c, i < L and isSinkTypeForParam(parameters[i])) + if n.typ != nil and hasDestructor(n.typ): + discard "produce temp creation" + result = newNodeIT(nkStmtListExpr, n.info, n.typ) + let tmp = getTemp(c, n.typ, n.info) + var sinkExpr = genSink(c, n.typ, tmp, n) + sinkExpr.add n + result.add sinkExpr + result.add tmp + c.destroys.add genDestroy(c, n.typ, tmp) + else: + result = n + of nkAsgn, nkFastAsgn: + if hasDestructor(n[0].typ) and n[1].kind notin {nkProcDef, nkDo, nkLambda}: + # rule (self-assignment-removal): + if n[1].kind == nkSym and n[0].kind == nkSym and n[0].sym == n[1].sym: + result = newNodeI(nkEmpty, n.info) + else: + result = moveOrCopy(n[0], n[1], c) + else: + result = copyNode(n) + recurse(n, result) + of nkNone..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, + nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef: + result = n + of nkCast, nkHiddenStdConv, nkHiddenSubConv, nkConv: + result = copyNode(n) + # Destination type + result.add n[0] + # Analyse the inner expression + result.add p(n[1], c) + of nkWhen: + # This should be a "when nimvm" node. + result = copyTree(n) + result[1][0] = p(result[1][0], c) + of nkRaiseStmt: + if optNimV2 in c.graph.config.globalOptions and n[0].kind != nkEmpty: + if n[0].kind in nkCallKinds: + let call = copyNode(n[0]) + recurse(n[0], call) + result = copyNode(n) + result.add call + else: + let t = n[0].typ + let tmp = getTemp(c, t, n.info) + var m = genCopyNoCheck(c, t, tmp, n[0]) + + m.add p(n[0], c) + result = newTree(nkStmtList, genWasMoved(tmp, c), m) + var toDisarm = n[0] + if toDisarm.kind == nkStmtListExpr: toDisarm = toDisarm.lastSon + if toDisarm.kind == nkSym and toDisarm.sym.owner == c.owner: + result.add genWasMoved(toDisarm, c) + result.add newTree(nkRaiseStmt, tmp) + else: + result = copyNode(n) + recurse(n, result) + of nkForStmt, nkParForStmt, nkWhileStmt: + inc c.inLoop + result = copyNode(n) + recurse(n, result) + dec c.inLoop + else: + result = copyNode(n) + recurse(n, result) + proc extractDestroysForTemporaries(c: Con, destroys: PNode): PNode = result = newNodeI(nkStmtList, destroys.info) - for i in 0.. 0: result.add c.topLevelVars if c.destroys.len > 0: - c.destroys.sons = reverseDestroys(c.destroys.sons) + reverseDestroys(c.destroys) if owner.kind == skModule: result.add newTryFinally(body, extractDestroysForTemporaries(c, c.destroys)) g.globalDestructors.add c.destroys @@ -860,5 +900,6 @@ proc injectDestructorCalls*(g: ModuleGraph; owner: PSym; n: PNode): PNode = result.add body dbg: - echo ">---------transformed-to--------->" + echo "------------------------------------" + echo owner.name.s, " transformed to: " echo result