XE6 to XE7 Update 1 Generics incompatibility.

XE6 to XE7 Update 1 Generics incompatibility.

I can’t say for sure if I ever tried this in XE7 first version, but I don’t think I did.

procedure TAbstractBaseFactory.CreateInner;

begin

  FInner := TBaseTypeClass(T).Create;  

end;

TBaseTypeClass(T).Create;  compiles and runs in XE6, but gives

[dcc32 Error] : E2010 Incompatible types: ‘T’ and ‘TAbstractBaseType’

in XE7.

Stefan Glienke – You showed me this trick for XE6 – any suggestions for XE7?

{code}

program CreateFromGenericTClassXE7;

{$APPTYPE CONSOLE}

{$R *.res}

uses

  System.SysUtils;

type

  TAbstractBaseType = class

  public

    Constructor Create; virtual;

    procedure DoSomething; virtual; abstract;

  end;

  TDescendantType = class(TAbstractBaseType)

  public

    procedure DoSomething; override;

  end;

type

  TBaseTypeClass = class of TAbstractBaseType;

type

  TAbstractBaseFactory = class abstract (TObject)

  private

    FInner: T;

    procedure SetInner(const Value: T);

  public

    constructor Create;

    procedure CreateInner; virtual;

    property Inner: T read FInner write SetInner;

  end;

{ TAbstractBaseFactory }

constructor TAbstractBaseFactory.Create;

begin

  Inherited;

end;

procedure TAbstractBaseFactory.CreateInner;

begin

  FInner := TBaseTypeClass(T).Create;  // [dcc32 Error] : E2010 Incompatible types: ‘T’ and ‘TAbstractBaseType’

end;

procedure TAbstractBaseFactory.SetInner(const Value: T);

begin

  FInner := Value;

end;

{ TDescendantType }

procedure TDescendantType.DoSomething;

begin

end;

{ TAbstractBaseType }

constructor TAbstractBaseType.Create;

begin

  Inherited;

end;

begin

  try

    { TODO -oUser -cConsole Main : Insert code here }

  except

    on E: Exception do

      Writeln(E.ClassName, ‘: ‘, E.Message);

  end;

end.

