summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/semmagic.nim72
-rw-r--r--tests/arc/t19401.nim32
-rw-r--r--tests/arc/t19402.nim32
3 files changed, 112 insertions, 24 deletions
diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim
index f7edc5592..48ea69648 100644
--- a/compiler/semmagic.nim
+++ b/compiler/semmagic.nim
@@ -449,6 +449,53 @@ proc semOld(c: PContext; n: PNode): PNode =
     localError(c.config, n[1].info, n[1].sym.name.s & " does not belong to " & getCurrOwner(c).name.s)
   result = n
 
+proc semNewFinalize(c: PContext; n: PNode): PNode =
+  # Make sure the finalizer procedure refers to a procedure
+  if n[^1].kind == nkSym and n[^1].sym.kind notin {skProc, skFunc}:
+    localError(c.config, n.info, "finalizer must be a direct reference to a proc")
+  elif optTinyRtti in c.config.globalOptions:
+    let nfin = skipConvCastAndClosure(n[^1])
+    let fin = case nfin.kind
+      of nkSym: nfin.sym
+      of nkLambda, nkDo: nfin[namePos].sym
+      else:
+        localError(c.config, n.info, "finalizer must be a direct reference to a proc")
+        nil
+    if fin != nil:
+      if fin.kind notin {skProc, skFunc}:
+        # calling convention is checked in codegen
+        localError(c.config, n.info, "finalizer must be a direct reference to a proc")
+
+      # check if we converted this finalizer into a destructor already:
+      let t = whereToBindTypeHook(c, fin.typ[1].skipTypes(abstractInst+{tyRef}))
+      if t != nil and getAttachedOp(c.graph, t, attachedDestructor) != nil and
+          getAttachedOp(c.graph, t, attachedDestructor).owner == fin:
+        discard "already turned this one into a finalizer"
+      else:
+        if sfForward in fin.flags:
+          let wrapperSym = newSym(skProc, getIdent(c.graph.cache, fin.name.s & "FinalizerWrapper"), nextSymId c.idgen, fin.owner, fin.info)
+          let selfSymNode = newSymNode(copySym(fin.ast[paramsPos][1][0].sym, nextSymId c.idgen))
+          wrapperSym.flags.incl sfUsed
+          let wrapper = c.semExpr(c, newProcNode(nkProcDef, fin.info, body = newTree(nkCall, newSymNode(fin), selfSymNode),
+            params = nkFormalParams.newTree(c.graph.emptyNode,
+                    newTree(nkIdentDefs, selfSymNode, fin.ast[paramsPos][1][1], c.graph.emptyNode)
+                    ),
+            name = newSymNode(wrapperSym), pattern = c.graph.emptyNode,
+            genericParams = c.graph.emptyNode, pragmas = c.graph.emptyNode, exceptions = c.graph.emptyNode), {})
+          var transFormedSym = turnFinalizerIntoDestructor(c, wrapperSym, wrapper.info)
+          transFormedSym.owner = fin
+          if c.config.backend == backendCpp or sfCompileToCpp in c.module.flags:
+            let origParamType = transFormedSym.ast[bodyPos][1].typ
+            let selfSymbolType = makePtrType(c, origParamType.skipTypes(abstractPtrs))
+            let selfPtr = newNodeI(nkHiddenAddr, transFormedSym.ast[bodyPos][1].info)
+            selfPtr.add transFormedSym.ast[bodyPos][1]
+            selfPtr.typ = selfSymbolType
+            transFormedSym.ast[bodyPos][1] = c.semExpr(c, selfPtr)
+          bindTypeHook(c, transFormedSym, n, attachedDestructor)
+        else:
+          bindTypeHook(c, turnFinalizerIntoDestructor(c, fin, n.info), n, attachedDestructor)
+  result = n
+
 proc semPrivateAccess(c: PContext, n: PNode): PNode =
   let t = n[1].typ[0].toObjectFromRefPtrGeneric
   c.currentScope.allowPrivateAccess.add t.sym
@@ -513,30 +560,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
     else:
       result = plugin(c, n)
   of mNewFinalize:
-    # Make sure the finalizer procedure refers to a procedure
-    if n[^1].kind == nkSym and n[^1].sym.kind notin {skProc, skFunc}:
-      localError(c.config, n.info, "finalizer must be a direct reference to a proc")
-    elif optTinyRtti in c.config.globalOptions:
-      let nfin = skipConvCastAndClosure(n[^1])
-      let fin = case nfin.kind
-        of nkSym: nfin.sym
-        of nkLambda, nkDo: nfin[namePos].sym
-        else:
-          localError(c.config, n.info, "finalizer must be a direct reference to a proc")
-          nil
-      if fin != nil:
-        if fin.kind notin {skProc, skFunc}:
-          # calling convention is checked in codegen
-          localError(c.config, n.info, "finalizer must be a direct reference to a proc")
-
-        # check if we converted this finalizer into a destructor already:
-        let t = whereToBindTypeHook(c, fin.typ[1].skipTypes(abstractInst+{tyRef}))
-        if t != nil and getAttachedOp(c.graph, t, attachedDestructor) != nil and
-            getAttachedOp(c.graph, t, attachedDestructor).owner == fin:
-          discard "already turned this one into a finalizer"
-        else:
-          bindTypeHook(c, turnFinalizerIntoDestructor(c, fin, n.info), n, attachedDestructor)
-    result = n
+    result = semNewFinalize(c, n)
   of mDestroy:
     result = n
     let t = n[1].typ.skipTypes(abstractVar)
diff --git a/tests/arc/t19401.nim b/tests/arc/t19401.nim
new file mode 100644
index 000000000..56702a4a2
--- /dev/null
+++ b/tests/arc/t19401.nim
@@ -0,0 +1,32 @@
+discard """
+  output: '''
+delete foo
+delete foo
+delete foo
+'''
+  matrix: "--mm:arc"
+"""
+
+type Foo = ref object
+  data: int
+proc delete(self: Foo) 
+proc newFoo: Foo =
+  let x = 12
+  discard x
+  new(result, delete)
+  result.data = x
+proc delete(self: Foo) =
+  doAssert self.data == 12
+  echo("delete foo")
+
+if isMainModule:
+  proc test() =
+    let x1 = newFoo()
+    let x2 = newFoo()
+    discard x1
+    discard x2
+    var x3: Foo
+    new(x3, delete)
+    x3.data = 12
+    discard x3
+  test()
diff --git a/tests/arc/t19402.nim b/tests/arc/t19402.nim
new file mode 100644
index 000000000..5ee6fc798
--- /dev/null
+++ b/tests/arc/t19402.nim
@@ -0,0 +1,32 @@
+discard """
+  output: '''
+delete foo
+delete foo
+delete foo
+'''
+  matrix: "--mm:arc"
+"""
+
+type Foo = ref object of RootObj
+  data: int
+proc delete(self: Foo)
+proc newFoo: Foo =
+  let x = 12
+  discard x
+  new(result, delete)
+  result.data = x
+proc delete(self: Foo) =
+  doAssert self.data == 12
+  echo("delete foo")
+
+if isMainModule:
+  proc test() =
+    let x1 = newFoo()
+    let x2 = newFoo()
+    discard x1
+    discard x2
+    var x3: Foo
+    new(x3, delete)
+    x3.data = 12
+    discard x3
+  test()
\ No newline at end of file