summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/ast.nim9
-rw-r--r--compiler/astalgo.nim36
-rw-r--r--compiler/cgen.nim5
-rw-r--r--compiler/dfa.nim2
-rw-r--r--compiler/docgen.nim7
-rw-r--r--compiler/jsgen.nim27
-rw-r--r--compiler/msgs.nim15
-rw-r--r--compiler/options.nim4
-rw-r--r--compiler/pragmas.nim2
-rw-r--r--compiler/ropes.nim26
-rw-r--r--compiler/sempass2.nim22
-rw-r--r--compiler/semtypes.nim4
-rw-r--r--compiler/semtypinst.nim6
-rw-r--r--compiler/transf.nim3
-rw-r--r--compiler/types.nim25
-rw-r--r--doc/manual.rst37
-rw-r--r--lib/packages/docutils/rst.nim4
-rw-r--r--tests/destructor/tdiscard.nim1
-rw-r--r--tests/destructor/tmove_objconstr.nim1
-rw-r--r--tests/destructor/tuse_result_prevents_sinks.nim1
-rw-r--r--tests/effects/tgcsafe3.nim21
-rw-r--r--tests/js/tbasics.nim14
22 files changed, 161 insertions, 111 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index b325e6d42..9ce102c2f 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -479,7 +479,7 @@ type
     tfNoSideEffect,   # procedure type does not allow side effects
     tfFinal,          # is the object final?
     tfInheritable,    # is the object inheritable?
-    tfAcyclic,        # type is acyclic (for GC optimization)
+    tfHasOwned,       # type contains an 'owned' type and must be moved
     tfEnumHasHoles,   # enum cannot be mapped into a range
     tfShallow,        # type can be shallow copied on assignment
     tfThread,         # proc type is marked as ``thread``; alias for ``gcsafe``
@@ -1472,6 +1472,13 @@ proc propagateToOwner*(owner, elem: PType) =
       o2.flags.incl tfHasAsgn
       owner.flags.incl tfHasAsgn
 
+  if tfHasOwned in elem.flags:
+    let o2 = owner.skipTypes({tyGenericInst, tyAlias, tySink})
+    if o2.kind in {tyTuple, tyObject, tyArray,
+                   tySequence, tyOpt, tySet, tyDistinct}:
+      o2.flags.incl tfHasOwned
+      owner.flags.incl tfHasOwned
+
   if owner.kind notin {tyProc, tyGenericInst, tyGenericBody,
                        tyGenericInvocation, tyPtr}:
     let elemB = elem.skipTypes({tyGenericInst, tyAlias, tySink})
diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim
index 8187341b3..be8d23b90 100644
--- a/compiler/astalgo.nim
+++ b/compiler/astalgo.nim
@@ -353,6 +353,12 @@ proc symToYaml(conf: ConfigRef; n: PSym, indent: int = 0, maxRecDepth: int = - 1
 
 import tables
 
+const backrefStyle = "\e[90m"
+const enumStyle = "\e[34m"
+const numberStyle = "\e[33m"
+const stringStyle = "\e[32m"
+const resetStyle  = "\e[0m"
+
 type
   DebugPrinter = object
     conf: ConfigRef
@@ -361,6 +367,7 @@ type
     indent: int
     currentLine: int
     firstItem: bool
+    useColor: bool
     res: string
 
 proc indentMore(this: var DebugPrinter) =
@@ -407,15 +414,29 @@ proc key(this: var DebugPrinter; key: string) =
   this.res.add "\": "
 
 proc value(this: var DebugPrinter; value: string) =
+  if this.useColor:
+    this.res.add stringStyle
   this.res.add "\""
   this.res.add value
   this.res.add "\""
+  if this.useColor:
+    this.res.add resetStyle
 
 proc value(this: var DebugPrinter; value: BiggestInt) =
+  if this.useColor:
+     this.res.add numberStyle
   this.res.add value
+  if this.useColor:
+     this.res.add resetStyle
 
 proc value[T: enum](this: var DebugPrinter; value: T) =
-  this.value $value
+  if this.useColor:
+     this.res.add enumStyle
+  this.res.add "\""
+  this.res.add $value
+  this.res.add "\""
+  if this.useColor:
+     this.res.add resetStyle
 
 proc value[T: enum](this: var DebugPrinter; value: set[T]) =
   this.openBracket
@@ -436,9 +457,13 @@ template earlyExit(this: var DebugPrinter; n: PType | PNode | PSym) =
   if index < 0:
     this.visited[cast[pointer](n)] = this.currentLine
   else:
+    if this.useColor:
+      this.res.add backrefStyle
     this.res.add "<defined "
     this.res.add(this.currentLine - index)
     this.res.add " lines upwards>"
+    if this.useColor:
+       this.res.add resetStyle
     return
 
 proc value(this: var DebugPrinter; value: PType): void
@@ -448,7 +473,7 @@ proc value(this: var DebugPrinter; value: PSym): void =
 
   this.openCurly
   this.key("kind")
-  this.value($value.kind)
+  this.value(value.kind)
   this.key("name")
   this.value(value.name.s)
   this.key("id")
@@ -477,6 +502,7 @@ proc value(this: var DebugPrinter; value: PType): void =
   if value.sym != nil:
     this.key "sym"
     this.value value.sym
+    #this.value value.sym.name.s
 
   if card(value.flags) > 0:
     this.key "flags"
@@ -529,7 +555,8 @@ proc value(this: var DebugPrinter; value: PNode): void =
     this.value value.strVal
   of nkSym:
     this.key "sym"
-    this.value(value.sym)
+    this.value value.sym
+    #this.value value.sym.name.s
   of nkIdent:
     if value.ident != nil:
       this.key "ident"
@@ -554,6 +581,7 @@ when declared(echo):
     var this: DebugPrinter
     this.visited = initTable[pointer, int]()
     this.renderSymType = true
+    this.useColor = not defined(windows)
     this.value(n)
     echo($this.res)
 
@@ -561,6 +589,7 @@ when declared(echo):
     var this: DebugPrinter
     this.visited = initTable[pointer, int]()
     this.renderSymType = true
+    this.useColor = not defined(windows)
     this.value(n)
     echo($this.res)
 
@@ -568,6 +597,7 @@ when declared(echo):
     var this: DebugPrinter
     this.visited = initTable[pointer, int]()
     this.renderSymType = true
+    this.useColor = not defined(windows)
     this.value(n)
     echo($this.res)
 
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index 45daa00d9..09076c520 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -42,8 +42,6 @@ when not declared(dynlib.libCandidates):
 when options.hasTinyCBackend:
   import tccgen
 
-# implementation
-
 proc hcrOn(m: BModule): bool = m.config.hcrOn
 proc hcrOn(p: BProc): bool = p.module.config.hcrOn
 
@@ -295,6 +293,7 @@ proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: TLoc,
     includeHeader(p.module, "<new>")
     linefmt(p, section, "new ($1) $2;$n", rdLoc(a), getTypeDesc(p.module, t))
 
+  if optNimV2 in p.config.globalOptions: return
   case analyseObjectWithTypeField(t)
   of frNone:
     discard
@@ -303,7 +302,7 @@ proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: TLoc,
     if not takeAddr: r = "(*$1)" % [r]
     var s = skipTypes(t, abstractInst)
     if not p.module.compileToCpp:
-      while (s.kind == tyObject) and (s.sons[0] != nil):
+      while s.kind == tyObject and s.sons[0] != nil:
         add(r, ".Sup")
         s = skipTypes(s.sons[0], skipPtrs)
     linefmt(p, section, "$1.m_type = $2;$n", r, genTypeInfo(p.module, t, a.lode.info))
diff --git a/compiler/dfa.nim b/compiler/dfa.nim
index 968b16945..cecb8394c 100644
--- a/compiler/dfa.nim
+++ b/compiler/dfa.nim
@@ -596,7 +596,7 @@ proc genCall(c: var Con; n: PNode) =
     gen(c, n[i])
     when false:
       if t != nil and i < t.len and t.sons[i].kind == tyVar:
-        # XXX This is wrong! Pass by var is a 'might def', not a 'must def'
+        # This is wrong! Pass by var is a 'might def', not a 'must def'
         # like the other defs we emit. This is not good enough for a move
         # optimizer.
         genDef(c, n[i])
diff --git a/compiler/docgen.nim b/compiler/docgen.nim
index e6847cad3..7e7666de4 100644
--- a/compiler/docgen.nim
+++ b/compiler/docgen.nim
@@ -67,7 +67,7 @@ proc attachToType(d: PDoc; p: PSym): PSym =
 
 template declareClosures =
   proc compilerMsgHandler(filename: string, line, col: int,
-                          msgKind: rst.MsgKind, arg: string) {.procvar.} =
+                          msgKind: rst.MsgKind, arg: string) {.procvar, gcsafe.} =
     # translate msg kind:
     var k: TMsgKind
     case msgKind
