From 9b31f6785947974e89d46bbc0dee11f1c78754dc Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 5 Jul 2018 11:31:27 +0200 Subject: [PATCH] turn destructors into finalizers --- compiler/ccgexprs.nim | 18 ++++++++++++--- .../turn_destroy_into_finalizer.nim | 22 +++++++++++++++++++ tests/gc/gcleak.nim | 6 ++--- 3 files changed, 40 insertions(+), 6 deletions(-) create mode 100644 tests/destructor/turn_destroy_into_finalizer.nim diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index dcde4e2707..fdafa1927e 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1135,9 +1135,21 @@ proc rawGenNew(p: BProc, a: TLoc, sizeExpr: Rope) = if sizeExpr.isNil: sizeExpr = "sizeof($1)" % [getTypeDesc(p.module, bt)] - let args = [getTypeDesc(p.module, typ), - genTypeInfo(p.module, typ, a.lode.info), - sizeExpr] + + let ti = genTypeInfo(p.module, typ, a.lode.info) + if bt.destructor != nil: + # the prototype of a destructor is ``=destroy(x: var T)`` and that of a + # finalizer is: ``proc (x: ref T) {.nimcall.}``. We need to check the calling + # convention at least: + if bt.destructor.typ == nil or bt.destructor.typ.callConv != ccDefault: + localError(p.module.config, a.lode.info, + "the destructor that is turned into a finalizer needs " & + "to have the 'nimcall' calling convention") + var f: TLoc + initLocExpr(p, newSymNode(bt.destructor), f) + addf(p.module.s[cfsTypeInit3], "$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)]) + + let args = [getTypeDesc(p.module, typ), ti, sizeExpr] if a.storage == OnHeap and usesNativeGC(p.config): # use newObjRC1 as an optimization if canFormAcycle(a.t): diff --git a/tests/destructor/turn_destroy_into_finalizer.nim b/tests/destructor/turn_destroy_into_finalizer.nim new file mode 100644 index 0000000000..f5b7055937 --- /dev/null +++ b/tests/destructor/turn_destroy_into_finalizer.nim @@ -0,0 +1,22 @@ +discard """ + output: '''true''' +""" + +type + Foo = object + id: int + +var destroyed: int + +proc `=destroy`(x: var Foo) = + #echo "finally ", x.id + inc destroyed + +proc main = + var r: ref Foo + for i in 1..50_000: + new(r) + r.id = i + echo destroyed > 30_000 + +main() diff --git a/tests/gc/gcleak.nim b/tests/gc/gcleak.nim index 8852a8d913..24ac1036a2 100644 --- a/tests/gc/gcleak.nim +++ b/tests/gc/gcleak.nim @@ -6,16 +6,16 @@ when defined(GC_setMaxPause): GC_setMaxPause 2_000 type - TTestObj = object of TObject + TTestObj = object of RootObj x: string -proc MakeObj(): TTestObj = +proc makeObj(): TTestObj = result.x = "Hello" for i in 1 .. 1_000_000: when defined(gcMarkAndSweep) or defined(boehmgc): GC_fullcollect() - var obj = MakeObj() + var obj = makeObj() if getOccupiedMem() > 300_000: quit("still a leak!") # echo GC_getstatistics()