summary refs log tree commit diff stats
path: root/tests/arc
diff options
context:
space:
mode:
Diffstat (limited to 'tests/arc')
-rw-r--r--tests/arc/amodule.nim21
-rw-r--r--tests/arc/bmodule.nim4
-rw-r--r--tests/arc/cmodule.nim4
-rw-r--r--tests/arc/dmodule.nim23
-rw-r--r--tests/arc/nim.cfg1
-rw-r--r--tests/arc/t14383.nim217
-rw-r--r--tests/arc/t14472.nim43
-rw-r--r--tests/arc/t14864.nim5
-rw-r--r--tests/arc/t15909.nim16
-rw-r--r--tests/arc/t16033.nim10
-rw-r--r--tests/arc/t16458.nim6
-rw-r--r--tests/arc/t16558.nim9
-rw-r--r--tests/arc/t17025.nim56
-rw-r--r--tests/arc/t17173.nim10
-rw-r--r--tests/arc/t17812.nim41
-rw-r--r--tests/arc/t18645.nim18
-rw-r--r--tests/arc/t18971.nim10
-rw-r--r--tests/arc/t18977.nim26
-rw-r--r--tests/arc/t19231.nim18
-rw-r--r--tests/arc/t19364.nim30
-rw-r--r--tests/arc/t19401.nim32
-rw-r--r--tests/arc/t19402.nim32
-rw-r--r--tests/arc/t19435.nim29
-rw-r--r--tests/arc/t19457.nim16
-rw-r--r--tests/arc/t19862.nim15
-rw-r--r--tests/arc/t20456.nim7
-rw-r--r--tests/arc/t20588.nim25
-rw-r--r--tests/arc/t21184.nim77
-rw-r--r--tests/arc/t22218.nim25
-rw-r--r--tests/arc/t22237.nim55
-rw-r--r--tests/arc/t22478.nim46
-rw-r--r--tests/arc/t22787.nim37
-rw-r--r--tests/arc/t23247.nim52
-rw-r--r--tests/arc/t9650.nim87
-rw-r--r--tests/arc/taliased_reassign.nim41
-rw-r--r--tests/arc/tamemfiles.nim110
-rw-r--r--tests/arc/tamodule.nim9
-rw-r--r--tests/arc/tarc_macro.nim57
-rw-r--r--tests/arc/tarc_orc.nim186
-rw-r--r--tests/arc/tarcmisc.nim836
-rw-r--r--tests/arc/tasyncawait.nim68
-rw-r--r--tests/arc/tasyncleak.nim21
-rw-r--r--tests/arc/tasyncleak2.nim88
-rw-r--r--tests/arc/tasyncleak3.nim47
-rw-r--r--tests/arc/tasyncleak4.nim21
-rw-r--r--tests/arc/tasyncorc.nim26
-rw-r--r--tests/arc/tcaseobj.nim366
-rw-r--r--tests/arc/tcaseobjcopy.nim253
-rw-r--r--tests/arc/tclosureiter.nim36
-rw-r--r--tests/arc/tcomputedgoto.nim44
-rw-r--r--tests/arc/tcomputedgotocopy.nim44
-rw-r--r--tests/arc/tconst_to_sink.nim26
-rw-r--r--tests/arc/tcontrolflow.nim118
-rw-r--r--tests/arc/tcursor_field_obj_constr.nim44
-rw-r--r--tests/arc/tcursor_on_localvar.nim161
-rw-r--r--tests/arc/tcursorloop.nim45
-rw-r--r--tests/arc/tcustomtrace.nim240
-rw-r--r--tests/arc/tdeepcopy.nim67
-rw-r--r--tests/arc/tdestroy_in_loopcond.nim74
-rw-r--r--tests/arc/tdup.nim71
-rw-r--r--tests/arc/testfile.txt2
-rw-r--r--tests/arc/texceptions.nim16
-rw-r--r--tests/arc/texplicit_sink.nim29
-rw-r--r--tests/arc/tfuncobj.nim38
-rw-r--r--tests/arc/thamming_orc.nim155
-rw-r--r--tests/arc/thamming_thinout.nim183
-rw-r--r--tests/arc/thard_alignment.nim148
-rw-r--r--tests/arc/thavlak_orc_stress.nim414
-rw-r--r--tests/arc/theavy_recursion.nim43
-rw-r--r--tests/arc/timportedobj.nim14
-rw-r--r--tests/arc/titeration_doesnt_copy.nim67
-rw-r--r--tests/arc/tkeys_lent.nim17
-rw-r--r--tests/arc/tmalloc.nim16
-rw-r--r--tests/arc/tmarshal.nim140
-rw-r--r--tests/arc/tmove_regression.nim22
-rw-r--r--tests/arc/tmovebug.nim841
-rw-r--r--tests/arc/tmovebugcopy.nim526
-rw-r--r--tests/arc/tnewseq_legacy.nim13
-rw-r--r--tests/arc/top_no_cursor2.nim53
-rw-r--r--tests/arc/topenarray.nim86
-rw-r--r--tests/arc/topt_cursor.nim57
-rw-r--r--tests/arc/topt_cursor2.nim76
-rw-r--r--tests/arc/topt_no_cursor.nim376
-rw-r--r--tests/arc/topt_refcursors.nim54
-rw-r--r--tests/arc/topt_wasmoved_destroy_pairs.nim94
-rw-r--r--tests/arc/torc_basic_test.nim138
-rw-r--r--tests/arc/torc_selfcycles.nim33
-rw-r--r--tests/arc/torcbench.nim38
-rw-r--r--tests/arc/torcmisc.nim66
-rw-r--r--tests/arc/tref_cast_error.nim15
-rw-r--r--tests/arc/trepr.nim97
-rw-r--r--tests/arc/trtree.nim640
-rw-r--r--tests/arc/tshared_ptr_crash.nim67
-rw-r--r--tests/arc/tstrformat.nim22
-rw-r--r--tests/arc/tstringliteral.nim17
-rw-r--r--tests/arc/tthread.nim63
-rw-r--r--tests/arc/tunref_cycle.nim26
-rw-r--r--tests/arc/tweave.nim157
-rw-r--r--tests/arc/tweavecopy.nim154
-rw-r--r--tests/arc/twrong_sinkinference.nim18
100 files changed, 9233 insertions, 0 deletions
diff --git a/tests/arc/amodule.nim b/tests/arc/amodule.nim
new file mode 100644
index 000000000..2b4204a66
--- /dev/null
+++ b/tests/arc/amodule.nim
@@ -0,0 +1,21 @@
+# bug #14219
+var vectors = @["a", "b", "c", "d", "e"]
+
+iterator testVectors(): string =
+  for vector in vectors:
+    yield vector
+
+var r = ""
+for item in testVectors():
+  r.add item
+echo r
+
+# bug #12990
+iterator test(): int {.closure.} =
+  yield 0
+
+let x = test
+while true:
+  let val = x()
+  if finished(x): break
+  echo val
diff --git a/tests/arc/bmodule.nim b/tests/arc/bmodule.nim
new file mode 100644
index 000000000..70c2cc645
--- /dev/null
+++ b/tests/arc/bmodule.nim
@@ -0,0 +1,4 @@
+import cmodule
+
+for i in @[1, 2, 3].cycle():
+  echo i
diff --git a/tests/arc/cmodule.nim b/tests/arc/cmodule.nim
new file mode 100644
index 000000000..6c6e6c0fa
--- /dev/null
+++ b/tests/arc/cmodule.nim
@@ -0,0 +1,4 @@
+iterator cycle*[T](s: openArray[T]): T =
+  let s = @s
+  for x in s:
+    yield x
diff --git a/tests/arc/dmodule.nim b/tests/arc/dmodule.nim
new file mode 100644
index 000000000..455ec7084
--- /dev/null
+++ b/tests/arc/dmodule.nim
@@ -0,0 +1,23 @@
+type
+  MinKind* = enum
+    minDictionary
+    minBool
+  MinValue* = object
+    case kind*: MinKind
+    of minDictionary:
+      symbols: seq[MinOperator]
+    else: discard
+  MinOperator = object
+
+# remove this inline pragma to make it compile
+proc `$`*(a: MinValue): string {.inline.} =
+  case a.kind
+  of minDictionary:
+    result = "hello"
+    for i in a.symbols:
+      result = "hello"
+  else: discard
+
+proc parseMinValue*(): MinValue =
+  # or this echo
+  echo result
diff --git a/tests/arc/nim.cfg b/tests/arc/nim.cfg
new file mode 100644
index 000000000..7c148b797
--- /dev/null
+++ b/tests/arc/nim.cfg
@@ -0,0 +1 @@
+--sinkInference:on
diff --git a/tests/arc/t14383.nim b/tests/arc/t14383.nim
new file mode 100644
index 000000000..96b505166
--- /dev/null
+++ b/tests/arc/t14383.nim
@@ -0,0 +1,217 @@
+discard """
+  cmd: "nim c --gc:arc $file"
+  output: '''
+hello
+hello
+@["a", "b"]
+---------------------
+plain:
+destroying: ('first', 42)
+destroying: ('second', 20)
+destroying: ('third', 12)
+
+Option[T]:
+destroying: ('first', 42)
+destroying: ('second', 20)
+destroying: ('third', 12)
+
+seq[T]:
+destroying: ('first', 42)
+destroying: ('second', 20)
+destroying: ('third', 12)
+
+1 1
+'''
+"""
+
+import dmodule
+
+var val = parseMinValue()
+if val.kind == minDictionary:
+  echo val
+
+#------------------------------------------------------------------------------
+# Issue #15238
+#------------------------------------------------------------------------------
+
+proc sinkArg(x: sink seq[string]) =
+  discard
+
+proc varArg(lst: var seq[string]) = 
+  sinkArg(lst)
+
+var x = @["a", "b"]
+varArg(x)
+echo x
+
+
+#------------------------------------------------------------------------------
+# Issue #15286
+#------------------------------------------------------------------------------
+
+import std/os
+discard getFileInfo(".")
+
+
+#------------------------------------------------------------------------------
+# Issue #15707
+#------------------------------------------------------------------------------
+
+type
+  JVMObject = ref object
+proc freeJVMObject(o: JVMObject) =
+  discard
+proc fromJObject(T: typedesc[JVMObject]): T =
+  result.new(cast[proc(r: T) {.nimcall.}](freeJVMObject))
+
+discard JVMObject.fromJObject()
+
+
+#------------------------------------------------------------------------------
+# Issue #15910
+#------------------------------------------------------------------------------
+
+import options
+
+type
+  Thing = object
+    name: string
+    age: int
+
+proc `=destroy`(thing: var Thing) =
+  if thing.name != "":
+    echo "destroying: ('", thing.name, "', ", thing.age, ")"
+  `=destroy`(thing.name)
+  `=destroy`(thing.age)
+
+proc plain() =
+  var t = Thing(name: "first", age: 42)
+  t = Thing(name: "second", age: 20)
+  t = Thing()
+  let u = Thing(name: "third", age: 12)
+
+proc optionT() =
+  var t = Thing(name: "first", age: 42).some
+  t = Thing(name: "second", age: 20).some
+  t = none(Thing)
+  let u = Thing(name: "third", age: 12).some
+
+proc seqT() =
+  var t = @[Thing(name: "first", age: 42)]
+  t = @[Thing(name: "second", age: 20)]
+  t = @[]
+  let u = @[Thing(name: "third", age: 12)]
+
+echo "---------------------"
+echo "plain:"
+plain()
+echo()
+
+echo "Option[T]:"
+optionT()
+echo()
+
+echo "seq[T]:"
+seqT()
+echo()
+
+
+#------------------------------------------------------------------------------
+# Issue #16120, const seq into sink
+#------------------------------------------------------------------------------
+
+proc main =
+  let avals = @[@[1.0'f32, 4.0, 7.0, 10.0]]
+  let rankdef = avals
+  echo avals.len, " ", rankdef.len
+
+main()
+
+
+#------------------------------------------------------------------------------
+# Issue #16722, ref on distinct type, wrong destructors called
+#------------------------------------------------------------------------------
+
+type
+  Obj = object of RootObj
+  ObjFinal = object
+  ObjRef = ref Obj
+  ObjFinalRef = ref ObjFinal
+  D = distinct Obj
+  DFinal = distinct ObjFinal
+  DRef = ref D
+  DFinalRef = ref DFinal
+
+proc `=destroy`(o: var Obj) =
+  doAssert false, "no Obj is constructed in this sample"
+
+proc `=destroy`(o: var ObjFinal) =
+  doAssert false, "no ObjFinal is constructed in this sample"
+
+var dDestroyed: int
+proc `=destroy`(d: var D) =
+  dDestroyed.inc
+
+proc `=destroy`(d: var DFinal) =
+  dDestroyed.inc
+
+func newD(): DRef =
+  DRef ObjRef()
+
+func newDFinal(): DFinalRef =
+  DFinalRef ObjFinalRef()
+
+proc testRefs() =
+  discard newD()
+  discard newDFinal()
+
+testRefs()
+
+doAssert(dDestroyed == 2)
+
+
+#------------------------------------------------------------------------------
+# Issue #16185, complex self-assingment elimination
+#------------------------------------------------------------------------------
+
+type
+  CpuStorage*[T] = ref CpuStorageObj[T]
+  CpuStorageObj[T] = object
+    size*: int
+    raw_buffer*: ptr UncheckedArray[T]
+  Tensor[T] = object
+    buf*: CpuStorage[T]
+  TestObject = object
+    x: Tensor[float]
+
+proc `=destroy`[T](s: var CpuStorageObj[T]) =
+  if s.raw_buffer != nil:
+    s.raw_buffer.deallocShared()
+    s.size = 0
+    s.raw_buffer = nil
+
+proc `=`[T](a: var CpuStorageObj[T]; b: CpuStorageObj[T]) {.error.}
+
+proc allocCpuStorage[T](s: var CpuStorage[T], size: int) =
+  new(s)
+  s.raw_buffer = cast[ptr UncheckedArray[T]](allocShared0(sizeof(T) * size))
+  s.size = size
+
+proc newTensor[T](size: int): Tensor[T] =
+  allocCpuStorage(result.buf, size)
+
+proc `[]`[T](t: Tensor[T], idx: int): T = t.buf.raw_buffer[idx]
+proc `[]=`[T](t: Tensor[T], idx: int, val: T) = t.buf.raw_buffer[idx] = val
+
+proc toTensor[T](s: seq[T]): Tensor[T] =
+  result = newTensor[T](s.len)
+  for i, x in s:
+    result[i] = x
+
+proc main2() =
+  var t: TestObject
+  t.x = toTensor(@[1.0, 2, 3, 4])
+  t.x = t.x  
+  doAssert(t.x.buf != nil) # self-assignment above should be eliminated
+
+main2()
diff --git a/tests/arc/t14472.nim b/tests/arc/t14472.nim
new file mode 100644
index 000000000..4ef661161
--- /dev/null
+++ b/tests/arc/t14472.nim
@@ -0,0 +1,43 @@
+discard """
+  valgrind: true
+  cmd: "nim cpp --gc:arc -d:useMalloc --deepcopy:on $file"
+"""
+
+type
+  ImportMaterial* = object
+    # Adding a field here makes the problem go away.
+
+  Mesh* = object
+    vertices: seq[float32]
+    material: ImportMaterial
+
+  ImportedScene* = object
+    meshes*: seq[Mesh]
+
+proc bork() : ImportedScene =
+  var mats: seq[ImportMaterial]
+
+  setLen(mats, 1)
+  add(result.meshes, Mesh(material: mats[0]))
+
+var s = bork()
+
+
+#------------------------------------------------------------------------
+# issue #15543
+
+import tables
+
+type
+  cdbl {.importc: "double".} = object
+
+  MyObject = ref object of RootObj
+    y: Table[string, cdbl]
+        
+
+proc test =
+  var x = new(MyObject)
+
+test()
+
+
diff --git a/tests/arc/t14864.nim b/tests/arc/t14864.nim
new file mode 100644
index 000000000..f59b14d2c
--- /dev/null
+++ b/tests/arc/t14864.nim
@@ -0,0 +1,5 @@
+discard """
+  cmd: "nim c --gc:arc $file"
+"""
+
+import bmodule
diff --git a/tests/arc/t15909.nim b/tests/arc/t15909.nim
new file mode 100644
index 000000000..f25c89daf
--- /dev/null
+++ b/tests/arc/t15909.nim
@@ -0,0 +1,16 @@
+discard """
+  action: run
+  cmd: "nim c --gc:arc $file"
+"""
+
+proc f1() {.noreturn.} = raise newException(CatchableError, "")
+
+proc f2(y: int): int =
+  if y != 0:
+    y
+  else:
+    f1()
+
+doAssert f2(5) == 5
+doAssertRaises(CatchableError):
+  discard f2(0)
diff --git a/tests/arc/t16033.nim b/tests/arc/t16033.nim
new file mode 100644
index 000000000..59ed22e4d
--- /dev/null
+++ b/tests/arc/t16033.nim
@@ -0,0 +1,10 @@
+discard """
+  targets: "c js"
+  matrix: "--gc:arc"
+"""
+
+# bug #16033
+when defined js:
+  doAssert not compileOption("gc", "arc")
+else:
+  doAssert compileOption("gc", "arc")
diff --git a/tests/arc/t16458.nim b/tests/arc/t16458.nim
new file mode 100644
index 000000000..6ae114287
--- /dev/null
+++ b/tests/arc/t16458.nim
@@ -0,0 +1,6 @@
+discard """
+  matrix: "--gc:orc --d:useNimRtl"
+  action: "compile"
+"""
+
+echo 134
\ No newline at end of file
diff --git a/tests/arc/t16558.nim b/tests/arc/t16558.nim
new file mode 100644
index 000000000..0dbe02b33
--- /dev/null
+++ b/tests/arc/t16558.nim
@@ -0,0 +1,9 @@
+discard """
+  matrix: "--gc:arc"
+  errormsg: "expression cannot be cast to 'int'"
+"""
+
+block: # bug #16558
+  var value = "hi there"
+  var keepInt: int
+  keepInt = cast[int](value)
diff --git a/tests/arc/t17025.nim b/tests/arc/t17025.nim
new file mode 100644
index 000000000..a64c59ac1
--- /dev/null
+++ b/tests/arc/t17025.nim
@@ -0,0 +1,56 @@
+discard """
+  cmd: "nim c --gc:arc $file"
+  output: '''
+{"Package": {"name": "hello"}, "Author": {"name": "name", "qq": "123456789", "email": "email"}}
+hello
+name
+123456789
+email
+hello
+name2
+987654321
+liame
+'''
+"""
+
+import parsecfg, streams, tables
+
+const cfg = """[Package]
+name=hello
+[Author]
+name=name
+qq=123456789
+email="email""""
+
+proc main() =
+    let stream = newStringStream(cfg)
+    let dict = loadConfig(stream)
+    var pname = dict.getSectionValue("Package","name")
+    var name = dict.getSectionValue("Author","name")
+    var qq = dict.getSectionValue("Author","qq")
+    var email = dict.getSectionValue("Author","email")
+    echo dict[]
+    echo pname & "\n" & name & "\n" & qq & "\n" & email
+    stream.close()
+
+main()
+
+proc getDict(): OrderedTableRef[string, OrderedTableRef[string, string]] =
+    result = newOrderedTable[string, OrderedTableRef[string, string]]()
+    result["Package"] = newOrderedTable[string, string]()
+    result["Package"]["name"] = "hello"
+    result["Author"] = newOrderedTable[string, string]()
+    result["Author"]["name"] = "name2"
+    result["Author"]["qq"] = "987654321"
+    result["Author"]["email"] = "liame"
+
+proc main2() =
+    let dict = getDict()
+    var pname = dict.getSectionValue("Package","name")
+    var name = dict.getSectionValue("Author","name")
+    var qq = dict.getSectionValue("Author","qq")
+    var email = dict.getSectionValue("Author","email")
+    echo pname & "\n" & name & "\n" & qq & "\n" & email
+
+main2()
+
diff --git a/tests/arc/t17173.nim b/tests/arc/t17173.nim
new file mode 100644
index 000000000..0acd886a2
--- /dev/null
+++ b/tests/arc/t17173.nim
@@ -0,0 +1,10 @@
+discard """
+  matrix: "--gc:refc; --gc:arc"
+"""
+
+import std/strbasics
+
+
+var a = "  vhellov   "
+strip(a)
+doAssert a == "vhellov"
diff --git a/tests/arc/t17812.nim b/tests/arc/t17812.nim
new file mode 100644
index 000000000..dd8ac89b0
--- /dev/null
+++ b/tests/arc/t17812.nim
@@ -0,0 +1,41 @@
+discard """
+  targets: "c js"
+  matrix: "--gc:refc; --gc:arc"
+"""
+
+import std/times
+
+block: # bug #17812
+  block:
+    type
+      Task = object
+        cb: proc ()
+
+    proc hello() = discard
+
+
+    let t = Task(cb: hello)
+
+    doAssert t.repr.len > 0
+
+
+  block:
+    type MyObj = object
+      field: DateTime
+
+
+    proc `$`(o: MyObj): string = o.repr
+
+    doAssert ($MyObj()).len > 0
+
+# bug #22175
+
+type Xxx = object
+  value: string
+
+proc complete(xxx: ref Xxx, v: sink string) =
+  xxx.value = move(v)
+
+let yyy = (ref Xxx)()
+
+yyy.complete("test")
diff --git a/tests/arc/t18645.nim b/tests/arc/t18645.nim
new file mode 100644
index 000000000..c5fddd4bb
--- /dev/null
+++ b/tests/arc/t18645.nim
@@ -0,0 +1,18 @@
+discard """
+  matrix: "--gc:arc; --gc:refc"
+  output: '''
+1
+2
+3
+'''
+"""
+
+proc bitTypeIdUnion() =
+  var bitId {.global.} = block:
+    0
+  inc bitId
+  echo bitId
+
+bitTypeIdUnion()
+bitTypeIdUnion()
+bitTypeIdUnion()
diff --git a/tests/arc/t18971.nim b/tests/arc/t18971.nim
new file mode 100644
index 000000000..9b587d956
--- /dev/null
+++ b/tests/arc/t18971.nim
@@ -0,0 +1,10 @@
+discard """
+  cmd: "nim c --gc:arc $file"
+"""
+
+type MyObj = ref object
+
+var o = MyObj()
+proc x: var MyObj = o
+
+var o2 = x()
diff --git a/tests/arc/t18977.nim b/tests/arc/t18977.nim
new file mode 100644
index 000000000..c775551a4
--- /dev/null
+++ b/tests/arc/t18977.nim
@@ -0,0 +1,26 @@
+discard """
+  matrix: "--mm:arc"
+"""
+
+type
+  E = enum
+    a, b, c, d
+  X = object
+    v: int
+  O = object
+    case kind: E
+    of a:
+      a: int
+    of {b, c}:
+      b: float
+    else:
+      d: X
+
+proc `=destroy`(x: var X) =
+  echo "x destroyed"
+
+var o = O(kind: d, d: X(v: 12345))
+doAssert o.d.v == 12345
+
+doAssertRaises(FieldDefect):
+  o.kind = a
diff --git a/tests/arc/t19231.nim b/tests/arc/t19231.nim
new file mode 100644
index 000000000..40fcf277c
--- /dev/null
+++ b/tests/arc/t19231.nim
@@ -0,0 +1,18 @@
+discard """
+  matrix: "--mm:orc"
+  targets: "c cpp"
+"""
+
+type
+  Game* = ref object
+
+proc free*(game: Game) =
+  var mixNumOpened:cint = 0
+  for i in 0..<mixNumOpened:
+    mixNumOpened -= 1
+
+proc newGame*(): Game =
+  new result, free
+
+var
+  game*: Game
diff --git a/tests/arc/t19364.nim b/tests/arc/t19364.nim
new file mode 100644
index 000000000..f520f3291
--- /dev/null
+++ b/tests/arc/t19364.nim
@@ -0,0 +1,30 @@
+discard """
+  cmd: '''nim c --gc:arc --expandArc:fooLeaks $file'''
+  nimout: '''
+--expandArc: fooLeaks
+
+var
+  tmpTuple_cursor
+  a_cursor
+  b_cursor
+  c_cursor
+tmpTuple_cursor = refTuple
+a_cursor = tmpTuple_cursor[0]
+b_cursor = tmpTuple_cursor[1]
+c_cursor = tmpTuple_cursor[2]
+-- end of expandArc ------------------------
+'''
+"""
+
+func fooLeaks(refTuple: tuple[a,
+                              b,
+                              c: seq[float]]): float =
+  let (a, b, c) = refTuple
+
+let refset = (a: newSeq[float](25_000_000),
+              b: newSeq[float](25_000_000),
+              c: newSeq[float](25_000_000))
+
+var res = newSeq[float](1_000_000)
+for i in 0 .. res.high:
+  res[i] = fooLeaks(refset)
diff --git a/tests/arc/t19401.nim b/tests/arc/t19401.nim
new file mode 100644
index 000000000..56702a4a2
--- /dev/null
+++ b/tests/arc/t19401.nim
@@ -0,0 +1,32 @@
+discard """
+  output: '''
+delete foo
+delete foo
+delete foo
+'''
+  matrix: "--mm:arc"
+"""
+
+type Foo = ref object
+  data: int
+proc delete(self: Foo) 
+proc newFoo: Foo =
+  let x = 12
+  discard x
+  new(result, delete)
+  result.data = x
+proc delete(self: Foo) =
+  doAssert self.data == 12
+  echo("delete foo")
+
+if isMainModule:
+  proc test() =
+    let x1 = newFoo()
+    let x2 = newFoo()
+    discard x1
+    discard x2
+    var x3: Foo
+    new(x3, delete)
+    x3.data = 12
+    discard x3
+  test()
diff --git a/tests/arc/t19402.nim b/tests/arc/t19402.nim
new file mode 100644
index 000000000..5ee6fc798
--- /dev/null
+++ b/tests/arc/t19402.nim
@@ -0,0 +1,32 @@
+discard """
+  output: '''
+delete foo
+delete foo
+delete foo
+'''
+  matrix: "--mm:arc"
+"""
+
+type Foo = ref object of RootObj
+  data: int
+proc delete(self: Foo)
+proc newFoo: Foo =
+  let x = 12
+  discard x
+  new(result, delete)
+  result.data = x
+proc delete(self: Foo) =
+  doAssert self.data == 12
+  echo("delete foo")
+
+if isMainModule:
+  proc test() =
+    let x1 = newFoo()
+    let x2 = newFoo()
+    discard x1
+    discard x2
+    var x3: Foo
+    new(x3, delete)
+    x3.data = 12
+    discard x3
+  test()
\ No newline at end of file
diff --git a/tests/arc/t19435.nim b/tests/arc/t19435.nim
new file mode 100644
index 000000000..519216bad
--- /dev/null
+++ b/tests/arc/t19435.nim
@@ -0,0 +1,29 @@
+discard """
+  matrix: "--gc:arc"
+"""
+
+# bug #19435
+{.experimental: "views".}
+
+type
+  Bar = object
+    placeholder: int
+  Foo = object
+    placeholder: int
+    c: seq[Bar] # remove this line to make things right
+
+func children*(s: var seq[Foo]): openArray[Foo] =
+  s.toOpenArray(0, s.len-1)
+
+proc test =
+  var foos = @[Foo(), Foo()]
+
+  assert foos.children.len == 2
+  var flag = true
+  for a in foos.children:
+    flag = false
+
+  if flag:
+    doAssert false
+
+test()
\ No newline at end of file
diff --git a/tests/arc/t19457.nim b/tests/arc/t19457.nim
new file mode 100644
index 000000000..78447ce82
--- /dev/null
+++ b/tests/arc/t19457.nim
@@ -0,0 +1,16 @@
+discard """
+  matrix: "--gc:refc; --gc:arc"
+"""
+
+# bug #19457
+proc gcd(x, y: seq[int]): seq[int] =
+    var
+      a = x
+      b = y
+    while b[0] > 0:
+      let c = @[a[0] mod b[0]]
+      a = b
+      b = c
+    return a
+
+doAssert gcd(@[1], @[2]) == @[1]
\ No newline at end of file
diff --git a/tests/arc/t19862.nim b/tests/arc/t19862.nim
new file mode 100644
index 000000000..6d3f57692
--- /dev/null
+++ b/tests/arc/t19862.nim
@@ -0,0 +1,15 @@
+discard """
+  matrix: "--gc:refc; --gc:arc"
+"""
+
+import std/widestrs
+
+# bug #19862
+type NewString = object
+
+proc len(s: NewString): int = 10
+
+converter toNewString(x: WideCStringObj): NewString = discard
+
+let w = newWideCString("test")
+doAssert len(w) == 4
diff --git a/tests/arc/t20456.nim b/tests/arc/t20456.nim
new file mode 100644
index 000000000..ace79255a
--- /dev/null
+++ b/tests/arc/t20456.nim
@@ -0,0 +1,7 @@
+discard """
+  cmd: "nim check $file"
+  action: "compile"
+"""
+
+when not defined(gcOrc):
+  {.error: "orc".}
diff --git a/tests/arc/t20588.nim b/tests/arc/t20588.nim
new file mode 100644
index 000000000..008bd1dcd
--- /dev/null
+++ b/tests/arc/t20588.nim
@@ -0,0 +1,25 @@
+discard """
+  cmd: "nim check --warnings:off --hints:off $file"
+  errormsg: ""
+  nimout: '''
+t20588.nim(20, 12) Error: illegal type conversion to 'auto'
+t20588.nim(21, 14) Error: illegal type conversion to 'typed'
+t20588.nim(22, 16) Error: illegal type conversion to 'untyped'
+t20588.nim(24, 7) Error: illegal type conversion to 'any'
+'''
+"""
+
+
+
+
+
+
+
+
+
+discard 0.0.auto
+discard typed("abc")
+discard untyped(4)
+var a = newSeq[bool](1000)
+if any(a):
+  echo "ok?"
\ No newline at end of file
diff --git a/tests/arc/t21184.nim b/tests/arc/t21184.nim
new file mode 100644
index 000000000..91d0c42c7
--- /dev/null
+++ b/tests/arc/t21184.nim
@@ -0,0 +1,77 @@
+discard """
+  matrix: "--mm:orc"
+"""
+
+import std/[with]
+
+type
+  Node* {.acyclic.} = ref object of RootObj
+    name: string
+    data: pointer
+    children: seq[Node]
+  TextNode = ref object of Node
+    text: string
+
+proc fakeEcho(s: string) =
+  if s.len < 0:
+    echo s
+
+proc newNode[T: Node](parent: Node): T =
+  new result
+  result.data = alloc0(250)
+  parent.children.add(result)
+
+proc newRootNode(): Node =
+  new result
+  result.data = alloc0(250)
+
+method printNode(node: Node) {.base.} =
+  fakeEcho node.name
+
+method printNode(node: TextNode) =
+  procCall printNode(Node(node))
+  fakeEcho node.text
+
+proc printChildren(node: Node) =
+  for child in node.children:
+    child.printNode()
+    printChildren(child)
+
+proc free(node: Node) =
+  for child in node.children:
+    free(child)
+  dealloc(node.data)
+
+template node(parent: Node, body: untyped): untyped =
+  var node = newNode[Node](parent)
+  with node:
+    body
+
+proc textNode(parent: Node, text: string) =
+  var node = newNode[TextNode](parent)
+  node.text = text
+
+template withRootNode(body: untyped): untyped =
+  var root = newRootNode()
+  root.name = "root"
+  with root:
+    body
+  root.printNode()
+  printChildren(root)
+  root.free()
+
+proc doTest() =
+  withRootNode:
+    node:
+      name = "child1"
+      node:
+        name = "child2"
+        node:
+          name = "child3"
+          textNode "Hello, world!"
+
+
+# bug #21171
+if isMainModule:
+  for i in 0..100000:
+    doTest()
diff --git a/tests/arc/t22218.nim b/tests/arc/t22218.nim
new file mode 100644
index 000000000..7837ed1d0
--- /dev/null
+++ b/tests/arc/t22218.nim
@@ -0,0 +1,25 @@
+discard """
+  cmd: "nim c --mm:arc $file"
+  errormsg: "'=copy' is not available for type <Obj>; requires a copy because it's not the last read of 'chan[]'; routine: test"
+"""
+
+# bug #22218
+type Obj[T] = object
+  v: T
+
+proc `=copy`[T](
+    dest: var Obj[T],
+    source: Obj[T]
+  ) {.error: "A channel cannot be copied".}
+
+from system/ansi_c import c_calloc
+
+proc test() =
+    var v: bool = true
+    var chan = cast[ptr Obj[int]](c_calloc(1, csize_t sizeof(Obj[int])))
+    var copy = chan[]
+
+    echo chan.v
+    echo v
+
+test()
\ No newline at end of file
diff --git a/tests/arc/t22237.nim b/tests/arc/t22237.nim
new file mode 100644
index 000000000..c6dbf768a
--- /dev/null
+++ b/tests/arc/t22237.nim
@@ -0,0 +1,55 @@
+discard """
+  matrix: "--mm:arc; --mm:orc"
+"""
+
+import std/macros
+import std/streams
+
+# bug #22237
+
+proc iterlines_closure2(f: File | Stream): iterator (): string =
+  result = iterator(): string =
+    for line in f.lines:
+      if line.len == 0:
+        break
+      yield line
+
+proc test() =
+  let f = newStringStream("""
+    1
+    2
+
+    3
+    4
+
+    5
+    6
+    7
+
+    8
+""")
+  while not f.atEnd():
+    let iterator_inst = iterlines_closure2(f)
+    for item in iterator_inst(): # Fails with "SIGSEGV: Illegal storage access. (Attempt to read from nil?)"
+      discard
+
+test()
+
+# bug #21160
+import sequtils
+
+iterator allMoves(fls: seq[int]): seq[int] =
+  yield fls
+
+proc neighbors(flrs: seq[int]): iterator: seq[int] =
+  return iterator(): seq[int] =
+    for flrs2 in allMoves(flrs):
+      yield flrs2
+      for flrs3 in allMoves(flrs2):
+        yield flrs3
+
+let f = @[1]
+for _ in neighbors(f):
+  discard
+for _ in neighbors(f):
+  discard
diff --git a/tests/arc/t22478.nim b/tests/arc/t22478.nim
new file mode 100644
index 000000000..5373fa161
--- /dev/null
+++ b/tests/arc/t22478.nim
@@ -0,0 +1,46 @@
+discard """
+  matrix: "-d:nimNoLentIterators --mm:arc"
+  output: '''PUSH DATA: {"test.message":{"test":{"nested":"v1"}}}'''
+  joinable: false
+"""
+
+# bug #22748
+import std/[json, typetraits, times]
+
+# publish
+
+proc publish*[T](payload: T) =
+  discard
+
+type MetricsPoint* = JsonNode
+
+proc push*(stat: string, data: JsonNode, usec: int64 = 0) =
+  let payload = newJObject()
+
+  # this results in a infinite recursion unless we deepCopy()
+  payload[stat] = data #.deepCopy
+
+  echo "PUSH DATA: ", payload
+
+  publish[MetricsPoint](payload)
+
+var scopes {.threadvar.}: seq[JsonNode]
+
+type WithTimeCallback*[T] = proc(data: var JsonNode): T
+
+proc pushScoped*[T](metric: string, blk: WithTimeCallback[T]): T {.gcsafe.} =
+  scopes.add newJObject()
+  defer: discard scopes.pop()
+
+  let stc = (cpuTime() * 1000_000).int64
+  result = blk(scopes[^1])
+  let dfc = (cpuTime() * 1000_000).int64 - stc
+
+  push(metric, scopes[^1], dfc)
+
+# demo code
+
+discard pushScoped[int]("test.message") do (data: var JsonNode) -> int:
+  data["test"] = %*{
+    "nested": "v1"
+  }
\ No newline at end of file
diff --git a/tests/arc/t22787.nim b/tests/arc/t22787.nim
new file mode 100644
index 000000000..5840a984b
--- /dev/null
+++ b/tests/arc/t22787.nim
@@ -0,0 +1,37 @@
+discard """
+  joinable: false
+"""
+
+import std/assertions
+
+proc foo =
+  var s:seq[string]
+  var res = ""
+
+  for i in 0..3:
+    s.add ("test" & $i)
+    s.add ("test" & $i)
+
+  var lastname:string
+
+  for i in s:
+    var name = i[0..4]
+
+    if name != lastname:
+      res.add "NEW:" & name & "\n"
+    else:
+      res.add name & ">" & lastname & "\n"
+
+    lastname = name
+
+  doAssert res == """
+NEW:test0
+test0>test0
+NEW:test1
+test1>test1
+NEW:test2
+test2>test2
+NEW:test3
+test3>test3
+"""
+foo()
\ No newline at end of file
diff --git a/tests/arc/t23247.nim b/tests/arc/t23247.nim
new file mode 100644
index 000000000..0fadc50cd
--- /dev/null
+++ b/tests/arc/t23247.nim
@@ -0,0 +1,52 @@
+discard """
+  matrix: ";-d:useMalloc"
+"""
+
+# bug #23247
+import std/hashes
+
+func baseAddr[T](x: openArray[T]): ptr T =
+  # Return the address of the zero:th element of x or `nil` if x is empty
+  if x.len == 0: nil else: cast[ptr T](x)
+
+func makeUncheckedArray[T](p: ptr T): ptr UncheckedArray[T] =
+  cast[ptr UncheckedArray[T]](p)
+
+type
+  LabelKey = object
+    data: seq[string]
+    refs: ptr UncheckedArray[string]
+    refslen: int
+
+  Gauge = ref object
+    metrics: seq[seq[seq[string]]]
+
+template values(key: LabelKey): openArray[string] =
+  if key.refslen > 0:
+    key.refs.toOpenArray(0, key.refslen - 1)
+  else:
+    key.data
+
+proc hash(key: LabelKey): Hash =
+  hash(key.values)
+
+proc view(T: type LabelKey, values: openArray[string]): T =
+  # TODO some day, we might get view types - until then..
+  LabelKey(refs: baseAddr(values).makeUncheckedArray(), refslen: values.len())
+
+template withValue2(k: untyped) =
+  discard hash(k)
+
+proc setGauge(
+    collector: Gauge,
+    labelValues: openArray[string],
+) =
+  let v = LabelKey.view(labelValues)
+  withValue2(v)
+  collector.metrics.add @[@labelValues, @labelValues]
+  discard @labelValues
+
+var nim_gc_mem_bytes = Gauge()
+let threadID = $getThreadId()
+setGauge(nim_gc_mem_bytes, @[threadID])
+setGauge(nim_gc_mem_bytes, @[threadID])
\ No newline at end of file
diff --git a/tests/arc/t9650.nim b/tests/arc/t9650.nim
new file mode 100644
index 000000000..a8182db68
--- /dev/null
+++ b/tests/arc/t9650.nim
@@ -0,0 +1,87 @@
+discard """
+  matrix: "--gc:arc"
+"""
+
+import typetraits
+
+# bug #9650
+type
+  SharedPtr*[T] = object
+    val: ptr tuple[atomicCounter: int, value: T]
+
+  Node*[T] = object
+    value: T
+    next: SharedPtr[Node[T]]
+
+  ForwardList*[T] = object
+    first: SharedPtr[Node[T]]
+
+proc `=destroy`*[T](p: var SharedPtr[T]) =
+  if p.val != nil:
+    let c = atomicDec(p.val[].atomicCounter)
+    if c == 0:
+      when not supportsCopyMem(T):
+         `=destroy`(p.val[])
+      dealloc(p.val)
+    p.val = nil
+
+proc `=`*[T](dest: var SharedPtr[T], src: SharedPtr[T]) =
+  if dest.val != src.val:
+    if dest.val != nil:
+      `=destroy`(dest)
+    if src.val != nil:
+      discard atomicInc(src.val[].atomicCounter)
+      dest.val = src.val
+
+proc `=sink`*[T](dest: var SharedPtr[T], src: SharedPtr[T]) =
+  if dest.val != nil and dest.val != src.val:
+    `=destroy`(dest)
+  dest.val = src.val
+
+ 
+proc newSharedPtr*[T](val: sink T): SharedPtr[T] =
+  result.val = cast[type(result.val)](alloc(sizeof(result.val[])))
+  reset(result.val[])
+  result.val.atomicCounter = 1
+  result.val.value = val
+
+proc isNil*[T](p: SharedPtr[T]): bool =
+  p.val == nil
+
+template `->`*[T](p: SharedPtr[T], name: untyped): untyped =
+  p.val.value.name
+
+proc createNode[T](val: T): SharedPtr[ Node[T] ]=
+  result = newSharedPtr(Node[T](value: val))
+
+proc push_front*[T](list: var ForwardList[T], val: T) =
+  var newElem = createNode(val)
+  newElem->next = list.first
+  list.first = newElem
+
+proc pop_front*[T](list: var ForwardList[T]) =
+  let head = list.first
+  list.first = head->next
+
+proc toString*[T](list: ForwardList[T]): string =
+  result = "["
+  var head = list.first
+  while not head.isNil:
+    result &= $(head->value) & ", "
+    head = head->next
+  result &= ']'
+
+block:
+  var x: ForwardList[int]
+  x.push_front(1)
+  x.push_front(2)
+  x.push_front(3)
+
+  doAssert toString(x) == "[3, 2, 1, ]"
+
+  x.pop_front()
+  x.pop_front()
+  doAssert toString(x) == "[1, ]"
+
+  x.pop_front()
+  doAssert toString(x) == "[]"
diff --git a/tests/arc/taliased_reassign.nim b/tests/arc/taliased_reassign.nim
new file mode 100644
index 000000000..5563fae8c
--- /dev/null
+++ b/tests/arc/taliased_reassign.nim
@@ -0,0 +1,41 @@
+discard """
+  matrix: "--mm:orc"
+"""
+
+# bug #20993
+
+type
+  Dual[int] = object # must be generic (even if fully specified)
+    p: int
+proc D(p: int): Dual[int] = Dual[int](p: p)
+proc `+`(x: Dual[int], y: Dual[int]): Dual[int] = D(x.p + y.p)
+
+type
+  Tensor[T] = object
+    buf: seq[T]
+proc newTensor*[T](s: int): Tensor[T] = Tensor[T](buf: newSeq[T](s))
+proc `[]`*[T](t: Tensor[T], idx: int): T = t.buf[idx]
+proc `[]=`*[T](t: var Tensor[T], idx: int, val: T) = t.buf[idx] = val
+
+proc `+.`[T](t1, t2: Tensor[T]): Tensor[T] =
+  let n = t1.buf.len
+  result = newTensor[T](n)
+  for i in 0 ..< n:
+    result[i] = t1[i] + t2[i]
+
+proc toTensor*[T](a: sink seq[T]): Tensor[T] =
+  ## This breaks it: Using `T` instead makes it work
+  type U = typeof(a[0])
+  var t: Tensor[U] # Tensor[T] works
+  t.buf = a
+  result = t
+
+proc loss() =
+  var B = toTensor(@[D(123)])
+  let a = toTensor(@[D(-10)])
+  B = B +. a
+  doAssert B[0].p == 113, "I want to be 113, but I am " & $B[0].p
+
+loss()
+
+
diff --git a/tests/arc/tamemfiles.nim b/tests/arc/tamemfiles.nim
new file mode 100644
index 000000000..97deb489f
--- /dev/null
+++ b/tests/arc/tamemfiles.nim
@@ -0,0 +1,110 @@
+discard """
+  output: '''
+loop 1a
+loop 1b; cols: @[1, x]
+loop 1c
+loop 1d
+loop 1a
+loop 1b; cols: @[2, y]
+loop 1c
+loop 1d
+'''
+  cmd: "nim c --gc:arc $file"
+"""
+
+# bug #13596
+
+import tables, memfiles, strutils, os
+
+type Splitr* = tuple[ repeat: bool, chrDlm: char, setDlm: set[char], n: int ]
+
+type csize = uint
+proc cmemchr*(s: pointer, c: char, n: csize): pointer {.
+  importc: "memchr", header: "<string.h>" .}
+proc `-!`*(p, q: pointer): int {.inline.} =
+  (cast[int](p) -% cast[int](q)).int
+proc `+!`*(p: pointer, i: int): pointer {.inline.} =
+  cast[pointer](cast[int](p) +% i)
+proc `+!`*(p: pointer, i: uint64): pointer {.inline.} =
+  cast[pointer](cast[uint64](p) + i)
+
+proc charEq(x, c: char): bool {.inline.} = x == c
+
+proc initSplitr*(delim: string): Splitr =
+  if delim == "white":          #User can use any other permutation if needed
+    result.repeat = true
+    result.chrDlm = ' '
+    result.setDlm = { ' ', '\t', '\n' }
+    result.n      = result.setDlm.card
+    return
+  for c in delim:
+    if c in result.setDlm:
+      result.repeat = true
+      continue
+    result.setDlm.incl(c)
+    inc(result.n)
+  if result.n == 1:             #support n==1 test to allow memchr optimization
+    result.chrDlm = delim[0]
+
+proc hash(x: MemSlice): int = 55542
+
+template defSplit[T](slc: T, fs: var seq[MemSlice], n: int, repeat: bool,
+                     sep: untyped, nextSep: untyped, isSep: untyped) {.dirty.} =
+  fs.setLen(if n < 1: 16 else: n)
+  var b   = slc.data
+  var eob = b +! slc.size
+  while repeat and eob -! b > 0 and isSep((cast[cstring](b))[0], sep):
+    b = b +! 1
+    if b == eob: fs.setLen(0); return
+  var e = nextSep(b, sep, (eob -! b).csize)
+  while e != nil:
+    if n < 1:                               #Unbounded msplit
+      if result == fs.len - 1:              #Expand capacity
+        fs.setLen(if fs.len < 512: 2*fs.len else: fs.len + 512)
+    elif result == n - 1:                   #Need 1 more slot for final field
+      break
+    fs[result].data = b
+    fs[result].size = e -! b
+    result += 1
+    while repeat and eob -! e > 0 and isSep((cast[cstring](e))[1], sep):
+      e = e +! 1
+    b = e +! 1
+    if eob -! b <= 0:
+      b = eob
+      break
+    e = nextSep(b, sep, (eob -! b).csize)
+  if not repeat or eob -! b > 0:
+    fs[result].data = b
+    fs[result].size = eob -! b
+    result += 1
+  fs.setLen(result)
+
+proc msplit*(s: MemSlice, fs: var seq[MemSlice], sep=' ', n=0,
+             repeat=false): int =
+  defSplit(s, fs, n, repeat, sep, cmemchr, charEq)
+
+proc split*(s: Splitr, line: MemSlice, cols: var seq[MemSlice],
+            n=0) {.inline.} =
+  discard msplit(line, cols, s.chrDlm, n, s.repeat)
+
+########################################################################
+# Using lines instead of memSlices & split instead of splitr.split seems
+# to mask the arc problem, as does simplifying `Table` to `seq[char]`.
+
+proc load(path: string, delim=" "): Table[MemSlice, seq[char]] =
+  let f = memfiles.open(path)
+  let splitr = initSplitr(delim)
+  var cols: seq[MemSlice] = @[ ]    # re-used seq buffer
+  var nwSq = newSeqOfCap[char](1)   # re-used seq value
+  nwSq.setLen 1
+  for line in memSlices(f, eat='\0'):
+    stderr.write "loop 1a\n"
+    splitr.split(line, cols, 2)
+    stderr.write "loop 1b; cols: ", cols, "\n"
+    let cs = cast[cstring](cols[0].data)
+    stderr.write "loop 1c\n"        #..reports exception here, but
+    nwSq[0] = cs[0]                 #..actually doing out of bounds here
+    stderr.write "loop 1d\n"
+    result[cols[1]] = nwSq
+
+discard load(getAppDir() / "testfile.txt")
diff --git a/tests/arc/tamodule.nim b/tests/arc/tamodule.nim
new file mode 100644
index 000000000..1412e0a7c
--- /dev/null
+++ b/tests/arc/tamodule.nim
@@ -0,0 +1,9 @@
+discard """
+  output: '''
+abcde
+0
+'''
+  cmd: "nim c --gc:arc $file"
+"""
+
+import amodule
diff --git a/tests/arc/tarc_macro.nim b/tests/arc/tarc_macro.nim
new file mode 100644
index 000000000..33ade1da4
--- /dev/null
+++ b/tests/arc/tarc_macro.nim
@@ -0,0 +1,57 @@
+import macros
+
+var destroyCalled = false
+macro bar() =
+  let s = newTree(nnkAccQuoted, ident"=destroy")
+  # let s = ident"`=destroy`" # this would not work
+  result = quote do:
+    type Foo = object
+    # proc `=destroy`(a: var Foo) = destroyCalled = true # this would not work
+    proc `s`(a: var Foo) = destroyCalled = true
+    block:
+      let a = Foo()
+bar()
+doAssert destroyCalled
+
+# custom `op`
+var destroyCalled2 = false
+macro bar(ident) =
+  var x = 1.5
+  result = quote("@") do:
+    type Foo = object
+    let `@ident` = 0 # custom op interpolated symbols need quoted (``)
+    proc `=destroy`(a: var Foo) =
+      doAssert @x == 1.5
+      doAssert compiles(@x == 1.5)
+      let b1 = @[1,2]
+      let b2 = @@[1,2]
+      doAssert $b1 == "[1, 2]"
+      doAssert $b2 == "@[1, 2]"
+      destroyCalled2 = true
+    block:
+      let a = Foo()
+bar(someident)
+doAssert destroyCalled2
+
+proc `&%`(x: int): int = 1
+proc `&%`(x, y: int): int = 2
+
+macro bar2() =
+  var x = 3
+  result = quote("&%") do:
+    var y = &%x # quoting operator
+    doAssert &%&%y == 1 # unary operator => need to escape
+    doAssert y &% y == 2 # binary operator => no need to escape
+    doAssert y == 3
+bar2()
+
+block:
+  macro foo(a: openArray[string] = []): string =
+    echo a # Segfault doesn't happen if this is removed
+    newLit ""
+
+  proc bar(a: static[openArray[string]] = []) =
+    const tmp = foo(a)
+
+  # bug #22909
+  doAssert not compiles(bar())
diff --git a/tests/arc/tarc_orc.nim b/tests/arc/tarc_orc.nim
new file mode 100644
index 000000000..f2c7de2fc
--- /dev/null
+++ b/tests/arc/tarc_orc.nim
@@ -0,0 +1,186 @@
+discard """
+  targets: "c cpp"
+  matrix: "--mm:arc; --mm:orc"
+"""
+
+block:
+  type
+    PublicKey = array[32, uint8]
+    PrivateKey = array[64, uint8]
+
+  proc ed25519_create_keypair(publicKey: ptr PublicKey; privateKey: ptr PrivateKey) =
+    publicKey[][0] = uint8(88)
+
+  type
+    KeyPair = object
+      public: PublicKey
+      private: PrivateKey
+
+  proc initKeyPair(): KeyPair =
+    ed25519_create_keypair(result.public.addr, result.private.addr)
+
+  let keys = initKeyPair()
+  doAssert keys.public[0] == 88
+
+
+template minIndexByIt: untyped =
+  var other = 3
+  other
+
+proc bug20303() =
+  var hlibs = @["hello", "world", "how", "are", "you"]
+  let res = hlibs[minIndexByIt()]
+  doAssert res == "are"
+
+bug20303()
+
+proc main() = # todo bug with templates
+  block: # bug #11267
+    var a: seq[char] = block: @[]
+    doAssert a == @[]
+    # 2
+    proc b: seq[string] =
+      discard
+      @[]
+    doAssert b() == @[]
+static: main()
+main()
+
+
+type Obj = tuple
+  value: int
+  arr: seq[int]
+
+proc bug(): seq[Obj] =
+  result.add (value: 0, arr: @[])
+  result[^1].value = 1
+  result[^1].arr.add 1
+
+# bug #19990
+let s = bug()
+doAssert s[0] == (value: 1, arr: @[1])
+
+block: # bug #21974
+  type Test[T] = ref object
+      values : seq[T]
+      counter: int
+
+  proc newTest[T](): Test[T] =
+      result         = new(Test[T])
+      result.values  = newSeq[T](16)
+      result.counter = 0
+
+  proc push[T](self: Test[T], value: T) =
+      self.counter += 1
+      if self.counter >= self.values.len:
+          self.values.setLen(self.values.len * 2)
+      self.values[self.counter - 1] = value
+
+  proc pop[T](self: Test[T]): T =
+      result         = self.values[0]
+      self.values[0] = self.values[self.counter - 1] # <--- This line
+      self.counter  -= 1
+
+
+  type X = tuple
+      priority: int
+      value   : string
+
+  var a = newTest[X]()
+  a.push((1, "One"))
+  doAssert a.pop.value == "One"
+
+# bug #21987
+
+type
+  EmbeddedImage* = distinct Image
+  Image = object
+    len: int
+
+proc imageCopy*(image: Image): Image {.nodestroy.}
+
+proc `=destroy`*(x: var Image) =
+  discard
+proc `=sink`*(dest: var Image; source: Image) =
+  `=destroy`(dest)
+  wasMoved(dest)
+
+proc `=dup`*(source: Image): Image {.nodestroy.} =
+  result = imageCopy(source)
+
+proc `=copy`*(dest: var Image; source: Image) =
+  dest = imageCopy(source) # calls =sink implicitly
+
+proc `=destroy`*(x: var EmbeddedImage) = discard
+
+proc `=dup`*(source: EmbeddedImage): EmbeddedImage {.nodestroy.} = source
+
+proc `=copy`*(dest: var EmbeddedImage; source: EmbeddedImage) {.nodestroy.} =
+  dest = source
+
+proc imageCopy*(image: Image): Image =
+  result = image
+
+proc main2 =
+  block:
+    var a = Image(len: 2).EmbeddedImage
+    var b = Image(len: 1).EmbeddedImage
+    b = a
+    doAssert Image(a).len == 2
+    doAssert Image(b).len == 2
+
+  block:
+    var a = Image(len: 2)
+    var b = Image(len: 1)
+    b = a
+    doAssert a.len == 2
+    doAssert b.len == 0
+
+main2()
+
+block:
+  type
+    TestObj = object of RootObj
+      name: string
+    
+    TestSubObj = object of TestObj
+      objname: string
+
+  proc `=destroy`(x: TestObj) =
+    `=destroy`(x.name)
+
+  proc `=destroy`(x: TestSubObj) =
+    `=destroy`(x.objname)
+    `=destroy`(TestObj(x))
+
+  proc testCase() =
+    let t1 {.used.} = TestSubObj(objname: "tso1", name: "to1")
+
+  proc main() =
+    testCase()
+
+  main()
+
+block: # bug #23858
+  type Object = object
+    a: int
+    b: ref int
+  var x = 0
+  proc fn(): auto {.cdecl.} =
+    inc x
+    return Object()
+  discard fn()
+  doAssert x == 1
+
+block: # bug #24147
+  type
+    O = object of RootObj
+      val: string
+    OO = object of O
+
+  proc `=copy`(dest: var O, src: O) =
+      dest.val = src.val
+
+  let oo = OO(val: "hello world")
+  var ooCopy : OO
+  `=copy`(ooCopy, oo)
diff --git a/tests/arc/tarcmisc.nim b/tests/arc/tarcmisc.nim
new file mode 100644
index 000000000..b4476ef4f
--- /dev/null
+++ b/tests/arc/tarcmisc.nim
@@ -0,0 +1,836 @@
+discard """
+  output: '''
+Destructor for TestTestObj
+=destroy called
+123xyzabc
+destroyed: false
+destroyed: false
+destroyed2: false
+destroyed2: false
+destroying variable: 2
+destroying variable: 1
+whiley ends :(
+1
+(x: "0")
+(x: "1")
+(x: "2")
+(x: "3")
+(x: "4")
+(x: "5")
+(x: "6")
+(x: "7")
+(x: "8")
+(x: "9")
+(x: "10")
+0
+new line before - @['a']
+new line after - @['a']
+finalizer
+aaaaa
+hello
+true
+copying
+123
+42
+@["", "d", ""]
+ok
+destroying variable: 20
+destroying variable: 10
+closed
+'''
+  cmd: "nim c --mm:arc --deepcopy:on -d:nimAllocPagesViaMalloc $file"
+"""
+
+block: # bug #23627
+  type
+    TestObj = object of RootObj
+
+    Test2 = object of RootObj
+      foo: TestObj
+
+    TestTestObj = object of RootObj
+      shit: TestObj
+
+  proc `=destroy`(x: TestTestObj) =
+    echo "Destructor for TestTestObj"
+    let test = Test2(foo: TestObj())
+
+  proc testCaseT() =
+    let tt1 {.used.} = TestTestObj(shit: TestObj())
+
+
+  proc main() =
+    testCaseT()
+
+  main()
+
+
+# bug #9401
+
+type
+  MyObj = object
+    len: int
+    data: ptr UncheckedArray[float]
+
+proc `=destroy`*(m: MyObj) =
+
+  echo "=destroy called"
+
+  if m.data != nil:
+    deallocShared(m.data)
+
+type
+  MyObjDistinct = distinct MyObj
+
+proc `=copy`*(m: var MyObj, m2: MyObj) =
+  if m.data == m2.data: return
+  if m.data != nil:
+    `=destroy`(m)
+  m.len = m2.len
+  if m.len > 0:
+    m.data = cast[ptr UncheckedArray[float]](allocShared(sizeof(float) * m.len))
+    copyMem(m.data, m2.data, sizeof(float) * m.len)
+
+
+proc `=sink`*(m: var MyObj, m2: MyObj) =
+  if m.data != m2.data:
+    if m.data != nil:
+      `=destroy`(m)
+    m.len = m2.len
+    m.data = m2.data
+
+proc newMyObj(len: int): MyObj =
+  result.len = len
+  result.data = cast[ptr UncheckedArray[float]](allocShared(sizeof(float) * len))
+
+proc newMyObjDistinct(len: int): MyObjDistinct =
+  MyObjDistinct(newMyObj(len))
+
+proc fooDistinct =
+  doAssert newMyObjDistinct(2).MyObj.len == 2
+
+fooDistinct()
+
+
+proc takeSink(x: sink string): bool = true
+
+proc b(x: sink string): string =
+  if takeSink(x):
+    return x & "abc"
+
+proc bbb(inp: string) =
+  let y = inp & "xyz"
+  echo b(y)
+
+bbb("123")
+
+
+# bug #13691
+type Variable = ref object
+  value: int
+
+proc `=destroy`(self: typeof(Variable()[])) =
+  echo "destroying variable: ",self.value
+
+proc newVariable(value: int): Variable =
+  result = Variable()
+  result.value = value
+  #echo "creating variable: ",result.value
+
+proc test(count: int) =
+  var v {.global.} = newVariable(10)
+
+  var count = count - 1
+  if count == 0: return
+
+  test(count)
+  echo "destroyed: ", v.isNil
+
+test(3)
+
+proc test2(count: int) =
+  block: #XXX: Fails with block currently
+    var v {.global.} = newVariable(20)
+
+    var count = count - 1
+    if count == 0: return
+
+    test2(count)
+    echo "destroyed2: ", v.isNil
+
+test2(3)
+
+proc whiley =
+  var a = newVariable(1)
+  while true:
+    var b = newVariable(2)
+    if true: raise newException(CatchableError, "test")
+
+try:
+  whiley()
+except CatchableError:
+  echo "whiley ends :("
+
+#------------------------------------------------------------------------------
+# issue #13810
+
+import streams
+
+type
+  A = ref AObj
+  AObj = object of RootObj
+    io: Stream
+  B = ref object of A
+    x: int
+
+proc `=destroy`(x: AObj) =
+  close(x.io)
+  echo "closed"
+
+var x = B(io: newStringStream("thestream"))
+
+
+#------------------------------------------------------------------------------
+# issue #14003
+
+proc cryptCTR*(nonce: var openArray[char]) =
+  nonce[1] = 'A'
+
+proc main() =
+  var nonce1 = "0123456701234567"
+  cryptCTR(nonce1)
+  doAssert(nonce1 == "0A23456701234567")
+  var nonce2 = "01234567"
+  cryptCTR(nonce2.toOpenArray(0, nonce2.len-1))
+  doAssert(nonce2 == "0A234567")
+
+main()
+
+# bug #14079
+import std/algorithm
+
+let
+  n = @["c", "b"]
+  q = @[("c", "2"), ("b", "1")]
+
+doAssert n.sortedByIt(it) == @["b", "c"], "fine"
+doAssert q.sortedByIt(it[0]) == @[("b", "1"), ("c", "2")], "fails under arc"
+
+
+#------------------------------------------------------------------------------
+# issue #14236
+
+type
+  MyType = object
+    a: seq[int]
+
+proc re(x: static[string]): static MyType =
+  MyType()
+
+proc match(inp: string, rg: static MyType) =
+  doAssert rg.a.len == 0
+
+match("ac", re"a(b|c)")
+
+#------------------------------------------------------------------------------
+# issue #14243
+
+type
+  Game* = ref object
+
+proc free*(game: Game) =
+  let a = 5
+
+proc newGame*(): Game =
+  new(result, free)
+
+var game*: Game
+
+
+#------------------------------------------------------------------------------
+# issue #14333
+
+type
+  SimpleLoop = object
+
+  Lsg = object
+    loops: seq[ref SimpleLoop]
+    root: ref SimpleLoop
+
+var lsg: Lsg
+lsg.loops.add lsg.root
+echo lsg.loops.len
+
+# bug #14495
+type
+  Gah = ref object
+    x: string
+
+proc bug14495 =
+  var owners: seq[Gah]
+  for i in 0..10:
+    owners.add Gah(x: $i)
+
+  var x: seq[Gah]
+  for i in 0..10:
+    x.add owners[i]
+
+  for i in 0..100:
+    setLen(x, 0)
+    setLen(x, 10)
+
+  for i in 0..x.len-1:
+    if x[i] != nil:
+      echo x[i][]
+
+  for o in owners:
+    echo o[]
+
+bug14495()
+
+# bug #14396
+type
+  Spinny = ref object
+    t: ref int
+    text: string
+
+proc newSpinny*(): Spinny =
+  Spinny(t: new(int), text: "hello")
+
+proc spinnyLoop(x: ref int, spinny: sink Spinny) =
+  echo x[]
+
+proc start*(spinny: sink Spinny) =
+  spinnyLoop(spinny.t, spinny)
+
+var spinner1 = newSpinny()
+spinner1.start()
+
+# bug #14345
+
+type
+  SimpleLoopB = ref object
+    children: seq[SimpleLoopB]
+    parent: SimpleLoopB
+
+proc addChildLoop(self: SimpleLoopB, loop: SimpleLoopB) =
+  self.children.add loop
+
+proc setParent(self: SimpleLoopB, parent: SimpleLoopB) =
+  self.parent = parent
+  self.parent.addChildLoop(self)
+
+var l = SimpleLoopB()
+l.setParent(l)
+
+
+# bug #14968
+import times
+let currentTime = now().utc
+
+
+# bug #14994
+import sequtils
+var newLine = @['a']
+let indent = newSeq[char]()
+
+echo "new line before - ", newline
+
+newline.insert(indent, 0)
+
+echo "new line after - ", newline
+
+# bug #15044
+
+type
+  Test = ref object
+
+proc test: Test =
+  # broken
+  new(result, proc(x: Test) =
+    echo "finalizer"
+  )
+
+proc tdirectFinalizer =
+  discard test()
+
+tdirectFinalizer()
+
+
+# bug #14480
+proc hello(): int =
+  result = 42
+
+var leaves {.global.} = hello()
+doAssert leaves == 42
+
+# bug #15052
+
+proc mutstrings =
+  var data = "hello"
+  for c in data.mitems():
+    c = 'a'
+  echo data
+
+mutstrings()
+
+# bug #15038
+
+type
+  Machine = ref object
+    hello: string
+
+var machineTypes: seq[tuple[factory: proc(): Machine]]
+
+proc registerMachine(factory: proc(): Machine) =
+  var mCreator = proc(): Machine =
+    result = factory()
+
+  machineTypes.add((factory: mCreator))
+
+proc facproc(): Machine =
+  result = Machine(hello: "hello")
+
+registerMachine(facproc)
+
+proc createMachine =
+  for machine in machineTypes:
+    echo machine.factory().hello
+
+createMachine()
+
+# bug #15122
+
+import tables
+
+type
+  BENodeKind = enum
+    tkBytes,
+    tkList,
+    tkDict
+
+  BENode = object
+    case kind: BENodeKind
+    of tkBytes: strVal: string
+    of tkList: listVal: seq[BENode]
+    of tkDict: dictVal: Table[string, BENode]
+
+var data = {
+  "examples": {
+    "values": BENode(
+      kind: tkList,
+      listVal: @[BENode(kind: tkBytes, strVal: "test")]
+    )
+  }.toTable()
+}.toTable()
+
+# For ARC listVal is empty for some reason
+doAssert data["examples"]["values"].listVal[0].strVal == "test"
+
+
+
+
+###############################################################################
+# bug #15405
+import parsexml
+const test_xml_str = "<A><B>value</B></A>"
+var stream = newStringStream(test_xml_str)
+var xml: XmlParser
+open(xml, stream, "test")
+var xml2 = deepCopy(xml)
+
+proc text_parser(xml: var XmlParser) =
+  var test_passed = false
+  while true:
+    xml.next()
+    case xml.kind
+    of xmlElementStart:
+      if xml.elementName == "B":
+        xml.next()
+        if xml.kind == xmlCharData and xml.charData == "value":
+          test_passed = true
+
+    of xmlEof: break
+    else: discard
+  xml.close()
+  doAssert(test_passed)
+
+text_parser(xml)
+text_parser(xml2)
+
+# bug #15599
+type
+  PixelBuffer = ref object
+
+proc newPixelBuffer(): PixelBuffer =
+  new(result) do (buffer: PixelBuffer):
+    echo "ok"
+
+discard newPixelBuffer()
+
+
+# bug #17199
+
+proc passSeq(data: seq[string]) =
+  # used the system.& proc initially
+  let wat = data & "hello"
+
+proc test2 =
+  let name = @["hello", "world"]
+  passSeq(name)
+  doAssert name == @["hello", "world"]
+
+static: test2() # was buggy
+test2()
+
+proc merge(x: sink seq[string], y: sink string): seq[string] =
+  newSeq(result, x.len + 1)
+  for i in 0..x.len-1:
+    result[i] = move(x[i])
+  result[x.len] = move(y)
+
+proc passSeq2(data: seq[string]) =
+  # used the system.& proc initially
+  let wat = merge(data, "hello")
+
+proc test3 =
+  let name = @["hello", "world"]
+  passSeq2(name)
+  doAssert name == @["hello", "world"]
+
+static: test3() # was buggy
+test3()
+
+# bug #17712
+proc t17712 =
+  var ppv = new int
+  discard @[ppv]
+  var el: ref int
+  el = [ppv][0]
+  echo el != nil
+
+t17712()
+
+# bug #18030
+
+type
+  Foo = object
+    n: int
+
+proc `=copy`(dst: var Foo, src: Foo) =
+  echo "copying"
+  dst.n = src.n
+
+proc `=sink`(dst: var Foo, src: Foo) =
+  echo "sinking"
+  dst.n = src.n
+
+var a: Foo
+
+proc putValue[T](n: T)
+
+proc useForward =
+  putValue(123)
+
+proc putValue[T](n: T) =
+  var b = Foo(n:n)
+  a = b
+  echo b.n
+
+useForward()
+
+
+# bug #17319
+type
+  BrokenObject = ref object
+    brokenType: seq[int]
+
+proc use(obj: BrokenObject) =
+  discard
+
+method testMethod(self: BrokenObject) {.base.} =
+  iterator testMethodIter() {.closure.} =
+    use(self)
+
+  var nameIterVar = testMethodIter
+  nameIterVar()
+
+let mikasa = BrokenObject()
+mikasa.testMethod()
+
+# bug #19205
+type
+  InputSectionBase* = object of RootObj
+    relocations*: seq[int]   # traced reference. string has a similar SIGSEGV.
+  InputSection* = object of InputSectionBase
+
+proc fooz(sec: var InputSectionBase) =
+  if sec of InputSection:  # this line SIGSEGV.
+    echo 42
+
+var sec = create(InputSection)
+sec[] = InputSection(relocations: newSeq[int]())
+fooz sec[]
+
+block:
+  type
+    Data = ref object
+      id: int
+  proc main =
+    var x = Data(id: 99)
+    var y = x
+    x[] = Data(id: 778)[]
+    doAssert y.id == 778
+    doAssert x[].id == 778
+  main()
+
+block: # bug #19857
+  type
+    ValueKind = enum VNull, VFloat, VObject # need 3 elements. Cannot remove VNull or VObject
+
+    Value = object
+      case kind: ValueKind
+      of VFloat: fnum: float
+      of VObject: tab: Table[int, int] # OrderedTable[T, U] also makes it fail.
+                                      # "simpler" types also work though
+      else: discard # VNull can be like this, but VObject must be filled
+
+    # required. Pure proc works
+    FormulaNode = proc(c: OrderedTable[string, int]): Value
+
+  proc toF(v: Value): float =
+    doAssert v.kind == VFloat
+    case v.kind
+    of VFloat: result = v.fnum
+    else: discard
+
+
+  proc foo() =
+    let fuck = initOrderedTable[string, int]()
+    proc cb(fuck: OrderedTable[string, int]): Value =
+                            # works:
+                            #result = Value(kind: VFloat, fnum: fuck["field_that_does_not_exist"].float)
+                            # broken:
+      discard "actuall runs!"
+      let t = fuck["field_that_does_not_exist"]
+      echo "never runs, but we crash after! ", t
+
+    doAssertRaises(KeyError):
+      let fn = FormulaNode(cb)
+      let v = fn(fuck)
+      #echo v
+      let res = v.toF()
+
+  foo()
+
+import std/options
+
+# bug #21592
+type Event* = object
+  code*: string
+
+type App* = ref object of RootObj
+  id*: string
+
+method process*(self: App): Option[Event] {.base.} =
+  raise Exception.new_exception("not impl")
+
+# bug #21617
+type Test2 = ref object of RootObj
+
+method bug(t: Test2): seq[float] {.base.} = discard
+
+block: # bug #22664
+  type
+    ElementKind = enum String, Number
+    Element = object
+      case kind: ElementKind
+      of String:
+        str: string
+      of Number:
+        num: float
+    Calc = ref object
+      stack: seq[Element]
+
+  var calc = new Calc
+
+  calc.stack.add Element(kind: Number, num: 200.0)
+  doAssert $calc.stack == "@[(kind: Number, num: 200.0)]"
+  let calc2 = calc
+  calc2.stack = calc.stack # This nulls out the object in the stack
+  doAssert $calc.stack == "@[(kind: Number, num: 200.0)]"
+  doAssert $calc2.stack == "@[(kind: Number, num: 200.0)]"
+
+block: # bug #19250
+  type
+    Bar[T] = object
+      err: proc(): string
+
+    Foo[T] = object
+      run: proc(): Bar[T]
+
+  proc bar[T](err: proc(): string): Bar[T] =
+    assert not err.isNil
+    Bar[T](err: err)
+
+  proc foo(): Foo[char] = 
+    result.run = proc(): Bar[char] =
+      # works
+      # result = Bar[char](err: proc(): string = "x")
+      # not work
+      result = bar[char](proc(): string = "x")
+
+  proc bug[T](fs: Foo[T]): Foo[T] =
+    result.run = proc(): Bar[T] =
+      let res = fs.run()
+      
+      # works
+      # var errors = @[res.err] 
+      
+      # not work
+      var errors: seq[proc(): string]
+      errors.add res.err
+      
+      return bar[T] do () -> string:
+        for err in errors:
+          result.add res.err()
+
+  doAssert bug(foo()).run().err() == "x"
+
+block: # bug #22259
+  type
+    ProcWrapper = tuple
+      p: proc() {.closure.}
+
+
+  proc f(wrapper: ProcWrapper) =
+    let s = @[wrapper.p]
+    let a = [wrapper.p]
+
+  proc main =
+    # let wrapper: ProcWrapper = ProcWrapper(p: proc {.closure.} = echo 10)
+    let wrapper: ProcWrapper = (p: proc {.closure.} = echo 10)
+    f(wrapper)
+
+  main()
+
+block:
+  block: # bug #22923
+    block:
+      let
+        a: int = 100
+        b: int32 = 200'i32
+
+      let
+        x = arrayWith(a, 8) # compiles
+        y = arrayWith(b, 8) # internal error
+        z = arrayWith(14, 8) # integer literal also results in a crash
+
+      doAssert x == [100, 100, 100, 100, 100, 100, 100, 100]
+      doAssert $y == "[200, 200, 200, 200, 200, 200, 200, 200]"
+      doAssert z == [14, 14, 14, 14, 14, 14, 14, 14]
+
+    block:
+      let a: string = "nim"
+      doAssert arrayWith(a, 3) == ["nim", "nim", "nim"]
+
+      let b: char = 'c'
+      doAssert arrayWith(b, 3) == ['c', 'c', 'c']
+
+      let c: uint = 300'u
+      doAssert $arrayWith(c, 3) == "[300, 300, 300]"
+
+block: # bug #23505
+  type
+    K = object
+    C = object
+      value: ptr K
+
+  proc init(T: type C): C =
+    let tmp = new K
+    C(value: addr tmp[])
+
+  discard init(C)
+
+block: # bug #23524
+  type MyType = object
+    a: int
+
+  proc `=destroy`(typ: MyType) = discard
+
+  var t1 = MyType(a: 100)
+  var t2 = t1 # Should be a copy?
+
+  proc main() =
+    t2 = t1
+    doAssert t1.a == 100
+    doAssert t2.a == 100
+
+  main()
+
+block: # bug #23907
+  type
+    Thingy = object
+      value: int
+
+    ExecProc[C] = proc(value: sink C): int {.nimcall.}
+
+  proc `=copy`(a: var Thingy, b: Thingy) {.error.}
+
+  var thingyDestroyCount = 0
+
+  proc `=destroy`(thingy: Thingy) =
+    assert(thingyDestroyCount <= 0)
+    thingyDestroyCount += 1
+
+  proc store(value: sink Thingy): int =
+    result = value.value
+
+  let callback: ExecProc[Thingy] = store
+
+  doAssert callback(Thingy(value: 123)) == 123
+
+import std/strutils
+
+block: # bug #23974
+  func g(e: seq[string]): lent seq[string] = result = e
+  proc k(f: string): seq[string] = f.split("/")
+  proc n() =
+    const r = "/d/"
+    let t =
+      if true:
+        k(r).g()
+      else:
+        k("/" & r).g()
+    echo t
+
+  n()
+
+block: # bug #23973
+  func g(e: seq[string]): lent seq[string] = result = e
+  proc k(f: string): seq[string] = f.split("/")
+  proc n() =
+    const r = "/test/empty"  # or "/test/empty/1"
+    let a = k(r).g()
+    let t =
+      if true:
+        k(r).g()
+      else:
+        k("/" & r).g()   # or raiseAssert ""
+    doAssert t == a
+
+  n()
+
+block: # bug #24141
+  func reverse(s: var openArray[char]) =
+    s[0] = 'f'
+
+  func rev(s: var string) =
+    s.reverse
+
+  proc main =
+    var abc = "abc"
+    abc.rev
+    doAssert abc == "fbc"
+
+  main()
diff --git a/tests/arc/tasyncawait.nim b/tests/arc/tasyncawait.nim
new file mode 100644
index 000000000..91a7453cd
--- /dev/null
+++ b/tests/arc/tasyncawait.nim
@@ -0,0 +1,68 @@
+discard """
+  outputsub: "result: 5000"
+  cmd: "nim c --gc:orc $file"
+"""
+
+import asyncdispatch, asyncnet, nativesockets, net, strutils
+from stdtest/netutils import bindAvailablePort
+
+var msgCount = 0
+
+const
+  swarmSize = 50
+  messagesToSend = 100
+
+var clientCount = 0
+
+proc sendMessages(client: AsyncFD) {.async.} =
+  for i in 0 ..< messagesToSend:
+    await send(client, "Message " & $i & "\c\L")
+
+proc launchSwarm(port: Port) {.async.} =
+  for i in 0 ..< swarmSize:
+    var sock = createAsyncNativeSocket()
+
+    await connect(sock, "localhost", port)
+    await sendMessages(sock)
+    closeSocket(sock)
+
+proc readMessages(client: AsyncFD) {.async.} =
+  # wrapping the AsyncFd into a AsyncSocket object
+  var sockObj = newAsyncSocket(client)
+  var (ipaddr, port) = sockObj.getPeerAddr()
+  doAssert ipaddr == "127.0.0.1"
+  (ipaddr, port) = sockObj.getLocalAddr()
+  doAssert ipaddr == "127.0.0.1"
+  while true:
+    var line = await recvLine(sockObj)
+    if line == "":
+      closeSocket(client)
+      clientCount.inc
+      break
+    else:
+      if line.startsWith("Message "):
+        msgCount.inc
+      else:
+        doAssert false
+
+proc createServer(server: AsyncFD) {.async.} =
+  discard server.SocketHandle.listen()
+  while true:
+    asyncCheck readMessages(await accept(server))
+
+proc main =
+  let server = createAsyncNativeSocket()
+  let port = bindAvailablePort(server.SocketHandle)
+  asyncCheck createServer(server)
+  asyncCheck launchSwarm(port)
+  while true:
+    poll()
+    if clientCount == swarmSize: break
+
+let mem = getOccupiedMem()
+main()
+
+doAssert msgCount == swarmSize * messagesToSend
+echo "result: ", msgCount
+GC_fullCollect()
+echo "memory: ", formatSize(getOccupiedMem() - mem)
diff --git a/tests/arc/tasyncleak.nim b/tests/arc/tasyncleak.nim
new file mode 100644
index 000000000..8e3a7b3e7
--- /dev/null
+++ b/tests/arc/tasyncleak.nim
@@ -0,0 +1,21 @@
+discard """
+  outputsub: "(allocCount: 4050, deallocCount: 4048)"
+  cmd: "nim c --gc:orc -d:nimAllocStats $file"
+"""
+
+import asyncdispatch
+# bug #15076
+const
+  # Just to occupy some RAM
+  BigData = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+
+proc doNothing(): Future[void] {.async.} =
+  discard
+
+proc main(): Future[void] {.async.} =
+  for x in 0 .. 1_000:
+    await doNothing()
+
+waitFor main()
+GC_fullCollect()
+echo getAllocStats()
diff --git a/tests/arc/tasyncleak2.nim b/tests/arc/tasyncleak2.nim
new file mode 100644
index 000000000..87d7e73f9
--- /dev/null
+++ b/tests/arc/tasyncleak2.nim
@@ -0,0 +1,88 @@
+discard """
+  output: "success"
+  cmd: "nim c --gc:orc $file"
+"""
+
+# issue #15076
+import deques, strutils, asyncdispatch
+
+proc doNothing(): Future[void] =
+  #[
+  var
+    :env
+    :env_1
+  try:
+    `=destroy`(:env)
+    internalNew(:env)
+    `=sink`(:env.retFuture1, newFuture("doNothing"))
+
+    `=destroy_1`(:env_1)
+    internalNew(:env_1)
+    `=`(:env_1.:up, :env)
+    `=sink_1`(:env.nameIterVar2, (doNothingIter, :env_1))
+
+    (doNothingNimAsyncContinue, :env)()
+    return `=_1`(result, :env.retFuture1)
+  finally:
+    `=destroy`(:env)
+  ]#
+
+  var retFuture = newFuture[void]("doNothing")
+  iterator doNothingIter(): FutureBase {.closure.} =
+    # inspected ARC code: looks correct!
+    block:
+      var qqq = initDeque[string]()
+      for i in 0 .. 1000:
+        qqq.addLast($i)
+    complete(retFuture) # env.up.retFuture1
+
+  var nameIterVar = doNothingIter  # iter_Env -> retFuture ->
+
+  proc doNothingNimAsyncContinue() {.closure.} =
+    # inspected ARC code: looks correct
+    if not nameIterVar.finished:
+      var next_gensym0 = nameIterVar()
+      while (not next_gensym0.isNil) and next_gensym0.finished:
+        next_gensym0 = nameIterVar()
+        if nameIterVar.finished:
+          break
+      if next_gensym0 != nil:
+        {.gcsafe.}:
+          next_gensym0.addCallback cast[proc () {.closure, gcsafe.}](doNothingNimAsyncContinue)
+
+  doNothingNimAsyncContinue()
+  return retFuture
+
+proc main(): Future[void] =
+  template await[T](f_gensym12: Future[T]): auto {.used.} =
+    var internalTmpFuture_gensym12: FutureBase = f_gensym12
+    yield internalTmpFuture_gensym12
+    (cast[typeof(f_gensym12)](internalTmpFuture_gensym12)).read()
+
+  var retFuture = newFuture[void]("main")
+  iterator mainIter(): FutureBase {.closure.} =
+    block:
+      for x in 0 .. 1000:
+        await doNothing()
+    complete(retFuture)
+
+  var nameIterVar_gensym11 = mainIter
+  proc mainNimAsyncContinue() {.closure.} =
+    if not nameIterVar_gensym11.finished:
+      var next_gensym11 = unown nameIterVar_gensym11()
+      while (not next_gensym11.isNil) and next_gensym11.finished:
+        next_gensym11 = unown nameIterVar_gensym11()
+        if nameIterVar_gensym11.finished:
+          break
+      if next_gensym11 != nil:
+        {.gcsafe.}:
+          next_gensym11.addCallback cast[proc () {.closure, gcsafe.}](mainNimAsyncContinue)
+
+  mainNimAsyncContinue()
+  return retFuture
+
+for i in 0..9:
+  waitFor main()
+  GC_fullCollect()
+  doAssert getOccupiedMem() < 1024
+echo "success"
diff --git a/tests/arc/tasyncleak3.nim b/tests/arc/tasyncleak3.nim
new file mode 100644
index 000000000..21e963b7f
--- /dev/null
+++ b/tests/arc/tasyncleak3.nim
@@ -0,0 +1,47 @@
+discard """
+  cmd: "nim c --gc:orc -d:useMalloc $file"
+  output: "true"
+  valgrind: "true"
+"""
+
+import strutils
+
+type
+  FutureBase* = ref object of RootObj  ## Untyped future.
+    finished: bool
+    stackTrace: seq[StackTraceEntry] ## For debugging purposes only.
+
+proc newFuture*(): FutureBase =
+  new(result)
+  result.finished = false
+  result.stackTrace = getStackTraceEntries()
+
+type
+  PDispatcher {.acyclic.} = ref object
+
+var gDisp: PDispatcher
+new gDisp
+
+proc testCompletion(): FutureBase =
+  var retFuture = newFuture()
+
+  iterator testCompletionIter(): FutureBase {.closure.} =
+    retFuture.finished = true
+    when not defined(nobug):
+      let disp = gDisp # even worse memory consumption this way...
+
+  var nameIterVar = testCompletionIter
+  proc testCompletionNimAsyncContinue() {.closure.} =
+    if not nameIterVar.finished:
+      discard nameIterVar()
+  testCompletionNimAsyncContinue()
+  return retFuture
+
+proc main =
+  for i in 0..10_000:
+    discard testCompletion()
+
+main()
+
+GC_fullCollect()
+echo getOccupiedMem() < 1024
diff --git a/tests/arc/tasyncleak4.nim b/tests/arc/tasyncleak4.nim
new file mode 100644
index 000000000..58cd7f0b7
--- /dev/null
+++ b/tests/arc/tasyncleak4.nim
@@ -0,0 +1,21 @@
+discard """
+  cmd: "nim c --gc:orc -d:useMalloc $file"
+  output: '''ok'''
+  valgrind: "leaks"
+"""
+
+# bug #15076
+import asyncdispatch
+
+var futures: seq[Future[void]]
+
+for i in 1..20:
+  futures.add sleepAsync 1
+  futures.add sleepAsync 1
+
+  futures.all.waitFor()
+  futures.setLen 0
+
+setGlobalDispatcher nil
+GC_fullCollect()
+echo "ok"
diff --git a/tests/arc/tasyncorc.nim b/tests/arc/tasyncorc.nim
new file mode 100644
index 000000000..63703b559
--- /dev/null
+++ b/tests/arc/tasyncorc.nim
@@ -0,0 +1,26 @@
+discard """
+  output: '''230000'''
+  cmd: '''nim c --gc:orc -d:useMalloc $file'''
+  valgrind: "leaks"
+"""
+
+# bug #14402
+
+import asynchttpserver, asyncdispatch, httpclient, strutils
+
+proc cb(req: Request) {.async, gcsafe.} =
+  const html = " ".repeat(230000)
+  await req.respond(Http200, html)
+
+var server = newAsyncHttpServer()
+asyncCheck server.serve(Port(8080), cb)
+
+proc test {.async.} =
+  var
+    client = newAsyncHttpClient()
+    resp = await client.get("http://localhost:8080")
+
+  let x = (await resp.body).len
+  echo x # crash
+
+waitFor test()
diff --git a/tests/arc/tcaseobj.nim b/tests/arc/tcaseobj.nim
new file mode 100644
index 000000000..3499f5c1e
--- /dev/null
+++ b/tests/arc/tcaseobj.nim
@@ -0,0 +1,366 @@
+discard """
+  valgrind: true
+  cmd: "nim c --gc:arc -d:useMalloc $file"
+  output: '''myobj destroyed
+myobj destroyed
+myobj destroyed
+A
+B
+begin
+end
+prevented
+(ok: true, value: "ok")
+@[(kind: P, pChildren: @[])]
+myobj destroyed
+'''
+"""
+
+# bug #13102
+
+type
+  D = ref object
+  R = object
+    case o: bool
+    of false:
+      discard
+    of true:
+      field: D
+
+iterator things(): R =
+  when true:
+    var
+      unit = D()
+    while true:
+      yield R(o: true, field: unit)
+  else:
+    while true:
+      var
+        unit = D()
+      yield R(o: true, field: unit)
+
+proc main =
+  var i = 0
+  for item in things():
+    discard item.field
+    inc i
+    if i == 2: break
+
+main()
+
+# bug #13149
+
+type
+  TMyObj = object
+    p: pointer
+    len: int
+
+proc `=destroy`(o: var TMyObj) =
+  if o.p != nil:
+    dealloc o.p
+    o.p = nil
+    echo "myobj destroyed"
+
+proc `=copy`(dst: var TMyObj, src: TMyObj) =
+  `=destroy`(dst)
+  dst.p = alloc(src.len)
+  dst.len = src.len
+
+proc `=sink`(dst: var TMyObj, src: TMyObj) =
+  `=destroy`(dst)
+  dst.p = src.p
+  dst.len = src.len
+
+type
+  TObjKind = enum Z, A, B
+  TCaseObj = object
+    case kind: TObjKind
+    of Z: discard
+    of A:
+      x1: int # this int plays important role
+      x2: TMyObj
+    of B:
+      y: TMyObj
+
+proc testSinks: TCaseObj =
+  result = TCaseObj(kind: A, x1: 5000, x2: TMyObj(len: 5, p: alloc(5)))
+  result = TCaseObj(kind: B, y: TMyObj(len: 3, p: alloc(3)))
+
+proc use(x: TCaseObj) = discard
+
+proc testCopies(i: int) =
+  var a: array[2, TCaseObj]
+  a[i] = TCaseObj(kind: A, x1: 5000, x2: TMyObj(len: 5, p: alloc(5)))
+  a[i+1] = a[i] # copy, cannot move
+  use(a[i])
+
+let x1 = testSinks()
+testCopies(0)
+
+# bug #12957
+
+type
+  PegKind* = enum
+    pkCharChoice,
+    pkSequence
+  Peg* = object ## type that represents a PEG
+    case kind: PegKind
+    of pkCharChoice: charChoice: ref set[char]
+    else: discard
+    sons: seq[Peg]
+
+proc charSet*(s: set[char]): Peg =
+  ## constructs a PEG from a character set `s`
+  result = Peg(kind: pkCharChoice)
+  new(result.charChoice)
+  result.charChoice[] = s
+
+proc len(a: Peg): int {.inline.} = return a.sons.len
+proc myadd(d: var Peg, s: Peg) {.inline.} = add(d.sons, s)
+
+proc sequence*(a: openArray[Peg]): Peg =
+  result = Peg(kind: pkSequence, sons: @[])
+  when false:
+    #works too:
+    result.myadd(a[0])
+    result.myadd(a[1])
+  for x in items(a):
+    # works:
+    #result.sons.add(x)
+    # fails:
+    result.myadd x
+  if result.len == 1:
+    result = result.sons[0] # this must not move!
+
+when true:
+  # bug #12957
+
+  proc p =
+    echo "A"
+    let x = sequence([charSet({'a'..'z', 'A'..'Z', '_'}),
+              charSet({'a'..'z', 'A'..'Z', '0'..'9', '_'})])
+    echo "B"
+  p()
+
+  proc testSubObjAssignment =
+    echo "begin"
+    # There must be extactly one element in the array constructor!
+    let x = sequence([charSet({'a'..'z', 'A'..'Z', '_'})])
+    echo "end"
+  testSubObjAssignment()
+
+
+#------------------------------------------------
+
+type
+  MyObject = object
+    x1: string
+    case kind1: bool
+      of false: y1: string
+      of true:
+          y2: seq[string]
+          case kind2: bool
+              of true: z1: string
+              of false:
+                z2: seq[string]
+                flag: bool
+    x2: string
+
+proc test_myobject =
+  var x: MyObject
+  x.x1 = "x1"
+  x.x2 = "x2"
+  x.y1 = "ljhkjhkjh"
+  {.cast(uncheckedAssign).}:
+    x.kind1 = true
+  x.y2 = @["1", "2"]
+  {.cast(uncheckedAssign).}:
+    x.kind2 = true
+  x.z1 = "yes"
+  {.cast(uncheckedAssign).}:
+    x.kind2 = false
+  x.z2 = @["1", "2"]
+  {.cast(uncheckedAssign).}:
+    x.kind2 = true
+  x.z1 = "yes"
+  x.kind2 = true # should be no effect
+  doAssert(x.z1 == "yes")
+  {.cast(uncheckedAssign).}:
+    x.kind2 = false
+  {.cast(uncheckedAssign).}:
+    x.kind1 = x.kind2 # support self assignment with effect
+
+  try:
+    x.kind1 = x.flag # flag is not accesible
+  except FieldDefect:
+    echo "prevented"
+
+  doAssert(x.x1 == "x1")
+  doAssert(x.x2 == "x2")
+
+
+test_myobject()
+
+
+#------------------------------------------------
+# bug #14244
+
+type
+  RocksDBResult*[T] = object
+    case ok*: bool
+    of true:
+      value*: T
+    else:
+      error*: string
+
+proc init(): RocksDBResult[string] =
+  {.cast(uncheckedAssign).}:
+    result.ok = true
+    result.value = "ok"
+
+echo init()
+
+
+#------------------------------------------------
+# bug #14312
+
+type MyObj = object
+  case kind: bool
+    of false: x0: int # would work with a type like seq[int]; value would be reset
+    of true: x1: string
+
+var a = MyObj(kind: false, x0: 1234)
+{.cast(uncheckedAssign).}:
+  a.kind = true
+doAssert(a.x1 == "")
+
+block:
+  # bug #15532
+  type Kind = enum
+    k0, k1
+
+  type Foo = object
+    y: int
+    case kind: Kind
+    of k0: x0: int
+    of k1: x1: int
+
+  const j0 = Foo(y: 1, kind: k0, x0: 2)
+  const j1 = Foo(y: 1, kind: k1, x1: 2)
+
+  doAssert j0.y == 1
+  doAssert j0.kind == k0
+  doAssert j1.kind == k1
+
+  doAssert j1.x1 == 2
+  doAssert j0.x0 == 2
+
+# ------------------------------------
+# bug #20305
+
+type
+  ContentNodeKind = enum
+    P, Br, Text
+  ContentNode = object
+    case kind: ContentNodeKind
+    of P: pChildren: seq[ContentNode]
+    of Br: discard
+    of Text: textStr: string
+
+proc bug20305 =
+  var x = ContentNode(kind: P, pChildren: @[
+    ContentNode(kind: P, pChildren: @[ContentNode(kind: Text, textStr: "brrr")])
+  ])
+  x.pChildren.add ContentNode(kind: Br)
+  x.pChildren.del(0)
+  {.cast(uncheckedAssign).}:
+    x.pChildren[0].kind = P
+  echo x.pChildren
+
+bug20305()
+
+# bug #21023
+block:
+  block:
+    type
+      MGErrorKind = enum
+        mgeUnexpected, mgeNotFound
+
+    type Foo = object
+      kind: MGErrorKind
+      ex: Exception
+
+    type Boo = object
+      a: seq[int]
+
+    type
+      Result2 = object
+        case o: bool
+        of false:
+          e: Foo
+        of true:
+          v: Boo
+
+    proc startSessionSync(): Result2 =
+      return Result2(o: true)
+
+    proc mainSync =
+      let ff = startSessionSync()
+      doAssert ff.o == true
+
+    mainSync()
+
+  block:
+    type
+      MGErrorKind = enum
+        mgeUnexpected, mgeNotFound
+
+    type Foo = object
+      kind: MGErrorKind
+      ex: Exception
+
+    type Boo = object
+      a: seq[int]
+
+    type
+      Result2 = object
+        case o: bool
+        of false:
+          e: Foo
+        of true:
+          v: Boo
+          s: int
+
+    proc startSessionSync(): Result2 =
+      return Result2(o: true, s: 12)
+
+    proc mainSync =
+      let ff = startSessionSync()
+      doAssert ff.s == 12
+
+    mainSync()
+
+import std/sequtils
+
+# bug #23690
+type
+  SomeObj* = object of RootObj
+
+  Item* = object
+    case kind*: 0..1
+    of 0:
+      a*: int
+      b*: SomeObj
+    of 1:
+      c*: string
+
+  ItemExt* = object
+    a*: Item
+    b*: string
+
+proc do1(x: int): seq[(string, Item)] =
+  result = @[("zero", Item(kind: 1, c: "first"))]
+
+proc do2(x: int, e: ItemExt): seq[(string, ItemExt)] =
+  do1(x).map(proc(v: (string, Item)): auto = (v[0], ItemExt(a: v[1], b: e.b)))
+
+doAssert $do2(0, ItemExt(a: Item(kind: 1, c: "second"), b: "third")) == """@[("zero", (a: (kind: 1, c: "first"), b: "third"))]"""
diff --git a/tests/arc/tcaseobjcopy.nim b/tests/arc/tcaseobjcopy.nim
new file mode 100644
index 000000000..fb26a4973
--- /dev/null
+++ b/tests/arc/tcaseobjcopy.nim
@@ -0,0 +1,253 @@
+discard """
+  valgrind: true
+  cmd: "nim c --gc:arc -d:useMalloc $file"
+  output: '''myobj destroyed
+myobj destroyed
+myobj destroyed
+A
+B
+begin
+end
+prevented
+(ok: true, value: "ok")
+myobj destroyed
+'''
+"""
+
+# bug #13102
+
+type
+  D = ref object
+  R = object
+    case o: bool
+    of false:
+      discard
+    of true:
+      field: D
+
+iterator things(): R =
+  when true:
+    var
+      unit = D()
+    while true:
+      yield R(o: true, field: unit)
+  else:
+    while true:
+      var
+        unit = D()
+      yield R(o: true, field: unit)
+
+proc main =
+  var i = 0
+  for item in things():
+    discard item.field
+    inc i
+    if i == 2: break
+
+main()
+
+# bug #13149
+
+type
+  TMyObj = object
+    p: pointer
+    len: int
+
+proc `=destroy`(o: var TMyObj) =
+  if o.p != nil:
+    dealloc o.p
+    o.p = nil
+    echo "myobj destroyed"
+
+proc `=copy`(dst: var TMyObj, src: TMyObj) =
+  `=destroy`(dst)
+  dst.p = alloc(src.len)
+  dst.len = src.len
+
+proc `=sink`(dst: var TMyObj, src: TMyObj) =
+  `=destroy`(dst)
+  dst.p = src.p
+  dst.len = src.len
+
+type
+  TObjKind = enum Z, A, B
+  TCaseObj = object
+    case kind: TObjKind
+    of Z: discard
+    of A:
+      x1: int # this int plays important role
+      x2: TMyObj
+    of B:
+      y: TMyObj
+
+proc testSinks: TCaseObj =
+  result = TCaseObj(kind: A, x1: 5000, x2: TMyObj(len: 5, p: alloc(5)))
+  result = TCaseObj(kind: B, y: TMyObj(len: 3, p: alloc(3)))
+
+proc use(x: TCaseObj) = discard
+
+proc testCopies(i: int) =
+  var a: array[2, TCaseObj]
+  a[i] = TCaseObj(kind: A, x1: 5000, x2: TMyObj(len: 5, p: alloc(5)))
+  a[i+1] = a[i] # copy, cannot move
+  use(a[i])
+
+let x1 = testSinks()
+testCopies(0)
+
+# bug #12957
+
+type
+  PegKind* = enum
+    pkCharChoice,
+    pkSequence
+  Peg* = object ## type that represents a PEG
+    case kind: PegKind
+    of pkCharChoice: charChoice: ref set[char]
+    else: discard
+    sons: seq[Peg]
+
+proc charSet*(s: set[char]): Peg =
+  ## constructs a PEG from a character set `s`
+  result = Peg(kind: pkCharChoice)
+  new(result.charChoice)
+  result.charChoice[] = s
+
+proc len(a: Peg): int {.inline.} = return a.sons.len
+proc myadd(d: var Peg, s: Peg) {.inline.} = add(d.sons, s)
+
+proc sequence*(a: openArray[Peg]): Peg =
+  result = Peg(kind: pkSequence, sons: @[])
+  when false:
+    #works too:
+    result.myadd(a[0])
+    result.myadd(a[1])
+  for x in items(a):
+    # works:
+    #result.sons.add(x)
+    # fails:
+    result.myadd x
+  if result.len == 1:
+    result = result.sons[0] # this must not move!
+
+when true:
+  # bug #12957
+
+  proc p =
+    echo "A"
+    let x = sequence([charSet({'a'..'z', 'A'..'Z', '_'}),
+              charSet({'a'..'z', 'A'..'Z', '0'..'9', '_'})])
+    echo "B"
+  p()
+
+  proc testSubObjAssignment =
+    echo "begin"
+    # There must be extactly one element in the array constructor!
+    let x = sequence([charSet({'a'..'z', 'A'..'Z', '_'})])
+    echo "end"
+  testSubObjAssignment()
+
+
+#------------------------------------------------
+
+type
+  MyObject = object
+    x1: string
+    case kind1: bool
+      of false: y1: string
+      of true:
+          y2: seq[string]
+          case kind2: bool
+              of true: z1: string
+              of false:
+                z2: seq[string]
+                flag: bool
+    x2: string
+
+proc test_myobject =
+  var x: MyObject
+  x.x1 = "x1"
+  x.x2 = "x2"
+  x.y1 = "ljhkjhkjh"
+  {.cast(uncheckedAssign).}:
+    x.kind1 = true
+  x.y2 = @["1", "2"]
+  {.cast(uncheckedAssign).}:
+    x.kind2 = true
+  x.z1 = "yes"
+  {.cast(uncheckedAssign).}:
+    x.kind2 = false
+  x.z2 = @["1", "2"]
+  {.cast(uncheckedAssign).}:
+    x.kind2 = true
+  x.z1 = "yes"
+  x.kind2 = true # should be no effect
+  doAssert(x.z1 == "yes")
+  {.cast(uncheckedAssign).}:
+    x.kind2 = false
+    x.kind1 = x.kind2 # support self assignment with effect
+
+  try:
+    x.kind1 = x.flag # flag is not accesible
+  except FieldDefect:
+    echo "prevented"
+
+  doAssert(x.x1 == "x1")
+  doAssert(x.x2 == "x2")
+
+
+test_myobject()
+
+
+#------------------------------------------------
+# bug #14244
+
+type
+  RocksDBResult*[T] = object
+    case ok*: bool
+    of true:
+      value*: T
+    else:
+      error*: string
+
+proc init(): RocksDBResult[string] =
+  {.cast(uncheckedAssign).}:
+    result.ok = true
+  result.value = "ok"
+
+echo init()
+
+
+#------------------------------------------------
+# bug #14312
+
+type MyObj = object
+  case kind: bool
+    of false: x0: int # would work with a type like seq[int]; value would be reset
+    of true: x1: string
+
+var a = MyObj(kind: false, x0: 1234)
+{.cast(uncheckedAssign).}:
+  a.kind = true
+doAssert(a.x1 == "")
+
+block:
+  # bug #15532
+  type Kind = enum
+    k0, k1
+
+  type Foo = object
+    y: int
+    case kind: Kind
+    of k0: x0: int
+    of k1: x1: int
+
+  const j0 = Foo(y: 1, kind: k0, x0: 2)
+  const j1 = Foo(y: 1, kind: k1, x1: 2)
+
+  doAssert j0.y == 1
+  doAssert j0.kind == k0
+  doAssert j1.kind == k1
+
+  doAssert j1.x1 == 2
+  doAssert j0.x0 == 2
diff --git a/tests/arc/tclosureiter.nim b/tests/arc/tclosureiter.nim
new file mode 100644
index 000000000..e4a2ceac6
--- /dev/null
+++ b/tests/arc/tclosureiter.nim
@@ -0,0 +1,36 @@
+discard """
+  cmd: '''nim c -d:nimAllocStats --gc:arc $file'''
+  output: '''(allocCount: 102, deallocCount: 102)'''
+"""
+
+type
+  FutureBase = ref object
+    someData: string
+
+const
+  # Just to occupy some RAM
+  BigData = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+
+iterator mainIter(): FutureBase {.closure.} =
+  for x in 0 .. 100:
+    var internalTmpFuture = FutureBase(someData: BigData)
+    yield internalTmpFuture
+
+proc main() =
+  var nameIterVar = mainIter
+  var next = nameIterVar()
+  while not isNil(next):
+    next = nameIterVar()
+    if not isNil(next):
+      doAssert next.someData.len == 97
+    # GC_unref(next)
+    # If you uncomment the GC_ref above,
+    # the program basically uses no memory after the run.
+    # but crashes with refc, which might indicate
+    # that arc/orc simply never frees the result of "next"?
+    if finished(nameIterVar):
+      break
+
+main()
+GC_fullCollect()
+echo getAllocStats()
diff --git a/tests/arc/tcomputedgoto.nim b/tests/arc/tcomputedgoto.nim
new file mode 100644
index 000000000..07487684a
--- /dev/null
+++ b/tests/arc/tcomputedgoto.nim
@@ -0,0 +1,44 @@
+discard """
+  cmd: '''nim c --mm:arc $file'''
+  output: '''
+2
+2
+destroyed
+'''
+"""
+
+type
+  ObjWithDestructor = object
+    a: int
+proc `=destroy`(self: ObjWithDestructor) =
+  echo "destroyed"
+
+proc `=copy`(self: var ObjWithDestructor, other: ObjWithDestructor) =
+  echo "copied"
+
+proc test(a: range[0..1], arg: ObjWithDestructor) =
+  var iteration = 0
+  while true:
+    {.computedGoto.}
+
+    let
+      b = int(a) * 2
+      c = a
+      d = arg
+      e = arg
+
+    discard c
+    discard d
+    discard e
+
+    inc iteration
+
+    case a
+    of 0:
+      assert false
+    of 1:
+      echo b
+      if iteration == 2:
+        break
+
+test(1, ObjWithDestructor())
diff --git a/tests/arc/tcomputedgotocopy.nim b/tests/arc/tcomputedgotocopy.nim
new file mode 100644
index 000000000..07487684a
--- /dev/null
+++ b/tests/arc/tcomputedgotocopy.nim
@@ -0,0 +1,44 @@
+discard """
+  cmd: '''nim c --mm:arc $file'''
+  output: '''
+2
+2
+destroyed
+'''
+"""
+
+type
+  ObjWithDestructor = object
+    a: int
+proc `=destroy`(self: ObjWithDestructor) =
+  echo "destroyed"
+
+proc `=copy`(self: var ObjWithDestructor, other: ObjWithDestructor) =
+  echo "copied"
+
+proc test(a: range[0..1], arg: ObjWithDestructor) =
+  var iteration = 0
+  while true:
+    {.computedGoto.}
+
+    let
+      b = int(a) * 2
+      c = a
+      d = arg
+      e = arg
+
+    discard c
+    discard d
+    discard e
+
+    inc iteration
+
+    case a
+    of 0:
+      assert false
+    of 1:
+      echo b
+      if iteration == 2:
+        break
+
+test(1, ObjWithDestructor())
diff --git a/tests/arc/tconst_to_sink.nim b/tests/arc/tconst_to_sink.nim
new file mode 100644
index 000000000..25f659341
--- /dev/null
+++ b/tests/arc/tconst_to_sink.nim
@@ -0,0 +1,26 @@
+discard """
+  output: '''@[(s1: "333", s2: ""), (s1: "abc", s2: "def"), (s1: "3x", s2: ""), (s1: "3x", s2: ""), (s1: "3x", s2: ""), (s1: "3x", s2: ""), (s1: "lastone", s2: "")]'''
+  matrix: "--gc:arc"
+  targets: "c cpp"
+"""
+
+# bug #13240
+
+type
+  Thing = object
+    s1: string
+    s2: string
+
+var box: seq[Thing]
+
+const c = [Thing(s1: "333"), Thing(s1: "abc", s2: "def")]
+
+for i in 0..high(c):
+  box.add c[i]
+
+for i in 0..3:
+  box.add Thing(s1: "3x")
+
+box.add Thing(s1: "lastone")
+
+echo box
diff --git a/tests/arc/tcontrolflow.nim b/tests/arc/tcontrolflow.nim
new file mode 100644
index 000000000..dbc115903
--- /dev/null
+++ b/tests/arc/tcontrolflow.nim
@@ -0,0 +1,118 @@
+discard """
+  output: '''begin A
+elif
+end A
+destroyed
+begin false
+if
+end false
+destroyed
+begin true
+if
+end true
+7
+##index 2 not in 0 .. 1##
+true
+'''
+  cmd: "nim c --gc:arc -d:danger $file"
+"""
+# we use the -d:danger switch to detect uninitialized stack
+# slots more reliably (there shouldn't be any, of course).
+
+type
+  Foo = object
+    id: int
+
+proc `=destroy`(x: var Foo) =
+  if x.id != 0:
+    echo "destroyed"
+    x.id = 0
+
+proc construct(): Foo = Foo(id: 3)
+
+proc elifIsEasy(cond: bool) =
+  echo "begin A"
+  if cond:
+    echo "if"
+  elif construct().id == 3:
+    echo "elif"
+  else:
+    echo "else"
+  echo "end A"
+
+elifIsEasy(false)
+
+
+proc orIsHard(cond: bool) =
+  echo "begin ", cond
+  if cond or construct().id == 3:
+    echo "if"
+  else:
+    echo "else"
+  echo "end ", cond
+
+orIsHard(false)
+orIsHard(true)
+
+type
+  Control = ref object
+    x: int
+
+  MouseEvent = ref object
+    control: Control
+    button: int
+
+proc run(data: Control) =
+  var evt = MouseEvent(button: 1)
+  evt.control = data
+  if evt.button == 1:
+    discard
+  else:
+    return
+
+  echo data.x
+
+var c = Control(x: 7)
+
+run(c)
+
+proc sysFatal(exceptn: typedesc, message: string) {.inline.} =
+  var buf = newStringOfCap(200)
+  add(buf, "##")
+  add(buf, message)
+  add(buf, "##")
+  echo buf
+
+proc ifexpr(i, a, b: int) {.compilerproc, noinline.} =
+  sysFatal(IndexDefect,
+    if b < a: "index out of bounds, the container is empty"
+    else: "index " & $i & " not in " & $a & " .. " & $b)
+
+ifexpr(2, 0, 1)
+
+# bug #14899
+template toSeq(): untyped =
+  block:
+    var result = @[1]
+    result
+
+proc clItems(s: seq[int]) =
+  assert s.len == 1
+
+proc escapeCheck =
+  clItems(toSeq())
+
+escapeCheck()
+
+# bug #14900
+
+proc seqsEqual(a, b: string): bool =
+  if false:
+    false
+  else:
+    (var result1 = a; result1) == (var result2 = b; result2)
+
+# can be const or var too
+let expected = "hello"
+
+echo seqsEqual(expected, expected)
diff --git a/tests/arc/tcursor_field_obj_constr.nim b/tests/arc/tcursor_field_obj_constr.nim
new file mode 100644
index 000000000..b87f734bd
--- /dev/null
+++ b/tests/arc/tcursor_field_obj_constr.nim
@@ -0,0 +1,44 @@
+discard """
+  output: '''a
+b
+c'''
+  cmd: "nim c --gc:arc $file"
+"""
+
+# bug #18469
+
+type
+  Edge = object
+    neighbor {.cursor.}: Node
+
+  NodeObj = object
+    neighbors: seq[Edge]
+    label: string
+    visited: bool
+  Node = ref NodeObj
+
+  Graph = object
+    nodes: seq[Node]
+
+proc `=destroy`(x: var NodeObj) =
+  echo x.label
+  `=destroy`(x.neighbors)
+  `=destroy`(x.label)
+
+proc addNode(self: var Graph; label: string): Node =
+  self.nodes.add(Node(label: label))
+  result = self.nodes[^1]
+
+proc addEdge(self: Graph; source, neighbor: Node) =
+  source.neighbors.add(Edge(neighbor: neighbor))
+
+proc main =
+  var graph: Graph
+  let nodeA = graph.addNode("a")
+  let nodeB = graph.addNode("b")
+  let nodeC = graph.addNode("c")
+
+  graph.addEdge(nodeA, neighbor = nodeB)
+  graph.addEdge(nodeA, neighbor = nodeC)
+
+main()
diff --git a/tests/arc/tcursor_on_localvar.nim b/tests/arc/tcursor_on_localvar.nim
new file mode 100644
index 000000000..0f53c5efa
--- /dev/null
+++ b/tests/arc/tcursor_on_localvar.nim
@@ -0,0 +1,161 @@
+discard """
+  output: '''Section: common
+  Param: Floats1
+Section: local
+  Param: Str
+  Param: Bool
+  Param: Floats2
+destroy Foo
+destroy Foo
+'''
+  cmd: '''nim c --gc:arc $file'''
+"""
+
+# bug #15325
+
+import tables
+import strutils
+
+const defaultSection = "***"
+
+type
+    Config* = ref object
+        table: OrderedTableRef[string, OrderedTable[string, string]]
+
+# ----------------------------------------------------------------------------------------------------------------------
+proc newConfig*(): Config =
+    result       = new(Config)
+    result.table = newOrderedTable[string, OrderedTable[string, string]]()
+
+# ----------------------------------------------------------------------------------------------------------------------
+proc add*(self: Config, param, value, section: string) {.nosinks.} =
+    let s = if section == "": defaultSection else: section
+
+    if not self.table.contains(s):
+        self.table[s] = initOrderedTable[string, string]()
+
+    self.table[s][param] = value
+
+# ----------------------------------------------------------------------------------------------------------------------
+proc sections*(self: Config): seq[string] =
+    for i in self.table.keys:
+        let s = if i == defaultSection: "" else: i
+        result.add(s)
+
+# ----------------------------------------------------------------------------------------------------------------------
+proc params*(self: Config, section: string): seq[string] =
+    let s = if section == "": defaultSection else: section
+
+    if self.table.contains(s):
+        for i in self.table[s].keys:
+            result.add(i)
+
+# ----------------------------------------------------------------------------------------------------------------------
+proc extract*(str, start, finish: string): string =
+    let startPos = str.find(start)
+
+    if startPos < 0:
+        return ""
+
+    let endPos = str.find(finish, startPos)
+
+    if endPos < 0:
+        return ""
+
+    return str[startPos + start.len() ..< endPos]
+
+# ----------------------------------------------------------------------------------------------------------------------
+proc loadString*(self: Config, text: string): tuple[valid: bool, errorInLine: int] {.discardable.} =
+    self.table.clear()
+
+    var data = ""
+
+    data = text
+
+    var
+        actualSection = ""
+        lineCount     = 0
+
+    for i in splitLines(data):
+        lineCount += 1
+
+        var line = strip(i)
+
+        if line.len() == 0:
+            continue
+
+        if line[0] == '#' or line[0] == ';':
+            continue
+
+        if line[0] == '[':
+            let section = strip(extract(line, "[", "]"))
+
+            if section.len() != 0:
+                actualSection = section
+            else:
+                self.table.clear()
+                return (false, lineCount)
+        else:
+            let equal = find(line, '=')
+
+            if equal <= 0:
+                self.table.clear()
+                return (false, lineCount)
+            else:
+                let
+                    param = strip(line[0 .. equal - 1])
+                    value = strip(line[equal + 1 .. ^1])
+
+                if param.len() == 0:
+                    self.table.clear()
+                    return (false, lineCount)
+                else:
+                    self.add(param, value, actualSection)
+
+    return (true, 0)
+
+# ----------------------------------------------------------------------------------------------------------------------
+when isMainModule:
+    var cfg = newConfig()
+
+    cfg.loadString("[common]\nFloats1 = 1,2,3\n[local]\nStr = \"String...\"\nBool = true\nFloats2 = 4, 5, 6\n")
+
+    for s in cfg.sections():
+        echo "Section: " & s
+
+        for p in cfg.params(s):
+            echo "  Param: " & p
+
+# bug #16437
+
+type
+  Foo = object
+  FooRef = ref Foo
+
+  Bar = ref object
+    f: FooRef
+
+proc `=destroy`(o: var Foo) =
+  echo "destroy Foo"
+
+proc testMe(x: Bar) =
+  var c = (if x != nil: x.f else: nil)
+  assert c != nil
+
+proc main =
+  var b = Bar(f: FooRef())
+  testMe(b)
+
+main()
+
+proc testMe2(x: Bar) =
+  var c: FooRef
+  c = (if x != nil: x.f else: nil)
+  assert c != nil
+
+proc main2 =
+  var b = Bar(f: FooRef())
+  testMe2(b)
+
+main2()
+
diff --git a/tests/arc/tcursorloop.nim b/tests/arc/tcursorloop.nim
new file mode 100644
index 000000000..a37a6a036
--- /dev/null
+++ b/tests/arc/tcursorloop.nim
@@ -0,0 +1,45 @@
+discard """
+  cmd: '''nim c --gc:arc --expandArc:traverse --hint:Performance:off $file'''
+  nimout: '''
+--expandArc: traverse
+
+var
+  it
+  jt_cursor
+try:
+  `=copy`(it, root)
+  block :tmp:
+    while (
+      not (it == nil)):
+      if true:
+        echo [it.s]
+        `=copy`(it, it.ri)
+  jt_cursor = root
+  if (
+    not (jt_cursor == nil)):
+    echo [jt_cursor.s]
+    jt_cursor = jt_cursor.ri
+finally:
+  `=destroy`(it)
+-- end of expandArc ------------------------
+'''
+"""
+
+type
+  Node = ref object
+    le, ri: Node
+    s: string
+
+proc traverse(root: Node) =
+  var it = root
+  while it != nil:
+    if true:
+      echo it.s
+      it = it.ri
+
+  var jt = root
+  if jt != nil:
+    echo jt.s
+    jt = jt.ri
+
+traverse(nil)
diff --git a/tests/arc/tcustomtrace.nim b/tests/arc/tcustomtrace.nim
new file mode 100644
index 000000000..5e0ecfb24
--- /dev/null
+++ b/tests/arc/tcustomtrace.nim
@@ -0,0 +1,240 @@
+discard """
+  outputsub: '''1
+2
+3
+4
+5
+6
+89
+90
+90
+0 0 1
+0 1 2
+0 2 3
+1 0 4
+1 1 5
+1 2 6
+1 3 7
+after 6 6
+MEM 0'''
+joinable: false
+  cmd: "nim c --gc:orc -d:useMalloc $file"
+  valgrind: "true"
+"""
+
+import typetraits
+
+type
+  myseq*[T] = object
+    len, cap: int
+    data: ptr UncheckedArray[T]
+
+# XXX make code memory safe for overflows in '*'
+var
+  allocCount, deallocCount: int
+
+proc `=destroy`*[T](x: var myseq[T]) =
+  if x.data != nil:
+    when not supportsCopyMem(T):
+      for i in 0..<x.len: `=destroy`(x[i])
+    dealloc(x.data)
+    inc deallocCount
+    x.data = nil
+    x.len = 0
+    x.cap = 0
+
+proc `=copy`*[T](a: var myseq[T]; b: myseq[T]) =
+  if a.data == b.data: return
+  if a.data != nil:
+    `=destroy`(a)
+    #dealloc(a.data)
+    #inc deallocCount
+    #a.data = nil
+  a.len = b.len
+  a.cap = b.cap
+  if b.data != nil:
+    a.data = cast[type(a.data)](alloc(a.cap * sizeof(T)))
+    inc allocCount
+    when supportsCopyMem(T):
+      copyMem(a.data, b.data, a.cap * sizeof(T))
+    else:
+      for i in 0..<a.len:
+        a.data[i] = b.data[i]
+
+proc `=sink`*[T](a: var myseq[T]; b: myseq[T]) =
+  if a.data != nil and a.data != b.data:
+    dealloc(a.data)
+    inc deallocCount
+  a.len = b.len
+  a.cap = b.cap
+  a.data = b.data
+
+proc `=trace`*[T](x: var myseq[T]; env: pointer) =
+  if x.data != nil:
+    for i in 0..<x.len: `=trace`(x[i], env)
+
+proc resize[T](s: var myseq[T]) =
+  let oldCap = s.cap
+  if oldCap == 0: s.cap = 8
+  else: s.cap = (s.cap * 3) shr 1
+  if s.data == nil: inc allocCount
+  s.data = cast[typeof(s.data)](realloc0(s.data, oldCap * sizeof(T), s.cap * sizeof(T)))
+
+proc reserveSlot[T](x: var myseq[T]): ptr T =
+  if x.len >= x.cap: resize(x)
+  result = addr(x.data[x.len])
+  inc x.len
+
+template add*[T](x: var myseq[T]; y: T) =
+  reserveSlot(x)[] = y
+
+proc shrink*[T](x: var myseq[T]; newLen: int) =
+  assert newLen <= x.len
+  assert newLen >= 0
+  when not supportsCopyMem(T):
+    for i in countdown(x.len - 1, newLen - 1):
+      `=destroy`(x.data[i])
+  x.len = newLen
+
+proc grow*[T](x: var myseq[T]; newLen: int; value: T) =
+  if newLen <= x.len: return
+  assert newLen >= 0
+  let oldCap = x.cap
+  if oldCap == 0: x.cap = newLen
+  else: x.cap = max(newLen, (oldCap * 3) shr 1)
+  if x.data == nil: inc allocCount
+  x.data = cast[type(x.data)](realloc0(x.data, oldCap * sizeof(T), x.cap * sizeof(T)))
+  for i in x.len..<newLen:
+    x.data[i] = value
+  x.len = newLen
+
+template default[T](t: typedesc[T]): T =
+  var v: T
+  v
+
+proc setLen*[T](x: var myseq[T]; newLen: int) {.deprecated.} =
+  if newlen < x.len: shrink(x, newLen)
+  else: grow(x, newLen, default(T))
+
+template `[]`*[T](x: myseq[T]; i: Natural): T =
+  assert i < x.len
+  x.data[i]
+
+template `[]=`*[T](x: myseq[T]; i: Natural; y: T) =
+  assert i < x.len
+  x.data[i] = y
+
+proc createSeq*[T](elems: varargs[T]): myseq[T] =
+  result.cap = max(elems.len, 2)
+  result.len = elems.len
+  result.data = cast[type(result.data)](alloc0(result.cap * sizeof(T)))
+  inc allocCount
+  when supportsCopyMem(T):
+    copyMem(result.data, addr(elems[0]), result.cap * sizeof(T))
+  else:
+    for i in 0..<result.len:
+      result.data[i] = elems[i]
+
+proc len*[T](x: myseq[T]): int {.inline.} = x.len
+
+proc main =
+  var s = createSeq(1, 2, 3, 4, 5, 6)
+  s.add 89
+  s.grow s.len + 2, 90
+  for i in 0 ..< s.len:
+    echo s[i]
+
+  var nested = createSeq(createSeq(1, 2, 3), createSeq(4, 5, 6, 7))
+  for i in 0 ..< nested.len:
+    for j in 0 ..< nested[i].len:
+      echo i, " ", j, " ", nested[i][j]
+
+main()
+echo "after ", allocCount, " ", deallocCount
+
+type
+  Node = ref object
+    name: char
+    sccId: int
+    kids: myseq[Node]
+    rc: int
+
+proc edge(a, b: Node) =
+  inc b.rc
+  a.kids.add b
+
+proc createNode(name: char): Node =
+  new result
+  result.name = name
+  result.kids = createSeq[Node]()
+
+proc use(x: Node) = discard
+
+proc buildComplexGraph: Node =
+  # see https://en.wikipedia.org/wiki/Strongly_connected_component for the
+  # graph:
+  let a = createNode('a')
+  let b = createNode('b')
+  let c = createNode('c')
+  let d = createNode('d')
+  let e = createNode('e')
+
+  a.edge c
+  c.edge b
+  c.edge e
+  b.edge a
+  d.edge c
+  e.edge d
+
+
+  let f = createNode('f')
+  b.edge f
+  e.edge f
+
+  let g = createNode('g')
+  let h = createNode('h')
+  let i = createNode('i')
+
+  f.edge g
+  f.edge i
+  g.edge h
+  h.edge i
+  i.edge g
+
+  let j = createNode('j')
+
+  h.edge j
+  i.edge j
+
+  let k = createNode('k')
+  let l = createNode('l')
+
+  f.edge k
+  k.edge l
+  l.edge k
+  k.edge j
+
+  let m = createNode('m')
+  let n = createNode('n')
+  let p = createNode('p')
+  let q = createNode('q')
+
+  m.edge n
+  n.edge p
+  n.edge q
+  q.edge p
+  p.edge m
+
+  q.edge k
+
+  d.edge m
+  e.edge n
+
+  result = a
+
+proc main2 =
+  let g = buildComplexGraph()
+
+main2()
+GC_fullCollect()
+echo "MEM ", getOccupiedMem()
diff --git a/tests/arc/tdeepcopy.nim b/tests/arc/tdeepcopy.nim
new file mode 100644
index 000000000..91d93aa28
--- /dev/null
+++ b/tests/arc/tdeepcopy.nim
@@ -0,0 +1,67 @@
+discard """
+  cmd: "nim c --gc:arc --deepcopy:on $file"
+  output: '''13 abc
+13 abc
+13 abc
+13 abc
+13 abc
+13 abc
+13 abc
+13 abc
+13 abc
+13 abc
+13 abc
+called deepCopy for int
+called deepCopy for int
+called deepCopy for int
+called deepCopy for int
+called deepCopy for int
+called deepCopy for int
+called deepCopy for int
+called deepCopy for int
+called deepCopy for int
+called deepCopy for int
+called deepCopy for int
+0'''
+"""
+
+type
+  PBinaryTree = ref object of RootObj
+    le, ri: PBinaryTree
+    value: int
+
+proc mainB =
+  var x: PBinaryTree
+  deepCopy(x, PBinaryTree(ri: PBinaryTree(le: PBinaryTree(value: 13))))
+
+  var y: string
+  deepCopy y, "abc"
+  echo x.ri.le.value, " ", y
+
+for i in 0..10:
+  mainB()
+
+
+type
+  Bar[T] = object
+    x: T
+
+proc `=deepCopy`[T](b: ref Bar[T]): ref Bar[T] =
+  result.new
+  result.x = b.x
+  when T is int:
+    echo "called deepCopy for int"
+  else:
+    echo "called deepCopy for something else"
+
+proc main =
+  var dummy, c: ref Bar[int]
+  new(dummy)
+  dummy.x = 44
+
+  deepCopy c, dummy
+
+for i in 0..10:
+  main()
+
+echo getOccupiedMem()
diff --git a/tests/arc/tdestroy_in_loopcond.nim b/tests/arc/tdestroy_in_loopcond.nim
new file mode 100644
index 000000000..62532664d
--- /dev/null
+++ b/tests/arc/tdestroy_in_loopcond.nim
@@ -0,0 +1,74 @@
+discard """
+  output: '''400 true'''
+  cmd: "nim c --gc:orc $file"
+"""
+
+type HeapQueue*[T] = object
+  data: seq[T]
+
+
+proc len*[T](heap: HeapQueue[T]): int {.inline.} =
+  heap.data.len
+
+proc `[]`*[T](heap: HeapQueue[T], i: Natural): T {.inline.} =
+  heap.data[i]
+
+proc push*[T](heap: var HeapQueue[T], item: T) =
+  heap.data.add(item)
+
+proc pop*[T](heap: var HeapQueue[T]): T =
+  result = heap.data.pop
+
+proc clear*[T](heap: var HeapQueue[T]) = heap.data.setLen 0
+
+
+type
+  Future = ref object of RootObj
+    s: string
+    callme: proc()
+
+var called = 0
+
+proc consume(f: Future) =
+  inc called
+
+proc newFuture(s: string): Future =
+  var r: Future
+  r = Future(s: s, callme: proc() =
+    consume r)
+  result = r
+
+var q: HeapQueue[tuple[finishAt: int64, fut: Future]]
+
+proc sleep(f: int64): Future =
+  q.push (finishAt: f, fut: newFuture("async-sleep"))
+
+proc processTimers =
+  # Pop the timers in the order in which they will expire (smaller `finishAt`).
+  var count = q.len
+  let t = high(int64)
+  while count > 0 and t >= q[0].finishAt:
+    q.pop().fut.callme()
+    dec count
+
+var futures: seq[Future]
+
+proc main =
+  for i in 1..200:
+    futures.add sleep(56020904056300)
+    futures.add sleep(56020804337500)
+    #futures.add sleep(2.0)
+
+    #futures.add sleep(4.0)
+
+    processTimers()
+    #q.pop()[1].callme()
+    #q.pop()[1].callme()
+
+    futures.setLen 0
+
+  q.clear()
+
+main()
+GC_fullCollect()
+echo called, " ", getOccupiedMem() < 160
diff --git a/tests/arc/tdup.nim b/tests/arc/tdup.nim
new file mode 100644
index 000000000..61f262a68
--- /dev/null
+++ b/tests/arc/tdup.nim
@@ -0,0 +1,71 @@
+discard """
+  cmd: "nim c --mm:arc --expandArc:foo --hints:off $file"
+  nimout: '''
+--expandArc: foo
+
+var
+  x
+  :tmpD
+  s
+  :tmpD_1
+x = Ref(id: 8)
+inc:
+  :tmpD = `=dup`(x)
+  :tmpD
+inc:
+  let blitTmp = x
+  blitTmp
+var id_1 = 777
+s = RefCustom(id_2: addr(id_1))
+inc_1 :
+  :tmpD_1 = `=dup_1`(s)
+  :tmpD_1
+inc_1 :
+  let blitTmp_1 = s
+  blitTmp_1
+-- end of expandArc ------------------------
+'''
+"""
+
+type
+  Ref = ref object
+    id: int
+
+  RefCustom = object
+    id: ptr int
+
+proc `=dup`(x: RefCustom): RefCustom =
+  result.id = x.id
+
+proc inc(x: sink Ref) =
+  inc x.id
+
+proc inc(x: sink RefCustom) =
+  inc x.id[]
+
+
+proc foo =
+  var x = Ref(id: 8)
+  inc(x)
+  inc(x)
+  var id = 777
+  var s = RefCustom(id: addr id)
+  inc s
+  inc s
+
+foo()
+
+proc foo2 =
+  var x = Ref(id: 8)
+  inc(x)
+  doAssert x.id == 9
+  inc(x)
+  doAssert x.id == 10
+  var id = 777
+  var s = RefCustom(id: addr id)
+  inc s
+  doAssert s.id[] == 778
+  inc s
+  doAssert s.id[] == 779
+
+foo2()
diff --git a/tests/arc/testfile.txt b/tests/arc/testfile.txt
new file mode 100644
index 000000000..5169490be
--- /dev/null
+++ b/tests/arc/testfile.txt
@@ -0,0 +1,2 @@
+1 x
+2 y
diff --git a/tests/arc/texceptions.nim b/tests/arc/texceptions.nim
new file mode 100644
index 000000000..c55b463fc
--- /dev/null
+++ b/tests/arc/texceptions.nim
@@ -0,0 +1,16 @@
+discard """
+  cmd: "nim cpp --gc:arc $file"
+"""
+
+block: # issue #13071
+  type MyExcept = object of CatchableError
+  proc gun()=
+    raise newException(MyExcept, "foo:")
+  proc fun()=
+    var a = ""
+    try:
+      gun()
+    except Exception as e:
+      a = e.msg & $e.name # was segfaulting here for `nim cpp --gc:arc`
+    doAssert a == "foo:MyExcept"
+  fun()
diff --git a/tests/arc/texplicit_sink.nim b/tests/arc/texplicit_sink.nim
new file mode 100644
index 000000000..4b021ed62
--- /dev/null
+++ b/tests/arc/texplicit_sink.nim
@@ -0,0 +1,29 @@
+discard """
+  output: '''de'''
+  cmd: '''nim c --mm:arc --expandArc:main $file'''
+  nimout: '''--expandArc: main
+
+var
+  a
+  b_cursor
+try:
+  a = f "abc"
+  b_cursor = "de"
+  `=sink`(a, b_cursor)
+  echo [a]
+finally:
+  `=destroy`(a)
+-- end of expandArc ------------------------'''
+"""
+
+# bug #20572
+
+proc f(s: string): string = s
+
+proc main =
+  var a = f "abc"
+  var b = "de"
+  `=sink`(a, b)
+  echo a
+
+main()
diff --git a/tests/arc/tfuncobj.nim b/tests/arc/tfuncobj.nim
new file mode 100644
index 000000000..50cd9425e
--- /dev/null
+++ b/tests/arc/tfuncobj.nim
@@ -0,0 +1,38 @@
+discard """
+  outputsub: '''f1
+f2
+f3'''
+  cmd: "nim c --gc:orc $file"
+  valgrind: true
+"""
+
+type
+  FuncObj = object
+    fn: proc (env: pointer) {.cdecl.}
+    env: pointer
+
+proc `=destroy`(x: var FuncObj) =
+  GC_unref(cast[RootRef](x.env))
+
+proc `=copy`(x: var FuncObj, y: FuncObj) {.error.}
+
+# bug #18433
+
+proc main =
+  var fs: seq[FuncObj]
+
+  proc wrap(p: proc()) =
+    proc closeOver() = p()
+    let env = rawEnv closeOver
+    GC_ref(cast[RootRef](env))
+    fs.add(FuncObj(fn: cast[proc(env: pointer){.cdecl.}](rawProc closeOver), env: env))
+
+  wrap(proc() {.closure.} = echo "f1")
+  wrap(proc() {.closure.} = echo "f2")
+  wrap(proc() {.closure.} = echo "f3")
+
+  for a in fs:
+    a.fn(a.env)
+
+main()
+
diff --git a/tests/arc/thamming_orc.nim b/tests/arc/thamming_orc.nim
new file mode 100644
index 000000000..777efb38e
--- /dev/null
+++ b/tests/arc/thamming_orc.nim
@@ -0,0 +1,155 @@
+discard """
+  output: '''(allocCount: 1114, deallocCount: 1112)
+created 491 destroyed 491'''
+  cmd: "nim c --gc:orc -d:nimAllocStats $file"
+"""
+
+# bug #18421
+
+# test Nim Hamming Number Lazy List algo with reference counts and not...
+# compile with "-d:release -d:danger" and test with various
+# memory managment GC's, allocators, threading, etc.
+# it should be guaranteed to work with zero memory leaks with `--gc:orc`...
+
+# compile with `-d:trace20` to trace creation and destruction of first 20 values.
+
+from math import log2
+
+# implement our own basic BigInt so the bigints library isn't necessary...
+type
+  BigInt = object
+    digits: seq[uint32]
+let zeroBigInt = BigInt(digits: @[ 0'u32 ])
+let oneBigInt = BigInt(digits: @[ 1'u32 ])
+
+proc shladd(bi: var BigInt; n: int; a: BigInt) =
+  # assume that both `bi` and `a` are sized correctly with
+  # msuint32 for both not containing a zero
+  let alen = a.digits.len
+  let mx = max(bi.digits.len, a.digits.len)
+  for i in bi.digits.len ..< mx: bi.digits.add 0'u32
+  var cry = 0'u64
+  for i in 0 ..< alen:
+    cry += (bi.digits[i].uint64 shl n) + a.digits[i].uint64
+    bi.digits[i] = cry.uint32; cry = cry shr 32
+  for i in alen ..< mx:
+    cry += bi.digits[i].uint64 shl n
+    bi.digits[i] = cry.uint32; cry = cry shr 32
+  if cry > 0'u64:
+    bi.digits.add cry.uint32
+
+proc `$`(x: BigInt): string =
+  if x.digits.len == 0 or (x.digits.len == 1 and x.digits[0] == 0'u32):
+    return "0"
+  result = ""; var n = x; var msd = n.digits.high
+  while msd >= 0:
+    if n.digits[msd] == 0'u32: msd.dec; continue
+    var brw = 0.uint64
+    for i in countdown(msd, 0):
+      let dvdnd = n.digits[i].uint64 + (brw shl 32)
+      let q = dvdnd div 10'u64; brw = dvdnd - q * 10'u64
+      n.digits[i] = q.uint32
+    result &= $brw
+  for i in 0 .. result.high shr 1: # reverse result string in place
+    let tmp = result[^(i + 1)]
+    result[^(i + 1)] = result[i]
+    result[i] = tmp
+
+type TriVal = (uint32, uint32, uint32)
+type LogRep = (float64, TriVal)
+type LogRepf = proc(x: LogRep): LogRep
+const one: LogRep = (0.0'f64, (0'u32, 0'u32, 0'u32))
+proc `<`(me: LogRep, othr: LogRep): bool = me[0] < othr[0]
+
+proc convertTriVal2BigInt(tpl: TriVal): BigInt =
+  result = oneBigInt
+  let (x2, x3, x5) = tpl
+  for _ in 1 .. x2: result.shladd 1, zeroBigInt
+  for _ in 1 .. x3: result.shladd 1, result
+  for _ in 1 .. x5: result.shladd 2, result
+
+const lb2 = 1.0'f64
+const lb3 = 3.0'f64.log2
+const lb5 = 5.0'f64.log2
+
+proc mul2(me: LogRep): LogRep =
+  let (lr, tpl) = me; let (x2, x3, x5) = tpl
+  (lr + lb2, (x2 + 1, x3, x5))
+
+proc mul3(me: LogRep): LogRep =
+  let (lr, tpl) = me; let (x2, x3, x5) = tpl
+  (lr + lb3, (x2, x3 + 1, x5))
+
+proc mul5(me: LogRep): LogRep =
+  let (lr, tpl) = me; let (x2, x3, x5) = tpl
+  (lr + lb5, (x2, x3, x5 + 1))
+
+type
+  LazyListObj = object
+    hd: LogRep
+    tlf: proc(): LazyList {.closure.}
+    tl: LazyList
+  LazyList = ref LazyListObj
+
+var destroyed = 0
+
+proc `=destroy`(ll: var LazyListObj) =
+  destroyed += 1
+  if ll.tlf == nil and ll.tl == nil: return
+
+  when defined(trace20):
+    echo "destroying:  ", (destroyed, ll.hd[1].convertTriVal2BigInt)
+  if ll.tlf != nil: ll.tlf.`=destroy`
+  if ll.tl != nil: ll.tl.`=destroy`
+  #wasMoved(ll)
+
+proc rest(ll: LazyList): LazyList = # not thread-safe; needs lock on thunk
+  if ll.tlf != nil: ll.tl = ll.tlf(); ll.tlf = nil
+  ll.tl
+
+var created = 0
+iterator hammings(until: int): TriVal =
+  proc merge(x, y: LazyList): LazyList =
+    let xh = x.hd; let yh = y.hd; created += 1
+    when defined(trace20):
+      echo "merge create:  ", (created - 1, (if xh < yh: xh else: yh)[1].convertTriVal2BigInt)
+    if xh < yh: LazyList(hd: xh, tlf: proc(): auto = merge x.rest, y)
+    else: LazyList(hd: yh, tlf: proc(): auto = merge x, y.rest)
+  proc smult(mltf: LogRepf; s: LazyList): LazyList =
+    proc smults(ss: LazyList): LazyList =
+      when defined(trace20):
+        echo "mult create:  ", (created, ss.hd.mltf[1].convertTriVal2BigInt)
+      created += 1; LazyList(hd: ss.hd.mltf, tlf: proc(): auto = ss.rest.smults)
+    s.smults
+  proc unnsm(s: LazyList, mltf: LogRepf): LazyList =
+    var r: LazyList = nil
+    when defined(trace20):
+      echo "first create:  ", (created, one[1].convertTriVal2BigInt)
+    let frst = LazyList(hd: one, tlf: proc(): LazyList = r); created += 1
+    r = if s == nil: smult(mltf, frst) else: s.merge smult(mltf, frst)
+    r
+  yield one[1]
+  var hmpll: LazyList = ((nil.unnsm mul5).unnsm mul3).unnsm mul2
+  for _ in 2 .. until:
+    yield hmpll.hd[1]; hmpll = hmpll.rest # almost forever
+
+proc main =
+  var s = ""
+  for h in hammings(20): s &= $h.convertTrival2BigInt & " "
+  doAssert s == "1 2 3 4 5 6 8 9 10 12 15 16 18 20 24 25 27 30 32 36 ",
+           "Algorithmic error finding first 20 Hamming numbers!!!"
+
+  when not defined(trace20):
+    var lsth: TriVal
+    for h in hammings(200): lsth = h
+    doAssert $lsth.convertTriVal2BigInt == "16200",
+             "Algorithmic error finding 200th Hamming number!!!"
+
+let mem = getOccupiedMem()
+main()
+GC_FullCollect()
+let mb = getOccupiedMem() - mem
+doAssert mb == 0, "Found memory leak of " & $mb & " bytes!!!"
+
+echo getAllocStats()
+echo "created ", created, " destroyed ", destroyed
diff --git a/tests/arc/thamming_thinout.nim b/tests/arc/thamming_thinout.nim
new file mode 100644
index 000000000..65c34ef49
--- /dev/null
+++ b/tests/arc/thamming_thinout.nim
@@ -0,0 +1,183 @@
+discard """
+  cmd: "nim c --gc:orc -d:nimThinout -r $file"
+  output: '''The first 20 hammings are:  1 2 3 4 5 MEM IS 0'''
+"""
+
+# test Nim Hamming Number Lazy List algo with reference counts and not...
+# compile with "-d:release -d:danger" and test with various
+# memory managment GC's, allocators, threading, etc.
+
+from times import epochTime
+from math import log2
+
+# implement our own basic BigInt so the bigints library isn't necessary...
+type
+  BigInt = object
+    digits: seq[uint32]
+let zeroBigInt = BigInt(digits: @[ 0'u32 ])
+let oneBigInt = BigInt(digits: @[ 1'u32 ])
+
+proc shladd(bi: var BigInt; n: int; a: BigInt) =
+  var cry = 0'u64
+  for i in 0 ..< min(bi.digits.len, a.digits.len):
+    cry += (bi.digits[i].uint64 shl n) + a.digits[i].uint64
+    bi.digits[i] = cry.uint32
+    cry = cry shr 32
+  if cry > 0'u64:
+    bi.digits.add cry.uint32
+
+proc `$`(x: BigInt): string =
+  if x.digits.len == 0 or (x.digits.len == 1 and x.digits[0] == 0'u32):
+    return "0"
+  var n = x
+  var msd = n.digits.high
+  result = ""
+  while msd >= 0:
+    if n.digits[msd] == 0'u32: msd.dec; continue
+    var brw = 0.uint64
+    for i in countdown(msd, 0):
+      let dvdnd = n.digits[i].uint64 + (brw shl 32)
+      let q = dvdnd div 10'u64; brw = dvdnd - q*10'u64; n.digits[i] = q.uint32
+    result &= $brw
+  for i in 0 .. result.high shr 1:
+    let tmp = result[^(i + 1)]
+    result[^(i + 1)] = result[i]
+    result[i] = tmp
+
+proc convertTrival2BigInt(tpl: (uint32, uint32, uint32)): BigInt =
+  result = oneBigInt
+  let (x2, x3, x5) = tpl
+  for _ in 1 .. x2: result.shladd 1, zeroBigInt
+  for _ in 1 .. x3: result.shladd 1, result
+  for _ in 1 .. x5: result.shladd 2, result
+
+type LogRep = (float64, (uint32, uint32, uint32))
+type LogRepf = proc(x: LogRep): LogRep
+const one: LogRep = (0.0f64, (0u32, 0u32, 0u32))
+proc `<`(me: LogRep, othr: LogRep): bool = me[0] < othr[0]
+
+const lb2 = 1.0'f64
+const lb3 = 3.0'f64.log2
+const lb5 = 5.0'f64.log2
+
+proc mul2(me: LogRep): LogRep =
+  let (lr, tpl) = me; let (x2, x3, x5) = tpl
+  (lr + lb2, (x2 + 1, x3, x5))
+
+proc mul3(me: LogRep): LogRep =
+  let (lr, tpl) = me; let (x2, x3, x5) = tpl
+  (lr + lb3, (x2, x3 + 1, x5))
+
+proc mul5(me: LogRep): LogRep =
+  let (lr, tpl) = me; let (x2, x3, x5) = tpl
+  (lr + lb5, (x2, x3, x5 + 1))
+
+type
+  LazyList = ref object
+    hd: LogRep
+    tlf: proc(): LazyList {.closure.}
+    tl: LazyList
+
+proc rest(ll: LazyList): LazyList = # not thread-safe; needs lock on thunk
+  if ll.tlf != nil:
+    ll.tl = ll.tlf()
+    ll.tlf = nil
+  ll.tl
+
+iterator hamming(until: int): (uint32, uint32, uint32) =
+  proc merge(x, y: LazyList): LazyList =
+    let xh = x.hd
+    let yh = y.hd
+    if xh < yh: LazyList(hd: xh, tlf: proc(): auto = merge x.rest, y)
+    else: LazyList(hd: yh, tlf: proc(): auto = merge x, y.rest)
+  proc smult(mltf: LogRepf; s: LazyList): LazyList =
+    proc smults(ss: LazyList): LazyList =
+      LazyList(hd: ss.hd.mltf, tlf: proc(): auto = ss.rest.smults)
+    s.smults
+  proc unnsm(s: LazyList, mltf: LogRepf): LazyList =
+    var r: LazyList = nil
+    let frst = LazyList(hd: one, tlf: proc(): LazyList = r)
+    r = if s == nil: smult mltf, frst else: s.merge smult(mltf, frst)
+    r
+  var hmpll: LazyList = ((nil.unnsm mul5).unnsm mul3).unnsm mul2
+  #  var hmpll: LazyList = nil; for m in [mul5, mul3, mul2]: echo one.m # ; hmpll = unnsm(hmpll, m)
+  yield one[1]
+  var cnt = 1
+  while hmpll != nil:
+    yield hmpll.hd[1]
+    hmpll = hmpll.rest # almost forever
+    cnt.inc
+    if cnt > until: break
+  #when declared(thinout):
+  thinout(hmpll)
+
+proc main =
+  stdout.write "The first 20 hammings are:  "
+  for h in hamming(4):
+    write stdout, h.convertTrival2BigInt, " "
+
+  for h in hamming(200):
+    discard h.convertTrival2BigInt
+
+let mem = getOccupiedMem()
+main()
+echo "MEM IS ", getOccupiedMem() - mem
+
+#[
+result = (smults, :envP.:up)(rest(:envP.ss2))
+
+proc anon =
+  var
+    :tmpD_284230
+    :tmpD_284233
+    :tmpD_284236
+  try:
+    `=sink_283407`(result_283502,
+      `=sink_283927`(:tmpD_284236, (smults_283495,
+        wasMoved_284234(:tmpD_284233)
+        `=_284014`(:tmpD_284233, :envP_283898.:up_283899)
+        :tmpD_284233))
+      :tmpD_284236(
+      `=sink_283407`(:tmpD_284230, rest_283366(:envP_283898.ss2_-283497))
+      :tmpD_284230))
+  finally:
+    `=destroy_283914`(:tmpD_284236)
+    `=destroy_283388`(:tmpD_284230)
+
+proc smuls(ss: LazyList_283350; :envP_283891): LazyList_283350  =
+  var :env_283913
+  try:
+    `=destroy_283951`(:env_283913)
+    internalNew_43643(:env_283913)
+    `=_283401`(:env_283913.ss2_-283497, ss_283497)
+    :env_283913.:up_283899 = :envP_283891
+    `=sink_283407`(result_283498, LazyList_283350(hd_283353: :envP_283891.mltf1_-283492(
+        :env_283913.ss2_-283497.hd_283353), tlf_283356: (:anonymous_283499,
+      let blitTmp_284227 = :env_283913
+      wasMoved_284228(:env_283913)
+      blitTmp_284227)))
+  finally:
+    `=destroy_283951`(:env_283913)
+
+proc smul =
+  var
+    :env_283969
+    :tmpD_284220
+  try:
+    `=destroy_284008`(:env_283969)
+    internalNew_43643(:env_283969)
+    `=_283976`(:env_283969.mltf1_-283492, mltf_283492)
+    proc smults(ss: LazyList_283350; :envP_283891): LazyList_283350 =
+      result_283498 = LazyList_283350(hd_283353: mltf_283492(ss_283497.hd_283353), tlf_283356: proc (
+          :envP_283898): auto_43100 = result_283502 = smults_283495(rest_283366(ss_283497)))
+
+    `=sink_283407`(result_283494,
+      `=sink_283927`(:tmpD_284220, (smults_283495,
+        let blitTmp_284218 = :env_283969
+        wasMoved_284219(:env_283969)
+        blitTmp_284218))
+      :tmpD_284220(s_283493))
+  finally:
+    `=destroy_283914`(:tmpD_284220)
+    `=destroy_284008`(:env_283969)
+]#
diff --git a/tests/arc/thard_alignment.nim b/tests/arc/thard_alignment.nim
new file mode 100644
index 000000000..30cfddb05
--- /dev/null
+++ b/tests/arc/thard_alignment.nim
@@ -0,0 +1,148 @@
+discard """
+disabled: "arm64"
+cmd: "nim c --mm:arc -u:nimPreviewNonVarDestructor $file"
+output: "y"
+"""
+
+# TODO: fixme: investigate why it failed with non-var destructors
+
+{.passC: "-march=native".}
+
+proc isAlignedCheck(p: pointer, alignment: int) = 
+  doAssert (cast[uint](p) and uint(alignment - 1)) == 0
+
+proc isAlignedCheck[T](p: ref T, alignment: int) = 
+  isAlignedCheck(cast[pointer](p), alignment)
+
+type
+  m256d {.importc: "__m256d", header: "immintrin.h".} = object
+
+proc set1(x: float): m256d {.importc: "_mm256_set1_pd", header: "immintrin.h".}
+func `+`(a,b: m256d): m256d {.importc: "_mm256_add_pd", header: "immintrin.h".}
+proc `$`(a: m256d): string =  
+  result = $(cast[ptr float](a.addr)[])
+
+
+var res: seq[seq[m256d]]
+
+for _ in 1..1000:
+  var x = newSeq[m256d](1)
+  x[0] = set1(1.0) # test if operation causes segfault
+  isAlignedCheck(x[0].addr, alignof(m256d))
+  res.add x
+
+var res2: seq[m256d]
+for i in 1..10000:
+  res2.setLen(res2.len + 1) # check if realloc works
+  isAlignedCheck(res2[0].addr, alignof(m256d))  
+
+proc lambdaGen(a, b: float, z: ref m256d) : auto =
+  var x1 = new(m256d)
+  var x2 = new(m256d)
+  isAlignedCheck(x1, alignof(m256d))
+  isAlignedCheck(x2, alignof(m256d))
+  x1[] = set1(2.0 + a)  
+  x2[] = set1(-23.0 - b)
+  let capturingLambda = proc(x: ref m256d): ref m256d =
+    var cc = new(m256d)
+    var bb = new(m256d)
+    isAlignedCheck(x1, alignof(m256d))
+    isAlignedCheck(x2, alignof(m256d))
+    isAlignedCheck(cc, alignof(m256d))
+    isAlignedCheck(bb, alignof(m256d))
+    isAlignedCheck(z, alignof(m256d))
+        
+    cc[] = x1[] + x1[] + z[]
+    bb[] = x2[] + set1(12.5) + z[]
+    
+    result = new(m256d)
+    isAlignedCheck(result, alignof(m256d))
+    result[] = cc[] + bb[] + x[]
+    
+  return capturingLambda
+
+var xx = new(m256d)
+xx[] = set1(10)
+isAlignedCheck(xx, alignOf(m256d))
+
+let f1 = lambdaGen(2.0 , 2.221, xx)
+let f2 = lambdaGen(-1.226 , 3.5, xx)
+isAlignedCheck(f1(xx), alignOf(m256d))
+isAlignedCheck(f2(xx), alignOf(m256d))
+
+
+#-----------------------------------------------------------------------------
+
+type
+  MyAligned = object of RootObj
+    a{.align: 128.}: float
+
+
+var f: MyAligned
+isAlignedCheck(f.addr, MyAligned.alignOf)
+
+var fref = new(MyAligned)
+isAlignedCheck(fref, MyAligned.alignOf)
+
+var fs: seq[MyAligned]
+var fr: seq[RootRef]
+
+for i in 0..1000:
+  fs.add MyAligned()
+  isAlignedCheck(fs[^1].addr, MyAligned.alignOf)
+  fs[^1].a = i.float
+
+  fr.add new(MyAligned)
+  isAlignedCheck(fr[^1], MyAligned.alignOf)
+  ((ref MyAligned)fr[^1])[].a = i.float
+
+for i in 0..1000:
+  doAssert(fs[i].a == i.float)
+  doAssert(((ref MyAligned)fr[i]).a == i.float)
+
+
+proc lambdaTest2(a: MyAligned, z: ref MyAligned): auto =
+  var x1: MyAligned
+  x1.a = a.a + z.a  
+  var x2: MyAligned
+  x2.a = a.a - z.a
+  let capturingLambda = proc(x: MyAligned): MyAligned =
+    var cc: MyAligned
+    var bb: MyAligned
+    isAlignedCheck(x1.addr, MyAligned.alignOf)
+    isAlignedCheck(x2.addr, MyAligned.alignOf)
+    isAlignedCheck(cc.addr, MyAligned.alignOf)
+    isAlignedCheck(bb.addr, MyAligned.alignOf)
+    isAlignedCheck(z, MyAligned.alignOf)
+        
+    cc.a = x1.a + x1.a + z.a
+    bb.a = x2.a - z.a
+    
+    isAlignedCheck(result.addr, MyAligned.alignOf)
+    result.a = cc.a + bb.a + x2.a
+    
+  return capturingLambda
+
+
+let q1 = lambdaTest2(MyAligned(a: 1.0), (ref MyAligned)(a: 2.0))
+let q2 = lambdaTest2(MyAligned( a: -1.0), (ref MyAligned)(a: -2.0))
+
+isAlignedCheck(rawEnv(q1), MyAligned.alignOf)
+isAlignedCheck(rawEnv(q2), MyAligned.alignOf)
+discard q1(MyAligned(a: 1.0))
+discard q2(MyAligned(a: -1.0))
+
+
+#-----------------------------------------------------------------------------
+
+block:
+  var s: seq[seq[MyAligned]]
+  for len in 0..128:
+    s.add newSeq[MyAligned](len)
+    for i in 0..<len:
+      s[^1][i] = MyAligned(a: 1.0)
+
+    if len > 0:
+      isAlignedCheck(s[^1][0].addr, MyAligned.alignOf)
+  
+echo "y"
diff --git a/tests/arc/thavlak_orc_stress.nim b/tests/arc/thavlak_orc_stress.nim
new file mode 100644
index 000000000..862e1a097
--- /dev/null
+++ b/tests/arc/thavlak_orc_stress.nim
@@ -0,0 +1,414 @@
+discard """
+  cmd: "nim c --gc:orc -d:useMalloc -d:nimStressOrc $file"
+  valgrind: "leaks"
+  output: "done"
+"""
+
+import tables
+import sets
+import net
+
+type
+  BasicBlock = object
+    inEdges: seq[ref BasicBlock]
+    outEdges: seq[ref BasicBlock]
+    name: int
+
+proc newBasicBlock(name: int): ref BasicBlock =
+  new(result)
+  result.inEdges = newSeq[ref BasicBlock]()
+  result.outEdges = newSeq[ref BasicBlock]()
+  result.name = name
+
+proc hash(x: ref BasicBlock): int {.inline.} =
+  result = x.name
+
+type
+  BasicBlockEdge = object
+    fr: ref BasicBlock
+    to: ref BasicBlock
+
+  Cfg = object
+    basicBlockMap: Table[int, ref BasicBlock]
+    edgeList: seq[BasicBlockEdge]
+    startNode: ref BasicBlock
+
+proc newCfg(): Cfg =
+  result.basicBlockMap = initTable[int, ref BasicBlock]()
+  result.edgeList = newSeq[BasicBlockEdge]()
+
+proc createNode(self: var Cfg, name: int): ref BasicBlock =
+  #var node: ref BasicBlock
+  result = self.basicBlockMap.getOrDefault(name)
+  if result == nil:
+    result = newBasicBlock(name)
+    self.basicBlockMap.add name, result
+
+  if self.startNode == nil:
+    self.startNode = result
+
+proc addEdge(self: var Cfg, edge: BasicBlockEdge) =
+  self.edgeList.add(edge)
+
+proc getNumNodes(self: Cfg): int =
+  self.basicBlockMap.len
+
+proc newBasicBlockEdge(cfg: var Cfg, fromName: int, toName: int) =
+  var newEdge = BasicBlockEdge()
+  newEdge.fr = cfg.createNode(fromName)
+  newEdge.to = cfg.createNode(toName)
+  newEdge.fr.outEdges.add(newEdge.to)
+  newEdge.to.inEdges.add(newEdge.fr)
+  cfg.addEdge(newEdge)
+
+type
+  SimpleLoop = object
+    basicBlocks: seq[ref BasicBlock] # TODO: set here
+    children: seq[ref SimpleLoop] # TODO: set here
+    parent: ref SimpleLoop
+    header: ref BasicBlock
+    isRoot: bool
+    isReducible: bool
+    counter: int
+    nestingLevel: int
+    depthLevel: int
+
+proc newSimpleLoop(): ref SimpleLoop =
+  new(result)
+  result.basicBlocks = newSeq[ref BasicBlock]()
+  result.children = newSeq[ref SimpleLoop]()
+  result.parent = nil
+  result.header = nil
+  result.isRoot = false
+  result.isReducible = true
+  result.counter = 0
+  result.nestingLevel = 0
+  result.depthLevel = 0
+
+proc addNode(self: ref SimpleLoop, bb: ref BasicBlock) =
+  self.basicBlocks.add bb
+
+proc addChildLoop(self: ref SimpleLoop, loop: ref SimpleLoop) =
+  self.children.add loop
+
+proc setParent(self: ref SimpleLoop, parent: ref SimpleLoop) =
+  self.parent = parent
+  self.parent.addChildLoop(self)
+
+proc setHeader(self: ref SimpleLoop, bb: ref BasicBlock) =
+  self.basicBlocks.add(bb)
+  self.header = bb
+
+proc setNestingLevel(self: ref SimpleLoop, level: int) =
+  self.nestingLevel = level
+  if level == 0: self.isRoot = true
+
+var loop_counter: int = 0
+
+type
+  Lsg = object
+    loops: seq[ref SimpleLoop]
+    root: ref SimpleLoop
+
+proc createNewLoop(self: var Lsg): ref SimpleLoop =
+  var s = newSimpleLoop()
+  loop_counter += 1
+  s.counter = loop_counter
+  s
+
+proc addLoop(self: var Lsg, l: ref SimpleLoop) =
+  self.loops.add l
+
+proc newLsg(): Lsg =
+  result.loops = newSeq[ref SimpleLoop]()
+  result.root = result.createNewLoop()
+  result.root.setNestingLevel(0)
+  result.addLoop(result.root)
+
+proc getNumLoops(self: Lsg): int =
+  self.loops.len
+
+type
+  UnionFindNode = object
+    parent: ref UnionFindNode
+    bb: ref BasicBlock
+    l: ref SimpleLoop
+    dfsNumber: int
+
+proc newUnionFindNode(): ref UnionFindNode =
+  new(result)
+  result.parent = nil
+  result.bb = nil
+  result.l = nil
+  result.dfsNumber = 0
+
+proc initNode(self: ref UnionFindNode, bb: ref BasicBlock, dfsNumber: int) =
+  self.parent = self
+  self.bb = bb
+  self.dfsNumber = dfsNumber
+
+proc findSet(self: ref UnionFindNode): ref UnionFindNode =
+  var nodeList = newSeq[ref UnionFindNode]()
+  var node = self
+
+  while node != node.parent:
+    var parent = node.parent
+    if parent != parent.parent: nodeList.add node
+    node = parent
+
+  for iter in nodeList: iter.parent = node.parent
+  node
+
+proc union(self: ref UnionFindNode, unionFindNode: ref UnionFindNode) =
+  self.parent = unionFindNode
+
+
+const
+  BB_NONHEADER    = 1 # a regular BB
+  BB_REDUCIBLE    = 2 # reducible loop
+  BB_SELF         = 3 # single BB loop
+  BB_IRREDUCIBLE  = 4 # irreducible loop
+  BB_DEAD         = 5 # a dead BB
+
+  # # Marker for uninitialized nodes.
+  UNVISITED = -1
+
+  # # Safeguard against pathologic algorithm behavior.
+  MAXNONBACKPREDS = (32 * 1024)
+
+type
+  HavlakLoopFinder = object
+    cfg: Cfg
+    lsg: Lsg
+
+proc newHavlakLoopFinder(cfg: sink Cfg, lsg: sink Lsg): HavlakLoopFinder =
+  result.cfg = cfg
+  result.lsg = lsg
+
+proc isAncestor(w: int, v: int, last: seq[int]): bool =
+  w <= v and v <= last[w]
+
+proc dfs(currentNode: ref BasicBlock, nodes: var seq[ref UnionFindNode], number: var Table[ref BasicBlock, int], last: var seq[int], current: int): int =
+  nodes[current].initNode(currentNode, current)
+  number[currentNode] = current
+
+  var lastid = current
+  for target in currentNode.outEdges:
+    if number[target] == UNVISITED:
+      lastid = dfs(target, nodes, number, last, lastid + 1)
+
+  last[number[currentNode]] = lastid
+  return lastid
+
+proc findLoops(self: var HavlakLoopFinder): int =
+  var startNode = self.cfg.startNode
+  if startNode == nil: return 0
+  var size = self.cfg.getNumNodes
+
+  var nonBackPreds = newSeq[HashSet[int]]()
+  var backPreds = newSeq[seq[int]]()
+  var number = initTable[ref BasicBlock, int]()
+  var header = newSeq[int](size)
+  var types = newSeq[int](size)
+  var last = newSeq[int](size)
+  var nodes = newSeq[ref UnionFindNode]()
+
+  for i in 1..size:
+    nonBackPreds.add initHashSet[int](1)
+    backPreds.add newSeq[int]()
+    nodes.add newUnionFindNode()
+
+  # Step a:
+  #   - initialize all nodes as unvisited.
+  #   - depth-first traversal and numbering.
+  #   - unreached BB's are marked as dead.
+  #
+  for v in self.cfg.basicBlockMap.values: number[v] = UNVISITED
+  discard dfs(startNode, nodes, number, last, 0)
+
+  # Step b:
+  #   - iterate over all nodes.
+  #
+  #   A backedge comes from a descendant in the DFS tree, and non-backedges
+  #   from non-descendants (following Tarjan).
+  #
+  #   - check incoming edges 'v' and add them to either
+  #     - the list of backedges (backPreds) or
+  #     - the list of non-backedges (nonBackPreds)
+  #
+  for w in 0 ..< size:
+    header[w] = 0
+    types[w]  = BB_NONHEADER
+
+    var nodeW = nodes[w].bb
+    if nodeW != nil:
+      for nodeV in nodeW.inEdges:
+        var v = number[nodeV]
+        if v != UNVISITED:
+          if isAncestor(w, v, last):
+            backPreds[w].add v
+          else:
+            nonBackPreds[w].incl v
+    else:
+      types[w] = BB_DEAD
+
+  # Start node is root of all other loops.
+  header[0] = 0
+
+  # Step c:
+  #
+  # The outer loop, unchanged from Tarjan. It does nothing except
+  # for those nodes which are the destinations of backedges.
+  # For a header node w, we chase backward from the sources of the
+  # backedges adding nodes to the set P, representing the body of
+  # the loop headed by w.
+  #
+  # By running through the nodes in reverse of the DFST preorder,
+  # we ensure that inner loop headers will be processed before the
+  # headers for surrounding loops.
+
+  for w in countdown(size - 1, 0):
+    # this is 'P' in Havlak's paper
+    var nodePool = newSeq[ref UnionFindNode]()
+
+    var nodeW = nodes[w].bb
+    if nodeW != nil: # dead BB
+      # Step d:
+      for v in backPreds[w]:
+        if v != w:
+          nodePool.add nodes[v].findSet
+        else:
+          types[w] = BB_SELF
+
+      # Copy nodePool to workList.
+      #
+      var workList = newSeq[ref UnionFindNode]()
+      for x in nodePool: workList.add x
+
+      if nodePool.len != 0: types[w] = BB_REDUCIBLE
+
+      # work the list...
+      #
+      while workList.len > 0:
+        var x = workList[0]
+        workList.del(0)
+
+        # Step e:
+        #
+        # Step e represents the main difference from Tarjan's method.
+        # Chasing upwards from the sources of a node w's backedges. If
+        # there is a node y' that is not a descendant of w, w is marked
+        # the header of an irreducible loop, there is another entry
+        # into this loop that avoids w.
+        #
+
+        # The algorithm has degenerated. Break and
+        # return in this case.
+        #
+        var nonBackSize = nonBackPreds[x.dfsNumber].len
+        if nonBackSize > MAXNONBACKPREDS: return 0
+
+        for iter in nonBackPreds[x.dfsNumber]:
+          var y = nodes[iter]
+          var ydash = y.findSet
+
+          if not isAncestor(w, ydash.dfsNumber, last):
+            types[w] = BB_IRREDUCIBLE
+            nonBackPreds[w].incl ydash.dfsNumber
+          else:
+            if ydash.dfsNumber != w and not nodePool.contains(ydash):
+              workList.add ydash
+              nodePool.add ydash
+
+      # Collapse/Unionize nodes in a SCC to a single node
+      # For every SCC found, create a loop descriptor and link it in.
+      #
+      if (nodePool.len > 0) or (types[w] == BB_SELF):
+        var l = self.lsg.createNewLoop
+
+        l.setHeader(nodeW)
+        l.isReducible = types[w] != BB_IRREDUCIBLE
+
+        # At this point, one can set attributes to the loop, such as:
+        #
+        # the bottom node:
+        #    iter  = backPreds(w).begin();
+        #    loop bottom is: nodes(iter).node;
+        #
+        # the number of backedges:
+        #    backPreds(w).size()
+        #
+        # whether this loop is reducible:
+        #    types(w) != BB_IRREDUCIBLE
+        #
+        nodes[w].l = l
+
+        for node in nodePool:
+          # Add nodes to loop descriptor.
+          header[node.dfsNumber] = w
+          node.union(nodes[w])
+
+          # Nested loops are not added, but linked together.
+          var node_l = node.l
+          if node_l != nil:
+            node_l.setParent(l)
+          else:
+            l.addNode(node.bb)
+
+        self.lsg.addLoop(l)
+
+  result = self.lsg.getNumLoops
+
+
+type
+  LoopTesterApp = object
+    cfg: Cfg
+    lsg: Lsg
+
+proc newLoopTesterApp(): LoopTesterApp =
+  result.cfg = newCfg()
+  result.lsg = newLsg()
+
+proc buildDiamond(self: var LoopTesterApp, start: int): int =
+  var bb0 = start
+  newBasicBlockEdge(self.cfg, bb0, bb0 + 1)
+  newBasicBlockEdge(self.cfg, bb0, bb0 + 2)
+  newBasicBlockEdge(self.cfg, bb0 + 1, bb0 + 3)
+  newBasicBlockEdge(self.cfg, bb0 + 2, bb0 + 3)
+  result = bb0 + 3
+
+proc buildConnect(self: var LoopTesterApp, start1: int, end1: int) =
+  newBasicBlockEdge(self.cfg, start1, end1)
+
+proc buildStraight(self: var LoopTesterApp, start: int, n: int): int =
+  for i in 0..n-1:
+    self.buildConnect(start + i, start + i + 1)
+  result = start + n
+
+proc buildBaseLoop(self: var LoopTesterApp, from1: int): int =
+  var header   = self.buildStraight(from1, 1)
+  var diamond1 = self.buildDiamond(header)
+  var d11      = self.buildStraight(diamond1, 1)
+  var diamond2 = self.buildDiamond(d11)
+  var footer   = self.buildStraight(diamond2, 1)
+
+  self.buildConnect(diamond2, d11)
+  self.buildConnect(diamond1, header)
+  self.buildConnect(footer, from1)
+  result = self.buildStraight(footer, 1)
+
+proc run(self: var LoopTesterApp) =
+  discard self.cfg.createNode(0)
+  discard self.buildBaseLoop(0)
+  discard self.cfg.createNode(1)
+  self.buildConnect(0, 2)
+
+  for i in 1..8:
+    # yes, it's 8 and it's correct.
+    var h = newHavlakLoopFinder(self.cfg, newLsg())
+    discard h.findLoops()
+
+  echo "done"
+
+var l = newLoopTesterApp()
+l.run()
diff --git a/tests/arc/theavy_recursion.nim b/tests/arc/theavy_recursion.nim
new file mode 100644
index 000000000..9813331f4
--- /dev/null
+++ b/tests/arc/theavy_recursion.nim
@@ -0,0 +1,43 @@
+discard """
+  output: "yay"
+  cmd: "nim c --gc:arc $file"
+"""
+
+# bug #15122
+
+import tables
+
+type
+  BENodeKind* = enum
+    tkEof,
+    tkBytes,
+    tkList,
+    tkDict
+
+  BENode* = object
+    case kind: BENodeKind
+    of tkBytes: strVal: string
+    of tkList: listVal: seq[BENode]
+    of tkDict: dictVal*: Table[string, BENode]
+    else:
+      discard
+
+proc unused(s: string): BENode =
+  # bad:
+  result = BENode(kind: tkBytes, strVal: "abc")
+
+proc main =
+  var data = {
+    "examples": {
+      "values": BENode(
+        kind: tkList,
+        listVal: @[BENode(kind: tkBytes, strVal: "test")]
+      )
+    }.toTable()
+  }.toTable()
+
+  # For ARC listVal is empty for some reason
+  doAssert data["examples"]["values"].listVal[0].strVal == "test"
+
+main()
+echo "yay"
diff --git a/tests/arc/timportedobj.nim b/tests/arc/timportedobj.nim
new file mode 100644
index 000000000..9a88a9468
--- /dev/null
+++ b/tests/arc/timportedobj.nim
@@ -0,0 +1,14 @@
+discard """
+  cmd: "nim c --gc:arc $file"
+  action: "compile"
+"""
+
+# bug #13269
+
+import posix
+proc foo*() =
+  var last = newSeq[Stat]()
+  var next = last
+  for i in 0..3:
+    last = next
+foo()
diff --git a/tests/arc/titeration_doesnt_copy.nim b/tests/arc/titeration_doesnt_copy.nim
new file mode 100644
index 000000000..e510a6eff
--- /dev/null
+++ b/tests/arc/titeration_doesnt_copy.nim
@@ -0,0 +1,67 @@
+discard """
+  output: "true"
+"""
+
+type
+  Idx = object
+    i: int
+  Node = object
+    n: int
+    next: seq[Idx]
+  FooBar = object
+    s: seq[Node]
+
+proc `=copy`(dest: var Idx; source: Idx) {.error.}
+proc `=copy`(dest: var Node; source: Node) {.error.}
+proc `=copy`(dest: var FooBar; source: FooBar) {.error.}
+
+proc doSomething(ss: var seq[int], s: FooBar) =
+  for i in 0 .. s.s.len-1:
+    for elm in items s.s[i].next:
+      ss.add s.s[elm.i].n
+
+when isMainModule:
+  const foo = FooBar(s: @[Node(n: 1, next: @[Idx(i: 0)])])
+  var ss: seq[int]
+  doSomething(ss, foo)
+  echo ss == @[1]
+
+from sequtils import mapIt
+from strutils import join
+
+proc toBinSeq*(b: uint8): seq[uint8] =
+  ## Return binary sequence from each bits of uint8.
+  runnableExamples:
+    from sequtils import repeat
+    doAssert 0'u8.toBinSeq == 0'u8.repeat(8)
+    doAssert 0b1010_1010.toBinSeq == @[1'u8, 0, 1, 0, 1, 0, 1, 0]
+  result = @[]
+  var c = b
+  for i in 1..8:
+    result.add (uint8(c and 0b1000_0000) shr 7)
+    c = c shl 1
+
+proc toBinString*(data: openArray[uint8], col: int): string =
+  ## Return binary string from each bits of uint8.
+  runnableExamples:
+    doAssert @[0b0000_1111'u8, 0b1010_1010].toBinString(8) == "0000111110101010"
+    doAssert @[0b1000_0000'u8, 0b0000_0000].toBinString(1) == "10"
+  result = ""
+  for b in items data.mapIt(it.toBinSeq.mapIt(it.`$`[0].char)):
+    for i, c in b:
+      if i < col:
+        result.add c
+
+doAssert @[0b0000_1111'u8, 0b1010_1010].toBinString(8) == "0000111110101010"
+doAssert @[0b1000_0000'u8, 0b0000_0000].toBinString(1) == "10"
+
+block: # bug #23982
+  iterator `..`(a, b: ptr int16): ptr int16 = discard
+  var a: seq[int16] #; let p = a[0].addr
+  var b: seq[ptr int16]
+
+  try:
+    for x in a[0].addr .. b[1]: # `p .. b[1]` works
+      discard
+  except IndexDefect:
+    discard
diff --git a/tests/arc/tkeys_lent.nim b/tests/arc/tkeys_lent.nim
new file mode 100644
index 000000000..2c92350b1
--- /dev/null
+++ b/tests/arc/tkeys_lent.nim
@@ -0,0 +1,17 @@
+discard """
+  output: '''{"string": 2}'''
+  cmd: "nim c --gc:orc $file"
+"""
+
+import tables
+
+proc use(x: int) = echo x
+
+proc main =
+  var tab = {"string": 1}.toTable
+  for keyAAA in tab.keys():
+    template alias(): untyped = tab[keyAAA]
+    alias() = 2
+  echo tab
+
+main()
diff --git a/tests/arc/tmalloc.nim b/tests/arc/tmalloc.nim
new file mode 100644
index 000000000..1d72646c8
--- /dev/null
+++ b/tests/arc/tmalloc.nim
@@ -0,0 +1,16 @@
+discard """
+  matrix: "--mm:arc -d:useMalloc; --mm:arc"
+"""
+
+block: # bug #22058
+  template foo(): auto =
+    {.noSideEffect.}:
+      newSeq[byte](1)
+
+  type V = object
+    v: seq[byte]
+
+  proc bar(): V =
+    V(v: foo())
+
+  doAssert bar().v == @[byte(0)]
diff --git a/tests/arc/tmarshal.nim b/tests/arc/tmarshal.nim
new file mode 100644
index 000000000..f7ec6e2c5
--- /dev/null
+++ b/tests/arc/tmarshal.nim
@@ -0,0 +1,140 @@
+discard """
+  cmd: "nim c --gc:orc $file"
+  output: '''
+{"age": 12, "bio": "Я Cletus", "blob": [65, 66, 67, 128], "name": "Cletus"}
+true
+true
+alpha 100
+omega 200
+0'''
+"""
+
+import marshal
+
+template testit(x) = discard $$to[typeof(x)]($$x)
+
+var x: array[0..4, array[0..4, string]] = [
+  ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"],
+  ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"],
+  ["test", "1", "2", "3", "4"]]
+
+proc blockA =
+  testit(x)
+  var test2: tuple[name: string, s: int] = ("tuple test", 56)
+  testit(test2)
+
+blockA()
+
+type
+  TE = enum
+    blah, blah2
+
+  TestObj = object
+    test, asd: int
+    case test2: TE
+    of blah:
+      help: string
+    else:
+      discard
+
+  PNode = ref TNode
+  TNode = object
+    next, prev: PNode
+    data: string
+
+proc buildList(): PNode =
+  new(result)
+  new(result.next)
+  new(result.prev)
+  result.data = "middle"
+  result.next.data = "next"
+  result.prev.data = "prev"
+  result.next.next = result.prev
+  result.next.prev = result
+  result.prev.next = result
+  result.prev.prev = result.next
+
+proc blockB =
+  var test3: TestObj
+  test3.test = 42
+  test3.test2 = blah
+  testit(test3)
+
+  var test4: ref tuple[a, b: string]
+  new(test4)
+  test4.a = "ref string test: A"
+  test4.b = "ref string test: B"
+  testit(test4)
+
+  var test5 = @[(0,1),(2,3),(4,5)]
+  testit(test5)
+
+  var test7 = buildList()
+  testit(test7)
+
+  var test6: set[char] = {'A'..'Z', '_'}
+  testit(test6)
+
+blockB()
+
+# bug #1352
+
+type
+  Entity = object of RootObj
+    name: string
+
+  Person = object of Entity
+    age: int
+    bio: string
+    blob: string
+
+proc blockC =
+  var instance1 = Person(name: "Cletus", age: 12,
+                        bio: "Я Cletus",
+                        blob: "ABC\x80")
+  echo($$instance1)
+  echo(to[Person]($$instance1).bio == instance1.bio) # true
+  echo(to[Person]($$instance1).blob == instance1.blob) # true
+
+blockC()
+
+# bug 5757
+
+type
+  Something = object
+    x: string
+    y: int
+
+proc blockD =
+  var data1 = """{"x": "alpha", "y": 100}"""
+  var data2 = """{"x": "omega", "y": 200}"""
+
+  var r = to[Something](data1)
+
+  echo r.x, " ", r.y
+
+  r = to[Something](data2)
+
+  echo r.x, " ", r.y
+
+blockD()
+
+type
+  Foo = object
+    a1: string
+    a2: string
+    a3: seq[string]
+    a4: seq[int]
+    a5: seq[int]
+    a6: seq[int]
+
+proc blockE =
+  var foo = Foo(a2: "", a4: @[], a6: @[1])
+  foo.a6.setLen 0
+  doAssert $$foo == """{"a1": "", "a2": "", "a3": [], "a4": [], "a5": [], "a6": []}"""
+  testit(foo)
+
+blockE()
+
+GC_fullCollect()
+echo getOccupiedMem()
diff --git a/tests/arc/tmove_regression.nim b/tests/arc/tmove_regression.nim
new file mode 100644
index 000000000..7d9a867c3
--- /dev/null
+++ b/tests/arc/tmove_regression.nim
@@ -0,0 +1,22 @@
+discard """
+  output: '''/1/2
+/1
+/
+'''
+""""
+
+# bug #22001
+
+import std / [os, strutils]
+
+proc finOp2(path, name: string): (string, File) = # Find & open FIRST `name`
+  var current = path
+  while true:
+    if current.isRootDir: break # <- current=="" => current.isRootDir
+    current = current.parentDir
+    let dir = current
+    echo dir.replace('\\', '/')  # Commenting out try/except below hides bug
+    try: result[0] = dir/name; result[1] = open(result[0]); return
+    except CatchableError: discard
+
+discard finOp2("/1/2/3", "4")  # All same if this->inside a proc
diff --git a/tests/arc/tmovebug.nim b/tests/arc/tmovebug.nim
new file mode 100644
index 000000000..8ad7c955c
--- /dev/null
+++ b/tests/arc/tmovebug.nim
@@ -0,0 +1,841 @@
+discard """
+  cmd: "nim c --gc:arc $file"
+  output: '''5
+(w: 5)
+(w: -5)
+c.text = hello
+c.text = hello
+p.text = hello
+p.toks = @["hello"]
+c.text = hello
+c[].text = hello
+pA.text = hello
+pA.toks = @["hello"]
+c.text = hello
+c.text = hello
+pD.text = hello
+pD.toks = @["hello"]
+c.text = hello
+c.text = hello
+pOD.text = hello
+pOD.toks = @["hello"]
+fff
+fff
+2
+fff
+fff
+2
+fff
+fff
+2
+mmm
+fff
+fff
+fff
+3
+mmm
+sink me (sink)
+assign me (not sink)
+sink me (not sink)
+sinked and not optimized to a bitcopy
+sinked and not optimized to a bitcopy
+sinked and not optimized to a bitcopy
+(data: @[0, 0])
+(data: @[0, 0])
+(data: @[0, 0])
+(data: @[0, 0])
+(data: @[0, 0])
+(data: @[0, 0])
+(data: @[0, 0])
+100
+hey
+hey
+(a: "a", b: 2)
+ho
+(a: "b", b: 3)
+(b: "b", a: 2)
+ho
+(b: "a", a: 3)
+hey
+break
+break
+hey
+ho
+hey
+ho
+ho
+king
+live long; long live
+king
+hi
+try
+bye
+()
+()
+()
+1
+destroy
+1
+destroy
+1
+destroy
+copy (self-assign)
+1
+destroy
+1
+destroy
+1
+destroy
+destroy
+copy
+@[(f: 2), (f: 2), (f: 3)]
+destroy
+destroy
+destroy
+sink
+sink
+destroy
+copy
+(f: 1)
+destroy
+destroy
+part-to-whole assigment:
+sink
+(children: @[])
+destroy
+sink
+(children: @[])
+destroy
+copy
+destroy
+(f: 1)
+destroy
+'''
+"""
+
+# move bug
+type
+  TMyObj = object
+    p: pointer
+    len: int
+
+var destroyCounter = 0
+
+proc `=destroy`(o: var TMyObj) =
+  if o.p != nil:
+    dealloc o.p
+    o.p = nil
+    inc destroyCounter
+
+proc `=`(dst: var TMyObj, src: TMyObj) =
+  `=destroy`(dst)
+  dst.p = alloc(src.len)
+  dst.len = src.len
+
+proc `=sink`(dst: var TMyObj, src: TMyObj) =
+  `=destroy`(dst)
+  dst.p = src.p
+  dst.len = src.len
+
+type
+  TObjKind = enum Z, A, B
+  TCaseObj = object
+    case kind: TObjKind
+    of Z: discard
+    of A:
+      x1: int # this int plays important role
+      x2: TMyObj
+    of B:
+      y: TMyObj
+
+proc use(a: TCaseObj) = discard
+
+proc moveBug(i: var int) =
+  var a: array[2, TCaseObj]
+  a[i] = TCaseObj(kind: A, x1: 5000, x2: TMyObj(len: 5, p: alloc(5))) # 1
+  a[i+1] = a[i] # 2
+  inc i
+  use(a[i-1])
+
+var x = 0
+moveBug(x)
+
+proc moveBug2(): (TCaseObj, TCaseObj) =
+  var a: array[2, TCaseObj]
+  a[0] = TCaseObj(kind: A, x1: 5000, x2: TMyObj(len: 5, p: alloc(5)))
+  a[1] = a[0] # can move 3
+  result[0] = TCaseObj(kind: A, x1: 5000, x2: TMyObj(len: 5, p: alloc(5))) # 4
+  result[1] = result[0] # 5
+
+proc main =
+  discard moveBug2()
+
+main()
+echo destroyCounter
+
+# bug #13314
+
+type
+  O = object
+    v: int
+  R = ref object
+    w: int
+
+proc `$`(r: R): string = $r[]
+
+proc tbug13314 =
+  var t5 = R(w: 5)
+  var execute = proc () =
+    echo t5
+
+  execute()
+  t5.w = -5
+  execute()
+
+tbug13314()
+
+#-------------------------------------------------------------------------
+# bug #13368
+
+import strutils
+proc procStat() =
+  for line in @["a b", "c d", "e f"]:
+    let cols = line.splitWhitespace(maxSplit=1)
+    let x = cols[0]
+    let (nm, rest) = (cols[0], cols[1])
+procStat()
+
+
+# bug #14269
+
+import sugar, strutils
+
+type
+  Cursor = object
+    text: string
+  Parsed = object
+    text: string
+    toks: seq[string]
+
+proc tokenize(c: var Cursor): seq[string] =
+  dump c.text
+  return c.text.splitWhitespace()
+
+proc parse(): Parsed =
+  var c = Cursor(text: "hello")
+  dump c.text
+  return Parsed(text: c.text, toks: c.tokenize) # note: c.tokenized uses c.text
+
+let p = parse()
+dump p.text
+dump p.toks
+
+
+proc tokenizeA(c: ptr Cursor): seq[string] =
+  dump c[].text
+  return c[].text.splitWhitespace()
+
+proc parseA(): Parsed =
+  var c = Cursor(text: "hello")
+  dump c.text
+  return Parsed(text: c.text, toks: c.addr.tokenizeA) # note: c.tokenized uses c.text
+
+let pA = parseA()
+dump pA.text
+dump pA.toks
+
+
+proc tokenizeD(c: Cursor): seq[string] =
+  dump c.text
+  return c.text.splitWhitespace()
+
+proc parseD(): Parsed =
+  var c = cast[ptr Cursor](alloc0(sizeof(Cursor)))
+  c[] = Cursor(text: "hello")
+  dump c.text
+  return Parsed(text: c.text, toks: c[].tokenizeD) # note: c.tokenized uses c.text
+
+let pD = parseD()
+dump pD.text
+dump pD.toks
+
+# Bug would only pop up with owned refs
+proc tokenizeOD(c: Cursor): seq[string] =
+  dump c.text
+  return c.text.splitWhitespace()
+
+proc parseOD(): Parsed =
+  var c = new Cursor
+  c[] = Cursor(text: "hello")
+  dump c.text
+  return Parsed(text: c.text, toks: c[].tokenizeOD) # note: c.tokenized uses c.text
+
+let pOD = parseOD()
+dump pOD.text
+dump pOD.toks
+
+when false:
+  # Bug would only pop up with owned refs and implicit derefs, but since they don't work together..
+  {.experimental: "implicitDeref".}
+  proc tokenizeOHD(c: Cursor): seq[string] =
+    dump c.text
+    return c.text.splitWhitespace()
+
+  proc parseOHD(): Parsed =
+    var c = new Cursor
+    c[] = Cursor(text: "hello")
+    dump c.text
+    return Parsed(text: c.text, toks: c.tokenizeOHD) # note: c.tokenized uses c.text
+
+  let pOHD = parseOHD()
+  dump pOHD.text
+  dump pOHD.toks
+
+# bug #13456
+
+iterator combinations[T](s: openArray[T], k: int): seq[T] =
+  let n = len(s)
+  assert k >= 0 and k <= n
+  var pos = newSeq[int](k)
+  var current = newSeq[T](k)
+  for i in 0..k-1:
+    pos[k-i-1] = i
+  var done = false
+  while not done:
+    for i in 0..k-1:
+      current[i] = s[pos[k-i-1]]
+    yield current
+    var i = 0
+    while i < k:
+      pos[i] += 1
+      if pos[i] < n-i:
+        for j in 0..i-1:
+          pos[j] = pos[i] + i - j
+        break
+      i += 1
+    if i >= k:
+      break
+
+type
+  UndefEx = object of ValueError
+
+proc main2 =
+  var delayedSyms = @[1, 2, 3]
+  var unp: seq[int]
+  block myb:
+    for a in 1 .. 2:
+      if delayedSyms.len > a:
+        unp = delayedSyms
+        for t in unp.combinations(a + 1):
+          try:
+            var h = false
+            for k in t:
+              echo "fff"
+            if h: continue
+            if true:
+              raise newException(UndefEx, "forward declaration")
+            break myb
+          except UndefEx:
+            echo t.len
+        echo "mmm"
+
+main2()
+
+
+
+type ME = object
+  who: string
+
+proc `=`(x: var ME, y: ME) =
+  if y.who.len > 0: echo "assign ",y.who
+
+proc `=sink`(x: var ME, y: ME) =
+  if y.who.len > 0: echo "sink ",y.who
+
+var dump: ME
+template use(x) = dump = x
+template def(x) = x = dump
+
+var c = true
+
+proc shouldSink() =
+  var x = ME(who: "me (sink)")
+  use(x) # we analyse this
+  if c: def(x)
+  else: def(x)
+  use(x) # ok, with the [else] part.
+
+shouldSink()
+
+dump = ME()
+
+proc shouldNotSink() =
+  var x = ME(who: "me (not sink)")
+  use(x) # we analyse this
+  if c: def(x)
+  use(x) # Not ok without the '[else]'
+
+shouldNotSink()
+
+# bug #14568
+import os
+
+type O2 = object
+  s: seq[int]
+
+proc `=sink`(dest: var O2, src: O2) =
+  echo "sinked and not optimized to a bitcopy"
+
+var testSeq: O2
+
+proc update() =
+  # testSeq.add(0) # uncommenting this line fixes the leak
+  testSeq = O2(s: @[])
+  testSeq.s.add(0)
+
+for i in 1..3:
+  update()
+
+
+# bug #14961
+type
+  Foo = object
+    data: seq[int]
+
+proc initFoo(len: int): Foo =
+  result = (let s = newSeq[int](len); Foo(data: s) )
+
+var f = initFoo(2)
+echo initFoo(2)
+
+proc initFoo2(len: int) =
+  echo   if true:
+             let s = newSeq[int](len); Foo(data: s)
+         else:
+             let s = newSeq[int](len); Foo(data: s)
+
+initFoo2(2)
+
+proc initFoo3(len: int) =
+  echo (block:
+         let s = newSeq[int](len); Foo(data: s))
+
+initFoo3(2)
+
+proc initFoo4(len: int) =
+  echo (let s = newSeq[int](len); Foo(data: s))
+
+initFoo4(2)
+
+proc initFoo5(len: int) =
+  echo (case true
+        of true:
+          let s = newSeq[int](len); Foo(data: s)
+        of false:
+          let s = newSeq[int](len); Foo(data: s))
+
+initFoo5(2)
+
+proc initFoo6(len: int) =
+  echo (block:
+          try:
+            let s = newSeq[int](len); Foo(data: s)
+          finally: discard)
+
+initFoo6(2)
+
+proc initFoo7(len: int) =
+  echo (block:
+          try:
+            raise newException(CatchableError, "sup")
+            let s = newSeq[int](len); Foo(data: s)
+          except CatchableError:
+            let s = newSeq[int](len); Foo(data: s) )
+
+initFoo7(2)
+
+
+# bug #14902
+iterator zip[T](s: openArray[T]): (T, T) =
+  var i = 0
+  while i < 10:
+    yield (s[i mod 2], s[i mod 2 + 1])
+    inc i
+
+var lastMem = int.high
+
+proc leak =
+  const len = 10
+  var x = @[newString(len), newString(len), newString(len)]
+
+  var c = 0
+  for (a, b) in zip(x):
+    let newMem = getOccupiedMem()
+    assert newMem <= lastMem
+    lastMem = newMem
+    c += a.len
+  echo c
+
+leak()
+
+
+proc consume(a: sink string) = echo a
+
+proc weirdScopes =
+  if (let a = "hey"; a.len > 0):
+    echo a
+
+  while (let a = "hey"; a.len > 0):
+    echo a
+    break
+
+  var a = block: (a: "a", b: 2)
+  echo a
+  (discard; a) = (echo "ho"; (a: "b", b: 3))
+  echo a
+
+  var b = try: (b: "b", a: 2)
+          except: raise
+  echo b
+  (discard; b) = (echo "ho"; (b: "a", a: 3))
+  echo b
+
+  var s = "break"
+  consume((echo "hey"; s))
+  echo s
+
+  echo (block:
+          var a = "hey"
+          (echo "hey"; "ho"))
+
+  var b2 = "ho"
+  echo (block:
+          var a = "hey"
+          (echo "hey"; b2))
+  echo b2
+
+  type status = enum
+    alive
+
+  var king = "king"
+  echo (block:
+          var a = "a"
+          when true:
+            var b = "b"
+            case alive
+            of alive:
+              try:
+                var c = "c"
+                if true:
+                  king
+                else:
+                  "the abyss"
+              except:
+                echo "he ded"
+                "dead king")
+  echo "live long; long live"
+  echo king
+
+weirdScopes()
+
+
+# bug #14985
+proc getScope(): string =
+  if true:
+    return "hi"
+  else:
+    "else"
+
+echo getScope()
+
+proc getScope3(): string =
+  try:
+    "try"
+  except:
+    return "except"
+
+echo getScope3()
+
+proc getScope2(): string =
+  case true
+  of true:
+    return "bye"
+  else:
+    "else"
+
+echo getScope2()
+
+
+#--------------------------------------------------------------------
+#bug  #15609
+
+type
+  Wrapper = object
+    discard
+
+proc newWrapper(): ref Wrapper =
+  new(result)
+  result
+
+
+proc newWrapper2(a: int): ref Wrapper =
+  new(result)
+  if a > 0:
+    result
+  else:
+    new(Wrapper)
+
+
+let w1 = newWrapper()
+echo $w1[]
+
+let w2 = newWrapper2(1)
+echo $w2[]
+
+let w3 = newWrapper2(-1)
+echo $w3[]
+
+
+#--------------------------------------------------------------------
+#self-assignments
+
+# Self-assignments that are not statically determinable will get
+# turned into `=copy` calls as caseBracketExprCopy demonstrates.
+# (`=copy` handles self-assignments at runtime)
+
+type
+  OO = object
+    f: int
+  W = object
+    o: OO
+
+proc `=destroy`(x: var OO) =
+  if x.f != 0:
+    echo "destroy"
+    x.f = 0
+
+proc `=sink`(x: var OO, y: OO) =
+  `=destroy`(x)
+  echo "sink"
+  x.f = y.f
+
+proc `=copy`(x: var OO, y: OO) =
+  if x.f != y.f:
+    `=destroy`(x)
+    echo "copy"
+    x.f = y.f
+  else:
+    echo "copy (self-assign)"
+
+proc caseSym =
+  var o = OO(f: 1)
+  o = o # NOOP
+  echo o.f # "1"
+  # "destroy"
+
+caseSym()
+
+proc caseDotExpr =
+  var w = W(o: OO(f: 1))
+  w.o = w.o # NOOP
+  echo w.o.f # "1"
+  # "destroy"
+
+caseDotExpr()
+
+proc caseBracketExpr =
+  var w = [0: OO(f: 1)]
+  w[0] = w[0] # NOOP
+  echo w[0].f # "1"
+  # "destroy"
+
+caseBracketExpr()
+
+proc caseBracketExprCopy =
+  var w = [0: OO(f: 1)]
+  let i = 0
+  w[i] = w[0] # "copy (self-assign)"
+  echo w[0].f # "1"
+  # "destroy"
+
+caseBracketExprCopy()
+
+proc caseDotExprAddr =
+  var w = W(o: OO(f: 1))
+  w.o = addr(w.o)[] # NOOP
+  echo w.o.f # "1"
+  # "destroy"
+
+caseDotExprAddr()
+
+proc caseBracketExprAddr =
+  var w = [0: OO(f: 1)]
+  addr(w[0])[] = addr(addr(w[0])[])[] # NOOP
+  echo w[0].f # "1"
+  # "destroy"
+
+caseBracketExprAddr()
+
+proc caseNotAConstant =
+  var i = 0
+  proc rand: int =
+    result = i
+    inc i
+  var s = @[OO(f: 1), OO(f: 2), OO(f: 3)]
+  s[rand()] = s[rand()] # "destroy" "copy"
+  echo s # @[(f: 2), (f: 2), (f: 3)]
+
+caseNotAConstant()
+
+proc potentialSelfAssign(i: var int) =
+  var a: array[2, OO]
+  a[i] = OO(f: 1) # turned into a memcopy
+  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:"
+
+type
+  Tree = object
+    children: seq[Tree]
+
+  TreeDefaultHooks = object
+    children: seq[TreeDefaultHooks]
+
+proc `=destroy`(x: var Tree) = echo "destroy"
+proc `=sink`(x: var Tree, y: Tree) = echo "sink"
+proc `=copy`(x: var Tree, y: Tree) = echo "copy"
+
+proc partToWholeSeq =
+  var t = Tree(children: @[Tree()])
+  t = t.children[0] # This should be sunk, but with the special transform (tmp = t.children[0]; wasMoved(0); `=sink`(t, tmp))
+
+  var tc = TreeDefaultHooks(children: @[TreeDefaultHooks()])
+  tc = tc.children[0] # Ditto; if this were sunk with the normal transform (`=sink`(t, t.children[0]); wasMoved(t.children[0]))
+  echo tc             #        then it would crash because t.children[0] does not exist after the call to `=sink`
+
+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
+
+proc `=destroy`(x: var List) = echo "destroy"
+proc `=sink`(x: var List, y: List) = echo "sink"
+proc `=copy`(x: var List, y: List) = echo "copy"
+
+proc partToWholeUnownedRef =
+  var t = List(next: new List)
+  t = t.next[] # Copy because t.next is not an owned ref, and thus t.next[] cannot be moved
+
+partToWholeUnownedRef()
+
+
+#--------------------------------------------------------------------
+# test that nodes that get copied during the transformation
+# (like dot exprs) don't loose their firstWrite/lastRead property
+
+type
+  OOO = object
+    initialized: bool
+
+  C = object
+    o: OOO
+
+proc `=destroy`(o: var OOO) =
+  doAssert o.initialized, "OOO was destroyed before initialization!"
+
+proc initO(): OOO =
+  OOO(initialized: true)
+
+proc initC(): C =
+  C(o: initO())
+
+proc pair(): tuple[a: C, b: C] =
+  result = (a: initC(), b: initC())# <- when firstWrite tries to find this node to start its analysis it fails, because injectdestructors uses copyTree/shallowCopy
+
+discard pair()
+
+
+# bug #17450
+proc noConsume(x: OO) {.nosinks.} = echo x
+
+proc main3 =
+  var i = 1
+  noConsume:
+    block:
+      OO(f: i)
+
+main3()
+
+# misc
+proc smoltest(x: bool): bool =
+  while true:
+    if true: return x
+
+discard smoltest(true)
+
+# bug #18002
+type
+  TTypeAttachedOp = enum
+    attachedAsgn
+    attachedSink
+    attachedTrace
+
+  PNode = ref object
+    discard
+
+proc genAddrOf(n: PNode) =
+  assert n != nil, "moved?!"
+
+proc atomicClosureOp =
+  let x = PNode()
+
+  genAddrOf:
+    block:
+      x
+
+  case attachedTrace
+  of attachedSink: discard
+  of attachedAsgn: discard
+  of attachedTrace: genAddrOf(x)
+
+atomicClosureOp()
+
+
+template assertEq(a, b: untyped): untyped =
+  block:
+    let
+      aval = a
+      bval = b
+
+    if aval != bval:
+      quit "bug!"
+
+proc convoluted =
+  let _ = (;
+    var val1: string;
+    if true: val1 = "22"
+    true
+  )
+
+  assertEq val1, "22"
+  assertEq val1, "22"
+
+convoluted()
diff --git a/tests/arc/tmovebugcopy.nim b/tests/arc/tmovebugcopy.nim
new file mode 100644
index 000000000..ec4315777
--- /dev/null
+++ b/tests/arc/tmovebugcopy.nim
@@ -0,0 +1,526 @@
+discard """
+  cmd: "nim c --gc:arc $file"
+  output: '''5
+(w: 5)
+(w: -5)
+c.text = hello
+c.text = hello
+p.text = hello
+p.toks = @["hello"]
+c.text = hello
+c[].text = hello
+pA.text = hello
+pA.toks = @["hello"]
+c.text = hello
+c.text = hello
+pD.text = hello
+pD.toks = @["hello"]
+c.text = hello
+c.text = hello
+pOD.text = hello
+pOD.toks = @["hello"]
+fff
+fff
+2
+fff
+fff
+2
+fff
+fff
+2
+mmm
+fff
+fff
+fff
+3
+mmm
+sink me (sink)
+assign me (not sink)
+sink me (not sink)
+sinked and not optimized to a bitcopy
+sinked and not optimized to a bitcopy
+sinked and not optimized to a bitcopy
+(data: @[0, 0])
+(data: @[0, 0])
+(data: @[0, 0])
+(data: @[0, 0])
+(data: @[0, 0])
+(data: @[0, 0])
+(data: @[0, 0])
+100
+hey
+hey
+(a: "a", b: 2)
+ho
+(a: "b", b: 3)
+(b: "b", a: 2)
+ho
+(b: "a", a: 3)
+hey
+break
+break
+hey
+ho
+hey
+ho
+ho
+king
+live long; long live
+king
+hi
+try
+bye
+'''
+"""
+
+# move bug
+type
+  TMyObj = object
+    p: pointer
+    len: int
+
+var destroyCounter = 0
+
+proc `=destroy`(o: var TMyObj) =
+  if o.p != nil:
+    dealloc o.p
+    o.p = nil
+    inc destroyCounter
+
+proc `=copy`(dst: var TMyObj, src: TMyObj) =
+  `=destroy`(dst)
+  dst.p = alloc(src.len)
+  dst.len = src.len
+
+proc `=sink`(dst: var TMyObj, src: TMyObj) =
+  `=destroy`(dst)
+  dst.p = src.p
+  dst.len = src.len
+
+type
+  TObjKind = enum Z, A, B
+  TCaseObj = object
+    case kind: TObjKind
+    of Z: discard
+    of A:
+      x1: int # this int plays important role
+      x2: TMyObj
+    of B:
+      y: TMyObj
+
+proc use(a: TCaseObj) = discard
+
+proc moveBug(i: var int) =
+  var a: array[2, TCaseObj]
+  a[i] = TCaseObj(kind: A, x1: 5000, x2: TMyObj(len: 5, p: alloc(5))) # 1
+  a[i+1] = a[i] # 2
+  inc i
+  use(a[i-1])
+
+var x = 0
+moveBug(x)
+
+proc moveBug2(): (TCaseObj, TCaseObj) =
+  var a: array[2, TCaseObj]
+  a[0] = TCaseObj(kind: A, x1: 5000, x2: TMyObj(len: 5, p: alloc(5)))
+  a[1] = a[0] # can move 3
+  result[0] = TCaseObj(kind: A, x1: 5000, x2: TMyObj(len: 5, p: alloc(5))) # 4
+  result[1] = result[0] # 5
+
+proc main =
+  discard moveBug2()
+
+main()
+echo destroyCounter
+
+# bug #13314
+
+type
+  O = object
+    v: int
+  R = ref object
+    w: int
+
+proc `$`(r: R): string = $r[]
+
+proc tbug13314 =
+  var t5 = R(w: 5)
+  var execute = proc () =
+    echo t5
+
+  execute()
+  t5.w = -5
+  execute()
+
+tbug13314()
+
+#-------------------------------------------------------------------------
+# bug #13368
+
+import strutils
+proc procStat() =
+  for line in @["a b", "c d", "e f"]:
+    let cols = line.splitWhitespace(maxSplit=1)
+    let x = cols[0]
+    let (nm, rest) = (cols[0], cols[1])
+procStat()
+
+
+# bug #14269
+
+import sugar, strutils
+
+type
+  Cursor = object
+    text: string
+  Parsed = object
+    text: string
+    toks: seq[string]
+
+proc tokenize(c: var Cursor): seq[string] =
+  dump c.text
+  return c.text.splitWhitespace()
+
+proc parse(): Parsed =
+  var c = Cursor(text: "hello")
+  dump c.text
+  return Parsed(text: c.text, toks: c.tokenize) # note: c.tokenized uses c.text
+
+let p = parse()
+dump p.text
+dump p.toks
+
+
+proc tokenizeA(c: ptr Cursor): seq[string] =
+  dump c[].text
+  return c[].text.splitWhitespace()
+
+proc parseA(): Parsed =
+  var c = Cursor(text: "hello")
+  dump c.text
+  return Parsed(text: c.text, toks: c.addr.tokenizeA) # note: c.tokenized uses c.text
+
+let pA = parseA()
+dump pA.text
+dump pA.toks
+
+
+proc tokenizeD(c: Cursor): seq[string] =
+  dump c.text
+  return c.text.splitWhitespace()
+
+proc parseD(): Parsed =
+  var c = cast[ptr Cursor](alloc0(sizeof(Cursor)))
+  c[] = Cursor(text: "hello")
+  dump c.text
+  return Parsed(text: c.text, toks: c[].tokenizeD) # note: c.tokenized uses c.text
+
+let pD = parseD()
+dump pD.text
+dump pD.toks
+
+# Bug would only pop up with owned refs
+proc tokenizeOD(c: Cursor): seq[string] =
+  dump c.text
+  return c.text.splitWhitespace()
+
+proc parseOD(): Parsed =
+  var c = new Cursor
+  c[] = Cursor(text: "hello")
+  dump c.text
+  return Parsed(text: c.text, toks: c[].tokenizeOD) # note: c.tokenized uses c.text
+
+let pOD = parseOD()
+dump pOD.text
+dump pOD.toks
+
+when false:
+  # Bug would only pop up with owned refs and implicit derefs, but since they don't work together..
+  {.experimental: "implicitDeref".}
+  proc tokenizeOHD(c: Cursor): seq[string] =
+    dump c.text
+    return c.text.splitWhitespace()
+
+  proc parseOHD(): Parsed =
+    var c = new Cursor
+    c[] = Cursor(text: "hello")
+    dump c.text
+    return Parsed(text: c.text, toks: c.tokenizeOHD) # note: c.tokenized uses c.text
+
+  let pOHD = parseOHD()
+  dump pOHD.text
+  dump pOHD.toks
+
+# bug #13456
+
+iterator combinations[T](s: openArray[T], k: int): seq[T] =
+  let n = len(s)
+  assert k >= 0 and k <= n
+  var pos = newSeq[int](k)
+  var current = newSeq[T](k)
+  for i in 0..k-1:
+    pos[k-i-1] = i
+  var done = false
+  while not done:
+    for i in 0..k-1:
+      current[i] = s[pos[k-i-1]]
+    yield current
+    var i = 0
+    while i < k:
+      pos[i] += 1
+      if pos[i] < n-i:
+        for j in 0..i-1:
+          pos[j] = pos[i] + i - j
+        break
+      i += 1
+    if i >= k:
+      break
+
+type
+  UndefEx = object of ValueError
+
+proc main2 =
+  var delayedSyms = @[1, 2, 3]
+  var unp: seq[int]
+  block myb:
+    for a in 1 .. 2:
+      if delayedSyms.len > a:
+        unp = delayedSyms
+        for t in unp.combinations(a + 1):
+          try:
+            var h = false
+            for k in t:
+              echo "fff"
+            if h: continue
+            if true:
+              raise newException(UndefEx, "forward declaration")
+            break myb
+          except UndefEx:
+            echo t.len
+        echo "mmm"
+
+main2()
+
+
+
+type ME = object
+  who: string
+
+proc `=copy`(x: var ME, y: ME) =
+  if y.who.len > 0: echo "assign ",y.who
+
+proc `=sink`(x: var ME, y: ME) =
+  if y.who.len > 0: echo "sink ",y.who
+
+var dump: ME
+template use(x) = dump = x
+template def(x) = x = dump
+
+var c = true
+
+proc shouldSink() =
+  var x = ME(who: "me (sink)")
+  use(x) # we analyse this
+  if c: def(x)
+  else: def(x)
+  use(x) # ok, with the [else] part.
+
+shouldSink()
+
+dump = ME()
+
+proc shouldNotSink() =
+  var x = ME(who: "me (not sink)")
+  use(x) # we analyse this
+  if c: def(x)
+  use(x) # Not ok without the '[else]'
+
+shouldNotSink()
+
+# bug #14568
+import os
+
+type O2 = object
+  s: seq[int]
+
+proc `=sink`(dest: var O2, src: O2) =
+  echo "sinked and not optimized to a bitcopy"
+
+var testSeq: O2
+
+proc update() =
+  # testSeq.add(0) # uncommenting this line fixes the leak
+  testSeq = O2(s: @[])
+  testSeq.s.add(0)
+
+for i in 1..3:
+  update()
+
+
+# bug #14961
+type
+  Foo = object
+    data: seq[int]
+
+proc initFoo(len: int): Foo =
+  result = (let s = newSeq[int](len); Foo(data: s) )
+
+var f = initFoo(2)
+echo initFoo(2)
+
+proc initFoo2(len: int) =
+  echo   if true:
+             let s = newSeq[int](len); Foo(data: s)
+         else:
+             let s = newSeq[int](len); Foo(data: s)
+
+initFoo2(2)
+
+proc initFoo3(len: int) =
+  echo (block:
+         let s = newSeq[int](len); Foo(data: s))
+
+initFoo3(2)
+
+proc initFoo4(len: int) =
+  echo (let s = newSeq[int](len); Foo(data: s))
+
+initFoo4(2)
+
+proc initFoo5(len: int) =
+  echo (case true
+        of true:
+          let s = newSeq[int](len); Foo(data: s)
+        of false:
+          let s = newSeq[int](len); Foo(data: s))
+
+initFoo5(2)
+
+proc initFoo6(len: int) =
+  echo (block:
+          try:
+            let s = newSeq[int](len); Foo(data: s)
+          finally: discard)
+
+initFoo6(2)
+
+proc initFoo7(len: int) =
+  echo (block:
+          try:
+            raise newException(CatchableError, "sup")
+            let s = newSeq[int](len); Foo(data: s)
+          except CatchableError:
+            let s = newSeq[int](len); Foo(data: s) )
+
+initFoo7(2)
+
+
+# bug #14902
+iterator zip[T](s: openArray[T]): (T, T) =
+  var i = 0
+  while i < 10:
+    yield (s[i mod 2], s[i mod 2 + 1])
+    inc i
+
+var lastMem = int.high
+
+proc leak =
+  const len = 10
+  var x = @[newString(len), newString(len), newString(len)]
+
+  var c = 0
+  for (a, b) in zip(x):
+    let newMem = getOccupiedMem()
+    assert newMem <= lastMem
+    lastMem = newMem
+    c += a.len
+  echo c
+
+leak()
+
+
+proc consume(a: sink string) = echo a
+
+proc weirdScopes =
+  if (let a = "hey"; a.len > 0):
+    echo a
+
+  while (let a = "hey"; a.len > 0):
+    echo a
+    break
+
+  var a = block: (a: "a", b: 2)
+  echo a
+  (discard; a) = (echo "ho"; (a: "b", b: 3))
+  echo a
+
+  var b = try: (b: "b", a: 2)
+          except: raise
+  echo b
+  (discard; b) = (echo "ho"; (b: "a", a: 3))
+  echo b
+
+  var s = "break"
+  consume((echo "hey"; s))
+  echo s
+
+  echo (block:
+          var a = "hey"
+          (echo "hey"; "ho"))
+
+  var b2 = "ho"
+  echo (block:
+          var a = "hey"
+          (echo "hey"; b2))
+  echo b2
+
+  type status = enum
+    alive
+
+  var king = "king"
+  echo (block:
+          var a = "a"
+          when true:
+            var b = "b"
+            case alive
+            of alive:
+              try:
+                var c = "c"
+                if true:
+                  king
+                else:
+                  "the abyss"
+              except:
+                echo "he ded"
+                "dead king")
+  echo "live long; long live"
+  echo king
+
+weirdScopes()
+
+
+# bug #14985
+proc getScope(): string =
+  if true:
+    "hi"
+  else:
+    "else"
+
+echo getScope()
+
+proc getScope3(): string =
+  try:
+    "try"
+  except:
+    "except"
+
+echo getScope3()
+
+proc getScope2(): string =
+  case true
+  of true:
+    "bye"
+  else:
+    "else"
+
+echo getScope2()
diff --git a/tests/arc/tnewseq_legacy.nim b/tests/arc/tnewseq_legacy.nim
new file mode 100644
index 000000000..4730d2c2b
--- /dev/null
+++ b/tests/arc/tnewseq_legacy.nim
@@ -0,0 +1,13 @@
+discard """
+  output: "(allocCount: 201, deallocCount: 201)"
+  cmd: "nim c --gc:orc -d:nimAllocStats $file"
+"""
+
+proc main(prefix: string) =
+  var c: seq[string]
+  for i in 0..<100:
+    newSeq(c, 100)
+    c[i] = prefix & $i
+
+main("abc")
+echo getAllocStats()
diff --git a/tests/arc/top_no_cursor2.nim b/tests/arc/top_no_cursor2.nim
new file mode 100644
index 000000000..5dfde57aa
--- /dev/null
+++ b/tests/arc/top_no_cursor2.nim
@@ -0,0 +1,53 @@
+discard """
+  output: '''true
+true
+true
+true
+true'''
+  cmd: "nim c --gc:arc $file"
+"""
+# bug #15361
+
+type
+  ErrorNodeKind = enum Branch, Leaf
+  Error = ref object
+    case kind: ErrorNodeKind
+      of Branch:
+        left: Error
+        right: Error
+      of Leaf:
+        leafError: string
+    input: string
+
+proc ret(input: string, lefterr, righterr: Error): Error =
+  result = Error(kind: Branch, left: lefterr, right: righterr, input: input)
+
+proc parser() =
+  var rerrors: Error
+  let lerrors = Error(
+    kind: Leaf,
+    leafError: "first error",
+    input: "123 ;"
+  )
+  # If you remove "block" - everything works
+  block:
+    let rresult = Error(
+      kind: Leaf,
+      leafError: "second error",
+      input: ";"
+    )
+    # this assignment is needed too
+    rerrors = rresult
+
+  # Returns Error(kind: Branch, left: lerrors, right: rerrors, input: "some val")
+  # needs to be a proc call for some reason, can't inline the result
+  var data = ret(input = "some val", lefterr = lerrors, righterr = rerrors)
+
+  echo data.left.leafError == "first error"
+  echo data.left.input == "123 ;"
+  # stacktrace shows this line
+  echo data.right.leafError == "second error"
+  echo data.right.input == ";"
+  echo data.input == "some val"
+
+parser()
diff --git a/tests/arc/topenarray.nim b/tests/arc/topenarray.nim
new file mode 100644
index 000000000..ba91666ba
--- /dev/null
+++ b/tests/arc/topenarray.nim
@@ -0,0 +1,86 @@
+discard """
+  input: "hi"
+  output: '''
+hi
+Nim
+'''
+  matrix: "--gc:arc -d:useMalloc; --gc:arc"
+"""
+{.experimental: "views".}
+
+block: # bug 18627
+  proc setPosition(params: openArray[string]) =
+    for i in params.toOpenArray(0, params.len - 1):
+      echo i
+
+  proc uciLoop() =
+    let params = @[readLine(stdin)]
+    setPosition(params)
+
+  uciLoop()
+
+  proc uciLoop2() =
+    let params = @["Nim"]
+    for i in params.toOpenArray(0, params.len - 1):
+      echo i
+  uciLoop2()
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+block: # bug #20954
+  block:
+    doAssertRaises(IndexDefect):
+      var v: array[10, int]
+
+      echo len(toOpenArray(v, 20, 30))
+
+  block:
+    doAssertRaises(IndexDefect):
+      var v: seq[int]
+
+      echo len(toOpenArray(v, 20, 30))
+
+# bug #20422
+
+proc f(a: var string) =
+  var v = a.toOpenArray(1, 3)
+  v[0] = 'a'
+
+var a = "Hello"
+f(a)
+doAssert a == "Hallo"
+
+# bug #22132
+block:
+  func foo[T](arr: openArray[T], idx: int = arr.low): string =
+    doAssert idx == 0
+    return $arr
+
+  let bug = ["0", "c", "a"]
+
+  let str = foo(bug)
+
+  const expected = """["0", "c", "a"]"""
+  doAssert str == expected
+
+  const noBugConst = ["0", "c", "a"]
+  doAssert foo(noBugConst) == expected
+  let noBugSeq = @["0", "c", "a"]
+  doAssert foo(noBugSeq) == expected
+
+block: # bug #20865
+  var p: pointer
+  var x: array[0, int]
+  # echo toOpenArray(x, 0, 1)[0] # Raises IndexDefect
+  doAssertRaises(IndexDefect):
+    echo toOpenArray(cast[ptr array[0, int]](p)[], 0, 1)[0] # Does not raise IndexDefect
+
+block: # bug #20987
+  var v: array[1, byte]
+
+  var p = cast[ptr array[0, byte]](addr v)
+
+  doAssertRaises(IndexDefect):
+    echo toOpenArray(p[], 1, 2)
+
diff --git a/tests/arc/topt_cursor.nim b/tests/arc/topt_cursor.nim
new file mode 100644
index 000000000..794132921
--- /dev/null
+++ b/tests/arc/topt_cursor.nim
@@ -0,0 +1,57 @@
+discard """
+  output: '''("string here", 80)'''
+  cmd: '''nim c --gc:arc --expandArc:main --expandArc:sio --hint:Performance:off $file'''
+  nimout: '''--expandArc: main
+
+var
+  x_cursor
+  :tmpD
+try:
+  x_cursor = ("hi", 5)
+  if cond:
+    x_cursor = ("different", 54) else:
+    x_cursor = ("string here", 80)
+  echo [
+    :tmpD = `$$`(x_cursor)
+    :tmpD]
+finally:
+  `=destroy`(:tmpD)
+-- end of expandArc ------------------------
+--expandArc: sio
+
+block :tmp:
+  var x_cursor
+  var f = open("debug.txt", fmRead, 8000)
+  try:
+    var res
+    try:
+      res = newStringOfCap(80)
+      block :tmp_1:
+        while readLine(f, res):
+          x_cursor = res
+          echo [x_cursor]
+    finally:
+      `=destroy`(res)
+  finally:
+    close(f)
+-- end of expandArc ------------------------'''
+"""
+
+proc main(cond: bool) =
+  var x = ("hi", 5) # goal: computed as cursor
+
+  x = if cond:
+        ("different", 54)
+      else:
+        ("string here", 80)
+
+  echo x
+
+main(false)
+
+proc sio =
+  for x in lines("debug.txt"):
+    echo x
+
+if false:
+  sio()
diff --git a/tests/arc/topt_cursor2.nim b/tests/arc/topt_cursor2.nim
new file mode 100644
index 000000000..4b566ed24
--- /dev/null
+++ b/tests/arc/topt_cursor2.nim
@@ -0,0 +1,76 @@
+discard """  
+  cmd: '''nim c --gc:arc $file'''
+  output: '''emptyemptyempty
+inner destroy
+'''
+"""
+
+# bug #15039
+
+import lists
+
+type
+  Token = ref object of RootObj
+    children: DoublyLinkedList[Token]
+
+  Paragraph = ref object of Token
+
+method `$`(token: Token): string {.base.} =
+  result = "empty"
+
+method `$`(token: Paragraph): string =
+  if token.children.head == nil:
+    result = ""
+  else:
+    for c in token.children:
+      result.add $c
+
+proc parseLeafBlockInlines(token: Token) =
+  token.children.append(Token())
+  token.children.append(Token()) # <-- this one AAA
+
+  var emNode = newDoublyLinkedNode(Token())
+  var i = 0
+
+  var it = token.children.head
+  while it != nil:
+    var nxt = it.next  # this is not a cursor, it takes over ownership.
+    var childNode = it
+    if i == 0:
+      childNode.next = emNode # frees the object allocated in line 29 marked with AAA
+    elif i == 1:
+      emNode.next = childNode  #
+    it = nxt # incref on freed data, 'nxt' is freed
+    inc i
+
+proc parse() =
+  var token = Token()
+  token.children.append Paragraph()
+  parseLeafBlockInlines(token.children.head.value)
+  for children in token.children:
+    echo children
+
+parse()
+
+
+#------------------------------------------------------------------------------
+# issue #15629
+
+type inner = object
+type outer = ref inner
+
+proc `=destroy`(b: var inner) =
+  echo "inner destroy"
+
+proc newOuter(): outer =
+  new(result)
+
+type holder = object
+  contents: outer
+
+proc main() = 
+  var t: holder
+  t.contents = newOuter()
+  
+main()
+
diff --git a/tests/arc/topt_no_cursor.nim b/tests/arc/topt_no_cursor.nim
new file mode 100644
index 000000000..0a4984a69
--- /dev/null
+++ b/tests/arc/topt_no_cursor.nim
@@ -0,0 +1,376 @@
+discard """
+  nimoutFull: true
+  cmd: '''nim c -r --warnings:off --hints:off --mm:arc --expandArc:newTarget --expandArc:delete --expandArc:p1 --expandArc:tt --hint:Performance:off --assertions:off --expandArc:extractConfig --expandArc:mergeShadowScope --expandArc:check $file'''
+  nimout: '''
+--expandArc: newTarget
+
+var
+  splat
+  :tmp
+  :tmp_1
+splat = splitDrive do:
+  let blitTmp = path
+  blitTmp
+:tmp = splat.drive
+`=wasMoved`(splat.drive)
+:tmp_1 = splat.path_1
+`=wasMoved`(splat.path_1)
+result = (
+  let blitTmp_1 = :tmp
+  blitTmp_1,
+  let blitTmp_2 = :tmp_1
+  blitTmp_2)
+`=destroy`(splat)
+-- end of expandArc ------------------------
+--expandArc: delete
+
+var
+  sibling
+  saved
+`=copy`(sibling, target.parent.left)
+`=copy`(saved, sibling.right)
+`=copy`(sibling.right, saved.left)
+`=sink`(sibling.parent, saved)
+`=destroy`(sibling)
+-- end of expandArc ------------------------
+--expandArc: p1
+
+var
+  lresult
+  lvalue
+  lnext
+  tmpTupleAsgn
+lresult = @[123]
+tmpTupleAsgn = (
+  let blitTmp = lresult
+  blitTmp, ";")
+lvalue = tmpTupleAsgn[0]
+lnext = tmpTupleAsgn[1]
+`=sink`(result.value, move lvalue)
+`=destroy`(lnext)
+`=destroy_1`(lvalue)
+-- end of expandArc ------------------------
+--expandArc: tt
+
+var
+  it_cursor
+  a
+  :tmpD
+  :tmpD_1
+  :tmpD_2
+try:
+  it_cursor = x
+  a = (
+    :tmpD = `=dup`(it_cursor.key)
+    :tmpD,
+    :tmpD_1 = `=dup`(it_cursor.val)
+    :tmpD_1)
+  echo [
+    :tmpD_2 = `$$`(a)
+    :tmpD_2]
+finally:
+  `=destroy`(:tmpD_2)
+  `=destroy_1`(a)
+-- end of expandArc ------------------------
+--expandArc: extractConfig
+
+var lan_ip
+try:
+  lan_ip = ""
+  block :tmp:
+    var line
+    var i = 0
+    let L = len(txt)
+    block :tmp_1:
+      while i < L:
+        var splitted
+        try:
+          line = txt[i]
+          splitted = split(line, " ", -1)
+          if splitted[0] == "opt":
+            `=copy`(lan_ip, splitted[1])
+          echo [lan_ip]
+          echo [splitted[1]]
+          {.push, overflowChecks: false.}
+          inc(i, 1)
+          {.pop.}
+        finally:
+          `=destroy`(splitted)
+finally:
+  `=destroy_1`(lan_ip)
+-- end of expandArc ------------------------
+--expandArc: mergeShadowScope
+
+var shadowScope
+`=copy`(shadowScope, c.currentScope)
+rawCloseScope(c)
+block :tmp:
+  var sym
+  var i = 0
+  let L = len(shadowScope.symbols)
+  block :tmp_1:
+    while i < L:
+      var :tmpD
+      sym = shadowScope.symbols[i]
+      addInterfaceDecl(c):
+        :tmpD = `=dup`(sym)
+        :tmpD
+      {.push, overflowChecks: false.}
+      inc(i, 1)
+      {.pop.}
+`=destroy`(shadowScope)
+-- end of expandArc ------------------------
+--expandArc: check
+
+var par
+this.isValid = fileExists(this.value)
+if dirExists(this.value):
+  var :tmpD
+  par = (dir:
+    :tmpD = `=dup`(this.value)
+    :tmpD, front: "") else:
+  var
+    :tmpD_1
+    :tmpD_2
+    :tmpD_3
+  par = (dir_1: parentDir(this.value), front_1:
+    :tmpD_1 = `=dup`(
+      :tmpD_3 = splitDrive do:
+        :tmpD_2 = `=dup`(this.value)
+        :tmpD_2
+      :tmpD_3.path)
+    :tmpD_1)
+  `=destroy`(:tmpD_3)
+if dirExists(par.dir):
+  `=sink`(this.matchDirs, getSubDirs(par.dir, par.front))
+else:
+  `=sink`(this.matchDirs, [])
+`=destroy`(par)
+-- end of expandArc ------------------------
+--expandArc: check
+
+check(this)
+-- end of expandArc ------------------------
+(package: "", ext: "meo")
+doing shady stuff...
+3
+6
+(@[1], @[2])
+192.168.0.1
+192.168.0.1
+192.168.0.1
+192.168.0.1
+'''
+"""
+
+import os, std/private/ntpath
+
+type Target = tuple[package, ext: string]
+
+proc newTarget*(path: string): Target =
+  let splat = path.splitDrive
+  result = (package: splat.drive, ext: splat.path)
+
+echo newTarget("meo")
+
+type
+  Node = ref object
+    left, right, parent: Node
+    value: int
+
+proc delete(target: var Node) =
+  var sibling = target.parent.left # b3
+  var saved = sibling.right # b3.right -> r4
+
+  sibling.right = saved.left # b3.right -> r4.left = nil
+  sibling.parent = saved # b3.parent -> r5 = r4
+
+  #[after this proc:
+        b 5
+      /   \
+    b 3     b 6
+  ]#
+
+
+#[before:
+      r 5
+    /   \
+  b 3    b 6 - to delete
+  /    \
+empty  r 4
+]#
+proc main =
+  var five = Node(value: 5)
+
+  var six = Node(value: 6)
+  six.parent = five
+  five.right = six
+
+  var three = Node(value: 3)
+  three.parent = five
+  five.left = three
+
+  var four = Node(value: 4)
+  four.parent = three
+  three.right = four
+
+  echo "doing shady stuff..."
+  delete(six)
+  # need both of these echos
+  echo five.left.value
+  echo five.right.value
+
+main()
+
+type
+  Maybe = object
+    value: seq[int]
+
+proc p1(): Maybe =
+  let lresult = @[123]
+  var lvalue: seq[int]
+  var lnext: string
+  (lvalue, lnext) = (lresult, ";")
+
+  result.value = move lvalue
+
+proc tissue15130 =
+  doAssert p1().value == @[123]
+
+tissue15130()
+
+type
+  KeyValue = tuple[key, val: seq[int]]
+
+proc tt(x: KeyValue) =
+  var it = x
+  let a = (it.key, it.val)
+  echo a
+
+proc encodedQuery =
+  var query: seq[KeyValue]
+  query.add (key: @[1], val: @[2])
+
+  for elem in query:
+    elem.tt()
+
+encodedQuery()
+
+# bug #15147
+
+proc s(input: string): (string, string) =
+  result = (";", "")
+
+proc charmatch(input: string): (string, string) =
+  result = ("123", input[0 .. input.high])
+
+proc plus(input: string) =
+  var
+    lvalue, rvalue: string # cursors
+    lnext: string # must be cursor!!!
+    rnext: string # cursor
+  let lresult = charmatch(input)
+  (lvalue, lnext) = lresult
+
+  let rresult = s(lnext)
+  (rvalue, rnext) = rresult
+
+plus("123;")
+
+func substrEq(s: string, pos: int, substr: string): bool =
+  var i = 0
+  var length = substr.len
+  while i < length and pos+i < s.len and s[pos+i] == substr[i]:
+    inc i
+  return i == length
+
+template stringHasSep(s: string, index: int, sep: string): bool =
+  s.substrEq(index, sep)
+
+template splitCommon(s, sep, maxsplit, sepLen) =
+  var last = 0
+  var splits = maxsplit
+
+  while last <= len(s):
+    var first = last
+    while last < len(s) and not stringHasSep(s, last, sep):
+      inc(last)
+    if splits == 0: last = len(s)
+    yield substr(s, first, last-1)
+    if splits == 0: break
+    dec(splits)
+    inc(last, sepLen)
+
+iterator split(s: string, sep: string, maxsplit = -1): string =
+  splitCommon(s, sep, maxsplit, sep.len)
+
+template accResult(iter: untyped) =
+  result = @[]
+  for x in iter: add(result, x)
+
+func split*(s: string, sep: string, maxsplit = -1): seq[string] =
+  accResult(split(s, sep, maxsplit))
+
+
+let txt = @["opt 192.168.0.1", "static_lease 192.168.0.1"]
+
+# bug #17033
+
+proc extractConfig() =
+  var lan_ip = ""
+
+  for line in txt:
+    let splitted = line.split(" ")
+    if splitted[0] == "opt":
+      lan_ip = splitted[1] # "borrow" is conditional and inside a loop.
+      # Not good enough...
+      # we need a flag that live-ranges are disjoint
+    echo lan_ip
+    echo splitted[1] # Without this line everything works
+
+extractConfig()
+
+
+type
+  Symbol = ref object
+    name: string
+
+  Scope = ref object
+    parent: Scope
+    symbols: seq[Symbol]
+
+  PContext = ref object
+    currentScope: Scope
+
+proc rawCloseScope(c: PContext) =
+  c.currentScope = c.currentScope.parent
+
+proc addInterfaceDecl(c: PContext; s: Symbol) =
+  c.currentScope.symbols.add s
+
+proc mergeShadowScope*(c: PContext) =
+  let shadowScope = c.currentScope
+  c.rawCloseScope
+  for sym in shadowScope.symbols:
+    c.addInterfaceDecl(sym)
+
+mergeShadowScope(PContext(currentScope: Scope(parent: Scope())))
+
+type
+  Foo = ref object
+    isValid*: bool
+    value*: string
+    matchDirs*: seq[string]
+
+proc getSubDirs(parent, front: string): seq[string] = @[]
+
+method check(this: Foo) {.base.} =
+  this.isValid = fileExists(this.value)
+  let par = if dirExists(this.value): (dir: this.value, front: "")
+            else: (dir: parentDir(this.value), front: splitDrive(this.value).path)
+  if dirExists(par.dir):
+    this.matchDirs = getSubDirs(par.dir, par.front)
+  else:
+    this.matchDirs = @[]
+
+check(Foo())
diff --git a/tests/arc/topt_refcursors.nim b/tests/arc/topt_refcursors.nim
new file mode 100644
index 000000000..8c638a4a1
--- /dev/null
+++ b/tests/arc/topt_refcursors.nim
@@ -0,0 +1,54 @@
+discard """
+  output: ''''''
+  cmd: '''nim c --gc:arc --expandArc:traverse --hint:Performance:off $file'''
+  nimout: '''
+--expandArc: traverse
+
+var
+  it_cursor
+  jt
+try:
+  it_cursor = root
+  block :tmp:
+    while (
+      not (it_cursor == nil)):
+      echo [it_cursor.s]
+      it_cursor = it_cursor.ri
+  `=copy`(jt, root)
+  block :tmp_1:
+    while (
+      not (jt == nil)):
+      var ri_1
+      try:
+        `=copy`(ri_1, jt.ri)
+        echo [jt.s]
+        `=sink`(jt, ri_1)
+        `=wasMoved`(ri_1)
+      finally:
+        `=destroy`(ri_1)
+finally:
+  `=destroy`(jt)
+-- end of expandArc ------------------------
+'''
+"""
+
+type
+  Node = ref object
+    le, ri: Node
+    s: string
+
+proc traverse(root: Node) =
+  var it = root
+  while it != nil:
+    echo it.s
+    it = it.ri
+
+  var jt = root
+  while jt != nil:
+    let ri = jt.ri
+    echo jt.s
+    jt = ri
+
+traverse(nil)
+
+# XXX: This optimization is not sound
diff --git a/tests/arc/topt_wasmoved_destroy_pairs.nim b/tests/arc/topt_wasmoved_destroy_pairs.nim
new file mode 100644
index 000000000..c96340a30
--- /dev/null
+++ b/tests/arc/topt_wasmoved_destroy_pairs.nim
@@ -0,0 +1,94 @@
+discard """
+  output: ''''''
+  cmd: '''nim c --gc:arc --expandArc:main --expandArc:tfor --hint:Performance:off $file'''
+  nimout: '''
+--expandArc: main
+
+var
+  a
+  b
+  x
+x = f()
+if cond:
+  add(a):
+    let blitTmp = x
+    blitTmp
+else:
+  add(b):
+    let blitTmp_1 = x
+    blitTmp_1
+`=destroy`(b)
+`=destroy`(a)
+-- end of expandArc ------------------------
+--expandArc: tfor
+
+var
+  a
+  b
+  x
+try:
+  x = f()
+  block :tmp:
+    var i_cursor
+    mixin inc
+    var i_1 = 0
+    block :tmp_1:
+      while i_1 < 4:
+        var :tmpD
+        i_cursor = i_1
+        if i_cursor == 2:
+          return
+        add(a):
+          :tmpD = `=dup`(x)
+          :tmpD
+        inc i_1, 1
+  if cond:
+    add(a):
+      let blitTmp = x
+      `=wasMoved`(x)
+      blitTmp
+  else:
+    add(b):
+      let blitTmp_1 = x
+      `=wasMoved`(x)
+      blitTmp_1
+finally:
+  `=destroy`(x)
+  `=destroy_1`(b)
+  `=destroy_1`(a)
+-- end of expandArc ------------------------
+'''
+"""
+
+proc f(): seq[int] =
+  @[1, 2, 3]
+
+proc main(cond: bool) =
+  var a, b: seq[seq[int]]
+  var x = f()
+  if cond:
+    a.add x
+  else:
+    b.add x
+
+# all paths move 'x' so no wasMoved(x); destroy(x) pair should be left in the
+# AST.
+
+main(false)
+
+
+proc tfor(cond: bool) =
+  var a, b: seq[seq[int]]
+
+  var x = f()
+
+  for i in 0 ..< 4:
+    if i == 2: return
+    a.add x
+
+  if cond:
+    a.add x
+  else:
+    b.add x
+
+tfor(false)
diff --git a/tests/arc/torc_basic_test.nim b/tests/arc/torc_basic_test.nim
new file mode 100644
index 000000000..73039fad7
--- /dev/null
+++ b/tests/arc/torc_basic_test.nim
@@ -0,0 +1,138 @@
+discard """
+  output: "MEM 0"
+  cmd: "nim c --gc:orc $file"
+"""
+
+type
+  Node = ref object
+    name: char
+    sccId: int
+    #a: array[3, Node]
+    a0, a1, a2: Node
+    rc: int
+
+proc edge(a, b: Node) =
+  inc b.rc
+  if a.a0 == nil: a.a0 = b
+  elif a.a1 == nil: a.a1 = b
+  else: a.a2 = b
+  when false:
+    var i = 0
+    while a.a[i] != nil: inc i
+    a.a[i] = b
+
+proc createNode(name: char): Node =
+  new result
+  result.name = name
+
+#[
+
+     +--------------------------------+
+     v                                |
++---------+      +------+             |
+|         |      |      |             |
+|  A      +----->+      |      +------+------+
++--+------+      |      |      |             |
+   |             |      |      |     C       ------------>  G  <--|
+   |             |  R   |      |             |
++--v------+      |      |      +-------------+
+|         |      |      |        ^
+|   B     <------+      |        |
+|         |      |      +--------+
++---------+      |      |
+                 +------+
+
+]#
+proc use(x: Node) = discard
+
+proc main =
+  let a = createNode('A')
+  let b = createNode('B')
+  let r = createNode('R')
+  let c = createNode('C')
+
+  a.edge b
+  a.edge r
+
+  r.edge b
+  r.edge c
+
+  let g = createNode('G')
+  g.edge g
+  g.edge g
+
+  c.edge g
+  c.edge a
+
+  use g
+  use b
+
+proc buildComplexGraph: Node =
+  # see https://en.wikipedia.org/wiki/Strongly_connected_component for the
+  # graph:
+  let a = createNode('a')
+  let b = createNode('b')
+  let c = createNode('c')
+  let d = createNode('d')
+  let e = createNode('e')
+
+  a.edge c
+  c.edge b
+  c.edge e
+  b.edge a
+  d.edge c
+  e.edge d
+
+
+  let f = createNode('f')
+  b.edge f
+  e.edge f
+
+  let g = createNode('g')
+  let h = createNode('h')
+  let i = createNode('i')
+
+  f.edge g
+  f.edge i
+  g.edge h
+  h.edge i
+  i.edge g
+
+  let j = createNode('j')
+
+  h.edge j
+  i.edge j
+
+  let k = createNode('k')
+  let l = createNode('l')
+
+  f.edge k
+  k.edge l
+  l.edge k
+  k.edge j
+
+  let m = createNode('m')
+  let n = createNode('n')
+  let p = createNode('p')
+  let q = createNode('q')
+
+  m.edge n
+  n.edge p
+  n.edge q
+  q.edge p
+  p.edge m
+
+  q.edge k
+
+  d.edge m
+  e.edge n
+
+  result = a
+
+proc main2 =
+  let g = buildComplexGraph()
+
+main()
+main2()
+GC_fullCollect()
+echo "MEM ", getOccupiedMem()
diff --git a/tests/arc/torc_selfcycles.nim b/tests/arc/torc_selfcycles.nim
new file mode 100644
index 000000000..ac4fa52ce
--- /dev/null
+++ b/tests/arc/torc_selfcycles.nim
@@ -0,0 +1,33 @@
+discard """
+  output: '''ok'''
+  cmd: '''nim c --gc:orc -d:useMalloc -d:nimStressOrc $file'''
+  valgrind: "leaks"
+"""
+
+# bug #15753
+
+type
+  NodeKind = enum
+    nkDancing,
+    nkColumn
+
+  DancingNode = ref object
+    right: DancingNode
+    column: DancingNode
+    kind: NodeKind
+
+proc newColumnNode(): DancingNode =
+  result = DancingNode(kind: nkColumn)
+  result.right = result
+  result.column = result
+
+proc createDLXList(): DancingNode =
+  result = newColumnNode()
+
+  for i in 0 .. 15:
+    let n = newColumnNode()
+    n.right = result.right
+    result = n
+  echo "ok"
+
+var dlxlist = createDLXList()
diff --git a/tests/arc/torcbench.nim b/tests/arc/torcbench.nim
new file mode 100644
index 000000000..4c9e65fee
--- /dev/null
+++ b/tests/arc/torcbench.nim
@@ -0,0 +1,38 @@
+discard """
+  output: '''true peak memory: true'''
+  cmd: "nim c --gc:orc -d:release $file"
+"""
+
+import lists, strutils, times
+
+type
+  Base = ref object of RootObj
+
+  Node = ref object of Base
+    parent: DoublyLinkedList[string]
+    le, ri: Node
+    self: Node # in order to create a cycle
+
+proc buildTree(parent: DoublyLinkedList[string]; depth: int): Node =
+  if depth == 0:
+    result = nil
+  elif depth == 1:
+    result = Node(parent: parent, le: nil, ri: nil, self: nil)
+    when not defined(gcArc):
+      result.self = result
+  else:
+    result = Node(parent: parent, le: buildTree(parent, depth - 1), ri: buildTree(parent, depth - 2), self: nil)
+    result.self = result
+
+proc main() =
+  for i in countup(1, 100):
+    var leakList = initDoublyLinkedList[string]()
+    for j in countup(1, 5000):
+      leakList.append(newString(200))
+    #GC_fullCollect()
+    for i in 0..400:
+      discard buildTree(leakList, 8)
+
+main()
+GC_fullCollect()
+echo getOccupiedMem() < 10 * 1024 * 1024, " peak memory: ", getMaxMem() < 10 * 1024 * 1024
diff --git a/tests/arc/torcmisc.nim b/tests/arc/torcmisc.nim
new file mode 100644
index 000000000..e41ad7c77
--- /dev/null
+++ b/tests/arc/torcmisc.nim
@@ -0,0 +1,66 @@
+discard """
+  output: '''success'''
+  cmd: "nim c --gc:orc -d:release $file"
+"""
+
+# bug #17170
+
+when true:
+  import asyncdispatch
+
+  type
+    Flags = ref object
+      returnedEof, reading: bool
+
+  proc dummy(): Future[string] {.async.} =
+    result = "foobar"
+
+  proc hello(s: Flags) {.async.} =
+    let buf =
+      try:
+        await dummy()
+      except CatchableError as exc:
+        # When an exception happens here, the Bufferstream is effectively
+        # broken and no more reads will be valid - for now, return EOF if it's
+        # called again, though this is not completely true - EOF represents an
+        # "orderly" shutdown and that's not what happened here..
+        s.returnedEof = true
+        raise exc
+      finally:
+        s.reading = false
+
+  waitFor hello(Flags())
+  echo "success"
+
+# bug #18240
+import tables
+
+type
+  TopicHandler* = proc(topic: string,
+                       data: seq[byte]): Future[void] {.gcsafe, raises: [Defect].}
+
+  PeerID* = object
+    data*: seq[byte]
+
+  PeerInfo* = ref object of RootObj
+    peerId*: PeerID
+
+  Connection* = ref object of RootObj
+    peerInfo*: PeerInfo
+
+  PubSubPeer* = ref object of RootObj
+    codec*: string
+
+  PubSub* = ref object of RootObj
+    topics*: Table[string, seq[TopicHandler]]
+    peers*: Table[PeerID, PubSubPeer]
+
+proc getOrCreatePeer*(myParam: PubSub, peerId: PeerID, protos: seq[string]): PubSubPeer =
+  myParam.peers.withValue(peerId, peer):
+    return peer[]
+
+method handleConn*(myParam: PubSub,
+                  conn: Connection,
+                  proto: string) {.base, async.} =
+  myParam.peers.withValue(conn.peerInfo.peerId, peer):
+    let peerB = peer[]
diff --git a/tests/arc/tref_cast_error.nim b/tests/arc/tref_cast_error.nim
new file mode 100644
index 000000000..20139c1be
--- /dev/null
+++ b/tests/arc/tref_cast_error.nim
@@ -0,0 +1,15 @@
+discard """
+  cmd: "nim c --gc:arc $file"
+  errormsg: "expression cannot be cast to 'ref RootObj'"
+  joinable: false
+"""
+
+type Variant* = object
+    refval: ref RootObj
+
+proc newVariant*[T](val: T): Variant =
+    let pt = T.new()
+    pt[] = val
+    result = Variant(refval: cast[ref RootObj](pt))
+
+var v = newVariant(@[1, 2, 3])
diff --git a/tests/arc/trepr.nim b/tests/arc/trepr.nim
new file mode 100644
index 000000000..abf28330a
--- /dev/null
+++ b/tests/arc/trepr.nim
@@ -0,0 +1,97 @@
+discard """
+  cmd: "nim c --gc:arc $file"
+  nimout: '''(a: true, n: doAssert)
+Table[system.string, trepr.MyType](data: @[], counter: 0)
+nil
+'''
+  output: '''
+nil
+2
+Obj(member: ref @["hello"])
+ref (member: ref @["hello"])
+ObjUa(v: 0, a: [...])
+'''
+"""
+
+# xxx consider merging with `tests/stdlib/trepr.nim` to increase overall test coverage
+
+import tables
+
+type
+  NimSym = distinct NimNode
+  MyType = tuple
+    a: bool
+    n: NimSym
+
+proc myproc(t: MyType) =
+  echo repr(t)
+
+proc myproc2(t: MyType) =
+  var x = Table[string, t]()
+  echo repr(x)
+
+proc myproc3(t: MyType) =
+  var x: TableRef[string, t]
+  echo repr(x)
+
+
+macro dumpSym(a: typed) =
+  myproc((a: true, n: NimSym(a)))
+  myproc2((a: true, n: NimSym(a)))
+  myproc3((a: true, n: NimSym(a)))
+
+dumpSym(doAssert)
+
+# bug 13731
+
+import os
+var a: File
+echo repr a
+
+# bug 13872
+
+echo repr(2'u16)
+
+# bug 14270
+
+type
+  Obj = ref object
+    member: ref seq[string]
+
+var c = Obj(member: new seq[string])
+c.member[] = @["hello"]
+echo c.repr
+
+var c2 = new tuple[member: ref seq[string]]
+c2.member = new seq[string]
+c2.member[] = @["hello"]
+echo c2.repr
+
+proc p2 =
+  echo "hey"
+
+discard repr p2
+
+
+#####################################################################
+# bug #15043
+
+import macros
+
+macro extract(): untyped =
+  result = newStmtList()
+  var x: seq[tuple[node: NimNode]]
+
+  proc test(n: NimNode) {.closure.} =
+    x.add (node: n)
+  
+  test(parseExpr("discard"))
+  
+extract()
+
+type
+  ObjUa = ref object
+    v: int
+    a: UncheckedArray[char]
+
+echo ObjUa().repr
diff --git a/tests/arc/trtree.nim b/tests/arc/trtree.nim
new file mode 100644
index 000000000..683403a62
--- /dev/null
+++ b/tests/arc/trtree.nim
@@ -0,0 +1,640 @@
+discard """
+  output: '''1 [2, 3, 4, 7]
+[0, 0]'''
+  targets: "c"
+  joinable: false
+disabled: 32bit
+  cmd: "nim c --gc:arc $file"
+"""
+
+# bug #13110: This test failed with --gc:arc.
+
+# this test wasn't written for 32 bit
+# don't join because the code is too messy.
+
+# Nim RTree and R*Tree implementation
+# S. Salewski, 06-JAN-2018
+
+# http://www-db.deis.unibo.it/courses/SI-LS/papers/Gut84.pdf
+# http://dbs.mathematik.uni-marburg.de/publications/myPapers/1990/BKSS90.pdf
+
+# RT: range type like float, int
+# D: Dimension
+# M: Max entries in one node
+# LT: leaf type
+
+type
+  Dim* = static[int]
+  Ext[RT] = tuple[a, b: RT] # extend (range)
+  Box*[D: Dim; RT] = array[D, Ext[RT]] # Rectangle for 2D
+  BoxCenter*[D: Dim; RT] = array[D, RT]
+  L*[D: Dim; RT, LT] = tuple[b: Box[D, RT]; l: LT] # called Index Entry or index record in the Guttman paper
+  H[M, D: Dim; RT, LT] = ref object of RootRef
+    parent: H[M, D, RT, LT]
+    numEntries: int
+    level: int
+  N[M, D: Dim; RT, LT] = tuple[b: Box[D, RT]; n: H[M, D, RT, LT]]
+  LA[M, D: Dim; RT, LT] = array[M, L[D, RT, LT]]
+  NA[M, D: Dim; RT, LT] = array[M, N[M, D, RT, LT]]
+  Leaf[M, D: Dim; RT, LT] = ref object of H[M, D, RT, LT]
+    a: LA[M, D, RT, LT]
+  Node[M, D: Dim; RT, LT] = ref object of H[M, D, RT, LT]
+    a: NA[M, D, RT, LT]
+
+  RTree*[M, D: Dim; RT, LT] = ref object of RootRef
+    root: H[M, D, RT, LT]
+    bigM: int
+    m: int
+
+  RStarTree*[M, D: Dim; RT, LT] = ref object of RTree[M, D, RT, LT]
+    firstOverflow: array[32, bool]
+    p: int
+
+proc newLeaf[M, D: Dim; RT, LT](): Leaf[M, D, RT, LT] =
+  new result
+
+proc newNode[M, D: Dim; RT, LT](): Node[M, D, RT, LT] =
+  new result
+
+proc newRTree*[M, D: Dim; RT, LT](minFill: range[30 .. 50] = 40): RTree[M, D, RT, LT] =
+  assert(M > 1 and M < 101)
+  new result
+  result.bigM = M
+  result.m = M * minFill div 100
+  result.root = newLeaf[M, D, RT, LT]()
+
+proc newRStarTree*[M, D: Dim; RT, LT](minFill: range[30 .. 50] = 40): RStarTree[M, D, RT, LT] =
+  assert(M > 1 and M < 101)
+  new result
+  result.bigM = M
+  result.m = M * minFill div 100
+  result.p = M * 30 div 100
+  result.root = newLeaf[M, D, RT, LT]()
+
+proc center(r: Box): auto =#BoxCenter[r.len, typeof(r[0].a)] =
+  var res: BoxCenter[r.len, typeof(r[0].a)]
+  for i in 0 .. r.high:
+    when r[0].a is SomeInteger:
+      res[i] = (r[i].a + r[i].b) div 2
+    elif r[0].a is SomeFloat:
+      res[i] = (r[i].a + r[i].b) / 2
+    else: assert false
+  return res
+
+proc distance(c1, c2: BoxCenter): auto =
+  var res: typeof(c1[0])
+  for i in 0 .. c1.high:
+    res += (c1[i] - c2[i]) * (c1[i] - c2[i])
+  return res
+
+proc overlap(r1, r2: Box): auto =
+  result = typeof(r1[0].a)(1)
+  for i in 0 .. r1.high:
+    result *= (min(r1[i].b, r2[i].b) - max(r1[i].a, r2[i].a))
+    if result <= 0: return 0
+
+proc union(r1, r2: Box): Box =
+  for i in 0 .. r1.high:
+    result[i].a = min(r1[i].a, r2[i].a)
+    result[i].b = max(r1[i].b, r2[i].b)
+
+proc intersect(r1, r2: Box): bool =
+  for i in 0 .. r1.high:
+    if r1[i].b < r2[i].a or r1[i].a > r2[i].b:
+      return false
+  return true
+
+proc area(r: Box): auto = #typeof(r[0].a) =
+  result = typeof(r[0].a)(1)
+  for i in 0 .. r.high:
+    result *= r[i].b - r[i].a
+
+proc margin(r: Box): auto = #typeof(r[0].a) =
+  result = typeof(r[0].a)(0)
+  for i in 0 .. r.high:
+    result += r[i].b - r[i].a
+
+# how much enlargement does r1 need to include r2
+proc enlargement(r1, r2: Box): auto =
+  area(union(r1, r2)) - area(r1)
+
+proc search*[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; b: Box[D, RT]): seq[LT] =
+  proc s[M, D: Dim; RT, LT](n: H[M, D, RT, LT]; b: Box[D, RT]; res: var seq[LT]) =
+    if n of Node[M, D, RT, LT]:
+      let h = Node[M, D, RT, LT](n)
+      for i in 0 ..< n.numEntries:
+        if intersect(h.a[i].b, b):
+          s(h.a[i].n, b, res)
+    elif n of Leaf[M, D, RT, LT]:
+      let h = Leaf[M, D, RT, LT](n)
+      for i in 0 ..< n.numEntries:
+        if intersect(h.a[i].b, b):
+          res.add(h.a[i].l)
+    else: assert false
+  result = newSeq[LT]()
+  s(t.root, b, result)
+
+# Insertion
+# a R*TREE proc
+proc chooseSubtree[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; b: Box[D, RT]; level: int): H[M, D, RT, LT] =
+  assert level >= 0
+  var it = t.root
+  while it.level > level:
+    let nn = Node[M, D, RT, LT](it)
+    var i0 = 0 # selected index
+    var minLoss = typeof(b[0].a).high
+    if it.level == 1: # childreen are leaves -- determine the minimum overlap costs
+      for i in 0 ..< it.numEntries:
+        let nx = union(nn.a[i].b, b)
+        var loss = 0
+        for j in 0 ..< it.numEntries:
+          if i == j: continue
+          loss += (overlap(nx, nn.a[j].b) - overlap(nn.a[i].b, nn.a[j].b)) # overlap (i, j) == (j, i), so maybe cache that?
+        var rep = loss < minLoss
+        if loss == minLoss:
+          let l2 = enlargement(nn.a[i].b, b) - enlargement(nn.a[i0].b, b)
+          rep = l2 < 0
+          if l2 == 0:
+            let l3 = area(nn.a[i].b) - area(nn.a[i0].b)
+            rep = l3 < 0
+            if l3 == 0:
+              rep = nn.a[i].n.numEntries < nn.a[i0].n.numEntries
+        if rep:
+          i0 = i
+          minLoss = loss
+    else:
+      for i in 0 ..< it.numEntries:
+        let loss = enlargement(nn.a[i].b, b)
+        var rep = loss < minLoss
+        if loss == minLoss:
+          let l3 = area(nn.a[i].b) - area(nn.a[i0].b)
+          rep = l3 < 0
+          if l3 == 0:
+            rep = nn.a[i].n.numEntries < nn.a[i0].n.numEntries
+        if rep:
+          i0 = i
+          minLoss = loss
+    it = nn.a[i0].n
+  return it
+
+proc pickSeeds[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; n: Node[M, D, RT, LT] | Leaf[M, D, RT, LT]; bx: Box[D, RT]): (int, int) =
+  var i0, j0: int
+  var bi, bj: typeof(bx)
+  var largestWaste = typeof(bx[0].a).low
+  for i in -1 .. n.a.high:
+    for j in 0 .. n.a.high:
+      if unlikely(i == j): continue
+      if unlikely(i < 0):
+        bi = bx
+      else:
+        bi = n.a[i].b
+      bj = n.a[j].b
+      let b = union(bi, bj)
+      let h = area(b) - area(bi) - area(bj)
+      if h > largestWaste:
+        largestWaste = h
+        i0 = i
+        j0 = j
+  return (i0, j0)
+
+proc pickNext[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; n0, n1, n2: Node[M, D, RT, LT] | Leaf[M, D, RT, LT]; b1, b2: Box[D, RT]): int =
+  let a1 = area(b1)
+  let a2 = area(b2)
+  var d = typeof(a1).low
+  for i in 0 ..< n0.numEntries:
+    let d1 = area(union(b1, n0.a[i].b)) - a1
+    let d2 = area(union(b2, n0.a[i].b)) - a2
+    if (d1 - d2) * (d1 - d2) > d:
+      result = i
+      d = (d1 - d2) * (d1 - d2)
+
+from algorithm import SortOrder, sort
+proc sortPlus[T](a: var openArray[T], ax: var T, cmp: proc (x, y: T): int {.closure.}, order = algorithm.SortOrder.Ascending) =
+  var j = 0
+  let sign = if order == algorithm.SortOrder.Ascending: 1 else: -1
+  for i in 1 .. a.high:
+    if cmp(a[i], a[j]) * sign < 0:
+      j = i
+  if cmp(a[j], ax) * sign < 0:
+    swap(ax, a[j])
+  a.sort(cmp, order)
+
+# R*TREE procs
+proc rstarSplit[M, D: Dim; RT, LT](t: RStarTree[M, D, RT, LT]; n: var Node[M, D, RT, LT] | var Leaf[M, D, RT, LT]; lx: L[D, RT, LT] | N[M, D, RT, LT]): typeof(n) =
+  type NL = typeof(lx)
+  var nBest: typeof(n)
+  new nBest
+  var lx = lx
+  when n is Node[M, D, RT, LT]:
+    lx.n.parent = n
+  var lxbest: typeof(lx)
+  var m0 = lx.b[0].a.typeof.high
+  for d2 in 0 ..< 2 * D:
+    let d = d2 div 2
+    if d2 mod 2 == 0:
+      sortPlus(n.a, lx, proc (x, y: NL): int = cmp(x.b[d].a, y.b[d].a))
+    else:
+      sortPlus(n.a, lx, proc (x, y: NL): int = cmp(x.b[d].b, y.b[d].b))
+    for i in t.m - 1 .. n.a.high - t.m + 1:
+      var b = lx.b
+      for j in 0 ..< i: # we can precalculate union() for range 0 .. t.m - 1, but that seems to give no real benefit.Maybe for very large M?
+        #echo "x",j
+        b = union(n.a[j].b, b)
+      var m = margin(b)
+      b = n.a[^1].b
+      for j in i ..< n.a.high: # again, precalculation of tail would be possible
+        #echo "y",j
+        b = union(n.a[j].b, b)
+      m += margin(b)
+      if m < m0:
+        nbest[] = n[]
+        lxbest = lx
+        m0 = m
+  var i0 = -1
+  var o0 = lx.b[0].a.typeof.high
+  for i in t.m - 1 .. n.a.typeof.high - t.m + 1:
+    var b1 = lxbest.b
+    for j in 0 ..< i:
+      b1 = union(nbest.a[j].b, b1)
+    var b2 = nbest.a[^1].b
+    for j in i ..< n.a.high:
+      b2 = union(nbest.a[j].b, b2)
+    let o = overlap(b1, b2)
+    if o < o0:
+      i0 = i
+      o0 = o
+  n.a[0] = lxbest
+  for i in 0 ..< i0:
+    n.a[i + 1] = nbest.a[i]
+  new result
+  result.level = n.level
+  result.parent = n.parent
+  for i in i0 .. n.a.high:
+    result.a[i - i0] = nbest.a[i]
+  n.numEntries = i0 + 1
+  result.numEntries = M - i0
+  when n is Node[M, D, RT, LT]:
+    for i in 0 ..< result.numEntries:
+      result.a[i].n.parent = result
+
+proc quadraticSplit[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; n: var Node[M, D, RT, LT] | var Leaf[M, D, RT, LT]; lx: L[D, RT, LT] | N[M, D, RT, LT]): typeof(n) =
+  var n1, n2: typeof(n)
+  var s1, s2: int
+  new n1
+  new n2
+  n1.parent = n.parent
+  n2.parent = n.parent
+  n1.level = n.level
+  n2.level = n.level
+  var lx = lx
+  when n is Node[M, D, RT, LT]:
+    lx.n.parent = n
+  (s1, s2) = pickSeeds(t, n, lx.b)
+  assert s1 >= -1 and s2 >= 0
+  if unlikely(s1 < 0):
+    n1.a[0] = lx
+  else:
+    n1.a[0] = n.a[s1]
+    dec(n.numEntries)
+    if s2 == n.numEntries: # important fix
+      s2 = s1
+    n.a[s1] = n.a[n.numEntries]
+  inc(n1.numEntries)
+  var b1 = n1.a[0].b
+  n2.a[0] = n.a[s2]
+  dec(n.numEntries)
+  n.a[s2] = n.a[n.numEntries]
+  inc(n2.numEntries)
+  var b2 = n2.a[0].b
+  if s1 >= 0:
+    n.a[n.numEntries] = lx
+    inc(n.numEntries)
+  while n.numEntries > 0 and n1.numEntries < (t.bigM + 1 - t.m) and n2.numEntries < (t.bigM + 1 - t.m):
+    let next = pickNext(t, n, n1, n2, b1, b2)
+    let d1 = area(union(b1, n.a[next].b)) - area(b1)
+    let d2 = area(union(b2, n.a[next].b)) - area(b2)
+    if (d1 < d2) or (d1 == d2 and ((area(b1) < area(b2)) or (area(b1) == area(b2) and n1.numEntries < n2.numEntries))):
+      n1.a[n1.numEntries] = n.a[next]
+      b1 = union(b1, n.a[next].b)
+      inc(n1.numEntries)
+    else:
+      n2.a[n2.numEntries] = n.a[next]
+      b2 = union(b2, n.a[next].b)
+      inc(n2.numEntries)
+    dec(n.numEntries)
+    n.a[next] = n.a[n.numEntries]
+  if n.numEntries == 0:
+    discard
+  elif n1.numEntries == (t.bigM + 1 - t.m):
+    while n.numEntries > 0:
+      dec(n.numEntries)
+      n2.a[n2.numEntries] = n.a[n.numEntries]
+      inc(n2.numEntries)
+  elif n2.numEntries == (t.bigM + 1 - t.m):
+    while n.numEntries > 0:
+      dec(n.numEntries)
+      n1.a[n1.numEntries] = n.a[n.numEntries]
+      inc(n1.numEntries)
+  when n is Node[M, D, RT, LT]:
+    for i in 0 ..< n2.numEntries:
+      n2.a[i].n.parent = n2
+  n[] = n1[]
+  return n2
+
+proc overflowTreatment[M, D: Dim; RT, LT](t: RStarTree[M, D, RT, LT]; n: var Node[M, D, RT, LT] | var Leaf[M, D, RT, LT]; lx: L[D, RT, LT] | N[M, D, RT, LT]): typeof(n)
+
+proc adjustTree[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; l, ll: H[M, D, RT, LT]; hb: Box[D, RT]) =
+  var n = l
+  var nn = ll
+  assert n != nil
+  while true:
+    if n == t.root:
+      if nn == nil:
+        break
+      t.root = newNode[M, D, RT, LT]()
+      t.root.level = n.level + 1
+      Node[M, D, RT, LT](t.root).a[0].n = n
+      n.parent = t.root
+      nn.parent = t.root
+      t.root.numEntries = 1
+    let p = Node[M, D, RT, LT](n.parent)
+    var i = 0
+    while p.a[i].n != n:
+      inc(i)
+    var b: typeof(p.a[0].b)
+    if n of Leaf[M, D, RT, LT]:
+      when false:#if likely(nn.isNil): # no performance gain
+        b = union(p.a[i].b, Leaf[M, D, RT, LT](n).a[n.numEntries - 1].b)
+      else:
+        b = Leaf[M, D, RT, LT](n).a[0].b
+        for j in 1 ..< n.numEntries:
+          b = trtree.union(b, Leaf[M, D, RT, LT](n).a[j].b)
+    elif n of Node[M, D, RT, LT]:
+      b = Node[M, D, RT, LT](n).a[0].b
+      for j in 1 ..< n.numEntries:
+        b = union(b, Node[M, D, RT, LT](n).a[j].b)
+    else:
+      assert false
+    #if nn.isNil and p.a[i].b == b: break # no performance gain
+    p.a[i].b = b
+    n = H[M, D, RT, LT](p)
+    if unlikely(nn != nil):
+      if nn of Leaf[M, D, RT, LT]:
+        b = Leaf[M, D, RT, LT](nn).a[0].b
+        for j in 1 ..< nn.numEntries:
+          b = union(b, Leaf[M, D, RT, LT](nn).a[j].b)
+      elif nn of Node[M, D, RT, LT]:
+        b = Node[M, D, RT, LT](nn).a[0].b
+        for j in 1 ..< nn.numEntries:
+          b = union(b, Node[M, D, RT, LT](nn).a[j].b)
+      else:
+        assert false
+      if p.numEntries < p.a.len:
+        p.a[p.numEntries].b = b
+        p.a[p.numEntries].n = nn
+        inc(p.numEntries)
+        assert n != nil
+        nn = nil
+      else:
+        let h: N[M, D, RT, LT] = (b, nn)
+        nn = quadraticSplit(t, p, h)
+    assert n == H[M, D, RT, LT](p)
+    assert n != nil
+    assert t.root != nil
+
+proc insert*[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; leaf: N[M, D, RT, LT] | L[D, RT, LT]; level: int = 0) =
+  when leaf is N[M, D, RT, LT]:
+    assert level > 0
+    type NodeLeaf = Node[M, D, RT, LT]
+  else:
+    assert level == 0
+    type NodeLeaf = Leaf[M, D, RT, LT]
+  for d in leaf.b:
+    assert d.a <= d.b
+  let l = NodeLeaf(chooseSubtree(t, leaf.b, level))
+  if l.numEntries < l.a.len:
+    l.a[l.numEntries] = leaf
+    inc(l.numEntries)
+    when leaf is N[M, D, RT, LT]:
+      leaf.n.parent = l
+    adjustTree(t, l, nil, leaf.b)
+  else:
+    let l2 = quadraticSplit(t, l, leaf)
+    assert l2.level == l.level
+    adjustTree(t, l, l2, leaf.b)
+
+# R*Tree insert procs
+proc rsinsert[M, D: Dim; RT, LT](t: RStarTree[M, D, RT, LT]; leaf: N[M, D, RT, LT] | L[D, RT, LT]; level: int)
+
+proc reInsert[M, D: Dim; RT, LT](t: RStarTree[M, D, RT, LT]; n: var Node[M, D, RT, LT] | var Leaf[M, D, RT, LT]; lx: L[D, RT, LT] | N[M, D, RT, LT]) =
+  type NL = typeof(lx)
+  var lx = lx
+  var buf: typeof(n.a)
+  let p = Node[M, D, RT, LT](n.parent)
+  var i = 0
+  while p.a[i].n != n:
+    inc(i)
+  let c = center(p.a[i].b)
+  sortPlus(n.a, lx, proc (x, y: NL): int = cmp(distance(center(x.b), c), distance(center(y.b), c)))
+  n.numEntries = M - t.p
+  swap(n.a[n.numEntries], lx)
+  inc n.numEntries
+  var b = n.a[0].b
+  for i in 1 ..< n.numEntries:
+    b = union(b, n.a[i].b)
+  p.a[i].b = b
+  for i in M - t.p + 1 .. n.a.high:
+    buf[i] = n.a[i]
+  rsinsert(t, lx, n.level)
+  for i in M - t.p + 1 .. n.a.high:
+    rsinsert(t, buf[i], n.level)
+
+proc overflowTreatment[M, D: Dim; RT, LT](t: RStarTree[M, D, RT, LT]; n: var Node[M, D, RT, LT] | var Leaf[M, D, RT, LT]; lx: L[D, RT, LT] | N[M, D, RT, LT]): typeof(n) =
+  if n.level != t.root.level and t.firstOverflow[n.level]:
+    t.firstOverflow[n.level] = false
+    reInsert(t, n, lx)
+    return nil
+  else:
+    let l2 = rstarSplit(t, n, lx)
+    assert l2.level == n.level
+    return l2
+
+proc rsinsert[M, D: Dim; RT, LT](t: RStarTree[M, D, RT, LT]; leaf: N[M, D, RT, LT] | L[D, RT, LT]; level: int) =
+  when leaf is N[M, D, RT, LT]:
+    assert level > 0
+    type NodeLeaf = Node[M, D, RT, LT]
+  else:
+    assert level == 0
+    type NodeLeaf = Leaf[M, D, RT, LT]
+  let l = NodeLeaf(chooseSubtree(t, leaf.b, level))
+  if l.numEntries < l.a.len:
+    l.a[l.numEntries] = leaf
+    inc(l.numEntries)
+    when leaf is N[M, D, RT, LT]:
+      leaf.n.parent = l
+    adjustTree(t, l, nil, leaf.b)
+  else:
+    when leaf is N[M, D, RT, LT]: # TODO do we need this?
+      leaf.n.parent = l
+    let l2 = overflowTreatment(t, l, leaf)
+    if l2 != nil:
+      assert l2.level == l.level
+      adjustTree(t, l, l2, leaf.b)
+
+proc insert*[M, D: Dim; RT, LT](t: RStarTree[M, D, RT, LT]; leaf: L[D, RT, LT]) =
+  for d in leaf.b:
+    assert d.a <= d.b
+  for i in mitems(t.firstOverflow):
+    i = true
+  rsinsert(t, leaf, 0)
+
+# delete
+proc findLeaf[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; leaf: L[D, RT, LT]): Leaf[M, D, RT, LT] =
+  proc fl[M, D: Dim; RT, LT](h: H[M, D, RT, LT]; leaf: L[D, RT, LT]): Leaf[M, D, RT, LT] =
+    var n = h
+    if n of Node[M, D, RT, LT]:
+      for i in 0 ..< n.numEntries:
+        if intersect(Node[M, D, RT, LT](n).a[i].b, leaf.b):
+          let l = fl(Node[M, D, RT, LT](n).a[i].n, leaf)
+          if l != nil:
+            return l
+    elif n of Leaf[M, D, RT, LT]:
+      for i in 0 ..< n.numEntries:
+        if Leaf[M, D, RT, LT](n).a[i] == leaf:
+          return Leaf[M, D, RT, LT](n)
+    else:
+      assert false
+    return nil
+  fl(t.root, leaf)
+
+proc condenseTree[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; leaf: Leaf[M, D, RT, LT]) =
+  var n: H[M, D, RT, LT] = leaf
+  var q = newSeq[H[M, D, RT, LT]]()
+  var b: typeof(leaf.a[0].b)
+  while n != t.root:
+    let p = Node[M, D, RT, LT](n.parent)
+    var i = 0
+    while p.a[i].n != n:
+      inc(i)
+    if n.numEntries < t.m:
+      dec(p.numEntries)
+      p.a[i] = p.a[p.numEntries]
+      q.add(n)
+    else:
+      if n of Leaf[M, D, RT, LT]:
+        b = Leaf[M, D, RT, LT](n).a[0].b
+        for j in 1 ..< n.numEntries:
+          b = union(b, Leaf[M, D, RT, LT](n).a[j].b)
+      elif n of Node[M, D, RT, LT]:
+        b = Node[M, D, RT, LT](n).a[0].b
+        for j in 1 ..< n.numEntries:
+          b = union(b, Node[M, D, RT, LT](n).a[j].b)
+      else:
+        assert false
+      p.a[i].b = b
+    n = n.parent
+  if t of RStarTree[M, D, RT, LT]:
+    for n in q:
+      if n of Leaf[M, D, RT, LT]:
+        for i in 0 ..< n.numEntries:
+          for i in mitems(RStarTree[M, D, RT, LT](t).firstOverflow):
+            i = true
+          rsinsert(RStarTree[M, D, RT, LT](t), Leaf[M, D, RT, LT](n).a[i], 0)
+      elif n of Node[M, D, RT, LT]:
+        for i in 0 ..< n.numEntries:
+          for i in mitems(RStarTree[M, D, RT, LT](t).firstOverflow):
+            i = true
+          rsinsert(RStarTree[M, D, RT, LT](t), Node[M, D, RT, LT](n).a[i], n.level)
+      else:
+        assert false
+  else:
+    for n in q:
+      if n of Leaf[M, D, RT, LT]:
+        for i in 0 ..< n.numEntries:
+          insert(t, Leaf[M, D, RT, LT](n).a[i])
+      elif n of Node[M, D, RT, LT]:
+        for i in 0 ..< n.numEntries:
+          insert(t, Node[M, D, RT, LT](n).a[i], n.level)
+      else:
+        assert false
+
+proc delete*[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; leaf: L[D, RT, LT]): bool {.discardable.} =
+  let l = findLeaf(t, leaf)
+  if l.isNil:
+    return false
+  else:
+    var i = 0
+    while l.a[i] != leaf:
+      inc(i)
+    dec(l.numEntries)
+    l.a[i] = l.a[l.numEntries]
+    condenseTree(t, l)
+    if t.root.numEntries == 1:
+      if t.root of Node[M, D, RT, LT]:
+        t.root = Node[M, D, RT, LT](t.root).a[0].n
+      t.root.parent = nil
+    return true
+
+
+var t = [4, 1, 3, 2]
+var xt = 7
+sortPlus(t, xt, system.cmp, SortOrder.Ascending)
+echo xt, " ", t
+
+type
+  RSE = L[2, int, int]
+  RSeq = seq[RSE]
+
+proc rseq_search(rs: RSeq; rse: RSE): seq[int] =
+  result = newSeq[int]()
+  for i in rs:
+    if intersect(i.b, rse.b):
+      result.add(i.l)
+
+proc rseq_delete(rs: var RSeq; rse: RSE): bool =
+  for i in 0 .. rs.high:
+    if rs[i] == rse:
+      #rs.delete(i)
+      rs[i] = rs[rs.high]
+      rs.setLen(rs.len - 1)
+      return true
+
+import random, algorithm
+
+proc test(n: int) =
+  var b: Box[2, int]
+  echo center(b)
+  var x1, x2, y1, y2: int
+  var t = newRStarTree[8, 2, int, int]()
+  #var t = newRTree[8, 2, int, int]()
+  var rs = newSeq[RSE]()
+  for i in 0 .. 5:
+    for i in 0 .. n - 1:
+      x1 = rand(1000)
+      y1 = rand(1000)
+      x2 = x1 + rand(25)
+      y2 = y1 + rand(25)
+      b = [(x1, x2), (y1, y2)]
+      let el: L[2, int, int] = (b, i + 7)
+      t.insert(el)
+      rs.add(el)
+
+    for i in 0 .. (n div 4):
+      let j = rand(rs.high)
+      var el = rs[j]
+      assert t.delete(el)
+      assert rs.rseq_delete(el)
+
+    for i in 0 .. n - 1:
+      x1 = rand(1000)
+      y1 = rand(1000)
+      x2 = x1 + rand(100)
+      y2 = y1 + rand(100)
+      b = [(x1, x2), (y1, y2)]
+      let el: L[2, int, int] = (b, i)
+      let r = search(t, b)
+      let r2 = rseq_search(rs, el)
+      assert r.len == r2.len
+      assert r.sorted(system.cmp) == r2.sorted(system.cmp)
+
+test(500)
diff --git a/tests/arc/tshared_ptr_crash.nim b/tests/arc/tshared_ptr_crash.nim
new file mode 100644
index 000000000..1794834db
--- /dev/null
+++ b/tests/arc/tshared_ptr_crash.nim
@@ -0,0 +1,67 @@
+discard """
+  cmd: "nim c --threads:on --gc:arc $file"
+  action: compile
+"""
+
+# bug #17893
+
+type
+  SharedPtr*[T] = object
+    val: ptr tuple[value: T, atomicCounter: int]
+
+proc `=destroy`*[T](p: var SharedPtr[T]) =
+  mixin `=destroy`
+  if p.val != nil:
+    if atomicLoadN(addr p.val[].atomicCounter, AtomicConsume) == 0:
+      `=destroy`(p.val[])
+      deallocShared(p.val)
+    else:
+      discard atomicDec(p.val[].atomicCounter)
+
+proc `=copy`*[T](dest: var SharedPtr[T], src: SharedPtr[T]) =
+  if src.val != nil:
+    discard atomicInc(src.val[].atomicCounter)
+  if dest.val != nil:
+    `=destroy`(dest)
+  dest.val = src.val
+
+proc newSharedPtr*[T](val: sink T): SharedPtr[T] {.nodestroy.} =
+  result.val = cast[typeof(result.val)](allocShared(sizeof(result.val[])))
+  result.val.atomicCounter = 0
+  result.val.value = val
+
+proc isNil*[T](p: SharedPtr[T]): bool {.inline.} =
+  p.val == nil
+
+proc `[]`*[T](p: SharedPtr[T]): var T {.inline.} =
+  when compileOption("boundChecks"):
+    doAssert(p.val != nil, "deferencing nil shared pointer")
+  result = p.val.value
+
+type
+  Sender*[T] = object
+    queue: SharedPtr[seq[T]]
+
+proc newSender*[T](queue: sink SharedPtr[seq[T]]): Sender[T] =
+  result = Sender[T](queue: queue)
+
+proc send*[T](self: Sender[T]; t: sink T) =
+  self.queue[].add t
+
+proc newChannel*(): Sender[int] =
+  let queue = newSharedPtr(newSeq[int]())
+  result = newSender(queue)
+
+
+var
+  p: Thread[Sender[int]]
+
+proc threadFn(tx: Sender[int]) =
+  send tx, 0
+
+proc multiThreadedChannel =
+  let tx = newChannel()
+  createThread(p, threadFn, tx)
+  joinThread(p)
+
+multiThreadedChannel()
diff --git a/tests/arc/tstrformat.nim b/tests/arc/tstrformat.nim
new file mode 100644
index 000000000..641f323da
--- /dev/null
+++ b/tests/arc/tstrformat.nim
@@ -0,0 +1,22 @@
+discard """
+  output: '''
+verstuff
+'''
+  cmd: "nim c --gc:arc $file"
+"""
+
+# bug #13622
+
+import strformat
+
+template necho*(args: string) {.dirty.} =
+  if getCurrentException() != nil:
+    echo args
+  else:
+    stdout.writeLine(args)
+
+proc main(cond: bool; arg: string) =
+  if cond:
+    necho &"ver{arg}\n"
+
+main(true, "stuff")
diff --git a/tests/arc/tstringliteral.nim b/tests/arc/tstringliteral.nim
new file mode 100644
index 000000000..c5fac22d8
--- /dev/null
+++ b/tests/arc/tstringliteral.nim
@@ -0,0 +1,17 @@
+discard """
+  matrix: "--mm:arc; --mm:orc"
+"""
+
+block: # issue #24080
+  var a = (s: "a")
+  var b = "a"
+  a.s.setLen 0
+  b = a.s
+  doAssert b == ""
+
+block: # issue #24080, longer string
+  var a = (s: "abc")
+  var b = "abc"
+  a.s.setLen 2
+  b = a.s
+  doAssert b == "ab"
diff --git a/tests/arc/tthread.nim b/tests/arc/tthread.nim
new file mode 100644
index 000000000..8a55a666e
--- /dev/null
+++ b/tests/arc/tthread.nim
@@ -0,0 +1,63 @@
+discard """
+  cmd: "nim cpp --gc:arc --threads:on $file"
+  output: '''ok1
+ok2
+destroyed
+destroyed
+destroyed
+'''
+"""
+import threadpool, os
+
+type
+  MyObj = object
+    p: int
+  MyObjRef = ref MyObj
+
+proc `=destroy`(x: var MyObj) =
+  if x.p != 0:
+    echo "destroyed"
+
+proc thread1(): string =
+  os.sleep(1000)
+  return "ok1"
+
+proc thread2(): ref string =
+  os.sleep(1000)
+  new(result)
+  result[] = "ok2"
+
+proc thread3(): ref MyObj =
+  os.sleep(1000)
+  new(result)
+  result[].p = 2
+
+var fv1 = spawn thread1()
+var fv2 = spawn thread2()
+var fv3 = spawn thread3()
+sync()
+echo ^fv1
+echo (^fv2)[]
+
+
+proc thread4(x: MyObjRef): MyObjRef {.nosinks.} =
+  os.sleep(1000)
+  result = x
+
+proc thread5(x: sink MyObjRef): MyObjRef =
+  os.sleep(1000)
+  result = x
+
+proc ref_forwarding_test =
+  var x = new(MyObj)
+  x[].p = 2
+  var y = spawn thread4(x)
+
+proc ref_sink_forwarding_test =
+  var x = new(MyObj)
+  x[].p = 2
+  var y = spawn thread5(x)
+
+ref_forwarding_test()
+ref_sink_forwarding_test()
+sync()
diff --git a/tests/arc/tunref_cycle.nim b/tests/arc/tunref_cycle.nim
new file mode 100644
index 000000000..82551b7f7
--- /dev/null
+++ b/tests/arc/tunref_cycle.nim
@@ -0,0 +1,26 @@
+discard """
+  outputsub: '''inside closure
+hello world'''
+  cmd: "nim c --gc:orc -d:useMalloc $file"
+  valgrind: true
+"""
+
+# bug #18579
+
+var fp: proc (env: pointer) {.cdecl.}
+var env: pointer
+
+proc store(f: proc (){.closure.}) =
+  proc closeOver() =
+    echo "inside closure"
+    f()
+  (fp,env) = (cast[proc(env: pointer){.cdecl.}](rawProc closeOver), rawEnv closeOver)
+  GC_ref(cast[RootRef](env))
+
+proc run() =
+  fp(env)
+  GC_unref(cast[RootRef](env))
+
+store(proc() = echo "hello world")
+run()
+GC_fullCollect()
diff --git a/tests/arc/tweave.nim b/tests/arc/tweave.nim
new file mode 100644
index 000000000..1c60ac403
--- /dev/null
+++ b/tests/arc/tweave.nim
@@ -0,0 +1,157 @@
+discard """
+  outputsub: '''Success'''
+  cmd: '''nim c --gc:arc --threads:on $file'''
+  disabled: "bsd"
+"""
+
+# bug #13936
+
+import std/atomics
+
+when defined(nimPreviewSlimSystem):
+  import std/[assertions, typedthreads]
+
+const MemBlockSize = 256
+
+type
+  ChannelSPSCSingle* = object
+    full{.align: 128.}: Atomic[bool]
+    itemSize*: uint8
+    buffer*{.align: 8.}: UncheckedArray[byte]
+
+proc `=`(
+    dest: var ChannelSPSCSingle,
+    source: ChannelSPSCSingle
+  ) {.error: "A channel cannot be copied".}
+
+proc initialize*(chan: var ChannelSPSCSingle, itemsize: SomeInteger) {.inline.} =
+  ## If ChannelSPSCSingle is used intrusive another data structure
+  ## be aware that it should be the last part due to ending by UncheckedArray
+  ## Also due to 128 bytes padding, it automatically takes half
+  ## of the default MemBlockSize
+  assert itemsize.int in 0 .. int high(uint8)
+  assert itemSize.int +
+          sizeof(chan.itemsize) +
+          sizeof(chan.full) < MemBlockSize
+
+  chan.itemSize = uint8 itemsize
+  chan.full.store(false, moRelaxed)
+
+func isEmpty*(chan: var ChannelSPSCSingle): bool {.inline.} =
+  not chan.full.load(moAcquire)
+
+func tryRecv*[T](chan: var ChannelSPSCSingle, dst: var T): bool {.inline.} =
+  ## Try receiving the item buffered in the channel
+  ## Returns true if successful (channel was not empty)
+  ##
+  ## ⚠ Use only in the consumer thread that reads from the channel.
+  assert (sizeof(T) == chan.itemsize.int) or
+          # Support dummy object
+          (sizeof(T) == 0 and chan.itemsize == 1)
+
+  let full = chan.full.load(moAcquire)
+  if not full:
+    return false
+  dst = cast[ptr T](chan.buffer.addr)[]
+  chan.full.store(false, moRelease)
+  return true
+
+func trySend*[T](chan: var ChannelSPSCSingle, src: sink T): bool {.inline.} =
+  ## Try sending an item into the channel
+  ## Reurns true if successful (channel was empty)
+  ##
+  ## ⚠ Use only in the producer thread that writes from the channel.
+  assert (sizeof(T) == chan.itemsize.int) or
+          # Support dummy object
+          (sizeof(T) == 0 and chan.itemsize == 1)
+
+  let full = chan.full.load(moAcquire)
+  if full:
+    return false
+  cast[ptr T](chan.buffer.addr)[] = src
+  chan.full.store(true, moRelease)
+  return true
+
+# Sanity checks
+# ------------------------------------------------------------------------------
+when isMainModule:
+
+  when not compileOption("threads"):
+    {.error: "This requires --threads:on compilation flag".}
+
+  template sendLoop[T](chan: var ChannelSPSCSingle,
+                       data: sink T,
+                       body: untyped): untyped =
+    while not chan.trySend(data):
+      body
+
+  template recvLoop[T](chan: var ChannelSPSCSingle,
+                       data: var T,
+                       body: untyped): untyped =
+    while not chan.tryRecv(data):
+      body
+
+  type
+    ThreadArgs = object
+      ID: WorkerKind
+      chan: ptr ChannelSPSCSingle
+
+    WorkerKind = enum
+      Sender
+      Receiver
+
+  template Worker(id: WorkerKind, body: untyped): untyped {.dirty.} =
+    if args.ID == id:
+      body
+
+  proc thread_func(args: ThreadArgs) =
+
+    # Worker RECEIVER:
+    # ---------
+    # <- chan
+    # <- chan
+    # <- chan
+    #
+    # Worker SENDER:
+    # ---------
+    # chan <- 42
+    # chan <- 53
+    # chan <- 64
+    Worker(Receiver):
+      var val: int
+      for j in 0 ..< 10:
+        args.chan[].recvLoop(val):
+          # Busy loop, in prod we might want to yield the core/thread timeslice
+          discard
+        echo "                  Receiver got: ", val
+        doAssert val == 42 + j*11
+
+    Worker(Sender):
+      doAssert args.chan.full.load(moRelaxed) == false
+      for j in 0 ..< 10:
+        let val = 42 + j*11
+        args.chan[].sendLoop(val):
+          # Busy loop, in prod we might want to yield the core/thread timeslice
+          discard
+        echo "Sender sent: ", val
+
+  proc main() =
+    echo "Testing if 2 threads can send data"
+    echo "-----------------------------------"
+    var threads: array[2, Thread[ThreadArgs]]
+
+    var chan = cast[ptr ChannelSPSCSingle](allocShared(MemBlockSize))
+    chan[].initialize(itemSize = sizeof(int))
+
+    createThread(threads[0], thread_func, ThreadArgs(ID: Receiver, chan: chan))
+    createThread(threads[1], thread_func, ThreadArgs(ID: Sender, chan: chan))
+
+    joinThread(threads[0])
+    joinThread(threads[1])
+
+    freeShared(chan)
+
+    echo "-----------------------------------"
+    echo "Success"
+
+  main()
diff --git a/tests/arc/tweavecopy.nim b/tests/arc/tweavecopy.nim
new file mode 100644
index 000000000..fc796b352
--- /dev/null
+++ b/tests/arc/tweavecopy.nim
@@ -0,0 +1,154 @@
+discard """
+  outputsub: '''Success'''
+  cmd: '''nim c --gc:arc --threads:on $file'''
+  disabled: "bsd"
+"""
+
+# bug #13936
+
+import std/atomics
+
+const MemBlockSize = 256
+
+type
+  ChannelSPSCSingle* = object
+    full{.align: 128.}: Atomic[bool]
+    itemSize*: uint8
+    buffer*{.align: 8.}: UncheckedArray[byte]
+
+proc `=copy`(
+    dest: var ChannelSPSCSingle,
+    source: ChannelSPSCSingle
+  ) {.error: "A channel cannot be copied".}
+
+proc initialize*(chan: var ChannelSPSCSingle, itemsize: SomeInteger) {.inline.} =
+  ## If ChannelSPSCSingle is used intrusive another data structure
+  ## be aware that it should be the last part due to ending by UncheckedArray
+  ## Also due to 128 bytes padding, it automatically takes half
+  ## of the default MemBlockSize
+  assert itemsize.int in 0 .. int high(uint8)
+  assert itemSize.int +
+          sizeof(chan.itemsize) +
+          sizeof(chan.full) < MemBlockSize
+
+  chan.itemSize = uint8 itemsize
+  chan.full.store(false, moRelaxed)
+
+func isEmpty*(chan: var ChannelSPSCSingle): bool {.inline.} =
+  not chan.full.load(moAcquire)
+
+func tryRecv*[T](chan: var ChannelSPSCSingle, dst: var T): bool {.inline.} =
+  ## Try receiving the item buffered in the channel
+  ## Returns true if successful (channel was not empty)
+  ##
+  ## ⚠ Use only in the consumer thread that reads from the channel.
+  assert (sizeof(T) == chan.itemsize.int) or
+          # Support dummy object
+          (sizeof(T) == 0 and chan.itemsize == 1)
+
+  let full = chan.full.load(moAcquire)
+  if not full:
+    return false
+  dst = cast[ptr T](chan.buffer.addr)[]
+  chan.full.store(false, moRelease)
+  return true
+
+func trySend*[T](chan: var ChannelSPSCSingle, src: sink T): bool {.inline.} =
+  ## Try sending an item into the channel
+  ## Reurns true if successful (channel was empty)
+  ##
+  ## ⚠ Use only in the producer thread that writes from the channel.
+  assert (sizeof(T) == chan.itemsize.int) or
+          # Support dummy object
+          (sizeof(T) == 0 and chan.itemsize == 1)
+
+  let full = chan.full.load(moAcquire)
+  if full:
+    return false
+  cast[ptr T](chan.buffer.addr)[] = src
+  chan.full.store(true, moRelease)
+  return true
+
+# Sanity checks
+# ------------------------------------------------------------------------------
+when isMainModule:
+
+  when not compileOption("threads"):
+    {.error: "This requires --threads:on compilation flag".}
+
+  template sendLoop[T](chan: var ChannelSPSCSingle,
+                       data: sink T,
+                       body: untyped): untyped =
+    while not chan.trySend(data):
+      body
+
+  template recvLoop[T](chan: var ChannelSPSCSingle,
+                       data: var T,
+                       body: untyped): untyped =
+    while not chan.tryRecv(data):
+      body
+
+  type
+    ThreadArgs = object
+      ID: WorkerKind
+      chan: ptr ChannelSPSCSingle
+
+    WorkerKind = enum
+      Sender
+      Receiver
+
+  template Worker(id: WorkerKind, body: untyped): untyped {.dirty.} =
+    if args.ID == id:
+      body
+
+  proc thread_func(args: ThreadArgs) =
+
+    # Worker RECEIVER:
+    # ---------
+    # <- chan
+    # <- chan
+    # <- chan
+    #
+    # Worker SENDER:
+    # ---------
+    # chan <- 42
+    # chan <- 53
+    # chan <- 64
+    Worker(Receiver):
+      var val: int
+      for j in 0 ..< 10:
+        args.chan[].recvLoop(val):
+          # Busy loop, in prod we might want to yield the core/thread timeslice
+          discard
+        echo "                  Receiver got: ", val
+        doAssert val == 42 + j*11
+
+    Worker(Sender):
+      doAssert args.chan.full.load(moRelaxed) == false
+      for j in 0 ..< 10:
+        let val = 42 + j*11
+        args.chan[].sendLoop(val):
+          # Busy loop, in prod we might want to yield the core/thread timeslice
+          discard
+        echo "Sender sent: ", val
+
+  proc main() =
+    echo "Testing if 2 threads can send data"
+    echo "-----------------------------------"
+    var threads: array[2, Thread[ThreadArgs]]
+
+    var chan = cast[ptr ChannelSPSCSingle](allocShared(MemBlockSize))
+    chan[].initialize(itemSize = sizeof(int))
+
+    createThread(threads[0], thread_func, ThreadArgs(ID: Receiver, chan: chan))
+    createThread(threads[1], thread_func, ThreadArgs(ID: Sender, chan: chan))
+
+    joinThread(threads[0])
+    joinThread(threads[1])
+
+    freeShared(chan)
+
+    echo "-----------------------------------"
+    echo "Success"
+
+  main()
diff --git a/tests/arc/twrong_sinkinference.nim b/tests/arc/twrong_sinkinference.nim
new file mode 100644
index 000000000..ecf09d28a
--- /dev/null
+++ b/tests/arc/twrong_sinkinference.nim
@@ -0,0 +1,18 @@
+discard """
+  cmd: "nim c --gc:arc $file"
+  errormsg: "type mismatch: got <proc (a: string, b: sink string){.noSideEffect, gcsafe.}>"
+  line: 18
+"""
+
+type
+  Foo = proc (a, b: string)
+
+proc take(x: Foo) =
+  x("a", "b")
+
+proc willSink(a, b: string) = # {.nosinks.} =
+  var arr: array[3, string]
+  var x = a
+  arr[0] = b
+
+take willSink