@@ -81,9 +81,10 @@ template declareClosures =
     of mwUnknownSubstitution: k = warnUnknownSubstitutionX
     of mwUnsupportedLanguage: k = warnLanguageXNotSupported
     of mwUnsupportedField: k = warnFieldXNotSupported
-    globalError(conf, newLineInfo(conf, AbsoluteFile filename, line, col), k, arg)
+    {.gcsafe.}:
+      globalError(conf, newLineInfo(conf, AbsoluteFile filename, line, col), k, arg)
 
-  proc docgenFindFile(s: string): string {.procvar.} =
+  proc docgenFindFile(s: string): string {.procvar, gcsafe.} =
     result = options.findFile(conf, s).string
     if result.len == 0:
       result = getCurrentDir() / s
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index e3ca6830c..627cc6cdf 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -1804,6 +1804,10 @@ proc genOf(p: PProc, n: PNode, r: var TCompRes) =
     r.res = "isObj($1.m_type, $2)" % [x.res, genTypeInfo(p, t)]
   r.kind = resExpr
 
+proc genDefault(p: PProc, n: PNode; r: var TCompRes) =
+  r.res = createVar(p, n.typ, indirect = false)
+  r.kind = resExpr
+
 proc genReset(p: PProc, n: PNode) =
   var x: TCompRes
   useMagic(p, "genericReset")
@@ -1938,6 +1942,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
   of mNewSeq: genNewSeq(p, n)
   of mNewSeqOfCap: unaryExpr(p, n, r, "", "[]")
   of mOf: genOf(p, n, r)
+  of mDefault: genDefault(p, n, r)
   of mReset: genReset(p, n)
   of mEcho: genEcho(p, n, r)
   of mNLen..mNError, mSlurp, mStaticExec:
@@ -2138,7 +2143,7 @@ proc genProcBody(p: PProc, prc: PSym): Rope =
   if hasFrameInfo(p):
     add(result, frameDestroy(p))
 
-proc optionaLine(p: Rope): Rope =
+proc optionalLine(p: Rope): Rope =
   if p == nil:
     return nil
   else:
@@ -2182,11 +2187,11 @@ proc genProc(oldProc: PProc, prc: PSym): Rope =
             [ returnType,
               name,
               header,
-              optionaLine(p.globals),
-              optionaLine(p.locals),
-              optionaLine(resultAsgn),
-              optionaLine(genProcBody(p, prc)),
-              optionaLine(p.indentLine(returnStmt))]
+              optionalLine(p.globals),
+              optionalLine(p.locals),
+              optionalLine(resultAsgn),
+              optionalLine(genProcBody(p, prc)),
+              optionalLine(p.indentLine(returnStmt))]
   else:
     result = ~"\L"
 
@@ -2203,11 +2208,11 @@ proc genProc(oldProc: PProc, prc: PSym): Rope =
     def = "function $#($#) {$n$#$#$#$#$#" %
             [ name,
               header,
-              optionaLine(p.globals),
-              optionaLine(p.locals),
-              optionaLine(resultAsgn),
-              optionaLine(genProcBody(p, prc)),
-              optionaLine(p.indentLine(returnStmt))]
+              optionalLine(p.globals),
+              optionalLine(p.locals),
+              optionalLine(resultAsgn),
+              optionalLine(genProcBody(p, prc)),
+              optionalLine(p.indentLine(returnStmt))]
 
   dec p.extraIndent
   result.add p.indentLine(def)
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index 78f253bdc..ca0425182 100644
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -324,14 +324,15 @@ proc log*(s: string) {.procvar.} =
     f.writeLine(s)
     close(f)
 
