discard """
  output: '''allocating
allocating
allocating
55
60
99
deallocating
deallocating
deallocating
allocating
deallocating
'''
joinable: false
"""

type
  SharedPtr*[T] = object
    x: ptr T

#proc isNil[T](s: SharedPtr[T]): bool {.inline.} = s.x.isNil

template incRef(x) =
  atomicInc(x.refcount)

template decRef(x): untyped = atomicDec(x.refcount)

proc makeShared*[T](x: sink T): SharedPtr[T] =
  # XXX could benefit from a macro that generates it.
  result = cast[SharedPtr[T]](allocShared(sizeof(x)))
  result.x[] = x
  echo "allocating"

proc `=destroy`*[T](dest: var SharedPtr[T]) =
  var s = dest.x
  if s != nil and decRef(s) == 0:
    `=destroy`(s[])
    deallocShared(s)
    echo "deallocating"
    dest.x = nil

proc `=`*[T](dest: var SharedPtr[T]; src: SharedPtr[T]) =
  var s = src.x
  if s != nil: incRef(s)
  #atomicSwap(dest, s)
  # XXX use an atomic store here:
  swap(dest.x, s)
  if s != nil and decRef(s) == 0:
    `=destroy`(s[])
    deallocShared(s)
    echo "deallocating"

proc `=sink`*[T](dest: var SharedPtr[T]; src: SharedPtr[T]) =
  ## XXX make this an atomic store:
  if dest.x != src.x:
    let s = dest.x
    if s != nil:
      `=destroy`(s[])
      deallocShared(s)
      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

template `.=`*[T](s: SharedPtr[T]; field, value: untyped) =
  s.x.field = value

from macros import unpackVarargs

template `.()`*[T](s: SharedPtr[T]; field: untyped, args: varargs[untyped]): untyped =
  unpackVarargs(s.x.field, args)


type
  Tree = SharedPtr[TreeObj]
  TreeObj = object
    refcount: int
    le, ri: Tree
    data: int

proc takesTree(a: Tree) =
  if not a.isNil:
    takesTree(a.le)
    echo a.data
    takesTree(a.ri)

proc createTree(data: int): Tree =
  result = makeShared(TreeObj(refcount: 1, data: data))

proc createTree(data: int; le, ri: Tree): Tree =
  result = makeShared(TreeObj(refcount: 1, le: le, ri: ri, data: data))


proc main =
  let le = createTree(55)
  let ri = createTree(99)
  let t = createTree(60, le, ri)
  takesTree(t)

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 `=`*[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 `=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

proc len*[T](m: MySeq[T]): int {.inline.} = m.len

proc newMySeq*[T](size: int, initial_value: T): MySeq[T] =
  result.len = size
  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
