Generics and incompatible types.

Generics and incompatible types.

I've been buggered with an error that has me tearing the little I have left of hair out.  

[dcc32 Error] DUnitX_PSDCarrierTypes.pas(75): E2010 Incompatible types: 'TPSDCarrierType' and 'TPSDBase'

The problem is that DUnitX_PSDCarrierTypes.pas only has 73 lines.

Has anyone seen this before?  Can you remember what the underlying cause was at the time?  Property or method visibility?  Missing unit inclusion? Abstract methods?

Let me introduce the classes

In PSDBase,
  TPSDBase = class (TLockableObject, ILogInterface)
    ...
   end;

and

  TTestBaseClass = class
  private
    FInstance: T;
    function GetInstance: TPSDBase;
  protected
    property Instance: TPSDBase read GetInstance;
   ...
end;

In PSDCarrierTypes,
  TPSDCarrierType = class(TPSDBase)
 ...
  end;

- 
unit DUnitX_PSDBase;

interface
uses
  PSDBase, PSDConstants,
  DUnitX.TestFramework;

type
  /// Basis generic test class for TPSDBase descendants
  TTestClass = class(TTestBaseClass)
  ...
end;

and 

[TestFixture]
TestTPSDBase = class(TTestClass)
  ...
end;

- 
unit DUnitX_PSDCarrierTypes;
interface
uses
  DUnitX.TestFramework,
  PSDConstants, PSDBase, DUnitX_PSDBase, PSDCarrierTypes;

type
  [TestFixture]
  TestTPSDCarrierType= class(TTestClass)
  ...
  end;

- 
The main app looks has the following uses

program DUnitX_PSD_BasicUnitTests;
uses
  EMemLeaks,
  EResLeaks,
  EDialogWinAPIEurekaLogDetailed,
  EDialogWinAPIStepsToReproduce,
  EDebugExports,
  EFixSafeCallException,
  EMapWin32,
  EAppVCL,
  ExceptionLog7,
  SysUtils,
  DUnitX.AutoDetect.Console,
  DUnitX.Loggers.Console,
  DUnitX.Loggers.Xml.NUnit,
  DUnitX.TestRunner,
  DUnitX.TestFramework,
  PSDBase in '..\PSDCommon\PSDBase.pas',
  PSDCarrierTypes in '..\PSDCommon\PSDCarrierTypes.pas',
  DUnitX_PSDBase in 'DUnitX_PSDBase.pas',
  DUnitX_PSDCarrierTypes in 'DUnitX_PSDCarrierTypes.pas';

At first, I thought there was something weird about the order of compilation, but no - it looks normal.

14:54:39,1355491 DUnitX.ConsoleWriter.Base.dcu
14:54:39,2945261 DUnitX.Utils.dcu
14:54:39,3847109 DUnitX.IoC.dcu
14:54:39,4146560 DUnitX.Windows.Console.dcu
14:54:39,4222502 DUnitX.AutoDetect.Console.dcu
14:54:39,4371663 DUnitX.Generics.dcu
14:54:39,4582339 DUnitX.Extensibility.dcu
14:54:39,5233700 DUnitX.InternalInterfaces.dcu
14:54:39,5430712 DUnitX.WeakReference.dcu
14:54:39,5612627 DUnitX.Test.dcu
14:54:39,5751848 DUnitX.TestFixture.dcu
14:54:39,6043794 DUnitX.RunResults.dcu
14:54:39,6422193 DUnitX.TestResult.dcu
14:54:39,6714326 DUnitX.FixtureResult.dcu
14:54:39,7038809 DUnitX.Extensibility.PluginManager.dcu
14:54:39,8034689 DUnitX.TestRunner.dcu
14:54:39,8136915 DUnitX.CommandLine.dcu
14:54:39,8669545 DUnitX.MemoryLeakMonitor.Default.dcu
14:54:39,9958192 DUnitX.FixtureProviderPlugin.dcu
14:54:40,1629623 DUnitX.TestFramework.dcu
14:54:40,2033539 DUnitX.Loggers.Console.dcu
14:54:40,2217283 DUnitX.Loggers.Null.dcu
14:54:40,2732231 DUnitX.Loggers.XML.NUnit.dcu
14:54:40,3435106 StringFunctions.dcu
14:54:40,3692211 CustomDebugOut.dcu
14:54:40,3768381 Functions.dcu
14:54:40,7058753 FileFunctions.dcu
14:54:40,7107884 TINEFunctions.dcu
14:54:40,7191973 TineInterfaces.dcu
14:54:40,7319419 PSDConstants.dcu
14:54:40,7487508 TineClasses.dcu
14:54:40,7766713 PSDXMLLog.dcu
14:54:40,7834018 PSDLogEvent.dcu
14:54:40,7919438 PSDLogExceptionManager.dcu
14:54:40,8042575 PSDLogDrivers.dcu
14:54:40,8155411 tiConsts.dcu
14:54:40,8266076 PSDResourcePool.dcu
14:54:40,8517546 PSDConnectivityStatus.dcu
14:54:40,8632446 PSD_Db_Abstract.dcu
14:54:40,8960845 PSD_TCPTelegramBase.dcu
14:54:40,9079624 PSD_TCP_Types.dcu
14:54:40,9199220 PSD_TCPTelegrams.dcu
14:54:41,0200024 PSDBase.dcu
14:54:41,1065126 PSDCarrierTypes.dcu
14:54:41,1135979 DUnitX_PSDBase.dcu

