summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAndreas Rumpf <rumpf_a@web.de>2021-07-20 22:15:06 +0200
committerGitHub <noreply@github.com>2021-07-20 22:15:06 +0200
commitf8519657c43f458db9c915cec62c59022041eb05 (patch)
treebaad0a54d733312a55e495d877c3847f928a02e4
parentcf0cf32d276002e850a87667fff62c4df12999d6 (diff)
downloadNim-f8519657c43f458db9c915cec62c59022041eb05.tar.gz
fixes #18469 (#18544)
* fixes #18469

* Update compiler/injectdestructors.nim
-rw-r--r--compiler/injectdestructors.nim25
-rw-r--r--compiler/jsgen.nim10
-rw-r--r--compiler/types.nim10
-rw-r--r--tests/arc/tcursor_field_obj_constr.nim44
4 files changed, 74 insertions, 15 deletions
diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim
index 6e4eaa817..cf09b3ff5 100644
--- a/compiler/injectdestructors.nim
+++ b/compiler/injectdestructors.nim
@@ -774,7 +774,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode =
       result = passCopyToSink(n, c, s)
   else:
     case n.kind
-    of nkBracket, nkObjConstr, nkTupleConstr, nkClosure, nkCurly:
+    of nkBracket, nkTupleConstr, nkClosure, nkCurly:
       # Let C(x) be the construction, 'x' the vector of arguments.
       # C(x) either owns 'x' or it doesn't.
       # If C(x) owns its data, we must consume C(x).
@@ -785,13 +785,11 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode =
       # don't destroy it"
       # but if C(x) is a ref it MUST own its data since we must destroy it
       # so then we have no choice but to use 'sinkArg'.
-      let isRefConstr = n.kind == nkObjConstr and n.typ.skipTypes(abstractInst).kind == tyRef
-      let m = if isRefConstr: sinkArg
-              elif mode == normal: normal
+      let m = if mode == normal: normal
               else: sinkArg
 
       result = copyTree(n)
-      for i in ord(n.kind in {nkObjConstr, nkClosure})..<n.len:
+      for i in ord(n.kind == nkClosure)..<n.len:
         if n[i].kind == nkExprColonExpr:
           result[i][1] = p(n[i][1], c, s, m)
         elif n[i].kind == nkRange:
@@ -799,6 +797,23 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode =
           result[i][1] = p(n[i][1], c, s, m)
         else:
           result[i] = p(n[i], c, s, m)
+    of nkObjConstr:
+      # see also the remark about `nkTupleConstr`.
+      let isRefConstr = n.typ.skipTypes(abstractInst).kind == tyRef
+      let m = if isRefConstr: sinkArg
+              elif mode == normal: normal
+              else: sinkArg
+
+      result = copyTree(n)
+      for i in 1..<n.len:
+        if n[i].kind == nkExprColonExpr:
+          let field = lookupFieldAgain(n.typ, n[i][0].sym)
+          if field != nil and sfCursor in field.flags:
+            result[i][1] = p(n[i][1], c, s, normal)
+          else:
+            result[i][1] = p(n[i][1], c, s, m)
+        else:
+          result[i] = p(n[i], c, s, m)
       if mode == normal and isRefConstr:
         result = ensureDestruction(result, n, c, s)
     of nkCallKinds:
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index 4b89b0cde..85947535e 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -2229,16 +2229,6 @@ proc genTupleConstr(p: PProc, n: PNode, r: var TCompRes) =
       r.res.addf("Field$#: $#", [i.rope, a.res])
   r.res.add("}")
 
-proc lookupFieldAgain(ty: PType; field: PSym): PSym =
-  var ty = ty
-  while ty != nil:
-    ty = ty.skipTypes(skipPtrs)
-    assert(ty.kind in {tyTuple, tyObject})
-    result = lookupInRecord(ty.n, field.name)
-    if result != nil: break
-    ty = ty[0]
-  if result == nil: result = field
-
 proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) =
   var a: TCompRes
   r.kind = resExpr
diff --git a/compiler/types.nim b/compiler/types.nim
index 49ebb1dae..0a50af56e 100644
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -1656,3 +1656,13 @@ proc isSinkTypeForParam*(t: PType): bool =
         result = false
       else:
         result = true
+
+proc lookupFieldAgain*(ty: PType; field: PSym): PSym =
+  var ty = ty
+  while ty != nil:
+    ty = ty.skipTypes(skipPtrs)
+    assert(ty.kind in {tyTuple, tyObject})
+    result = lookupInRecord(ty.n, field.name)
+    if result != nil: break
+    ty = ty[0]
+  if result == nil: result = field
diff --git a/tests/arc/tcursor_field_obj_constr.nim b/tests/arc/tcursor_field_obj_constr.nim
new file mode 100644
index 000000000..b87f734bd
--- /dev/null
+++ b/tests/arc/tcursor_field_obj_constr.nim
@@ -0,0 +1,44 @@
+discard """
+  output: '''a
+b
+c'''
+  cmd: "nim c --gc:arc $file"
+"""
+
+# bug #18469
+
+type
+  Edge = object
+    neighbor {.cursor.}: Node
+
+  NodeObj = object
+    neighbors: seq[Edge]
+    label: string
+    visited: bool
+  Node = ref NodeObj
+
+  Graph = object
+    nodes: seq[Node]
+
+proc `=destroy`(x: var NodeObj) =
+  echo x.label
+  `=destroy`(x.neighbors)
+  `=destroy`(x.label)
+
+proc addNode(self: var Graph; label: string): Node =
+  self.nodes.add(Node(label: label))
+  result = self.nodes[^1]
+
+proc addEdge(self: Graph; source, neighbor: Node) =
+  source.neighbors.add(Edge(neighbor: neighbor))
+
+proc main =
+  var graph: Graph
+  let nodeA = graph.addNode("a")
+  let nodeB = graph.addNode("b")
+  let nodeC = graph.addNode("c")
+
+  graph.addEdge(nodeA, neighbor = nodeB)
+  graph.addEdge(nodeA, neighbor = nodeC)
+
+main()