Open metagn opened 2 years ago
This also happens if you just use ptr UncheckedArray
:
type
ArrayInfo[T] = ptr object
length: int
data: ptr UncheckedArray[T]
Array*[T] = object
info: ArrayInfo[T]
proc `=destroy`*[T](arr: var Array[T]) =
echo repr(arr.info)
if not arr.info.isNil:
if not arr.info.data.isNil:
let len = arr.info.length
for i in 0 ..< len:
`=destroy`(arr.info.data[i])
dealloc(arr.info.data)
arr.info.data = nil
dealloc(arr.info)
arr.info = nil
iterator items*[T](x: Array[T]): T =
let L = x.info.length
for i in 0 ..< L:
yield x.info.data[i]
proc toArray*[T](arr: sink openarray[T]): Array[T] =
result.info = cast[typeof(result.info)](alloc(sizeof(result.info.data)))
result.info.length = arr.len
result.info.data = cast[typeof(result.info.data)](alloc(arr.len * sizeof(T)))
for i in 0 ..< arr.len:
result.info.data[i] = arr[i]
proc `$`*[T](x: Array[T]): string =
result = "Array("
var firstElement = true
for value in items(x):
if firstElement:
firstElement = false
else:
result.add(", ")
when value isnot string and value isnot seq and compiles(value.isNil):
if value.isNil:
result.add "nil"
else:
result.addQuoted(value)
else:
result.addQuoted(value)
result.add(")")
type Foo = object
case atom: bool
of false:
node: Array[Foo]
of true:
leaf: int
proc tree(arr: varargs[Foo]): Foo =
Foo(atom: false, node: toArray(@arr))
proc leaf(x: int): Foo = Foo(atom: true, leaf: x)
echo tree(leaf(1), tree(leaf(2), tree(leaf(3))))
nil
nil
...
nil
ptr 0x7f0848e4c050 --> [length = 1,
data = ptr 0x7f0848e4c070 --> [...]]
ptr 0x7f0848e4c090 --> [length = 2,
data = ptr 0x7f0848e4b140 --> [...]]
ptr 0x7f0848e4c050 --> [length = 1,
data = nil]
(atom: false, node: Array((atom: true, leaf: 1), (atom: false, node: Array((atom: true, leaf: 2), (atom: false, node: Array((atom: true, leaf: 3)))))))
ptr 0x7f0848e4c0b0 --> [length = 7809635517442387828,
data = ptr 0x65646f6e202c6573 --> [...]]
Traceback (most recent call last)
/playground/nim/lib/system.nim(1621) in
/usercode/in.nim(14) =destroy
SIGSEGV: Illegal storage access. (Attempt to read from nil?)
Segmentation fault (core dumped)
Notice the length 7809635517442387828. With arc/orc, the data
pointer becomes the number 50 instead.
All variations work if you turn Foo
into a ref object.
type
ArrayInfo[T] = ptr object
length: int
data: UncheckedArray[T]
Array*[T] = object
info: ArrayInfo[T]
proc `=destroy`*[T](arr: var Array[T]) =
echo "info: ", cast[uint](arr.info)
if not arr.info.isNil:
let len = arr.info.length
echo "len: ", len
for i in 0 ..< len:
`=destroy`(arr.info.data[i])
dealloc(arr.info)
arr.info = nil
iterator items*[T](x: Array[T]): T =
let L = x.info.length
for i in 0 ..< L:
yield x.info.data[i]
proc toArray*[T](arr: sink openarray[T]): Array[T] =
result.info = cast[typeof(result.info)](alloc(sizeof(result.info.length) + arr.len * sizeof(T)))
result.info.length = arr.len
for i in 0 ..< arr.len:
result.info.data[i] = arr[i]
proc `$`*[T](x: Array[T]): string =
result = "Array("
var firstElement = true
for value in items(x):
if firstElement:
firstElement = false
else:
result.add(", ")
when value isnot string and value isnot seq and compiles(value.isNil):
if value.isNil:
result.add "nil"
else:
result.addQuoted(value)
else:
result.addQuoted(value)
result.add(")")
type Foo = ref object
case atom: bool
of false:
node: Array[Foo]
of true:
leaf: int
proc `$`(f: Foo): string = $f[]
proc tree(arr: varargs[Foo]): Foo =
Foo(atom: false, node: toArray(@arr))
proc leaf(x: int): Foo = Foo(atom: true, leaf: x)
echo tree(leaf(1), tree(leaf(2), tree(leaf(3))))
(atom: false, node: Array((atom: true, leaf: 1), (atom: false, node: Array((atom: true, leaf: 2), (atom: false, node: Array((atom: true, leaf: 3)))))))
info: 6488416
len: 2
info: 6488320
len: 2
info: 6492240
len: 1
Notice the lack of extra destructions. This is with arc though, refc doesn't do any destructions at the end, even with proc main
.
This happens with all GCs, and does not seem to be related to the handling of tailing
UncheckedArray
, asptr UncheckedArray
also fails. Adding a refcount field and only destroying when it is 0 also doesn't help.Example
It works correctly if
Foo
is aref object
.Current Output
Replacing
repr(arr.info)
withcast[int](arr.info)
:Notice: The first pointer with length 1 is printed again except with length 4 this time, and at some point a pointer becomes the number 4. With ARC, it becomes 20 instead of 4.
Other variations such as
echo (cast[int](arr.info), arr.info[].length)
give stuff like (on 1.6):which is an invalid state as well. Removing the
echo
makes everything work, but this is just due to luck, and many things trigger this bug. If you just addecho len
afterlet len = arr.info.length
you get (with 1.6):The 8 here is invalid, but it's such a small divergence that it doesn't affect anything and no segfault happens. 1.4 and earlier segfaults after the first 3 echoes. On devel the 8 becomes 2, but
repr
still gives the same original invalid state. The problem here is that=destroy
getting called more than once for each pointer creates an invalid state rather than anil
state.Expected Output
or something slightly different, I'm not sure.
Additional Information