-proc quit(conf: ConfigRef; msg: TMsgKind) =
+proc quit(conf: ConfigRef; msg: TMsgKind) {.gcsafe.} =
   if defined(debug) or msg == errInternal or hintStackTrace in conf.notes:
-    if stackTraceAvailable() and isNil(conf.writelnHook):
-      writeStackTrace()
-    else:
-      styledMsgWriteln(fgRed, "No stack traceback available\n" &
-          "To create a stacktrace, rerun compilation with ./koch temp " &
-          conf.command & " <file>")
+    {.gcsafe.}:
+      if stackTraceAvailable() and isNil(conf.writelnHook):
+        writeStackTrace()
+      else:
+        styledMsgWriteln(fgRed, "No stack traceback available\n" &
+            "To create a stacktrace, rerun compilation with ./koch temp " &
+            conf.command & " <file>")
   quit 1
 
 proc handleError(conf: ConfigRef; msg: TMsgKind, eh: TErrorHandling, s: string) =
diff --git a/compiler/options.nim b/compiler/options.nim
index 4bb17a03f..49ec6e87c 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -249,9 +249,9 @@ type
     suggestVersion*: int
     suggestMaxResults*: int
     lastLineInfo*: TLineInfo
-    writelnHook*: proc (output: string) {.closure.}
+    writelnHook*: proc (output: string) {.closure.} # cannot make this gcsafe yet because of Nimble
     structuredErrorHook*: proc (config: ConfigRef; info: TLineInfo; msg: string;
-                                severity: Severity) {.closure.}
+                                severity: Severity) {.closure, gcsafe.}
     cppCustomNamespace*: string
 
 proc hcrOn*(conf: ConfigRef): bool = return optHotCodeReloading in conf.globalOptions
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index addc9e9f3..364006dd1 100644
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -933,7 +933,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
       of wAcyclic:
         noVal(c, it)
         if sym.typ == nil: invalidPragma(c, it)
-        else: incl(sym.typ.flags, tfAcyclic)
+        # now: ignored
       of wShallow:
         noVal(c, it)
         if sym.typ == nil: invalidPragma(c, it)
diff --git a/compiler/ropes.nim b/compiler/ropes.nim
index 0d6d7d78f..2071ab46a 100644
--- a/compiler/ropes.nim
+++ b/compiler/ropes.nim
@@ -82,12 +82,13 @@ proc newRope(data: string = ""): Rope =
   result.L = -len(data)
   result.data = data
 
-var
-  cache: array[0..2048*2 - 1, Rope] # XXX Global here!
+when not compileOption("threads"):
+  var
+    cache: array[0..2048*2 - 1, Rope]
 
-proc resetRopeCache* =
-  for i in low(cache)..high(cache):
-    cache[i] = nil
+  proc resetRopeCache* =
+    for i in low(cache)..high(cache):
+      cache[i] = nil
 
 proc ropeInvariant(r: Rope): bool =
   if r == nil:
@@ -107,13 +108,16 @@ var gCacheMisses* = 0
 var gCacheIntTries* = 0
 
 proc insertInCache(s: string): Rope =
-  inc gCacheTries
-  var h = hash(s) and high(cache)
-  result = cache[h]
-  if isNil(result) or result.data != s:
-    inc gCacheMisses
+  when declared(cache):
+    inc gCacheTries
+    var h = hash(s) and high(cache)
+    result = cache[h]
+    if isNil(result) or result.data != s:
+      inc gCacheMisses
+      result = newRope(s)
+      cache[h] = result
+  else:
     result = newRope(s)
-    cache[h] = result
 
 proc rope*(s: string): Rope =
   ## Converts a string to a rope.
diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim
index 923558a8d..9d46c0b0c 100644
--- a/compiler/sempass2.nim
+++ b/compiler/sempass2.nim
@@ -721,6 +721,17 @@ proc cstringCheck(tracked: PEffects; n: PNode) =
     message(tracked.config, n.info, warnUnsafeCode, renderTree(n))
 
 proc track(tracked: PEffects, n: PNode) =
