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...

Reply via email to