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/helper.nim3
-rw-r--r--tests/destructor/nim.cfg1
-rw-r--r--tests/destructor/objFile.nim8
-rw-r--r--tests/destructor/smart_ptr.nim43
-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/t7346.nim14
-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.nim85
-rw-r--r--tests/destructor/tbintree2.nim101
-rw-r--r--tests/destructor/tcaseobj_transitions.nim49
-rw-r--r--tests/destructor/tcast.nim14
-rw-r--r--tests/destructor/tcomplexobjconstr.nim56
-rw-r--r--tests/destructor/tconst_smart_ptr.nim7
-rw-r--r--tests/destructor/tconsume_twice.nim15
-rw-r--r--tests/destructor/tcustomseqs.nim11
-rw-r--r--tests/destructor/tcustomstrings.nim24
-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.nim101
-rw-r--r--tests/destructor/tdestructor3.nim151
-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.nim56
-rw-r--r--tests/destructor/terror_module.nim20
-rw-r--r--tests/destructor/texceptions.nim29
-rw-r--r--tests/destructor/texplicit_move.nim30
-rw-r--r--tests/destructor/tfinalizer.nim31
-rw-r--r--tests/destructor/tgcdestructors.nim203
-rw-r--r--tests/destructor/tgcleak4.nim47
-rw-r--r--tests/destructor/tglobaldestructor.nim9
-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/tinvalid_rebind.nim15
-rw-r--r--tests/destructor/tmatrix.nim135
-rw-r--r--tests/destructor/tmisc_destructors.nim43
-rw-r--r--tests/destructor/tmove.nim18
-rw-r--r--tests/destructor/tmove_objconstr.nim151
-rw-r--r--tests/destructor/tnewruntime_misc.nim155
-rw-r--r--tests/destructor/tnewruntime_strutils.nim254
-rw-r--r--tests/destructor/tnonvardestructor.nim247
-rw-r--r--tests/destructor/tobjfield_analysis.nim51
-rw-r--r--tests/destructor/topt.nim61
-rw-r--r--tests/destructor/topttree.nim7
-rw-r--r--tests/destructor/towned_binary_tree.nim91
-rw-r--r--tests/destructor/tprevent_assign.nim33
-rw-r--r--tests/destructor/tprevent_assign2.nim56
-rw-r--r--tests/destructor/tprevent_assign3.nim54
-rw-r--r--tests/destructor/trecursive.nim60
-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.nim130
-rw-r--r--tests/destructor/turn_destroy_into_finalizer.nim26
-rw-r--r--tests/destructor/tuse_ownedref_after_move.nim57
-rw-r--r--tests/destructor/tuse_result_prevents_sinks.nim37
-rw-r--r--tests/destructor/tv2_cast.nim116
-rw-r--r--tests/destructor/tv2_raise.nim53
-rw-r--r--tests/destructor/twasmoved.nim14
-rw-r--r--tests/destructor/twasmoved_error.nim37
-rw-r--r--tests/destructor/twidgets.nim76
-rw-r--r--tests/destructor/twidgets_unown.nim72
84 files changed, 4905 insertions, 71 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/helper.nim b/tests/destructor/helper.nim
new file mode 100644
index 000000000..466065747
--- /dev/null
+++ b/tests/destructor/helper.nim
@@ -0,0 +1,3 @@
+type
+  MyTestObject*[T] = object
+    p: ptr T
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/objFile.nim b/tests/destructor/objFile.nim
new file mode 100644
index 000000000..436c090d1
--- /dev/null
+++ b/tests/destructor/objFile.nim
@@ -0,0 +1,8 @@
+type Obj* = object
+  v*: int
+
+proc `=destroy`(this: var Obj) =
+  echo "igotdestroyed"
+  this.v = -1
+
+var test* = Obj(v: 42)
diff --git a/tests/destructor/smart_ptr.nim b/tests/destructor/smart_ptr.nim
new file mode 100644
index 000000000..5079dc9db
--- /dev/null
+++ b/tests/destructor/smart_ptr.nim
@@ -0,0 +1,43 @@
+
+type
+  SharedPtr*[T] = object
+    val: ptr tuple[atomicCounter: int, value: T]
+
+proc `=destroy`*[T](p: var SharedPtr[T]) =
+  mixin `=destroy`
+  if p.val != nil:
+    let c = atomicDec(p.val[].atomicCounter)
+    if c == 0:
+      `=destroy`(p.val.value)
+      freeShared(p.val)
+    p.val = nil
+
+proc `=`*[T](dest: var SharedPtr[T], src: SharedPtr[T]) {.inline.} =
+  if dest.val != src.val:
+    if dest.val != nil:
+      `=destroy`(dest)
+    if src.val != nil:
+      discard atomicInc(src.val[].atomicCounter)
+    dest.val = src.val
+
+proc newSharedPtr*[T](val: sink T): SharedPtr[T] =
+  result.val = cast[type(result.val)](allocShared0(sizeof(result.val[])))
+  result.val.atomicCounter = 1
+  result.val.value = val
+
+func get*[T](p: SharedPtr[T]): var T {.inline.} =
+  p.val.value
+
+func isNil*[T](p: SharedPtr[T]): bool {.inline.} =
+  p.val == nil
+
+proc cas*[T](p, old_val: var SharedPtr[T], new_val: SharedPtr[T]): bool {.inline.} =
+  if old_val.val == new_val.val:
+    result = true
+  else:
+    result = cas(p.val.addr, old_val.val, new_val.val)
+    if result:
+      `=destroy`(old_val)
+      if new_val.val != nil:
+        discard atomicInc(new_val.val[].atomicCounter)
+
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/t7346.nim b/tests/destructor/t7346.nim
new file mode 100644
index 000000000..3834d39ff
--- /dev/null
+++ b/tests/destructor/t7346.nim
@@ -0,0 +1,14 @@
+discard """
+joinable: false
+"""
+
+# This bug could only be reproduced with --newruntime
+
+type
+  Obj = object
+    a: int
+
+proc `=`(a: var Obj, b: Obj) = discard
+
+let a: seq[Obj] = @[] # bug #7346
+let b = newSeq[Obj]() # bug #7345
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 d20596415..82870ac82 100644
--- a/tests/destructor/tatomicptrs.nim
+++ b/tests/destructor/tatomicptrs.nim
@@ -8,8 +8,10 @@ allocating
 deallocating
 deallocating
 deallocating
