parent
377f716766
commit
e6f0d4a5a5
4 changed files with 57 additions and 28 deletions
|
|
@ -48,6 +48,13 @@ type
|
||||||
|
|
||||||
const toDebug {.strdefine.} = ""
|
const toDebug {.strdefine.} = ""
|
||||||
|
|
||||||
|
proc hasDestructor(c: Con; t: PType): bool {.inline.} =
|
||||||
|
result = ast.hasDestructor(t)
|
||||||
|
when toDebug.len > 0:
|
||||||
|
# for more effective debugging
|
||||||
|
if not result and c.graph.config.selectedGC in {gcArc, gcOrc}:
|
||||||
|
assert(not containsGarbageCollectedRef(t))
|
||||||
|
|
||||||
template dbg(body) =
|
template dbg(body) =
|
||||||
when toDebug.len > 0:
|
when toDebug.len > 0:
|
||||||
if c.owner.name.s == toDebug or toDebug == "always":
|
if c.owner.name.s == toDebug or toDebug == "always":
|
||||||
|
|
@ -330,7 +337,7 @@ proc genDiscriminantAsgn(c: var Con; s: var Scope; n: PNode): PNode =
|
||||||
let leDotExpr = if le.kind == nkCheckedFieldExpr: le[0] else: le
|
let leDotExpr = if le.kind == nkCheckedFieldExpr: le[0] else: le
|
||||||
let objType = leDotExpr[0].typ
|
let objType = leDotExpr[0].typ
|
||||||
|
|
||||||
if hasDestructor(objType):
|
if hasDestructor(c, objType):
|
||||||
if objType.attachedOps[attachedDestructor] != nil and
|
if objType.attachedOps[attachedDestructor] != nil and
|
||||||
sfOverriden in objType.attachedOps[attachedDestructor].flags:
|
sfOverriden in objType.attachedOps[attachedDestructor].flags:
|
||||||
localError(c.graph.config, n.info, errGenerated, """Assignment to discriminant for objects with user defined destructor is not supported, object must have default destructor.
|
localError(c.graph.config, n.info, errGenerated, """Assignment to discriminant for objects with user defined destructor is not supported, object must have default destructor.
|
||||||
|
|
@ -364,8 +371,8 @@ proc genDefaultCall(t: PType; c: Con; info: TLineInfo): PNode =
|
||||||
|
|
||||||
proc destructiveMoveVar(n: PNode; c: var Con; s: var Scope): PNode =
|
proc destructiveMoveVar(n: PNode; c: var Con; s: var Scope): PNode =
|
||||||
# generate: (let tmp = v; reset(v); tmp)
|
# generate: (let tmp = v; reset(v); tmp)
|
||||||
if not hasDestructor(n.typ):
|
if not hasDestructor(c, n.typ):
|
||||||
assert n.kind != nkSym or not hasDestructor(n.sym.typ)
|
assert n.kind != nkSym or not hasDestructor(c, n.sym.typ)
|
||||||
result = copyTree(n)
|
result = copyTree(n)
|
||||||
else:
|
else:
|
||||||
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
|
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
|
||||||
|
|
@ -393,7 +400,7 @@ proc isCapturedVar(n: PNode): bool =
|
||||||
proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode =
|
proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode =
|
||||||
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
|
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
|
||||||
let tmp = c.getTemp(s, n.typ, n.info)
|
let tmp = c.getTemp(s, n.typ, n.info)
|
||||||
if hasDestructor(n.typ):
|
if hasDestructor(c, n.typ):
|
||||||
result.add c.genWasMoved(tmp)
|
result.add c.genWasMoved(tmp)
|
||||||
var m = c.genCopy(tmp, n)
|
var m = c.genCopy(tmp, n)
|
||||||
m.add p(n, c, s, normal)
|
m.add p(n, c, s, normal)
|
||||||
|
|
@ -432,7 +439,7 @@ proc containsConstSeq(n: PNode): bool =
|
||||||
proc ensureDestruction(arg, orig: PNode; c: var Con; s: var Scope): PNode =
|
proc ensureDestruction(arg, orig: PNode; c: var Con; s: var Scope): PNode =
|
||||||
# it can happen that we need to destroy expression contructors
|
# it can happen that we need to destroy expression contructors
|
||||||
# like [], (), closures explicitly in order to not leak them.
|
# like [], (), closures explicitly in order to not leak them.
|
||||||
if arg.typ != nil and hasDestructor(arg.typ):
|
if arg.typ != nil and hasDestructor(c, arg.typ):
|
||||||
# produce temp creation for (fn, env). But we need to move 'env'?
|
# produce temp creation for (fn, env). But we need to move 'env'?
|
||||||
# This was already done in the sink parameter handling logic.
|
# This was already done in the sink parameter handling logic.
|
||||||
result = newNodeIT(nkStmtListExpr, arg.info, arg.typ)
|
result = newNodeIT(nkStmtListExpr, arg.info, arg.typ)
|
||||||
|
|
@ -521,7 +528,7 @@ template processScopeExpr(c: var Con; s: var Scope; ret: PNode, processCall: unt
|
||||||
# tricky because you would have to intercept moveOrCopy at a certain point
|
# tricky because you would have to intercept moveOrCopy at a certain point
|
||||||
let tmp = c.getTemp(s.parent[], ret.typ, ret.info)
|
let tmp = c.getTemp(s.parent[], ret.typ, ret.info)
|
||||||
tmp.sym.flags.incl sfSingleUsedTemp
|
tmp.sym.flags.incl sfSingleUsedTemp
|
||||||
let cpy = if ret.typ.hasDestructor:
|
let cpy = if hasDestructor(c, ret.typ):
|
||||||
moveOrCopy(tmp, ret, c, s, isDecl = true)
|
moveOrCopy(tmp, ret, c, s, isDecl = true)
|
||||||
else:
|
else:
|
||||||
newTree(nkFastAsgn, tmp, p(ret, c, s, normal))
|
newTree(nkFastAsgn, tmp, p(ret, c, s, normal))
|
||||||
|
|
@ -774,10 +781,10 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode =
|
||||||
result = newNodeI(nkStmtList, n.info)
|
result = newNodeI(nkStmtList, n.info)
|
||||||
for it in n:
|
for it in n:
|
||||||
var ri = it[^1]
|
var ri = it[^1]
|
||||||
if it.kind == nkVarTuple and hasDestructor(ri.typ):
|
if it.kind == nkVarTuple and hasDestructor(c, ri.typ):
|
||||||
let x = lowerTupleUnpacking(c.graph, it, c.owner)
|
let x = lowerTupleUnpacking(c.graph, it, c.owner)
|
||||||
result.add p(x, c, s, consumed)
|
result.add p(x, c, s, consumed)
|
||||||
elif it.kind == nkIdentDefs and hasDestructor(it[0].typ) and not isCursor(it[0], c):
|
elif it.kind == nkIdentDefs and hasDestructor(c, it[0].typ) and not isCursor(it[0], c):
|
||||||
for j in 0..<it.len-2:
|
for j in 0..<it.len-2:
|
||||||
let v = it[j]
|
let v = it[j]
|
||||||
if v.kind == nkSym:
|
if v.kind == nkSym:
|
||||||
|
|
@ -797,7 +804,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode =
|
||||||
v.add itCopy
|
v.add itCopy
|
||||||
result.add v
|
result.add v
|
||||||
of nkAsgn, nkFastAsgn:
|
of nkAsgn, nkFastAsgn:
|
||||||
if hasDestructor(n[0].typ) and n[1].kind notin {nkProcDef, nkDo, nkLambda} and
|
if hasDestructor(c, n[0].typ) and n[1].kind notin {nkProcDef, nkDo, nkLambda} and
|
||||||
not isCursor(n[0], c):
|
not isCursor(n[0], c):
|
||||||
# rule (self-assignment-removal):
|
# rule (self-assignment-removal):
|
||||||
if n[1].kind == nkSym and n[0].kind == nkSym and n[0].sym == n[1].sym:
|
if n[1].kind == nkSym and n[0].kind == nkSym and n[0].sym == n[1].sym:
|
||||||
|
|
@ -828,7 +835,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode =
|
||||||
result = shallowCopy(n)
|
result = shallowCopy(n)
|
||||||
for i in 0 ..< n.len:
|
for i in 0 ..< n.len:
|
||||||
result[i] = p(n[i], c, s, normal)
|
result[i] = p(n[i], c, s, normal)
|
||||||
if n.typ != nil and hasDestructor(n.typ):
|
if n.typ != nil and hasDestructor(c, n.typ):
|
||||||
if mode == normal:
|
if mode == normal:
|
||||||
result = ensureDestruction(result, n, c, s)
|
result = ensureDestruction(result, n, c, s)
|
||||||
|
|
||||||
|
|
@ -858,7 +865,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode =
|
||||||
result[0] = p(n[0], c, s, normal)
|
result[0] = p(n[0], c, s, normal)
|
||||||
for i in 1 ..< n.len:
|
for i in 1 ..< n.len:
|
||||||
result[i] = n[i]
|
result[i] = n[i]
|
||||||
if mode == sinkArg and hasDestructor(n.typ):
|
if mode == sinkArg and hasDestructor(c, n.typ):
|
||||||
if isAnalysableFieldAccess(n, c.owner) and isLastRead(n, c):
|
if isAnalysableFieldAccess(n, c.owner) and isLastRead(n, c):
|
||||||
s.wasMoved.add c.genWasMoved(n)
|
s.wasMoved.add c.genWasMoved(n)
|
||||||
else:
|
else:
|
||||||
|
|
@ -868,7 +875,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode =
|
||||||
result = shallowCopy(n)
|
result = shallowCopy(n)
|
||||||
for i in 0 ..< n.len:
|
for i in 0 ..< n.len:
|
||||||
result[i] = p(n[i], c, s, normal)
|
result[i] = p(n[i], c, s, normal)
|
||||||
if mode == sinkArg and hasDestructor(n.typ):
|
if mode == sinkArg and hasDestructor(c, n.typ):
|
||||||
if isAnalysableFieldAccess(n, c.owner) and isLastRead(n, c):
|
if isAnalysableFieldAccess(n, c.owner) and isLastRead(n, c):
|
||||||
# consider 'a[(g; destroy(g); 3)]', we want to say 'wasMoved(a[3])'
|
# consider 'a[(g; destroy(g); 3)]', we want to say 'wasMoved(a[3])'
|
||||||
# without the junk, hence 'c.genWasMoved(n)'
|
# without the junk, hence 'c.genWasMoved(n)'
|
||||||
|
|
@ -1004,7 +1011,7 @@ proc injectDestructorCalls*(g: ModuleGraph; owner: PSym; n: PNode): PNode =
|
||||||
let params = owner.typ.n
|
let params = owner.typ.n
|
||||||
for i in 1..<params.len:
|
for i in 1..<params.len:
|
||||||
let t = params[i].sym.typ
|
let t = params[i].sym.typ
|
||||||
if isSinkTypeForParam(t) and hasDestructor(t.skipTypes({tySink})):
|
if isSinkTypeForParam(t) and hasDestructor(c, t.skipTypes({tySink})):
|
||||||
scope.final.add c.genDestroy(params[i])
|
scope.final.add c.genDestroy(params[i])
|
||||||
#if optNimV2 in c.graph.config.globalOptions:
|
#if optNimV2 in c.graph.config.globalOptions:
|
||||||
# injectDefaultCalls(n, c)
|
# injectDefaultCalls(n, c)
|
||||||
|
|
|
||||||
|
|
@ -507,6 +507,8 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
var actions = newNodeI(nkStmtList, c.info)
|
var actions = newNodeI(nkStmtList, c.info)
|
||||||
let elemType = t.lastSon
|
let elemType = t.lastSon
|
||||||
|
|
||||||
|
createTypeBoundOps(c.g, c.c, elemType, c.info)
|
||||||
|
|
||||||
if isFinal(elemType):
|
if isFinal(elemType):
|
||||||
addDestructorCall(c, elemType, actions, genDeref(x, nkDerefExpr))
|
addDestructorCall(c, elemType, actions, genDeref(x, nkDerefExpr))
|
||||||
actions.add callCodegenProc(c.g, "nimRawDispose", c.info, x)
|
actions.add callCodegenProc(c.g, "nimRawDispose", c.info, x)
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,19 @@ proc `<=`(a, b: TLockLevel): bool {.borrow.}
|
||||||
proc `==`(a, b: TLockLevel): bool {.borrow.}
|
proc `==`(a, b: TLockLevel): bool {.borrow.}
|
||||||
proc max(a, b: TLockLevel): TLockLevel {.borrow.}
|
proc max(a, b: TLockLevel): TLockLevel {.borrow.}
|
||||||
|
|
||||||
|
proc createTypeBoundOps(tracked: PEffects, typ: PType; info: TLineInfo) =
|
||||||
|
if typ == nil: return
|
||||||
|
when false:
|
||||||
|
let realType = typ.skipTypes(abstractInst)
|
||||||
|
if realType.kind == tyRef and
|
||||||
|
optSeqDestructors in tracked.config.globalOptions:
|
||||||
|
createTypeBoundOps(tracked.graph, tracked.c, realType.lastSon, info)
|
||||||
|
|
||||||
|
createTypeBoundOps(tracked.graph, tracked.c, typ, info)
|
||||||
|
if (tfHasAsgn in typ.flags) or
|
||||||
|
optSeqDestructors in tracked.config.globalOptions:
|
||||||
|
tracked.owner.flags.incl sfInjectDestructors
|
||||||
|
|
||||||
proc isLocalVar(a: PEffects, s: PSym): bool =
|
proc isLocalVar(a: PEffects, s: PSym): bool =
|
||||||
# and (s.kind != skParam or s.typ.kind == tyOut)
|
# and (s.kind != skParam or s.typ.kind == tyOut)
|
||||||
s.kind in {skVar, skResult} and sfGlobal notin s.flags and
|
s.kind in {skVar, skResult} and sfGlobal notin s.flags and
|
||||||
|
|
@ -400,6 +413,7 @@ proc trackTryStmt(tracked: PEffects, n: PNode) =
|
||||||
if b[j].isInfixAs():
|
if b[j].isInfixAs():
|
||||||
assert(b[j][1].kind == nkType)
|
assert(b[j][1].kind == nkType)
|
||||||
catches(tracked, b[j][1].typ)
|
catches(tracked, b[j][1].typ)
|
||||||
|
createTypeBoundOps(tracked, b[j][2].typ, b[j][2].info)
|
||||||
else:
|
else:
|
||||||
assert(b[j].kind == nkType)
|
assert(b[j].kind == nkType)
|
||||||
catches(tracked, b[j].typ)
|
catches(tracked, b[j].typ)
|
||||||
|
|
@ -720,20 +734,6 @@ proc checkRange(c: PEffects; value: PNode; typ: PType) =
|
||||||
checkLe(c, lowBound, value)
|
checkLe(c, lowBound, value)
|
||||||
checkLe(c, value, highBound)
|
checkLe(c, value, highBound)
|
||||||
|
|
||||||
proc createTypeBoundOps(tracked: PEffects, typ: PType; info: TLineInfo) =
|
|
||||||
if typ == nil: return
|
|
||||||
let realType = typ.skipTypes(abstractInst)
|
|
||||||
when false:
|
|
||||||
# XXX fix this in liftdestructors instead
|
|
||||||
if realType.kind == tyRef and
|
|
||||||
optSeqDestructors in tracked.config.globalOptions:
|
|
||||||
createTypeBoundOps(tracked.graph, tracked.c, realType.lastSon, info)
|
|
||||||
|
|
||||||
createTypeBoundOps(tracked.graph, tracked.c, typ, info)
|
|
||||||
if (tfHasAsgn in typ.flags) or
|
|
||||||
optSeqDestructors in tracked.config.globalOptions:
|
|
||||||
tracked.owner.flags.incl sfInjectDestructors
|
|
||||||
|
|
||||||
proc trackCall(tracked: PEffects; n: PNode) =
|
proc trackCall(tracked: PEffects; n: PNode) =
|
||||||
template gcsafeAndSideeffectCheck() =
|
template gcsafeAndSideeffectCheck() =
|
||||||
if notGcSafe(op) and not importedFromC(a):
|
if notGcSafe(op) and not importedFromC(a):
|
||||||
|
|
@ -799,7 +799,6 @@ proc trackCall(tracked: PEffects; n: PNode) =
|
||||||
|
|
||||||
# check required for 'nim check':
|
# check required for 'nim check':
|
||||||
if n[1].typ.len > 0:
|
if n[1].typ.len > 0:
|
||||||
createTypeBoundOps(tracked, n[1].typ.lastSon, n.info)
|
|
||||||
createTypeBoundOps(tracked, n[1].typ, n.info)
|
createTypeBoundOps(tracked, n[1].typ, n.info)
|
||||||
# new(x, finalizer): Problem: how to move finalizer into 'createTypeBoundOps'?
|
# new(x, finalizer): Problem: how to move finalizer into 'createTypeBoundOps'?
|
||||||
|
|
||||||
|
|
|
||||||
21
tests/arc/tasyncleak.nim
Normal file
21
tests/arc/tasyncleak.nim
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
discard """
|
||||||
|
outputsub: "(allocCount: 6013, deallocCount: 6007)"
|
||||||
|
cmd: "nim c --gc:orc -d:nimAllocStats $file"
|
||||||
|
"""
|
||||||
|
|
||||||
|
import asyncdispatch
|
||||||
|
# bug #15076
|
||||||
|
const
|
||||||
|
# Just to occupy some RAM
|
||||||
|
BigData = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||||
|
|
||||||
|
proc doNothing(): Future[void] {.async.} =
|
||||||
|
discard
|
||||||
|
|
||||||
|
proc main(): Future[void] {.async.} =
|
||||||
|
for x in 0 .. 1_000:
|
||||||
|
await doNothing()
|
||||||
|
|
||||||
|
waitFor main()
|
||||||
|
GC_fullCollect()
|
||||||
|
echo getAllocStats()
|
||||||
Loading…
Add table
Add a link
Reference in a new issue