Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-devel / 2023-02

Unexpected "Range check error while evaluating constants" when compiling for Win64

14 messages · 6 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Bart

Hi,

This code compiles happily for Win32, but refuses to compile for Win64:
====
program test;
{$mode objfpc}
{$h+}
uses
  Registry;
type
  TA = class
  private
    FRootKey: HKey;
  public
    //Win64/X86_64 Error: Range check error while evaluating constants
(18446744071562067969 must be between -2147483648 and 4294967295)
    property RootKey: HKey read FRootKey write FRootKey default
HKEY_CURRENT_USER;  //-2147483647
  end;
begin
end.
===

Is this "by design" or is it a bug?

On Windows HKEY_CURRENT_USER is defined as HKEY(longint($80000001)); HKey is defined as HANDLE = System.THandle = QWord on 64-bit, but it is DWord on 32-bit.
So infact the value of HKEY_CURRENT_USER would be 2147483649 (as a DWord) on 32-bit, and 18446744071562067969 (as a QWord) on 64-bit?

Shouldn't HKEY_CURRENT_USER et al. be defined as HKEY(DWORD(somevalue)) instead?

Tested with fpc 3.2.2 and fpc main 3.3.1-2495-g6453af40d8

Bart

Reply by email

J. Gareth Moreton
to Bart

If HKey is unsigned, then yes, the definition should be HKEY(DWORD($80000001)).  I do wonder why Win64 produces that particular error though because the final destination is an unsigned 64-bit integer.

Kit

Bart wrote 37 lines
Hi, This code compiles happily for Win32, but refuses to compile for Win64: ==== program test; {$mode objfpc} {$h+} uses Registry; type TA = class private FRootKey: HKey; public //Win64/X86_64 Error: Range check error while evaluating constants (18446744071562067969 must be between -2147483648 and 4294967295) property RootKey: HKey read FRootKey write FRootKey default HKEY_CURRENT_USER; //-2147483647 end; begin end. === Is this "by design" or is it a bug? On Windows HKEY_CURRENT_USER is defined as HKEY(longint($80000001)); HKey is defined as HANDLE = System.THandle = QWord on 64-bit, but it is DWord on 32-bit. So infact the value of HKEY_CURRENT_USER would be 2147483649 (as a DWord) on 32-bit, and 18446744071562067969 (as a QWord) on 64-bit? Shouldn't HKEY_CURRENT_USER et al. be defined as HKEY(DWORD(somevalue)) instead? Tested with fpc 3.2.2 and fpc main 3.3.1-2495-g6453af40d8

Reply by email

Bart
to J. Gareth Moreton
PM J. Gareth Moreton wrote
If HKey is unsigned, then yes, the definition should be HKEY(DWORD($80000001)). I do wonder why Win64 produces that particular error though because the final destination is an unsigned 64-bit integer.

Because QWord(some not so big negative number) = some large positive number.

Bart

Reply by email

J. Gareth Moreton
to Bart

Yeah, of course, since LongInt($80000001), before typecasting to HKEY, is specifically a signed constant.  So obvious!!

Kit

Bart wrote 7 lines
On Sun, Feb 12, 2023 at 6:26 PM J. Gareth Moreton via fpc-devel wrote: > If HKey is unsigned, then yes, the definition should be > HKEY(DWORD($80000001)). I do wonder why Win64 produces that particular > error though because the final destination is an unsigned 64-bit integer. Because QWord(some not so big negative number) = some large positive number.

Reply by email

Yuriy Sydorov
to J. Gareth Moreton
J. Gareth Moreton wrote
Yeah, of course, since LongInt($80000001), before typecasting to HKEY, is specifically a signed constant.  So obvious!!
Declarations like this have been made on purpose:
HKEY_CURRENT_USER = HKEY(longint($80000001));

On Win64 HKEY_CURRENT_USER must be $FFFFFFFF80000001, but on Win32 it must remain $80000001.

See commit 2012220aec67494ac34329dabe8b6a05dacbb9c1 for details.

Don't break it if you decide to fix something.

