Something funky about UInt32 comparisons

The only reference in the documentation is that both expressions must be of the same type.
Greater than or equal
So it seems that Xojo’s compiler lacks type promotion but have undefined behaviour.

Var Value As UInt64 = 18446744073709551615
If Value <= 0 Then

0x10011e679 <+89>:  xorl   %eax, %eax
0x10011e67b <+91>:  movb   %al, %cl
0x10011e67d <+93>:  movq   $-0x1, -0x10(%rbp)
0x10011e685 <+101>: movq   $-0x1, -0x20(%rbp)
0x10011e68d <+109>: movb   $0x1, -0x11(%rbp)
0x10011e691 <+113>: testb  $0x1, %cl
0x10011e694 <+116>: jne    0x10011e698               ; <+120>

So this compares the first bit of the 64-bit integer.

with static variable it compares zero to the value, but uses setle to get a boolean for whether it is <=:

0x10011e671 <+97>:  movq   %rax, -0x18(%rbp)
0x10011e675 <+101>: cmpq   $0x0, %rax
0x10011e679 <+105>: setle  %cl
0x10011e67c <+108>: andb   $0x1, %cl
0x10011e67f <+111>: movb   %cl, -0x9(%rbp)
0x10011e682 <+114>: cmpb   $0x0, %cl
0x10011e685 <+117>: je     0x10011e69d               ; <+141>

With both static variables we can try to avoid some optimization:

0x10011e669 <+121>: movq   0x269e0(%rip), %rax       ; Window1.Window1.__Init.Event_Open.Value
0x10011e670 <+128>: cmpq   0x269e1(%rip), %rax       ; Window1.Window1.__Init.Event_Open.ZeroValue
0x10011e677 <+135>: setbe  %cl
0x10011e67a <+138>: andb   $0x1, %cl
0x10011e67d <+141>: movb   %cl, -0x9(%rbp)
0x10011e680 <+144>: cmpb   $0x0, %cl
0x10011e683 <+147>: je     0x10011e69b               ; <+171>

right would probably be the ja instructions like Xcode uses it to compare UInt64:

Ltmp0:
	.loc	2 16 12 prologue_end    ## test/main.c:16:12
	cmpq	$0, _Value(%rip)
Ltmp1:
	.loc	2 16 6 is_stmt 0        ## test/main.c:16:6
	ja	LBB0_2
1 Like

You mean

20 years ago compilers already have found a way to deal with this:

The compiler internally has a special “undecided type” type for literal values, and it converts it to a matching type only when it comes into an expression with a specific type:

uint-var op literal → literal becomes uint
int-var op literal → literal becomes int

(op being any operator, e.g. “+” or “>”)

That’s a proven concept, and it would avoid the miscalculation that this report points out.

What if the literal is negative? Then that solution goes pear-shaped.

Using the first bit though would work for a comparison with 0, but not for a comparison with another number.

The basic problem is that signed and unsigned are two different ranges, one centered around zero (eg -10 to 10), the other everything moved to the positive (eg 0 to 20) … and if you compare then you have to decide which range you want to compare, you can’t shoehorn one into the other.

You COULD cast everything to 64bit which makes the problem less prevalent but doesn’t solve it.

It’s some work to do this in a compiler math correct.

e.g. for

if uintvar < intvar then

the compiler needs to generate something like

if intvar >= 0 and uintvar < UInt(intvar) then

to make it mathematical correct.
Currently this is left to the Xojo developer to know about edge cases and work around them.

I am very well aware of that. If I understand what Tempelmann wrote - and I very well may not - the solution is to use a hidden data type that developers don’t have access to. I believe that would only require one bit more than the largest integer type. So on comparison, both sides would be promoted to this special data type in order to be compared correctly. Again, not my area of expertise.

I don’t believe we should just be throwing our hands up and saying “well computers are funny, tough luck.” Anybody reading the code should be able to see what was intended. The compiler should be able to see that too.

1 Like

This already exists as TextLiteral data type for text vs. string in the compiler.

It’s a bug related to the incapacity of THIS compiler to infer correctly values, specially literals. I remember a conversation with Joe, the compiler guy, about something like this for Currency where something like Dim v As Currency = 1234.4567 resulted in a different value being stored in the currency because the compiler did not handle currency literals directly, handled as a floating point and converted the value after resulting in FP distortions.

I’m not seeing your logic here. That a case has been verified for a long time doesn’t mean it’s impacting a lot of people. This particular case doesn’t even have a rank, that’s how few people are impacted. Ideally we’d be able to fix every bug but you know that’s not practical.

Regarding this bug, it appears from the comments Aaron Ballman made on the original case that this behavior is by design for backwards compatibility but I’d have to have an engineer review it again to make sure that’s really true.

It does appear to be a duplicate of case 2218 which was closed as by design. But again, I’d need to review it further to be absolutely sure of that.

I hope you don’t operate under the illusion that because a case doesn’t have a rank that people aren’t affected by it.

9 Likes

Xojo does have some real problems in not meeting developers expectations, when it comes to some cases of integer handling.

But none of those cases is stopping an app to ship, or urges a developer to complain.
Still it erodes the trust in the compiler and the company.

7 Likes

Wrong behavior for the sake of backwards compatibility is still wrong behavior. You’ll simply never convince me that this is a proper comparison. Other compilers can handle this. Xojo’s should too.

7 Likes

Xojo should even be better and protect the unexperienced developer from mistakes!

e.g. by doing extra steps for UInt data types as shown above.

2 Likes

<https://xojo.com/issue/40465>

I reported this issue some years ago and it was closed as not a bug.

But I reported it for 64bit vs 32bit but similar issue.

Yeah it’s the same issue. Based on my recent conversations with Norman, his mind definitely seems changed regarding wether or not this is a bug. It can be explained, but that doesn’t mean it isn’t a bug.

Given that you quoted me, you know that’s not what I said. I never said that people aren’t affected. I said that when a case doesn’t have a rank, few people are impacted by it. The ranking is a big factor in helping us identify which cases are having the greatest impact. That a case is entered at all means someone is affected.

To be fair to Xojo, there is a warning if you have it turned on. It’s very clear about what is happening. I still think it’s wrong, but at least there is a warning about it.

Edit: Unfortunately, the warning is so absurdly noisy, it might as well not exist. My project has 843 warnings about it.

1 Like

We definitely need to make the errors and warnings more clear and that’s something we plan to address. This isn’t the only one that could be made more clear.

1 Like

Maybe I’m alone in this, but I dont put feedback points on bugs. In my view, those should be fixed regardless and not need users complaining about them to be fixed.

4 Likes

In this case it’s noisy because numeric literals are always Int32 or Int64, depending on build architecture. So any numeric type that isn’t declared as Integer triggers the warning. And really, the warning is 100% correct. It’s the compiler that isn’t being smart enough to do something sane. It means if I want to assign 0 to my UInt64 property, I need to use Self.mMask = CType(0, UInt64) to avoid the bug and therefore the warning.

That’s not ok.

3 Likes