View Issue Details

IDProjectCategoryView StatusLast Update
0035992FPCCompilerpublic2019-11-29 00:35
ReporterImants GulbisAssigned ToSven Barth 
PrioritynormalSeverityminorReproducibilityalways
Status assignedResolutionopen 
Product Version3.3.1Product Build 
Target VersionFixed in Version 
Summary0035992: Problems with Generic speciaization
DescriptionGeneric specialization do not work in generic class if type witch used for specialization is generic type and already once specialized.
I got this error:
project1.lpr(28,32) Error: Duplicate identifier "TTestGeneric$1$crcE0B4E8C7"

This can be fixed by declaring redundant specialized type and using in everywhere so that specialization would happen only once.
Tagsgenerics
Fixed in Revision
FPCOldBugId
FPCTarget
Attached Files

Activities

Imants Gulbis

2019-08-21 08:29

reporter  

generics.zip (1,722 bytes)

Imants Gulbis

2019-08-21 08:31

reporter   ~0117761

It looks like it is problem only with specialization. It works in Delphi mode too without redundant type

Thaddy de Koning

2019-08-21 12:55

reporter   ~0117762

Last edited: 2019-08-21 12:56

View 2 revisions

I think your code is wrong, you specialize the same T twice in the same context, not sure but try this:
program project11;
{$Mode objfpc}
type

  generic TTestGeneric<T> = class
  public
    Value: T;
  end;
  
  { TTestClass }
  generic TTestClass<T> = class
  private
   public
    procedure Test(Value: T);
  end;

{ TTestClass }

procedure TTestClass.Test(Value: T);
var
  V: specialize TTestGeneric<T>;
 
begin
  V := specialize TTestGeneric<T>.Create;
  V.Value := Value;
  writeln(V.Value);
  V.Free
end;


begin
  with specialize TTestClass<Integer>.Create do
  begin
    Test(1);
    Free
  end;
end.

Thaddy de Koning

2019-08-21 16:33

reporter   ~0117765

Last edited: 2019-08-21 16:33

View 2 revisions

Tested some more: I am sure my code is correct and is also what you mean.
If you agree, please close.

Akira1364

2019-08-21 17:49

reporter   ~0117768

Last edited: 2019-08-21 17:54

View 4 revisions

No, this is definitely a bug in {$mode ObjFPC} generics handling, thaddy. As they said, it works fine in {$mode Delphi}:

program project1;

{$mode Delphi}

type
  TTestGeneric<T> = class
  public
    Value: T;
  end;

  { TTestClass }

  TTestClass<T> = class
  private
    FValue: TTestGeneric<T>;
  public
    procedure Test(Value: T);
  end;

{ TTestClass }

procedure TTestClass<T>.Test(Value: T);
var
  V: TTestGeneric<T>;
begin
  V := TTestGeneric<T>.Create;
  V.Value := Value;
  V.Free;
end;

begin
  with TTestClass<Integer>.Create do
  begin
    Test(1);
    Free;
  end;
end.

Another unfortunate example of needing to account for both modes in the compiler leading to bugs due to the extra complexity, I think.

Personally though I'm not sure I'd call the type alias solution "redundant". IMO that's a much cleaner way to write generics, in both modes:

program project1;

{$mode Delphi}

type
  TTestGeneric<T> = class
  public
    Value: T;
  end;

  { TTestClass }

  TTestClass<T> = class
  public type
    TValue = TTestGeneric<T>;
  public
    procedure Test(Value: T);
  end;

{ TTestClass }

procedure TTestClass<T>.Test(Value: T);
var
  V: TValue;
begin
  V := TValue.Create;
  V.Value := Value;
  V.Free;
end;

begin
  with TTestClass<Integer>.Create do
  begin
    Test(1);
    Free;
  end;
end.

and for {$mode ObjFPC}:

program project1;

{$mode ObjFPC}

type
  generic TTestGeneric<T> = class
  public
    Value: T;
  end;

  { TTestClass }

  generic TTestClass<T> = class
  public type
    TValue = specialize TTestGeneric<T>;
  public
    procedure Test(Value: T);
  end;

{ TTestClass }

procedure TTestClass.Test(Value: T);
var
  V: TValue;
begin
  V := TValue.Create;
  V.Value := Value;
  V.Free;
end;

begin
  with specialize TTestClass<Integer>.Create do
  begin
    Test(1);
    Free;
  end;
end.

Thaddy de Koning

2019-08-21 19:19

reporter   ~0117769

Last edited: 2019-08-21 19:26

View 2 revisions

There is a distinct difference and I *think* my interpretation of the syntax is 100% correct - if correct it is *not* a bug - , but let's see what Sven says.
Btw: my interpretation is how I would naturally write it in objfpc mode (if I have to). And I wrote the exact equivalent for the Delphi code, which I also tested.

Akira1364

2019-08-21 22:36

reporter   ~0117773

Last edited: 2019-08-21 22:41

View 2 revisions

Literally all you did was completely remove the "FValue: TTestGeneric<T>" private declaration from TTestClass, which has nothing to do with interpretations of syntax and is just hiding the bug by deleting the code that causes it.

