Little details have big consequences

| | Comments (5)

A bug report wandered its way into my field of vision that seemed to make no sense. If you had an enumeration that wasn't 32-bits in size, you couldn't have it as a return value from a method, or as a property type. What's more, the error would tell you it was with the class' name, and give you a confusing error message! The error read: Expected (some integer type), but got (some enumeration type) -- what the heck? The return value is pretty explicit about being that enumeration type, so what could possibly be going wrong?

Well, it turns out that sensible details happen to cause much bigger issues that you might think!

We have a design constraint with enumerations with regards to how data comes in and out of them. The rule is this: enumerations require an explicit typecast to put an integer into them, or to get an integer out of them. So it's not legal to say someEnum = 0, or dim i as Integer = someEnum. You have to use the casting operator.

Which brings us to a second design constraint, one with the casting operator. I've discussed the casting operator before. But let's recap here. Basically, you can only cast the bits (change their meaning), not convert the bits (change their size). So that means you can cast from an Int32 to a UInt32, but you cannot cast from a UInt32 to a UInt8.

The final part to this problem isn't so much a design constraint so much as it is an optimization. Variants will always convert integer values less than 32-bits in size to be 32-bit integers (of the proper sign) when storing their data. This should clue you in on where the problem comes in -- if you try the following code, you'll get a compile error in 2008r2 and earlier:


Enum Test as UInt8
Foo
End Enum

dim v as Variant = Test.Foo


The reason you get that compile error is because the variant code was trying to convert an enumeration to be a UInt32, sort of like this:

dim i as UInt32 = UInt32( Test.Foo )
dim v as Variant = i

Of course, that code won't work because there's no explicit conversion operator that will work, since Foo is a UInt8, not a UInt32. Basically -- the compiler was creating its own type mismatch exception when trying to handle converting an enumeration to a variant.

Instead, the compiler needs to generate code more like this:


dim i as UInt8 = UInt8( Test.Foo )
dim i2 as UInt32 = i
dim v as Variant = i2

This won't have the type mismatch exception because the first operation gets the enumeration to an integer of the proper size. Then it handles the conversion via the only way currently exposed -- implicit conversions with assignment. Finally, it can make the conversion into a variant. If this doesn't scream "REALbasic needs an explicit conversion operator".... ;-)

So back to the original bug report -- why would return values cause a problem? Because of Introspection! The compiler generates some stubs for introspection to work properly, and those stubs rely on variants. Everything must be storable into a variant, or else the whole introspection system comes grinding to a halt. So the code the compiler generated for introspection was trying to assign the enumeration into a variant behind the scenes, and since there's no way for the IDE to show you this hidden code it did the best thing it could: tell you about it on the item's name.

Sometimes the little details cascade together to have larger consequences, eh?

5 Comments

Nice catch.

I'm glad the introspection system exercises variant assignment; it's a nice way to ensure that all possible datatypes can be converted to variant and back.

It might be fun to add ByRef support to the introspection stubs sometime: you could do this by iterating through the parameters, post-call, and generating code to assign the temp value back to the parameter array element. This is slightly different than the normal ByRef behavior, but less different than the current absence of support!

@Aaron

I ran into this very thing a week ago. I was trying to write a 32 bit enum to a file using BinaryStream.WriteInt32. For the life of me I couldn't figure out why a 32 bit enum wasn't acceptable. In frustration I tried explicit casting and it worked fine.

Now I know why!

Very interesting and informative, as usual.

Sounds like my bug report... That error really surprised me.

If it is, I got an Email saying it is fixed ... So did you re-write the introspection code?

- Karen

@Karen -- I think you did report it , and no -- I just fixed the bug. :-P Nothing nearly so crazy, just needed to be able to convert enumerations into variants.

Leave a comment