diff options
Diffstat (limited to 'tests/arc')
62 files changed, 2839 insertions, 109 deletions
diff --git a/tests/arc/t16458.nim b/tests/arc/t16458.nim new file mode 100644 index 000000000..6ae114287 --- /dev/null +++ b/tests/arc/t16458.nim @@ -0,0 +1,6 @@ +discard """ + matrix: "--gc:orc --d:useNimRtl" + action: "compile" +""" + +echo 134 \ No newline at end of file diff --git a/tests/arc/t16558.nim b/tests/arc/t16558.nim new file mode 100644 index 000000000..0dbe02b33 --- /dev/null +++ b/tests/arc/t16558.nim @@ -0,0 +1,9 @@ +discard """ + matrix: "--gc:arc" + errormsg: "expression cannot be cast to 'int'" +""" + +block: # bug #16558 + var value = "hi there" + var keepInt: int + keepInt = cast[int](value) diff --git a/tests/arc/t17812.nim b/tests/arc/t17812.nim new file mode 100644 index 000000000..dd8ac89b0 --- /dev/null +++ b/tests/arc/t17812.nim @@ -0,0 +1,41 @@ +discard """ + targets: "c js" + matrix: "--gc:refc; --gc:arc" +""" + +import std/times + +block: # bug #17812 + block: + type + Task = object + cb: proc () + + proc hello() = discard + + + let t = Task(cb: hello) + + doAssert t.repr.len > 0 + + + block: + type MyObj = object + field: DateTime + + + proc `$`(o: MyObj): string = o.repr + + doAssert ($MyObj()).len > 0 + +# bug #22175 + +type Xxx = object + value: string + +proc complete(xxx: ref Xxx, v: sink string) = + xxx.value = move(v) + +let yyy = (ref Xxx)() + +yyy.complete("test") diff --git a/tests/arc/t18645.nim b/tests/arc/t18645.nim new file mode 100644 index 000000000..c5fddd4bb --- /dev/null +++ b/tests/arc/t18645.nim @@ -0,0 +1,18 @@ +discard """ + matrix: "--gc:arc; --gc:refc" + output: ''' +1 +2 +3 +''' +""" + +proc bitTypeIdUnion() = + var bitId {.global.} = block: + 0 + inc bitId + echo bitId + +bitTypeIdUnion() +bitTypeIdUnion() +bitTypeIdUnion() diff --git a/tests/arc/t18971.nim b/tests/arc/t18971.nim new file mode 100644 index 000000000..9b587d956 --- /dev/null +++ b/tests/arc/t18971.nim @@ -0,0 +1,10 @@ +discard """ + cmd: "nim c --gc:arc $file" +""" + +type MyObj = ref object + +var o = MyObj() +proc x: var MyObj = o + +var o2 = x() diff --git a/tests/arc/t18977.nim b/tests/arc/t18977.nim new file mode 100644 index 000000000..c775551a4 --- /dev/null +++ b/tests/arc/t18977.nim @@ -0,0 +1,26 @@ +discard """ + matrix: "--mm:arc" +""" + +type + E = enum + a, b, c, d + X = object + v: int + O = object + case kind: E + of a: + a: int + of {b, c}: + b: float + else: + d: X + +proc `=destroy`(x: var X) = + echo "x destroyed" + +var o = O(kind: d, d: X(v: 12345)) +doAssert o.d.v == 12345 + +doAssertRaises(FieldDefect): + o.kind = a diff --git a/tests/arc/t19231.nim b/tests/arc/t19231.nim new file mode 100644 index 000000000..40fcf277c --- /dev/null +++ b/tests/arc/t19231.nim @@ -0,0 +1,18 @@ +discard """ + matrix: "--mm:orc" + targets: "c cpp" +""" + +type + Game* = ref object + +proc free*(game: Game) = + var mixNumOpened:cint = 0 + for i in 0..<mixNumOpened: + mixNumOpened -= 1 + +proc newGame*(): Game = + new result, free + +var + game*: Game diff --git a/tests/arc/t19364.nim b/tests/arc/t19364.nim new file mode 100644 index 000000000..f520f3291 --- /dev/null +++ b/tests/arc/t19364.nim @@ -0,0 +1,30 @@ +discard """ + cmd: '''nim c --gc:arc --expandArc:fooLeaks $file''' + nimout: ''' +--expandArc: fooLeaks + +var + tmpTuple_cursor + a_cursor + b_cursor + c_cursor +tmpTuple_cursor = refTuple +a_cursor = tmpTuple_cursor[0] +b_cursor = tmpTuple_cursor[1] +c_cursor = tmpTuple_cursor[2] +-- end of expandArc ------------------------ +''' +""" + +func fooLeaks(refTuple: tuple[a, + b, + c: seq[float]]): float = + let (a, b, c) = refTuple + +let refset = (a: newSeq[float](25_000_000), + b: newSeq[float](25_000_000), + c: newSeq[float](25_000_000)) + +var res = newSeq[float](1_000_000) +for i in 0 .. res.high: + res[i] = fooLeaks(refset) diff --git a/tests/arc/t19401.nim b/tests/arc/t19401.nim new file mode 100644 index 000000000..56702a4a2 --- /dev/null +++ b/tests/arc/t19401.nim @@ -0,0 +1,32 @@ +discard """ + output: ''' +delete foo +delete foo +delete foo +''' + matrix: "--mm:arc" +""" + +type Foo = ref object + data: int +proc delete(self: Foo) +proc newFoo: Foo = + let x = 12 + discard x + new(result, delete) + result.data = x +proc delete(self: Foo) = + doAssert self.data == 12 + echo("delete foo") + +if isMainModule: + proc test() = + let x1 = newFoo() + let x2 = newFoo() + discard x1 + discard x2 + var x3: Foo + new(x3, delete) + x3.data = 12 + discard x3 + test() diff --git a/tests/arc/t19402.nim b/tests/arc/t19402.nim new file mode 100644 index 000000000..5ee6fc798 --- /dev/null +++ b/tests/arc/t19402.nim @@ -0,0 +1,32 @@ +discard """ + output: ''' +delete foo +delete foo +delete foo +''' + matrix: "--mm:arc" +""" + +type Foo = ref object of RootObj + data: int +proc delete(self: Foo) +proc newFoo: Foo = + let x = 12 + discard x + new(result, delete) + result.data = x +proc delete(self: Foo) = + doAssert self.data == 12 + echo("delete foo") + +if isMainModule: + proc test() = + let x1 = newFoo() + let x2 = newFoo() + discard x1 + discard x2 + var x3: Foo + new(x3, delete) + x3.data = 12 + discard x3 + test() \ No newline at end of file diff --git a/tests/arc/t19435.nim b/tests/arc/t19435.nim new file mode 100644 index 000000000..519216bad --- /dev/null +++ b/tests/arc/t19435.nim @@ -0,0 +1,29 @@ +discard """ + matrix: "--gc:arc" +""" + +# bug #19435 +{.experimental: "views".} + +type + Bar = object + placeholder: int + Foo = object + placeholder: int + c: seq[Bar] # remove this line to make things right + +func children*(s: var seq[Foo]): openArray[Foo] = + s.toOpenArray(0, s.len-1) + +proc test = + var foos = @[Foo(), Foo()] + + assert foos.children.len == 2 + var flag = true + for a in foos.children: + flag = false + + if flag: + doAssert false + +test() \ No newline at end of file diff --git a/tests/arc/t19457.nim b/tests/arc/t19457.nim new file mode 100644 index 000000000..78447ce82 --- /dev/null +++ b/tests/arc/t19457.nim @@ -0,0 +1,16 @@ +discard """ + matrix: "--gc:refc; --gc:arc" +""" + +# bug #19457 +proc gcd(x, y: seq[int]): seq[int] = + var + a = x + b = y + while b[0] > 0: + let c = @[a[0] mod b[0]] + a = b + b = c + return a + +doAssert gcd(@[1], @[2]) == @[1] \ No newline at end of file diff --git a/tests/arc/t19862.nim b/tests/arc/t19862.nim new file mode 100644 index 000000000..6d3f57692 --- /dev/null +++ b/tests/arc/t19862.nim @@ -0,0 +1,15 @@ +discard """ + matrix: "--gc:refc; --gc:arc" +""" + +import std/widestrs + +# bug #19862 +type NewString = object + +proc len(s: NewString): int = 10 + +converter toNewString(x: WideCStringObj): NewString = discard + +let w = newWideCString("test") +doAssert len(w) == 4 diff --git a/tests/arc/t20456.nim b/tests/arc/t20456.nim new file mode 100644 index 000000000..ace79255a --- /dev/null +++ b/tests/arc/t20456.nim @@ -0,0 +1,7 @@ +discard """ + cmd: "nim check $file" + action: "compile" +""" + +when not defined(gcOrc): + {.error: "orc".} diff --git a/tests/arc/t20588.nim b/tests/arc/t20588.nim new file mode 100644 index 000000000..008bd1dcd --- /dev/null +++ b/tests/arc/t20588.nim @@ -0,0 +1,25 @@ +discard """ + cmd: "nim check --warnings:off --hints:off $file" + errormsg: "" + nimout: ''' +t20588.nim(20, 12) Error: illegal type conversion to 'auto' +t20588.nim(21, 14) Error: illegal type conversion to 'typed' +t20588.nim(22, 16) Error: illegal type conversion to 'untyped' +t20588.nim(24, 7) Error: illegal type conversion to 'any' +''' +""" + + + + + + + + + +discard 0.0.auto +discard typed("abc") +discard untyped(4) +var a = newSeq[bool](1000) +if any(a): + echo "ok?" \ No newline at end of file diff --git a/tests/arc/t21184.nim b/tests/arc/t21184.nim new file mode 100644 index 000000000..91d0c42c7 --- /dev/null +++ b/tests/arc/t21184.nim @@ -0,0 +1,77 @@ +discard """ + matrix: "--mm:orc" +""" + +import std/[with] + +type + Node* {.acyclic.} = ref object of RootObj + name: string + data: pointer + children: seq[Node] + TextNode = ref object of Node + text: string + +proc fakeEcho(s: string) = + if s.len < 0: + echo s + +proc newNode[T: Node](parent: Node): T = + new result + result.data = alloc0(250) + parent.children.add(result) + +proc newRootNode(): Node = + new result + result.data = alloc0(250) + +method printNode(node: Node) {.base.} = + fakeEcho node.name + +method printNode(node: TextNode) = + procCall printNode(Node(node)) + fakeEcho node.text + +proc printChildren(node: Node) = + for child in node.children: + child.printNode() + printChildren(child) + +proc free(node: Node) = + for child in node.children: + free(child) + dealloc(node.data) + +template node(parent: Node, body: untyped): untyped = + var node = newNode[Node](parent) + with node: + body + +proc textNode(parent: Node, text: string) = + var node = newNode[TextNode](parent) + node.text = text + +template withRootNode(body: untyped): untyped = + var root = newRootNode() + root.name = "root" + with root: + body + root.printNode() + printChildren(root) + root.free() + +proc doTest() = + withRootNode: + node: + name = "child1" + node: + name = "child2" + node: + name = "child3" + textNode "Hello, world!" + + +# bug #21171 +if isMainModule: + for i in 0..100000: + doTest() diff --git a/tests/arc/t22218.nim b/tests/arc/t22218.nim new file mode 100644 index 000000000..7837ed1d0 --- /dev/null +++ b/tests/arc/t22218.nim @@ -0,0 +1,25 @@ +discard """ + cmd: "nim c --mm:arc $file" + errormsg: "'=copy' is not available for type <Obj>; requires a copy because it's not the last read of 'chan[]'; routine: test" +""" + +# bug #22218 +type Obj[T] = object + v: T + +proc `=copy`[T]( + dest: var Obj[T], + source: Obj[T] + ) {.error: "A channel cannot be copied".} + +from system/ansi_c import c_calloc + +proc test() = + var v: bool = true + var chan = cast[ptr Obj[int]](c_calloc(1, csize_t sizeof(Obj[int]))) + var copy = chan[] + + echo chan.v + echo v + +test() \ No newline at end of file diff --git a/tests/arc/t22237.nim b/tests/arc/t22237.nim new file mode 100644 index 000000000..c6dbf768a --- /dev/null +++ b/tests/arc/t22237.nim @@ -0,0 +1,55 @@ +discard """ + matrix: "--mm:arc; --mm:orc" +""" + +import std/macros +import std/streams + +# bug #22237 + +proc iterlines_closure2(f: File | Stream): iterator (): string = + result = iterator(): string = + for line in f.lines: + if line.len == 0: + break + yield line + +proc test() = + let f = newStringStream(""" + 1 + 2 + + 3 + 4 + + 5 + 6 + 7 + + 8 +""") + while not f.atEnd(): + let iterator_inst = iterlines_closure2(f) + for item in iterator_inst(): # Fails with "SIGSEGV: Illegal storage access. (Attempt to read from nil?)" + discard + +test() + +# bug #21160 +import sequtils + +iterator allMoves(fls: seq[int]): seq[int] = + yield fls + +proc neighbors(flrs: seq[int]): iterator: seq[int] = + return iterator(): seq[int] = + for flrs2 in allMoves(flrs): + yield flrs2 + for flrs3 in allMoves(flrs2): + yield flrs3 + +let f = @[1] +for _ in neighbors(f): + discard +for _ in neighbors(f): + discard diff --git a/tests/arc/t22478.nim b/tests/arc/t22478.nim new file mode 100644 index 000000000..5373fa161 --- /dev/null +++ b/tests/arc/t22478.nim @@ -0,0 +1,46 @@ +discard """ + matrix: "-d:nimNoLentIterators --mm:arc" + output: '''PUSH DATA: {"test.message":{"test":{"nested":"v1"}}}''' + joinable: false +""" + +# bug #22748 +import std/[json, typetraits, times] + +# publish + +proc publish*[T](payload: T) = + discard + +type MetricsPoint* = JsonNode + +proc push*(stat: string, data: JsonNode, usec: int64 = 0) = + let payload = newJObject() + + # this results in a infinite recursion unless we deepCopy() + payload[stat] = data #.deepCopy + + echo "PUSH DATA: ", payload + + publish[MetricsPoint](payload) + +var scopes {.threadvar.}: seq[JsonNode] + +type WithTimeCallback*[T] = proc(data: var JsonNode): T + +proc pushScoped*[T](metric: string, blk: WithTimeCallback[T]): T {.gcsafe.} = + scopes.add newJObject() + defer: discard scopes.pop() + + let stc = (cpuTime() * 1000_000).int64 + result = blk(scopes[^1]) + let dfc = (cpuTime() * 1000_000).int64 - stc + + push(metric, scopes[^1], dfc) + +# demo code + +discard pushScoped[int]("test.message") do (data: var JsonNode) -> int: + data["test"] = %*{ + "nested": "v1" + } \ No newline at end of file diff --git a/tests/arc/t22787.nim b/tests/arc/t22787.nim new file mode 100644 index 000000000..5840a984b --- /dev/null +++ b/tests/arc/t22787.nim @@ -0,0 +1,37 @@ +discard """ + joinable: false +""" + +import std/assertions + +proc foo = + var s:seq[string] + var res = "" + + for i in 0..3: + s.add ("test" & $i) + s.add ("test" & $i) + + var lastname:string + + for i in s: + var name = i[0..4] + + if name != lastname: + res.add "NEW:" & name & "\n" + else: + res.add name & ">" & lastname & "\n" + + lastname = name + + doAssert res == """ +NEW:test0 +test0>test0 +NEW:test1 +test1>test1 +NEW:test2 +test2>test2 +NEW:test3 +test3>test3 +""" +foo() \ No newline at end of file diff --git a/tests/arc/t23247.nim b/tests/arc/t23247.nim new file mode 100644 index 000000000..0fadc50cd --- /dev/null +++ b/tests/arc/t23247.nim @@ -0,0 +1,52 @@ +discard """ + matrix: ";-d:useMalloc" +""" + +# bug #23247 +import std/hashes + +func baseAddr[T](x: openArray[T]): ptr T = + # Return the address of the zero:th element of x or `nil` if x is empty + if x.len == 0: nil else: cast[ptr T](x) + +func makeUncheckedArray[T](p: ptr T): ptr UncheckedArray[T] = + cast[ptr UncheckedArray[T]](p) + +type + LabelKey = object + data: seq[string] + refs: ptr UncheckedArray[string] + refslen: int + + Gauge = ref object + metrics: seq[seq[seq[string]]] + +template values(key: LabelKey): openArray[string] = + if key.refslen > 0: + key.refs.toOpenArray(0, key.refslen - 1) + else: + key.data + +proc hash(key: LabelKey): Hash = + hash(key.values) + +proc view(T: type LabelKey, values: openArray[string]): T = + # TODO some day, we might get view types - until then.. + LabelKey(refs: baseAddr(values).makeUncheckedArray(), refslen: values.len()) + +template withValue2(k: untyped) = + discard hash(k) + +proc setGauge( + collector: Gauge, + labelValues: openArray[string], +) = + let v = LabelKey.view(labelValues) + withValue2(v) + collector.metrics.add @[@labelValues, @labelValues] + discard @labelValues + +var nim_gc_mem_bytes = Gauge() +let threadID = $getThreadId() +setGauge(nim_gc_mem_bytes, @[threadID]) +setGauge(nim_gc_mem_bytes, @[threadID]) \ No newline at end of file diff --git a/tests/arc/t9650.nim b/tests/arc/t9650.nim new file mode 100644 index 000000000..a8182db68 --- /dev/null +++ b/tests/arc/t9650.nim @@ -0,0 +1,87 @@ +discard """ + matrix: "--gc:arc" +""" + +import typetraits + +# bug #9650 +type + SharedPtr*[T] = object + val: ptr tuple[atomicCounter: int, value: T] + + Node*[T] = object + value: T + next: SharedPtr[Node[T]] + + ForwardList*[T] = object + first: SharedPtr[Node[T]] + +proc `=destroy`*[T](p: var SharedPtr[T]) = + if p.val != nil: + let c = atomicDec(p.val[].atomicCounter) + if c == 0: + when not supportsCopyMem(T): + `=destroy`(p.val[]) + dealloc(p.val) + p.val = nil + +proc `=`*[T](dest: var SharedPtr[T], src: SharedPtr[T]) = + if dest.val != src.val: + if dest.val != nil: + `=destroy`(dest) + if src.val != nil: + discard atomicInc(src.val[].atomicCounter) + dest.val = src.val + +proc `=sink`*[T](dest: var SharedPtr[T], src: SharedPtr[T]) = + if dest.val != nil and dest.val != src.val: + `=destroy`(dest) + dest.val = src.val + + +proc newSharedPtr*[T](val: sink T): SharedPtr[T] = + result.val = cast[type(result.val)](alloc(sizeof(result.val[]))) + reset(result.val[]) + result.val.atomicCounter = 1 + result.val.value = val + +proc isNil*[T](p: SharedPtr[T]): bool = + p.val == nil + +template `->`*[T](p: SharedPtr[T], name: untyped): untyped = + p.val.value.name + +proc createNode[T](val: T): SharedPtr[ Node[T] ]= + result = newSharedPtr(Node[T](value: val)) + +proc push_front*[T](list: var ForwardList[T], val: T) = + var newElem = createNode(val) + newElem->next = list.first + list.first = newElem + +proc pop_front*[T](list: var ForwardList[T]) = + let head = list.first + list.first = head->next + +proc toString*[T](list: ForwardList[T]): string = + result = "[" + var head = list.first + while not head.isNil: + result &= $(head->value) & ", " + head = head->next + result &= ']' + +block: + var x: ForwardList[int] + x.push_front(1) + x.push_front(2) + x.push_front(3) + + doAssert toString(x) == "[3, 2, 1, ]" + + x.pop_front() + x.pop_front() + doAssert toString(x) == "[1, ]" + + x.pop_front() + doAssert toString(x) == "[]" diff --git a/tests/arc/taliased_reassign.nim b/tests/arc/taliased_reassign.nim new file mode 100644 index 000000000..5563fae8c --- /dev/null +++ b/tests/arc/taliased_reassign.nim @@ -0,0 +1,41 @@ +discard """ + matrix: "--mm:orc" +""" + +# bug #20993 + +type + Dual[int] = object # must be generic (even if fully specified) + p: int +proc D(p: int): Dual[int] = Dual[int](p: p) +proc `+`(x: Dual[int], y: Dual[int]): Dual[int] = D(x.p + y.p) + +type + Tensor[T] = object + buf: seq[T] +proc newTensor*[T](s: int): Tensor[T] = Tensor[T](buf: newSeq[T](s)) +proc `[]`*[T](t: Tensor[T], idx: int): T = t.buf[idx] +proc `[]=`*[T](t: var Tensor[T], idx: int, val: T) = t.buf[idx] = val + +proc `+.`[T](t1, t2: Tensor[T]): Tensor[T] = + let n = t1.buf.len + result = newTensor[T](n) + for i in 0 ..< n: + result[i] = t1[i] + t2[i] + +proc toTensor*[T](a: sink seq[T]): Tensor[T] = + ## This breaks it: Using `T` instead makes it work + type U = typeof(a[0]) + var t: Tensor[U] # Tensor[T] works + t.buf = a + result = t + +proc loss() = + var B = toTensor(@[D(123)]) + let a = toTensor(@[D(-10)]) + B = B +. a + doAssert B[0].p == 113, "I want to be 113, but I am " & $B[0].p + +loss() + + diff --git a/tests/arc/tarc_macro.nim b/tests/arc/tarc_macro.nim new file mode 100644 index 000000000..33ade1da4 --- /dev/null +++ b/tests/arc/tarc_macro.nim @@ -0,0 +1,57 @@ +import macros + +var destroyCalled = false +macro bar() = + let s = newTree(nnkAccQuoted, ident"=destroy") + # let s = ident"`=destroy`" # this would not work + result = quote do: + type Foo = object + # proc `=destroy`(a: var Foo) = destroyCalled = true # this would not work + proc `s`(a: var Foo) = destroyCalled = true + block: + let a = Foo() +bar() +doAssert destroyCalled + +# custom `op` +var destroyCalled2 = false +macro bar(ident) = + var x = 1.5 + result = quote("@") do: + type Foo = object + let `@ident` = 0 # custom op interpolated symbols need quoted (``) + proc `=destroy`(a: var Foo) = + doAssert @x == 1.5 + doAssert compiles(@x == 1.5) + let b1 = @[1,2] + let b2 = @@[1,2] + doAssert $b1 == "[1, 2]" + doAssert $b2 == "@[1, 2]" + destroyCalled2 = true + block: + let a = Foo() +bar(someident) +doAssert destroyCalled2 + +proc `&%`(x: int): int = 1 +proc `&%`(x, y: int): int = 2 + +macro bar2() = + var x = 3 + result = quote("&%") do: + var y = &%x # quoting operator + doAssert &%&%y == 1 # unary operator => need to escape + doAssert y &% y == 2 # binary operator => no need to escape + doAssert y == 3 +bar2() + +block: + macro foo(a: openArray[string] = []): string = + echo a # Segfault doesn't happen if this is removed + newLit "" + + proc bar(a: static[openArray[string]] = []) = + const tmp = foo(a) + + # bug #22909 + doAssert not compiles(bar()) diff --git a/tests/arc/tarc_orc.nim b/tests/arc/tarc_orc.nim new file mode 100644 index 000000000..f2c7de2fc --- /dev/null +++ b/tests/arc/tarc_orc.nim @@ -0,0 +1,186 @@ +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: var 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: var 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() + +block: + type + TestObj = object of RootObj + name: string + + TestSubObj = object of TestObj + objname: string + + proc `=destroy`(x: TestObj) = + `=destroy`(x.name) + + proc `=destroy`(x: TestSubObj) = + `=destroy`(x.objname) + `=destroy`(TestObj(x)) + + proc testCase() = + let t1 {.used.} = TestSubObj(objname: "tso1", name: "to1") + + proc main() = + testCase() + + main() + +block: # bug #23858 + type Object = object + a: int + b: ref int + var x = 0 + proc fn(): auto {.cdecl.} = + inc x + return Object() + discard fn() + doAssert x == 1 + +block: # bug #24147 + type + O = object of RootObj + val: string + OO = object of O + + proc `=copy`(dest: var O, src: O) = + dest.val = src.val + + let oo = OO(val: "hello world") + var ooCopy : OO + `=copy`(ooCopy, oo) diff --git a/tests/arc/tarcmisc.nim b/tests/arc/tarcmisc.nim index 55803085f..b4476ef4f 100644 --- a/tests/arc/tarcmisc.nim +++ b/tests/arc/tarcmisc.nim @@ -1,5 +1,7 @@ discard """ output: ''' +Destructor for TestTestObj +=destroy called 123xyzabc destroyed: false destroyed: false @@ -26,14 +28,90 @@ new line after - @['a'] finalizer aaaaa hello +true +copying +123 +42 +@["", "d", ""] ok -closed destroying variable: 20 destroying variable: 10 +closed ''' - cmd: "nim c --gc:arc --deepcopy:on $file" + cmd: "nim c --mm:arc --deepcopy:on -d:nimAllocPagesViaMalloc $file" """ +block: # bug #23627 + type + TestObj = object of RootObj + + Test2 = object of RootObj + foo: TestObj + + TestTestObj = object of RootObj + shit: TestObj + + proc `=destroy`(x: TestTestObj) = + echo "Destructor for TestTestObj" + let test = Test2(foo: TestObj()) + + proc testCaseT() = + let tt1 {.used.} = TestTestObj(shit: TestObj()) + + + proc main() = + testCaseT() + + main() + + +# bug #9401 + +type + MyObj = object + len: int + data: ptr UncheckedArray[float] + +proc `=destroy`*(m: MyObj) = + + echo "=destroy called" + + if m.data != nil: + deallocShared(m.data) + +type + MyObjDistinct = distinct MyObj + +proc `=copy`*(m: var MyObj, m2: MyObj) = + if m.data == m2.data: return + if m.data != nil: + `=destroy`(m) + m.len = m2.len + if m.len > 0: + m.data = cast[ptr UncheckedArray[float]](allocShared(sizeof(float) * m.len)) + copyMem(m.data, m2.data, sizeof(float) * m.len) + + +proc `=sink`*(m: var MyObj, m2: MyObj) = + if m.data != m2.data: + if m.data != nil: + `=destroy`(m) + m.len = m2.len + m.data = m2.data + +proc newMyObj(len: int): MyObj = + result.len = len + result.data = cast[ptr UncheckedArray[float]](allocShared(sizeof(float) * len)) + +proc newMyObjDistinct(len: int): MyObjDistinct = + MyObjDistinct(newMyObj(len)) + +proc fooDistinct = + doAssert newMyObjDistinct(2).MyObj.len == 2 + +fooDistinct() + + proc takeSink(x: sink string): bool = true proc b(x: sink string): string = @@ -51,7 +129,7 @@ bbb("123") type Variable = ref object value: int -proc `=destroy`(self: var typeof(Variable()[])) = +proc `=destroy`(self: typeof(Variable()[])) = echo "destroying variable: ",self.value proc newVariable(value: int): Variable = @@ -71,7 +149,7 @@ proc test(count: int) = test(3) proc test2(count: int) = - #block: #XXX: Fails with block currently + block: #XXX: Fails with block currently var v {.global.} = newVariable(20) var count = count - 1 @@ -105,7 +183,7 @@ type B = ref object of A x: int -proc `=destroy`(x: var AObj) = +proc `=destroy`(x: AObj) = close(x.io) echo "closed" @@ -390,3 +468,369 @@ proc newPixelBuffer(): PixelBuffer = discard newPixelBuffer() + +# bug #17199 + +proc passSeq(data: seq[string]) = + # used the system.& proc initially + let wat = data & "hello" + +proc test2 = + let name = @["hello", "world"] + passSeq(name) + doAssert name == @["hello", "world"] + +static: test2() # was buggy +test2() + +proc merge(x: sink seq[string], y: sink string): seq[string] = + newSeq(result, x.len + 1) + for i in 0..x.len-1: + result[i] = move(x[i]) + result[x.len] = move(y) + +proc passSeq2(data: seq[string]) = + # used the system.& proc initially + let wat = merge(data, "hello") + +proc test3 = + let name = @["hello", "world"] + passSeq2(name) + doAssert name == @["hello", "world"] + +static: test3() # was buggy +test3() + +# bug #17712 +proc t17712 = + var ppv = new int + discard @[ppv] + var el: ref int + el = [ppv][0] + echo el != nil + +t17712() + +# bug #18030 + +type + Foo = object + n: int + +proc `=copy`(dst: var Foo, src: Foo) = + echo "copying" + dst.n = src.n + +proc `=sink`(dst: var Foo, src: Foo) = + echo "sinking" + dst.n = src.n + +var a: Foo + +proc putValue[T](n: T) + +proc useForward = + putValue(123) + +proc putValue[T](n: T) = + var b = Foo(n:n) + a = b + echo b.n + +useForward() + + +# bug #17319 +type + BrokenObject = ref object + brokenType: seq[int] + +proc use(obj: BrokenObject) = + discard + +method testMethod(self: BrokenObject) {.base.} = + iterator testMethodIter() {.closure.} = + use(self) + + var nameIterVar = testMethodIter + nameIterVar() + +let mikasa = BrokenObject() +mikasa.testMethod() + +# bug #19205 +type + InputSectionBase* = object of RootObj + relocations*: seq[int] # traced reference. string has a similar SIGSEGV. + InputSection* = object of InputSectionBase + +proc fooz(sec: var InputSectionBase) = + if sec of InputSection: # this line SIGSEGV. + echo 42 + +var sec = create(InputSection) +sec[] = InputSection(relocations: newSeq[int]()) +fooz sec[] + +block: + type + Data = ref object + id: int + proc main = + var x = Data(id: 99) + var y = x + x[] = Data(id: 778)[] + doAssert y.id == 778 + doAssert x[].id == 778 + main() + +block: # bug #19857 + type + ValueKind = enum VNull, VFloat, VObject # need 3 elements. Cannot remove VNull or VObject + + Value = object + case kind: ValueKind + of VFloat: fnum: float + of VObject: tab: Table[int, int] # OrderedTable[T, U] also makes it fail. + # "simpler" types also work though + else: discard # VNull can be like this, but VObject must be filled + + # required. Pure proc works + FormulaNode = proc(c: OrderedTable[string, int]): Value + + proc toF(v: Value): float = + doAssert v.kind == VFloat + case v.kind + of VFloat: result = v.fnum + else: discard + + + proc foo() = + let fuck = initOrderedTable[string, int]() + proc cb(fuck: OrderedTable[string, int]): Value = + # works: + #result = Value(kind: VFloat, fnum: fuck["field_that_does_not_exist"].float) + # broken: + discard "actuall runs!" + let t = fuck["field_that_does_not_exist"] + echo "never runs, but we crash after! ", t + + doAssertRaises(KeyError): + let fn = FormulaNode(cb) + let v = fn(fuck) + #echo v + let res = v.toF() + + foo() + +import std/options + +# bug #21592 +type Event* = object + code*: string + +type App* = ref object of RootObj + id*: string + +method process*(self: App): Option[Event] {.base.} = + raise Exception.new_exception("not impl") + +# bug #21617 +type Test2 = ref object of RootObj + +method bug(t: Test2): seq[float] {.base.} = discard + +block: # bug #22664 + type + ElementKind = enum String, Number + Element = object + case kind: ElementKind + of String: + str: string + of Number: + num: float + Calc = ref object + stack: seq[Element] + + var calc = new Calc + + calc.stack.add Element(kind: Number, num: 200.0) + doAssert $calc.stack == "@[(kind: Number, num: 200.0)]" + let calc2 = calc + calc2.stack = calc.stack # This nulls out the object in the stack + doAssert $calc.stack == "@[(kind: Number, num: 200.0)]" + doAssert $calc2.stack == "@[(kind: Number, num: 200.0)]" + +block: # bug #19250 + type + Bar[T] = object + err: proc(): string + + Foo[T] = object + run: proc(): Bar[T] + + proc bar[T](err: proc(): string): Bar[T] = + assert not err.isNil + Bar[T](err: err) + + proc foo(): Foo[char] = + result.run = proc(): Bar[char] = + # works + # result = Bar[char](err: proc(): string = "x") + # not work + result = bar[char](proc(): string = "x") + + proc bug[T](fs: Foo[T]): Foo[T] = + result.run = proc(): Bar[T] = + let res = fs.run() + + # works + # var errors = @[res.err] + + # not work + var errors: seq[proc(): string] + errors.add res.err + + return bar[T] do () -> string: + for err in errors: + result.add res.err() + + doAssert bug(foo()).run().err() == "x" + +block: # bug #22259 + type + ProcWrapper = tuple + p: proc() {.closure.} + + + proc f(wrapper: ProcWrapper) = + let s = @[wrapper.p] + let a = [wrapper.p] + + proc main = + # let wrapper: ProcWrapper = ProcWrapper(p: proc {.closure.} = echo 10) + let wrapper: ProcWrapper = (p: proc {.closure.} = echo 10) + f(wrapper) + + main() + +block: + block: # bug #22923 + block: + let + a: int = 100 + b: int32 = 200'i32 + + let + x = arrayWith(a, 8) # compiles + y = arrayWith(b, 8) # internal error + z = arrayWith(14, 8) # integer literal also results in a crash + + doAssert x == [100, 100, 100, 100, 100, 100, 100, 100] + doAssert $y == "[200, 200, 200, 200, 200, 200, 200, 200]" + doAssert z == [14, 14, 14, 14, 14, 14, 14, 14] + + block: + let a: string = "nim" + doAssert arrayWith(a, 3) == ["nim", "nim", "nim"] + + let b: char = 'c' + doAssert arrayWith(b, 3) == ['c', 'c', 'c'] + + let c: uint = 300'u + doAssert $arrayWith(c, 3) == "[300, 300, 300]" + +block: # bug #23505 + type + K = object + C = object + value: ptr K + + proc init(T: type C): C = + let tmp = new K + C(value: addr tmp[]) + + discard init(C) + +block: # bug #23524 + type MyType = object + a: int + + proc `=destroy`(typ: MyType) = discard + + var t1 = MyType(a: 100) + var t2 = t1 # Should be a copy? + + proc main() = + t2 = t1 + doAssert t1.a == 100 + doAssert t2.a == 100 + + main() + +block: # bug #23907 + type + Thingy = object + value: int + + ExecProc[C] = proc(value: sink C): int {.nimcall.} + + proc `=copy`(a: var Thingy, b: Thingy) {.error.} + + var thingyDestroyCount = 0 + + proc `=destroy`(thingy: Thingy) = + assert(thingyDestroyCount <= 0) + thingyDestroyCount += 1 + + proc store(value: sink Thingy): int = + result = value.value + + let callback: ExecProc[Thingy] = store + + doAssert callback(Thingy(value: 123)) == 123 + +import std/strutils + +block: # bug #23974 + func g(e: seq[string]): lent seq[string] = result = e + proc k(f: string): seq[string] = f.split("/") + proc n() = + const r = "/d/" + let t = + if true: + k(r).g() + else: + k("/" & r).g() + echo t + + n() + +block: # bug #23973 + func g(e: seq[string]): lent seq[string] = result = e + proc k(f: string): seq[string] = f.split("/") + proc n() = + const r = "/test/empty" # or "/test/empty/1" + let a = k(r).g() + let t = + if true: + k(r).g() + else: + k("/" & r).g() # or raiseAssert "" + doAssert t == a + + n() + +block: # bug #24141 + func reverse(s: var openArray[char]) = + s[0] = 'f' + + func rev(s: var string) = + s.reverse + + proc main = + var abc = "abc" + abc.rev + doAssert abc == "fbc" + + main() diff --git a/tests/arc/tasyncawait.nim b/tests/arc/tasyncawait.nim index 75d9bc9b5..91a7453cd 100644 --- a/tests/arc/tasyncawait.nim +++ b/tests/arc/tasyncawait.nim @@ -40,7 +40,7 @@ proc readMessages(client: AsyncFD) {.async.} = clientCount.inc break else: - if line.startswith("Message "): + if line.startsWith("Message "): msgCount.inc else: doAssert false diff --git a/tests/arc/tasyncleak.nim b/tests/arc/tasyncleak.nim index eb0c45213..8e3a7b3e7 100644 --- a/tests/arc/tasyncleak.nim +++ b/tests/arc/tasyncleak.nim @@ -1,5 +1,5 @@ discard """ - outputsub: "(allocCount: 4302, deallocCount: 4300)" + outputsub: "(allocCount: 4050, deallocCount: 4048)" cmd: "nim c --gc:orc -d:nimAllocStats $file" """ diff --git a/tests/arc/tcaseobj.nim b/tests/arc/tcaseobj.nim index a967a509b..3499f5c1e 100644 --- a/tests/arc/tcaseobj.nim +++ b/tests/arc/tcaseobj.nim @@ -10,6 +10,7 @@ begin end prevented (ok: true, value: "ok") +@[(kind: P, pChildren: @[])] myobj destroyed ''' """ @@ -59,7 +60,7 @@ proc `=destroy`(o: var TMyObj) = o.p = nil echo "myobj destroyed" -proc `=`(dst: var TMyObj, src: TMyObj) = +proc `=copy`(dst: var TMyObj, src: TMyObj) = `=destroy`(dst) dst.p = alloc(src.len) dst.len = src.len @@ -169,18 +170,24 @@ proc test_myobject = x.x1 = "x1" x.x2 = "x2" x.y1 = "ljhkjhkjh" - x.kind1 = true + {.cast(uncheckedAssign).}: + x.kind1 = true x.y2 = @["1", "2"] - x.kind2 = true + {.cast(uncheckedAssign).}: + x.kind2 = true x.z1 = "yes" - x.kind2 = false + {.cast(uncheckedAssign).}: + x.kind2 = false x.z2 = @["1", "2"] - x.kind2 = true + {.cast(uncheckedAssign).}: + x.kind2 = true x.z1 = "yes" x.kind2 = true # should be no effect doAssert(x.z1 == "yes") - x.kind2 = false - x.kind1 = x.kind2 # support self assignment with effect + {.cast(uncheckedAssign).}: + x.kind2 = false + {.cast(uncheckedAssign).}: + x.kind1 = x.kind2 # support self assignment with effect try: x.kind1 = x.flag # flag is not accesible @@ -206,8 +213,9 @@ type error*: string proc init(): RocksDBResult[string] = - result.ok = true - result.value = "ok" + {.cast(uncheckedAssign).}: + result.ok = true + result.value = "ok" echo init() @@ -221,7 +229,8 @@ type MyObj = object of true: x1: string var a = MyObj(kind: false, x0: 1234) -a.kind = true +{.cast(uncheckedAssign).}: + a.kind = true doAssert(a.x1 == "") block: @@ -244,3 +253,114 @@ block: doAssert j1.x1 == 2 doAssert j0.x0 == 2 + +# ------------------------------------ +# bug #20305 + +type + ContentNodeKind = enum + P, Br, Text + ContentNode = object + case kind: ContentNodeKind + of P: pChildren: seq[ContentNode] + of Br: discard + of Text: textStr: string + +proc bug20305 = + var x = ContentNode(kind: P, pChildren: @[ + ContentNode(kind: P, pChildren: @[ContentNode(kind: Text, textStr: "brrr")]) + ]) + x.pChildren.add ContentNode(kind: Br) + x.pChildren.del(0) + {.cast(uncheckedAssign).}: + x.pChildren[0].kind = P + echo x.pChildren + +bug20305() + +# bug #21023 +block: + block: + type + MGErrorKind = enum + mgeUnexpected, mgeNotFound + + type Foo = object + kind: MGErrorKind + ex: Exception + + type Boo = object + a: seq[int] + + type + Result2 = object + case o: bool + of false: + e: Foo + of true: + v: Boo + + proc startSessionSync(): Result2 = + return Result2(o: true) + + proc mainSync = + let ff = startSessionSync() + doAssert ff.o == true + + mainSync() + + block: + type + MGErrorKind = enum + mgeUnexpected, mgeNotFound + + type Foo = object + kind: MGErrorKind + ex: Exception + + type Boo = object + a: seq[int] + + type + Result2 = object + case o: bool + of false: + e: Foo + of true: + v: Boo + s: int + + proc startSessionSync(): Result2 = + return Result2(o: true, s: 12) + + proc mainSync = + let ff = startSessionSync() + doAssert ff.s == 12 + + mainSync() + +import std/sequtils + +# bug #23690 +type + SomeObj* = object of RootObj + + Item* = object + case kind*: 0..1 + of 0: + a*: int + b*: SomeObj + of 1: + c*: string + + ItemExt* = object + a*: Item + b*: string + +proc do1(x: int): seq[(string, Item)] = + result = @[("zero", Item(kind: 1, c: "first"))] + +proc do2(x: int, e: ItemExt): seq[(string, ItemExt)] = + do1(x).map(proc(v: (string, Item)): auto = (v[0], ItemExt(a: v[1], b: e.b))) + +doAssert $do2(0, ItemExt(a: Item(kind: 1, c: "second"), b: "third")) == """@[("zero", (a: (kind: 1, c: "first"), b: "third"))]""" diff --git a/tests/arc/tcaseobjcopy.nim b/tests/arc/tcaseobjcopy.nim index ed07b404e..fb26a4973 100644 --- a/tests/arc/tcaseobjcopy.nim +++ b/tests/arc/tcaseobjcopy.nim @@ -169,18 +169,23 @@ proc test_myobject = x.x1 = "x1" x.x2 = "x2" x.y1 = "ljhkjhkjh" - x.kind1 = true + {.cast(uncheckedAssign).}: + x.kind1 = true x.y2 = @["1", "2"] - x.kind2 = true + {.cast(uncheckedAssign).}: + x.kind2 = true x.z1 = "yes" - x.kind2 = false + {.cast(uncheckedAssign).}: + x.kind2 = false x.z2 = @["1", "2"] - x.kind2 = true + {.cast(uncheckedAssign).}: + x.kind2 = true x.z1 = "yes" x.kind2 = true # should be no effect doAssert(x.z1 == "yes") - x.kind2 = false - x.kind1 = x.kind2 # support self assignment with effect + {.cast(uncheckedAssign).}: + x.kind2 = false + x.kind1 = x.kind2 # support self assignment with effect try: x.kind1 = x.flag # flag is not accesible @@ -206,7 +211,8 @@ type error*: string proc init(): RocksDBResult[string] = - result.ok = true + {.cast(uncheckedAssign).}: + result.ok = true result.value = "ok" echo init() @@ -221,7 +227,8 @@ type MyObj = object of true: x1: string var a = MyObj(kind: false, x0: 1234) -a.kind = true +{.cast(uncheckedAssign).}: + a.kind = true doAssert(a.x1 == "") block: diff --git a/tests/arc/tcomputedgoto.nim b/tests/arc/tcomputedgoto.nim index 541a748c6..07487684a 100644 --- a/tests/arc/tcomputedgoto.nim +++ b/tests/arc/tcomputedgoto.nim @@ -1,16 +1,19 @@ discard """ - cmd: '''nim c --gc:arc $file''' - output: '''2 -2''' + cmd: '''nim c --mm:arc $file''' + output: ''' +2 +2 +destroyed +''' """ type ObjWithDestructor = object a: int -proc `=destroy`(self: var ObjWithDestructor) = +proc `=destroy`(self: ObjWithDestructor) = echo "destroyed" -proc `=`(self: var ObjWithDestructor, other: ObjWithDestructor) = +proc `=copy`(self: var ObjWithDestructor, other: ObjWithDestructor) = echo "copied" proc test(a: range[0..1], arg: ObjWithDestructor) = @@ -38,4 +41,4 @@ proc test(a: range[0..1], arg: ObjWithDestructor) = if iteration == 2: break -test(1, ObjWithDestructor()) \ No newline at end of file +test(1, ObjWithDestructor()) diff --git a/tests/arc/tcomputedgotocopy.nim b/tests/arc/tcomputedgotocopy.nim index 8337123ba..07487684a 100644 --- a/tests/arc/tcomputedgotocopy.nim +++ b/tests/arc/tcomputedgotocopy.nim @@ -1,13 +1,16 @@ discard """ - cmd: '''nim c --gc:arc $file''' - output: '''2 -2''' + cmd: '''nim c --mm:arc $file''' + output: ''' +2 +2 +destroyed +''' """ type ObjWithDestructor = object a: int -proc `=destroy`(self: var ObjWithDestructor) = +proc `=destroy`(self: ObjWithDestructor) = echo "destroyed" proc `=copy`(self: var ObjWithDestructor, other: ObjWithDestructor) = diff --git a/tests/arc/tconst_to_sink.nim b/tests/arc/tconst_to_sink.nim index ddcc46e67..25f659341 100644 --- a/tests/arc/tconst_to_sink.nim +++ b/tests/arc/tconst_to_sink.nim @@ -1,6 +1,7 @@ discard """ output: '''@[(s1: "333", s2: ""), (s1: "abc", s2: "def"), (s1: "3x", s2: ""), (s1: "3x", s2: ""), (s1: "3x", s2: ""), (s1: "3x", s2: ""), (s1: "lastone", s2: "")]''' - cmd: "nim c --gc:arc $file" + matrix: "--gc:arc" + targets: "c cpp" """ # bug #13240 diff --git a/tests/arc/tcontrolflow.nim b/tests/arc/tcontrolflow.nim index 80cc2b187..dbc115903 100644 --- a/tests/arc/tcontrolflow.nim +++ b/tests/arc/tcontrolflow.nim @@ -76,7 +76,7 @@ var c = Control(x: 7) run(c) -proc sysFatal(exceptn: typedesc, message: string) {.inline, noreturn.} = +proc sysFatal(exceptn: typedesc, message: string) {.inline.} = var buf = newStringOfCap(200) add(buf, "##") add(buf, message) diff --git a/tests/arc/tcursor_field_obj_constr.nim b/tests/arc/tcursor_field_obj_constr.nim new file mode 100644 index 000000000..b87f734bd --- /dev/null +++ b/tests/arc/tcursor_field_obj_constr.nim @@ -0,0 +1,44 @@ +discard """ + output: '''a +b +c''' + cmd: "nim c --gc:arc $file" +""" + +# bug #18469 + +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: var NodeObj) = + echo x.label + `=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)) + +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() diff --git a/tests/arc/tcursorloop.nim b/tests/arc/tcursorloop.nim new file mode 100644 index 000000000..a37a6a036 --- /dev/null +++ b/tests/arc/tcursorloop.nim @@ -0,0 +1,45 @@ +discard """ + cmd: '''nim c --gc:arc --expandArc:traverse --hint:Performance:off $file''' + nimout: ''' +--expandArc: traverse + +var + it + jt_cursor +try: + `=copy`(it, root) + block :tmp: + while ( + not (it == nil)): + if true: + echo [it.s] + `=copy`(it, it.ri) + jt_cursor = root + if ( + not (jt_cursor == nil)): + echo [jt_cursor.s] + jt_cursor = jt_cursor.ri +finally: + `=destroy`(it) +-- end of expandArc ------------------------ +''' +""" + +type + Node = ref object + le, ri: Node + s: string + +proc traverse(root: Node) = + var it = root + while it != nil: + if true: + echo it.s + it = it.ri + + var jt = root + if jt != nil: + echo jt.s + jt = jt.ri + +traverse(nil) diff --git a/tests/arc/tcustomtrace.nim b/tests/arc/tcustomtrace.nim new file mode 100644 index 000000000..5e0ecfb24 --- /dev/null +++ b/tests/arc/tcustomtrace.nim @@ -0,0 +1,240 @@ +discard """ + outputsub: '''1 +2 +3 +4 +5 +6 +89 +90 +90 +0 0 1 +0 1 2 +0 2 3 +1 0 4 +1 1 5 +1 2 6 +1 3 7 +after 6 6 +MEM 0''' +joinable: false + cmd: "nim c --gc:orc -d:useMalloc $file" + valgrind: "true" +""" + +import typetraits + +type + myseq*[T] = object + len, cap: int + data: ptr UncheckedArray[T] + +# XXX make code memory safe for overflows in '*' +var + allocCount, deallocCount: int + +proc `=destroy`*[T](x: var myseq[T]) = + if x.data != nil: + when not supportsCopyMem(T): + for i in 0..<x.len: `=destroy`(x[i]) + dealloc(x.data) + inc deallocCount + x.data = nil + x.len = 0 + x.cap = 0 + +proc `=copy`*[T](a: var myseq[T]; b: myseq[T]) = + if a.data == b.data: return + if a.data != nil: + `=destroy`(a) + #dealloc(a.data) + #inc deallocCount + #a.data = nil + a.len = b.len + a.cap = b.cap + if b.data != nil: + a.data = cast[type(a.data)](alloc(a.cap * sizeof(T))) + inc allocCount + when supportsCopyMem(T): + copyMem(a.data, b.data, a.cap * sizeof(T)) + else: + for i in 0..<a.len: + a.data[i] = b.data[i] + +proc `=sink`*[T](a: var myseq[T]; b: myseq[T]) = + if a.data != nil and a.data != b.data: + dealloc(a.data) + inc deallocCount + a.len = b.len + a.cap = b.cap + a.data = b.data + +proc `=trace`*[T](x: var myseq[T]; env: pointer) = + if x.data != nil: + for i in 0..<x.len: `=trace`(x[i], env) + +proc resize[T](s: var myseq[T]) = + let oldCap = s.cap + if oldCap == 0: s.cap = 8 + else: s.cap = (s.cap * 3) shr 1 + if s.data == nil: inc allocCount + s.data = cast[typeof(s.data)](realloc0(s.data, oldCap * sizeof(T), s.cap * sizeof(T))) + +proc reserveSlot[T](x: var myseq[T]): ptr T = + if x.len >= x.cap: resize(x) + result = addr(x.data[x.len]) + inc x.len + +template add*[T](x: var myseq[T]; y: T) = + reserveSlot(x)[] = y + +proc shrink*[T](x: var myseq[T]; newLen: int) = + assert newLen <= x.len + assert newLen >= 0 + when not supportsCopyMem(T): + for i in countdown(x.len - 1, newLen - 1): + `=destroy`(x.data[i]) + x.len = newLen + +proc grow*[T](x: var myseq[T]; newLen: int; value: T) = + if newLen <= x.len: return + assert newLen >= 0 + let oldCap = x.cap + if oldCap == 0: x.cap = newLen + else: x.cap = max(newLen, (oldCap * 3) shr 1) + if x.data == nil: inc allocCount + x.data = cast[type(x.data)](realloc0(x.data, oldCap * sizeof(T), x.cap * sizeof(T))) + for i in x.len..<newLen: + x.data[i] = value + x.len = newLen + +template default[T](t: typedesc[T]): T = + var v: T + v + +proc setLen*[T](x: var myseq[T]; newLen: int) {.deprecated.} = + if newlen < x.len: shrink(x, newLen) + else: grow(x, newLen, default(T)) + +template `[]`*[T](x: myseq[T]; i: Natural): T = + assert i < x.len + x.data[i] + +template `[]=`*[T](x: myseq[T]; i: Natural; y: T) = + assert i < x.len + x.data[i] = y + +proc createSeq*[T](elems: varargs[T]): myseq[T] = + result.cap = max(elems.len, 2) + result.len = elems.len + result.data = cast[type(result.data)](alloc0(result.cap * sizeof(T))) + inc allocCount + when supportsCopyMem(T): + copyMem(result.data, addr(elems[0]), result.cap * sizeof(T)) + else: + for i in 0..<result.len: + result.data[i] = elems[i] + +proc len*[T](x: myseq[T]): int {.inline.} = x.len + +proc main = + var s = createSeq(1, 2, 3, 4, 5, 6) + s.add 89 + s.grow s.len + 2, 90 + for i in 0 ..< s.len: + echo s[i] + + var nested = createSeq(createSeq(1, 2, 3), createSeq(4, 5, 6, 7)) + for i in 0 ..< nested.len: + for j in 0 ..< nested[i].len: + echo i, " ", j, " ", nested[i][j] + +main() +echo "after ", allocCount, " ", deallocCount + +type + Node = ref object + name: char + sccId: int + kids: myseq[Node] + rc: int + +proc edge(a, b: Node) = + inc b.rc + a.kids.add b + +proc createNode(name: char): Node = + new result + result.name = name + result.kids = createSeq[Node]() + +proc use(x: Node) = discard + +proc buildComplexGraph: Node = + # see https://en.wikipedia.org/wiki/Strongly_connected_component for the + # graph: + let a = createNode('a') + let b = createNode('b') + let c = createNode('c') + let d = createNode('d') + let e = createNode('e') + + a.edge c + c.edge b + c.edge e + b.edge a + d.edge c + e.edge d + + + let f = createNode('f') + b.edge f + e.edge f + + let g = createNode('g') + let h = createNode('h') + let i = createNode('i') + + f.edge g + f.edge i + g.edge h + h.edge i + i.edge g + + let j = createNode('j') + + h.edge j + i.edge j + + let k = createNode('k') + let l = createNode('l') + + f.edge k + k.edge l + l.edge k + k.edge j + + let m = createNode('m') + let n = createNode('n') + let p = createNode('p') + let q = createNode('q') + + m.edge n + n.edge p + n.edge q + q.edge p + p.edge m + + q.edge k + + d.edge m + e.edge n + + result = a + +proc main2 = + let g = buildComplexGraph() + +main2() +GC_fullCollect() +echo "MEM ", getOccupiedMem() diff --git a/tests/arc/tdup.nim b/tests/arc/tdup.nim new file mode 100644 index 000000000..61f262a68 --- /dev/null +++ b/tests/arc/tdup.nim @@ -0,0 +1,71 @@ +discard """ + cmd: "nim c --mm:arc --expandArc:foo --hints:off $file" + nimout: ''' +--expandArc: foo + +var + x + :tmpD + s + :tmpD_1 +x = Ref(id: 8) +inc: + :tmpD = `=dup`(x) + :tmpD +inc: + let blitTmp = x + blitTmp +var id_1 = 777 +s = RefCustom(id_2: addr(id_1)) +inc_1 : + :tmpD_1 = `=dup_1`(s) + :tmpD_1 +inc_1 : + let blitTmp_1 = s + blitTmp_1 +-- end of expandArc ------------------------ +''' +""" + +type + Ref = ref object + id: int + + RefCustom = object + id: ptr int + +proc `=dup`(x: RefCustom): RefCustom = + result.id = x.id + +proc inc(x: sink Ref) = + inc x.id + +proc inc(x: sink RefCustom) = + inc x.id[] + + +proc foo = + var x = Ref(id: 8) + inc(x) + inc(x) + var id = 777 + var s = RefCustom(id: addr id) + inc s + inc s + +foo() + +proc foo2 = + var x = Ref(id: 8) + inc(x) + doAssert x.id == 9 + inc(x) + doAssert x.id == 10 + var id = 777 + var s = RefCustom(id: addr id) + inc s + doAssert s.id[] == 778 + inc s + doAssert s.id[] == 779 + +foo2() diff --git a/tests/arc/texplicit_sink.nim b/tests/arc/texplicit_sink.nim new file mode 100644 index 000000000..4b021ed62 --- /dev/null +++ b/tests/arc/texplicit_sink.nim @@ -0,0 +1,29 @@ +discard """ + output: '''de''' + cmd: '''nim c --mm:arc --expandArc:main $file''' + nimout: '''--expandArc: main + +var + a + b_cursor +try: + a = f "abc" + b_cursor = "de" + `=sink`(a, b_cursor) + echo [a] +finally: + `=destroy`(a) +-- end of expandArc ------------------------''' +""" + +# bug #20572 + +proc f(s: string): string = s + +proc main = + var a = f "abc" + var b = "de" + `=sink`(a, b) + echo a + +main() diff --git a/tests/arc/tfuncobj.nim b/tests/arc/tfuncobj.nim new file mode 100644 index 000000000..50cd9425e --- /dev/null +++ b/tests/arc/tfuncobj.nim @@ -0,0 +1,38 @@ +discard """ + outputsub: '''f1 +f2 +f3''' + cmd: "nim c --gc:orc $file" + valgrind: true +""" + +type + FuncObj = object + fn: proc (env: pointer) {.cdecl.} + env: pointer + +proc `=destroy`(x: var FuncObj) = + GC_unref(cast[RootRef](x.env)) + +proc `=copy`(x: var FuncObj, y: FuncObj) {.error.} + +# bug #18433 + +proc main = + var fs: seq[FuncObj] + + proc wrap(p: proc()) = + proc closeOver() = p() + let env = rawEnv closeOver + GC_ref(cast[RootRef](env)) + fs.add(FuncObj(fn: cast[proc(env: pointer){.cdecl.}](rawProc closeOver), env: env)) + + wrap(proc() {.closure.} = echo "f1") + wrap(proc() {.closure.} = echo "f2") + wrap(proc() {.closure.} = echo "f3") + + for a in fs: + a.fn(a.env) + +main() + diff --git a/tests/arc/thamming_orc.nim b/tests/arc/thamming_orc.nim new file mode 100644 index 000000000..777efb38e --- /dev/null +++ b/tests/arc/thamming_orc.nim @@ -0,0 +1,155 @@ +discard """ + output: '''(allocCount: 1114, deallocCount: 1112) +created 491 destroyed 491''' + cmd: "nim c --gc:orc -d:nimAllocStats $file" +""" + +# bug #18421 + +# test Nim Hamming Number Lazy List algo with reference counts and not... +# compile with "-d:release -d:danger" and test with various +# memory managment GC's, allocators, threading, etc. +# it should be guaranteed to work with zero memory leaks with `--gc:orc`... + +# compile with `-d:trace20` to trace creation and destruction of first 20 values. + +from math import log2 + +# implement our own basic BigInt so the bigints library isn't necessary... +type + BigInt = object + digits: seq[uint32] +let zeroBigInt = BigInt(digits: @[ 0'u32 ]) +let oneBigInt = BigInt(digits: @[ 1'u32 ]) + +proc shladd(bi: var BigInt; n: int; a: BigInt) = + # assume that both `bi` and `a` are sized correctly with + # msuint32 for both not containing a zero + let alen = a.digits.len + let mx = max(bi.digits.len, a.digits.len) + for i in bi.digits.len ..< mx: bi.digits.add 0'u32 + var cry = 0'u64 + for i in 0 ..< alen: + cry += (bi.digits[i].uint64 shl n) + a.digits[i].uint64 + bi.digits[i] = cry.uint32; cry = cry shr 32 + for i in alen ..< mx: + cry += bi.digits[i].uint64 shl n + bi.digits[i] = cry.uint32; cry = cry shr 32 + if cry > 0'u64: + bi.digits.add cry.uint32 + +proc `$`(x: BigInt): string = + if x.digits.len == 0 or (x.digits.len == 1 and x.digits[0] == 0'u32): + return "0" + result = ""; var n = x; var msd = n.digits.high + while msd >= 0: + if n.digits[msd] == 0'u32: msd.dec; continue + var brw = 0.uint64 + for i in countdown(msd, 0): + let dvdnd = n.digits[i].uint64 + (brw shl 32) + let q = dvdnd div 10'u64; brw = dvdnd - q * 10'u64 + n.digits[i] = q.uint32 + result &= $brw + for i in 0 .. result.high shr 1: # reverse result string in place + let tmp = result[^(i + 1)] + result[^(i + 1)] = result[i] + result[i] = tmp + +type TriVal = (uint32, uint32, uint32) +type LogRep = (float64, TriVal) +type LogRepf = proc(x: LogRep): LogRep +const one: LogRep = (0.0'f64, (0'u32, 0'u32, 0'u32)) +proc `<`(me: LogRep, othr: LogRep): bool = me[0] < othr[0] + +proc convertTriVal2BigInt(tpl: TriVal): BigInt = + result = oneBigInt + let (x2, x3, x5) = tpl + for _ in 1 .. x2: result.shladd 1, zeroBigInt + for _ in 1 .. x3: result.shladd 1, result + for _ in 1 .. x5: result.shladd 2, result + +const lb2 = 1.0'f64 +const lb3 = 3.0'f64.log2 +const lb5 = 5.0'f64.log2 + +proc mul2(me: LogRep): LogRep = + let (lr, tpl) = me; let (x2, x3, x5) = tpl + (lr + lb2, (x2 + 1, x3, x5)) + +proc mul3(me: LogRep): LogRep = + let (lr, tpl) = me; let (x2, x3, x5) = tpl + (lr + lb3, (x2, x3 + 1, x5)) + +proc mul5(me: LogRep): LogRep = + let (lr, tpl) = me; let (x2, x3, x5) = tpl + (lr + lb5, (x2, x3, x5 + 1)) + +type + LazyListObj = object + hd: LogRep + tlf: proc(): LazyList {.closure.} + tl: LazyList + LazyList = ref LazyListObj + +var destroyed = 0 + +proc `=destroy`(ll: var LazyListObj) = + destroyed += 1 + if ll.tlf == nil and ll.tl == nil: return + + when defined(trace20): + echo "destroying: ", (destroyed, ll.hd[1].convertTriVal2BigInt) + if ll.tlf != nil: ll.tlf.`=destroy` + if ll.tl != nil: ll.tl.`=destroy` + #wasMoved(ll) + +proc rest(ll: LazyList): LazyList = # not thread-safe; needs lock on thunk + if ll.tlf != nil: ll.tl = ll.tlf(); ll.tlf = nil + ll.tl + +var created = 0 +iterator hammings(until: int): TriVal = + proc merge(x, y: LazyList): LazyList = + let xh = x.hd; let yh = y.hd; created += 1 + when defined(trace20): + echo "merge create: ", (created - 1, (if xh < yh: xh else: yh)[1].convertTriVal2BigInt) + if xh < yh: LazyList(hd: xh, tlf: proc(): auto = merge x.rest, y) + else: LazyList(hd: yh, tlf: proc(): auto = merge x, y.rest) + proc smult(mltf: LogRepf; s: LazyList): LazyList = + proc smults(ss: LazyList): LazyList = + when defined(trace20): + echo "mult create: ", (created, ss.hd.mltf[1].convertTriVal2BigInt) + created += 1; LazyList(hd: ss.hd.mltf, tlf: proc(): auto = ss.rest.smults) + s.smults + proc unnsm(s: LazyList, mltf: LogRepf): LazyList = + var r: LazyList = nil + when defined(trace20): + echo "first create: ", (created, one[1].convertTriVal2BigInt) + let frst = LazyList(hd: one, tlf: proc(): LazyList = r); created += 1 + r = if s == nil: smult(mltf, frst) else: s.merge smult(mltf, frst) + r + yield one[1] + var hmpll: LazyList = ((nil.unnsm mul5).unnsm mul3).unnsm mul2 + for _ in 2 .. until: + yield hmpll.hd[1]; hmpll = hmpll.rest # almost forever + +proc main = + var s = "" + for h in hammings(20): s &= $h.convertTrival2BigInt & " " + doAssert s == "1 2 3 4 5 6 8 9 10 12 15 16 18 20 24 25 27 30 32 36 ", + "Algorithmic error finding first 20 Hamming numbers!!!" + + when not defined(trace20): + var lsth: TriVal + for h in hammings(200): lsth = h + doAssert $lsth.convertTriVal2BigInt == "16200", + "Algorithmic error finding 200th Hamming number!!!" + +let mem = getOccupiedMem() +main() +GC_FullCollect() +let mb = getOccupiedMem() - mem +doAssert mb == 0, "Found memory leak of " & $mb & " bytes!!!" + +echo getAllocStats() +echo "created ", created, " destroyed ", destroyed diff --git a/tests/arc/thard_alignment.nim b/tests/arc/thard_alignment.nim index e644572f0..30cfddb05 100644 --- a/tests/arc/thard_alignment.nim +++ b/tests/arc/thard_alignment.nim @@ -1,9 +1,11 @@ discard """ disabled: "arm64" -cmd: "nim c --gc:arc $file" +cmd: "nim c --mm:arc -u:nimPreviewNonVarDestructor $file" output: "y" """ +# TODO: fixme: investigate why it failed with non-var destructors + {.passC: "-march=native".} proc isAlignedCheck(p: pointer, alignment: int) = @@ -18,7 +20,7 @@ type proc set1(x: float): m256d {.importc: "_mm256_set1_pd", header: "immintrin.h".} func `+`(a,b: m256d): m256d {.importc: "_mm256_add_pd", header: "immintrin.h".} proc `$`(a: m256d): string = - result = $(cast[ptr float](a.unsafeAddr)[]) + result = $(cast[ptr float](a.addr)[]) var res: seq[seq[m256d]] diff --git a/tests/arc/titeration_doesnt_copy.nim b/tests/arc/titeration_doesnt_copy.nim new file mode 100644 index 000000000..e510a6eff --- /dev/null +++ b/tests/arc/titeration_doesnt_copy.nim @@ -0,0 +1,67 @@ +discard """ + output: "true" +""" + +type + Idx = object + i: int + Node = object + n: int + next: seq[Idx] + FooBar = object + s: seq[Node] + +proc `=copy`(dest: var Idx; source: Idx) {.error.} +proc `=copy`(dest: var Node; source: Node) {.error.} +proc `=copy`(dest: var FooBar; source: FooBar) {.error.} + +proc doSomething(ss: var seq[int], s: FooBar) = + for i in 0 .. s.s.len-1: + for elm in items s.s[i].next: + ss.add s.s[elm.i].n + +when isMainModule: + const foo = FooBar(s: @[Node(n: 1, next: @[Idx(i: 0)])]) + var ss: seq[int] + doSomething(ss, foo) + echo ss == @[1] + +from sequtils import mapIt +from strutils import join + +proc toBinSeq*(b: uint8): seq[uint8] = + ## Return binary sequence from each bits of uint8. + runnableExamples: + from sequtils import repeat + doAssert 0'u8.toBinSeq == 0'u8.repeat(8) + doAssert 0b1010_1010.toBinSeq == @[1'u8, 0, 1, 0, 1, 0, 1, 0] + result = @[] + var c = b + for i in 1..8: + result.add (uint8(c and 0b1000_0000) shr 7) + c = c shl 1 + +proc toBinString*(data: openArray[uint8], col: int): string = + ## Return binary string from each bits of uint8. + runnableExamples: + doAssert @[0b0000_1111'u8, 0b1010_1010].toBinString(8) == "0000111110101010" + doAssert @[0b1000_0000'u8, 0b0000_0000].toBinString(1) == "10" + result = "" + for b in items data.mapIt(it.toBinSeq.mapIt(it.`$`[0].char)): + for i, c in b: + if i < col: + result.add c + +doAssert @[0b0000_1111'u8, 0b1010_1010].toBinString(8) == "0000111110101010" +doAssert @[0b1000_0000'u8, 0b0000_0000].toBinString(1) == "10" + +block: # bug #23982 + iterator `..`(a, b: ptr int16): ptr int16 = discard + var a: seq[int16] #; let p = a[0].addr + var b: seq[ptr int16] + + try: + for x in a[0].addr .. b[1]: # `p .. b[1]` works + discard + except IndexDefect: + discard diff --git a/tests/arc/tkeys_lent.nim b/tests/arc/tkeys_lent.nim new file mode 100644 index 000000000..2c92350b1 --- /dev/null +++ b/tests/arc/tkeys_lent.nim @@ -0,0 +1,17 @@ +discard """ + output: '''{"string": 2}''' + cmd: "nim c --gc:orc $file" +""" + +import tables + +proc use(x: int) = echo x + +proc main = + var tab = {"string": 1}.toTable + for keyAAA in tab.keys(): + template alias(): untyped = tab[keyAAA] + alias() = 2 + echo tab + +main() diff --git a/tests/arc/tmalloc.nim b/tests/arc/tmalloc.nim new file mode 100644 index 000000000..1d72646c8 --- /dev/null +++ b/tests/arc/tmalloc.nim @@ -0,0 +1,16 @@ +discard """ + matrix: "--mm:arc -d:useMalloc; --mm:arc" +""" + +block: # bug #22058 + template foo(): auto = + {.noSideEffect.}: + newSeq[byte](1) + + type V = object + v: seq[byte] + + proc bar(): V = + V(v: foo()) + + doAssert bar().v == @[byte(0)] diff --git a/tests/arc/tmove_regression.nim b/tests/arc/tmove_regression.nim new file mode 100644 index 000000000..7d9a867c3 --- /dev/null +++ b/tests/arc/tmove_regression.nim @@ -0,0 +1,22 @@ +discard """ + output: '''/1/2 +/1 +/ +''' +"""" + +# bug #22001 + +import std / [os, strutils] + +proc finOp2(path, name: string): (string, File) = # Find & open FIRST `name` + var current = path + while true: + if current.isRootDir: break # <- current=="" => current.isRootDir + current = current.parentDir + let dir = current + echo dir.replace('\\', '/') # Commenting out try/except below hides bug + try: result[0] = dir/name; result[1] = open(result[0]); return + except CatchableError: discard + +discard finOp2("/1/2/3", "4") # All same if this->inside a proc diff --git a/tests/arc/tmovebug.nim b/tests/arc/tmovebug.nim index 888027186..8ad7c955c 100644 --- a/tests/arc/tmovebug.nim +++ b/tests/arc/tmovebug.nim @@ -93,6 +93,7 @@ destroy destroy destroy sink +sink destroy copy (f: 1) @@ -107,6 +108,8 @@ sink destroy copy destroy +(f: 1) +destroy ''' """ @@ -290,7 +293,7 @@ when false: # bug #13456 -iterator combinations[T](s: openarray[T], k: int): seq[T] = +iterator combinations[T](s: openArray[T], k: int): seq[T] = let n = len(s) assert k >= 0 and k <= n var pos = newSeq[int](k) @@ -453,7 +456,7 @@ initFoo7(2) # bug #14902 -iterator zip[T](s: openarray[T]): (T, T) = +iterator zip[T](s: openArray[T]): (T, T) = var i = 0 while i < 10: yield (s[i mod 2], s[i mod 2 + 1]) @@ -765,8 +768,74 @@ proc initC(): C = C(o: initO()) proc pair(): tuple[a: C, b: C] = - result.a = initC() # <- when firstWrite tries to find this node to start its analysis it fails, because injectdestructors uses copyTree/shallowCopy - result.b = initC() + result = (a: initC(), b: initC())# <- when firstWrite tries to find this node to start its analysis it fails, because injectdestructors uses copyTree/shallowCopy discard pair() + +# bug #17450 +proc noConsume(x: OO) {.nosinks.} = echo x + +proc main3 = + var i = 1 + noConsume: + block: + OO(f: i) + +main3() + +# misc +proc smoltest(x: bool): bool = + while true: + if true: return x + +discard smoltest(true) + +# bug #18002 +type + TTypeAttachedOp = enum + attachedAsgn + attachedSink + attachedTrace + + PNode = ref object + discard + +proc genAddrOf(n: PNode) = + assert n != nil, "moved?!" + +proc atomicClosureOp = + let x = PNode() + + genAddrOf: + block: + x + + case attachedTrace + of attachedSink: discard + of attachedAsgn: discard + of attachedTrace: genAddrOf(x) + +atomicClosureOp() + + +template assertEq(a, b: untyped): untyped = + block: + let + aval = a + bval = b + + if aval != bval: + quit "bug!" + +proc convoluted = + let _ = (; + var val1: string; + if true: val1 = "22" + true + ) + + assertEq val1, "22" + assertEq val1, "22" + +convoluted() diff --git a/tests/arc/tmovebugcopy.nim b/tests/arc/tmovebugcopy.nim index 7c5228147..ec4315777 100644 --- a/tests/arc/tmovebugcopy.nim +++ b/tests/arc/tmovebugcopy.nim @@ -253,7 +253,7 @@ when false: # bug #13456 -iterator combinations[T](s: openarray[T], k: int): seq[T] = +iterator combinations[T](s: openArray[T], k: int): seq[T] = let n = len(s) assert k >= 0 and k <= n var pos = newSeq[int](k) @@ -416,7 +416,7 @@ initFoo7(2) # bug #14902 -iterator zip[T](s: openarray[T]): (T, T) = +iterator zip[T](s: openArray[T]): (T, T) = var i = 0 while i < 10: yield (s[i mod 2], s[i mod 2 + 1]) diff --git a/tests/arc/tnewseq_legacy.nim b/tests/arc/tnewseq_legacy.nim new file mode 100644 index 000000000..4730d2c2b --- /dev/null +++ b/tests/arc/tnewseq_legacy.nim @@ -0,0 +1,13 @@ +discard """ + output: "(allocCount: 201, deallocCount: 201)" + cmd: "nim c --gc:orc -d:nimAllocStats $file" +""" + +proc main(prefix: string) = + var c: seq[string] + for i in 0..<100: + newSeq(c, 100) + c[i] = prefix & $i + +main("abc") +echo getAllocStats() diff --git a/tests/arc/topenarray.nim b/tests/arc/topenarray.nim new file mode 100644 index 000000000..ba91666ba --- /dev/null +++ b/tests/arc/topenarray.nim @@ -0,0 +1,86 @@ +discard """ + input: "hi" + output: ''' +hi +Nim +''' + matrix: "--gc:arc -d:useMalloc; --gc:arc" +""" +{.experimental: "views".} + +block: # bug 18627 + proc setPosition(params: openArray[string]) = + for i in params.toOpenArray(0, params.len - 1): + echo i + + proc uciLoop() = + let params = @[readLine(stdin)] + setPosition(params) + + uciLoop() + + proc uciLoop2() = + let params = @["Nim"] + for i in params.toOpenArray(0, params.len - 1): + echo i + uciLoop2() + +when defined(nimPreviewSlimSystem): + import std/assertions + +block: # bug #20954 + block: + doAssertRaises(IndexDefect): + var v: array[10, int] + + echo len(toOpenArray(v, 20, 30)) + + block: + doAssertRaises(IndexDefect): + var v: seq[int] + + echo len(toOpenArray(v, 20, 30)) + +# bug #20422 + +proc f(a: var string) = + var v = a.toOpenArray(1, 3) + v[0] = 'a' + +var a = "Hello" +f(a) +doAssert a == "Hallo" + +# bug #22132 +block: + func foo[T](arr: openArray[T], idx: int = arr.low): string = + doAssert idx == 0 + return $arr + + let bug = ["0", "c", "a"] + + let str = foo(bug) + + const expected = """["0", "c", "a"]""" + doAssert str == expected + + const noBugConst = ["0", "c", "a"] + doAssert foo(noBugConst) == expected + let noBugSeq = @["0", "c", "a"] + doAssert foo(noBugSeq) == expected + +block: # bug #20865 + var p: pointer + var x: array[0, int] + # echo toOpenArray(x, 0, 1)[0] # Raises IndexDefect + doAssertRaises(IndexDefect): + echo toOpenArray(cast[ptr array[0, int]](p)[], 0, 1)[0] # Does not raise IndexDefect + +block: # bug #20987 + var v: array[1, byte] + + var p = cast[ptr array[0, byte]](addr v) + + doAssertRaises(IndexDefect): + echo toOpenArray(p[], 1, 2) + diff --git a/tests/arc/topt_cursor.nim b/tests/arc/topt_cursor.nim index 5c35b454f..794132921 100644 --- a/tests/arc/topt_cursor.nim +++ b/tests/arc/topt_cursor.nim @@ -12,7 +12,7 @@ try: x_cursor = ("different", 54) else: x_cursor = ("string here", 80) echo [ - :tmpD = `$`(x_cursor) + :tmpD = `$$`(x_cursor) :tmpD] finally: `=destroy`(:tmpD) diff --git a/tests/arc/topt_no_cursor.nim b/tests/arc/topt_no_cursor.nim index f1eb8575a..0a4984a69 100644 --- a/tests/arc/topt_no_cursor.nim +++ b/tests/arc/topt_no_cursor.nim @@ -1,34 +1,24 @@ discard """ - output: '''(repo: "", package: "meo", ext: "") -doing shady stuff... -3 -6 -(@[1], @[2]) -192.168.0.1 -192.168.0.1 -192.168.0.1 -192.168.0.1''' - cmd: '''nim c --gc:arc --expandArc:newTarget --expandArc:delete --expandArc:p1 --expandArc:tt --hint:Performance:off --assertions:off --expandArc:extractConfig $file''' - nimout: '''--expandArc: newTarget + nimoutFull: true + cmd: '''nim c -r --warnings:off --hints:off --mm:arc --expandArc:newTarget --expandArc:delete --expandArc:p1 --expandArc:tt --hint:Performance:off --assertions:off --expandArc:extractConfig --expandArc:mergeShadowScope --expandArc:check $file''' + nimout: ''' +--expandArc: newTarget var splat :tmp :tmp_1 - :tmp_2 -splat = splitFile(path) -:tmp = splat.dir -wasMoved(splat.dir) -:tmp_1 = splat.name -wasMoved(splat.name) -:tmp_2 = splat.ext -wasMoved(splat.ext) +splat = splitDrive do: + let blitTmp = path + blitTmp +:tmp = splat.drive +`=wasMoved`(splat.drive) +:tmp_1 = splat.path_1 +`=wasMoved`(splat.path_1) result = ( - let blitTmp = :tmp - blitTmp, - let blitTmp_1 = :tmp_1 + let blitTmp_1 = :tmp blitTmp_1, - let blitTmp_2 = :tmp_2 + let blitTmp_2 = :tmp_1 blitTmp_2) `=destroy`(splat) -- end of expandArc ------------------------ @@ -49,14 +39,14 @@ var lresult lvalue lnext - _ + tmpTupleAsgn lresult = @[123] -_ = ( +tmpTupleAsgn = ( let blitTmp = lresult blitTmp, ";") -lvalue = _[0] -lnext = _[1] -result.value = move lvalue +lvalue = tmpTupleAsgn[0] +lnext = tmpTupleAsgn[1] +`=sink`(result.value, move lvalue) `=destroy`(lnext) `=destroy_1`(lvalue) -- end of expandArc ------------------------ @@ -71,14 +61,12 @@ var try: it_cursor = x a = ( - wasMoved(:tmpD) - `=copy`(:tmpD, it_cursor.key) + :tmpD = `=dup`(it_cursor.key) :tmpD, - wasMoved(:tmpD_1) - `=copy`(:tmpD_1, it_cursor.val) + :tmpD_1 = `=dup`(it_cursor.val) :tmpD_1) echo [ - :tmpD_2 = `$`(a) + :tmpD_2 = `$$`(a) :tmpD_2] finally: `=destroy`(:tmpD_2) @@ -103,21 +91,85 @@ try: `=copy`(lan_ip, splitted[1]) echo [lan_ip] echo [splitted[1]] + {.push, overflowChecks: false.} inc(i, 1) + {.pop.} finally: `=destroy`(splitted) finally: `=destroy_1`(lan_ip) --- end of expandArc ------------------------''' +-- end of expandArc ------------------------ +--expandArc: mergeShadowScope + +var shadowScope +`=copy`(shadowScope, c.currentScope) +rawCloseScope(c) +block :tmp: + var sym + var i = 0 + let L = len(shadowScope.symbols) + block :tmp_1: + while i < L: + var :tmpD + sym = shadowScope.symbols[i] + addInterfaceDecl(c): + :tmpD = `=dup`(sym) + :tmpD + {.push, overflowChecks: false.} + inc(i, 1) + {.pop.} +`=destroy`(shadowScope) +-- end of expandArc ------------------------ +--expandArc: check + +var par +this.isValid = fileExists(this.value) +if dirExists(this.value): + var :tmpD + par = (dir: + :tmpD = `=dup`(this.value) + :tmpD, front: "") else: + var + :tmpD_1 + :tmpD_2 + :tmpD_3 + par = (dir_1: parentDir(this.value), front_1: + :tmpD_1 = `=dup`( + :tmpD_3 = splitDrive do: + :tmpD_2 = `=dup`(this.value) + :tmpD_2 + :tmpD_3.path) + :tmpD_1) + `=destroy`(:tmpD_3) +if dirExists(par.dir): + `=sink`(this.matchDirs, getSubDirs(par.dir, par.front)) +else: + `=sink`(this.matchDirs, []) +`=destroy`(par) +-- end of expandArc ------------------------ +--expandArc: check + +check(this) +-- end of expandArc ------------------------ +(package: "", ext: "meo") +doing shady stuff... +3 +6 +(@[1], @[2]) +192.168.0.1 +192.168.0.1 +192.168.0.1 +192.168.0.1 +''' """ -import os +import os, std/private/ntpath -type Target = tuple[repo, package, ext: string] +type Target = tuple[package, ext: string] proc newTarget*(path: string): Target = - let splat = path.splitFile - result = (repo: splat.dir, package: splat.name, ext: splat.ext) + let splat = path.splitDrive + result = (package: splat.drive, ext: splat.path) echo newTarget("meo") @@ -277,3 +329,48 @@ proc extractConfig() = echo splitted[1] # Without this line everything works extractConfig() + + +type + Symbol = ref object + name: string + + Scope = ref object + parent: Scope + symbols: seq[Symbol] + + PContext = ref object + currentScope: Scope + +proc rawCloseScope(c: PContext) = + c.currentScope = c.currentScope.parent + +proc addInterfaceDecl(c: PContext; s: Symbol) = + c.currentScope.symbols.add s + +proc mergeShadowScope*(c: PContext) = + let shadowScope = c.currentScope + c.rawCloseScope + for sym in shadowScope.symbols: + c.addInterfaceDecl(sym) + +mergeShadowScope(PContext(currentScope: Scope(parent: Scope()))) + +type + Foo = ref object + isValid*: bool + value*: string + matchDirs*: seq[string] + +proc getSubDirs(parent, front: string): seq[string] = @[] + +method check(this: Foo) {.base.} = + this.isValid = fileExists(this.value) + let par = if dirExists(this.value): (dir: this.value, front: "") + else: (dir: parentDir(this.value), front: splitDrive(this.value).path) + if dirExists(par.dir): + this.matchDirs = getSubDirs(par.dir, par.front) + else: + this.matchDirs = @[] + +check(Foo()) diff --git a/tests/arc/topt_refcursors.nim b/tests/arc/topt_refcursors.nim index f097150a3..8c638a4a1 100644 --- a/tests/arc/topt_refcursors.nim +++ b/tests/arc/topt_refcursors.nim @@ -1,26 +1,35 @@ discard """ output: '''''' cmd: '''nim c --gc:arc --expandArc:traverse --hint:Performance:off $file''' - nimout: '''--expandArc: traverse + nimout: ''' +--expandArc: traverse var it_cursor - jt_cursor -it_cursor = root -block :tmp: - while ( - not (it_cursor == nil)): - echo [it_cursor.s] - it_cursor = it_cursor.ri -jt_cursor = root -block :tmp_1: - while ( - not (jt_cursor == nil)): - var ri_1_cursor - ri_1_cursor = jt_cursor.ri - echo [jt_cursor.s] - jt_cursor = ri_1_cursor --- end of expandArc ------------------------''' + jt +try: + it_cursor = root + block :tmp: + while ( + not (it_cursor == nil)): + echo [it_cursor.s] + it_cursor = it_cursor.ri + `=copy`(jt, root) + block :tmp_1: + while ( + not (jt == nil)): + var ri_1 + try: + `=copy`(ri_1, jt.ri) + echo [jt.s] + `=sink`(jt, ri_1) + `=wasMoved`(ri_1) + finally: + `=destroy`(ri_1) +finally: + `=destroy`(jt) +-- end of expandArc ------------------------ +''' """ type diff --git a/tests/arc/topt_wasmoved_destroy_pairs.nim b/tests/arc/topt_wasmoved_destroy_pairs.nim index 2f971f112..c96340a30 100644 --- a/tests/arc/topt_wasmoved_destroy_pairs.nim +++ b/tests/arc/topt_wasmoved_destroy_pairs.nim @@ -1,7 +1,8 @@ discard """ output: '''''' cmd: '''nim c --gc:arc --expandArc:main --expandArc:tfor --hint:Performance:off $file''' - nimout: '''--expandArc: main + nimout: ''' +--expandArc: main var a @@ -29,6 +30,7 @@ try: x = f() block :tmp: var i_cursor + mixin inc var i_1 = 0 block :tmp_1: while i_1 < 4: @@ -37,25 +39,25 @@ try: if i_cursor == 2: return add(a): - wasMoved(:tmpD) - `=copy`(:tmpD, x) + :tmpD = `=dup`(x) :tmpD inc i_1, 1 if cond: add(a): let blitTmp = x - wasMoved(x) + `=wasMoved`(x) blitTmp else: add(b): let blitTmp_1 = x - wasMoved(x) + `=wasMoved`(x) blitTmp_1 finally: `=destroy`(x) `=destroy_1`(b) `=destroy_1`(a) --- end of expandArc ------------------------''' +-- end of expandArc ------------------------ +''' """ proc f(): seq[int] = diff --git a/tests/arc/torcmisc.nim b/tests/arc/torcmisc.nim index 20dd18fb3..e41ad7c77 100644 --- a/tests/arc/torcmisc.nim +++ b/tests/arc/torcmisc.nim @@ -32,3 +32,35 @@ when true: waitFor hello(Flags()) echo "success" +# bug #18240 +import tables + +type + TopicHandler* = proc(topic: string, + data: seq[byte]): Future[void] {.gcsafe, raises: [Defect].} + + PeerID* = object + data*: seq[byte] + + PeerInfo* = ref object of RootObj + peerId*: PeerID + + Connection* = ref object of RootObj + peerInfo*: PeerInfo + + PubSubPeer* = ref object of RootObj + codec*: string + + PubSub* = ref object of RootObj + topics*: Table[string, seq[TopicHandler]] + peers*: Table[PeerID, PubSubPeer] + +proc getOrCreatePeer*(myParam: PubSub, peerId: PeerID, protos: seq[string]): PubSubPeer = + myParam.peers.withValue(peerId, peer): + return peer[] + +method handleConn*(myParam: PubSub, + conn: Connection, + proto: string) {.base, async.} = + myParam.peers.withValue(conn.peerInfo.peerId, peer): + let peerB = peer[] diff --git a/tests/arc/tref_cast_error.nim b/tests/arc/tref_cast_error.nim index b0d2faf77..20139c1be 100644 --- a/tests/arc/tref_cast_error.nim +++ b/tests/arc/tref_cast_error.nim @@ -1,6 +1,6 @@ discard """ cmd: "nim c --gc:arc $file" - errormsg: "expression cannot be cast to ref RootObj" + errormsg: "expression cannot be cast to 'ref RootObj'" joinable: false """ diff --git a/tests/arc/trepr.nim b/tests/arc/trepr.nim index 50d433208..abf28330a 100644 --- a/tests/arc/trepr.nim +++ b/tests/arc/trepr.nim @@ -9,6 +9,7 @@ nil 2 Obj(member: ref @["hello"]) ref (member: ref @["hello"]) +ObjUa(v: 0, a: [...]) ''' """ @@ -87,3 +88,10 @@ macro extract(): untyped = test(parseExpr("discard")) extract() + +type + ObjUa = ref object + v: int + a: UncheckedArray[char] + +echo ObjUa().repr diff --git a/tests/arc/tshared_ptr_crash.nim b/tests/arc/tshared_ptr_crash.nim new file mode 100644 index 000000000..1794834db --- /dev/null +++ b/tests/arc/tshared_ptr_crash.nim @@ -0,0 +1,67 @@ +discard """ + cmd: "nim c --threads:on --gc:arc $file" + action: compile +""" + +# bug #17893 + +type + SharedPtr*[T] = object + val: ptr tuple[value: T, atomicCounter: int] + +proc `=destroy`*[T](p: var SharedPtr[T]) = + mixin `=destroy` + if p.val != nil: + if atomicLoadN(addr p.val[].atomicCounter, AtomicConsume) == 0: + `=destroy`(p.val[]) + deallocShared(p.val) + else: + discard atomicDec(p.val[].atomicCounter) + +proc `=copy`*[T](dest: var SharedPtr[T], src: SharedPtr[T]) = + if src.val != nil: + discard atomicInc(src.val[].atomicCounter) + if dest.val != nil: + `=destroy`(dest) + dest.val = src.val + +proc newSharedPtr*[T](val: sink T): SharedPtr[T] {.nodestroy.} = + result.val = cast[typeof(result.val)](allocShared(sizeof(result.val[]))) + result.val.atomicCounter = 0 + result.val.value = val + +proc isNil*[T](p: SharedPtr[T]): bool {.inline.} = + p.val == nil + +proc `[]`*[T](p: SharedPtr[T]): var T {.inline.} = + when compileOption("boundChecks"): + doAssert(p.val != nil, "deferencing nil shared pointer") + result = p.val.value + +type + Sender*[T] = object + queue: SharedPtr[seq[T]] + +proc newSender*[T](queue: sink SharedPtr[seq[T]]): Sender[T] = + result = Sender[T](queue: queue) + +proc send*[T](self: Sender[T]; t: sink T) = + self.queue[].add t + +proc newChannel*(): Sender[int] = + let queue = newSharedPtr(newSeq[int]()) + result = newSender(queue) + + +var + p: Thread[Sender[int]] + +proc threadFn(tx: Sender[int]) = + send tx, 0 + +proc multiThreadedChannel = + let tx = newChannel() + createThread(p, threadFn, tx) + joinThread(p) + +multiThreadedChannel() diff --git a/tests/arc/tstringliteral.nim b/tests/arc/tstringliteral.nim new file mode 100644 index 000000000..c5fac22d8 --- /dev/null +++ b/tests/arc/tstringliteral.nim @@ -0,0 +1,17 @@ +discard """ + matrix: "--mm:arc; --mm:orc" +""" + +block: # issue #24080 + var a = (s: "a") + var b = "a" + a.s.setLen 0 + b = a.s + doAssert b == "" + +block: # issue #24080, longer string + var a = (s: "abc") + var b = "abc" + a.s.setLen 2 + b = a.s + doAssert b == "ab" diff --git a/tests/arc/tunref_cycle.nim b/tests/arc/tunref_cycle.nim new file mode 100644 index 000000000..82551b7f7 --- /dev/null +++ b/tests/arc/tunref_cycle.nim @@ -0,0 +1,26 @@ +discard """ + outputsub: '''inside closure +hello world''' + cmd: "nim c --gc:orc -d:useMalloc $file" + valgrind: true +""" + +# bug #18579 + +var fp: proc (env: pointer) {.cdecl.} +var env: pointer + +proc store(f: proc (){.closure.}) = + proc closeOver() = + echo "inside closure" + f() + (fp,env) = (cast[proc(env: pointer){.cdecl.}](rawProc closeOver), rawEnv closeOver) + GC_ref(cast[RootRef](env)) + +proc run() = + fp(env) + GC_unref(cast[RootRef](env)) + +store(proc() = echo "hello world") +run() +GC_fullCollect() diff --git a/tests/arc/tweave.nim b/tests/arc/tweave.nim index 220d65f97..1c60ac403 100644 --- a/tests/arc/tweave.nim +++ b/tests/arc/tweave.nim @@ -8,6 +8,9 @@ discard """ import std/atomics +when defined(nimPreviewSlimSystem): + import std/[assertions, typedthreads] + const MemBlockSize = 256 type diff --git a/tests/arc/twrong_sinkinference.nim b/tests/arc/twrong_sinkinference.nim index 59930a9fa..ecf09d28a 100644 --- a/tests/arc/twrong_sinkinference.nim +++ b/tests/arc/twrong_sinkinference.nim @@ -1,6 +1,6 @@ discard """ cmd: "nim c --gc:arc $file" - errormsg: "type mismatch: got <proc (a: string, b: sink string){.noSideEffect, gcsafe, locks: 0.}>" + errormsg: "type mismatch: got <proc (a: string, b: sink string){.noSideEffect, gcsafe.}>" line: 18 """ |