...then the compilation fails for unit DUnitX_PSDCarrierType, 2 lines beyond end of unit.

Comments

  1. I have seen this before. It is obviously a compiler bug. In my case, it was because I had an AS statement , i.e. in your case it would be

    T as TPDSBAse

    when I changed it to

    TPDFBase(T),

    everything started compiling correctly. Now, I also did a bunch of other changes to try to fix it, but this seemed to be the one that did the trick for me.

    ReplyDelete
  2. Had a few ' as ', but it was other classes.  Changed them to TypeName(Reference) - but it didn't help.  I also merged the TTestBaseClass into TTestClass and removed TTestBaseClass from the PSDBase Unit. Still stuck T.T

    ReplyDelete
  3. Please remove irrelevant stuff (like DUnitX) and reduce your issue to the bare minimum.

    ReplyDelete
  4. Generics in Delphi, such a good time...

    ReplyDelete
  5. Stefan Glienke - Although that brings it down to 22 units, it is far from trivial to prune more code.  TPSDBase is a big-ass service class (4500 lines).  I wonder if EMBT will accept that much code for identifying the problem, and at the same time keep that code to themselves?

    ReplyDelete
  6. By the way the E2010 is correct: TPSDCarrierType and TPSDBase are not compatible . They are only compatible the other way around. So somewhere in your code you are assigning a TPSDBase to a TPSDCarrierType (most likely in some of the generic types). This might be caused by type constaints. So check for that.

    Edit: the Instance property and its getter needs to to be T and not TPSDBase.

    ReplyDelete
  7. Asbjørn Heid Yes, always there to be blamed although the bug is in your own code, right? :p

    ReplyDelete
  8. Stefan Glienke - Thanks for proving that I am an idiot!  ;)

    In DUnitX_PSDBase,

    procedure TTestClass.CreateFromJSON(const aJSON: String);
    begin
      FInstance := TPSDBase.CreateFromJSON (aJSON);
      Assert.IsTrue(Assigned(Instance));
    end;

    should read
    procedure TTestClass.CreateFromJSON(const aJSON: String);
    begin
      TPSDBase(FInstance) := T.CreateFromJSON (aJSON);
      Assert.IsTrue(Assigned(Instance));
    end;

    Now it compiles!

    I guess I have to make a sample of this and submit a QC, because the error leads you on a wild goose chase.  Imaging if my Generics code was nested even deeper...

    ReplyDelete
  9. Stefan Glienke Well normally I'm the last one to blame the compiler... but when you get BS error messages like that, refering to lines that doesn't exist, it's hard to avoid ;)

    ReplyDelete
  10. The error is correct. If you read the documentation of E2010 you see "The first type in this message is the type expected, and the second type is the type that was given."

    The problem with generics is the compiler obviously does not stop at the correct line. I would even argue that the line fInstance := TPSDBase.Create is not valid.  The compiler does not complain about it because of the constraint but it is only valid for one particular case: if T is TPSDBase but not for any other subclass of TPSDBase)

    ReplyDelete
  11. Stefan Glienke Yeah, but it's hard to trust the rest of the message when it refers to imaginary lines of code...

    While it made sense in this case, I've had a lot of cases when the error message was completely bogus when dealing with generics.

    Though as mentioned in the forums, most of my issues have been fixed so hopefully XE6 will be decent when it comes to generics.

    ReplyDelete
  12. Lars Fosdal I reported the (imo) root cause of this problem: qc.embarcadero.com/wc/qcmain.aspx?d=123895

    ReplyDelete
  13. Stefan Glienke - Should prolly mention that the problem is even more confusing if the descendent class is in a different unit, since the error appears to be in that second unit, instead of the first.

    ReplyDelete
  14. You are free to report the issue with the bogus/wrong line number compiler error and mention that :) My report is about the issue that the use of a constraint does hide the error until you instantiate the class while it should be triggered even without ever instantiating it.

    ReplyDelete
  15. Well, if they fix yours, they fix mine - so I won't bother as it will be marked as duplicate anyways.

    ReplyDelete

Post a Comment