summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/injectdestructors.nim21
-rw-r--r--tests/destructor/tdangingref_simple.nim32
2 files changed, 50 insertions, 3 deletions
diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim
index 0ea04c638..c2d135e86 100644
--- a/compiler/injectdestructors.nim
+++ b/compiler/injectdestructors.nim
@@ -338,10 +338,23 @@ proc genOp(c: Con; t: PType; kind: TTypeAttachedOp; dest, ri: PNode): PNode =
   addrExp.add(dest)
   result = newTree(nkCall, newSymNode(op), addrExp)
 
+when false:
+  proc preventMoveRef(dest, ri: PNode): bool =
+    let lhs = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink})
+    var ri = ri
+    if ri.kind in nkCallKinds and ri[0].kind == nkSym and ri[0].sym.magic == mUnown:
+      ri = ri[1]
+    let rhs = ri.typ.skipTypes({tyGenericInst, tyAlias, tySink})
+    result = lhs.kind == tyRef and rhs.kind == tyOwned
+
+proc canBeMoved(t: PType): bool {.inline.} =
+  let t = t.skipTypes({tyGenericInst, tyAlias, tySink})
+  result = t.kind != tyRef and t.attachedOps[attachedSink] != nil
+
 proc genSink(c: Con; t: PType; dest, ri: PNode): PNode =
   let t = t.skipTypes({tyGenericInst, tyAlias, tySink})
   let k = if t.attachedOps[attachedSink] != nil: attachedSink
-           else: attachedAsgn
+          else: attachedAsgn
   if t.attachedOps[k] != nil:
     result = genOp(c, t, k, dest, ri)
   else:
@@ -659,7 +672,8 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
       var snk = genSink(c, dest.typ, dest, ri)
       snk.add ri
       result = newTree(nkStmtList, snk, genWasMoved(ri, c))
-    elif ri.sym.kind != skParam and ri.sym.owner == c.owner and isLastRead(ri, c):
+    elif ri.sym.kind != skParam and ri.sym.owner == c.owner and
+        isLastRead(ri, c) and canBeMoved(dest.typ):
       # Rule 3: `=sink`(x, z); wasMoved(z)
       var snk = genSink(c, dest.typ, dest, ri)
       snk.add ri
@@ -691,7 +705,8 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
       result = genCopy(c, dest.typ, dest, ri)
       result.add p(ri, c)
   else:
-    if isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c):
+    if isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c) and
+        canBeMoved(dest.typ):
       # Rule 3: `=sink`(x, z); wasMoved(z)
       var snk = genSink(c, dest.typ, dest, ri)
       snk.add ri
diff --git a/tests/destructor/tdangingref_simple.nim b/tests/destructor/tdangingref_simple.nim
new file mode 100644
index 000000000..279581b0f
--- /dev/null
+++ b/tests/destructor/tdangingref_simple.nim
@@ -0,0 +1,32 @@
+discard """
+  output: '''a
+[FATAL] dangling references exist
+'''
+  exitCode: 1
+  cmd: "nim c --newruntime $file"
+"""
+
+# bug #11350
+
+type
+  Node = ref object
+    data: int
+
+proc use(x: Node) = discard
+
+proc main =
+  var x = Node(data: 3) # inferred to be an ``owned ref``
+  var dangling = unown x
+  assert dangling.data == 3
+  #use x
+  #dangling = nil
+  # reassignment causes the memory of what ``x`` points to to be freed:
+  echo "a"
+  x = Node(data: 4)
+  echo "b"
+  # accessing 'dangling' here is invalid as it is nil.
+  # at scope exit the memory of what ``x`` points to is freed
+  if dangling != nil:
+    echo dangling.data
+
+main()