summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/btrees.nim2
-rw-r--r--compiler/ccgtypes.nim11
-rw-r--r--compiler/dfa.nim2
-rw-r--r--compiler/liftdestructors.nim14
-rw-r--r--compiler/semdata.nim4
-rw-r--r--compiler/types.nim6
-rw-r--r--compiler/vm.nim2
-rw-r--r--compiler/vmdef.nim2
-rw-r--r--lib/std/packedsets.nim2
-rw-r--r--lib/system.nim1
-rw-r--r--lib/system/orc.nim31
11 files changed, 50 insertions, 27 deletions
diff --git a/compiler/btrees.nim b/compiler/btrees.nim
index 5f78c07fe..d5e7e8099 100644
--- a/compiler/btrees.nim
+++ b/compiler/btrees.nim
@@ -16,7 +16,7 @@ const
   Mhalf = M div 2
 
 type
-  Node[Key, Val] = ref object
+  Node[Key, Val] {.acyclic.} = ref object
     entries: int
     keys: array[M, Key]
     case isInternal: bool
diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim
index 767525fd5..6e5ffeaa4 100644
--- a/compiler/ccgtypes.nim
+++ b/compiler/ccgtypes.nim
@@ -1301,6 +1301,10 @@ proc genHook(m: BModule; t: PType; info: TLineInfo; op: TTypeAttachedOp): Rope =
 
     genProc(m, theProc)
     result = theProc.loc.r
+
+    when false:
+      if not canFormAcycle(t) and op == attachedTrace:
+        echo "ayclic but has this =trace ", t, " ", theProc.ast
   else:
     when false:
       if op == attachedTrace and m.config.selectedGC == gcOrc and
