I'm porting a Delphi project to 64 bits and I have a problem with a line of code which has the IN
operator.
The compiler raise this error
E2010 Incompatible types: 'Integer' and 'Int64'
I wrote this sample app to replicate the problem.
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
Var
I : Integer;
L : Array of string;
begin
try
if I in [0, High(L)] then
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
readln;
end.
This code works ok in 32 bits, but why doesn't it compile in Delphi XE2 64 bits? How I can fix this issue?
*UPDATE *
It seems which my post caused a lot of confusion (sorry for that) , just for explain the original code which i'm porting is more complex, and I just wrote this code as a sample to illustrate the issue. the original code uses an in operator to check if a value (minor than 255) belongs to a group of values (all minor or equal to 255) like so
i in [0,1,3,50,60,70,80,127,High(LArray)]
This code can't be compiled because the High
function is returning a 8 byte value, which is not a ordinal value. and the In operator can be only used in sets with ordinal values.
FYI, the size of the results returned by the High function is different depending of the parameter passed as argument.
Check this sample
Writeln(SizeOf(High(Byte)));
Writeln(SizeOf(High(Char)));
Writeln(SizeOf(High(Word)));
Writeln(SizeOf(High(Integer)));
Writeln(SizeOf(High(NativeInt)));
Writeln(SizeOf(High(TBytes)));
Finally, you can fix your code casting the result of High function to integer.
if I in [0, Integer(High(L))] then
UPDATE
Check the additional info provided by David and remember to be very careful when you use the in
operator to check the membership of a value in set with variable values. The in
operator only checks the least significant byte of each element (in delphi 32 bits).
Check this sample
i:=257;
Writeln( 1 in [i]);
This return true because the low byte of 257 is 1.
And in Delphi 64 bits, the values greater than 255 are removed of the set. So this code
i:=257;
Writeln( 1 in [i]);
will return false because is equivalent to
Writeln( 1 in []);
What RRUZ says is quite correct.
To add a little bit more explanation, in 64 bit Delphi, dynamic array indices can be 64 bits wide. This is clearly needed, for example, when working with a large TBytes memory block. And so the high
function must return a value of a type wide enough to hold all possible indices. So, high
when applied to a dynamic array, returns a value of type Int64
.
Once you start compiling 64 bit code the in
operator is unsuited to the problem you are trying to solve. Whilst you could use the cast that RRUZ suggests, it may be clearer to write the code like this
if (I=low(L)) or (I=high(L)) then
Whilst the in
operator makes for quite readable code, it is my opinion that a cast to Integer
is not acceptable here. That will simply set a trap for you to fall into when you first have an array with more than high(Integer)
elements. When that happens the code with the cast will stop working.
But in fact the problems run far deeper than this. The in
version of the code fails long before you reach high(Integer)
elements. It turns out that your code, whilst it compiles, does not really work. For example, consider this program:
program WeirdSets;
{$APPTYPE CONSOLE}
uses
SysUtils;
var
a: array of Integer;
begin
SetLength(a, 257);
Writeln(BoolToStr(Length(a) in [0, Length(a)], True));
end.
You would expect this program to output True
but in fact it outputs False
. If instead you were to write
Writeln(BoolToStr(Length(a) in [0, 257], True));
then the compiler reports:
[DCC Error] WeirdSets.dpr(9): E1012 Constant expression violates subrange bounds
The fundamental issue here is that sets are limited to 256 elements so as soon as you have an array with length greater than that, your code stops working.
Sadly, Delphi's support for sets is simply inadequate and is in urgent need of attention.
I also wonder whether you actually meant to write
if I in [0..High(L)] then
If so then I would recommend that you use the InRange
function from Math
.
if InRange(I, 0, High(L)) then
or even better
if InRange(I, low(L), High(L)) then
The most serious problem with the OP code is that in
operator is limited to set
size, i.e. [0..255]. Try this in any 32 bit version of Delphi to avoid the 64 bit issue:
var
I: Integer;
L: array of Integer;
begin
SetLength(L, 1000);
I:= 999;
Assert(I in [0, High(L)]); // fails !
end;
The OP is lucky if Length(L) <= 256
always, otherwise it is a bug you probably never thought of.
To find this bug switch range checking on:
{$R+}
procedure TForm1.Button2Click(Sender: TObject);
var
I: Integer;
A: array of Integer;
begin
SetLength(A, 1000);
I:= 999;
if I in [0, High(A)] then ShowMessage('OK!'); // Project .. raised exception
// class ERangeError with message 'Range check error'.
end;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With