summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/dfa.nim100
-rw-r--r--compiler/injectdestructors.nim4
-rw-r--r--tests/arc/tmovebug.nim31
3 files changed, 106 insertions, 29 deletions
diff --git a/compiler/dfa.nim b/compiler/dfa.nim
index efd826594..f3985822b 100644
--- a/compiler/dfa.nim
+++ b/compiler/dfa.nim
@@ -574,32 +574,84 @@ proc skipConvDfa*(n: PNode): PNode =
       result = result[1]
     else: break
 
-proc aliases*(obj, field: PNode): bool =
-  var n = field
-  var obj = obj
-  while true:
-    case obj.kind
-    of {nkObjDownConv, nkObjUpConv, nkAddr, nkHiddenAddr, nkDerefExpr, nkHiddenDeref}:
-      obj = obj[0]
-    of PathKinds1:
-      obj = obj[1]
-    else: break
-  while true:
-    if sameTrees(obj, n): return true
-    case n.kind
-    of PathKinds0:
-      n = n[0]
-    of PathKinds1:
-      n = n[1]
-    else: break
+type AliasKind* = enum
+  yes, no, maybe
+
+proc aliases*(obj, field: PNode): AliasKind =
+  # obj -> field:
+  # x -> x: true
+  # x -> x.f: true
+  # x.f -> x: false
+  # x.f -> x.f: true
+  # x.f -> x.v: false
+  # x -> x[0]: true
+  # x[0] -> x: false
+  # x[0] -> x[0]: true
+  # x[0] -> x[1]: false
+  # x -> x[i]: true
+  # x[i] -> x: false
+  # x[i] -> x[i]: maybe; Further analysis could make this return true when i is a runtime-constant
+  # x[i] -> x[j]: maybe; also returns maybe if only one of i or j is a compiletime-constant
+  template collectImportantNodes(result, n) =
+    var result: seq[PNode]
+    var n = n
+    while true:
+      case n.kind
+      of PathKinds0 - {nkDotExpr, nkCheckedFieldExpr, nkBracketExpr}:
+        n = n[0]
+      of PathKinds1:
+        n = n[1]
+      of nkDotExpr, nkCheckedFieldExpr, nkBracketExpr:
+        result.add n
+        n = n[0]
+      of nkSym:
+        result.add n; break
+      else: return no
+
+  collectImportantNodes(objImportantNodes, obj)
+  collectImportantNodes(fieldImportantNodes, field)
+
+  # If field is less nested than obj, then it cannot be part of/aliased by obj
+  if fieldImportantNodes.len < objImportantNodes.len: return no
+
+  result = yes
+  for i in 1..objImportantNodes.len:
+    # We compare the nodes leading to the location of obj and field
+    # with each other.
+    # We continue until they diverge, in which case we return no, or
+    # until we reach the location of obj, in which case we do not need
+    # to look further, since field must be part of/aliased by obj now.
+    # If we encounter an element access using an index which is a runtime value,
+    # we simply return maybe instead of yes; should further nodes not diverge.
+    let currFieldPath = fieldImportantNodes[^i]
+    let currObjPath = objImportantNodes[^i]
+
+    if currFieldPath.kind != currObjPath.kind:
+      return no
+
+    case currFieldPath.kind
+    of nkSym:
+      if currFieldPath.sym != currObjPath.sym: return no
+    of nkDotExpr, nkCheckedFieldExpr:
+      if currFieldPath[1].sym != currObjPath[1].sym: return no
+    of nkBracketExpr:
+      if currFieldPath[1].kind in nkLiterals and currObjPath[1].kind in nkLiterals:
+        if currFieldPath[1].intVal != currObjPath[1].intVal:
+          return no
+      else:
+        result = maybe
+    else: assert false # unreachable
 
 type InstrTargetKind* = enum
   None, Full, Partial
 
 proc instrTargets*(insloc, loc: PNode): InstrTargetKind =