+  template gcsafeAndSideeffectCheck() =
+    if notGcSafe(op) and not importedFromC(a):
+      # and it's not a recursive call:
+      if not (a.kind == nkSym and a.sym == tracked.owner):
+        if warnGcUnsafe in tracked.config.notes: warnAboutGcUnsafe(n, tracked.config)
+        markGcUnsafe(tracked, a)
+    if tfNoSideEffect notin op.flags and not importedFromC(a):
+      # and it's not a recursive call:
+      if not (a.kind == nkSym and a.sym == tracked.owner):
+        markSideEffect(tracked, a)
+
   case n.kind
   of nkSym:
     useVar(tracked, n)
@@ -764,18 +775,11 @@ proc track(tracked: PEffects, n: PNode) =
           propagateEffects(tracked, n, a.sym)
         elif isIndirectCall(a, tracked.owner):
           assumeTheWorst(tracked, n, op)
+          gcsafeAndSideeffectCheck()
       else:
         mergeEffects(tracked, effectList.sons[exceptionEffects], n)
         mergeTags(tracked, effectList.sons[tagEffects], n)
-        if notGcSafe(op) and not importedFromC(a):
-          # and it's not a recursive call:
-          if not (a.kind == nkSym and a.sym == tracked.owner):
-            if warnGcUnsafe in tracked.config.notes: warnAboutGcUnsafe(n, tracked.config)
-            markGcUnsafe(tracked, a)
-        if tfNoSideEffect notin op.flags and not importedFromC(a):
-          # and it's not a recursive call:
-          if not (a.kind == nkSym and a.sym == tracked.owner):
-            markSideEffect(tracked, a)
+        gcsafeAndSideeffectCheck()
     if a.kind != nkSym or a.sym.magic != mNBindSym:
       for i in 1 ..< len(n): trackOperand(tracked, n.sons[i], paramType(op, i), a)
     if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}:
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index e717c6e07..0fabd5531 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -1854,7 +1854,9 @@ proc processMagicType(c: PContext, m: PSym) =
     case m.name.s
     of "lent": setMagicType(c.config, m, tyLent, c.config.target.ptrSize)
     of "sink": setMagicType(c.config, m, tySink, szUncomputedSize)
-    of "owned": setMagicType(c.config, m, tyOwned, c.config.target.ptrSize)
+    of "owned":
+      setMagicType(c.config, m, tyOwned, c.config.target.ptrSize)
+      incl m.typ.flags, tfHasOwned
     else: localError(c.config, m.info, errTypeExpected)
   else: localError(c.config, m.info, errTypeExpected)
 
diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim
index 483588e6b..e3380c0e3 100644
--- a/compiler/semtypinst.nim
+++ b/compiler/semtypinst.nim
@@ -16,16 +16,12 @@ const
   tfInstClearedFlags = {tfHasMeta, tfUnresolved}
 
 proc checkPartialConstructedType(conf: ConfigRef; info: TLineInfo, t: PType) =
-  if tfAcyclic in t.flags and skipTypes(t, abstractInst).kind != tyObject:
-    localError(conf, info, "invalid pragma: acyclic")
-  elif t.kind in {tyVar, tyLent} and t.sons[0].kind in {tyVar, tyLent}:
+  if t.kind in {tyVar, tyLent} and t.sons[0].kind in {tyVar, tyLent}:
     localError(conf, info, "type 'var var' is not allowed")
 
 proc checkConstructedType*(conf: ConfigRef; info: TLineInfo, typ: PType) =
   var t = typ.skipTypes({tyDistinct})
   if t.kind in tyTypeClasses: discard
-  elif tfAcyclic in t.flags and skipTypes(t, abstractInst).kind != tyObject:
-    localError(conf, info, "invalid pragma: acyclic")
   elif t.kind in {tyVar, tyLent} and t.sons[0].kind in {tyVar, tyLent}:
     localError(conf, info, "type 'var var' is not allowed")
   elif computeSize(conf, t) == szIllegalRecursion:
diff --git a/compiler/transf.nim b/compiler/transf.nim
index ec390da9a..ed4a7018d 100644
--- a/compiler/transf.nim
+++ b/compiler/transf.nim
@@ -893,7 +893,8 @@ proc transform(c: PTransf, n: PNode): PTransNode =
                   nkBlockStmt, nkBlockExpr}:
       oldDeferAnchor = c.deferAnchor
       c.deferAnchor = n
