IllegalCastException even when controls share a common parent

Feedback Case Number: https://tracker.xojo.com/xojoinc/xojo/-/issues/76713

Here’s a fun one to look out for. I suspect it has something to do with the fact that the new API2 controls have some magic happening under the hood.

Var Controls() As DesktopUIControl = Array(TextField1, TextField2, TextField3)
If PopupMenu1 IsA DesktopUIControl Then
  Controls.Add(PopupMenu1)
  MessageBox("No bug")
Else
  MessageBox("PopupMenu1 is not a DesktopUIControl")
End If

You’ll get an IllegalCastException on Controls.Add(PopupMenu1). The controls are their obvious types and do exist. The bug report has a sample project.

Is PopupMenu a DesktopPopupMenu or an old API 1 PopupMenu? PopupMenu (API1) will not mix with API2 controls and vice versa.

OK, I can see looking at the attached project it is the correct type. Odd issue.

I think the IllegalCastException is correct, but on the wrong line of code. :slight_smile: It should be on the first line. I don’t see how you should be able to assign an array of DesktopTextField to a variable of type DesktopUIControl().

Xojo’s handling of arrays has always bothered me - functionally there are somewhere between intrinsics and objects - and this just makes me itch even more.

It won’t let you get away with this abstract analogue:

dim mySubClassOnes() as SubClass1

mySubClassOnes=Array(new SubClass2)

…where both subClasses inherit from a common SuperClass.

Yeah, I think you’re correct. I mentioned in the case that the array is likely actually an array of DesktopTextField, and I’m more convinced now. We know that Array only works for a consistent type and is unable to find a common parent, so there’s no doubt in my mind that it’s an array of DesktopTextField. But it is allowed to be assigned to an array of DesktopUIControl, which may or may not be a bug.

Correct.

Dim v as variant = array(TextField1)

…gives you an array of DesktopTextField.

Ah! But THIS fails:

dim v as variant

v=Array(TextField1)

dim myControls() as DesktopUIControl

myControls=v

with the expected TypeMismatchException. That’s correct behavior.

This works though…

Var Controls() As DesktopUIControl 
controls.add TextField1
controls.Add TextField2
controls.Add TextField3

If PopupMenu1 IsA DesktopUIControl Then
  Controls.Add(PopupMenu1)
  MessageBox("No bug")
Else
  MessageBox("PopupMenu1 is not a DesktopUIControl")
End If

I suspect that Controls array is actually being declared as an array of DesktopTextField because that’s what the array is… so a compiler bug.

1 Like

The bug must be somehow specific to those Desktop* controls, because this fails to compile:

dim mySubClassOnes() as SubClass1 = Array(new SubClass2)

Well, in an api2 project, DesktopTextFields are DesktopUIcontrol subclasses, so it does not surprise me, but the behavior is definitely squirrelly.

Casting ALL items as the ancestor (it shouldn’t need to) works

Var Controls1() As DesktopUIControl = Array(_
  DesktopUIControl(TextField1), _
  DesktopUIControl(TextField2), _
  DesktopUIControl(TextField3)_
)

If PopupMenu1 IsA DesktopUIControl Then
  Controls1.Add(PopupMenu1)
  MessageBox("No bug")
Else
  MessageBox("PopupMenu1 is not a DesktopUIControl")
End If

There’s something wrong with the Array type resolution.

This breaks things:
image

But empty it works:
image

In both cases the arrays seems of the DesktopUIControl() type, but acts as if it is not when having content of a not exact type.

There isn’t. Array requires all members to be the exact same type. It makes no attempt to find a common ancestor, and also doesn’t care what it is being assigned to.

For example, these both don’t work:

Var Controls() As DesktopUIControl = Array(TextField1, PopupMenu1)
Var Numbers() As Integer = Array(1, 1.0)

So in my example code, we are creating an array of DesktopTextField and assigning it to a property defined as DesktopUIControl. That doesn’t convert it to an array of DesktopUIControl, so trying to add a DesktopPopupMenu to an array of DesktopTextField rightly fails.

My example code is effectively equivalent to this code, which compiles just fine, but exhibits the same exception:

Var TextFields(2) As DesktopTextField
TextFields(0) = TextField1
TextFields(1) = TextField2
TextFields(2) = TextField3

Var Controls() As DesktopUIControl = TextFields
Controls.Add(PopupMenu1)

What I can’t decide is whether or not assigning an array of DesktopTextField to an array of DesktopUIControl is a bug. It feels wrong, but maybe there is a use case I’m not thinking of.

In a

Var Controls1() As DesktopUIControl = Array(TextField1)

