Hi,
I'm very new to Nim and got a question. For a binary tree I tried object variants and the "not nil" annotation. The following compiles (not explicitly initializing each field):
t = VTree(kind: nkNode, value: x)
but crashes:
SIGSEGV: Illegal storage access. (Attempt to read from nil?)
Why is the "not nil" annotation not checked?
type
NodeKind = enum
nkEmpty,
nkNode
VTree = ref VNode not nil # not nil ignored?
VNode = object
case kind: NodeKind
of nkEmpty:
discard
of nkNode:
value: int
left, right: VTree not nil # not nil ignored?
let empty = VTree(kind: nkEmpty)
proc insert(t: var VTree, x: int) =
case t.kind
of nkEmpty:
# compiles, but:
# SIGSEGV: Illegal storage access. (Attempt to read from nil?)
# t = VTree(kind: nkNode, value: x)
# works fine:
t = VTree(kind: nkNode, value: x, left: empty, right: empty)
of nkNode:
if x < t.value:
t.left.insert(x)
elif x > t.value:
t.right.insert(x)
var vn: VTree = empty
vn.insert(3)
vn.insert(30)
vn.insert(1)
vn.insert(5)
I would be grateful, if somebody could shed light on this.
Thanks!
It works, but because of "not nil"...
VTree = ref VNode not nil
...
left, right: VTree not nil
... I was expecting the compiler to catch the problem that this...
let empty = VTree(kind: nkEmpty)
... leaves left and right nil. Otherwise what is the point of "not nil"?
I think it is a compiler bug. IMHO it should work analog to this:
type
XNode = object
value: int
left, right: ref XNode not nil
let xn1 = XNode(value: 1)
#> Error: field not initialized: left
echo xn1.repr
let xn2 = XNode(value: 1, left: nil, right: nil)
#> Error: type mismatch: got (nil) but expected 'ref XNode not nil'
echo xn2.repr
If you remove the "not nil" both lines compile and generate the same object (with nil for left and right)
I don't see why that should be different with an object variant?
Where does it change the node? It will be replaced with the new one. If it would change the node kind it had to use "system.reset()" and modify the kind "field".
Another example for what I think is a bug. Notice there is only one kind in the object:
type
nKind = enum nOne
XNode = object
case kind: nKind:
of nOne:
value: int
left, right: ref XNode not nil
let xn1 = XNode(value: 1)
# Works but ends up with nil
echo xn1.repr
let xn2 = XNode(value: 1, left: nil, right: nil)
#> Still gives Error: type mismatch: got (nil) but expected 'ref XNode not nil'
echo xn2.repr
@OderWat,you are right,it didn't check for those default nil values.
And the type was matched nkNode because After insert it has a value field.
var vn: VTree = empty
echo vn.kind #it gives nkEmpty.
vn.insert(3)
echo vn.kind #it gives nkNode.