summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/semasgn.nim13
-rw-r--r--compiler/semtypinst.nim5
-rw-r--r--tests/destructor/tcustomseqs.nim126
3 files changed, 133 insertions, 11 deletions
diff --git a/compiler/semasgn.nim b/compiler/semasgn.nim
index 19d31ec73..cb4728097 100644
--- a/compiler/semasgn.nim
+++ b/compiler/semasgn.nim
@@ -318,16 +318,9 @@ proc overloadedAsgn(c: PContext; dest, src: PNode): PNode =
 proc liftTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) =
   ## In the semantic pass this is called in strategic places
   ## to ensure we lift assignment, destructors and moves properly.
-  ## Since this is done in the sem* routines generics already have
-  ## been resolved for us and do not complicate the logic any further.
-  ## We have to ensure that the 'tfHasDesctructor' flags bubbles up
-  ## in the generic instantiations though.
   ## The later 'destroyer' pass depends on it.
   if not newDestructors or not hasDestructor(typ): return
   # we generate the destructor first so that other operators can depend on it:
-  if typ.destructor == nil:
-    liftBody(c, typ, attachedDestructor, info)
-  if typ.assignment == nil:
-    liftBody(c, typ, attachedAsgn, info)
-  if typ.sink == nil:
-    liftBody(c, typ, attachedSink, info)
+  if typ.destructor == nil: liftBody(c, typ, attachedDestructor, info)
+  if typ.assignment == nil: liftBody(c, typ, attachedAsgn, info)
+  if typ.sink == nil: liftBody(c, typ, attachedSink, info)
diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim
index 5d0f0177c..3b99ea562 100644
--- a/compiler/semtypinst.nim
+++ b/compiler/semtypinst.nim
@@ -363,7 +363,10 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
         # '=' needs to be instantiated for generics when the type is constructed:
         newbody.field = cl.c.instTypeBoundOp(cl.c, opr, result, cl.info,
                                              attachedAsgn, 1)
-    if not newDestructors: typeBound(assignment)
+    if newDestructors:
+      typeBound(destructor)
+      typeBound(sink)
+    typeBound(assignment)
     let methods = skipTypes(bbody, abstractPtrs).methods
     for col, meth in items(methods):
       # we instantiate the known methods belonging to that type, this causes
diff --git a/tests/destructor/tcustomseqs.nim b/tests/destructor/tcustomseqs.nim
new file mode 100644
index 000000000..45059d7de
--- /dev/null
+++ b/tests/destructor/tcustomseqs.nim
@@ -0,0 +1,126 @@
+discard """
+  output: '''1
+2
+3
+4
+5
+6
+after 1 1'''
+  cmd: '''nim c --newruntime $file'''
+"""
+
+import typetraits
+
+type
+  myseq*[T] = object
+    len, cap: int
+    data: ptr UncheckedArray[T]
+
+# XXX make code memory safe for overflows in '*'
+var
+  allocCount, deallocCount: int
+
+proc `=destroy`*[T](x: var myseq[T]) =
+  if x.data != nil:
+    when not supportsCopyMem(T):
+      for i in 0..<x.len: `=destroy`(x[i])
+    dealloc(x.data)
+    inc deallocCount
+    x.data = nil
+    x.len = 0
+    x.cap = 0
+
+proc `=`*[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
+  a.len = b.len
+  a.cap = b.cap
+  if b.data != nil:
+    a.data = cast[type(a.data)](alloc(a.cap * sizeof(T)))
+    inc allocCount
+    when supportsCopyMem(T):
+      copyMem(a.data, b.data, a.cap * sizeof(T))
+    else:
+      for i in 0..<a.len:
+        a.data[i] = b.data[i]
+
+proc `=sink`*[T](a: var myseq[T]; b: myseq[T]) =
+  if a.data != nil and a.data != b.data:
+    dealloc(a.data)
+    inc deallocCount
+  a.len = b.len
+  a.cap = b.cap
+  a.data = b.data
+
+proc resize[T](s: var seq[T]) =
+  if s.cap == 0: s.cap = 8
+  else: s.cap = (s.cap * 3) shr 1
+  if s.data == nil: inc allocCount
+  s.data = cast[type(s.data)](realloc(s.data, s.cap * sizeof(T)))
+
+proc reserveSlot[T](x: var seq[T]): ptr T =
+  if x.len >= x.cap: resize(x)
+  result = addr(x.data[x.len])
+  inc x.len
+
+template add*[T](x: var seq[T]; y: T) =
+  reserveSlot(x)[] = y
+
+proc shrink*[T](x: var myseq[T]; newLen: int) =
+  assert newLen <= x.len
+  assert newLen >= 0
+  when not supportsCopyMem(T):
+    for i in countdown(x.len - 1, newLen - 1):
+      `=destroy`(x.data[i])
+  x.len = newLen
+
+proc grow*[T](x: var myseq[T]; newLen: int; value: T) =
+  if newLen <= x.len: return
+  assert newLen >= 0
+  if x.cap == 0: x.cap = newLen
+  else: x.cap = max(newLen, (x.cap * 3) shr 1)
+  if x.data == nil: inc allocCount
+  x.data = cast[type(x.data)](realloc(x.data, x.cap * sizeof(T)))
+  for i in x.len..<newLen:
+    x.data[i] = value
+  x.len = newLen
+
+template default[T](t: typedesc[T]): T =
+  var v: T
+  v
+
+proc setLen*[T](x: var myseq[T]; newLen: int) {.deprecated.} =
+  if newlen < x.len: shrink(x, newLen)
+  else: grow(x, newLen, default(T))
+
+template `[]`*[T](x: myseq[T]; i: Natural): T =
+  assert i < x.len
+  x.data[i]
+
+template `[]=`*[T](x: myseq[T]; i: Natural; y: T) =
+  assert i < x.len
+  x.data[i] = y
+
+proc createSeq*[T](elems: varargs[T]): myseq[T] =
+  result.cap = elems.len
+  result.len = elems.len
+  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))
+  else:
+    for i in 0..<result.len:
+      result.data[i] = elems[i]
+
+proc len*[T](x: myseq[T]): int {.inline.} = x.len
+
+proc main =
+  let s = createSeq(1, 2, 3, 4, 5, 6)
+  for i in 0 ..< s.len:
+    echo s[i]
+
+main()
+echo "after ", allocCount, " ", deallocCount