Class loses properties after method call

Yes.

New data suggests it’s even weirder than we thought.

If you turn on “Show Object IDs” in the debugger, you can see each object’s pointer. Same pointer means it’s literally the same object. Different pointer suggests a new object has been created.

Here’s what I see.
(Note that the pointers are weird, the differences are in the high order bytes not the low order bytes. I wonder if these are being displayed wrong?)

In any case, we see 3 different objects!

This is not a case where objects have been Casted improperly after all.

The only way this could be happening (suggested above by @Christian_Schmitz and others) is if there’s a Operator_Convert() happening.

Unfortunately Operator_Convert() does not fire the Constructor() - so debugging this is difficult since you can’t simply put a breakpoint.

Poking around in Introspection more, I realize that Rect has two converters:

Operator_Convert() as Rect // converts to Rect
Operator_Convert(rhs as Rect) // converts from Rect

However, I still think there’s a bug, because it should simply be impossible for Operator_Convert() defined in a Superclass to return a new instance of a Subclass.

In fact, when I try to reproduce this (using only my own classes, not Xojo.Rect) I can’t - the compiler sensibly refuses to compile.

I Googled this and it looks like some languages do support this kind of functionality.

Maybe this is a side effect of Xojo updating the version of llvm they are using.

If this didn’t work in previous versions of Xojo then they possibly need to re-enforce it again.

Given

Class Animal
  Method Operator_Convert() as String
  Method Operator_Convert(rhs as String)

Class Human inherits Animal
Class Dog inherits Animal
var Fido as new Dog
var Bob as Human = Fido   // Type mismatch error.  Expected class Human, but got class Dog
var Fido as new Dog
var s as String = Fido
var Bob as Human = s  // this compiles and runs!

Now I’m really confused - it appears that Operator_Convert() defined in a superclass can create an instance of a derived subclass. I didn’t think that was legal.

1 Like

If instead of String, I use REALbasic.Rect, then it compiles and runs.

Class Animal
  Method Operator_Convert() as REALbasic.Rect
  Method Operator_Convert(rhs as REALbasic.Rect)

Class Human inherits Animal
Class Dog inherits Animal

var Fido as new Dog
var Bob as Human = Fido   
// this compiles and runs now.  
// At runtime, Operator_Convert() is called once to create an intermediary object
// which then gets Operator_Convert()ed  into the final instance.

in fact, it seems that any xojo class (DatabaseColumn, StyleRun…) when used as the common to/from converter will work.

However, if you use an intrinsic type such as String, or a class you defined such as “MyClass”, then it does not work.

Putting this in bug report terms:

"If two classes ClassA and ClassB have a common superclass, and the superclass has Operator_Convert() to and from a common type, assigning an instance of ClassA to ClassB will succeed, but only when the common type is a framework class (such as StyleRun, REALBasic.Rect, etc.). If the common type is an intrinsic (such as String) or a user-defined class, the compiler throws a Type Mismatch error.

Nice to see that the doubts about my programming skills were unjustified for the last two days. :grin:

2 Likes

On behalf of those who have never saw motivation to question your ability, all I have here is gratitude for you finding something weird that moved us to a deeper analysis to conclude that something seems conceptually very wrong, wrong to the point that potentially it may be causing an unbelievable mess silently.

5 Likes

Self-doubt is nothing to be ashamed of … :hugs:

1 Like

I just posted this on the case, but want to put it here for discussion as well…

Looking at all this and trying out the example project, my guess is that you guys are correct that the Rect “class” has an Operator_Convert on it that returns a Rect, but what it does internally is probably the issue.

NOTE: As I no longer have access to the source, this is only a guess.

My guess is that the Operator_Convert method is trying to be smart to return a copy of itself instead of another instance of itself. The internals might look like:

Function Operator_Convert() as Rect
  Dim r as new Rect
  r.left = self.left
  R.top = self.top
  r.width = self.width
  r.height = self.height

  Return r
End Function

And if you think about how rects are often used, this is a perfectly good solution because you wouldn’t usually want to be pointing to the same exact rect when you get a copy of one.

I would also guess that “fixing” this bug would cause all kinds of unintentional behavior changes in both user projects and in the IDE itself because the framework itself probably relies on this behavior.

So creating a MyRect class, then subclassing that, would probably be a good way to resolve this?

It’s broken. Don’t say “don’t fix it, because the IDE may be counting in some wrong bad behavior”, a wrong behavior should not exist and should not be silently messing the users code. Just fix everything at once, you can force compiler errors in the IDE code pointing out places needing review, Xojo should just fix everything at once instead of sweeping bugs under the carpet. The bugs under the carpet will grow there and spread to the entire house.

3 Likes

Basically, yes. This behavior makes it dangerous to subclass Rect. Unless/until fixed, I’d make a class that has a property of type Rect instead.

If a Clone method was needed, one could’ve been added as a method, or another constructor that had a Rect parameter. The current implementation is just wrong. It might’ve seemed clever to whoever did it, but it yields unexpected results and should be corrected.

4 Likes

We’ll have to agree to disagree. I prefer the Rect behavior the way it is now, but I tend to not subclass things like Rect.

Since the Rect class has a clone method, my point of view is that if the operator_convert kept the same instance, this would be the best of both worlds:
• avoid the hard-to-find bug for which this thread is about
• if you want to have a distinct rect (e.g. for moving only one out of two), use the clone method; you expect properties of subclasses to be ignored right when writing the code.

I understand your position. I just don’t agree.

In just about every other language I use, Rect is a structure and writing code like RectA = RectB gets you a copy. If I were to guess, either Rect was a structure and was converted to a class to add the other behaviors or it was designed that way in the beginning to emulate the structure behavior. I just don’t remember, nor do i have the code to look at to confirm my guess.

According to tests performed by others in this topic and on the Feedback Case, REALbasic.Rect does not exhibit this behavior and gives compiler errors. I have not verified these, but take them at their word. That would suggest that it is a relatively new behavior and one that would not be expected by longtime users. I certainly don’t expect a conversion that results in data loss to happen when I’m passing around subclasses.

I won’t re-read the entire thread right now (not enough time for that currently) but I seem to recall some users talked about rect (not REALbasic.rect) also giving compiler errors on earlier Xojo builds (2020 or 2021, IIRC), so your suggestion has one more point.

I think that was an issue with a plugin that has a Rect class which was causing compiler errors, and was also seen in 2022r1.1. That’s noted both here on the thread and in the Feedback Case.

Edit: Confirmed

1 Like