summary refs log tree commit diff stats
path: root/tests/destructor
diff options
context:
space:
mode:
Diffstat (limited to 'tests/destructor')
-rw-r--r--tests/destructor/const_smart_ptr.nim75
-rw-r--r--tests/destructor/nim.cfg1
-rw-r--r--tests/destructor/smart_ptr.nim6
-rw-r--r--tests/destructor/t12037.nim34
-rw-r--r--tests/destructor/t16607.nim23
-rw-r--r--tests/destructor/t17198.nim32
-rw-r--r--tests/destructor/t23748.nim31
-rw-r--r--tests/destructor/t23837.nim51
-rw-r--r--tests/destructor/t5342.nim24
-rw-r--r--tests/destructor/t6434.nim27
-rw-r--r--tests/destructor/t7346.nim3
-rw-r--r--tests/destructor/t9440.nim52
-rw-r--r--tests/destructor/tarc.nim184
-rw-r--r--tests/destructor/tarc2.nim31
-rw-r--r--tests/destructor/tarc3.nim101
-rw-r--r--tests/destructor/tarctypesections.nim70
-rw-r--r--tests/destructor/tarray_indexing.nim75
-rw-r--r--tests/destructor/tasync_prototype.nim59
-rw-r--r--tests/destructor/tasync_prototype_cyclic.nim55
-rw-r--r--tests/destructor/tatomicptrs.nim33
-rw-r--r--tests/destructor/tbintree2.nim101
-rw-r--r--tests/destructor/tcaseobj_transitions.nim18
-rw-r--r--tests/destructor/tcomplexobjconstr.nim56
-rw-r--r--tests/destructor/tconst_smart_ptr.nim7
-rw-r--r--tests/destructor/tconsume_twice.nim4
-rw-r--r--tests/destructor/tcustomseqs.nim2
-rw-r--r--tests/destructor/tcustomstrings.nim22
-rw-r--r--tests/destructor/tcycle1.nim54
-rw-r--r--tests/destructor/tcycle2.nim36
-rw-r--r--tests/destructor/tcycle3.nim98
-rw-r--r--tests/destructor/tdangingref_simple.nim32
-rw-r--r--tests/destructor/tdestructor.nim58
-rw-r--r--tests/destructor/tdestructor3.nim148
-rw-r--r--tests/destructor/tdestructor_too_late.nim14
-rw-r--r--tests/destructor/tdistinctseq.nim8
-rw-r--r--tests/destructor/tdont_return_unowned_from_owned.nim49
-rw-r--r--tests/destructor/texceptions.nim29
-rw-r--r--tests/destructor/tfinalizer.nim31
-rw-r--r--tests/destructor/tgcdestructors.nim41
-rw-r--r--tests/destructor/tgcleak4.nim47
-rw-r--r--tests/destructor/tglobaldestructor.nim2
-rw-r--r--tests/destructor/tgotoexc_leak.nim19
-rwxr-xr-xtests/destructor/tgotoexceptions.nim117
-rw-r--r--tests/destructor/tgotoexceptions2.nim104
-rw-r--r--tests/destructor/tgotoexceptions3.nim7
-rw-r--r--tests/destructor/tgotoexceptions4.nim60
-rw-r--r--tests/destructor/tgotoexceptions5.nim45
-rw-r--r--tests/destructor/tgotoexceptions6.nim10
-rw-r--r--tests/destructor/tgotoexceptions7.nim49
-rw-r--r--tests/destructor/tgotoexceptions8.nim76
-rw-r--r--tests/destructor/tmatrix.nim11
-rw-r--r--tests/destructor/tmisc_destructors.nim43
-rw-r--r--tests/destructor/tmove.nim18
-rw-r--r--tests/destructor/tmove_objconstr.nim64
-rw-r--r--tests/destructor/tnewruntime_misc.nim155
-rw-r--r--tests/destructor/tnewruntime_strutils.nim85
-rw-r--r--tests/destructor/tnonvardestructor.nim247
-rw-r--r--tests/destructor/topttree.nim1
-rw-r--r--tests/destructor/towned_binary_tree.nim11
-rw-r--r--tests/destructor/tprevent_assign.nim2
-rw-r--r--tests/destructor/tprevent_assign2.nim11
-rw-r--r--tests/destructor/tprevent_assign3.nim11
-rw-r--r--tests/destructor/trecursive.nim26
-rw-r--r--tests/destructor/tselect.nim50
-rw-r--r--tests/destructor/tsetjmp_raise.nim11
-rw-r--r--tests/destructor/tsimpleclosure.nim62
-rw-r--r--tests/destructor/tsink.nim70
-rw-r--r--tests/destructor/ttuple.nim84
-rw-r--r--tests/destructor/tuse_ownedref_after_move.nim7
-rw-r--r--tests/destructor/tuse_result_prevents_sinks.nim7
-rw-r--r--tests/destructor/tv2_cast.nim102
-rw-r--r--tests/destructor/tv2_raise.nim25
-rw-r--r--tests/destructor/twasmoved.nim14
-rw-r--r--tests/destructor/twasmoved_error.nim37
-rw-r--r--tests/destructor/twidgets.nim12
-rw-r--r--tests/destructor/twidgets_unown.nim15
76 files changed, 3292 insertions, 200 deletions
diff --git a/tests/destructor/const_smart_ptr.nim b/tests/destructor/const_smart_ptr.nim
new file mode 100644
index 000000000..25dd46500
--- /dev/null
+++ b/tests/destructor/const_smart_ptr.nim
@@ -0,0 +1,75 @@
+type
+  ConstPtr*[T] = object
+    val: ptr T
+
+proc `=destroy`*[T](p: ConstPtr[T]) =
+  if p.val != nil:
+    `=destroy`(p.val[])
+    dealloc(p.val)
+
+proc `=copy`*[T](dest: var ConstPtr[T], src: ConstPtr[T]) {.error.}
+
+proc `=sink`*[T](dest: var ConstPtr[T], src: ConstPtr[T]) {.inline.} =
+  if dest.val != nil and dest.val != src.val:
+    `=destroy`(dest)
+  dest.val = src.val
+
+proc newConstPtr*[T](val: sink T): ConstPtr[T] {.inline.} =
+  result.val = cast[type(result.val)](alloc(sizeof(result.val[])))
+  reset(result.val[])
+  result.val[] = val
+
+converter convertConstPtrToObj*[T](p: ConstPtr[T]): lent T =
+  result = p.val[]
+
+
+#-------------------------------------------------------------
+
+type
+  MySeqNonCopyable* = object
+    len: int
+    data: ptr UncheckedArray[float]
+
+proc `=destroy`*(m: MySeqNonCopyable) {.inline.} =
+  if m.data != nil:
+    deallocShared(m.data)
+
+proc `=copy`*(m: var MySeqNonCopyable, m2: MySeqNonCopyable) {.error.}
+
+proc `=sink`*(m: var MySeqNonCopyable, m2: MySeqNonCopyable) {.inline.} =
+  if m.data != m2.data:
+    if m.data != nil:
+      `=destroy`(m)
+    m.len = m2.len
+    m.data = m2.data
+
+proc len*(m: MySeqNonCopyable): int {.inline.} = m.len
+
+proc `[]`*(m: MySeqNonCopyable; i: int): float {.inline.} =
+  m.data[i.int]
+
+proc `[]=`*(m: var MySeqNonCopyable; i: int, val: float) {.inline.} =
+  m.data[i.int] = val
+
+proc setTo(s: var MySeqNonCopyable, val: float) =
+  for i in 0..<s.len.int:
+    s.data[i] = val
+
+proc newMySeq*(size: int, initial_value = 0.0): MySeqNonCopyable =
+  result.len = size
+  if size > 0:
+    result.data = cast[ptr UncheckedArray[float]](createShared(float, size))
+  result.setTo(initial_value)
+
+#----------------------------------------------------------------------
+
+
+proc test*(x1: int): ConstPtr[MySeqNonCopyable] {.inline.} = # remove inline here to make it work as expected
+  if x1 == 0:
+    let x = newMySeq(1, 0.0)
+    result = newConstPtr(x)
+  else:
+    let y = newMySeq(x1, 0.0)
+    result = newConstPtr(y)
+
+discard test(10)
diff --git a/tests/destructor/nim.cfg b/tests/destructor/nim.cfg
new file mode 100644
index 000000000..7c148b797
--- /dev/null
+++ b/tests/destructor/nim.cfg
@@ -0,0 +1 @@
+--sinkInference:on
diff --git a/tests/destructor/smart_ptr.nim b/tests/destructor/smart_ptr.nim
index 7c3141d22..5079dc9db 100644
--- a/tests/destructor/smart_ptr.nim
+++ b/tests/destructor/smart_ptr.nim
@@ -20,12 +20,6 @@ proc `=`*[T](dest: var SharedPtr[T], src: SharedPtr[T]) {.inline.} =
       discard atomicInc(src.val[].atomicCounter)
     dest.val = src.val
 
-proc `=sink`*[T](dest: var SharedPtr[T], src: SharedPtr[T]) {.inline.} =
-  if dest.val != src.val:
-    if dest.val != nil:
-      `=destroy`(dest)
-    dest.val = src.val
-
 proc newSharedPtr*[T](val: sink T): SharedPtr[T] =
   result.val = cast[type(result.val)](allocShared0(sizeof(result.val[])))
   result.val.atomicCounter = 1
diff --git a/tests/destructor/t12037.nim b/tests/destructor/t12037.nim
new file mode 100644
index 000000000..30266690f
--- /dev/null
+++ b/tests/destructor/t12037.nim
@@ -0,0 +1,34 @@
+discard """
+  cmd: '''nim c --gc:arc $file'''
+  output: '''
+showing original type, length, and contents seq[int] 1 @[42]
+copy length and contents 1 @[42]
+'''
+"""
+
+proc test() =
+  var sq1 = @[42]
+  echo "showing original type, length, and contents ", sq1.typeof, " ", sq1.len, " ", sq1
+  doAssert cast[int](sq1[0].addr) != 0
+  var sq2 = sq1 # copy of original
+  echo "copy length and contents ", sq2.len, " ", sq2
+  doAssert cast[int](sq2[0].addr) != 0
+  doAssert cast[int](sq1[0].addr) != 0
+
+test()
+
+
+#############################################
+### bug 12820
+import tables
+var t = initTable[string, seq[ptr int]]()
+discard t.hasKeyOrPut("f1", @[])
+
+
+#############################################
+### bug #12989
+proc bug(start: (seq[int], int)) =
+  let (s, i) = start
+
+let input = @[0]
+bug((input, 0))
diff --git a/tests/destructor/t16607.nim b/tests/destructor/t16607.nim
new file mode 100644
index 000000000..f98a6d517
--- /dev/null
+++ b/tests/destructor/t16607.nim
@@ -0,0 +1,23 @@
+discard """
+  matrix: "--gc:refc; --gc:arc"
+"""
+
+# bug #16607
+
+type
+  O {.requiresInit.} = object
+    initialized: bool
+
+proc `=destroy`(o: var O) =
+  doAssert o.initialized, "O was destroyed before initialization!"
+
+proc initO(): O =
+  O(initialized: true)
+
+proc pair(): tuple[a, b: O] =
+  result = (a: initO(), b: initO())
+
+proc main() =
+  discard pair()
+
+main()
diff --git a/tests/destructor/t17198.nim b/tests/destructor/t17198.nim
new file mode 100644
index 000000000..098db8245
--- /dev/null
+++ b/tests/destructor/t17198.nim
@@ -0,0 +1,32 @@
+discard """
+  cmd: '''nim c --gc:arc $file'''
+  output: '''
+other
+'''
+"""
+
+import std/macros
+
+macro bigCaseStmt(arg: untyped): untyped =
+  result = nnkCaseStmt.newTree(arg)
+
+  # try to change 2000 to a bigger value if it doesn't crash
+  for x in 0 ..< 2000:
+    result.add nnkOfBranch.newTree(newStrLitNode($x), newStrLitNode($x))
+
+  result.add nnkElse.newTree(newStrLitNode("other"))
+
+macro bigIfElseExpr(): untyped =
+  result = nnkIfExpr.newTree()
+
+  for x in 0 ..< 1000:
+    result.add nnkElifExpr.newTree(newLit(false), newStrLitNode($x))
+
+  result.add nnkElseExpr.newTree(newStrLitNode("other"))
+
+proc test(arg: string): string =
+  echo bigIfElseExpr()
+
+  result = bigCaseStmt(arg)
+
+discard test("test")
diff --git a/tests/destructor/t23748.nim b/tests/destructor/t23748.nim
new file mode 100644
index 000000000..a3738733e
--- /dev/null
+++ b/tests/destructor/t23748.nim
@@ -0,0 +1,31 @@
+discard """
+  matrix: "--gc:refc; --gc:arc"
+  output: '''
+hello 42
+hello 42
+len = 2
+'''
+"""
+
+# bug #23748
+
+type
+  O = ref object
+    s: string
+    cb: seq[proc()]
+
+proc push1(o: O, i: int) =
+  let o = o
+  echo o.s, " ", i
+  o.cb.add(proc() = echo o.s, " ", i)
+
+proc push2(o: O, i: int) =
+  let o = o
+  echo o.s, " ", i
+  proc p() = echo o.s, " ", i
+  o.cb.add(p)
+
+let o = O(s: "hello", cb: @[])
+o.push1(42)
+o.push2(42)
+echo "len = ", o.cb.len
diff --git a/tests/destructor/t23837.nim b/tests/destructor/t23837.nim
new file mode 100644
index 000000000..e219dd6b5
--- /dev/null
+++ b/tests/destructor/t23837.nim
@@ -0,0 +1,51 @@
+discard """
+  output: '''
+Deallocating OwnedString
+HelloWorld
+'''
+  matrix: "--cursorinference:on; --cursorinference:off"
+  target: "c"
+"""
+
+# bug #23837
+{.
+  emit: [
+    """
+#include <stdlib.h>
+#include <string.h>
+char *allocCString() {
+    char *result = (char *) malloc(10 + 1);
+    strcpy(result, "HelloWorld");
+    return result;
+}
+
+"""
+  ]
+.}
+
+proc rawWrapper(): cstring {.importc: "allocCString", cdecl.}
+proc free(p: pointer) {.importc: "free", cdecl.}
+
+# -------------------------
+
+type OwnedString = distinct cstring
+
+proc `=destroy`(s: OwnedString) =
+  free(cstring s)
+  echo "Deallocating OwnedString"
+
+func `$`(s: OwnedString): string {.borrow.}
+
+proc leakyWrapper(): string =
+  let ostring = rawWrapper().OwnedString
+  $ostring
+
+# -------------------------
+
+proc main() =
+  # destructor not called - definitely lost: 11 bytes in 1 blocks
+  # doesn't leak with --cursorInference:off
+  let s = leakyWrapper()
+  echo s
+
+main()
\ No newline at end of file
diff --git a/tests/destructor/t5342.nim b/tests/destructor/t5342.nim
new file mode 100644
index 000000000..0acd5ef9d
--- /dev/null
+++ b/tests/destructor/t5342.nim
@@ -0,0 +1,24 @@
+discard """
+  matrix: "--mm:refc; --mm:arc"
+  targets: "c js"
+  output: '''
+1
+2
+here
+2
+1
+'''
+"""
+
+
+type
+  A = object
+    id: int
+  B = object
+    a: A
+proc `=destroy`(a: var A) = echo a.id
+var x = A(id: 1)
+var y = B(a: A(id: 2))
+`=destroy`(x)
+`=destroy`(y)
+echo "here"
\ No newline at end of file
diff --git a/tests/destructor/t6434.nim b/tests/destructor/t6434.nim
deleted file mode 100644
index 4e78d0469..000000000
--- a/tests/destructor/t6434.nim
+++ /dev/null
@@ -1,27 +0,0 @@
-discard """
-  exitcode: 0
-  output: ""
-  joinable: false
-"""
-
-type
-  Foo* = object
-    boo: int
-
-var sink_counter = 0
-var assign_counter = 0
-
-proc `=sink`(dest: var Foo, src: Foo) =
-  sink_counter.inc
-
-proc `=`(dest: var Foo, src: Foo) =
-  assign_counter.inc
-
-proc test(): auto =
-  var a,b : Foo
-  return (a, b, Foo(boo: 5))
-
-var (a, b, _) = test()
-
-doAssert assign_counter == 0
-doAssert sink_counter == 12 # + 3 because of the conservative tuple unpacking transformation
diff --git a/tests/destructor/t7346.nim b/tests/destructor/t7346.nim
index 9e5292a61..3834d39ff 100644
--- a/tests/destructor/t7346.nim
+++ b/tests/destructor/t7346.nim
@@ -2,8 +2,7 @@ discard """
 joinable: false
 """
 
-when not defined(nimNewRuntime):
-  {.error: "This bug could only be reproduced with --newruntime".}
+# This bug could only be reproduced with --newruntime
 
 type
   Obj = object
diff --git a/tests/destructor/t9440.nim b/tests/destructor/t9440.nim
new file mode 100644
index 000000000..153bb303d
--- /dev/null
+++ b/tests/destructor/t9440.nim
@@ -0,0 +1,52 @@
+discard """
+  matrix: "--gc:refc; --gc:orc; --gc:arc"
+  output: '''
+()
+Destroyed
+()
+Destroyed
+()
+Destroyed
+end
+-------------------------
+()
+Destroyed
+end
+'''
+
+"""
+
+# bug #9440
+block:
+  type
+    X = object
+
+  proc `=destroy`(x: var X) =
+    echo "Destroyed"
+
+  proc main() =
+    for x in 0 .. 2:
+      var obj = X()
+      echo obj
+    # The destructor call is invoked after "end" is printed
+    echo "end"
+
+  main()
+
+echo "-------------------------"
+
+block:
+  type
+    X = object
+
+  proc `=destroy`(x: var X) =
+    echo "Destroyed"
+
+  proc main() =
+    block:
+      var obj = X()
+      echo obj
+      # The destructor is not called when obj goes out of scope
+    echo "end"
+
+  main()
diff --git a/tests/destructor/tarc.nim b/tests/destructor/tarc.nim
new file mode 100644
index 000000000..54d75a410
--- /dev/null
+++ b/tests/destructor/tarc.nim
@@ -0,0 +1,184 @@
+discard """
+  output: '''
+@[1, 2, 3]
+Success
+@["a", "b", "c"]
+Hello
+1
+2
+0
+List
+@["4", "5", "6", "", "", "a", ""]
+@["", "", "a", ""]
+'''
+  cmd: '''nim c --gc:arc $file'''
+"""
+
+import os
+import math
+import lists
+import strutils
+
+proc mkleak() =
+  # allocate 1 MB via linked lists
+  let numberOfLists = 100
+  for i in countUp(1, numberOfLists):
+    var leakList = initDoublyLinkedList[string]()
+    let numberOfLeaks = 5000
+    for j in countUp(1, numberOfLeaks):
+      leakList.append(newString(200))
+
+proc mkManyLeaks() =
+  for i in 0..0:
+    mkleak()
+  echo "Success"
+
+iterator foobar(c: string): seq[string] {.closure.} =
+  yield @["a", "b", c]
+
+proc tsimpleClosureIterator =
+  var myc = "c"
+  for it in foobar(myc):
+    echo it
+
+type
+  LazyList = ref object
+    c: proc() {.closure.}
+
+proc tlazyList =
+  let dep = @[1, 2, 3]
+  var x = LazyList(c: proc () = echo(dep))
+  x.c()
+
+type
+  Foo = ref object
+
+proc tleakingNewStmt =
+  var x: Foo
+  for i in 0..10:
+    new(x)
+
+iterator infinite(): int {.closure.} =
+  var i = 0
+  while true:
+    yield i
+    inc i
+
+iterator take(it: iterator (): int, numToTake: int): int {.closure.} =
+  var i = 0
+  for x in it():
+    if i >= numToTake:
+      break
+    yield x
+    inc i
+
+proc take3 =
+  for x in infinite.take(3):
+    discard
+
+
+type
+  A = ref object of RootObj
+    x: int
+
+  B = ref object of A
+    more: string
+
+proc inheritanceBug(param: string) =
+  var s: (A, A)
+  s[0] = B(more: "a" & param)
+  s[1] = B(more: "a" & param)
+
+
+type
+  PAsyncHttpServer = ref object
+    value: string
+
+proc serve(server: PAsyncHttpServer) = discard
+
+proc leakObjConstr =
+  serve(PAsyncHttpServer(value: "asdas"))
+
+let startMem = getOccupiedMem()
+take3()
+tlazyList()
+inheritanceBug("whatever")
+mkManyLeaks()
+tsimpleClosureIterator()
+tleakingNewStmt()
+leakObjConstr()
+
+# bug #12964
+
+type
+  Token* = ref object of RootObj
+  Li* = ref object of Token
+
+proc bug12964*() =
+  var token = Li()
+  var tokens = @[Token()]
+  tokens.add token
+
+bug12964()
+
+# bug #13119
+import streams
+
+proc bug13119 =
+  var m = newStringStream("Hello world")
+  let buffer = m.readStr(5)
+  echo buffer
+  m.close
+
+bug13119()
+
+# bug #13105
+
+type
+  Result[T, E] = object
+    a: T
+    b: E
+  D = ref object
+    x: int
+  R = Result[D, int]
+
+proc bug13105 =
+  for n in [R(b: 1), R(b: 2)]:
+    echo n.b
+
+bug13105()
+
+echo getOccupiedMem() - startMem
+
+
+#------------------------------------------------------------------------------
+# issue #14294
+
+import tables
+
+type
+  TagKind = enum
+    List = 0, Compound
+
+  Tag = object
+    case kind: TagKind
+    of List:
+      values: seq[Tag]
+    of Compound:
+      compound: Table[string, Tag]
+
+var a = Tag(kind: List)
+var b = a
+echo a.kind
+var c = a
+
+proc testAdd(i: int; yyy: openArray[string]) =
+  var x: seq[string]
+  x.add [$i, $(i+1), $(i+2)]
+  x.add yyy
+  echo x
+
+var y = newSeq[string](4)
+y[2] = "a"
+testAdd(4, y)
+echo y
diff --git a/tests/destructor/tarc2.nim b/tests/destructor/tarc2.nim
new file mode 100644
index 000000000..a7d7b4945
--- /dev/null
+++ b/tests/destructor/tarc2.nim
@@ -0,0 +1,31 @@
+discard """
+  output: '''leak: false'''
+  cmd: '''nim c --gc:orc $file'''
+"""
+
+type
+  T = ref object
+    s: seq[T]
+    data: string
+
+proc create(): T = T(s: @[], data: "abc")
+
+proc addX(x: T; data: string) =
+  x.data = data
+
+{.push sinkInference: off.}
+
+proc addX(x: T; child: T) =
+  x.s.add child
+
+{.pop.}
+
+proc main(rootName: string) =
+  var root = create()
+  root.data = rootName
+  root.addX root
+
+let mem = getOccupiedMem()
+main("yeah")
+GC_fullCollect()
+echo "leak: ", getOccupiedMem() - mem > 0
diff --git a/tests/destructor/tarc3.nim b/tests/destructor/tarc3.nim
new file mode 100644
index 000000000..55d0ea42d
--- /dev/null
+++ b/tests/destructor/tarc3.nim
@@ -0,0 +1,101 @@
+
+discard """
+  cmd: '''nim c --gc:arc $file'''
+"""
+
+when defined(cpp):
+  {.passC: "-std=gnu++2a".}
+
+type
+  TokenKind* = enum
+    tkColon
+    tkComma
+    tkString
+    tkNumber
+    tkInt64
+    tkIdent
+
+  Token* = object
+    case kind*: TokenKind
+    of tkString: strVal*: string
+    of tkNumber: numVal*: float
+    of tkInt64: int64Val*: int64
+    of tkIdent: ident*: string
+    else: discard
+    pos*: Natural
+
+
+  Token2* = object
+    case kind*: TokenKind
+    of tkString: strVal*: string
+    of tkNumber: numVal*: float
+    of tkInt64, tkColon..tkComma:
+      str1*: array[2, string]
+      float: float
+    else: discard
+    pos*: Natural
+
+  Token3* = object
+    case kind*: TokenKind
+    of tkNumber: numVal*: float
+    of tkInt64, tkComma..tkString: ff: seq[float]
+    else: str1*: string
+  
+  Token4* = object
+    case kind*: TokenKind
+    of tkNumber: numVal*: float
+    of tkInt64, tkComma..tkString: ff: seq[float]
+    else: str1*: string
+    case kind2*: TokenKind
+    of tkNumber: 
+      numVal2*: float
+      intSeqVal3*: seq[int]
+    of tkInt64, tkComma..tkString: 
+      case kind3*: TokenKind
+      of tkNumber: numVal3*: float
+      of tkInt64, tkComma..tkString: 
+        ff3: seq[float]
+        ff5: string
+      else: 
+        str3*: string
+        mysrq: seq[int]
+    else: 
+      case kind4*: TokenKind
+      of tkNumber: numVal4*: float
+      of tkInt64, tkComma..tkString: ff4: seq[float]
+      else: str4*: string
+  
+  BaseLexer* = object of RootObj
+    input*: string
+    pos*: Natural
+
+  Json5Lexer* = object of BaseLexer
+
+  JsonLexer* = object of BaseLexer
+    allowComments*: bool
+    allowSpecialFloats*: bool
+
+  Lexer* = Json5Lexer | JsonLexer
+
+  Parser[T: Lexer] = object
+    l: T
+    tok: Token
+    tok2: Token2
+    tok3: Token3
+    tok4: Token4
+    allowTrailingComma: bool
+    allowIdentifierObjectKey: bool
+
+proc initJson5Lexer*(input: string): Json5Lexer =
+  result.input = input
+
+proc parseJson5*(input: string)  =
+  var p = Parser[Json5Lexer](
+    l: initJson5Lexer(input),
+    allowTrailingComma: true,
+    allowIdentifierObjectKey: true
+  )
+
+
+let x = "string"
+parseJson5(x)
\ No newline at end of file
diff --git a/tests/destructor/tarctypesections.nim b/tests/destructor/tarctypesections.nim
new file mode 100644
index 000000000..da81f1884
--- /dev/null
+++ b/tests/destructor/tarctypesections.nim
@@ -0,0 +1,70 @@
+discard """
+  output: "MEM 0"
+  cmd: "nim c --gc:arc $file"
+"""
+
+type
+  RefNode = ref object
+    le, ri: RefNode
+    name: char
+
+proc edge0(a, b: RefNode) =
+  if a.le == nil: a.le = b
+  else: a.ri = b
+
+proc createNode0(name: char): RefNode =
+  new result
+  result.name = name
+
+proc main0 =
+  let r = createNode0('R')
+  let c = createNode0('C')
+  c.edge0 r
+
+
+type
+  NodeDesc = object
+    le, ri: Node
+    name: char
+  Node = ref NodeDesc
+
+proc edge(a, b: Node) =
+  if a.le == nil: a.le = b
+  else: a.ri = b
+
+proc createNode(name: char): Node =
+  new result
+  result.name = name
+
+proc main =
+  let r = createNode('R')
+  let c = createNode('C')
+  c.edge r
+
+
+type
+  NodeB = ref NodeBo
+  NodeBo = object
+    le, ri: NodeB
+    name: char
+
+proc edge(a, b: NodeB) =
+  if a.le == nil: a.le = b
+  else: a.ri = b
+
+proc createNodeB(name: char): NodeB =
+  new result
+  result.name = name
+
+
+proc mainB =
+  let r = createNodeB('R')
+  let c = createNodeB('C')
+  c.edge r
+
+
+let memB = getOccupiedMem()
+main0()
+main()
+mainB()
+echo "MEM ", getOccupiedMem() - memB
diff --git a/tests/destructor/tarray_indexing.nim b/tests/destructor/tarray_indexing.nim
new file mode 100644
index 000000000..a9dfdf4ed
--- /dev/null
+++ b/tests/destructor/tarray_indexing.nim
@@ -0,0 +1,75 @@
+discard """
+  output: '''allocating 1048576 65536
+filling page from 1048576 len 65536'''
+  cmd: '''nim c --gc:arc $file'''
+"""
+
+# bug #12669
+
+type
+    MemState* = enum
+        memPrivate
+
+    MemPermisison* = enum
+        memperm_Read
+
+    MemInfo* = ref object
+        base*, size*: uint32
+        state*: MemState
+        perm*: set[MemPermisison]
+
+    MemBlock = ref object
+        info: MemInfo
+        data: seq[byte]
+
+    UserProcessMemory* = ref object
+        pageAccess: array[0x40000, ptr UncheckedArray[byte]]
+        pages: array[0x40000, MemInfo]
+        blocks: seq[owned MemBlock]
+
+proc allocMemory*(mem: UserProcessMemory, base, size: uint32) =
+    let
+        roundedBase = base and not(0xFFF'u32)
+        roundedSize = (size + 0xFFF) and not(0xFFF'u32)
+
+    echo "allocating ", base, " ", size
+    for i in (roundedBase shr 12)..<((roundedBase + roundedSize) shr 12):
+        #echo "span ", i
+        doAssert mem.pages[i] == nil
+        # TODO: beserer fehler
+
+    let memBlock = MemBlock(
+        info: MemInfo(
+            base: roundedBase,
+            size: roundedSize,
+            state: memPrivate,
+            perm: {memperm_Read}
+        ),
+        data: newSeq[byte](roundedSize))
+    for i in 0..<(roundedSize shr 12):
+        mem.pages[i + (roundedBase shr 12)] = memBlock.info
+        #echo cast[uint64](addr mem.pageAccess[i + (roundedBase shr 12)])
+        mem.pageAccess[i + (roundedBase shr 12)] = cast[ptr UncheckedArray[byte]](addr memBlock.data[i * 0x1000])
+    mem.blocks.add memBlock
+
+    #for i in (roundedBase shr 12)..<((roundedBase + roundedSize) shr 12):
+    #    assert mem.pageAccess[i] != nil
+
+proc fillPages*(mem: UserProcessMemory, start: uint32, data: seq[byte]) =
+    echo "filling page from ", start, " len ", data.len
+    assert (start and not(0xFFF'u32)) == start
+    assert (uint32(data.len) and not(0xFFF'u32)) == uint32(data.len)
+    for i in (start shr 12)..<((start + uint32(data.len)) shr 12):
+        #echo cast[uint64](addr mem.pageAccess[i])
+        let page = mem.pageAccess[i]
+        assert page != nil
+        #copyMem(page, addr data[i * 0x1000 - start], 0x1000)
+
+const base = 0x00100000
+
+proc a(): owned UserProcessMemory =
+    result = UserProcessMemory()
+    result.allocMemory(base, 0x1000 * 16)
+    result.fillPages(base, newSeq[byte](0x1000 * 16))
+
+discard a()
diff --git a/tests/destructor/tasync_prototype.nim b/tests/destructor/tasync_prototype.nim
new file mode 100644
index 000000000..81fd824e9
--- /dev/null
+++ b/tests/destructor/tasync_prototype.nim
@@ -0,0 +1,59 @@
+discard """
+  output: '''asdas
+processClient end
+false
+MEMORY 0
+'''
+  cmd: '''nim c --gc:arc $file'''
+"""
+
+type
+  PAsyncHttpServer = ref object
+    value: string
+  PFutureBase {.acyclic.} = ref object
+    callback: proc () {.closure.}
+    value: string
+    failed: bool
+
+proc accept(server: PAsyncHttpServer): PFutureBase =
+  new(result)
+  result.callback = proc () =
+    discard
+  server.value = "hahaha"
+
+proc processClient(): PFutureBase =
+  new(result)
+
+proc serve(server: PAsyncHttpServer): PFutureBase =
+  iterator serveIter(): PFutureBase {.closure.} =
+    echo server.value
+    while true:
+      var acceptAddrFut = server.accept()
+      yield acceptAddrFut
+      var fut = acceptAddrFut.value
+
+      # with the new scope based destruction, this cannot
+      # possibly work:
+      var f {.cursor.} = processClient()
+      # It also seems to be the wrong way how to avoid the
+      # cycle. The cycle is caused by capturing the 'env'
+      # part from 'env.f'.
+      when true:
+        f.callback =
+          proc () =
+            echo("processClient end")
+            echo(f.failed)
+      yield f
+  var x = serveIter
+  for i in 0 .. 1:
+    result = x()
+    if result.callback != nil:
+      result.callback()
+
+let mem = getOccupiedMem()
+
+proc main =
+  discard serve(PAsyncHttpServer(value: "asdas"))
+
+main()
+echo "MEMORY ", getOccupiedMem() - mem
diff --git a/tests/destructor/tasync_prototype_cyclic.nim b/tests/destructor/tasync_prototype_cyclic.nim
new file mode 100644
index 000000000..8ba73a8fc
--- /dev/null
+++ b/tests/destructor/tasync_prototype_cyclic.nim
@@ -0,0 +1,55 @@
+discard """
+  output: '''asdas
+processClient end
+false
+MEMORY 0
+'''
+  cmd: '''nim c --gc:orc $file'''
+"""
+
+type
+  PAsyncHttpServer = ref object
+    value: string
+  PFutureBase = ref object
+    callback: proc () {.closure.}
+    value: string
+    failed: bool
+
+proc accept(server: PAsyncHttpServer): PFutureBase =
+  new(result)
+  result.callback = proc () =
+    discard
+  server.value = "hahaha"
+
+proc processClient(): PFutureBase =
+  new(result)
+
+proc serve(server: PAsyncHttpServer): PFutureBase =
+  iterator serveIter(): PFutureBase {.closure.} =
+    echo server.value
+    while true:
+      var acceptAddrFut = server.accept()
+      yield acceptAddrFut
+      var fut = acceptAddrFut.value
+
+      var f = processClient()
+      when true:
+        f.callback =
+          proc () =
+            echo("processClient end")
+            echo(f.failed)
+      yield f
+  var x = serveIter
+  for i in 0 .. 1:
+    result = x()
+    if result.callback != nil:
+      result.callback()
+
+let mem = getOccupiedMem()
+
+proc main =
+  discard serve(PAsyncHttpServer(value: "asdas"))
+
+main()
+GC_fullCollect()
+echo "MEMORY ", getOccupiedMem() - mem
diff --git a/tests/destructor/tatomicptrs.nim b/tests/destructor/tatomicptrs.nim
index d043195c9..82870ac82 100644
--- a/tests/destructor/tatomicptrs.nim
+++ b/tests/destructor/tatomicptrs.nim
@@ -27,7 +27,7 @@ template decRef(x): untyped = atomicDec(x.refcount)
 
 proc makeShared*[T](x: sink T): SharedPtr[T] =
   # XXX could benefit from a macro that generates it.
-  result = cast[SharedPtr[T]](allocShared(sizeof(x)))
+  result = cast[SharedPtr[T]](allocShared0(sizeof(x)))
   result.x[] = x
   echo "allocating"
 
@@ -39,7 +39,7 @@ proc `=destroy`*[T](dest: var SharedPtr[T]) =
     echo "deallocating"
     dest.x = nil
 
-proc `=`*[T](dest: var SharedPtr[T]; src: SharedPtr[T]) =
+proc `=copy`*[T](dest: var SharedPtr[T]; src: SharedPtr[T]) =
   var s = src.x
   if s != nil: incRef(s)
   #atomicSwap(dest, s)
@@ -50,6 +50,9 @@ proc `=`*[T](dest: var SharedPtr[T]; src: SharedPtr[T]) =
     deallocShared(s)
     echo "deallocating"
 
+proc `=dup`*[T](src: SharedPtr[T]): SharedPtr[T] =
+  `=copy`(result, src)
+
 proc `=sink`*[T](dest: var SharedPtr[T]; src: SharedPtr[T]) =
   ## XXX make this an atomic store:
   if dest.x != src.x:
@@ -72,6 +75,7 @@ template `.=`*[T](s: SharedPtr[T]; field, value: untyped) =
 from macros import unpackVarargs
 
 template `.()`*[T](s: SharedPtr[T]; field: untyped, args: varargs[untyped]): untyped =
+  # xxx this isn't used, the test should be improved
   unpackVarargs(s.x.field, args)
 
 
@@ -119,7 +123,7 @@ proc `=destroy`*[T](m: var MySeq[T]) {.inline.} =
     deallocShared(m.data)
     m.data = nil
 
-proc `=`*[T](m: var MySeq[T], m2: MySeq[T]) =
+proc `=copy`*[T](m: var MySeq[T], m2: MySeq[T]) =
   if m.data == m2.data: return
   if m.data != nil:
     `=destroy`(m)
@@ -130,20 +134,43 @@ proc `=`*[T](m: var MySeq[T], m2: MySeq[T]) =
     m.data = cast[ptr UncheckedArray[T]](allocShared(bytes))
     copyMem(m.data, m2.data, bytes)
 
+proc `=dup`*[T](m: MySeq[T]): MySeq[T] =
+  `=copy`[T](result, m)
+
 proc `=sink`*[T](m: var MySeq[T], m2: MySeq[T]) {.inline.} =
   if m.data != m2.data:
     if m.data != nil:
       `=destroy`(m)
     m.len = m2.len
     m.data = m2.data
+    m.refcount = m2.refcount
 
 proc len*[T](m: MySeq[T]): int {.inline.} = m.len
 
 proc newMySeq*[T](size: int, initial_value: T): MySeq[T] =
   result.len = size
+  result.refcount = 1
   if size > 0:
     result.data = cast[ptr UncheckedArray[T]](allocShared(sizeof(T) * size))
 
 
 let x = makeShared(newMySeq(10, 1.0))
 doAssert: x.get().len == 10
+
+
+
+#-------------------------------------------------------
+#bug #12882
+
+type
+  ValueObject = object
+    v: MySeq[int]
+    name: string
+
+  TopObject = object
+    internal: seq[ValueObject]
+
+var zz = new(TopObject)
+
+
+
diff --git a/tests/destructor/tbintree2.nim b/tests/destructor/tbintree2.nim
new file mode 100644
index 000000000..d56c2850b
--- /dev/null
+++ b/tests/destructor/tbintree2.nim
@@ -0,0 +1,101 @@
+discard """
+  cmd: '''nim c -d:nimAllocStats --newruntime $file'''
+  output: '''0
+(allocCount: 5, deallocCount: 5)'''
+"""
+
+import system / ansi_c
+
+import random
+
+type Node = ref object
+  x, y: int32
+  left, right: owned Node
+
+proc newNode(x: int32): owned Node =
+  result = Node(x: x, y: rand(high int32).int32)
+
+proc merge(lower, greater: owned Node): owned Node =
+  if lower.isNil:
+    result = greater
+  elif greater.isNil:
+    result = lower
+  elif lower.y < greater.y:
+    lower.right = merge(move lower.right, greater)
+    result = lower
+  else:
+    greater.left = merge(lower, move greater.left)
+    result = greater
+
+proc splitBinary(orig: owned Node, value: int32): (owned Node, owned Node) =
+  if orig.isNil:
+    result = (nil, nil)
+  elif orig.x < value:
+    let splitPair = splitBinary(move orig.right, value)
+    orig.right = splitPair[0]
+    result = (orig, splitPair[1])
+  else:
+    let splitPair = splitBinary(move orig.left, value)
+    orig.left = splitPair[1]
+    result = (splitPair[0], orig)
+
+proc merge3(lower, equal, greater: owned Node): owned Node =
+  merge(merge(lower, equal), greater)
+
+proc split(orig: owned Node, value: int32): tuple[lower, equal, greater: owned Node] =
+  let
+    (lower, equalGreater) = splitBinary(orig, value)
+    (equal, greater) = splitBinary(equalGreater, value + 1)
+  result = (lower, equal, greater)
+
+type Tree = object
+  root: owned Node
+
+proc `=destroy`(t: var Tree) {.nodestroy.} =
+  var s: seq[owned Node] = @[t.root]
+  while s.len > 0:
+    let x = s.pop
+    if x.left != nil: s.add(x.left)
+    if x.right != nil: s.add(x.right)
+    `=dispose`(x)
+  `=destroy`(s)
+
+proc hasValue(self: var Tree, x: int32): bool =
+  let splited = split(move self.root, x)
+  result = not splited.equal.isNil
+  self.root = merge3(splited.lower, splited.equal, splited.greater)
+
+proc insert(self: var Tree, x: int32) =
+  var splited = split(move self.root, x)
+  if splited.equal.isNil:
+    splited.equal = newNode(x)
+  self.root = merge3(splited.lower, splited.equal, splited.greater)
+
+proc erase(self: var Tree, x: int32) =
+  let splited = split(move self.root, x)
+  self.root = merge(splited.lower, splited.greater)
+
+proc main() =
+  var
+    tree = Tree()
+    cur = 5'i32
+    res = 0
+
+  for i in 1 ..< 10:
+    let a = i mod 3
+    cur = (cur * 57 + 43) mod 10007
+    case a:
+    of 0:
+      tree.insert(cur)
+    of 1:
+      tree.erase(cur)
+    of 2:
+      if tree.hasValue(cur):
+        res += 1
+    else:
+      discard
+  echo res
+
+dumpAllocStats:
+  main()
+
diff --git a/tests/destructor/tcaseobj_transitions.nim b/tests/destructor/tcaseobj_transitions.nim
index 9377d57b0..61464101f 100644
--- a/tests/destructor/tcaseobj_transitions.nim
+++ b/tests/destructor/tcaseobj_transitions.nim
@@ -1,5 +1,5 @@
 discard """
-  cmd: '''nim c --newruntime $file'''
+  cmd: '''nim c --gc:arc $file'''
   output: '''no crash'''
 """
 
@@ -31,3 +31,19 @@ var y = MyCaseObjectB(kind: A)
 y.x = 1
 y.kind = C
 echo "no crash"
+
+
+#################
+## bug #12821
+
+type
+  RefBaseObject* = ref object of RootObj
+    case kind: bool
+      of true: a: int
+      of false: b: float
+
+  MyRefObject = ref object of RefBaseObject
+    x: float
+
+let z = new(MyRefObject)
+z.kind = false
diff --git a/tests/destructor/tcomplexobjconstr.nim b/tests/destructor/tcomplexobjconstr.nim
new file mode 100644
index 000000000..aea0ad1fe
--- /dev/null
+++ b/tests/destructor/tcomplexobjconstr.nim
@@ -0,0 +1,56 @@
+discard """
+  output: '''true
+OK'''
+  cmd: "nim c --gc:arc $file"
+"""
+
+# bug #12826
+
+type
+  MyObject1* = object of RootObj
+    z*: string
+
+  MyObject2* = object of RootObj
+    x*: float
+    name*: string
+    subobj: MyObject1
+    case flag*: bool
+    of false:
+      more: array[3, MyObject1]
+    of true: y*: float
+
+var x = new(MyObject2)
+doAssert x of MyObject2
+doAssert x.subobj of MyObject1
+doAssert x.more[2] of MyObject1
+doAssert x.more[2] of RootObj
+
+var y: MyObject2
+doAssert y of MyObject2
+doAssert y.subobj of MyObject1
+doAssert y.more[2] of MyObject1
+doAssert y.more[2] of RootObj
+
+echo "true"
+
+# bug #12978
+type
+  Vector2* = object of RootObj
+    x*, y*: float
+
+type
+  Vertex* = ref object
+    point*: Vector2
+
+proc newVertex*(p: Vector2): Vertex =
+  return Vertex(point: p)
+
+proc createVertex*(p: Vector2): Vertex =
+  result = newVertex(p)
+
+proc p =
+  var x = Vector2(x: 1, y: 2)
+  let other = createVertex(x)
+  echo "OK"
+
+p()
diff --git a/tests/destructor/tconst_smart_ptr.nim b/tests/destructor/tconst_smart_ptr.nim
new file mode 100644
index 000000000..39fe12612
--- /dev/null
+++ b/tests/destructor/tconst_smart_ptr.nim
@@ -0,0 +1,7 @@
+discard """
+  action: "compile"
+"""
+
+import const_smart_ptr
+
+discard test(0)
diff --git a/tests/destructor/tconsume_twice.nim b/tests/destructor/tconsume_twice.nim
index 8687b3ce5..b0a039e9b 100644
--- a/tests/destructor/tconsume_twice.nim
+++ b/tests/destructor/tconsume_twice.nim
@@ -1,7 +1,7 @@
 discard """
   cmd: "nim c --newruntime $file"
-  errormsg: "sink parameter `a` is already consumed at tconsume_twice.nim(11, 10)"
-  line: 13
+  errormsg: "'=copy' is not available for type <owned Foo>; requires a copy because it's not the last read of 'a'; another read is done here: tconsume_twice.nim(13, 10); routine: consumeTwice"
+  line: 11
 """
 type
   Foo = ref object
diff --git a/tests/destructor/tcustomseqs.nim b/tests/destructor/tcustomseqs.nim
index 4087dc4a8..17a19f871 100644
--- a/tests/destructor/tcustomseqs.nim
+++ b/tests/destructor/tcustomseqs.nim
@@ -121,7 +121,7 @@ proc createSeq*[T](elems: varargs[T]): myseq[T] =
   result.data = cast[type(result.data)](alloc(result.cap * sizeof(T)))
   inc allocCount
   when supportsCopyMem(T):
-    copyMem(result.data, unsafeAddr(elems[0]), result.cap * sizeof(T))
+    copyMem(result.data, addr(elems[0]), result.cap * sizeof(T))
   else:
     for i in 0..<result.len:
       result.data[i] = elems[i]
diff --git a/tests/destructor/tcustomstrings.nim b/tests/destructor/tcustomstrings.nim
index 9ee2da33a..31891856b 100644
--- a/tests/destructor/tcustomstrings.nim
+++ b/tests/destructor/tcustomstrings.nim
@@ -8,8 +8,6 @@ after 20 20'''
 joinable: false
 """
 
-{.this: self.}
-
 type
   mystring = object
     len, cap: int
@@ -51,7 +49,7 @@ proc resize(self: var mystring) =
   if self.cap == 0: self.cap = 8
   else: self.cap = (self.cap * 3) shr 1
   if self.data == nil: inc allocCount
-  self.data = cast[type(data)](realloc(self.data, self.cap + 1))
+  self.data = cast[type(self.data)](realloc(self.data, self.cap + 1))
 
 proc add*(self: var mystring; c: char) =
   if self.len >= self.cap: resize(self)
@@ -60,22 +58,22 @@ proc add*(self: var mystring; c: char) =
   inc self.len
 
 proc ensure(self: var mystring; newLen: int) =
-  if newLen >= cap:
-    cap = max((cap * 3) shr 1, newLen)
-    if cap > 0:
-      if data == nil: inc allocCount
-      data = cast[type(data)](realloc(data, cap + 1))
+  if newLen >= self.cap:
+    self.cap = max((self.cap * 3) shr 1, newLen)
+    if self.cap > 0:
+      if self.data == nil: inc allocCount
+      self.data = cast[type(self.data)](realloc(self.data, self.cap + 1))
 
 proc add*(self: var mystring; y: mystring) =
-  let newLen = len + y.len
+  let newLen = self.len + y.len
   ensure(self, newLen)
-  copyMem(addr data[len], y.data, y.data.len + 1)
-  len = newLen
+  copyMem(addr self.data[self.len], y.data, y.data.len + 1)
+  self.len = newLen
 
 proc create*(lit: string): mystring =
   let newLen = lit.len
   ensure(result, newLen)
-  copyMem(addr result.data[result.len], unsafeAddr lit[0], newLen + 1)
+  copyMem(addr result.data[result.len], addr lit[0], newLen + 1)
   result.len = newLen
 
 proc `&`*(a, b: mystring): mystring =
diff --git a/tests/destructor/tcycle1.nim b/tests/destructor/tcycle1.nim
new file mode 100644
index 000000000..8dc552294
--- /dev/null
+++ b/tests/destructor/tcycle1.nim
@@ -0,0 +1,54 @@
+discard """
+  output: "MEM 0"
+  cmd: "nim c --gc:orc $file"
+"""
+
+type
+  Node = ref object of RootObj
+    le, ri: Node
+    name: char
+
+proc edge(a, b: Node) =
+  if a.le == nil: a.le = b
+  else: a.ri = b
+
+proc createNode(name: char): Node =
+  new result
+  result.name = name
+
+#[
+
++---------+      +------+
+|         |      |      |
+|  A      +----->+      <------+-------------+
++--+------+      |      |      |             |
+   |             |      |      |     C       |
+   |             |  R   |      |             |
++--v------+      |      |      +-------------+
+|         |      |      |        ^
+|   B     <------+      |        |
+|         |      |      +--------+
++---------+      |      |
+                 +------+
+
+]#
+
+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
+
+  c.edge r
+
+
+let mem = getOccupiedMem()
+main()
+GC_fullCollect()
+echo "MEM ", getOccupiedMem() - mem
diff --git a/tests/destructor/tcycle2.nim b/tests/destructor/tcycle2.nim
new file mode 100644
index 000000000..7b03101fe
--- /dev/null
+++ b/tests/destructor/tcycle2.nim
@@ -0,0 +1,36 @@
+discard """
+  output: "MEM 0"
+  cmd: "nim c --gc:orc $file"
+"""
+
+type
+  Node = ref object
+    kids: seq[Node]
+    data: string
+
+proc main(x: int) =
+  var n = Node(kids: @[], data: "3" & $x)
+  let m = n
+  n.kids.add m
+
+type
+  NodeA = ref object
+    s: char
+    a: array[3, NodeA]
+
+proc m: NodeA =
+  result = NodeA(s: 'a')
+  result.a[0] = result
+  result.a[1] = result
+  result.a[2] = result
+
+proc mainA =
+  for i in 0..10:
+    discard m()
+
+let mem = getOccupiedMem()
+main(90)
+mainA()
+GC_fullCollect()
+
+echo "MEM ", getOccupiedMem() - mem
diff --git a/tests/destructor/tcycle3.nim b/tests/destructor/tcycle3.nim
new file mode 100644
index 000000000..8662136e7
--- /dev/null
+++ b/tests/destructor/tcycle3.nim
@@ -0,0 +1,98 @@
+discard """
+  output: '''BEGIN
+END
+END 2
+cpu.nes false
+cpu step nes is nil? - false
+0'''
+  cmd: '''nim c --gc:orc $file'''
+"""
+
+# extracted from thavlak.nim
+
+type
+  BasicBlock = ref object
+    inEdges: seq[BasicBlock]
+    outEdges: seq[BasicBlock]
+    name: int
+
+proc newBasicBlock(name: int): BasicBlock =
+  result = BasicBlock(
+    inEdges: newSeq[BasicBlock](),
+    outEdges: newSeq[BasicBlock](),
+    name: name
+  )
+
+type
+  Cfg = object
+    basicBlockMap: seq[BasicBlock]
+    startNode: BasicBlock
+
+proc newCfg(): Cfg =
+  result = Cfg(
+    basicBlockMap: newSeq[BasicBlock](),
+    startNode: nil)
+
+proc createNode(cfg: var Cfg, name: int): BasicBlock =
+  if name < cfg.basicBlockMap.len:
+    result = cfg.basicBlockMap[name]
+  else:
+    result = newBasicBlock(name)
+    cfg.basicBlockMap.setLen name+1
+    cfg.basicBlockMap[name] = result
+
+proc newBasicBlockEdge(cfg: var Cfg, fromName, toName: int) =
+  echo "BEGIN"
+  let fr = cfg.createNode(fromName)
+  let to = cfg.createNode(toName)
+
+  fr.outEdges.add(to)
+  to.inEdges.add(fr)
+
+proc run(cfg: var Cfg) =
+  cfg.startNode = cfg.createNode(0) # RC = 2
+  newBasicBlockEdge(cfg, 0, 1) #
+  echo "END"
+
+  discard cfg.createNode(1)
+
+proc main =
+  var c = newCfg()
+  c.run
+  echo "END 2"
+
+# bug #14159
+type
+  NES = ref object
+    cpu: CPU
+    apu: APU
+
+  CPU = ref object
+    nes: NES
+
+  APU = object
+    nes: NES
+    cpu: CPU
+
+proc initAPU(nes: sink NES): APU {.nosinks.} =
+  result.nes = nes
+  result.cpu = nes.cpu
+
+proc step(cpu: CPU): int =
+  echo "cpu.nes ", cpu.isNil
+  echo "cpu step nes is nil? - ", cpu.nes.isNil()
+
+proc newNES(): NES =
+  new result
+  result.cpu = CPU(nes: result)
+  result.apu = initAPU(result)
+
+proc bug14159 =
+  var nesConsole = newNES()
+  discard nesConsole.cpu.step()
+
+let mem = getOccupiedMem()
+main()
+bug14159()
+GC_fullCollect()
+echo getOccupiedMem() - mem
diff --git a/tests/destructor/tdangingref_simple.nim b/tests/destructor/tdangingref_simple.nim
new file mode 100644
index 000000000..279581b0f
--- /dev/null
+++ b/tests/destructor/tdangingref_simple.nim
@@ -0,0 +1,32 @@
+discard """
+  output: '''a
+[FATAL] dangling references exist
+'''
+  exitCode: 1
+  cmd: "nim c --newruntime $file"
+"""
+
+# bug #11350
+
+type
+  Node = ref object
+    data: int
+
+proc use(x: Node) = discard
+
+proc main =
+  var x = Node(data: 3) # inferred to be an ``owned ref``
+  var dangling = unown x
+  assert dangling.data == 3
+  #use x
+  #dangling = nil
+  # reassignment causes the memory of what ``x`` points to to be freed:
+  echo "a"
+  x = Node(data: 4)
+  echo "b"
+  # accessing 'dangling' here is invalid as it is nil.
+  # at scope exit the memory of what ``x`` points to is freed
+  if dangling != nil:
+    echo dangling.data
+
+main()
diff --git a/tests/destructor/tdestructor.nim b/tests/destructor/tdestructor.nim
index 780a45288..e081eb251 100644
--- a/tests/destructor/tdestructor.nim
+++ b/tests/destructor/tdestructor.nim
@@ -1,31 +1,27 @@
 discard """
-  output: '''----
+  output: '''----1
 myobj constructed
 myobj destroyed
-----
+----2
 mygeneric1 constructed
 mygeneric1 destroyed
-----
+----3
 mygeneric2 constructed
 mygeneric2 destroyed
 myobj destroyed
-----
+----4
 mygeneric3 constructed
 mygeneric1 destroyed
-----
+----5
 mydistinctObj constructed
 myobj destroyed
 mygeneric2 destroyed
-------------------
-----
-----
-myobj destroyed
-myobj destroyed
-myobj destroyed
-myobj destroyed
+------------------8
 mygeneric1 destroyed
----
+----6
 myobj destroyed
+----7
+---9
 myobj destroyed
 myobj destroyed
 '''
@@ -37,8 +33,10 @@ type
     p: pointer
 
 proc `=destroy`(o: var TMyObj) =
-  if o.p != nil: dealloc o.p
-  echo "myobj destroyed"
+  if o.p != nil:
+    dealloc o.p
+    o.p = nil
+    echo "myobj destroyed"
 
 type
   TMyGeneric1[T] = object
@@ -116,19 +114,19 @@ proc mydistinctObj =
 
   echo "mydistinctObj constructed"
 
-echo "----"
+echo "----1"
 myobj()
 
-echo "----"
+echo "----2"
 mygeneric1()
 
-echo "----"
+echo "----3"
 mygeneric2[int](10)
 
-echo "----"
+echo "----4"
 mygeneric3()
 
-echo "----"
+echo "----5"
 mydistinctObj()
 
 proc caseobj =
@@ -136,16 +134,16 @@ proc caseobj =
     var o1 = TCaseObj(kind: A, x: TMyGeneric1[int](x: 10))
 
   block:
-    echo "----"
+    echo "----6"
     var o2 = TCaseObj(kind: B, y: open())
 
   block:
-    echo "----"
+    echo "----7"
     var o3 = TCaseObj(kind: D, innerKind: B, r: "test",
                       p: TMyGeneric3[int, float, string](x: 10, y: 1.0, z: "test"))
 
 
-echo "------------------"
+echo "------------------8"
 caseobj()
 
 proc caseobj_test_sink: TCaseObj =
@@ -155,5 +153,15 @@ proc caseobj_test_sink: TCaseObj =
   result = TCaseObj(kind: B, y: open())
 
 
-echo "---"
-discard caseobj_test_sink()
\ No newline at end of file
+echo "---9"
+discard caseobj_test_sink()
+
+# issue #14315
+
+type Vector*[T] = object
+  x1: int
+  # x2: T # uncomment will remove error
+
+# proc `=destroy`*(x: var Vector[int]) = discard # this will remove error
+proc `=destroy`*[T](x: var Vector[T]) = discard
+var a: Vector[int] # Error: unresolved generic parameter
diff --git a/tests/destructor/tdestructor3.nim b/tests/destructor/tdestructor3.nim
index a1de284ae..3f5eb2cc1 100644
--- a/tests/destructor/tdestructor3.nim
+++ b/tests/destructor/tdestructor3.nim
@@ -1,11 +1,20 @@
 discard """
-  output: '''assign
+  output: '''
+assign
 destroy
 destroy
 5
 123
 destroy Foo: 123
-destroy Foo: 5'''
+destroy Foo: 5
+(x1: (val: ...))
+destroy
+---------------
+app begin
+(val: ...)
+destroy
+app end
+'''
 joinable: false
 """
 
@@ -14,17 +23,18 @@ joinable: false
 type T = object
 
 proc `=`(lhs: var T, rhs: T) =
-    echo "assign"
+  echo "assign"
 
 proc `=destroy`(v: var T) =
-    echo "destroy"
+  echo "destroy"
 
 proc use(x: T) = discard
 
 proc usedToBeBlock =
-    var v1 : T
-    var v2 : T = v1
-    use v1
+  var v1 = T()
+  var v2: T = v1
+  discard addr(v2) # prevent cursorfication
+  use v1
 
 usedToBeBlock()
 
@@ -49,3 +59,127 @@ proc main =
   test(toFooPtr(123))
 
 main()
+
+# bug #11517
+type
+  UniquePtr*[T] = object
+    val: ptr T
+
+proc `=destroy`*[T](p: var UniquePtr[T]) =
+  mixin `=destroy`
+  echo "destroy"
+  if p.val != nil:
+    `=destroy`(p.val[])
+    dealloc(p.val)
+    p.val = nil
+
+proc `=`*[T](dest: var UniquePtr[T], src: UniquePtr[T]) {.error.}
+
+proc `=sink`*[T](dest: var UniquePtr[T], src: UniquePtr[T]) {.inline.} =
+  if dest.val != src.val:
+    if dest.val != nil:
+      `=destroy`(dest)
+    dest.val = src.val
+
+proc newUniquePtr*[T](val: sink T): UniquePtr[T] =
+  result.val = create(T)
+  result.val[] = val
+
+#-------------------------------------------------------------
+
+type
+  MyObject = object of RootObj
+    x1: UniquePtr[int]
+
+  MyObject2 = object of MyObject
+
+proc newObj2(x:int, y: float): MyObject2 =
+  MyObject2(x1: newUniquePtr(x))
+
+proc test =
+  let obj2 = newObj2(1, 1.0)
+  echo obj2
+
+test()
+
+
+#------------------------------------------------------------
+# Issue #12883
+
+type
+  TopObject = object
+    internal: UniquePtr[int]
+
+proc deleteTop(p: ptr TopObject) =
+  if p != nil:
+    `=destroy`(p[]) # !!! this operation used to leak the integer
+    deallocshared(p)
+
+proc createTop(): ptr TopObject =
+  result = cast[ptr TopObject](allocShared0(sizeof(TopObject)))
+  result.internal = newUniquePtr(1)
+
+proc test2() =
+  let x = createTop()
+  echo $x.internal
+  deleteTop(x)
+
+echo "---------------"
+echo "app begin"
+test2()
+echo "app end"
+
+# bug #14601
+
+when true: # D20200607T202043
+  type Foo2 = object
+    x: int
+    x2: array[10, int]
+
+  type Vec = object
+    vals: seq[Foo2]
+
+  proc `=destroy`*(a: var Foo2) {.inline.} =
+    discard
+
+  proc initFoo2(x: int): Foo2 = Foo2(x: x)
+
+  proc add2(v: var Vec, a: Foo2) = # ditto with `a: sink Foo2`
+    v.vals.add a
+
+  proc add3(v: var Vec, a: Foo2) = # ditto with `a: sink Foo2`
+    v.vals = @[a]
+
+  proc add4(v: var Vec, a: sink Foo2) = # ditto with `a: sink Foo2`
+    v.vals.add a
+
+  proc add5(v: var Vec, a: sink Foo2) = # ditto with `a: sink Foo2`
+    v.vals = @[a]
+
+  proc main2()=
+    var a: Vec
+    var b = Foo2(x: 10)
+    a.add2 b # ok
+    a.vals.add Foo2(x: 10) # ok
+    a.add2 initFoo2(x = 10) # ok
+    a.add2 Foo2(x: 10) # bug
+    a.add3 initFoo2(x = 10) # ok
+    a.add3 Foo2(x: 10) # bug
+    a.add4 initFoo2(x = 10) # ok
+    a.add4 Foo2(x: 10) # bug
+    a.add5 initFoo2(x = 10) # ok
+    a.add5 Foo2(x: 10) # bug
+  main2()
+
+
+
+#------------------------------------------------------------
+# Issue #15825
+
+type
+  Union = string | int | char
+
+proc run(a: sink Union) =
+  discard
+
+run("123")
diff --git a/tests/destructor/tdestructor_too_late.nim b/tests/destructor/tdestructor_too_late.nim
new file mode 100644
index 000000000..76d1dde84
--- /dev/null
+++ b/tests/destructor/tdestructor_too_late.nim
@@ -0,0 +1,14 @@
+discard """
+  errormsg: "cannot bind another '=destroy' to: Obj; previous declaration was constructed here implicitly: tdestructor_too_late.nim(7, 16)"
+"""
+type Obj* = object
+  v*: int
+
+proc something(this: sink Obj) = 
+  discard
+
+proc `=destroy`(this: var Obj) =
+  echo "igotdestroyed"
+  this.v = -1
+
+var test* = Obj(v: 42)
\ No newline at end of file
diff --git a/tests/destructor/tdistinctseq.nim b/tests/destructor/tdistinctseq.nim
new file mode 100644
index 000000000..5a2ac5ead
--- /dev/null
+++ b/tests/destructor/tdistinctseq.nim
@@ -0,0 +1,8 @@
+discard """
+  matrix: "-u:nimPreviewNonVarDestructor;"
+"""
+type DistinctSeq* = distinct seq[int]
+
+# `=destroy`(cast[ptr DistinctSeq](0)[])
+var x = @[].DistinctSeq
+`=destroy`(x)
diff --git a/tests/destructor/tdont_return_unowned_from_owned.nim b/tests/destructor/tdont_return_unowned_from_owned.nim
index 5794dec1d..ffe87cd76 100644
--- a/tests/destructor/tdont_return_unowned_from_owned.nim
+++ b/tests/destructor/tdont_return_unowned_from_owned.nim
@@ -1,21 +1,40 @@
 discard """
   cmd: "nim check --newruntime --hints:off $file"
-  nimout: '''tdont_return_unowned_from_owned.nim(24, 10) Error: cannot return an owned pointer as an unowned pointer; use 'owned(Obj)' as the return type
-tdont_return_unowned_from_owned.nim(27, 10) Error: cannot return an owned pointer as an unowned pointer; use 'owned(Obj)' as the return type
-tdont_return_unowned_from_owned.nim(30, 6) Error: type mismatch: got <Obj>
+  nimout: '''
+tdont_return_unowned_from_owned.nim(26, 13) Error: assignment produces a dangling ref: the unowned ref lives longer than the owned ref
+tdont_return_unowned_from_owned.nim(27, 13) Error: assignment produces a dangling ref: the unowned ref lives longer than the owned ref
+tdont_return_unowned_from_owned.nim(31, 10) Error: cannot return an owned pointer as an unowned pointer; use 'owned(RootRef)' as the return type
+tdont_return_unowned_from_owned.nim(43, 10) Error: cannot return an owned pointer as an unowned pointer; use 'owned(Obj)' as the return type
+tdont_return_unowned_from_owned.nim(46, 10) Error: cannot return an owned pointer as an unowned pointer; use 'owned(Obj)' as the return type
+tdont_return_unowned_from_owned.nim(49, 6) Error: type mismatch: got <Obj>
 but expected one of:
 proc new[T](a: var ref T; finalizer: proc (x: ref T) {.nimcall.})
+  first type mismatch at position: 2
+  missing parameter: finalizer
 2 other mismatching symbols have been suppressed; compile with --showAllMismatches:on to see them
 
 expression: new(result)
-tdont_return_unowned_from_owned.nim(30, 6) Error: illformed AST:
-tdont_return_unowned_from_owned.nim(38, 13) Error: assignment produces a dangling ref: the unowned ref lives longer than the owned ref
-tdont_return_unowned_from_owned.nim(39, 13) Error: assignment produces a dangling ref: the unowned ref lives longer than the owned ref
-tdont_return_unowned_from_owned.nim(43, 10) Error: cannot return an owned pointer as an unowned pointer; use 'owned(RootRef)' as the return type
+tdont_return_unowned_from_owned.nim(49, 6) Error: illformed AST: 
 '''
-  errormsg: "cannot return an owned pointer as an unowned pointer; use 'owned(RootRef)' as the return type"
-  line: 43
+  errormsg: "illformed AST:"
 """
+
+
+
+proc testA(result: var (RootRef, RootRef)) =
+  let r: owned RootRef = RootRef()
+  result[0] = r
+  result[1] = RootRef()
+
+proc testB(): RootRef =
+  let r: owned RootRef = RootRef()
+  result = r
+
+
+
+
+
+## line 30
 # bug #11073
 type
   Obj = ref object
@@ -27,17 +46,11 @@ proc newObjB(): Obj =
   result = Obj()
 
 proc newObjC(): Obj =
-  new(result)
+  new(result) # illFormedAst raises GlobalError,
+              # without pipeline parsing, it needs to placed at the end
+              # in case that it disturbs other errors
 
 let a = newObjA()
 let b = newObjB()
 let c = newObjC()
 
-proc testA(result: var (RootRef, RootRef)) =
-  let r: owned RootRef = RootRef()
-  result[0] = r
-  result[1] = RootRef()
-
-proc testB(): RootRef =
-  let r: owned RootRef = RootRef()
-  result = r
diff --git a/tests/destructor/texceptions.nim b/tests/destructor/texceptions.nim
new file mode 100644
index 000000000..335ca23be
--- /dev/null
+++ b/tests/destructor/texceptions.nim
@@ -0,0 +1,29 @@
+discard """
+  cmd: '''nim c --gc:arc $file'''
+  output: '''0'''
+"""
+
+proc other =
+  raise newException(ValueError, "stuff happening")
+
+proc indirectViaProcCall =
+  var correct = 0
+  for i in 1 .. 20:
+    try:
+      other()
+    except:
+      let x = getCurrentException()
+      correct += ord(x of ValueError)
+  doAssert correct == 20
+
+proc direct =
+  for i in 1 .. 20:
+    try:
+      raise newException(ValueError, "stuff happening")
+    except ValueError:
+      discard
+
+let mem = getOccupiedMem()
+indirectViaProcCall()
+direct()
+echo getOccupiedMem() - mem
diff --git a/tests/destructor/tfinalizer.nim b/tests/destructor/tfinalizer.nim
new file mode 100644
index 000000000..eb2cd09af
--- /dev/null
+++ b/tests/destructor/tfinalizer.nim
@@ -0,0 +1,31 @@
+discard """
+  cmd: "nim c --gc:arc $file"
+  output: '''Foo(field: "Dick Laurent", k: ka, x: 0.0)
+Nobody is dead
+Dick Laurent is dead'''
+"""
+
+type
+  Kind = enum
+    ka, kb
+  Foo = ref object
+    field: string
+    case k: Kind
+    of ka: x: float
+    of kb: discard
+
+#var x = Foo(field: "lovely")
+proc finalizer(x: Foo) =
+  echo x.field, " is dead"
+
+var x: Foo
+new(x, finalizer)
+x.field = "Dick Laurent"
+# reference to a great movie. If you haven't seen it, highly recommended.
+
+echo repr x
+
+# bug #13112: bind the same finalizer multiple times:
+var xx: Foo
+new(xx, finalizer)
+xx.field = "Nobody"
diff --git a/tests/destructor/tgcdestructors.nim b/tests/destructor/tgcdestructors.nim
index bc9f57d20..07a3731a0 100644
--- a/tests/destructor/tgcdestructors.nim
+++ b/tests/destructor/tgcdestructors.nim
@@ -1,15 +1,18 @@
 discard """
-  cmd: '''nim c --newruntime $file'''
+  cmd: '''nim c -d:nimAllocStats --gc:arc $file'''
   output: '''hi
 ho
 ha
 @["arg", "asdfklasdfkl", "asdkfj", "dfasj", "klfjl"]
 @[1, 2, 3]
 @["red", "yellow", "orange", "rtrt1", "pink"]
-30 30'''
+a: @[4, 2, 3]
+0
+30
+true
+(allocCount: 27, deallocCount: 27)'''
 """
 
-import allocators
 include system / ansi_c
 
 proc main =
@@ -169,6 +172,32 @@ proc testWarm =
 
 testWarm()
 
-#echo s
-let (a, d) = allocCounters()
-discard cprintf("%ld %ld\n", a, d)
+proc mutConstSeq() =
+  # bug #11524
+  var a = @[1,2,3]
+  a[0] = 4
+  echo "a: ", a
+
+mutConstSeq()
+
+proc mainSeqOfCap =
+  # bug #11098
+  var s = newSeqOfCap[int](10)
+  echo s.len
+
+  var s2 = newSeqUninitialized[int](30)
+  echo s2.len
+
+mainSeqOfCap()
+
+# bug #11614
+
+let ga = "foo"
+
+proc takeAinArray =
+  let b = [ga]
+
+takeAinArray()
+echo ga == "foo"
+
+echo getAllocStats()
diff --git a/tests/destructor/tgcleak4.nim b/tests/destructor/tgcleak4.nim
new file mode 100644
index 000000000..4299c8841
--- /dev/null
+++ b/tests/destructor/tgcleak4.nim
@@ -0,0 +1,47 @@
+discard """
+  outputsub: "no leak: "
+  cmd: "nim c --gc:arc $file"
+"""
+# bug #12758
+type
+  TExpr {.inheritable.} = object ## abstract base class for an expression
+  PLiteral = ref TLiteral
+  TLiteral = object of TExpr
+    x: int
+    op1: string
+  TPlusExpr = object of TExpr
+    a, b: ref TExpr
+    op2: string
+
+method eval(e: ref TExpr): int {.base.} =
+  # override this base method
+  quit "to override!"
+
+method eval(e: ref TLiteral): int = return e.x
+
+method eval(e: ref TPlusExpr): int =
+  # watch out: relies on dynamic binding
+  return eval(e.a) + eval(e.b)
+
+proc newLit(x: int): ref TLiteral =
+  new(result)
+  result.x = x
+  result.op1 = $getOccupiedMem()
+
+proc newPlus(a, b: ref TExpr): ref TPlusExpr =
+  new(result)
+  result.a = a
+  result.b = b
+  result.op2 = $getOccupiedMem()
+
+const Limit = when compileOption("gc", "markAndSweep") or compileOption("gc", "boehm"): 5*1024*1024 else: 500_000
+
+for i in 0..100_000:
+  var s: array[0..11, ref TExpr]
+  for j in 0..high(s):
+    s[j] = newPlus(newPlus(newLit(j), newLit(2)), newLit(4))
+    if eval(s[j]) != j+6:
+      quit "error: wrong result"
+  if getOccupiedMem() > Limit: quit("still a leak!")
+
+echo "no leak: ", getOccupiedMem()
diff --git a/tests/destructor/tglobaldestructor.nim b/tests/destructor/tglobaldestructor.nim
index 403f670a0..4d002a092 100644
--- a/tests/destructor/tglobaldestructor.nim
+++ b/tests/destructor/tglobaldestructor.nim
@@ -1,5 +1,5 @@
 discard """
-  cmd: '''nim c --newruntime $file'''
+  cmd: '''nim c --gc:arc $file'''
   output: '''(v: 42)
 igotdestroyed'''
 """
diff --git a/tests/destructor/tgotoexc_leak.nim b/tests/destructor/tgotoexc_leak.nim
new file mode 100644
index 000000000..c8a234085
--- /dev/null
+++ b/tests/destructor/tgotoexc_leak.nim
@@ -0,0 +1,19 @@
+discard """
+  output: '''0
+true'''
+  cmd: "nim c --gc:arc $file"
+"""
+
+# bug #22398
+
+for i in 0 ..< 10_000:
+  try:
+    try:
+      raise newException(ValueError, "")
+    except CatchableError:
+      discard
+      raise newException(ValueError, "") # or raise getCurrentException(), just raise works ok
+  except ValueError:
+    discard
+echo getOccupiedMem()
+echo getCurrentException() == nil
diff --git a/tests/destructor/tgotoexceptions.nim b/tests/destructor/tgotoexceptions.nim
new file mode 100755
index 000000000..f76592270
--- /dev/null
+++ b/tests/destructor/tgotoexceptions.nim
@@ -0,0 +1,117 @@
+discard """
+  output: '''
+msg1
+msg2
+finally2
+finally1
+begin
+one iteration!
+caught!
+except1
+finally1
+caught! 2
+BEFORE
+FINALLY
+BEFORE
+EXCEPT
+FINALLY
+RECOVER
+BEFORE
+EXCEPT: IOError: hi
+FINALLY
+'''
+  cmd: "nim c --gc:arc --exceptions:goto $file"
+"""
+
+#bug 7204
+proc nested_finally =
+  try:
+    raise newException(KeyError, "msg1")
+  except KeyError as ex:
+    echo ex.msg
+    try:
+      raise newException(ValueError, "msg2")
+    except:
+      echo getCurrentExceptionMsg()
+    finally:
+      echo "finally2"
+  finally:
+    echo "finally1"
+
+nested_finally()
+
+proc doraise =
+  raise newException(ValueError, "gah")
+
+proc main =
+  while true:
+    try:
+      echo "begin"
+      doraise()
+    finally:
+      echo "one ", "iteration!"
+
+try:
+  main()
+except:
+  echo "caught!"
+
+when true:
+  proc p =
+    try:
+      raise newException(Exception, "Hello")
+    except:
+      echo "except1"
+      raise
+    finally:
+      echo "finally1"
+
+  try:
+    p()
+  except:
+    echo "caught! 2"
+
+
+proc noException =
+  try:
+    echo "BEFORE"
+
+  except:
+    echo "EXCEPT"
+    raise
+
+  finally:
+    echo "FINALLY"
+
+try: noException()
+except: echo "RECOVER"
+
+proc reraise_in_except =
+  try:
+    echo "BEFORE"
+    raise newException(IOError, "")
+
+  except IOError:
+    echo "EXCEPT"
+    raise
+
+  finally:
+    echo "FINALLY"
+
+try: reraise_in_except()
+except: echo "RECOVER"
+
+proc return_in_except =
+  try:
+    echo "BEFORE"
+    raise newException(IOError, "hi")
+
+  except:
+    echo "EXCEPT: ", getCurrentException().name, ": ", getCurrentExceptionMsg()
+    return
+
+  finally:
+    echo "FINALLY"
+
+try: return_in_except()
+except: echo "RECOVER"
diff --git a/tests/destructor/tgotoexceptions2.nim b/tests/destructor/tgotoexceptions2.nim
new file mode 100644
index 000000000..057caf7b7
--- /dev/null
+++ b/tests/destructor/tgotoexceptions2.nim
@@ -0,0 +1,104 @@
+discard """
+  cmd: "nim c --gc:arc --exceptions:goto $file"
+  output: '''
+B1
+B2
+catch
+A1
+1
+B1
+B2
+catch
+A1
+A2
+0
+B1
+B2
+A1
+1
+B1
+B2
+A1
+A2
+3
+A
+B
+C
+'''
+"""
+
+# More thorough test of return-in-finaly
+
+var raiseEx = true
+var returnA = true
+var returnB = false
+
+proc main: int =
+  try: #A
+    try: #B
+      if raiseEx:
+        raise newException(OSError, "")
+      return 3
+    finally: #B
+      echo "B1"
+      if returnB:
+        return 2
+      echo "B2"
+  except OSError: #A
+    echo "catch"
+  finally: #A
+    echo "A1"
+    if returnA:
+      return 1
+    echo "A2"
+
+for x in [true, false]:
+  for y in [true, false]:
+    # echo "raiseEx: " & $x
+    # echo "returnA: " & $y
+    # echo "returnB: " & $z
+    # in the original test returnB was set to true too and
+    # this leads to swallowing the OSError exception. This is
+    # somewhat compatible with Python but it's non-sense, 'finally'
+    # should not be allowed to swallow exceptions. The goto based
+    # implementation does something sane so we don't "correct" its
+    # behavior just to be compatible with v1.
+    raiseEx = x
+    returnA = y
+    echo main()
+
+# Various tests of return nested in double try/except statements
+
+proc test1() =
+
+  defer: echo "A"
+
+  try:
+    raise newException(OSError, "Problem")
+  except OSError:
+    return
+
+test1()
+
+
+proc test2() =
+
+  defer: echo "B"
+
+  try:
+    return
+  except OSError:
+    discard
+
+test2()
+
+proc test3() =
+  try:
+    try:
+      raise newException(OSError, "Problem")
+    except OSError:
+      return
+  finally:
+    echo "C"
+
+test3()
diff --git a/tests/destructor/tgotoexceptions3.nim b/tests/destructor/tgotoexceptions3.nim
new file mode 100644
index 000000000..308d288b2
--- /dev/null
+++ b/tests/destructor/tgotoexceptions3.nim
@@ -0,0 +1,7 @@
+discard """
+  cmd: "nim c --gc:arc --exceptions:goto $file"
+  outputsub: "Error: unhandled exception: Problem [OSError]"
+  exitcode: "1"
+"""
+
+raise newException(OSError, "Problem")
diff --git a/tests/destructor/tgotoexceptions4.nim b/tests/destructor/tgotoexceptions4.nim
new file mode 100644
index 000000000..b2b481256
--- /dev/null
+++ b/tests/destructor/tgotoexceptions4.nim
@@ -0,0 +1,60 @@
+discard """
+  cmd: "nim c --gc:arc --exceptions:goto $file"
+  output: '''caught in gun
+caught in fun
+caughtsome msgMyExcept
+in finally
+caught1
+123
+123'''
+"""
+
+when true:
+  # bug #13070
+  type MyExcept = object of CatchableError
+  proc gun() =
+    try:
+      raise newException(MyExcept, "some msg")
+    except Exception as eab:
+      echo "caught in gun"
+      raise eab
+
+  proc fun() =
+    try:
+      gun()
+    except Exception as e:
+      echo "caught in fun"
+      echo("caught", e.msg, e.name)
+    finally:
+      echo "in finally"
+  fun()
+
+when true:
+  # bug #13072
+  type MyExceptB = object of CatchableError
+  proc gunB() =
+    raise newException(MyExceptB, "some msg")
+  proc funB() =
+    try:
+      gunB()
+    except CatchableError:
+      echo "caught1"
+  funB()
+
+# bug #13782
+
+import strutils
+var n = 123
+
+try: n = parseInt("xxx")
+except: discard
+
+echo n
+
+proc sameTestButForLocalVar =
+  var n = 123
+  try: n = parseInt("xxx")
+  except: discard
+  echo n
+
+sameTestButForLocalVar()
diff --git a/tests/destructor/tgotoexceptions5.nim b/tests/destructor/tgotoexceptions5.nim
new file mode 100644
index 000000000..695aab0a4
--- /dev/null
+++ b/tests/destructor/tgotoexceptions5.nim
@@ -0,0 +1,45 @@
+discard """
+  output: '''
+before
+swallowed
+before
+swallowed B
+'''
+  cmd: "nim c --gc:arc --exceptions:goto -d:ssl $file"
+"""
+
+# bug #13599
+proc main() =
+  try:
+    echo "before"
+    raise newException(CatchableError, "foo")
+  except AssertionDefect:
+    echo "caught"
+  echo "after"
+
+try:
+  main()
+except:
+  echo "swallowed"
+
+proc mainB() =
+  try:
+    echo "before"
+    raise newException(CatchableError, "foo")
+  # except CatchableError: # would work
+  except AssertionDefect:
+    echo "caught"
+  except:
+    raise
+  echo "after"
+
+try:
+  mainB()
+except:
+  echo "swallowed B"
+
+# bug #14647
+import httpclient
+
+newAsyncHttpClient().close()
+
diff --git a/tests/destructor/tgotoexceptions6.nim b/tests/destructor/tgotoexceptions6.nim
new file mode 100644
index 000000000..7c01f6a52
--- /dev/null
+++ b/tests/destructor/tgotoexceptions6.nim
@@ -0,0 +1,10 @@
+discard """
+  cmd: "nim c --gc:arc --exceptions:goto $file"
+  outputsub: "Error: unhandled exception: virus detected [ValueError]"
+  exitcode: "1"
+"""
+
+# bug #13436
+proc foo =
+  raise newException(ValueError, "virus detected")
+foo()
diff --git a/tests/destructor/tgotoexceptions7.nim b/tests/destructor/tgotoexceptions7.nim
new file mode 100644
index 000000000..c04bd6ba0
--- /dev/null
+++ b/tests/destructor/tgotoexceptions7.nim
@@ -0,0 +1,49 @@
+discard """
+  cmd: "nim c --gc:arc --exceptions:goto --panics:off $file"
+  output: '''prevented!
+caught
+AssertionDefect
+900'''
+"""
+
+type
+  E = enum
+    kindA, kindB
+  Obj = object
+    case kind: E
+    of kindA: s: string
+    of kindB: i: int
+
+  ObjA = ref object of RootObj
+  ObjB = ref object of ObjA
+
+proc takeRange(x: range[0..4]) = discard
+
+proc bplease(x: ObjB) = discard
+
+proc helper = doAssert(false)
+
+proc main(i: int) =
+  var obj = Obj(kind: kindA, s: "abc")
+  {.cast(uncheckedAssign).}:
+    obj.kind = kindB
+  obj.i = 2
+  try:
+    var objA = ObjA()
+    bplease(ObjB(objA))
+  except ObjectConversionDefect:
+    echo "prevented!"
+
+  try:
+    takeRange(i)
+  except RangeDefect:
+    echo "caught"
+
+  try:
+    helper()
+  except AssertionDefect:
+    echo "AssertionDefect"
+
+  echo i * i
+
+main(30)
diff --git a/tests/destructor/tgotoexceptions8.nim b/tests/destructor/tgotoexceptions8.nim
new file mode 100644
index 000000000..8ed2ed0ba
--- /dev/null
+++ b/tests/destructor/tgotoexceptions8.nim
@@ -0,0 +1,76 @@
+discard """
+  output: '''A
+B
+X
+inner finally
+Y
+outer finally
+msg1
+msg2
+finally2
+finally1
+true'''
+  cmd: "nim c --gc:arc $file"
+"""
+
+# bug #13668
+
+proc main =
+  try:
+    try:
+      raise newException(IOError, "IOError")
+
+    except:
+      echo "A"
+      raise newException(CatchableError, "CatchableError")
+
+  except:
+    echo "B"
+    #discard
+
+proc mainB =
+  try:
+    try:
+      raise newException(IOError, "IOError")
+
+    except:
+      echo "X"
+      raise newException(CatchableError, "CatchableError")
+    finally:
+      echo "inner finally"
+
+  except:
+    echo "Y"
+    #discard
+  finally:
+    echo "outer finally"
+
+main()
+mainB()
+
+when true:
+  #bug 7204
+  proc nested_finally =
+    try:
+      raise newException(KeyError, "msg1")
+    except KeyError as ex:
+      echo ex.msg
+      try:
+        # pop exception
+        raise newException(ValueError, "msg2") # push: exception stack (1 entry)
+      except:
+        echo getCurrentExceptionMsg()
+        # pop exception (except)
+      finally:
+        echo "finally2"
+      # pop exception (except KeyError as ex)
+    finally:
+      echo "finally1"
+
+  nested_finally()
+
+# bug #14925
+proc test(b: bool) =
+  echo b
+
+test(try: true except: false)
diff --git a/tests/destructor/tmatrix.nim b/tests/destructor/tmatrix.nim
index a3bd59df3..2fd5af789 100644
--- a/tests/destructor/tmatrix.nim
+++ b/tests/destructor/tmatrix.nim
@@ -31,7 +31,7 @@ proc `=sink`*(a: var Matrix; b: Matrix) =
   a.m = b.m
   a.n = b.n
 
-proc `=`*(a: var Matrix; b: Matrix) =
+proc `=copy`*(a: var Matrix; b: Matrix) =
   if a.data != nil and a.data != b.data:
     dealloc(a.data)
     deallocCount.inc
@@ -43,6 +43,9 @@ proc `=`*(a: var Matrix; b: Matrix) =
     allocCount.inc
     copyMem(a.data, b.data, b.m * b.n * sizeof(float))
 
+proc `=dup`*(a: Matrix): Matrix =
+  `=copy`(result, a)
+
 proc matrix*(m, n: int, s: float): Matrix =
   ## Construct an m-by-n constant matrix.
   result.m = m
@@ -96,14 +99,16 @@ proc info =
   allocCount = 0
   deallocCount = 0
 
+proc copy(a: Matrix): Matrix = a
+
 proc test1 =
   var a = matrix(5, 5, 1.0)
-  var b = a
+  var b = copy a
   var c = a + b
 
 proc test2 =
   var a = matrix(5, 5, 1.0)
-  var b = a
+  var b = copy a
   var c = -a
 
 proc test3 =
diff --git a/tests/destructor/tmisc_destructors.nim b/tests/destructor/tmisc_destructors.nim
new file mode 100644
index 000000000..082cb0f78
--- /dev/null
+++ b/tests/destructor/tmisc_destructors.nim
@@ -0,0 +1,43 @@
+discard """
+  output: '''@[0]
+@[1]
+@[2]
+@[3]'''
+  joinable: false
+"""
+
+# bug #6434
+
+type
+  Foo* = object
+    boo: int
+
+var sink_counter = 0
+var assign_counter = 0
+
+proc `=sink`(dest: var Foo, src: Foo) =
+  sink_counter.inc
+
+proc `=`(dest: var Foo, src: Foo) =
+  assign_counter.inc
+
+proc createFoo(): Foo = Foo(boo: 0)
+
+proc test(): auto =
+  var a, b = createFoo()
+  return (a, b, Foo(boo: 5))
+
+var (ag, bg, _) = test()
+
+doAssert assign_counter == 0
+doAssert sink_counter == 0
+
+# bug #11510
+proc main =
+  for i in 0 ..< 4:
+    var buffer: seq[int] # = @[] # uncomment to make it work
+    # var buffer: string # also this is broken
+    buffer.add i
+    echo buffer
+
+main()
diff --git a/tests/destructor/tmove.nim b/tests/destructor/tmove.nim
new file mode 100644
index 000000000..2762aff90
--- /dev/null
+++ b/tests/destructor/tmove.nim
@@ -0,0 +1,18 @@
+discard """
+  targets: "c cpp"
+"""
+
+block:
+  var called = 0
+
+  proc bar(a: var int): var int =
+    inc called
+    result = a
+
+  proc foo =
+    var a = 2
+    var s = move bar(a)
+    doAssert called == 1
+    doAssert s == 2
+
+  foo()
diff --git a/tests/destructor/tmove_objconstr.nim b/tests/destructor/tmove_objconstr.nim
index bfc819ceb..cdc1eb1c0 100644
--- a/tests/destructor/tmove_objconstr.nim
+++ b/tests/destructor/tmove_objconstr.nim
@@ -8,7 +8,7 @@ test destroyed 0
 4
 Pony is dying!'''
 joinable: false
-target: "C"
+targets: "c"
 """
 
 # bug #4214
@@ -50,7 +50,7 @@ proc `=destroy`(o: var Pony) =
   echo "Pony is dying!"
 
 proc getPony: Pony =
-  result.name = "Sparkles"
+  result = Pony(name: "Sparkles")
 
 iterator items(p: Pony): int =
   for i in 1..4:
@@ -58,9 +58,9 @@ iterator items(p: Pony): int =
 
 for x in getPony():
   echo x
-# XXX this needs to be enabled once top level statements
-# produce destructor calls again.
-#echo "Pony is dying!"
+
+
+
 
 
 #------------------------------------------------------------
@@ -112,7 +112,7 @@ proc myfunc2(x, y: int): tuple[a: MySeqNonCopyable, b:int, c:MySeqNonCopyable] =
   var cc = newMySeq(y, 5.0)
   (a: case x:
     of 1:
-      let (z1, z2) = myfunc(x,y)
+      let (z1, z2) = myfunc(x, y)
       z2
     elif x > 5: raise newException(ValueError, "new error")
     else: newMySeq(x, 1.0),
@@ -137,28 +137,29 @@ doAssert seq3[0] == 1.0
 var seq4, seq5: MySeqNonCopyable
 (seq4, i, seq5) = myfunc2(2, 3)
 
-seq4 = block:
-  var tmp = newMySeq(4, 1.0)
-  tmp[0] = 3.0
-  tmp
+proc foo =
+  seq4 = block:
+    var tmp = newMySeq(4, 1.0)
+    tmp[0] = 3.0
+    tmp
 
-doAssert seq4[0] == 3.0
+  doAssert seq4[0] == 3.0
 
-import macros
 
-seq4 =
-  if i > 0: newMySeq(2, 5.0)
-  elif i < -100: raise newException(ValueError, "Parse Error")
-  else: newMySeq(2, 3.0)
+  seq4 =
+    if i > 0: newMySeq(2, 5.0)
+    elif i < -100: raise newException(ValueError, "Parse Error")
+    else: newMySeq(2, 3.0)
 
-seq4 =
-  case (char) i:
-    of 'A', {'W'..'Z'}: newMySeq(2, 5.0)
-    of 'B': quit(-1)
-    else:
-      let (x1, x2, x3) = myfunc2(2, 3)
-      x3
+  seq4 =
+    case (char) i:
+      of 'A', {'W'..'Z'}: newMySeq(2, 5.0)
+      of 'B': quit(-1)
+      else:
+        let (x1, x2, x3) = myfunc2(2, 3)
+        x3
 
+foo()
 
 #------------------------------------------------------------
 #-- Move into array constructor
@@ -175,4 +176,19 @@ proc myfuncLoop(x: int): MySeqNonCopyable =
     var cc = newMySeq(i, 5.0)
     result = cc
 
-discard myfuncLoop(3)
\ No newline at end of file
+discard myfuncLoop(3)
+
+#------------------------------------------------------------
+# Move into table via openArray
+#------------------------------------------------------------
+
+type
+  TableNonCopyable = object
+    x: seq[(string, MySeqNonCopyable)]
+
+proc toTable(pairs: sink openArray[(string, MySeqNonCopyable)]): TableNonCopyable =
+  discard
+
+
+let mytable = {"a": newMySeq(2, 5.0)}.toTable
+
diff --git a/tests/destructor/tnewruntime_misc.nim b/tests/destructor/tnewruntime_misc.nim
new file mode 100644
index 000000000..21c70557d
--- /dev/null
+++ b/tests/destructor/tnewruntime_misc.nim
@@ -0,0 +1,155 @@
+discard """
+  cmd: '''nim cpp -d:nimAllocStats --newruntime --threads:on $file'''
+  output: '''(field: "value")
+Indeed
+axc
+(v: 10)
+...
+destroying GenericObj[T] GenericObj[system.int]
+test
+(allocCount: 12, deallocCount: 10)
+3'''
+"""
+
+import system / ansi_c
+
+import tables
+
+type
+  Node = ref object
+    field: string
+
+# bug #11807
+import os
+putEnv("HEAPTRASHING", "Indeed")
+
+let s1 = getAllocStats()
+
+
+proc newTableOwned[A, B](initialSize = defaultInitialSize): owned(TableRef[A, B]) =
+  new(result)
+  result[] = initTable[A, B](initialSize)
+
+proc main =
+  var w = newTableOwned[string, owned Node]()
+  w["key"] = Node(field: "value")
+  echo w["key"][]
+  echo getEnv("HEAPTRASHING")
+
+  # bug #11891
+  var x = "abc"
+  x[1] = 'x'
+  echo x
+
+main()
+
+# bug #11745
+
+type
+  Foo = object
+    bar: seq[int]
+
+var x = [Foo()]
+
+# bug #11563
+type
+  MyTypeType = enum
+    Zero, One
+  MyType = object
+    case kind: MyTypeType
+    of Zero:
+      s*: seq[MyType]
+    of One:
+      x*: int
+var t: MyType
+
+# bug #11254
+proc test(p: owned proc()) =
+  let x = (proc())p
+
+test(proc() = discard)
+
+# bug #10689
+
+type
+  O = object
+    v: int
+
+proc `=sink`(d: var O, s: O) =
+  d.v = s.v
+
+proc selfAssign =
+  var o = O(v: 10)
+  o = o
+  echo o
+
+selfAssign()
+
+# bug #11833
+type FooAt = object
+
+proc testWrongAt() =
+  var x = @[@[FooAt()]]
+
+testWrongAt()
+
+#-------------------------------------------------
+type
+  Table[A, B] = object
+    x: seq[(A, B)]
+
+
+proc toTable[A,B](p: sink openArray[(A, B)]): Table[A, B] =
+  for zz in mitems(p):
+    result.x.add move(zz)
+
+
+let table = {"a": new(int)}.toTable()
+
+# bug # #12051
+
+type
+  GenericObj[T] = object
+    val: T
+  Generic[T] = owned ref GenericObj[T]
+
+proc `=destroy`[T](x: var GenericObj[T]) =
+  echo "destroying GenericObj[T] ", x.typeof # to know when its being destroyed
+
+proc main12() =
+  let gnrc = Generic[int](val: 42)
+  echo "..."
+
+main12()
+
+#####################################################################
+## bug #12827
+type
+  MyObject = object
+    x: string
+    y: seq[string]
+    needs_ref: ref int
+
+proc xx(xml: string): MyObject =
+  let stream = xml
+  result.x  = xml
+  defer: echo stream
+
+
+discard xx("test")
+
+# Windows has 1 extra allocation in `getEnv` - there it allocates parameter to
+# `_wgetenv` (WideCString). Therefore subtract by 1 to match other OSes'
+# allocation.
+when defined(windows):
+  import std/importutils
+  privateAccess(AllocStats)
+  echo getAllocStats() - s1 - AllocStats(allocCount: 1, deallocCount: 1)
+else:
+  echo getAllocStats() - s1
+
+# bug #13457
+var s = "abcde"
+s.setLen(3)
+
+echo s.cstring.len
diff --git a/tests/destructor/tnewruntime_strutils.nim b/tests/destructor/tnewruntime_strutils.nim
index 5b8684354..9c8d41973 100644
--- a/tests/destructor/tnewruntime_strutils.nim
+++ b/tests/destructor/tnewruntime_strutils.nim
@@ -1,22 +1,47 @@
 discard """
-  cmd: '''nim c --newruntime $file'''
-  output: '''442 442'''
+  valgrind: true
+  cmd: '''nim c -d:nimAllocStats --gc:arc -d:useMalloc $file'''
+  output: '''
+@[(input: @["KXSC", "BGMC"]), (input: @["PXFX"]), (input: @["WXRQ", "ZSCZD"])]
+14
+First tasks completed.
+Second tasks completed.
+test1'''
 """
 
-import strutils, os
+import strutils, os, std / wordwrap
 
-import core / allocators
 import system / ansi_c
 
 # bug #11004
 proc retTuple(): (seq[int], int) =
-  # XXX this doesn't allocate yet but probably it should
   return (@[1], 1)
 
+# bug #12899
+
+import sequtils, strmisc
+
+const input = ["KXSC, BGMC => 7 PTHL", "PXFX => LBZJ", "WXRQ, ZSCZD => HLQM"]
+
+type
+  Reaction = object
+    input: seq[string]
+
+proc bug12899 =
+  var reactions: seq[Reaction] = @[]
+  for l in input:
+    let x = l.partition(" => ")
+    reactions.add Reaction(input: @(x[0].split(", ")))
+
+  let x = $reactions
+  echo x
+
+bug12899()
+
+
 proc nonStaticTests =
   doAssert formatBiggestFloat(1234.567, ffDecimal, -1) == "1234.567000"
-  when not defined(js):
-    doAssert formatBiggestFloat(1234.567, ffDecimal, 0) == "1235."           # <=== bug 8242
+  doAssert formatBiggestFloat(1234.567, ffDecimal, 0) == "1235." # bugs 8242, 12586
   doAssert formatBiggestFloat(1234.567, ffDecimal, 1) == "1234.6"
   doAssert formatBiggestFloat(0.00000000001, ffDecimal, 11) == "0.00000000001"
   doAssert formatBiggestFloat(0.00000000001, ffScientific, 1, ',') in
@@ -78,12 +103,12 @@ proc staticTests =
     inp = """ this is a long text --  muchlongerthan10chars and here
                 it goes"""
     outp = " this is a\nlong text\n--\nmuchlongerthan10chars\nand here\nit goes"
-  doAssert wordWrap(inp, 10, false) == outp
+  doAssert wrapWords(inp, 10, false) == outp
 
   let
     longInp = """ThisIsOneVeryLongStringWhichWeWillSplitIntoEightSeparatePartsNow"""
     longOutp = "ThisIsOn\neVeryLon\ngStringW\nhichWeWi\nllSplitI\nntoEight\nSeparate\nPartsNow"
-  doAssert wordWrap(longInp, 8, true) == longOutp
+  doAssert wrapWords(longInp, 8, true) == longOutp
 
   doAssert "$animal eats $food." % ["animal", "The cat", "food", "fish"] ==
             "The cat eats fish."
@@ -187,5 +212,43 @@ proc staticTests =
 nonStaticTests()
 staticTests()
 
-let (a, d) = allocCounters()
-discard cprintf("%ld %ld\n", a, d)
+# bug #12965
+let xaa = @[""].join()
+let xbb = @["", ""].join()
+
+# bug #16365
+
+# Task 1:
+when true:
+  # Task 1_a:
+  var test_string_a = "name_something"
+  echo test_string_a.len()
+  let new_len_a = test_string_a.len - "_something".len()
+  test_string_a.setLen new_len_a
+
+  echo "First tasks completed."
+
+# Task 2:
+when true:
+  # Task 2_a
+  var test_string: string
+  let some_string = "something"
+  for i in some_string.items:
+    test_string.add $i
+
+  # Task 2_b
+  var test_string_b = "name_something"
+  let new_len_b = test_string_b.len - "_something".len()
+  test_string_b.setLen new_len_b
+
+  echo "Second tasks completed."
+
+# bug #17450
+proc main =
+  var i = 1
+  echo:
+    block:
+      "test" & $i
+
+main()
+
diff --git a/tests/destructor/tnonvardestructor.nim b/tests/destructor/tnonvardestructor.nim
new file mode 100644
index 000000000..1b4413790
--- /dev/null
+++ b/tests/destructor/tnonvardestructor.nim
@@ -0,0 +1,247 @@
+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: 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: 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()

+

+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: NodeObj) =

+  `=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))

+

+block:

+  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()

+

+block:

+  type RefObj = ref object

+

+  proc `[]`(val: static[int]) = # works with different name/overload or without static arg

+    discard

+

+  template noRef(T: typedesc): typedesc = # works without template indirection

+    typeof(default(T)[])

+

+  proc `=destroy`(x: noRef(RefObj)) =

+    discard

+

+  proc foo =

+    var x = new RefObj

+    doAssert $(x[]) == "()"

+

+  # bug #11705

+  foo()

+

+block: # bug #22197

+  type

+    H5IdObj = object

+    H5Id = ref H5IdObj

+

+    FileID = distinct H5Id

+

+    H5GroupObj = object

+      file_id: FileID

+    H5Group = ref H5GroupObj

+

+  ## This would make it work!

+  #proc `=destroy`*(x: FileID) = `=destroy`(cast[H5Id](x))

+  ## If this does not exist, it also works!

+  proc newFileID(): FileID = FileID(H5Id())

+

+  proc `=destroy`(grp: H5GroupObj) =

+    ## Closes the group and resets all references to nil.

+    if cast[pointer](grp.fileId) != nil:

+      `=destroy`(grp.file_id)

+

+  var grp = H5Group()

+  reset(grp.file_id)

+  reset(grp)

+

+import std/tables

+

+block: # bug #22286

+  type

+    A = object

+    B = object

+      a: A

+    C = object

+      b: B

+

+  proc `=destroy`(self: A) =

+    echo "destroyed"

+

+  proc `=destroy`(self: C) =

+    `=destroy`(self.b)

+

+  var c = C()

+

+block: # https://forum.nim-lang.org/t/10642

+  type AObj = object

+    name: string

+    tableField: Table[string, string]

+

+  proc `=destroy`(x: AObj) =

+    `=destroy`(x.name)

+    `=destroy`(x.tableField)

diff --git a/tests/destructor/topttree.nim b/tests/destructor/topttree.nim
index fa5495689..8cf757e8b 100644
--- a/tests/destructor/topttree.nim
+++ b/tests/destructor/topttree.nim
@@ -1,4 +1,5 @@
 discard """
+  disabled: i386
   output: '''10.0
 60.0
 90.0
diff --git a/tests/destructor/towned_binary_tree.nim b/tests/destructor/towned_binary_tree.nim
index 372b1d3d8..fb635e7c6 100644
--- a/tests/destructor/towned_binary_tree.nim
+++ b/tests/destructor/towned_binary_tree.nim
@@ -1,7 +1,7 @@
 discard """
-  cmd: '''nim c --newruntime $file'''
-  output: '''331665
-allocs 0'''
+  cmd: '''nim c -d:nimAllocStats --gc:arc $file'''
+  output: '''31665
+(allocCount: 33334, deallocCount: 33334)'''
 """
 
 #  bug #11053
@@ -72,7 +72,7 @@ proc main() =
     cur = 5'i32
     res = 0
 
-  for i in 1 ..< 1000000:
+  for i in 1 ..< 100000:
     let a = i mod 3
     cur = (cur * 57 + 43) mod 10007
     case a:
@@ -87,6 +87,5 @@ proc main() =
       discard
   echo res
 
-when isMainModule:
+dumpAllocStats:
   main()
-  echo "allocs ", allocs
diff --git a/tests/destructor/tprevent_assign.nim b/tests/destructor/tprevent_assign.nim
index 108ccc371..4c484ebc1 100644
--- a/tests/destructor/tprevent_assign.nim
+++ b/tests/destructor/tprevent_assign.nim
@@ -1,5 +1,5 @@
 discard """
-  errormsg: "'=' is not available for type <Foo>; requires a copy because it's not the last read of 'otherTree'"
+  errormsg: "'=copy' is not available for type <Foo>; requires a copy because it's not the last read of 'otherTree'"
   line: 29
 """
 
diff --git a/tests/destructor/tprevent_assign2.nim b/tests/destructor/tprevent_assign2.nim
index 0e4481710..eb5588b1a 100644
--- a/tests/destructor/tprevent_assign2.nim
+++ b/tests/destructor/tprevent_assign2.nim
@@ -1,7 +1,7 @@
 discard """
-  errormsg: "'=' is not available for type <Foo>; requires a copy because it's not the last read of 'otherTree'"
+  errormsg: "'=dup' is not available for type <Foo>, which is inferred from unavailable '=copy'; requires a copy because it's not the last read of 'otherTree'; another read is done here: tprevent_assign2.nim(51, 31); routine: preventThis"
   file: "tprevent_assign2.nim"
-  line: 48
+  line: 49
 """
 
 type
@@ -9,7 +9,8 @@ type
     x: int
 
 proc `=destroy`(f: var Foo) = f.x = 0
-proc `=`(a: var Foo; b: Foo) {.error.} # = a.x = b.x
+proc `=copy`(a: var Foo; b: Foo) {.error.} # = a.x = b.x
+
 proc `=sink`(a: var Foo; b: Foo) = a.x = b.x
 
 proc createTree(x: int): Foo =
@@ -18,7 +19,7 @@ proc createTree(x: int): Foo =
 proc take2(a, b: sink Foo) =
   echo a.x, " ", b.x
 
-proc allowThis() =
+when false:
   var otherTree: Foo
   try:
     for i in 0..3:
@@ -51,5 +52,5 @@ proc preventThis() =
       else:
         discard
 
-allowThis()
+#allowThis()
 preventThis()
diff --git a/tests/destructor/tprevent_assign3.nim b/tests/destructor/tprevent_assign3.nim
index a8a35ea5e..aa834a66c 100644
--- a/tests/destructor/tprevent_assign3.nim
+++ b/tests/destructor/tprevent_assign3.nim
@@ -1,7 +1,7 @@
 discard """
-  errormsg: "'=' is not available for type <Foo>; requires a copy because it's not the last read of 'otherTree'"
+  errormsg: "'=dup' is not available for type <Foo>; requires a copy because it's not the last read of 'otherTree'"
   file: "tprevent_assign3.nim"
-  line: 46
+  line: 47
 """
 
 type
@@ -9,7 +9,8 @@ type
     x: int
 
 proc `=destroy`(f: var Foo) = f.x = 0
-proc `=`(a: var Foo; b: Foo) {.error.} # = a.x = b.x
+proc `=copy`(a: var Foo; b: Foo) {.error.} # = a.x = b.x
+proc `=dup`(a: Foo): Foo {.error.}
 proc `=sink`(a: var Foo; b: Foo) = a.x = b.x
 
 proc createTree(x: int): Foo =
@@ -18,7 +19,7 @@ proc createTree(x: int): Foo =
 proc take2(a, b: sink Foo) =
   echo a.x, " ", b.x
 
-proc allowThis() =
+when false:
   var otherTree: Foo
   try:
     for i in 0..3:
@@ -47,7 +48,7 @@ proc preventThis2() =
   finally:
     echo otherTree
 
-allowThis()
+#allowThis()
 preventThis2()
 
 
diff --git a/tests/destructor/trecursive.nim b/tests/destructor/trecursive.nim
index 55e67f52a..e7afa6ba9 100644
--- a/tests/destructor/trecursive.nim
+++ b/tests/destructor/trecursive.nim
@@ -32,3 +32,29 @@ proc test1() =
   echo "test1 OK"
 
 test1()
+
+#------------------------------------------------------------------------------
+# issue #14217
+
+type
+  MyObject = object
+    p: ptr int
+
+proc `=destroy`(x: var MyObject) =
+  if x.p != nil:
+    deallocShared(x.p)
+
+proc `=`(x: var MyObject, y: MyObject) {.error.}
+
+proc newMyObject(i: int): MyObject = 
+  result.p = createShared(int)
+  result.p[] = i
+
+proc test: seq[MyObject] = 
+  for i in 0..3:
+    let x = newMyObject(i)
+    result.add x
+
+var x = test()
+for i in 0..3:
+  doAssert(x[i].p[] == i)
diff --git a/tests/destructor/tselect.nim b/tests/destructor/tselect.nim
new file mode 100644
index 000000000..c22bf7203
--- /dev/null
+++ b/tests/destructor/tselect.nim
@@ -0,0 +1,50 @@
+discard """
+   output: '''abcsuffix
+xyzsuffix
+destroy foo 2
+destroy foo 1
+'''
+  cmd: '''nim c --gc:arc $file'''
+"""
+
+proc select(cond: bool; a, b: sink string): string =
+  if cond:
+    result = a # moves a into result
+  else:
+    result = b # moves b into result
+
+proc test(param: string; cond: bool) =
+  var x = "abc" & param
+  var y = "xyz" & param
+
+  # possible self-assignment:
+  x = select(cond, x, y)
+
+  echo x
+  # 'select' must communicate what parameter has been
+  # consumed. We cannot simply generate:
+  # (select(...); wasMoved(x); wasMoved(y))
+
+test("suffix", true)
+test("suffix", false)
+
+
+
+#--------------------------------------------------------------------
+# issue #13659
+
+type
+  Foo = ref object
+    data: int
+    parent: Foo
+
+proc `=destroy`(self: var type(Foo()[])) =
+  echo "destroy foo ", self.data
+  for i in self.fields: i.reset
+
+proc getParent(self: Foo): Foo = self.parent
+
+var foo1 = Foo(data: 1)
+var foo2 = Foo(data: 2, parent: foo1)
+
+foo2.getParent.data = 1
\ No newline at end of file
diff --git a/tests/destructor/tsetjmp_raise.nim b/tests/destructor/tsetjmp_raise.nim
new file mode 100644
index 000000000..3a9803f39
--- /dev/null
+++ b/tests/destructor/tsetjmp_raise.nim
@@ -0,0 +1,11 @@
+discard """
+  outputsub: "index 2 not in 0 .. 0 [IndexDefect]"
+  exitcode: 1
+  cmd: "nim c --gc:arc --exceptions:setjmp $file"
+"""
+
+# bug #12961
+# --gc:arc --exceptions:setjmp
+let a = @[1]
+echo a[2]
+
diff --git a/tests/destructor/tsimpleclosure.nim b/tests/destructor/tsimpleclosure.nim
new file mode 100644
index 000000000..9626dd6f8
--- /dev/null
+++ b/tests/destructor/tsimpleclosure.nim
@@ -0,0 +1,62 @@
+discard """
+  cmd: '''nim c -d:nimAllocStats --gc:arc $file'''
+  output: '''a b
+70
+hello
+hello
+hello
+(allocCount: 3, deallocCount: 3)'''
+"""
+
+import system / ansi_c
+
+proc main(): owned(proc()) =
+  var a = "a"
+  var b = "b"
+  result = proc() =
+    echo a, " ", b
+
+
+proc foo(f: (iterator(): int)) =
+  for i in f(): echo i
+
+proc wrap =
+  let p = main()
+  p()
+
+  let fIt = iterator(): int = yield 70
+  foo fIt
+
+wrap()
+
+# bug #11533
+proc say = echo "hello"
+
+# Error: internal error: genAssignment: tyNil
+var err0: proc() = say
+err0()
+
+var ok0: proc()
+ok0 = say
+ok0()
+
+var ok1 = say
+ok1()
+
+when false:
+  # bug #12443
+  func newStringIterator(s: string): owned(iterator(): char) =
+    result = iterator(): char =
+      var pos = 0
+      while pos < s.len:
+        yield s[pos]
+        inc pos
+
+  proc stringIter() =
+    let si = newStringIterator("foo")
+    for i in si():
+      echo i
+
+  stringIter()
+
+echo getAllocStats()
diff --git a/tests/destructor/tsink.nim b/tests/destructor/tsink.nim
new file mode 100644
index 000000000..e8750ad7c
--- /dev/null
+++ b/tests/destructor/tsink.nim
@@ -0,0 +1,70 @@
+discard """
+  matrix: "--mm:arc"
+"""
+
+type AnObject = object of RootObj
+  value*: int
+
+proc mutate(shit: sink AnObject) =
+  shit.value = 1
+
+proc foo = # bug #23359
+  var bar = AnObject(value: 42)
+  mutate(bar)
+  doAssert bar.value == 42
+
+foo()
+
+block: # bug #23902
+  proc foo(a: sink string): auto = (a, a)
+
+  proc bar(a: sink int): auto = return a
+
+  proc foo(a: sink string) =
+    var x = (a, a)
+
+block: # bug #24175
+  block:
+    func mutate(o: sink string): string =
+      o[1] = '1'
+      result = o
+
+    static:
+      let s = "999"
+      let m = mutate(s)
+      doAssert s == "999"
+      doAssert m == "919"
+
+    func foo() =
+      let s = "999"
+      let m = mutate(s)
+      doAssert s == "999"
+      doAssert m == "919"
+
+    static:
+      foo()
+    foo()
+
+  block:
+    type O = object
+      a: int
+
+    func mutate(o: sink O): O =
+      o.a += 1
+      o
+
+    static:
+      let x = O(a: 1)
+      let y = mutate(x)
+      doAssert x.a == 1
+      doAssert y.a == 2
+
+    proc foo() =
+      let x = O(a: 1)
+      let y = mutate(x)
+      doAssert x.a == 1
+      doAssert y.a == 2
+
+    static:
+      foo()
+    foo()
diff --git a/tests/destructor/ttuple.nim b/tests/destructor/ttuple.nim
index ec12dfc3a..d0ea72c60 100644
--- a/tests/destructor/ttuple.nim
+++ b/tests/destructor/ttuple.nim
@@ -1,6 +1,9 @@
 
 discard """
-   output: '''5.0 10.0'''
+   output: '''5.0 10.0
+=destroy
+=destroy
+'''
 """
 
 type
@@ -46,3 +49,82 @@ let h = MyOpt[float](has: true, val: 5.0)
 myproc(h)
 
 
+#-------------------------------------------------------------
+type
+  MyObject* = object
+    len*: int
+    amount: UncheckedArray[float]
+
+  MyObjPtr* = ptr MyObject
+
+  MyObjContainer* {.byref.} = object
+    size1: int
+    size2: int
+    data: ptr UncheckedArray[MyObjPtr]
+
+ 
+proc size1*(m: MyObjContainer): int {.inline.} = m.size1
+proc size2*(m: MyObjContainer): int {.inline.} = m.size2
+
+proc allocateMyObjPtr(size2: int): MyObjPtr =
+  cast[MyObjPtr](allocShared(sizeof(MyObject) + sizeof(float) * size2.int))
+
+proc `=destroy`*(m: var MyObjContainer) {.inline.} =
+  if m.data != nil:
+    for i in 0..<m.size1:
+      if m.data[i] != nil:
+        deallocShared(m.data[i])
+        m.data[i] = nil
+    deallocShared(m.data)
+    echo "=destroy"
+    m.data = nil
+
+proc `=sink`*(m: var MyObjContainer, m2: MyObjContainer) {.inline.} =
+  if m.data != m2.data:
+    `=destroy`(m)
+  m.size1 = m2.size1
+  m.size2 = m2.size2  
+  m.data = m2.data
+
+
+proc `=`*(m: var MyObjContainer, m2: MyObjContainer) {.error.}
+  ## non copyable
+
+func newMyObjContainer*(size2: Natural): MyObjContainer =
+  result.size2 = size2
+
+proc push(m: var MyObjContainer, cf: MyObjPtr) =
+  ## Add MyObjPtr to MyObjContainer, shallow copy
+  m.size1.inc
+  m.data = cast[ptr UncheckedArray[MyObjPtr]](reallocShared(m.data, m.size1 * sizeof(MyObjPtr)))
+  m.data[m.size1 - 1] = cf
+
+ 
+proc add*(m: var MyObjContainer, amount: float) =
+  assert m.size2 > 0, "MyObjContainer is not initialized, use newMyObjContainer() to initialize object before use"
+  let cf = allocateMyObjPtr(m.size2)
+  for i in 0..<m.size2:
+    cf.amount[i.int] = amount
+
+  m.push(cf)
+
+proc add*(dest: var MyObjContainer, src: sink MyObjContainer) =
+  # merge containers
+
+  for i in 0..<src.size1:
+    dest.push src.data[i]
+    src.data[i] = nil
+
+ 
+proc test = 
+  var cf1 = newMyObjContainer(100)
+  cf1.add(1)
+  cf1.add(2)
+
+  var cf3 = newMyObjContainer(100)
+  cf3.add(2)
+  cf3.add(3)
+
+  cf1.add(cf3)
+
+test()
diff --git a/tests/destructor/tuse_ownedref_after_move.nim b/tests/destructor/tuse_ownedref_after_move.nim
index 148696ee2..69348d530 100644
--- a/tests/destructor/tuse_ownedref_after_move.nim
+++ b/tests/destructor/tuse_ownedref_after_move.nim
@@ -1,10 +1,9 @@
 discard """
   cmd: '''nim c --newruntime $file'''
-  errormsg: "'=' is not available for type <owned Widget>; requires a copy because it's not the last read of ':env.b1()'; another read is done here: tuse_ownedref_after_move.nim(53, 4)"
-  line: 49
+  errormsg: "'=copy' is not available for type <owned Button>; requires a copy because it's not the last read of ':envAlt.b1'; routine: main"
+  line: 48
 """
 
-import core / allocators
 import system / ansi_c
 
 type
@@ -56,5 +55,3 @@ proc main =
 
 main()
 
-let (a, d) = allocCounters()
-discard cprintf("%ld %ld  new: %ld\n", a, d, allocs)
diff --git a/tests/destructor/tuse_result_prevents_sinks.nim b/tests/destructor/tuse_result_prevents_sinks.nim
index 37b5af9b2..e74c16da3 100644
--- a/tests/destructor/tuse_result_prevents_sinks.nim
+++ b/tests/destructor/tuse_result_prevents_sinks.nim
@@ -1,6 +1,6 @@
 discard """
   output: ""
-  target: "C"
+  targets: "c"
 """
 
 # bug #9594
@@ -17,15 +17,20 @@ proc `=sink`(self: var Foo; other: Foo) =
 
 proc `=destroy`(self: var Foo) = discard
 
+template preventCursorInference(x) =
+  let p = addr(x)
+
 proc test(): Foo =
   result = Foo()
   let temp = result
+  preventCursorInference temp
   doAssert temp.i > 0
   return result
 
 proc testB(): Foo =
   result = Foo()
   let temp = result
+  preventCursorInference temp
   doAssert temp.i > 0
 
 discard test()
diff --git a/tests/destructor/tv2_cast.nim b/tests/destructor/tv2_cast.nim
index 8a4d69ef0..48bdf67dd 100644
--- a/tests/destructor/tv2_cast.nim
+++ b/tests/destructor/tv2_cast.nim
@@ -1,11 +1,81 @@
 discard """
-  cmd: '''nim c --newruntime $file'''
   output: '''@[1]
 @[116, 101, 115, 116]
-test
-@[1953719668, 875770417]'''
+@[1953719668, 875770417]
+destroying O1'''
+  cmd: '''nim c --mm:arc --expandArc:main --expandArc:main1 --expandArc:main2 --expandArc:main3 --hints:off --assertions:off $file'''
+  nimout: '''
+--expandArc: main
+
+var
+  data
+  :tmpD
+data = cast[string](encode(cast[seq[byte]](
+  :tmpD = newString(100)
+  :tmpD)))
+`=destroy`(:tmpD)
+`=destroy`(data)
+-- end of expandArc ------------------------
+--expandArc: main1
+
+var
+  s
+  data
+s = newString(100)
+data = cast[string](encode(toOpenArrayByte(s, 0, len(s) - 1)))
+`=destroy`(data)
+`=destroy`(s)
+-- end of expandArc ------------------------
+--expandArc: main2
+
+var
+  s
+  data
+s = newSeq(100)
+data = cast[string](encode(s))
+`=destroy`(data)
+`=destroy_1`(s)
+-- end of expandArc ------------------------
+--expandArc: main3
+
+var
+  data
+  :tmpD
+data = cast[string](encode do:
+  :tmpD = newSeq(100)
+  :tmpD)
+`=destroy`(:tmpD)
+`=destroy_1`(data)
+-- end of expandArc ------------------------
+'''
 """
 
+func encode*(src: openArray[byte]): seq[byte] =
+  result = newSeq[byte](src.len)
+
+template compress*(src: string): string =
+  cast[string](encode(cast[seq[byte]](src)))
+
+proc main =
+  let data = compress(newString(100))
+main()
+
+proc main1 =
+  var
+    s = newString(100)
+  let data = cast[string](encode(s.toOpenArrayByte(0, s.len-1)))
+main1()
+
+proc main2 =
+  var
+    s = newSeq[byte](100)
+  let data = cast[string](encode(s))
+main2()
+
+proc main3 =
+  let data = cast[string](encode(newSeq[byte](100)))
+main3()
+
 # bug #11018
 discard cast[seq[uint8]](@[1])
 discard cast[seq[uint8]]("test")
@@ -13,7 +83,7 @@ echo cast[seq[uint8]](@[1])
 echo cast[seq[uint8]]("test")
 
 discard cast[string](@[116'u8, 101, 115, 116])
-echo cast[string](@[116'u8, 101, 115, 116])
+#echo cast[string](@[116'u8, 101, 115, 116, 0])
 var a = cast[seq[uint32]]("test1234")
 a.setLen(2)
 echo a
@@ -21,4 +91,26 @@ echo a
 
 #issue 11204
 var ac {.compileTime.} = @["a", "b"]
-const bc = ac.len
\ No newline at end of file
+const bc = ac.len
+
+
+type
+  O = object of RootRef
+    i: int
+
+  O1 = object of O
+  O2 = object of O
+
+proc `=destroy`(o: var O) =
+  echo "destroying O"
+
+proc `=destroy`(o: var O1) =
+  echo "destroying O1"
+
+proc `=destroy`(o: var O2) =
+  echo "destroying O2"
+
+proc test =
+  let o3 = cast[ref O2]((ref O1)())
+
+test()
diff --git a/tests/destructor/tv2_raise.nim b/tests/destructor/tv2_raise.nim
index f8d6d30b5..66b0aec30 100644
--- a/tests/destructor/tv2_raise.nim
+++ b/tests/destructor/tv2_raise.nim
@@ -1,12 +1,12 @@
 discard """
-  cmd: '''nim c --newruntime $file'''
-  output: '''OK 2
-4 1'''
+  valgrind: true
+  cmd: '''nim c -d:nimAllocStats --newruntime $file'''
+  output: '''OK 3
+(allocCount: 7, deallocCount: 4)'''
 """
 
 import strutils, math
 import system / ansi_c
-import core / allocators
 
 proc mainA =
   try:
@@ -36,7 +36,18 @@ except ValueError:
 except:
   discard
 
-echo "OK ", ok
+#  bug #11577
+
+proc newError*: owned(ref Exception) {.noinline.} =
+  new(result)
+
+proc mainC =
+  raise newError()
+
+try:
+  mainC()
+except:
+  inc ok
 
-let (a, d) = allocCounters()
-discard cprintf("%ld %ld\n", a, d)
+echo "OK ", ok
+echo getAllocStats()
diff --git a/tests/destructor/twasmoved.nim b/tests/destructor/twasmoved.nim
new file mode 100644
index 000000000..566322702
--- /dev/null
+++ b/tests/destructor/twasmoved.nim
@@ -0,0 +1,14 @@
+type
+  Foo = object
+    id: int
+
+proc `=wasMoved`(x: var Foo) =
+  x.id = -1
+
+proc foo =
+  var s = Foo(id: 999)
+  var m = move s
+  doAssert s.id == -1
+  doAssert m.id == 999
+
+foo()
diff --git a/tests/destructor/twasmoved_error.nim b/tests/destructor/twasmoved_error.nim
new file mode 100644
index 000000000..1cd57e3df
--- /dev/null
+++ b/tests/destructor/twasmoved_error.nim
@@ -0,0 +1,37 @@
+discard """
+  cmd: '''nim c --mm:arc $file'''
+  errormsg: "'=wasMoved' is not available for type <Game>; routine: main"
+"""
+
+# bug #19291
+
+const
+  screenWidth = 800
+  screenHeight = 450
+
+var
+  ready = false
+type
+  Game = object
+
+proc `=destroy`(x: var Game) =
+  assert ready, "Window is already opened"
+  ready = false
+
+proc `=sink`(x: var Game; y: Game) {.error.}
+proc `=copy`(x: var Game; y: Game) {.error.}
+proc `=wasMoved`(x: var Game) {.error.}
+
+proc initGame(width, height: int32, title: string): Game =
+  assert not ready, "Window is already closed"
+  ready = true
+
+proc update(x: Game) = discard
+
+proc main =
+  var g = initGame(screenWidth, screenHeight, "Tetris raylib")
+  g.update()
+  var g2 = g
+  echo "hello"
+
+main()
diff --git a/tests/destructor/twidgets.nim b/tests/destructor/twidgets.nim
index 64fe8ab96..f13868110 100644
--- a/tests/destructor/twidgets.nim
+++ b/tests/destructor/twidgets.nim
@@ -1,11 +1,10 @@
 discard """
-  cmd: '''nim c --newruntime $file'''
+  cmd: '''nim c -d:nimAllocStats --newruntime $file'''
   output: '''button
 clicked!
-1 1  alloc/dealloc pairs: 0'''
+(allocCount: 4, deallocCount: 4)'''
 """
 
-import core / allocators
 import system / ansi_c
 
 type
@@ -63,7 +62,7 @@ proc main =
   var b = newButton("button", nil)
   let u: Button = b
   b.onclick = proc () =
-    b.caption = "clicked!"
+    u.caption = "clicked!"
   w.add b
 
   w.draw()
@@ -72,7 +71,6 @@ proc main =
 
   w.draw()
 
-main()
+dumpAllocstats:
+  main()
 
-let (a, d) = allocCounters()
-discard cprintf("%ld %ld  alloc/dealloc pairs: %ld\n", a, d, allocs)
diff --git a/tests/destructor/twidgets_unown.nim b/tests/destructor/twidgets_unown.nim
index 5e53a0e5b..8653d5c28 100644
--- a/tests/destructor/twidgets_unown.nim
+++ b/tests/destructor/twidgets_unown.nim
@@ -1,11 +1,10 @@
 discard """
-  cmd: '''nim c --newruntime $file'''
+  cmd: '''nim c -d:nimAllocStats --newruntime $file'''
   output: '''button
 clicked!
-3 3  alloc/dealloc pairs: 0'''
+(allocCount: 6, deallocCount: 6)'''
 """
 
-import core / allocators
 import system / ansi_c
 
 type
@@ -52,8 +51,10 @@ proc main =
 
   var b = newButton("button", nil)
   let u = unown b
+  var clicked = "clicked"
   b.onclick = proc () =
-    b.caption = "clicked!"
+    clicked.add "!"
+    u.caption = clicked
   w.add b
 
   w.draw()
@@ -67,7 +68,5 @@ proc main =
   if a != nil:
     a()
 
-main()
-
-let (a, d) = allocCounters()
-discard cprintf("%ld %ld  alloc/dealloc pairs: %ld\n", a, d, allocs)
+dumpAllocStats:
+  main()