-  if sameTrees(insloc, loc) or insloc.aliases(loc):
+  case insloc.aliases(loc)
+  of yes:
     Full    # x -> x; x -> x.f
-  elif loc.aliases(insloc):
+  of maybe:
+    Partial # We treat this like a partial write/read
+  elif loc.aliases(insloc) != no:
     Partial # x.f -> x
   else: None
 
@@ -607,16 +659,10 @@ proc isAnalysableFieldAccess*(orig: PNode; owner: PSym): bool =
   var n = orig
   while true:
     case n.kind
-    of PathKinds0 - {nkBracketExpr, nkHiddenDeref, nkDerefExpr}:
+    of PathKinds0 - {nkHiddenDeref, nkDerefExpr}:
       n = n[0]
     of PathKinds1:
       n = n[1]
-    of nkBracketExpr:
-      # in a[i] the 'i' must be known
-      if n.len > 1 and n[1].kind in {nkCharLit..nkUInt64Lit}:
-        n = n[0]
-      else:
-        return false
     of nkHiddenDeref, nkDerefExpr:
       # We "own" sinkparam[].loc but not ourVar[].location as it is a nasty
       # pointer indirection.
diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim
index cb9547bf2..3069d742c 100644
--- a/compiler/injectdestructors.nim
+++ b/compiler/injectdestructors.nim
@@ -969,7 +969,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode =
 
 proc sameLocation*(a, b: PNode): bool =
   proc sameConstant(a, b: PNode): bool =
-    a.kind in nkLiterals and exprStructuralEquivalent(a, b)
+    a.kind in nkLiterals and a.intVal == b.intVal
 
   const nkEndPoint = {nkSym, nkDotExpr, nkCheckedFieldExpr, nkBracketExpr}
   if a.kind in nkEndPoint and b.kind in nkEndPoint:
@@ -1006,7 +1006,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, isDecl = false): PNod
         # unpacking of tuple: take over the elements
         result = c.genSink(dest, p(ri, c, s, consumed), isDecl)
       elif isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c):
-        if not aliases(dest, ri):
+        if aliases(dest, ri) == no:
           # Rule 3: `=sink`(x, z); wasMoved(z)
           var snk = c.genSink(dest, ri, isDecl)
           result = newTree(nkStmtList, snk, c.genWasMoved(ri))
diff --git a/tests/arc/tmovebug.nim b/tests/arc/tmovebug.nim
index 71962e21d..7c8fbc235 100644
--- a/tests/arc/tmovebug.nim
+++ b/tests/arc/tmovebug.nim
@@ -92,10 +92,20 @@ copy
 destroy
 destroy
 destroy
+sink
+sink
+destroy
+copy
+(f: 1)
+destroy
+destroy
 part-to-whole assigment:
 sink
 (children: @[])
 destroy
+sink
+(children: @[])
+destroy
 copy
 destroy
 '''
@@ -675,6 +685,16 @@ proc caseNotAConstant =
 
 caseNotAConstant()
 
+proc potentialSelfAssign(i: var int) =
+  var a: array[2, OO]
+  a[i] = OO(f: 1)
+  a[1] = OO(f: 2)
+  a[i+1] = a[i] # This must not =sink, but =copy
+  inc i
+  echo a[i-1] # (f: 1)
+
+potentialSelfAssign (var xi = 0; xi)
+
 
 #--------------------------------------------------------------------
 echo "part-to-whole assigment:"
@@ -700,6 +720,17 @@ proc partToWholeSeq =
 
 partToWholeSeq()
 
+proc partToWholeSeqRTIndex =
+  var i = 0
+  var t = Tree(children: @[Tree()])
+  t = t.children[i] # See comment in partToWholeSeq
+
+  var tc = TreeDefaultHooks(children: @[TreeDefaultHooks()])
+  tc = tc.children[i] # See comment in partToWholeSeq
+  echo tc
+
+partToWholeSeqRTIndex()
+
 type List = object
   next: ref List