1

I'm using Delphi 10.3 Community Edition, targeting Windows 32-bit.

I'm writing an interface type. Its class internally uses a TList<T> (and some other objects). In order to avoid having to write several pass-through one-liner methods, I'm using the implements keyword to delegate, like this:

program MRE;

{$APPTYPE CONSOLE}

uses
  System.Generics.Collections;

type
  IMyList = interface
    ['{8483DB08-57EA-40D0-BFCE-5E39804CA15B}']
    function Add (const ACard: Cardinal): Integer;
    function GetEnumerator: TList<Cardinal>.TEnumerator;
  end;

  TMyList = class (TInterfacedObject, IMyList)
    constructor Create;
    destructor Destroy; override;
  private
    FCards: TList<Cardinal>;
  public
    property Cards: TList<Cardinal>
      read FCards 
      implements IMyList;
  end;

  constructor TMyList.Create;
  begin
    FCards := TList<Cardinal>.Create;
  end;

  destructor TMyList.Destroy;
  begin
    FCards.Free
  end;

begin
  var Test: IMyList := TMyList.Create;
  Test.Add (3);
  for var c in Test do
    WriteLn (c);
  ReadLn
end.

This works fine and prints 3, as expected.

However, when I experienced strange crashes in the more complex version of the program, I was able to distill the bug down to the declaration of a type. Just add a single line (anywhere between the type declarations):

type
  TCardList = TList<Cardinal>; { **** this isn't even used anywhere! }

The code still compiles, but the application now crashes with an Access Violation ('write of address 0x004ee838'). It's not just the call to Add(). When you remove that, it still crashes when the for loop tries to get the enumerator.

Are there some weird restrictions to delegation that I'm missing here, or could this be a compiler deficiency?

EDIT: As requested in the comments, here is the same code again with the single line that triggers the crash already added:

program MRE;
  {$APPTYPE CONSOLE}

uses
  System.Generics.Collections;

type
  TCardList = TList<Cardinal>; { **** this isn't even used anywhere! }

  IMyList = interface
    ['{8483DB08-57EA-40D0-BFCE-5E39804CA15B}']
    function Add (const ACard: Cardinal): Integer;
    function GetEnumerator: TList<Cardinal>.TEnumerator;
  end;

  TMyList = class (TInterfacedObject, IMyList)
    constructor Create;
    destructor Destroy; override;
  private
    FCards: TList<Cardinal>;
  public
    property Cards: TList<Cardinal>
      read FCards
      implements IMyList;
  end;

  constructor TMyList.Create;
  begin
    FCards := TList<Cardinal>.Create;
  end;

  destructor TMyList.Destroy;
  begin
    FCards.Free
  end;

begin
  var Test: IMyList := TMyList.Create;
  Test.Add (3);
  for var c in Test do
    WriteLn (c);
  ReadLn
end.

This produces the crash mentioned above.

  • 1
    Sounds like a bug to me. Can you please update your example to include a [mcve] of the Access Vioation in action? – Remy Lebeau May 09 '21 at 01:25
  • @RemyLebeau: I don't think I can make the example code above much shorter. If you just wanted me to add a second complete copy of the code with the one line that causes the crash already inserted, I now edited that into the end of the question. – Valerian K. May 09 '21 at 08:57
  • 1
    Using the MRE, I can reproduce the access violation using D10.4.2 (Without recently published patches). The AV effectively does not occur anymore when removing the unused line `TCardList = TList;`. This looks really like a bug in the compiler. – fpiette May 09 '21 at 09:04
  • Then it needs to be [reported to Embarcadero](https://quality.embarcadero.com). – Remy Lebeau May 09 '21 at 15:10
  • @ValerianK. Could you create a bug report at https://quality.embarcadero.com/ and publish the number here? – fpiette May 10 '21 at 08:11

0 Answers0