Watch, Follow, &
Connect with Us
Public Report
Report From: Delphi-BCB/Compiler/Delphi/Generics    [ Add a report in this area ]  
Report #:  69461   Status: Closed
Generic type incompatible with itself?
Project:  Delphi Build #:  17555
Version:    12.0 Submitted By:   Mason Wheeler
Report Type:  Basic functionality failure Date Reported:  11/30/2008 11:57:57 AM
Severity:    Serious / Highly visible problem Last Updated: 3/20/2012 2:24:39 AM
Platform:    All platforms Internal Tracking #:   266981
Resolution: Fixed (Resolution Comments) Resolved in Build: : 12.0.3298.19003
Duplicate of:  None
Voting and Rating
Overall Rating: No Ratings Yet
0.00 out of 5
Total Votes: 10
Description
've got a TDictionary that stores a bunch of objects indexed by name, and I'd like to be able to examine all of the objects. So I tried this:

var enumerator: TMyObject;
begin
   for enumerator in myDictionary do

But that wouldn't compile. "Incompatible types: 'TMyObject' and 'TPair'

So I tried it a bit differently:

var enumerator: TPair<string, TMyObject>;

That didn't compile either. This error message is even stranger: Incompatible types: 'TPair<string, TMyObject>' and 'TPair<string, MyUnit.TMyObject>'.

It gets worse.  I tried to create a wrapper class around the dictionary that would handle the data retrieval internally.  But now I can't call any of its methods.  Any method call, even ones with no parameters, gets this:

[DCC Error] E2010 Incompatible types: 'TMyDictionary<MyUnit.TMyDictionary<TKey,TIndex,TValue>.TKey,MyUnit.TMyDictionary<TKey,TIndex,TValue>.TIndex,MyUnit.TMyDictionary<TKey,TIndex,TValue>.TValue>' and 'TMyDictionary<System.string,System.string,turbu_characters.TMyObject>'

This is a complete show-stopper. Anyone know how to fix this?
Steps to Reproduce:
None
Workarounds
None
Attachment
None
Comments

Uwe Schuster at 11/30/2008 3:34:49 PM -
Provide a full example!

Mine does compile and work:

program DictionaryTest;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  Generics.Collections;

type
  TMyObject = class(TObject)
  private
    FIntValue: Integer;
  public
    constructor Create(AValue: Integer);
    property IntValue: Integer read FIntValue;
  end;

constructor TMyObject.Create(AValue: Integer);
begin
  FIntValue := AValue;
end;

var
  Dictionary: TDictionary<string, TMyObject>;
  Pair: TPair<string, TMyObject>;
  MyObject: TMyObject;
begin
  Dictionary := TDictionary<string, TMyObject>.Create;
  try
    Dictionary.Add('One', TMyObject.Create(1));
    Dictionary.Add('Two', TMyObject.Create(2));
    Dictionary.Add('Three', TMyObject.Create(3));
    for Pair in Dictionary do
      Writeln('Key=' + Pair.Key + ' IntValue=' + IntToStr(Pair.Value.IntValue));
    for MyObject in Dictionary.Values do
      Writeln('IntValue=' + IntToStr(MyObject.IntValue));
  finally
    Dictionary.Free;
  end;
  ReadLn;
end.

Mason Wheeler at 11/30/2008 7:05:42 PM -
Apparently this is a bit of an edge case.  On further testing, this only occurs when the generic-based object is a property of another object that's declared in a different unit. So:

unit Unit1;

interface
uses
   Generics.Collections;

type

  TMyObject = class(TObject)
  private
    FIntValue: Integer;
  public
    constructor Create(AValue: Integer);
    property IntValue: Integer read FIntValue;
  end;

  TMyDictionary = TDictionary<string, TMyObject>;

  TMyContainer = class(TObject)
  private
    FDictionary: TMyDictionary;
  public
    constructor Create;
    property Dictionary: TMyDictionary read FDictionary;
  end;


implementation

constructor TMyContainer.Create;
begin
  inherited Create;
  FDictionary := TMyDictionary.Create;
end;

constructor TMyObject.Create(AValue: Integer);
begin
  FIntValue := AValue;
end;

end.

============================================================

program Project1;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  Generics.Collections,
  Unit1 in 'Unit1.pas';

var
  Pair: TPair<string, TMyObject>;
  MyObject: TMyObject;
  myContainer: TMyContainer;
begin
    myContainer := TMyContainer.Create;
    myContainer.Dictionary.Add('One', TMyObject.Create(1));
    myContainer.Dictionary.Add('Two', TMyObject.Create(2));
    myContainer.Dictionary.Add('Three', TMyObject.Create(3));
    for Pair in myContainer.Dictionary do
      Writeln('Key=' + Pair.Key + ' IntValue=' + IntToStr(Pair.Value.IntValue));
    for MyObject in myContainer.Dictionary.Values do
      Writeln('IntValue=' + IntToStr(MyObject.IntValue));
  ReadLn;
end.

This is based on the example already provided, and is the simplest way I could come up with to reproduce the bug.  Can anyone get this to compile?

Uwe Schuster at 12/1/2008 5:30:01 AM -
I can't compile this as well. Also if I change in the workaround

  for Pair in myDictionary do

into

  for Pair in TMyDictionary(myDictionary) do

(which should be the same) then I get the same error.

Mason Wheeler at 11/30/2008 7:43:16 PM -
I managed to develop a successful workaround. It's a bit aggravating to have to do this, though:

program Project1;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  Generics.Collections,
  Unit1 in 'Unit1.pas';

var
  Pair: TPair<string, TMyObject>;
  MyObject: TMyObject;
  myContainer: TMyContainer;
  myDictionary: TMyDictionary;
begin
    myContainer := TMyContainer.Create;
    myDictionary := myContainer.Dictionary;
    myDictionary.Add('One', TMyObject.Create(1));
    myDictionary.Add('Two', TMyObject.Create(2));
    myDictionary.Add('Three', TMyObject.Create(3));
    for Pair in myDictionary do
      Writeln('Key=' + Pair.Key + ' IntValue=' + IntToStr(Pair.Value.IntValue));
    for MyObject in myDictionary.Values do
      Writeln('IntValue=' + IntToStr(MyObject.IntValue));
  ReadLn;
end.



This will compile.

Mason Wheeler at 12/1/2008 9:53:02 AM -
On further testing, this error seems to only appear in records containing generic types, such as TDictionary's TPair.

Server Response from: ETNACODE01