+allocating
+deallocating
 '''
-  cmd: '''nim c --newruntime $file'''
+joinable: false
 """
 
 type
@@ -23,10 +25,9 @@ template incRef(x) =
 
 template decRef(x): untyped = atomicDec(x.refcount)
 
-proc makeShared*[T](x: T): SharedPtr[T] =
-  # XXX could benefit from 'sink' parameter.
+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"
 
@@ -38,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)
@@ -49,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:
@@ -59,6 +63,9 @@ proc `=sink`*[T](dest: var SharedPtr[T]; src: SharedPtr[T]) =
       echo "deallocating"
     dest.x = src.x
 
+proc get*[T](s: SharedPtr[T]): lent T =
+  s.x[]
+
 template `.`*[T](s: SharedPtr[T]; field: untyped): untyped =
   s.x.field
 
@@ -68,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)
 
 
@@ -99,3 +107,70 @@ proc main =
 
 main()
 
+
+
+#-------------------------------------------------------
+#bug #9781
+
+type
+  MySeq* [T] = object
+    refcount: int
+    len: int
+    data: ptr UncheckedArray[T]
+
+proc `=destroy`*[T](m: var MySeq[T]) {.inline.} =
+  if m.data != nil:
+    deallocShared(m.data)
+    m.data = nil
+
+proc `=copy`*[T](m: var MySeq[T], m2: MySeq[T]) =
+  if m.data == m2.data: return
+  if m.data != nil:
+    `=destroy`(m)
+
+  m.len = m2.len
+  let bytes = m.len.int * sizeof(float)
+  if bytes > 0:
+    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
new file mode 100644
index 000000000..61464101f
--- /dev/null
+++ b/tests/destructor/tcaseobj_transitions.nim
@@ -0,0 +1,49 @@
+discard """
+  cmd: '''nim c --gc:arc $file'''
+  output: '''no crash'''
+"""
+
+# bug #11205
+
+type
+  MyEnum = enum
+    A, B, C
+  MyCaseObject = object
+    case kind: MyEnum
+    of A: iseq: seq[int]
+    of B: fseq: seq[float]
+    of C: str: string
+
+
+  MyCaseObjectB = object # carefully constructed to use the same enum,
+                         # but a different object type!
+    case kind: MyEnum
+    of A, C: x: int
+    of B: fseq: seq[float]
+
+
+var x = MyCaseObject(kind: A)
+x.iseq.add 1
+#x.kind = B
+#x.fseq.add -3.0
+
+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/tcast.nim b/tests/destructor/tcast.nim
new file mode 100644
index 000000000..35a7d874b
--- /dev/null
+++ b/tests/destructor/tcast.nim
@@ -0,0 +1,14 @@
+# Make sure we don't walk cast[T] type section while injecting sinks/destructors
+block:
+  type
+    XY[T] = object
+      discard
+
+  proc `=`[T](x: var XY[T]; v: XY[T]) {.error.}
+  proc `=sink`[T](x: var XY[T]; v: XY[T]) {.error.}
+
+  proc main[T]() =
+    var m = cast[ptr XY[T]](alloc0(sizeof(XY[T])))
+    doAssert(m != nil)
+
+  main[int]()
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
new file mode 100644
index 000000000..b0a039e9b
--- /dev/null
+++ b/tests/destructor/tconsume_twice.nim
@@ -0,0 +1,15 @@
+discard """
+  cmd: "nim c --newruntime $file"
+  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
+
+proc use(a: owned Foo): bool = discard
+proc consumeTwice(a: owned Foo): owned Foo =
+  if use(a):
+    return
+  return a
+
+assert consumeTwice(Foo()) != nil
diff --git a/tests/destructor/tcustomseqs.nim b/tests/destructor/tcustomseqs.nim
index 97d7c07b6..17a19f871 100644
--- a/tests/destructor/tcustomseqs.nim
+++ b/tests/destructor/tcustomseqs.nim
@@ -16,7 +16,7 @@ discard """
 1 2 6
 1 3 7
 after 6 6'''
-  cmd: '''nim c --newruntime $file'''
+joinable: false
 """
 
 import typetraits
@@ -43,9 +43,10 @@ proc `=destroy`*[T](x: var myseq[T]) =
 proc `=`*[T](a: var myseq[T]; b: myseq[T]) =
   if a.data == b.data: return
   if a.data != nil:
-    dealloc(a.data)
-    inc deallocCount
-    a.data = nil
+    `=destroy`(a)
+    #dealloc(a.data)
+    #inc deallocCount
+    #a.data = nil
   a.len = b.len
   a.cap = b.cap
   if b.data != nil:
@@ -120,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 1a78df20b..31891856b 100644
--- a/tests/destructor/tcustomstrings.nim
+++ b/tests/destructor/tcustomstrings.nim
@@ -5,11 +5,9 @@ foo bar to appendmore here
 foo bar to appendmore here
 foo bar to appendmore here
 after 20 20'''
-  cmd: '''nim c --newruntime $file'''
+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 c9f1caf2d..e081eb251 100644
--- a/tests/destructor/tdestructor.nim
+++ b/tests/destructor/tdestructor.nim
@@ -1,27 +1,30 @@
 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
+------------------8
 mygeneric1 destroyed
-----
+----6
+myobj destroyed
+----7
+---9
 myobj destroyed
-----
-----
 myobj destroyed
 '''
-  cmd: '''nim c --newruntime $file'''
-  disabled: "true"
 """
 
 type
@@ -29,6 +32,13 @@ type
     x, y: int
     p: pointer
 
+proc `=destroy`(o: var TMyObj) =
+  if o.p != nil:
+    dealloc o.p
+    o.p = nil
+    echo "myobj destroyed"
+
+type
   TMyGeneric1[T] = object
     x: T
 
@@ -36,37 +46,40 @@ type
     x: A
     y: B
 
+proc `=destroy`(o: var TMyGeneric1[int]) =
+  echo "mygeneric1 destroyed"
+
+proc `=destroy`[A, B](o: var TMyGeneric2[A, B]) =
+  echo "mygeneric2 destroyed"
+
+type
   TMyGeneric3[A, B, C] = object
     x: A
     y: B
     z: C
 
-  TObjKind = enum A, B, C, D
+  TDistinctObjX = distinct TMyGeneric3[TMyObj, TMyGeneric2[int, int], int]
+  TDistinctObj = TDistinctObjX
+
+  TObjKind = enum Z, A, B, C, D
 
   TCaseObj = object
+    z: TMyGeneric3[TMyObj, float, int]
     case kind: TObjKind
+    of Z: discard
     of A:
       x: TMyGeneric1[int]
     of B, C:
       y: TMyObj
     else:
       case innerKind: TObjKind
+      of Z: discard
       of A, B, C:
         p: TMyGeneric3[int, float, string]
       of D:
         q: TMyGeneric3[TMyObj, int, int]
       r: string
 
-proc `=destroy`(o: var TMyObj) =
-  if o.p != nil: dealloc o.p
-  echo "myobj destroyed"
-
-proc `=destroy`(o: var TMyGeneric1[int]) =
-  echo "mygeneric1 destroyed"
-
-proc `=destroy`[A, B](o: var TMyGeneric2[A, B]) =
-  echo "mygeneric2 destroyed"
-
 proc open: TMyObj =
   # allow for superfluous ()
   result = (TMyObj(x: 1, y: 2, p: alloc(3)))
@@ -95,36 +108,60 @@ proc mygeneric3 =
 
   echo "mygeneric3 constructed"
 
-echo "----"
+proc mydistinctObj =
+  var x = TMyGeneric3[TMyObj, TMyGeneric2[int, int], int](
+    x: open(), y: TMyGeneric2[int, int](x: 5, y: 15), z: 20)
+
+  echo "mydistinctObj constructed"
+
+echo "----1"
 myobj()
 
-echo "----"
+echo "----2"
 mygeneric1()
 
-echo "----"
+echo "----3"
 mygeneric2[int](10)
 
-echo "----"
+echo "----4"
 mygeneric3()
 
+echo "----5"
+mydistinctObj()
+
 proc caseobj =
   block:
-    echo "----"
     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"))
 
-  block:
-    echo "----"
-    var o4 = TCaseObj(kind: D, innerKind: D, r: "test",
-                      q: TMyGeneric3[TMyObj, int, int](x: open(), y: 1, z: 0))
 
+echo "------------------8"
 caseobj()
 
+proc caseobj_test_sink: TCaseObj =
+  # check that lifted sink can destroy case val correctly
+  result = TCaseObj(kind: D, innerKind: D, r: "test",
+                      q: TMyGeneric3[TMyObj, int, int](x: open(), y: 1, z: 0))
+  result = TCaseObj(kind: B, y: open())
+
+
+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 3e177d3cd..3f5eb2cc1 100644
--- a/tests/destructor/tdestructor3.nim
+++ b/tests/destructor/tdestructor3.nim
@@ -1,12 +1,21 @@
 discard """
-  output: '''assign
+  output: '''
+assign
 destroy
 destroy
 5
 123
+destroy Foo: 123
 destroy Foo: 5
-destroy Foo: 123'''
-  cmd: '''nim c --newruntime $file'''
+(x1: (val: ...))
+destroy
+---------------
+app begin
+(val: ...)
+destroy
+app end
+'''
+joinable: false
 """
 
 # bug #2821
