possible bug: casting arrays

BlitzMax Forums/BlitzMax Programming/possible bug: casting arrays

Yasha(Posted 2015) [#1]
I've encountered a behaviour that looks like a bug, but it might be by-design so let's talk it through first.

Consider this code:



I create two arrays of b instances, then successfully cast the arrays to type c[] and even Int[], without ever using naughty pointer hacks. WTF.

I had previously assumed array casting worked like object casting, and that the system would return null if the array had a base element type that didn't match the requested one. But in this example, arrays with a base element type of b - no one-way relation to c, [i]certainly no relation to Int - are cast to incompatible types without returning null!

I found two exceptions where the casts did work like expected: casting to an array type from Object seems to return Null for the wrong element type (lending credence to the idea that this is how it's supposed to work), and in some situations (but not others), casting to a specific array type from Array doesn't work and causes a compile-time error (but as shown in the example, it does work if you simply go through an intermediate variable).


What is the expected correct behaviour when casting arrays? Surely not this yawning hole in type safety, right?

(There's obviously some room for debate on the topic of what should happen, e.g. if you create an a[] and populate it with b instances and then cast to b[] - but you surely shouldn't be able to cast it to c[]!)


Yasha(Posted 2015) [#2]
I've encountered a behaviour that looks like a bug, but it might be by-design so let's talk it through first.

Consider this code:



I create two arrays of b instances, then successfully cast the arrays to type c[] and even Int[], without ever using naughty pointer hacks. WTF.

I had previously assumed array casting worked like object casting, and that the system would return null if the array had a base element type that didn't match the requested one. But in this example, arrays with a base element type of b - no one-way relation to c, certainly no relation to Int - are cast to incompatible types without returning null!

I found two exceptions where the casts did work like expected: casting to an array type from Object seems to return Null for the wrong element type (lending credence to the idea that this is how it's supposed to work), and in some situations (but not others), casting to a specific array type from Array doesn't work and causes a compile-time error (but as shown in the example, it does work if you simply go through an intermediate variable).


What is the expected correct behaviour when casting arrays? Surely not this yawning hole in type safety, right?

(There's obviously some room for debate on the topic of what should happen, e.g. if you create an a[] and populate it with b instances and then cast to b[] - but you surely shouldn't be able to cast it to c[]!)


zzz(Posted 2015) [#3]
What output do you get from this? "Valid" casting or not, I cant quite get my head around why this is behaving as it is.

Edit: nvm, messed up converting the output.

Well, it obviously breaks badly if the extending types are of different size (different number of fields), allowing what should be safe variables to reference random memory.


Yasha(Posted 2015) [#4]
Among other things, this mechanism could easily be reversed to allow you to convert an array of ints or floats to an array of objects. Segfaults and heap corruption ahoy. (Although admittedly an unlikely scenario simply because of how uncommon it is to actually use Array as the type for anything.)

Thinking about it some more, my guess is that the motivating design issue was that you need to be able to cast down past the "created type" of an array in order for TList.ToArray to work: since it doesn't know the type of the TList's elements, it can only directly create the returned array as an Object[] (that there being a solid argument for having generics in a language).

However, that doesn't explain why there's a) no check of element types even in Debug mode (I could see it being left off for performance in Release mode), and b) why Array doesn't perform the same check as Object, since there's no clear obstacle to doing so and it clearly works in at least one place. The check is clearly possible since arrays work correctly with EachIn.

EDIT: actually no they don't... add this to the above example:

Print "eachin (correct):"
For Local x:c = EachIn arr5
	Print x.i
Next

Print "eachin (incorrect):"
For Local x:c = EachIn arr2
	Print x.i
Next


EachIn arr5 works correctly, as the requested element type is a subtype of the declared array type (Object[]) and it performs the check and never prints anything. EachIn arr2 appears to skip the test as the array is already typed "correctly" for the requested elements (c[]) - this would be a legitimate optimization, if it weren't for the above example showing that you can't always rely on the elements being that type! (OK the optimization is still legit, the problem is that the above issue re-breaks it)