-  if n.typ != nil and tfHasAsgn in n.typ.flags:
+  if (n.typ != nil and tfHasAsgn in n.typ.flags) or
+      optNimV2 in c.graph.config.globalOptions:
     c.needsDestroyPass = true
   case n.kind
   of nkSym:
diff --git a/compiler/types.nim b/compiler/types.nim
index 86e5e756a..b71888906 100644
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -68,17 +68,6 @@ const
   typedescPtrs* = abstractPtrs + {tyTypeDesc}
   typedescInst* = abstractInst + {tyTypeDesc, tyOwned}
 
-type
-  TTypeFieldResult* = enum
-    frNone,                   # type has no object type field
-    frHeader,                 # type has an object type field only in the header
-    frEmbedded                # type has an object type field somewhere embedded
-
-proc analyseObjectWithTypeField*(t: PType): TTypeFieldResult
-  # this does a complex analysis whether a call to ``objectInit`` needs to be
-  # made or intializing of the type field suffices or if there is no type field
-  # at all in this type.
-
 proc invalidGenericInst*(f: PType): bool =
   result = f.kind == tyGenericInst and lastSon(f) == nil
 
@@ -242,11 +231,16 @@ proc containsObject*(t: PType): bool =
   result = searchTypeFor(t, isObjectPredicate)
 
 proc isObjectWithTypeFieldPredicate(t: PType): bool =
-
   result = t.kind == tyObject and t.sons[0] == nil and
       not (t.sym != nil and {sfPure, sfInfixCall} * t.sym.flags != {}) and
       tfFinal notin t.flags
 
+type
+  TTypeFieldResult* = enum
+    frNone,                   # type has no object type field
+    frHeader,                 # type has an object type field only in the header
+    frEmbedded                # type has an object type field somewhere embedded
+
 proc analyseObjectWithTypeFieldAux(t: PType,
                                    marker: var IntSet): TTypeFieldResult =
   var res: TTypeFieldResult
