diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 231ece0ce..fa960b5cf 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -48,6 +48,13 @@ type 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) = when toDebug.len > 0: 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 objType = leDotExpr[0].typ - if hasDestructor(objType): + if hasDestructor(c, objType): if objType.attachedOps[attachedDestructor] != nil and 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. @@ -364,8 +371,8 @@ proc genDefaultCall(t: PType; c: Con; info: TLineInfo): PNode = proc destructiveMoveVar(n: PNode; c: var Con; s: var Scope): PNode = # generate: (let tmp = v; reset(v); tmp) - if not hasDestructor(n.typ): - assert n.kind != nkSym or not hasDestructor(n.sym.typ) + if not hasDestructor(c, n.typ): + assert n.kind != nkSym or not hasDestructor(c, n.sym.typ) result = copyTree(n) else: 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 = result = newNodeIT(nkStmtListExpr, n.info, n.typ) let tmp = c.getTemp(s, n.typ, n.info) - if hasDestructor(n.typ): + if hasDestructor(c, n.typ): result.add c.genWasMoved(tmp) var m = c.genCopy(tmp, n) 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 = # it can happen that we need to destroy expression contructors # 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'? # This was already done in the sink parameter handling logic. 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 let tmp = c.getTemp(s.parent[], ret.typ, ret.info) 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) else: 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) for it in n: 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) 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.. 0: - createTypeBoundOps(tracked, n[1].typ.lastSon, n.info) createTypeBoundOps(tracked, n[1].typ, n.info) # new(x, finalizer): Problem: how to move finalizer into 'createTypeBoundOps'? diff --git a/tests/arc/tasyncleak.nim b/tests/arc/tasyncleak.nim new file mode 100644 index 000000000..16e7446a7 --- /dev/null +++ b/tests/arc/tasyncleak.nim @@ -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()