summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/dfa.nim38
-rw-r--r--tests/destructor/tuse_ownedref_after_move.nim60
2 files changed, 90 insertions, 8 deletions
diff --git a/compiler/dfa.nim b/compiler/dfa.nim
index cdb912163..436fd699f 100644
--- a/compiler/dfa.nim
+++ b/compiler/dfa.nim
@@ -32,7 +32,6 @@
 import ast, astalgo, types, intsets, tables, msgs, options, lineinfos, renderer
 
 from patterns import sameTrees
-from aliases import isPartOf, TAnalysisResult
 
 type
   InstrKind* = enum
@@ -570,7 +569,8 @@ const
   InterestingSyms = {skVar, skResult, skLet, skParam, skForVar, skTemp}
   PathKinds* = {nkDotExpr, nkCheckedFieldExpr,
                 nkBracketExpr, nkDerefExpr, nkHiddenDeref,
-                nkAddr, nkHiddenAddr}
+                nkAddr, nkHiddenAddr,
+                nkHiddenStdConv, nkHiddenSubConv, nkObjDownConv, nkObjUpConv}
 
 proc genUse(c: var Con; orig: PNode) =
   var n = orig
@@ -581,6 +581,27 @@ proc genUse(c: var Con; orig: PNode) =
   if n.kind == nkSym and n.sym.kind in InterestingSyms:
     c.code.add Instr(n: orig, kind: use, sym: if iters > 0: nil else: n.sym)
 
+proc aliases(obj, field: PNode): bool =
+  var n = field
+  var obj = obj
+  while obj.kind in {nkHiddenSubConv, nkHiddenStdConv, nkObjDownConv, nkObjUpConv}:
+    obj = obj[0]
+  while true:
+    if sameTrees(obj, n): return true
+    case n.kind
+    of nkDotExpr, nkCheckedFieldExpr, nkHiddenSubConv, nkHiddenStdConv,
+       nkObjDownConv, nkObjUpConv, nkHiddenDeref:
+      n = n[0]
+    of nkBracketExpr:
+      let x = n[0]
+      if x.typ != nil and x.typ.skipTypes(abstractInst).kind == tyTuple:
+        n = x
+      else:
+        break
+    else:
+      break
+  return false
+
 proc instrTargets*(ins: Instr; loc: PNode): bool =
   assert ins.kind in {def, use}
   if ins.sym != nil and loc.kind == nkSym:
@@ -593,14 +614,16 @@ proc instrTargets*(ins: Instr; loc: PNode): bool =
     # def x; question: does it affect the 'x.f'? Yes.
     # use x.f;  question: does it affect the full 'x'? No.
     # use x; question does it affect 'x.f'? Yes.
-    result = isPartOf(ins.n, loc) == arYes
+    result = aliases(ins.n, loc) or aliases(loc, ins.n)
 
 proc isAnalysableFieldAccess*(n: PNode; owner: PSym): bool =
   var n = n
   while true:
-    if n.kind in {nkDotExpr, nkCheckedFieldExpr, nkHiddenSubConv, nkHiddenStdConv, nkObjDownConv, nkObjUpConv}:
+    case n.kind
+    of nkDotExpr, nkCheckedFieldExpr, nkHiddenSubConv, nkHiddenStdConv,
+       nkObjDownConv, nkObjUpConv, nkHiddenDeref:
       n = n[0]
-    elif n.kind == nkBracketExpr:
+    of nkBracketExpr:
       let x = n[0]
       if x.typ != nil and x.typ.skipTypes(abstractInst).kind == tyTuple:
         n = x
@@ -710,10 +733,9 @@ proc gen(c: var Con; n: PNode) =
     for x in n: gen(c, x)
   of nkPragmaBlock: gen(c, n.lastSon)
   of nkDiscardStmt: gen(c, n.sons[0])
-  of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkExprColonExpr, nkExprEqExpr,
-     nkCast:
+  of nkConv, nkExprColonExpr, nkExprEqExpr, nkCast:
     gen(c, n.sons[1])
-  of nkObjDownConv, nkStringToCString, nkCStringToString: gen(c, n.sons[0])
+  of nkStringToCString, nkCStringToString: gen(c, n.sons[0])
   of nkVarSection, nkLetSection: genVarSection(c, n)
   of nkDefer:
     doAssert false, "dfa construction pass requires the elimination of 'defer'"
diff --git a/tests/destructor/tuse_ownedref_after_move.nim b/tests/destructor/tuse_ownedref_after_move.nim
new file mode 100644
index 000000000..148696ee2
--- /dev/null
+++ b/tests/destructor/tuse_ownedref_after_move.nim
@@ -0,0 +1,60 @@
+discard """
+  cmd: '''nim c --newruntime $file'''
+  errormsg: "'=' is not available for type <owned Widget>; requires a copy because it's not the last read of ':env.b1()'; another read is done here: tuse_ownedref_after_move.nim(53, 4)"
+  line: 49
+"""
+
+import core / allocators
+import system / ansi_c
+
+type
+  Widget* = ref object of RootObj
+    drawImpl: owned(proc (self: Widget))
+
+  Button* = ref object of Widget
+    caption: string
+    onclick: owned(proc())
+
+  Window* = ref object of Widget
+    elements: seq[owned Widget]
+
+
+proc newButton(caption: string; onclick: owned(proc())): owned Button =
+  proc draw(self: Widget) =
+    let b = Button(self)
+    echo b.caption
+
+  result = Button(drawImpl: draw, caption: caption, onclick: onclick)
+
+proc newWindow(): owned Window =
+  proc draw(self: Widget) =
+    let w = Window(self)
+    for e in w.elements:
+      if not e.drawImpl.isNil: e.drawImpl(e)
+
+  result = Window(drawImpl: draw, elements: @[])
+
+proc draw(w: Widget) =
+  if not w.drawImpl.isNil: w.drawImpl(w)
+
+proc add*(w: Window; elem: owned Widget) =
+  w.elements.add elem
+
+proc main =
+  var w = newWindow()
+
+  var b = newButton("button", nil)
+  b.onclick = proc () =
+    b.caption = "clicked!"
+  w.add b
+
+  w.draw()
+  # simulate button click:
+  b.onclick()
+
+  w.draw()
+
+main()
+
+let (a, d) = allocCounters()
+discard cprintf("%ld %ld  new: %ld\n", a, d, allocs)