Controls1 should be a DesktopUIControl() array accepting any DesktopUIControl descendant
It even lists as one in the debug, but the compiler acts like ignoring this and considering it as a DesktopTextField() array.

If this works

image

This should too

image

The declared variable is not acting as it was designed

Bug

If you want to open an issue to request that Array finds common ancestors and/or casts, you are welcome to. But right now that’s not what it does. It creates an array of DesktopTextField because that’s what it is designed to do.

That’s not what’s is going on and not the fix.

The Controls1 variable MUST be an Array of DestopUIControl as the dev requested,
if it isn’t… Bug.

There’s a bugged resolution and conversion in the array attribution TypedArrayVar=TypedArrayContent

The necessary type we already have, its pointed in TypedArrayVar, in this case it is DesktopUIControl, on the content side, ignoring any enhancements, today we know that Xojo already solves the array type to some unique one, in this case DesktopTextField, all you need is to ask after this part of the resolution if DesktopUIControl is an allowed receiver of DesktopTextField. Coincidently in this case it is (If not, a compiler error about type resolution). Now you need just instantiate an array of DesktopUIControls and set its contents to the listed DesktopTextFields

Think of Array as a function. When you call a function, you have absolutely no idea what the result will be assigned to. So there are two actions going on:

  1. Create the array with Array
  2. Assign it to the variable

What you’re asking for is Array to consider what it is being assigned to or used for it. It doesn’t do that. Maybe the compiler could - I don’t know, I’m not a compiler engineer - but it’s not what it does. I can’t think of much in Xojo that perform specialized tasks such as that. The If() function comes to mind. But it’s the exception, not the rule.

Again, if you want to open a feature request to improve Array(), you can do that.

1 Like

Literal arrays are functions in Xojo (and I don’t know how), but not in any 99% of other languages where they are solved at compile time. But… I was trying to help the community on the understanding of the nature of the fail, just it. I have no hopes that Xojo will fix this bug.

And If you don’t want it solved, my hopes are even lower. :smile:

Just do some of the workarounds we already discussed.

It’s a type mismatch error. There is no abstract Array type in Xojo, despite what the documentation sometimes implies. It’s always “array of (some type)” and that’s why your code fails. You’re assigning an array of type A to a variable of type B - the compiler should throw a big stinkin’ fit before it even compiles, because it knows enough about the types involved.

I’ve run into plenty of situations where the notion of casting an array of some subclass to its super class would be useful. At this point it has to be done manually, looping through and creating a new array with casted versions of the elements of the prior array – which is probably what any Xojo framework code would have to do in any case – but it would be nice to have language support because it would allow compile-time type checking, and the terminology could make it clearer in code, something like how we currently cast objects:

dim subClassArray() as someSubClass

mySuperArray = mySuperClass(subClassArray)

As an aside, an abstract Array type in Xojo would be awesome and would enable a ton of interesting generic functionality. Perhaps once preemptive threads are done they can find some time for this. :slight_smile: Right now, if you want to duplicate an Array, you have to write a function for every type of Array you’ll ever encounter. If there was a real Array type that all Arrays inherited from, you could write a single generic function that would handle it:

Function DuplicateArray(a as Array) as Array
   //loops through the elements of a and returns a new array containing the same objects
End
2 Likes

Interesting that if you use 2 types then you get a Variant, but if you try to assign an array of 1 type to a Variant it fails.

Works:

Var Controls() As DesktopUIControl = Array(TextField1, TextField2, TextField3)

Error:

Var Controls() As Variant = Array(TextField1, TextField2, TextField3)

Works:

Var Controls() As Variant = Array(TextField1, TextField2, PopupMenu1)

Error:

Var Controls() As DesktopUIControl = Array(TextField1, TextField2, PopupMenu1)

Thank you, is good to learn something every day.

A mess. Sorry. :smile:

All those should work. Better… arrays should be solved at compile time as literals, as VB.Net does for example:

Module ArraysDemoModule

    Class MyStr
    End Class
    
    Class MyStr1
        Inherits MyStr
        Public something As String
    End Class

    Class MyWeirdStr 
        Public something As String
    End Class

    Sub Main()
        
        Dim w() = { new MyStr1 , new MyWeirdStr } ' assumed Object(), OK
        Dim wo() As Object = { new MyStr1 , new MyWeirdStr } ' same but explicit, OK
        
        Dim x() As MyStr1 = { new MyStr1 , new MyStr1 } ' array of MyStr1, OK
        
        Dim y() As MyStr = { new MyStr1 , new MyStr } ' array of the common MyStr, OK
        
        Dim z() As MyStr = { new MyStr1 , new MyWeirdStr } ' Error: Value of type 'MyWeirdStr' cannot be converted to 'MyStr'

    End Sub
    
End Module