[fpc-devel] potential bug, generics vs helpers

Sven Barth pascaldragon at googlemail.com
Fri Jan 27 16:54:28 CET 2012


Am 27.01.2012 16:23, schrieb Kornel Kisielewicz:
> Hello all,
>
> This my first mail to the list, and while this message might fit
> fpc-pascal, I wasn't sure about it, due to the fact that it has
> questions only a dev can answer, and possibly hints some bugs. Using
> FPC 2.6.0 / Win32 on Windows 7/64.

fpc-pascal would have been ok as well (maybe even better, because it's 
about usage of FPC...). Nevertheless the devs are normally subscribed to 
at least fpc-pascal and fpc-devel.

>
> So first of all, there's either a mistake in the manual, or FPC bug:
>
> ( following Ref 8.3 Generic class specialization )
>
> {$MODE OBJFPC}
> program generic_bug1;
> type generic TList<T>  = class  x : T; end;
>
> // Note that 2 specializations of a generic type with
> // the same types in a placeholder are not assignment
> // compatible. In the following example:
>
> type
>    TA = specialize TList<Pointer>;
>    TB = specialize TList<Pointer>;
>
> // variables of types TA and TB cannot be assigned to each
> // other, i.e the following assignment will be invalid:
> Var
>    A : TA;
>    B : TB;
>
> begin
>    A:=B;
> end.
>
> Well it turns out it's not invalid at all. It compiles without
> warnings, and if added surrounding sensible code, works all well.
> Seems as the compiler treats TA and TB as the same types. However if
> we define TA and TB in a different units it doesn't work anymore --
> which isn't surprising. So either the manual should be updated, or the
> "reusage" of the generic should be removed.

In my opinion the documentation should be updated, because in Delphi 
(and FPC's mode Delphi in trunk) you use code like the following:

procedure Foo;
var
   A: TList<Pointer>;
   B: TList<Pointer>;
begin
   A := TList<Pointer>;
   B := A;
end;

So it would not make sense for A and B to be of different types as you 
can also write

var
   A, B: TList<Pointer>;

One might argue that

type
   TA = specialize TList<Pointer>;
   TB = specialize TList<Pointer>;

is something different, but I say it is not.

>
> While this may seem very cosmetic, I found a case where it really
> makes a difference:
>
> {$MODE OBJFPC}
> {$MODESWITCH ADVANCEDRECORDS}
> program generic_bug2;
> type
> generic TList<T>  = record
>    x : T;
> end;
>
> TA = specialize TList<Single>;
> TB = specialize TList<Single>;
>
> type TAHelper = record helper for TA
>    procedure Write;
> end;
> procedure TAHelper.Write;
> begin
>    Write('A');
> end;
>
> type TBHelper = record helper for TB
>    procedure Write;
> end;
> procedure TBHelper.Write;
> begin
>    Write('B');
> end;
>
> var  A : TA;
>      B : TB;
>
> begin
>    A.Write;
>    B.Write;
> end.
>
> So the above program outputs "AA" -- because type TA and TB are the
> same. However, if we split out the generic and TA into another unit:
>
> // Unit
>
> {$MODE OBJFPC}
> {$MODESWITCH ADVANCEDRECORDS}
> unit generic_bug3_unit;
> interface
>
> type generic TList<T>  = record
>    x : T;
> end;
>
> type TA = specialize TList<Single>;
> type TAHelper = record helper for TA
>    procedure Write;
> end;
>
> implementation
>
> procedure TAHelper.Write;
> begin
>    Writeln('A');
> end;
> end.
>
> // Program
>
> {$MODE OBJFPC}
> {$MODESWITCH ADVANCEDRECORDS}
> program generic_bug3;
> uses generic_bug3_unit;
>
> type
>    TB = specialize TList<Single>;
>
> type TBHelper = record helper for TB
>    procedure Write;
> end;
>
> procedure TBHelper.Write;
> begin
>    Writeln('B');
> end;
>
> var
>    A : TA;
>    B : TB;
>
> begin
>    A.Write;
>    B.Write;
> end.
>
> ... then TA and TB are treated as separate types, hence each has it's
> own helper -- program outputs "AB".
>

This is interesting. In my opinion (under the aspect that TA = TB 
currently) it should print "BB" in both cases. I'll need to investigate 
this.

Also the case that both spezializations in the same unit are treated the 
same, but aren't in the other one. Also something I'll need to 
investigate (also in FPC 2.7.1).

> 2) In the second program, is it OK that the compiler silently discards
> the first Helper class, or maybe should it produce an error? Note that
> this also works for non-generic records (what does Delphi do?)

As I said above: given that TA is considered equal to TB it should print 
the same letter in both cases.

>
> 3) (most important for me) this all was discovered because I wanted to
> find a way to have two different "flavors" of a base record, extended
> by different helpers (eg. Vector4D base class extended to a
> Quaternion, and a Color) -- using generics provided a easy way to make
> a base record "evolve" into two distinct types, so in one unit I have
> TGLColor4 = specialize TVector4<  Single>; with a TGGColor4Helper and
> in another I have TQuaternion = specialize TVector4<Single>  with a
> TQuaternionHelper). I really like this solution, but depending on the
> way the above may change, I don't know if it won't be "fixed" not to
> work anymore in a later version of FPC?

Currently they will sometimes be the same and sometimes not. This would 
be ok if we wouldn't have helpers, but as we have helpers now as well a 
solution for this needs to be found.

Regards,
Sven



More information about the fpc-devel mailing list