@@ -1326,9 +1330,12 @@ proc genTypeInfoV2Impl(m: BModule, t, origType: PType, name: Rope; info: TLineIn
   let traceImpl = genHook(m, t, info, attachedTrace)
   let disposeImpl = genHook(m, t, info, attachedDispose)
 
-  addf(m.s[cfsTypeInit3], "$1.destructor = (void*)$2; $1.size = sizeof($3); $1.align = NIM_ALIGNOF($3); $1.name = $4;$n; $1.traceImpl = (void*)$5; $1.disposeImpl = (void*)$6;", [
+  var flags = 0
+  if not canFormAcycle(t): flags = flags or 1
+
+  addf(m.s[cfsTypeInit3], "$1.destructor = (void*)$2; $1.size = sizeof($3); $1.align = NIM_ALIGNOF($3); $1.name = $4;$n; $1.traceImpl = (void*)$5; $1.disposeImpl = (void*)$6; $1.flags = $7;", [
     name, destroyImpl, getTypeDesc(m, t), typeName,
-    traceImpl, disposeImpl])
+    traceImpl, disposeImpl, rope(flags)])
 
   if t.kind == tyObject and t.len > 0 and t[0] != nil and optEnableDeepCopy in m.config.globalOptions:
     discard genTypeInfoV1(m, t, info)
diff --git a/compiler/dfa.nim b/compiler/dfa.nim
index 501d39f45..ea60dbc59 100644
--- a/compiler/dfa.nim
+++ b/compiler/dfa.nim
@@ -467,7 +467,7 @@ proc genCase(c: var Con; n: PNode) =
       # treat the last branch as 'else' if this is an exhaustive case statement.
       c.gen(it.lastSon)
       if endings.len != 0:
-          c.patch(endings[^1])
+        c.patch(endings[^1])
     else:
       forkT(it.lastSon):
         c.gen(it.lastSon)
diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim
index de45beb52..43f87e179 100644
--- a/compiler/liftdestructors.nim
+++ b/compiler/liftdestructors.nim
@@ -513,8 +513,9 @@ proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
     forallElements(c, t, body, x, y)
     body.add genBuiltin(c, mDestroy, "destroy", x)
   of attachedTrace:
-    # follow all elements:
-    forallElements(c, t, body, x, y)
+    if canFormAcycle(t.elemType):
+      # follow all elements:
+      forallElements(c, t, body, x, y)
   of attachedDispose:
     forallElements(c, t, body, x, y)
     body.add genBuiltin(c, mDestroy, "destroy", x)
@@ -550,10 +551,11 @@ proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
     doAssert t.destructor != nil
     body.add destructorCall(c, t.destructor, x)
   of attachedTrace:
-    let op = getAttachedOp(c.g, t, c.kind)
-    if op == nil:
-      return # protect from recursion
-    body.add newHookCall(c, op, x, y)
+    if t.kind != tyString and canFormAcycle(t.elemType):
+      let op = getAttachedOp(c.g, t, c.kind)
+      if op == nil:
+        return # protect from recursion
+      body.add newHookCall(c, op, x, y)
   of attachedDispose:
     let op = getAttachedOp(c.g, t, c.kind)
     if op == nil:
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index d26e57320..f763a00e4 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -29,8 +29,8 @@ type
 
   POptionEntry* = ref TOptionEntry
   PProcCon* = ref TProcCon
-  TProcCon* = object          # procedure context; also used for top-level
-                              # statements
+  TProcCon* {.acyclic.} = object # procedure context; also used for top-level
+                                 # statements
     owner*: PSym              # the symbol this context belongs to
     resultSym*: PSym          # the result symbol (if we are in a proc)
     selfSym*: PSym            # the 'self' symbol (if available)
diff --git a/compiler/types.nim b/compiler/types.nim
index cd67d1ad9..bc970a6d2 100644
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -359,13 +359,13 @@ proc canFormAcycleAux(marker: var IntSet, typ: PType, startId: int): bool =
   if tfAcyclic in t.flags: return
   case t.kind
   of tyTuple, tyObject, tyRef, tySequence, tyArray, tyOpenArray, tyVarargs:
-    if not containsOrIncl(marker, t.id):
+    if t.id == startId:
+      result = true
+    elif not containsOrIncl(marker, t.id):
       for i in 0..<t.len:
         result = canFormAcycleAux(marker, t[i], startId)
         if result: return
       if t.n != nil: result = canFormAcycleNode(marker, t.n, startId)
-    else:
-      result = t.id == startId
     # Inheritance can introduce cyclic types, however this is not relevant
     # as the type that is passed to 'new' is statically known!
     # er but we use it also for the write barrier ...
diff --git a/compiler/vm.nim b/compiler/vm.nim
index e13fc5bb7..b540ac071 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -524,7 +524,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
   # Used to keep track of where the execution is resumed.
   var savedPC = -1
   var savedFrame: PStackFrame
-  when defined(gcArc):
+  when defined(gcArc) or defined(gcOrc):
     template updateRegsAlias = discard
     template regs: untyped = tos.slots
   else:
diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim
index 5c1d30a52..2044b860a 100644
--- a/compiler/vmdef.nim
+++ b/compiler/vmdef.nim
@@ -270,7 +270,7 @@ type
     procToCodePos*: Table[int, int]
 
   PStackFrame* = ref TStackFrame
-  TStackFrame* = object
+  TStackFrame* {.acyclic.} = object
     prc*: PSym                 # current prc; proc that is evaluated
     slots*: seq[TFullReg]      # parameters passed to the proc + locals;
                               # parameters come first
diff --git a/lib/std/packedsets.nim b/lib/std/packedsets.nim
index 2b5896be0..e209d2aae 100644
--- a/lib/std/packedsets.nim
+++ b/lib/std/packedsets.nim
@@ -38,7 +38,7 @@ const
   IntMask = 1 shl IntShift - 1
 
 type
-  Trunk = ref object
+  Trunk {.acyclic.} = ref object
     next: Trunk                                 # all nodes are connected with this pointer
     key: int                                    # start address at bit 0
     bits: array[0..IntsPerTrunk - 1, BitScalar] # a bit vector
diff --git a/lib/system.nim b/lib/system.nim
index aecda4a70..9317c3440 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -1805,6 +1805,7 @@ when not defined(js) and defined(nimV2):
       traceImpl: pointer
       disposeImpl: pointer
       typeInfoV1: pointer # for backwards compat, usually nil
+      flags: int
     PNimTypeV2 = ptr TNimTypeV2
 
 when notJSnotNims and defined(nimSeqsV2):
diff --git a/lib/system/orc.nim b/lib/system/orc.nim
index 8a62ef4bb..df81afcf1 100644
--- a/lib/system/orc.nim
+++ b/lib/system/orc.nim
@@ -109,9 +109,21 @@ proc free(s: Cell; desc: PNimTypeV2) {.inline.} =
 
   nimRawDispose(p, desc.align)
 
+template orcAssert(cond, msg) =
+  when logOrc:
+    if not cond:
+      cfprintf(cstderr, "[Bug!] %s\n", msg)
+      quit 1
+
+when logOrc:
+  proc strstr(s, sub: cstring): cstring {.header: "<string.h>", importc.}
+
 proc nimTraceRef(q: pointer; desc: PNimTypeV2; env: pointer) {.compilerRtl, inline.} =
   let p = cast[ptr pointer](q)
   if p[] != nil:
+
+    orcAssert strstr(desc.name, "TType") == nil, "following a TType but it's acyclic!"
+
     var j = cast[ptr GcEnv](env)
     j.traceStack.add(head p[], desc)
 
@@ -121,12 +133,6 @@ proc nimTraceRefDyn(q: pointer; env: pointer) {.compilerRtl, inline.} =
     var j = cast[ptr GcEnv](env)
     j.traceStack.add(head p[], cast[ptr PNimTypeV2](p[])[])
 
-template orcAssert(cond, msg) =
-  when logOrc:
-    if not cond:
-      cfprintf(cstderr, "[Bug!] %s\n", msg)
-      quit 1
-
 var
   roots {.threadvar.}: CellSeq
 
@@ -402,6 +408,8 @@ proc registerCycle(s: Cell; desc: PNimTypeV2) =
   when logOrc:
     writeCell("[added root]", s, desc)
 
+  orcAssert strstr(desc.name, "TType") == nil, "added a TType as a root!"
+
 proc GC_runOrc* =
   ## Forces a cycle collection pass.
   collectCycles()
@@ -436,10 +444,15 @@ proc GC_disableMarkAndSweep*() =
   ## For `--gc:orc` an alias for `GC_disableOrc`.
   GC_disableOrc()
 
+const
+  acyclicFlag = 1 # see also cggtypes.nim, proc genTypeInfoV2Impl
+
 when optimizedOrc:
-  template markedAsCyclic(s: Cell): bool = (s.rc and maybeCycle) != 0
+  template markedAsCyclic(s: Cell; desc: PNimTypeV2): bool =
+    (desc.flags and acyclicFlag) == 0 and (s.rc and maybeCycle) != 0
 else:
-  template markedAsCyclic(s: Cell): bool = true
+  template markedAsCyclic(s: Cell; desc: PNimTypeV2): bool =
+    (desc.flags and acyclicFlag) == 0
 
 proc rememberCycle(isDestroyAction: bool; s: Cell; desc: PNimTypeV2) {.noinline.} =
   if isDestroyAction:
@@ -448,7 +461,7 @@ proc rememberCycle(isDestroyAction: bool; s: Cell; desc: PNimTypeV2) {.noinline.
   else:
     # do not call 'rememberCycle' again unless this cell
     # got an 'incRef' event:
-    if s.rootIdx == 0 and markedAsCyclic(s):
+    if s.rootIdx == 0 and markedAsCyclic(s, desc):
       s.setColor colBlack
       registerCycle(s, desc)