[fpc-devel] potential bug, generics vs helpers

Kornel Kisielewicz kornel.kisielewicz at gmail.com
Fri Jan 27 16:23:10 CET 2012


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.

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.

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".

Now there are several questions I have in regards to this (way to
long) introduction:

1) In case of the first program and the A := B assignment - is the
manual wrong (and it will stay working this way) or the compiler
should generate separate instances?

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?)

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?

regards,
-- 
regards,
Kornel Kisielewicz
http://chaosforge.org/



More information about the fpc-devel mailing list