@@ -14,14 +23,18 @@ destroy Foo: 123'''
 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
+  var v1 = T()
+  var v2: T = v1
+  discard addr(v2) # prevent cursorfication
+  use v1
 
 usedToBeBlock()
 
@@ -46,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
new file mode 100644
index 000000000..ffe87cd76
--- /dev/null
+++ b/tests/destructor/tdont_return_unowned_from_owned.nim
@@ -0,0 +1,56 @@
+discard """
+  cmd: "nim check --newruntime --hints:off $file"
+  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(49, 6) Error: illformed AST: 
+'''
+  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
+
+proc newObjA(): Obj =
+  result = new Obj
+
+proc newObjB(): Obj =
+  result = Obj()
+
+proc newObjC(): Obj =
+  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()
+
diff --git a/tests/destructor/terror_module.nim b/tests/destructor/terror_module.nim
new file mode 100644
index 000000000..f3d7c9b26
--- /dev/null
+++ b/tests/destructor/terror_module.nim
@@ -0,0 +1,20 @@
+discard """
+joinable: false
+cmd: "nim check $file"
+errormsg: "type bound operation `=deepcopy` can be defined only in the same module with its type (MyTestObject)"
+nimout: '''
+terror_module.nim(14, 1) Error: type bound operation `=destroy` can be defined only in the same module with its type (MyTestObject)
+terror_module.nim(16, 1) Error: type bound operation `=sink` can be defined only in the same module with its type (MyTestObject)
+terror_module.nim(18, 1) Error: type bound operation `=` can be defined only in the same module with its type (MyTestObject)
+terror_module.nim(20, 1) Error: type bound operation `=deepcopy` can be defined only in the same module with its type (MyTestObject)
+'''
+"""
+import helper
+
+proc `=destroy`[T](x: var MyTestObject[T]) = discard
+
+proc `=sink`[T](x: var MyTestObject[T], y:MyTestObject[T]) = discard
+
+proc `=`[T](x: var MyTestObject[T], y: MyTestObject[T]) = discard
+
+proc `=deepcopy`[T](x: ptr MyTestObject[T]): ptr MyTestObject[T] = discard
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/texplicit_move.nim b/tests/destructor/texplicit_move.nim
new file mode 100644
index 000000000..93795af42
--- /dev/null
+++ b/tests/destructor/texplicit_move.nim
@@ -0,0 +1,30 @@
+
+discard """
+  output: '''3
+0
+0
+10
+destroyed!
+'''
+joinable: false
+"""
+
+type
+  myseq* = object
+    f: int
+
+proc `=destroy`*(x: var myseq) =
+  echo "destroyed!"
+
+var
+  x: myseq
+x.f = 3
+echo move(x.f)
+echo x.f
+
+# bug #9743
+let a = create int
+a[] = 10
+var b = move a[]
+echo a[]
+echo b
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
new file mode 100644
index 000000000..07a3731a0
--- /dev/null
+++ b/tests/destructor/tgcdestructors.nim
@@ -0,0 +1,203 @@
+discard """
+  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"]
+a: @[4, 2, 3]
+0
+30
+true
+(allocCount: 27, deallocCount: 27)'''
+"""
+
+include system / ansi_c
+
+proc main =
+  var s: seq[string] = @[]
+  for i in 0..<80: s.add "foo"
+
+main()
+
+const
+  test = @["hi", "ho", "ha"]
+
+for t in test:
+  echo t
+
+type
+  InterpolatedKind* = enum
+    ikStr,                   ## ``str`` part of the interpolated string
+    ikDollar,                ## escaped ``$`` part of the interpolated string
+    ikVar,                   ## ``var`` part of the interpolated string
+    ikExpr                   ## ``expr`` part of the interpolated string
+
+iterator interpolatedFragments*(s: string): tuple[kind: InterpolatedKind,
+                                                  value: string] =
+  var i = 0
+  var kind: InterpolatedKind
+  while true:
+    var j = i
+    if j < s.len and s[j] == '$':
+      if j+1 < s.len and s[j+1] == '{':
+        inc j, 2
+        var nesting = 0
+        block curlies:
+          while j < s.len:
+            case s[j]
+            of '{': inc nesting
+            of '}':
+              if nesting == 0:
+                inc j
+                break curlies
+              dec nesting
+            else: discard
+            inc j
+          raise newException(ValueError,
+            "Expected closing '}': " & substr(s, i, s.high))
+        inc i, 2 # skip ${
+        kind = ikExpr
+      elif j+1 < s.len and s[j+1] in {'A'..'Z', 'a'..'z', '_'}:
+        inc j, 2
+        while j < s.len and s[j] in {'A'..'Z', 'a'..'z', '0'..'9', '_'}: inc(j)
+        inc i # skip $
+        kind = ikVar
+      elif j+1 < s.len and s[j+1] == '$':
+        inc j, 2
+        inc i # skip $
+        kind = ikDollar
+      else:
+        raise newException(ValueError,
+          "Unable to parse a varible name at " & substr(s, i, s.high))
+    else:
+      while j < s.len and s[j] != '$': inc j
+      kind = ikStr
+    if j > i:
+      # do not copy the trailing } for ikExpr:
+      yield (kind, substr(s, i, j-1-ord(kind == ikExpr)))
+    else:
+      break
+    i = j
+
+proc parseCmdLine(c: string): seq[string] =
+  result = @[]
+  var i = 0
+  var a = ""
+  while true:
+    setLen(a, 0)
+    while i < c.len and c[i] in {' ', '\t', '\l', '\r'}: inc(i)
+    if i >= c.len: break
+    var inQuote = false
+    while i < c.len:
+      case c[i]
+      of '\\':
+        var j = i
+        while j < c.len and c[j] == '\\': inc(j)
+        if j < c.len and c[j] == '"':
+          for k in 1..(j-i) div 2: a.add('\\')
+          if (j-i) mod 2 == 0:
+            i = j
+          else:
+            a.add('"')
+            i = j+1
+        else:
+          a.add(c[i])
+          inc(i)
+      of '"':
+        inc(i)
+        if not inQuote: inQuote = true
+        elif i < c.len and c[i] == '"':
+          a.add(c[i])
+          inc(i)
+        else:
+          inQuote = false
+          break
+      of ' ', '\t':
+        if not inQuote: break
+        a.add(c[i])
+        inc(i)
+      else:
+        a.add(c[i])
+        inc(i)
+    add(result, a)
+
+
+proc other =
+  let input = "$test{}  $this is ${an{  example}}  "
+  let expected = @[(ikVar, "test"), (ikStr, "{}  "), (ikVar, "this"),
+                  (ikStr, " is "), (ikExpr, "an{  example}"), (ikStr, "  ")]
+  var i = 0
+  for s in interpolatedFragments(input):
+    doAssert s == expected[i]
+    inc i
+
+  echo parseCmdLine("arg asdfklasdfkl asdkfj dfasj klfjl")
+
+other()
+
+# bug #11050
+
+type
+  Obj* = object
+    f*: seq[int]
+
+method main(o: Obj) {.base.} =
+  for newb in o.f:
+    discard
+
+# test that o.f was not moved!
+proc testforNoMove =
+  var o = Obj(f: @[1, 2, 3])
+  main(o)
+  echo o.f
+
+testforNoMove()
+
+# bug #11065
+type
+  Warm = seq[string]
+
+proc testWarm =
+  var w: Warm
+  w = @["red", "yellow", "orange"]
+
+  var x = "rt"
+  var y = "rt1"
+  w.add(x & y)
+
+  w.add("pink")
+  echo w
+
+testWarm()
+
+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
new file mode 100644
index 000000000..4d002a092
--- /dev/null
+++ b/tests/destructor/tglobaldestructor.nim
@@ -0,0 +1,9 @@
+discard """
+  cmd: '''nim c --gc:arc $file'''
+  output: '''(v: 42)
+igotdestroyed'''
+"""
+
+import objFile
+
+echo test
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/tinvalid_rebind.nim b/tests/destructor/tinvalid_rebind.nim
new file mode 100644
index 000000000..0f15c8f9e
--- /dev/null
+++ b/tests/destructor/tinvalid_rebind.nim
@@ -0,0 +1,15 @@
+discard """
+joinable: false
+cmd: "nim check $file"
+errormsg: "cannot bind another '=destroy' to: Foo; previous declaration was constructed here implicitly: tinvalid_rebind.nim(12, 7)"
+line: 14
+"""
+
+type
+  Foo[T] = object
+
+proc main =
+  var f: Foo[int]
+
+proc `=destroy`[T](f: var Foo[T]) =
+  discard
diff --git a/tests/destructor/tmatrix.nim b/tests/destructor/tmatrix.nim
new file mode 100644
index 000000000..2fd5af789
--- /dev/null
+++ b/tests/destructor/tmatrix.nim
@@ -0,0 +1,135 @@
+discard """
+  output: '''after 2 2
+after 2 2
+after 2 2
+after 2 2'''
+"""
+# bug #9263
+type
+  Matrix* = object
+    # Array for internal storage of elements.
+    data: ptr UncheckedArray[float]
+    # Row and column dimensions.
+    m*, n*: int
+
+var
+  allocCount, deallocCount: int
+
+proc `=destroy`*(m: var Matrix) =
+  if m.data != nil:
+    dealloc(m.data)
+    deallocCount.inc
+    m.data = nil
+    m.m = 0
+    m.n = 0
+
+proc `=sink`*(a: var Matrix; b: Matrix) =
+  if a.data != nil and a.data != b.data:
+    dealloc(a.data)
+    deallocCount.inc
+  a.data = b.data
+  a.m = b.m
+  a.n = b.n
+
+proc `=copy`*(a: var Matrix; b: Matrix) =
+  if a.data != nil and a.data != b.data:
+    dealloc(a.data)
+    deallocCount.inc
+    a.data = nil
+  a.m = b.m
+  a.n = b.n
+  if b.data != nil:
+    a.data = cast[type(a.data)](alloc(a.m * a.n * sizeof(float)))
+    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
+  result.n = n
+  result.data = cast[type(result.data)](alloc(m * n * sizeof(float)))
+  allocCount.inc
+  for i in 0 ..< m * n:
+    result.data[i] = s
+
+proc len(m: Matrix): int = m.n * m.m
+
+proc `[]`*(m: Matrix, i, j: int): float {.inline.} =
+  ## Get a single element.
+  m.data[i * m.n + j]
+
+proc `[]`*(m: var Matrix, i, j: int): var float {.inline.} =
+  ## Get a single element.
+  m.data[i * m.n + j]
+
+proc `[]=`*(m: var Matrix, i, j: int, s: float) =
+  ## Set a single element.
+  m.data[i * m.n + j] = s
+
+proc `-`*(m: sink Matrix): Matrix =
+  ## Unary minus
+  result = m
+  for i in 0 ..< result.m:
+    for j in 0 ..< result.n:
+      result[i, j] = -result[i, j]
+
+proc `+`*(a: sink Matrix; b: Matrix): Matrix =
+  ## ``C = A + B``
+  doAssert(b.m == a.m and b.n == a.n, "Matrix dimensions must agree.")
+  doAssert(a.len == b.len) # non destructive use before sink is ok
+  result = a
+  for i in 0 ..< result.m:
+    for j in 0 ..< result.n:
+      result[i, j] = result[i, j] + b[i, j]
+
+proc `-`*(a: sink Matrix; b: Matrix): Matrix =
+  ## ``C = A - B``
+  assert(b.m == a.m and b.n == a.n, "Matrix dimensions must agree.")
+  doAssert(a.len == b.len) # non destructive use before sink is ok
+  result = a
+  for i in 0 ..< result.m:
+    for j in 0 ..< result.n:
+      result[i, j] = a[i, j] - b[i, j]
+
+proc info =
+  echo "after ", allocCount, " ", deallocCount
+  allocCount = 0
+  deallocCount = 0
+
+proc copy(a: Matrix): Matrix = a
+
+proc test1 =
+  var a = matrix(5, 5, 1.0)
+  var b = copy a
+  var c = a + b
+
+proc test2 =
+  var a = matrix(5, 5, 1.0)
+  var b = copy a
+  var c = -a
+
+proc test3 =
+  var a = matrix(5, 5, 1.0)
+  var b = matrix(5, 5, 2.0)
+  #    a = a - b
+  b = -b + a
+
+proc test4 =
+  # bug #9294
+  var a = matrix(5, 5, 1.0)
+  a = -a + a
+
+test1()
+info()
+
+test2()
+info()
+
+test3()
+info()
+
+test4()
+info()
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 8aa12ed05..cdc1eb1c0 100644
--- a/tests/destructor/tmove_objconstr.nim
+++ b/tests/destructor/tmove_objconstr.nim
@@ -7,7 +7,8 @@ test destroyed 0
 3
 4
 Pony is dying!'''
-  cmd: '''nim c --newruntime $file'''
+joinable: false
+targets: "c"
 """
 
 # bug #4214
@@ -36,24 +37,158 @@ proc pointlessWrapper(s: string): Data =
 proc main =
   var x = pointlessWrapper"test"
 
-when isMainModule:
+when true:
   main()
 
 # bug #985
 
 type
-    Pony = object
-        name: string
+  Pony = object
+    name: string
 
 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:
-        yield i
+  for i in 1..4:
+    yield i
 
 for x in getPony():
-    echo x
+  echo x
+
+
+
+
+
+#------------------------------------------------------------
+#-- Move into tuple constructor and move on tuple unpacking
+#------------------------------------------------------------
+
+type
+  MySeqNonCopyable* = object
+    len: int
+    data: ptr UncheckedArray[float]
+
+proc `=destroy`*(m: var MySeqNonCopyable) {.inline.} =
+  if m.data != nil:
+    deallocShared(m.data)
+    m.data = nil
+
+proc `=`*(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, 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 myfunc(x, y: int): (MySeqNonCopyable, MySeqNonCopyable) =
+  result = (newMySeq(x, 1.0), newMySeq(y, 5.0))
+
+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)
+      z2
+    elif x > 5: raise newException(ValueError, "new error")
+    else: newMySeq(x, 1.0),
+   b: 0,
+   c: block:
+        var tmp = if y > 0: move(cc) else: newMySeq(1, 3.0)
+        tmp[0] = 5
+        tmp
+  )
+
+
+let (seq1, seq2) = myfunc(2, 3)
+doAssert seq1.len == 2
+doAssert seq1[0] == 1.0
+doAssert seq2.len == 3
+doAssert seq2[0] == 5.0
+
+var (seq3, i, _) = myfunc2(2, 3)
+doAssert seq3.len == 2
+doAssert seq3[0] == 1.0
+
+var seq4, seq5: MySeqNonCopyable
+(seq4, i, seq5) = myfunc2(2, 3)
+
+proc foo =
+  seq4 = block:
+    var tmp = newMySeq(4, 1.0)
+    tmp[0] = 3.0
+    tmp
+
+  doAssert seq4[0] == 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
+
+foo()
+
+#------------------------------------------------------------
+#-- Move into array constructor
+#------------------------------------------------------------
+
+var ii = 1
+let arr2 = [newMySeq(2, 5.0), if i > 1: newMySeq(3, 1.0) else: newMySeq(0, 0.0)]
+var seqOfSeq2 = @[newMySeq(2, 5.0), newMySeq(3, 1.0)]
+
+
+## issue #10462
+proc myfuncLoop(x: int): MySeqNonCopyable =
+  for i in 0..<x:
+    var cc = newMySeq(i, 5.0)
+    result = cc
+
+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
new file mode 100644
index 000000000..9c8d41973
--- /dev/null
+++ b/tests/destructor/tnewruntime_strutils.nim
@@ -0,0 +1,254 @@
+discard """
+  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, std / wordwrap
+
+import system / ansi_c
+
+# bug #11004
+proc retTuple(): (seq[int], int) =
+  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"
+  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
+                                                    ["1,0e-11", "1,0e-011"]
+
+  doAssert "$# $3 $# $#" % ["a", "b", "c"] == "a c b c"
+  doAssert "${1}12 ${-1}$2" % ["a", "b"] == "a12 bb"
+
+  block: # formatSize tests
+    when not defined(js):
+      doAssert formatSize((1'i64 shl 31) + (300'i64 shl 20)) == "2.293GiB"   # <=== bug #8231
+    doAssert formatSize((2.234*1024*1024).int) == "2.234MiB"
+    doAssert formatSize(4096) == "4KiB"
+    doAssert formatSize(4096, prefix=bpColloquial, includeSpace=true) == "4 kB"
+    doAssert formatSize(4096, includeSpace=true) == "4 KiB"
+    doAssert formatSize(5_378_934, prefix=bpColloquial, decimalSep=',') == "5,13MB"
+
+  block: # formatEng tests
+    doAssert formatEng(0, 2, trim=false) == "0.00"
+    doAssert formatEng(0, 2) == "0"
+    doAssert formatEng(53, 2, trim=false) == "53.00"
+    doAssert formatEng(0.053, 2, trim=false) == "53.00e-3"
+    doAssert formatEng(0.053, 4, trim=false) == "53.0000e-3"
+    doAssert formatEng(0.053, 4, trim=true) == "53e-3"
+    doAssert formatEng(0.053, 0) == "53e-3"
+    doAssert formatEng(52731234) == "52.731234e6"
+    doAssert formatEng(-52731234) == "-52.731234e6"
+    doAssert formatEng(52731234, 1) == "52.7e6"
+    doAssert formatEng(-52731234, 1) == "-52.7e6"
+    doAssert formatEng(52731234, 1, decimalSep=',') == "52,7e6"
+    doAssert formatEng(-52731234, 1, decimalSep=',') == "-52,7e6"
+
+    doAssert formatEng(4100, siPrefix=true, unit="V") == "4.1 kV"
+    doAssert formatEng(4.1, siPrefix=true, unit="V", useUnitSpace=true) == "4.1 V"
+    doAssert formatEng(4.1, siPrefix=true) == "4.1" # Note lack of space
+    doAssert formatEng(4100, siPrefix=true) == "4.1 k"
+    doAssert formatEng(4.1, siPrefix=true, unit="", useUnitSpace=true) == "4.1 " # Includes space
+    doAssert formatEng(4100, siPrefix=true, unit="") == "4.1 k"
+    doAssert formatEng(4100) == "4.1e3"
+    doAssert formatEng(4100, unit="V", useUnitSpace=true) == "4.1e3 V"
+    doAssert formatEng(4100, unit="", useUnitSpace=true) == "4.1e3 "
+    # Don't use SI prefix as number is too big
+    doAssert formatEng(3.1e22, siPrefix=true, unit="a", useUnitSpace=true) == "31e21 a"
+    # Don't use SI prefix as number is too small
+    doAssert formatEng(3.1e-25, siPrefix=true, unit="A", useUnitSpace=true) == "310e-27 A"
+
+proc staticTests =
+  doAssert align("abc", 4) == " abc"
+  doAssert align("a", 0) == "a"
+  doAssert align("1232", 6) == "  1232"
+  doAssert align("1232", 6, '#') == "##1232"
+
+  doAssert alignLeft("abc", 4) == "abc "
+  doAssert alignLeft("a", 0) == "a"
+  doAssert alignLeft("1232", 6) == "1232  "
+  doAssert alignLeft("1232", 6, '#') == "1232##"
+
+  let
+    inp = """ this is a long text --  muchlongerthan10chars and here
+                it goes"""
+    outp = " this is a\nlong text\n--\nmuchlongerthan10chars\nand here\nit goes"
+  doAssert wrapWords(inp, 10, false) == outp
+
+  let
+    longInp = """ThisIsOneVeryLongStringWhichWeWillSplitIntoEightSeparatePartsNow"""
+    longOutp = "ThisIsOn\neVeryLon\ngStringW\nhichWeWi\nllSplitI\nntoEight\nSeparate\nPartsNow"
+  doAssert wrapWords(longInp, 8, true) == longOutp
+
+  doAssert "$animal eats $food." % ["animal", "The cat", "food", "fish"] ==
+            "The cat eats fish."
+
+  doAssert "-ld a-ldz -ld".replaceWord("-ld") == " a-ldz "
+  doAssert "-lda-ldz -ld abc".replaceWord("-ld") == "-lda-ldz  abc"
+
+  doAssert "-lda-ldz -ld abc".replaceWord("") == "-lda-ldz -ld abc"
+  doAssert "oo".replace("", "abc") == "oo"
+
+  type MyEnum = enum enA, enB, enC, enuD, enE
+  doAssert parseEnum[MyEnum]("enu_D") == enuD
+
+  doAssert parseEnum("invalid enum value", enC) == enC
+
+  doAssert center("foo", 13) == "     foo     "
+  doAssert center("foo", 0) == "foo"
+  doAssert center("foo", 3, fillChar = 'a') == "foo"
+  doAssert center("foo", 10, fillChar = '\t') == "\t\t\tfoo\t\t\t\t"
+
+  doAssert count("foofoofoo", "foofoo") == 1
+  doAssert count("foofoofoo", "foofoo", overlapping = true) == 2
+  doAssert count("foofoofoo", 'f') == 3
+  doAssert count("foofoofoobar", {'f','b'}) == 4
+
+  doAssert strip("  foofoofoo  ") == "foofoofoo"
+  doAssert strip("sfoofoofoos", chars = {'s'}) == "foofoofoo"
+  doAssert strip("barfoofoofoobar", chars = {'b', 'a', 'r'}) == "foofoofoo"
+  doAssert strip("stripme but don't strip this stripme",
+                  chars = {'s', 't', 'r', 'i', 'p', 'm', 'e'}) ==
+                  " but don't strip this "
+  doAssert strip("sfoofoofoos", leading = false, chars = {'s'}) == "sfoofoofoo"
+  doAssert strip("sfoofoofoos", trailing = false, chars = {'s'}) == "foofoofoos"
+
+  doAssert "  foo\n  bar".indent(4, "Q") == "QQQQ  foo\nQQQQ  bar"
+
+  doAssert "abba".multiReplace(("a", "b"), ("b", "a")) == "baab"
+  doAssert "Hello World.".multiReplace(("ello", "ELLO"), ("World.", "PEOPLE!")) == "HELLO PEOPLE!"
+  doAssert "aaaa".multiReplace(("a", "aa"), ("aa", "bb")) == "aaaaaaaa"
+
+  doAssert rsplit("foo bar", seps=Whitespace) == @["foo", "bar"]
+  doAssert rsplit(" foo bar", seps=Whitespace, maxsplit=1) == @[" foo", "bar"]
+  doAssert rsplit(" foo bar ", seps=Whitespace, maxsplit=1) == @[" foo bar", ""]
+  doAssert rsplit(":foo:bar", sep=':') == @["", "foo", "bar"]
+  doAssert rsplit(":foo:bar", sep=':', maxsplit=2) == @["", "foo", "bar"]
+  doAssert rsplit(":foo:bar", sep=':', maxsplit=3) == @["", "foo", "bar"]
+  doAssert rsplit("foothebar", sep="the") == @["foo", "bar"]
+
+  doAssert(unescape(r"\x013", "", "") == "\x013")
+
+  doAssert join(["foo", "bar", "baz"]) == "foobarbaz"
+  doAssert join(@["foo", "bar", "baz"], ", ") == "foo, bar, baz"
+  doAssert join([1, 2, 3]) == "123"
+  doAssert join(@[1, 2, 3], ", ") == "1, 2, 3"
+
+  doAssert """~~!!foo
+~~!!bar
+~~!!baz""".unindent(2, "~~!!") == "foo\nbar\nbaz"
+
+  doAssert """~~!!foo
+~~!!bar
+~~!!baz""".unindent(2, "~~!!aa") == "~~!!foo\n~~!!bar\n~~!!baz"
+  doAssert """~~foo
+~~  bar
+~~  baz""".unindent(4, "~") == "foo\n  bar\n  baz"
+  doAssert """foo
+  bar
+    baz
+  """.unindent(4) == "foo\nbar\nbaz\n"
+  doAssert """foo
+    bar
+    baz
+  """.unindent(2) == "foo\n  bar\n  baz\n"
+  doAssert """foo
+      bar
+      baz
+    """.unindent(100) == "foo\nbar\nbaz\n"
+
+  doAssert """foo
+      foo
+      bar
+    """.unindent() == "foo\nfoo\nbar\n"
+
+  let s = " this is an example  "
+  let s2 = ":this;is;an:example;;"
+
+  doAssert s.split() == @["", "this", "is", "an", "example", "", ""]
+  doAssert s2.split(seps={':', ';'}) == @["", "this", "is", "an", "example", "", ""]
+  doAssert s.split(maxsplit=4) == @["", "this", "is", "an", "example  "]
+  doAssert s.split(' ', maxsplit=1) == @["", "this is an example  "]
+  doAssert s.split(" ", maxsplit=4) == @["", "this", "is", "an", "example  "]
+
+  doAssert s.splitWhitespace() == @["this", "is", "an", "example"]
+  doAssert s.splitWhitespace(maxsplit=1) == @["this", "is an example  "]
+  doAssert s.splitWhitespace(maxsplit=2) == @["this", "is", "an example  "]
+  doAssert s.splitWhitespace(maxsplit=3) == @["this", "is", "an", "example  "]
+  doAssert s.splitWhitespace(maxsplit=4) == @["this", "is", "an", "example"]
+
+  discard retTuple()
+
+nonStaticTests()
+staticTests()
+
+# 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/tobjfield_analysis.nim b/tests/destructor/tobjfield_analysis.nim
new file mode 100644
index 000000000..83f394c3b
--- /dev/null
+++ b/tests/destructor/tobjfield_analysis.nim
@@ -0,0 +1,51 @@
+discard """
+  output: '''works'''
+"""
+
+#  bug #11095
+
+type
+  MyVal[T] = object
+    f: ptr T
+
+proc `=destroy`[T](x: var MyVal[T]) =
+  if x.f != nil:
+    dealloc(x.f)
+
+proc `=sink`[T](x1: var MyVal[T], x2: MyVal[T]) =
+  if x1.f != x2.f:
+    `=destroy`(x1)
+    x1.f = x2.f
+
+proc `=`[T](x1: var MyVal[T], x2: MyVal[T]) {.error.}
+
+proc newVal[T](x: sink T): MyVal[T] =
+  result.f = create(T)
+  result.f[] = x
+
+proc set[T](x: var MyVal[T], val: T) =
+  x.f[] = val
+
+proc sinkMe[T](x: sink MyVal[T]) =
+  discard
+
+var flag = false
+
+proc main =
+  var y = case flag
+    of true:
+      var x1 = newVal[float](1.0)
+      var x2 = newVal[float](2.0)
+      (newVal(x1), newVal(x2))
+
+    of false:
+      var x1 = newVal[float](1.0)
+      var x2 = newVal[float](2.0)
+      (newVal(x1), newVal(x2))
+
+  sinkMe y[0]
+  sinkMe y[1]
+  echo "works"
+
+main()
+
diff --git a/tests/destructor/topt.nim b/tests/destructor/topt.nim
new file mode 100644
index 000000000..4adda1914
--- /dev/null
+++ b/tests/destructor/topt.nim
@@ -0,0 +1,61 @@
+
+discard """
+  output: '''5
+vseq destroy
+'''
+joinable: false
+"""
+type
+  opt*[T] = object
+    case exists: bool
+      of true: val: T
+      of false: discard
+
+proc some*[T](val: sink T): opt[T] {.inline.} =
+  ## Returns an ``opt`` that has the value.
+  ## nil is considered as none for reference types
+  result = opt[T](exists: true, val: val)
+
+proc none*(T: typedesc): opt[T] {.inline.} =
+  ## Returns an ``opt`` for this type that has no value.
+  # the default is the none type
+  discard
+
+proc none*[T]: opt[T] {.inline.} =
+  ## Alias for ``none(T)``.
+  none(T)
+
+proc unsafeGet*[T](self: opt[T]): lent T {.inline.} =
+  ## Returns the value of a ``some``. Behavior is undefined for ``none``.
+  self.val
+
+type
+  VSeq*[T] = object
+    len: int
+    data: ptr UncheckedArray[T]
+
+proc `=destroy`*[T](m: var VSeq[T]) {.inline.} =
+  if m.data != nil:
+    echo "vseq destroy"
+    dealloc(m.data)
+    m.data = nil
+
+proc `=`*[T](m: var VSeq[T], m2: VSeq[T]) {.error.}
+
+proc `=sink`*[T](m: var VSeq[T], m2: VSeq[T]) {.inline.} =
+  if m.data != m2.data:
+    `=destroy`(m)
+  m.len = m2.len
+  m.data = m2.data
+
+proc newVSeq*[T](len: int): VSeq[T] =
+  ## Only support sequence creation from scalar size because creation from
+  ## vetorized size can't reproduce the original scalar size
+  result.len = len
+  if len > 0:
+    result.data = cast[ptr UncheckedArray[T]](alloc(sizeof(T) * len))
+
+let x = some newVSeq[float](5)
+echo x.unsafeGet.len
+let y = none(VSeq[float])
+
diff --git a/tests/destructor/topttree.nim b/tests/destructor/topttree.nim
index 924644392..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
@@ -8,7 +9,7 @@ discard """
 90.0
 120.0
 8 8'''
