There seems to be a bug, however, by which this check doesn't catch such an
error, even in debug mode.
For example, try the following example.
type
Kind = enum
Zeroth,
First,
Thing = object
case kind: Kind
of Zeroth:
f: float
of First:
i: int
var t = Thing(kind: Zeroth, f: 3.14)
t.kind = First
echo t.i
The code erroneously interprets the float as an int! This is a major safety
issue.
Output: 4614253070214989087
I believe that this is a bug (see above issues), because the inverse situation:
var t = Thing(kind: First, i: 123)
t.kind = Zeroth
echo t.f
raises an exception as it should. In fact, I believe (don't quote me on this,
but I think I'm understanding correctly), that this is caused by the
FieldDiscriminantCheck proc in system/assign.nim!
The proc is fairly simple:
proc FieldDiscriminantCheck(oldDiscVal, newDiscVal: int,
a: ptr array[0x7fff, ptr TNimNode],
L: int) {.compilerProc.} =
var oldBranch = selectBranch(oldDiscVal, L, a)
var newBranch = selectBranch(newDiscVal, L, a)
if newBranch != oldBranch and oldDiscVal != 0:
sysFatal(FieldError, "assignment to discriminant changes object branch")
So this would explain how this bug comes about. The first listed enum variant
will be encoded as 0. Here this is Zeroth. When attempting to change the branch
from First to Zeroth, both conditions pass, and the exception is properly
raised. Yet, in the broken example, the second condition fails.
I am not sure why this condition is here. I assume that there is some
significance of a discriminant of 0, but it seems to cause a conflict with the
enum encoding, and it definitely breaks memory safety.
This may be really easy or really hard to fix...