28 thoughts on “XE6 to XE7 Update 1 Generics incompatibility.


  1. Edit (what I wrote earlier was crap – I should have thought a bit more before writing): 


    This is a fix for a compiler error you got earlier up to XE6 when you instantiated the type.


    Add this line to your main:


    TAbstractBaseFactory.Create.CreateInner;


    It will raise a E2010 Incompatible types: ‘TDescendantType’ and ‘TAbstractBaseType’ in some completely off location up to XE6. That is because you are assigning the result of the TAbstractBaseType ctor which is always a TAbstractBaseType to T which of course is of TAbstractBaseType but due to the generic inherits from it.


    So in both cases (before XE7 and after XE7) you have to cast FInner to TAbstractBaseType to make this work.


  2. Lars Fosdal I edited what I wrote earlier. So it’s not a bug but a fix for some very annoying bug actually. I cannot say how many times I had to incrementally comment out code to get to the line that really caused the error because the compiler stopped at some completely off locations.


  3. Interestingly, this did not solve it!


    [dcc32 Error] PSD_db_FireDAC.pas(1533): E2010 Incompatible types: ‘TFDPhysDriverLink’ and ‘FireDAC.Phys.TFDPhysDriverLink’


  4. Parametric wise, the code was identical to the example.


    type


      TFDPhysDriverLinkClass = class of TFDPhysDriverLink;


      TPSDFireDatabasePool = class abstract (TPSDAbstractDatabasePool)


      private


        FFireDriverLink: T;


        procedure SetFireDriverLink(const Value: T);


      protected


        function CreateResourceInstance: TPSDDatabase_Abstract; override;


      public


        class function UseThreadCache: Boolean; override;


        function DriverIdentity: String; override;


        procedure DriverInit; override;


        procedure DriverDone; override;


        property FireDriverLink: T read FFireDriverLink write SetFireDriverLink;


      end;




    Originally:


    procedure TPSDFireDatabasePool.DriverInit;


    begin


      FireDriverLink := TFDPhysDriverLinkClass(T).Create(nil);


    end;


    If I change it to 


    procedure TPSDFireDatabasePool.DriverInit;


    begin


      TFDPhysDriverLink(FFireDriverLink) := TFDPhysDriverLinkClass(T).Create(nil);


    end;


    I get the above error.


  5. First my belief is that the old code never actually worked when using the class with T being something else than TFDPhysDriverLink exactly (the reason I mentioned above).


    The error leads me to the guess that you have some naming conflict. Do you have some other type called TFDPhysDriverLink or called the generic type parameter like that?


  6. The actual pool used in declared in a different unit.


    unit PSD_db_FireDAC_MSSQL;


    interface


    uses


      PSD_db_FireDAC, FireDAC.Stan.Consts, FireDAC.Phys.ODBCBase, FireDAC.Phys.MSSQL;


    type


      TPSDFireDatabasePoolMSSQL = class(TPSDFireDatabasePool);


    implementation


    end.


    This is done to minimize pulling in code specific to the database.


  7. Nope, TFDPhysDriverLink is from FireDAC.


    Changing to 


      TPSDFireDatabasePool = class abstract (TPSDAbstractDatabasePool)


      public


        type   TFDPhysDriverLinkClass = class of FireDAC.Phys.TFDPhysDriverLink;


    doesn’t help. Same error.


  8. Well unless the compiler went completely bonkers (did you restart the IDE?) the error: Incompatible types: ‘TFDPhysDriverLink’ and ‘FireDAC.Phys.TFDPhysDriverLink’ is pretty clear. Ctrl+Click on the type used to hardcast FFireDriverLink and check where that leads you.


  9. Doh – I found what I’d change to break to the code – I rolled back the code to redo the changes before my last comment- and now I spotted the diff.


    I had moved the declaration of the 


    type


      TFDPhysDriverLinkClass = class of FireDAC.Phys.TFDPhysDriverLink;


    inside the abstract class. (See comment from 2:28)


    That did not help 😛


    It’s a bit strange, though – that declaration is pretty specific.


  10. Lars Fosdal Following code also works:


      TAbstractBaseFactory = class abstract (TObject)


    and


    procedure TAbstractBaseFactory.CreateInner;


    begin


      FInner := T.Create; 


    end;


  11. Dalija Prasnikar No, it does not because the ctor constraint does only work with the default ctor. Maybe in the example code it will work but the real code is dealing with TComponent classes.


  12. Surely this should be fixable, as there would be only one virtual or nonvirtual ctor in the abstract class parameter specifier that matches the call.


  13. I guess you can say that while trying to load the ammo, I bruised a finger when trying to insert the cartridge the wrong way. I did however identify the problem, and let the world know that was my mistake.  I still needed to typecast the magazine to insert it, though – something that previously was not necessary.  So much metaphors! 😉


  14. “something that previously was not necessary”


    I still don’t believe that because the E2010 will occur when you instantiate the generic class – at least it did on XE and XE6 with the example code in the original post.


  15. Lars Fosdal There were some changes in XE7 compiler that might be connected with this. I have private report that was closed “As designed” because there were some fixes that introduced regression. While my report is slightly different, root cause might be the same.


    [XE7 REGRESSION] Bogus “Incompatible types” error


    http://qc.embarcadero.com/wc/qcmain.aspx?d=126567


    This is simplified test case from that report for those that are not able to see it:


    type Constraint = class end;


    type G = class // or RECORD, or INTERFACE


    procedure Method(const X: T);


    property Setter: T write Method;


    end;


    procedure G.Method(const X: T);


    begin


    end;


    type Test = class


    F: G;


    procedure E2010(const X: Constraint);


    end;


    procedure Test.E2010(const X: Constraint);


    begin


    F.Method(X); // OK


    F.Setter := X // E2010 Incompatible types ‘T’ and ‘Constraint’


    end;


    var Instantiate: Test;


    end.

Leave a Reply