-  cmd: '''nim c --newruntime $file'''
+joinable: false
 """
 
 import typetraits
@@ -22,6 +23,7 @@ var
 
 proc `=destroy`*[T](x: var opt[T]) =
   if x.data != nil:
+    mixin `=destroy`
     when not supportsCopyMem(T):
       `=destroy`(x.data[])
     dealloc(x.data)
@@ -90,6 +92,8 @@ proc write(t: opt[Tree]) =
     write stdout, it.data, "\n"
     write(it.ri)
 
+proc use(t: opt[Tree]) = discard
+
 proc main =
   var t: opt[Tree]
   insert t, 60.0
@@ -99,6 +103,7 @@ proc main =
   write t
   let copy = t
   write copy
+  use t
 
 main()
 echo allocCount, " ", deallocCount
diff --git a/tests/destructor/towned_binary_tree.nim b/tests/destructor/towned_binary_tree.nim
new file mode 100644
index 000000000..fb635e7c6
--- /dev/null
+++ b/tests/destructor/towned_binary_tree.nim
@@ -0,0 +1,91 @@
+discard """
+  cmd: '''nim c -d:nimAllocStats --gc:arc $file'''
+  output: '''31665
+(allocCount: 33334, deallocCount: 33334)'''
+"""
+
+#  bug #11053
+
+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(lower.right, greater)
+    result = lower
+  else:
+    greater.left = merge(lower, 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(orig.right, value)
+    orig.right = splitPair[0]
+    result = (orig, splitPair[1])
+  else:
+    let splitPair = splitBinary(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 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 ..< 100000:
+    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/tprevent_assign.nim b/tests/destructor/tprevent_assign.nim
new file mode 100644
index 000000000..4c484ebc1
--- /dev/null
+++ b/tests/destructor/tprevent_assign.nim
@@ -0,0 +1,33 @@
+discard """
+  errormsg: "'=copy' is not available for type <Foo>; requires a copy because it's not the last read of 'otherTree'"
+  line: 29
+"""
+
+type
+  Foo = object
+    x: int
+
+proc `=destroy`(f: var Foo) = f.x = 0
+proc `=`(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 =
+  Foo(x: x)
+
+proc take2(a, b: sink Foo) =
+  echo a.x, " ", b.x
+
+proc allowThis() =
+  # all these temporary lets are harmless:
+  let otherTree = createTree(44)
+  let b = otherTree
+  let c = b
+  take2(createTree(34), c)
+
+proc preventThis() =
+  let otherTree = createTree(44)
+  let b = otherTree
+  take2(createTree(34), otherTree)
+
+allowThis()
+preventThis()
diff --git a/tests/destructor/tprevent_assign2.nim b/tests/destructor/tprevent_assign2.nim
new file mode 100644
index 000000000..eb5588b1a
--- /dev/null
+++ b/tests/destructor/tprevent_assign2.nim
@@ -0,0 +1,56 @@
+discard """
+  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: 49
+"""
+
+type
+  Foo = object
+    x: int
+
+proc `=destroy`(f: var Foo) = f.x = 0
+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 =
+  Foo(x: x)
+
+proc take2(a, b: sink Foo) =
+  echo a.x, " ", b.x
+
+when false:
+  var otherTree: Foo
+  try:
+    for i in 0..3:
+      while true:
+        #if i == 0:
+        otherTree = createTree(44)
+        case i
+        of 0:
+          echo otherTree
+          take2(createTree(34), otherTree)
+        of 1:
+          take2(createTree(34), otherTree)
+        else:
+          discard
+  finally:
+    discard
+
+proc preventThis() =
+  var otherTree: Foo
+  for i in 0..3:
+    while true:
+      if i == 0:
+        otherTree = createTree(44)
+      case i
+      of 0:
+        echo otherTree
+        take2(createTree(34), otherTree)
+      of 1:
+        take2(createTree(34), otherTree)
+      else:
+        discard
+
+#allowThis()
+preventThis()
diff --git a/tests/destructor/tprevent_assign3.nim b/tests/destructor/tprevent_assign3.nim
new file mode 100644
index 000000000..aa834a66c
--- /dev/null
+++ b/tests/destructor/tprevent_assign3.nim
@@ -0,0 +1,54 @@
+discard """
+  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: 47
+"""
+
+type
+  Foo = object
+    x: int
+
+proc `=destroy`(f: var Foo) = f.x = 0
+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 =
+  Foo(x: x)
+
+proc take2(a, b: sink Foo) =
+  echo a.x, " ", b.x
+
+when false:
+  var otherTree: Foo
+  try:
+    for i in 0..3:
+      while true:
+        #if i == 0:
+        otherTree = createTree(44)
+        case i
+        of 0:
+          echo otherTree
+          take2(createTree(34), otherTree)
+        of 1:
+          take2(createTree(34), otherTree)
+        else:
+          discard
+  finally:
+    discard
+
+proc preventThis2() =
+  var otherTree: Foo
+  try:
+    try:
+      otherTree = createTree(44)
+      echo otherTree
+    finally:
+      take2(createTree(34), otherTree)
+  finally:
+    echo otherTree
+
+#allowThis()
+preventThis2()
+
+
diff --git a/tests/destructor/trecursive.nim b/tests/destructor/trecursive.nim
new file mode 100644
index 000000000..e7afa6ba9
--- /dev/null
+++ b/tests/destructor/trecursive.nim
@@ -0,0 +1,60 @@
+
+discard """
+   output: '''
+test1 OK
+'''
+"""
+
+import smart_ptr
+
+type
+  Node[T] = object
+    value: T
+    next: SharedPtr[Node[T]]
+
+  ForwardList[T] = object
+    first: SharedPtr[Node[T]]
+    len: Natural
+
+proc pushFront*[T] (list: var ForwardList[T], val: sink T) =
+  var newNode = newSharedPtr(Node[T](value: val))
+  var result = false
+  while not result:
+    var head = list.first
+    newNode.get.next = head
+    result = list.first.cas(head, newNode)
+  list.len.atomicInc()
+
+proc test1() =
+  var list: ForwardList[int]
+  list.pushFront(1)
+  doAssert list.len == 1
+  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
new file mode 100644
index 000000000..d0ea72c60
--- /dev/null
+++ b/tests/destructor/ttuple.nim
@@ -0,0 +1,130 @@
+
+discard """
+   output: '''5.0 10.0
+=destroy
+=destroy
+'''
+"""
+
+type
+  MyOpt[T] = object
+    case has: bool:
+      of true: val: T
+      of false: nil
+
+  MyVal = object
+    f: ptr float
+
+proc `=destroy`(x: var MyVal) =
+  if x.f != nil:
+    dealloc(x.f)
+
+proc `=sink`(x1: var MyVal, x2: Myval) =
+  if x1.f != x2.f:
+    `=destroy`(x1)
+    x1.f = x2.f
+
+proc `=`(x1: var MyVal, x2: Myval) =
+  if x1.f != x2.f:
+    `=destroy`(x1)
+    x1.f = create(float)
+    x1.f[] = x2.f[]
+
+proc newVal(x: float): MyVal =
+  result.f = create(float)
+  result.f[] = x
+
+template getIt[T, R](self: MyOpt[T], body: untyped, default: R): R =
+  if self.has:
+    template it: untyped {.inject.} = self.val
+    body
+  else:
+    default
+
+proc myproc(h: MyOpt[float]) =
+  let (a, b) = h.getIt((newVal(it), newVal(it * 2)), (newVal(1.0), newVal(1.0)))
+  echo a.f[], " ", b.f[]
+
+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/turn_destroy_into_finalizer.nim b/tests/destructor/turn_destroy_into_finalizer.nim
new file mode 100644
index 000000000..1409c1c57
--- /dev/null
+++ b/tests/destructor/turn_destroy_into_finalizer.nim
@@ -0,0 +1,26 @@
+discard """
+  output: "turn_destroy_into_finalizer works"
+  joinable: false
+"""
+
+type
+  Foo = object
+    id: int
+
+var destroyed: int
+
+proc `=destroy`(x: var Foo) =
+  #echo "finally ", x.id
+  inc destroyed
+
+proc main =
+  var r: ref Foo
+  for i in 1..50_000:
+    new(r)
+    r.id = i
+  if destroyed > 30_000:
+    echo "turn_destroy_into_finalizer works"
+  else:
+    echo "turn_destroy_into_finalizer failed: ", destroyed
+
+main()
diff --git a/tests/destructor/tuse_ownedref_after_move.nim b/tests/destructor/tuse_ownedref_after_move.nim
new file mode 100644
index 000000000..69348d530
--- /dev/null
+++ b/tests/destructor/tuse_ownedref_after_move.nim
@@ -0,0 +1,57 @@
+discard """
+  cmd: '''nim c --newruntime $file'''
+  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 system / ansi_c
+
+type
+  Widget* = ref object of RootObj
+    drawImpl: owned(proc (self: Widget))
+
+  Button* = ref object of Widget
+    caption: string
+    onclick: owned(proc())
+
+  Window* = ref object of Widget
+    elements: seq[owned Widget]
+
+
+proc newButton(caption: string; onclick: owned(proc())): owned Button =
+  proc draw(self: Widget) =
+    let b = Button(self)
+    echo b.caption
+
+  result = Button(drawImpl: draw, caption: caption, onclick: onclick)
+
+proc newWindow(): owned Window =
+  proc draw(self: Widget) =
+    let w = Window(self)
+    for e in w.elements:
+      if not e.drawImpl.isNil: e.drawImpl(e)
+
+  result = Window(drawImpl: draw, elements: @[])
+
+proc draw(w: Widget) =
+  if not w.drawImpl.isNil: w.drawImpl(w)
+
+proc add*(w: Window; elem: owned Widget) =
+  w.elements.add elem
+
+proc main =
+  var w = newWindow()
+
+  var b = newButton("button", nil)
+  b.onclick = proc () =
+    b.caption = "clicked!"
+  w.add b
+
+  w.draw()
+  # simulate button click:
+  b.onclick()
+
+  w.draw()
+
+main()
+
diff --git a/tests/destructor/tuse_result_prevents_sinks.nim b/tests/destructor/tuse_result_prevents_sinks.nim
new file mode 100644
index 000000000..e74c16da3
--- /dev/null
+++ b/tests/destructor/tuse_result_prevents_sinks.nim
@@ -0,0 +1,37 @@
+discard """
+  output: ""
+  targets: "c"
+"""
+
+# bug #9594
+
+type
+  Foo = object
+    i: int
+
+proc `=`(self: var Foo; other: Foo) =
+  self.i = other.i + 1
+
+proc `=sink`(self: var Foo; other: Foo) =
+  self.i = other.i
+
+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()
+discard testB()
diff --git a/tests/destructor/tv2_cast.nim b/tests/destructor/tv2_cast.nim
new file mode 100644
index 000000000..48bdf67dd
--- /dev/null
+++ b/tests/destructor/tv2_cast.nim
@@ -0,0 +1,116 @@
+discard """
+  output: '''@[1]
+@[116, 101, 115, 116]
+@[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")
+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, 0])
+var a = cast[seq[uint32]]("test1234")
+a.setLen(2)
+echo a
+
+
+#issue 11204
+var ac {.compileTime.} = @["a", "b"]
+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
new file mode 100644
index 000000000..66b0aec30
--- /dev/null
+++ b/tests/destructor/tv2_raise.nim
@@ -0,0 +1,53 @@
+discard """
+  valgrind: true
+  cmd: '''nim c -d:nimAllocStats --newruntime $file'''
+  output: '''OK 3
+(allocCount: 7, deallocCount: 4)'''
+"""
+
+import strutils, math
+import system / ansi_c
+
+proc mainA =
+  try:
+    var e: owned(ref ValueError)
+    new(e)
+    e.msg = "message"
+    raise e
+  except Exception as e:
+    raise
+
+
+proc main =
+  raise newException(ValueError, "argh")
+
+var ok = 0
+try:
+  mainA()
+except ValueError:
+  inc ok
+except:
+  discard
+
+try:
+  main()
+except ValueError:
+  inc ok
+except:
+  discard
+
+#  bug #11577
+
+proc newError*: owned(ref Exception) {.noinline.} =
+  new(result)
+
+proc mainC =
+  raise newError()
+
+try:
+  mainC()
+except:
+  inc ok
+
+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
new file mode 100644
index 000000000..f13868110
--- /dev/null
+++ b/tests/destructor/twidgets.nim
@@ -0,0 +1,76 @@
+discard """
+  cmd: '''nim c -d:nimAllocStats --newruntime $file'''
+  output: '''button
+clicked!
+(allocCount: 4, deallocCount: 4)'''
+"""
+
+import system / ansi_c
+
+type
+  Widget* = ref object of RootObj
+    drawImpl: owned(proc (self: Widget))
+
+  Button* = ref object of Widget
+    caption: string
+    onclick: owned(proc())
+
+  Window* = ref object of Widget
+    elements: seq[owned Widget]
+
+
+proc newButton(caption: string; onclick: owned(proc())): owned Button =
+  proc draw(self: Widget) =
+    let b = Button(self)
+    echo b.caption
+
+  #result = Button(drawImpl: draw, caption: caption, onclick: onclick)
+  new(result)
+  result.drawImpl = draw
+  result.caption = caption
+  result.onclick = onclick
+
+iterator unitems*[T](a: seq[owned T]): T {.inline.} =
+  ## Iterates over each item of `a`.
+  var i = 0
+  let L = len(a)
+  while i < L:
+    yield a[i]
+    inc(i)
+    assert(len(a) == L, "the length of the seq changed while iterating over it")
+
+proc newWindow(): owned Window =
+  proc windraw(self: Widget) =
+    let w = Window(self)
+    for i in 0..<len(w.elements):
+      let e = Widget(w.elements[i])
+      let d = (proc(self: Widget))e.drawImpl
+      if not d.isNil: d(e)
+
+  result = Window(drawImpl: windraw, elements: @[])
+
+proc draw(w: Widget) =
+  let d = (proc(self: Widget))w.drawImpl
+  if not d.isNil: d(w)
+
+proc add*(w: Window; elem: owned Widget) =
+  w.elements.add elem
+
+proc main =
+  var w = newWindow()
+
+  var b = newButton("button", nil)
+  let u: Button = b
+  b.onclick = proc () =
+    u.caption = "clicked!"
+  w.add b
+
+  w.draw()
+  # simulate button click:
+  u.onclick()
+
+  w.draw()
+
+dumpAllocstats:
+  main()
+
diff --git a/tests/destructor/twidgets_unown.nim b/tests/destructor/twidgets_unown.nim
new file mode 100644
index 000000000..8653d5c28
--- /dev/null
+++ b/tests/destructor/twidgets_unown.nim
@@ -0,0 +1,72 @@
+discard """
+  cmd: '''nim c -d:nimAllocStats --newruntime $file'''
+  output: '''button
+clicked!
+(allocCount: 6, deallocCount: 6)'''
+"""
+
+import system / ansi_c
+
+type
+  Widget* = ref object of RootObj
+    drawImpl: owned(proc (self: Widget))
+
+  Button* = ref object of Widget
+    caption: string
+    onclick: owned(proc())
+
+  Window* = ref object of Widget
+    elements: seq[owned Widget]
+
+
+proc newButton(caption: string; onclick: owned(proc())): owned Button =
+  proc draw(self: Widget) =
+    let b = Button(self)
+    echo b.caption
+
+  #result = Button(drawImpl: draw, caption: caption, onclick: onclick)
+  new(result)
+  result.drawImpl = draw
+  result.caption = caption
+  result.onclick = onclick
+
+proc newWindow(): owned Window =
+  proc windraw(self: Widget) =
+    let w = Window(self)
+    for e in unown(w.elements):
+      let d = unown e.drawImpl
+      if not d.isNil: d(e)
+
+  result = Window(drawImpl: windraw, elements: @[])
+
+proc draw(w: Widget) =
+  let d = unown w.drawImpl
+  if not d.isNil: d(w)
+
+proc add*(w: Window; elem: owned Widget) =
+  w.elements.add elem
+
+proc main =
+  var w = newWindow()
+
+  var b = newButton("button", nil)
+  let u = unown b
+  var clicked = "clicked"
+  b.onclick = proc () =
+    clicked.add "!"
+    u.caption = clicked
+  w.add b
+
+  w.draw()
+  # simulate button click:
+  u.onclick()
+
+  w.draw()
+
+  # bug #11257
+  var a: owned proc()
+  if a != nil:
+    a()
+
+dumpAllocStats:
+  main()