summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
authorAraq <rumpf_a@web.de>2019-11-28 09:32:14 +0100
committerAndreas Rumpf <rumpf_a@web.de>2019-11-28 23:00:34 +0100
commit2dea9203791996f1c946c8f4708dc8ca5342180b (patch)
treea611a979b3b7f323fb1713a117a9afdda16b4f19 /compiler
parent7e747d11c66405f08cc7c69e5afc18348663275e (diff)
downloadNim-2dea9203791996f1c946c8f4708dc8ca5342180b.tar.gz
ARC: implemented a simple cycle detector
Diffstat (limited to 'compiler')
-rw-r--r--compiler/injectdestructors.nim33
-rw-r--r--compiler/lambdalifting.nim10
-rw-r--r--compiler/lineinfos.nim5
3 files changed, 42 insertions, 6 deletions
diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim
index 8c7170577..7fc2ccd78 100644
--- a/compiler/injectdestructors.nim
+++ b/compiler/injectdestructors.nim
@@ -19,6 +19,8 @@ import
   strutils, options, dfa, lowerings, tables, modulegraphs, msgs,
   lineinfos, parampatterns, sighashes
 
+from trees import exprStructuralEquivalent
+
 type
   Con = object
     owner: PSym
@@ -461,6 +463,35 @@ proc isCursor(n: PNode): bool =
   else:
     result = false
 
+proc cycleCheck(n: PNode; c: var Con) =
+  if c.graph.config.selectedGC != gcDestructors: return
+  var value = n[1]
+  if value.kind == nkClosure:
+    value = value[1]
+  if value.kind == nkNilLit: return
+  let destTyp = n[0].typ.skipTypes(abstractInst)
+  if destTyp.kind != tyRef and not (destTyp.kind == tyProc and destTyp.callConv == ccClosure):
+    return
+
+  var x = n[0]
+  var field: PNode = nil
+  while true:
+    if x.kind == nkDotExpr:
+      if field == nil: field = x[1]
+      x = x[0]
+    elif x.kind in {nkBracketExpr, nkCheckedFieldExpr, nkDerefExpr, nkHiddenDeref}:
+      x = x[0]
+    else:
+      break
+    if exprStructuralEquivalent(x, value, strictSymEquality = true):
+      let msg =
+        if field != nil:
+          "'$#' creates an uncollectable ref cycle; annotate '$#' with .cursor" % [$n, $field]
+        else:
+          "'$#' creates an uncollectable ref cycle" % [$n]
+      message(c.graph.config, n.info, warnCycleCreated, msg)
+      break
+
 proc p(n: PNode; c: var Con; consumed = false): PNode =
   case n.kind
   of nkCallKinds:
@@ -544,6 +575,8 @@ proc p(n: PNode; c: var Con; consumed = false): PNode =
       if n[1].kind == nkSym and n[0].kind == nkSym and n[0].sym == n[1].sym:
         result = newNodeI(nkEmpty, n.info)
       else:
+        if n[0].kind in {nkDotExpr, nkCheckedFieldExpr}:
+          cycleCheck(n, c)
         result = moveOrCopy(n[0], n[1], c)
     else:
       result = copyNode(n)
diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim
index 336d6c314..ea19097b8 100644
--- a/compiler/lambdalifting.nim
+++ b/compiler/lambdalifting.nim
@@ -366,13 +366,15 @@ proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) =
     if upField.typ.skipTypes({tyOwned, tyRef, tyPtr}) != fieldType.skipTypes({tyOwned, tyRef, tyPtr}):
       localError(c.graph.config, dep.info, "internal error: up references do not agree")
 
-    if c.graph.config.selectedGC == gcDestructors and sfCursor notin upField.flags:
-      localError(c.graph.config, dep.info, "internal error: up reference is not a .cursor")
+    when false:
+      if c.graph.config.selectedGC == gcDestructors and sfCursor notin upField.flags:
+        localError(c.graph.config, dep.info, "internal error: up reference is not a .cursor")
   else:
     let result = newSym(skField, upIdent, obj.owner, obj.owner.info)
     result.typ = fieldType
-    if c.graph.config.selectedGC == gcDestructors:
-      result.flags.incl sfCursor
+    when false:
+      if c.graph.config.selectedGC == gcDestructors:
+        result.flags.incl sfCursor
     rawAddField(obj, result)
 
 discard """
diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim
index d315f4d33..479ed2f75 100644
--- a/compiler/lineinfos.nim
+++ b/compiler/lineinfos.nim
@@ -37,7 +37,7 @@ type
     warnEachIdentIsTuple,
     warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2,
     warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed,
-    warnInconsistentSpacing, warnCaseTransition, warnUser,
+    warnInconsistentSpacing, warnCaseTransition, warnCycleCreated, warnUser,
     hintSuccess, hintSuccessX, hintCC,
     hintLineTooLong, hintXDeclaredButNotUsed,
     hintConvToBaseNotNeeded,
@@ -94,6 +94,7 @@ const
     warnResultShadowed: "Special variable 'result' is shadowed.",
     warnInconsistentSpacing: "Number of spaces around '$#' is not consistent",
     warnCaseTransition: "Potential object case transition, instantiate new object instead",
+    warnCycleCreated: "$1",
     warnUser: "$1",
     hintSuccess: "operation successful: $#",
     hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#; $#)",
@@ -139,7 +140,7 @@ const
     "UnsafeCode", "UnusedImport", "EachIdentIsTuple",
     "ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit",
     "GcMem", "Destructor", "LockLevel", "ResultShadowed",
-    "Spacing", "CaseTransition", "User"]
+    "Spacing", "CaseTransition", "CycleCreated", "User"]
 
   HintsToStr* = [
     "Success", "SuccessX", "CC", "LineTooLong",