There's not *supposed* to be any difference at all between {$mode ObjFPC} and {$mode Delphi} generics other than that one requires "generic" and "specialize" and the other doesn't.

Thaddy de Koning

2019-08-21 22:42

reporter   ~0117774

Last edited: 2019-08-21 22:44

View 2 revisions

I am sorry, that is removing the *programmer* bug that caused it. That's a completely different interpretation.
You are not quite sure about objfpc mode either, are you <wry smile>?
In this case the value had to be removed, because that is an error: two T's in the same scope. That is programmer error. The T's are resolved to the same, hence the error.

Imants Gulbis

2019-08-22 07:09

reporter   ~0117780

The thing that I did not use private variable in my *test* example for bug reproduction does not mean I did not yse it in my real code. I need private variable and I need my local variable. This was only example how to reproduce bug. In my real code I just switched to delphi mode and deleted specialize and generic keywords.
I do not think that delpi and objfpc modes should work differently.

Thaddy de Koning

2019-08-22 16:57

reporter   ~0117784

I would read https://freepascal.org/docs-html/current/ref/ref.html#QQ2-107-135 and previous , because this is certainly not the case in all circumstances.
I prefer mode delphi but some things can only be achieved in mode objfpc.

Akira1364

2019-08-23 14:55

reporter   ~0117795

Last edited: 2019-08-23 15:33

View 3 revisions

The docs aren't necessarily correct when it comes to generics, though. I can tell you for certain the section called "scope considerations" is simply wrong (and actually doesn't exist anymore in the trunk docs because I reported it as being wrong a while ago.)

So for example, something like this *does* work in {$mode ObjFPC}, not just {$mode Delphi}:

program Test;

{$mode ObjFPC}
{$modeswitch AdvancedRecords}

type
  generic TTest<T> = record
    Field: T;
    class function Create(const Val: T): TTest; static; inline;
  end;
  
  class function TTest.Create(const Val: T): TTest;
  begin
    Result.Field := Val;
  end;

var
  T: specialize TTest<LongInt>;
begin
  T := specialize TTest<LongInt>.Create(12);
  WriteLn(T.Field);
end.

Sven Barth

2019-08-23 17:50

manager   ~0117805

It is a generic bug, though the cause is an unrelated difference between the Delphi and non-Delphi modes: duplicate declarations of types.

E.g. the following will fail in non-Delphi modes as well while it compiles in the Delphi modes:

=== code begin ===

program tdup;

{$mode objfpc}

type
  TTest = class
  public type
    TFoo = record
    end;
  public
    procedure Test;
  end;

{ TTest }

procedure TTest.Test;
type
  TFoo = record
  end;
begin

end;

begin

end.

=== code end ===

The problem with generics is that they're doing essentially what the above example does while the generic is parsed (not during specialization!).
I've already found a fix where it reuses the already existing specialization in the parent class of the method (way more efficient anyway), I just need to run it through the test suite. :)

Thaddy de Koning

2019-08-23 19:03

reporter   ~0117808

That is enlightening to me, thx.

Issue History

Date Modified Username Field Change
2019-08-21 08:29 Imants Gulbis New Issue
2019-08-21 08:29 Imants Gulbis File Added: generics.zip
2019-08-21 08:31 Imants Gulbis Note Added: 0117761
2019-08-21 12:55 Thaddy de Koning Note Added: 0117762
2019-08-21 12:56 Thaddy de Koning Note Edited: 0117762 View Revisions
2019-08-21 16:33 Thaddy de Koning Note Added: 0117765
2019-08-21 16:33 Thaddy de Koning Note Edited: 0117765 View Revisions
2019-08-21 17:49 Akira1364 Note Added: 0117768
2019-08-21 17:52 Akira1364 Note Edited: 0117768 View Revisions
2019-08-21 17:54 Akira1364 Note Edited: 0117768 View Revisions
2019-08-21 17:54 Akira1364 Note Edited: 0117768 View Revisions
2019-08-21 19:19 Thaddy de Koning Note Added: 0117769
2019-08-21 19:26 Thaddy de Koning Note Edited: 0117769 View Revisions
2019-08-21 22:36 Akira1364 Note Added: 0117773
2019-08-21 22:41 Akira1364 Note Edited: 0117773 View Revisions
2019-08-21 22:42 Thaddy de Koning Note Added: 0117774
2019-08-21 22:44 Thaddy de Koning Note Edited: 0117774 View Revisions
2019-08-22 07:09 Imants Gulbis Note Added: 0117780
2019-08-22 16:57 Thaddy de Koning Note Added: 0117784
2019-08-23 14:55 Akira1364 Note Added: 0117795
2019-08-23 14:56 Akira1364 Note Edited: 0117795 View Revisions
2019-08-23 15:33 Akira1364 Note Edited: 0117795 View Revisions
2019-08-23 17:50 Sven Barth Note Added: 0117805
2019-08-23 17:50 Sven Barth Assigned To => Sven Barth
2019-08-23 17:50 Sven Barth Status new => assigned
2019-08-23 19:03 Thaddy de Koning Note Added: 0117808
2019-11-29 00:35 Sven Barth Tag Attached: generics