@@ -276,7 +270,10 @@ proc analyseObjectWithTypeFieldAux(t: PType,
   else:
     discard
 
-proc analyseObjectWithTypeField(t: PType): TTypeFieldResult =
+proc analyseObjectWithTypeField*(t: PType): TTypeFieldResult =
+  # this does a complex analysis whether a call to ``objectInit`` needs to be
+  # made or intializing of the type field suffices or if there is no type field
+  # at all in this type.
   var marker = initIntSet()
   result = analyseObjectWithTypeFieldAux(t, marker)
 
@@ -323,9 +320,7 @@ proc canFormAcycleNode(marker: var IntSet, n: PNode, startId: int): bool =
 proc canFormAcycleAux(marker: var IntSet, typ: PType, startId: int): bool =
   result = false
   if typ == nil: return
-  if tfAcyclic in typ.flags: return
   var t = skipTypes(typ, abstractInst+{tyOwned}-{tyTypeDesc})
-  if tfAcyclic in t.flags: return
   case t.kind
   of tyTuple, tyObject, tyRef, tySequence, tyArray, tyOpenArray, tyVarargs:
     if not containsOrIncl(marker, t.id):
diff --git a/doc/manual.rst b/doc/manual.rst
index a2d51cf08..09daf4a95 100644
--- a/doc/manual.rst
+++ b/doc/manual.rst
@@ -6925,41 +6925,8 @@ The ``noreturn`` pragma is used to mark a proc that never returns.
 
 acyclic pragma
 --------------
-The ``acyclic`` pragma can be used for object types to mark them as acyclic
-even though they seem to be cyclic. This is an **optimization** for the garbage
-collector to not consider objects of this type as part of a cycle:
-
-.. code-block:: nim
-  type
-    Node = ref NodeObj
-    NodeObj {.acyclic.} = object
-      left, right: Node
-      data: string
-
-Or if we directly use a ref object:
-
-.. code-block:: nim
-  type
-    Node = ref object {.acyclic.}
-      left, right: Node
-      data: string
-
-In the example a tree structure is declared with the ``Node`` type. Note that
-the type definition is recursive and the GC has to assume that objects of
-this type may form a cyclic graph. The ``acyclic`` pragma passes the
-information that this cannot happen to the GC. If the programmer uses the
-``acyclic`` pragma for data types that are in reality cyclic, the GC may leak
-memory, but nothing worse happens.
-
-**Future directions**: The ``acyclic`` pragma may become a property of a
-``ref`` type:
-
-.. code-block:: nim
-  type
-    Node = acyclic ref NodeObj
-    NodeObj = object
-      left, right: Node
-      data: string
+The ``acyclic`` pragma applies to type declarations. It is deprecated and
+ignored.
 
 
 final pragma
diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim
index 0b077b1f1..6e4e3f5dd 100644
--- a/lib/packages/docutils/rst.nim
+++ b/lib/packages/docutils/rst.nim
@@ -45,8 +45,8 @@ type
     mwUnsupportedField
 
   MsgHandler* = proc (filename: string, line, col: int, msgKind: MsgKind,
-                       arg: string) {.closure.} ## what to do in case of an error
-  FindFileHandler* = proc (filename: string): string {.closure.}
+                       arg: string) {.closure, gcsafe.} ## what to do in case of an error
+  FindFileHandler* = proc (filename: string): string {.closure, gcsafe.}
 
 const
   messages: array[MsgKind, string] = [
diff --git a/tests/destructor/tdiscard.nim b/tests/destructor/tdiscard.nim
index ad09a1900..57b5ecb9b 100644
--- a/tests/destructor/tdiscard.nim
+++ b/tests/destructor/tdiscard.nim
@@ -1,5 +1,6 @@
 discard """
 joinable: false
+target: "C"
 """
 
 type
diff --git a/tests/destructor/tmove_objconstr.nim b/tests/destructor/tmove_objconstr.nim
index 6e11d9b27..bfc819ceb 100644
--- a/tests/destructor/tmove_objconstr.nim
+++ b/tests/destructor/tmove_objconstr.nim
@@ -8,6 +8,7 @@ test destroyed 0
 4
 Pony is dying!'''
 joinable: false
+target: "C"
 """
 
 # bug #4214
diff --git a/tests/destructor/tuse_result_prevents_sinks.nim b/tests/destructor/tuse_result_prevents_sinks.nim
index 4c32eac91..37b5af9b2 100644
--- a/tests/destructor/tuse_result_prevents_sinks.nim
+++ b/tests/destructor/tuse_result_prevents_sinks.nim
@@ -1,5 +1,6 @@
 discard """
   output: ""
+  target: "C"
 """
 
 # bug #9594
diff --git a/tests/effects/tgcsafe3.nim b/tests/effects/tgcsafe3.nim
new file mode 100644
index 000000000..5137efe4c
--- /dev/null
+++ b/tests/effects/tgcsafe3.nim
@@ -0,0 +1,21 @@
+discard """
+  errormsg: "'myproc' is not GC-safe as it accesses 'global_proc' which is a global using GC'ed memory"
+  line: 12
+  cmd: "nim $target --hints:on --threads:on $options $file"
+"""
+
+var useGcMem = "string here"
+
+var global_proc: proc(a: string) {.nimcall.} = proc (a: string) =
+  echo useGcMem
+
+proc myproc(i: int) {.gcsafe.} =
+  when false:
+    if global_proc != nil:
+      echo "a"
+    if isNil(global_proc):
+      return
+
+  global_proc("ho")
+
+myproc(0)
diff --git a/tests/js/tbasics.nim b/tests/js/tbasics.nim
index 33616776f..b297bb037 100644
--- a/tests/js/tbasics.nim
+++ b/tests/js/tbasics.nim
@@ -46,3 +46,17 @@ proc test2 =
   echo int(val)
 
 test2()
+
+
+var someGlobal = default(array[5, int])
+for x in someGlobal: doAssert(x == 0)
+
+proc tdefault =
+  var x = default(int)
+  doAssert(x == 0)
+  proc inner(v: openarray[string]) =
+    doAssert(v.len == 0)
+
+  inner(default(seq[string]))
+
+tdefault()