diff options
author | Andreas Rumpf <rumpf_a@web.de> | 2020-11-15 15:47:42 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-11-15 15:47:42 +0100 |
commit | 7eb34d170a489c2a0ed90d921f3f7cc6813f19c1 (patch) | |
tree | 88fbcfdc45f6d75eb888fa3048582ccc683b0ab6 | |
parent | 92da06e64ece2f87e259c17439c085074d77ef97 (diff) | |
download | Nim-7eb34d170a489c2a0ed90d921f3f7cc6813f19c1.tar.gz |
fixes #15753 [backport:1.4] (#15971)
-rw-r--r-- | compiler/liftdestructors.nim | 95 | ||||
-rw-r--r-- | tests/arc/torc_selfcycles.nim | 33 |
2 files changed, 109 insertions, 19 deletions
diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index 9b6c179db..943dd074e 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -392,6 +392,16 @@ proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode = v.addVar(result, lowerings.newIntLit(c.g, body.info, first)) body.add v +proc declareTempOf(c: var TLiftCtx; body: PNode; value: PNode): PNode = + var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), nextId(c.idgen), c.fn, c.info) + temp.typ = value.typ + incl(temp.flags, sfFromGeneric) + + var v = newNodeI(nkVarSection, c.info) + result = newSymNode(temp) + v.addVar(result, value) + body.add v + proc addIncStmt(c: var TLiftCtx; body, i: PNode) = let incCall = genBuiltin(c.g, mInc, "inc", i) incCall.add lowerings.newIntLit(c.g, c.info, 1) @@ -508,43 +518,75 @@ proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = discard "strings are atomic and have no inner elements that are to trace" proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = + #[ bug #15753 is really subtle. Usually the classical write barrier for reference + counting looks like this:: + + incRef source # increment first; this takes care of self-assignments1 + decRef dest + dest[] = source + + However, 'decRef dest' might trigger a cycle collection and then the collector + traverses the graph. It is crucial that when it follows the pointers the assignment + 'dest[] = source' already happened so that we don't do trial deletion on a wrong + graph -- this causes premature freeing of objects! The correct barrier looks like + this:: + + let tmp = dest + incRef source + dest[] = source + decRef tmp + + ]# var actions = newNodeI(nkStmtList, c.info) let elemType = t.lastSon createTypeBoundOps(c.g, c.c, elemType, c.info, c.idgen) + let isCyclic = c.g.config.selectedGC == gcOrc and types.canFormAcycle(elemType) + + let tmp = + if isCyclic and c.kind in {attachedAsgn, attachedSink}: + declareTempOf(c, body, x) + else: + x if isFinal(elemType): - addDestructorCall(c, elemType, actions, genDeref(x, nkDerefExpr)) + addDestructorCall(c, elemType, actions, genDeref(tmp, nkDerefExpr)) var alignOf = genBuiltin(c.g, mAlignOf, "alignof", newNodeIT(nkType, c.info, elemType)) alignOf.typ = getSysType(c.g, c.info, tyInt) - actions.add callCodegenProc(c.g, "nimRawDispose", c.info, x, alignOf) + actions.add callCodegenProc(c.g, "nimRawDispose", c.info, tmp, alignOf) else: - addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(x, nkDerefExpr)) - actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, x) - - let isCyclic = c.g.config.selectedGC == gcOrc and types.canFormAcycle(elemType) + addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(tmp, nkDerefExpr)) + actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, tmp) var cond: PNode if isCyclic: if isFinal(elemType): let typInfo = genBuiltin(c.g, mGetTypeInfoV2, "getTypeInfoV2", newNodeIT(nkType, x.info, elemType)) typInfo.typ = getSysType(c.g, c.info, tyPointer) - cond = callCodegenProc(c.g, "nimDecRefIsLastCyclicStatic", c.info, x, typInfo) + cond = callCodegenProc(c.g, "nimDecRefIsLastCyclicStatic", c.info, tmp, typInfo) else: - cond = callCodegenProc(c.g, "nimDecRefIsLastCyclicDyn", c.info, x) + cond = callCodegenProc(c.g, "nimDecRefIsLastCyclicDyn", c.info, tmp) else: cond = callCodegenProc(c.g, "nimDecRefIsLast", c.info, x) cond.typ = getSysType(c.g, x.info, tyBool) case c.kind of attachedSink: - body.add genIf(c, cond, actions) - body.add newAsgnStmt(x, y) + if isCyclic: + body.add newAsgnStmt(x, y) + body.add genIf(c, cond, actions) + else: + body.add genIf(c, cond, actions) + body.add newAsgnStmt(x, y) of attachedAsgn: body.add genIf(c, y, callCodegenProc(c.g, if isCyclic: "nimIncRefCyclic" else: "nimIncRef", c.info, y)) - body.add genIf(c, cond, actions) - body.add newAsgnStmt(x, y) + if isCyclic: + body.add newAsgnStmt(x, y) + body.add genIf(c, cond, actions) + else: + body.add genIf(c, cond, actions) + body.add newAsgnStmt(x, y) of attachedDestructor: body.add genIf(c, cond, actions) of attachedDeepCopy: assert(false, "cannot happen") @@ -571,19 +613,30 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = let xenv = genBuiltin(c.g, mAccessEnv, "accessEnv", x) xenv.typ = getSysType(c.g, c.info, tyPointer) + let isCyclic = c.g.config.selectedGC == gcOrc + let tmp = + if isCyclic and c.kind in {attachedAsgn, attachedSink}: + declareTempOf(c, body, xenv) + else: + xenv + var actions = newNodeI(nkStmtList, c.info) - actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, xenv) + actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, tmp) let decRefProc = - if c.g.config.selectedGC == gcOrc: "nimDecRefIsLastCyclicDyn" + if isCyclic: "nimDecRefIsLastCyclicDyn" else: "nimDecRefIsLast" - let cond = callCodegenProc(c.g, decRefProc, c.info, xenv) + let cond = callCodegenProc(c.g, decRefProc, c.info, tmp) cond.typ = getSysType(c.g, x.info, tyBool) case c.kind of attachedSink: - body.add genIf(c, cond, actions) - body.add newAsgnStmt(x, y) + if isCyclic: + body.add newAsgnStmt(x, y) + body.add genIf(c, cond, actions) + else: + body.add genIf(c, cond, actions) + body.add newAsgnStmt(x, y) of attachedAsgn: let yenv = genBuiltin(c.g, mAccessEnv, "accessEnv", y) yenv.typ = getSysType(c.g, c.info, tyPointer) @@ -591,8 +644,12 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = if c.g.config.selectedGC == gcOrc: "nimIncRefCyclic" else: "nimIncRef" body.add genIf(c, yenv, callCodegenProc(c.g, incRefProc, c.info, yenv)) - body.add genIf(c, cond, actions) - body.add newAsgnStmt(x, y) + if isCyclic: + body.add newAsgnStmt(x, y) + body.add genIf(c, cond, actions) + else: + body.add genIf(c, cond, actions) + body.add newAsgnStmt(x, y) of attachedDestructor: body.add genIf(c, cond, actions) of attachedDeepCopy: assert(false, "cannot happen") diff --git a/tests/arc/torc_selfcycles.nim b/tests/arc/torc_selfcycles.nim new file mode 100644 index 000000000..ac4fa52ce --- /dev/null +++ b/tests/arc/torc_selfcycles.nim @@ -0,0 +1,33 @@ +discard """ + output: '''ok''' + cmd: '''nim c --gc:orc -d:useMalloc -d:nimStressOrc $file''' + valgrind: "leaks" +""" + +# bug #15753 + +type + NodeKind = enum + nkDancing, + nkColumn + + DancingNode = ref object + right: DancingNode + column: DancingNode + kind: NodeKind + +proc newColumnNode(): DancingNode = + result = DancingNode(kind: nkColumn) + result.right = result + result.column = result + +proc createDLXList(): DancingNode = + result = newColumnNode() + + for i in 0 .. 15: + let n = newColumnNode() + n.right = result.right + result = n + echo "ok" + +var dlxlist = createDLXList() |