Sign in Join

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

Mailing lists / fpc-devel / 2022-09

Overloading problem in generic class.

10 messages · 4 people ·

Reply by email Subscribe to this list Your subscription

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

Bart

Hi,

The following program will compile for Windows 32 and 64 bit. It fails to compile for Linix 64 bit (and if I understand correctly, also for MacOS 64 bit).

=== begin code ===
program test;
{$mode objfpc}
{$H+}
{$ifdef FPC_HAS_TYPE_EXTENDED}
function SameValue(const A, B: Extended): Boolean;inline; overload;
begin
  writeln('SameValue Extended');
  Result := A=B;
end;
{$endif}
{$ifdef FPC_HAS_TYPE_DOUBLE}
function SameValue(const A, B: Double): Boolean;inline; overload;
begin
  writeln('SameValue Double');
  Result := A=B;
end;
{$endif}
function SameValue(const A, B: Single): Boolean;inline; overload;
begin
  writeln('SameValue Single');
  Result := A=B;
end;
type
  generic TBase<T> = class
  private
    const _Def = 1;
  public
    FValue: T;
    function IsStored: Boolean;
  end;
function TBase.IsStored: Boolean;
begin
  Result := SameValue(FValue, T(_Def));  // << this is line 40
end;
type
  TDouble = specialize TBase<Double>;
var
  D: TDouble;
const
  ADef = 1.0;
begin
  {$ifdef WINDOWS}
  write('Windows ');
  {$endif}
  {$ifdef darwin}
  write('MacOS ');
  {$endif}
  {$ifdef linux}
  write('Linux ');
  {$endif}
  {$ifdef CPU32}
  writeln('32-bit');
  {$ENDIF}
  {$ifdef CPU64}
  writeln('64-bit');
  {$ENDIF}
  {$ifdef FPC_HAS_TYPE_EXTENDED}
  writeln('FPC_HAS_TYPE_EXTENDED');
  {$ELSE}
  writeln('NOT FPC_HAS_TYPE_EXTENDED');
  {$endif}
  {$ifdef FPC_HAS_TYPE_DOUBLE}
  writeln('FPC_HAS_TYPE_DOUBLE');
  {$ELSE}
  writeln('NOT FPC_HAS_TYPE_DOUBLE');
  {$endif}
  write('Literal 1.0  : ');
  SameValue(1.0,ADef);
  write('Literal 4E38 : '); //cannot be a single
  SameValue(4E38,ADef);
  {$ifdef FPC_HAS_TYPE_EXTENDED}
  write('Literal 2E308: '); //cannot be a Double
  SameValue(2E308,ADef);
  {$endif}
  write('TDouble      : ');
  D := TDouble.Create;
  D.FValue := 1.0;
  D.IsStored;
  D.Free;
end.
=== end code ===
Linux 64-bit
Compile Project, Target: test: Exit code 1, Errors: 1, Hints: 3
test.lpr(40,13) Error: Can't determine which overloaded function to call
test.lpr(22,10) Hint: Found declaration: SameValue(const Single;const
Single):Boolean;
test.lpr(15,10) Hint: Found declaration: SameValue(const Double;const
Double):Boolean;
test.lpr(7,10) Hint: Found declaration: SameValue(const Extended;const
Extended):Boolean;

If I remove the typecast then it compiles OK on all platforms.

(fpc 3.2.2 of fpc main seem to do the same)

I simply do not understand where the difference between Windows/Non-Windows comes from.

Some background:
We had a crash in the Lazarus IDE when setting MaxValue (declared as Double) to a value > MaxSingle: Floating point overflow. (See https://gitlab.com/freepascal.org/lazarus/lazarus/-/issues/39792) The backtrace suggest that this happens in the function MaxValueStored: this simply does:
function TCustomFloatSpinEdit.MaxValueStored: Boolean; begin

  Result := not SameValue(FMaxValue, DefMaxValue);

end;
DefMaxValue is declared as a private constant:
private const

    DefMaxValue = 0;

The crash goes away if we do either of two things:
- in the call to SameValue typecast DefMaxValue to Double
- declare DefMaxValue as Double(0)

So far, so good.
But we also have a component TFloatSpinEditEx which is a generic implementation of similar kind.
This component uses the same method for the MaxValueStored and thus also crashed in the IDE.
So, I tried the same solution there: typecast DefMaxValue as T(DefMaxValue) in the call to SameValue and then it turned out this wouldn't compile on MacOS and Linux 64 bit.

Bart

Reply by email

Maxim Ganetsky
to Bart

18.09.2022 12:37, Bart via fpc-devel пишет:

Show quoted text 5 lines
Hi, The following program will compile for Windows 32 and 64 bit. It fails to compile for Linix 64 bit (and if I understand correctly, also for MacOS 64 bit).

<...>

Show quoted text 16 lines
Linux 64-bit Compile Project, Target: test: Exit code 1, Errors: 1, Hints: 3 test.lpr(40,13) Error: Can't determine which overloaded function to call test.lpr(22,10) Hint: Found declaration: SameValue(const Single;const Single):Boolean; test.lpr(15,10) Hint: Found declaration: SameValue(const Double;const Double):Boolean; test.lpr(7,10) Hint: Found declaration: SameValue(const Extended;const Extended):Boolean; If I remove the typecast then it compiles OK on all platforms. (fpc 3.2.2 of fpc main seem to do the same) I simply do not understand where the difference between Windows/Non-Windows comes from.

As far as I see from CI build log, FPC 3.2.0 is the culprit. FPC 3.2.2 compiles your code fine both on Linux and Windows.

Best regards, Maxim Ganetsky

Reply by email

Martin Frb
to Bart: The following program will compile for Windows 32 and 64 bit. It fails to compile for…
Bart wrote 6 lines
Hi, The following program will compile for Windows 32 and 64 bit. It fails to compile for Linix 64 bit (and if I understand correctly, also for MacOS 64 bit).

Just enable fpc_math_samevalue_bug

{$if FPC_FullVersion=30202}{$ifdef Win64}
   {$define fpc_math_samevalue_bug}
{$endif}{$endif}
Make it
{$if FPC_FullVersion<=30202}
   {$define fpc_math_samevalue_bug}
{$endif}

--------------------
I wonder anyway

  TCustomSpinEditEx = class(specialize TSpinEditExBase<Int64>)

But "SameValue" does not exist for int64 (or any int)? So I assume the compiler converts the int to a float for calling SameValue => that seems undesirable.

So we should have a function (in the scope for the generic)

     function SameValue(AValue1, AValue2: Int64): Boolean; overload;

Reply by email

Bart
to Martin Frb: Just enable fpc_math_samevalue_bug {$if FPC_FullVersion=30202}{$ifdef Win64}   {$define…
PM Martin Frb wrote 5 lines
Just enable fpc_math_samevalue_bug {$if FPC_FullVersion=30202}{$ifdef Win64} {$define fpc_math_samevalue_bug} {$endif}{$endif}

This suggest that the bug is in Windows 3.2.2

Make it {$if FPC_FullVersion<=30202} {$define fpc_math_samevalue_bug} {$endif}

This suggest that the bug is in all versions up until 3.2.2 (so fixed in 3.1.1 and merged to 3.2 fixes branch)
That is incorrect, since it will compile on Windows with 3.2.2 for both 32 and 64 bit.

So, back to original question:
Should it (the example with the cast to the generic type: T(_Def)) compile on all platforms, or should it not compile on Windows? If it should compile it can be fixed for the correct compiler+platform versions, if not, it should be fixed in a different way.

Show quoted text 6 lines
I wonder anyway TCustomSpinEditEx = class(specialize TSpinEditExBase<Int64>) But "SameValue" does not exist for int64 (or any int)? So I assume the compiler converts the int to a float

Yes it does, and then calls the Single overload of SameValue. Given the current values of DefMaxValue and DefMinValue, this is safe. Unwanted maybe, but only called in OI when setting Max/MinValue, so performance penalty is not realy an issue here.
If OTOH we decide to make DefMaxValue = High(Int64, which is not very likely), then the Single overload of SameValue seems to return TRUE if it compares High(Int64) and High(Int64)-1.

(Note:
The base class can have a virtual abstract InternalSameValue function thet then needs to be implemented in all descendant classes. This will break all derived classes that are not part of the Lazarus distribution, e.g. the ExCtrls package
(https://wiki.freepascal.org/ExCtrls).
This may be the cleaner solution though.
)

Bart

Reply by email

Bart
to Bart: On Sun, Sep 18, 2022 at 1:34 PM Martin Frb via fpc-devel This suggest that the bug is in…
PM Bart wrote 5 lines
> Just enable fpc_math_samevalue_bug > > {$if FPC_FullVersion=30202}{$ifdef Win64} > {$define fpc_math_samevalue_bug} > {$endif}{$endif}

OMG.
I only found out right now that this define is already there.

Oh, and on Linux my fpc was still 3.2.0, so my example did not compile there.

I think I will place all the idef-ed code (in the interface) together, so it's easier to spot this is there at all, and easier to remove once 3.2.0 i sno longer supported.

Bart

Reply by email

Martin Frb
to Bart: On Sun, Sep 18, 2022 at 1:34 PM Martin Frb via fpc-devel This suggest that the bug is in…
Bart wrote 15 lines
I wonder anyway > TCustomSpinEditEx = class(specialize TSpinEditExBase<Int64>) > > But "SameValue" does not exist for int64 (or any int)? > So I assume the compiler converts the int to a float (Note: The base class can have a virtual abstract InternalSameValue function thet then needs to be implemented in all descendant classes. This will break all derived classes that are not part of the Lazarus distribution, e.g. the ExCtrls package (https://wiki.freepascal.org/ExCtrls). This may be the cleaner solution though. )

And if the base class has
    function SameValue(AValue1, AValue2: T): Boolean; virtual; // but not abstract

Which implements the old call, without typecast?

It may need to be abstract in case of
{$ifdef fpc_math_samevalue_bug}
because the non-typecast will otherwise fail.
But for that define, there already is an abstract class, that all derived classes must have.
Or it could have different bodies, depending on compiler version and OS. (that would to the best of abilities help 3rd party code)

And since it is then overridden in the classes in our code => those will do the correct calls.

Reply by email

Bart
to Werner Pamler: Sorry if I was not precise here: I only tested with fpc 3.2.0.
PM Werner Pamler wrote
> Werner reported that it does not compile on MacOS (64 bit) with fpc 3.3.1. Sorry if I was not precise here: I only tested with fpc 3.2.0.

I misread, my mistake.

Q1: these ifdef-ed code: does it have to be in the protected section of the interface, or can it be moved to the private section? I'lld like to group them together.

Q2: can you set MaxValue to +Inf in the OI with these changes you made? If so, we should close both issues as fixed.

Q3: since 3.2.2 can compile the SameValue(FMaxValue, T(DefMaxValue)), would that also resolve
https://gitlab.com/freepascal.org/lazarus/lazarus/-/issues/39490 (Compilation issue with fpc 3.2.2 in TSpinEditExBase when a descendant class work with currencies)?
Currently the define is not accurate, at least for then issue in the example in my first post it is incorrect, since this affects all compiler versions < 3.2.2, but not 3.2.2 itself.
Or are these two different bugs, affecting different compiler versions? If that is the case, we either need 2 defines for the bugs, or we implement SameValue unconditionally (see also the remarks about SameValue and Int64 above).

Bart

Reply by email

Bart
to Martin Frb: And if the base class has     function SameValue(AValue1, AValue2: T): Boolean;…
PM Martin Frb wrote 5 lines
And if the base class has function SameValue(AValue1, AValue2: T): Boolean; virtual; // but not abstract Which implements the old call, without typecast?

I think Werner already tried that and it faile on type Currency in Win64 with fpc 3.2.2.
See https://gitlab.com/freepascal.org/lazarus/lazarus/-/issues/39490

Also, overriding is needed for anything with a range > Single, so you only spare yourself the implementation in TSpinEditEx, where you could do a simple A=B comparison.

Bart

Reply by email