Error "Range check error while evaluating constants (18446744071562067969 must be between -2147483648 and 4294967295)" is wrong here:

property RootKey: HKey read FRootKey write FRootKey default HKEY_CURRENT_USER;

Since HKEY = THandle = QWord on Win64, the reported range for it is incorrect.

Yuriy.

Show quoted text 11 lines
On 12/02/2023 20:43, Bart via fpc-devel wrote: > On Sun, Feb 12, 2023 at 6:26 PM J. Gareth Moreton via fpc-devel > wrote: >> If HKey is unsigned, then yes, the definition should be >> HKEY(DWORD($80000001)).  I do wonder why Win64 produces that particular >> error though because the final destination is an unsigned 64-bit integer. > Because QWord(some not so big negative number) = some large positive number. > _______________________________________________ fpc-devel maillist  -  https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel

Reply by email

Jonas Maebe
to Yuriy Sydorov
Yuriy Sydorov wrote 6 lines
property RootKey: HKey read FRootKey write FRootKey default HKEY_CURRENT_USER; Since HKEY = THandle = QWord on Win64, the reported range for it is incorrect.

Afaik it's because "default" doesn't support qword values, iirc because of (at least old) Delphi compatibility.

Jonas

Reply by email

Bart
to Yuriy Sydorov: Declarations like this have been made on purpose: HKEY_CURRENT_USER =…
PM Yuriy Sydorov wrote 6 lines
Declarations like this have been made on purpose: HKEY_CURRENT_USER = HKEY(longint($80000001)); On Win64 HKEY_CURRENT_USER must be $FFFFFFFF80000001, but on Win32 it must remain $80000001. See commit 2012220aec67494ac34329dabe8b6a05dacbb9c1 for details.

