I'm getting some odd behavior that I don't understand. I've simplified it down to this example:
type
NodeKind = enum A, B
Node = object
case kind: NodeKind
of A:
children: seq[Node]
of B:
value: int # <-------- this line
const root = Node(kind: A, children: @[Node(), Node()])
static: echo "COMPILETIME CHILDREN ", root.children.len
echo "RUNTIME CHILDREN ", root.children.len
When run it produces this:
COMPILETIME CHILDREN 2
RUNTIME CHILDREN 0
However if you change the marked line to discard:
COMPILETIME CHILDREN 2
RUNTIME CHILDREN 2
Changing the const to var produces the correct result either way, but the actual code this is based on needs to be a const.
I'm not sure if this is a bug or if I'm just misunderstanding something
i don't know what's going on either, but swapping A and B:
Node = object
case kind: NodeKind
of B:
value: int
of A:
children: seq[Node]
works
looking at the generated C code this is definitely a bug
when root is instantiated _both root.value and root.children are initialized if root.value is set to 0 after root.children has been set, it overwrites the seq pointer
which isn't great, but maybe that's not what const is for, i'm new here
allow me to suggest
let root{.compiletime.} = Node...
which works all the ways
Thanks @mratsim for pointing me to those issues! I did not find them before. It looks like this isn't supposed to work and should be flagged by the compiler.
@shirleyquirk thank you for also looking into the issue. I had tried that but with my object init proc it only works if the procedure is also called at runtime. You can test this by using:
proc initNode: Node =
echo "init node"
result.kind = A
result.children = @[Node(), Node()]
and changing the const decl to
let root{.compiletime.} = initNode()
When run you will see that initNode is called at compiletime and also at runtime. This doesn't work for my usecase where the information is only available at compiletime. If the proc is marked with the {.compileTime.} pragma it is not called at runtime and the fields are zeroed out again.
Just for completeness here are the things I tried before creating this forum post:
const root = initNode()
# doesn't work as discussed above, some fields are zeroed out at runtime
let root {.compileTime.} = initNode()
# doesn't work as explained above, initNode must be called also at runtime to work correctly
let root = static(initNode())
# doesn't work, same as example 1
var root {.compileTime.}: Node
static: root = initNode()
# stops initNode being called at runtime without the {.compileTime.} pragma on the proc, but fields are still zeroed out
It looks like this isn't supposed to work and should be flagged by the compiler.
It's more like it was a use-case that was missed and should be added to the compiler.
Current workarounds: