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