Found this piece of info:
(https://rt.cpan.org/Public/Bug/Display.html?id=108950)

The Windows SDK defines the HKEY values for the predefined keys by first casting an original literal value (>= 0x80000000) to LONG (signed 32-bit), then to ULONG_PTR (unsigned 32- or 64-bit, depending on architecture), then to HKEY, such as in the following macro definition for HKEY_CLASSES_ROOT in winreg.h:

#define HKEY_CLASSES_ROOT ((HKEY) (ULONG_PTR)((LONG)0x80000000))

For 32-bit builds this results in the final HKEY value being identical to the original literal value, but for 64-bit builds, because of the intermediate signed LONG cast which is then cast to the larger unsigned ULONG_PTR type, the original value is effectively sign-extended to 64-bits, resulting in the most significant 32 bits being set. So in 64-bit architectures HKEY_CLASSES_ROOT is 0xFFFFFFFF80000000, HKEY_CURRENT_USER is 0xFFFFFFFF80000001, and so on.

It seems that most of the Windows Reg* API functions (perhaps all of the ones exposed by Win32API::Registry) compensate for being passed 32-bit versions of those predefined key handles, which apparently is why Win32API::Registry isn't completely broken on 64-bit Perl builds. I only noticed the problem when trying to pass the Win32API::Registry::HKEY_* values to RegOverridePredefKey (used via Win32::API), which seems to be an exception and fails with error code 6 (ERROR_INVALID_HANDLE) when passed 32-bit versions of those handles.

Show quoted text 7 lines
Error "Range check error while evaluating constants (18446744071562067969 must be between -2147483648 and 4294967295)" is wrong here: property RootKey: HKey read FRootKey write FRootKey default HKEY_CURRENT_USER; Since HKEY = THandle = QWord on Win64, the reported range for it is incorrect.

If the reported range is incorrect, then the error is incorrect: the value is perfectly inside the range of a QWord...

So, the question remains: is the compilation error a bug?

Bart

Reply by email

Bart
to Bart: On Sun, Feb 12, 2023 at 10:43 PM Yuriy Sydorov via fpc-devel Found this piece of info:…
PM Bart wrote 8 lines
For 32-bit builds this results in the final HKEY value being identical to the original literal value, but for 64-bit builds, because of the intermediate signed LONG cast which is then cast to the larger unsigned ULONG_PTR type, the original value is effectively sign-extended to 64-bits, resulting in the most significant 32 bits being set. So in 64-bit architectures HKEY_CLASSES_ROOT is 0xFFFFFFFF80000000, HKEY_CURRENT_USER is 0xFFFFFFFF80000001, and so on.

Notice that it seems that I can use QWord($0000000080000001) for HKE_CURRENT_USER on Win64 (at least for RegOpenKeyReadOnly).

Bart

Reply by email

Bart
to Jonas Maebe: Afaik it's because "default" doesn't support qword values, iirc because of (at least old)…
PM Jonas Maebe wrote
Afaik it's because "default" doesn't support qword values, iirc because of (at least old) Delphi compatibility.

OK, I removed the "default".

Theoretically would that lead to problems when storing the value in lfm and reading it back using different bitness versions of Lazarus? Should I even publish such a property?
(I don't think Lazarus has a property editor for HKEY...)

Bart

Reply by email

Ondrej Pokorny
to Bart
Bart wrote 11 lines
On Sun, Feb 12, 2023 at 10:47 PM Jonas Maebe via fpc-devel wrote: > Afaik it's because "default" doesn't support qword values, iirc because > of (at least old) Delphi compatibility. OK, I removed the "default". Theoretically would that lead to problems when storing the value in lfm and reading it back using different bitness versions of Lazarus? Should I even publish such a property? (I don't think Lazarus has a property editor for HKEY...)

I wouldn't publish the HKEY property but create an own enumeration for it:

TRegKey = (rgClassesRoot, rgCurrentUser, ....)

and publish this instead. Pro: you can put only valid values into the property and object inspector shows you nice enum names instead of strange integers.

Then make a simple transformation to HKEY:

RegKeyToHKey: array[TRegKey] of HKEY = (HKEY_CLASSES_ROOT,
HKEY_CURRENT_USER, ...);

Ondrej

Reply by email

Michael Van Canneyt
to Ondrej Pokorny
Ondrej Pokorny wrote 25 lines
On 12.02.2023 23:08, Bart via fpc-devel wrote: > On Sun, Feb 12, 2023 at 10:47 PM Jonas Maebe via fpc-devel > wrote: > >> Afaik it's because "default" doesn't support qword values, iirc because >> of (at least old) Delphi compatibility. > OK, I removed the "default". > > Theoretically would that lead to problems when storing the value in > lfm and reading it back using different bitness versions of Lazarus? > Should I even publish such a property? > (I don't think Lazarus has a property editor for HKEY...) I wouldn't publish the HKEY property but create an own enumeration for it: TRegKey = (rgClassesRoot, rgCurrentUser, ....) and publish this instead. Pro: you can put only valid values into the property and object inspector shows you nice enum names instead of strange integers. Then make a simple transformation to HKEY: RegKeyToHKey: array[TRegKey] of HKEY = (HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, ...);

Yes, I also think this is the best solution...

Michael.

Reply by email

Bart
to Ondrej Pokorny: I wouldn't publish the HKEY property but create an own enumeration for it: TRegKey =…
AM Ondrej Pokorny wrote
I wouldn't publish the HKEY property but create an own enumeration for it: TRegKey = (rgClassesRoot, rgCurrentUser, ....)

...

Show quoted text 5 lines
Then make a simple transformation to HKEY: RegKeyToHKey: array[TRegKey] of HKEY = (HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, ...);

Yep, thought of that as too.
Perhaps the best options.
Thanks.

Bart

Reply by email

Bart
to Yuriy Sydorov: Declarations like this have been made on purpose: HKEY_CURRENT_USER =…
PM Yuriy Sydorov wrote 6 lines
Error "Range check error while evaluating constants (18446744071562067969 must be between -2147483648 and 4294967295)" is wrong here: property RootKey: HKey read FRootKey write FRootKey default HKEY_CURRENT_USER; Since HKEY = THandle = QWord on Win64, the reported range for it is incorrect.

Reported: https://gitlab.com/freepascal.org/fpc/source/-/issues/40148 (with simplified example).

Bart

Reply by email