diff options
Diffstat (limited to 'tests/gc/closureleak.nim')
-rw-r--r-- | tests/gc/closureleak.nim | 52 |
1 files changed, 52 insertions, 0 deletions
diff --git a/tests/gc/closureleak.nim b/tests/gc/closureleak.nim new file mode 100644 index 000000000..e67beb513 --- /dev/null +++ b/tests/gc/closureleak.nim @@ -0,0 +1,52 @@ +discard """ + outputsub: "true" + disabled: "32bit" +""" + +type + TFoo* = object + id: int + fn: proc() {.closure.} +var foo_counter = 0 +var alive_foos = newseq[int](0) + +when defined(gcDestructors): + proc `=destroy`(some: TFoo) = + alive_foos.del alive_foos.find(some.id) + # TODO: fixme: investigate why `=destroy` requires `some.fn` to be `gcsafe` + # the debugging info below came from `symPrototype` in the liftdestructors + # proc (){.closure, gcsafe.}, {tfThread, tfHasAsgn, tfCheckedForDestructor, tfExplicitCallConv} + # var proc (){.closure, gcsafe.}, {tfHasGCedMem} + # it worked by accident with var T destructors because in the sempass2 + # + # let argtype = skipTypes(a.typ, abstractInst) # !!! it does't skip `tyVar` + # if argtype.kind == tyProc and notGcSafe(argtype) and not tracked.inEnforcedGcSafe: + # localError(tracked.config, n.info, $n & " is not GC safe") + {.cast(gcsafe).}: + `=destroy`(some.fn) + +else: + proc free*(some: ref TFoo) = + #echo "Tfoo #", some.id, " freed" + alive_foos.del alive_foos.find(some.id) + +proc newFoo*(): ref TFoo = + when defined(gcDestructors): + new result + else: + new result, free + + result.id = foo_counter + alive_foos.add result.id + inc foo_counter + +for i in 0 ..< 10: + discard newFoo() + +for i in 0 ..< 10: + let f = newFoo() + f.fn = proc = + echo f.id + +GC_fullcollect() +echo alive_foos.len <= 3 |