10

I just faced a strange compiler error when trying to define nested generic record.

Nesting works fine with classes and interfaces, but not with records somehow.

type
  TRec<T> = record
    Value: T;
  end;

  TCls = class
  public
    Rec: TRec<TRec<Integer>>;
  end;

This is not compiled on Delphi Berlin 10.1.2 and also no luck on Tokyo 10.2.3. Is this a limitation of language or a compiler issue?

The error message is:

[dcc32 Error] Project1.dpr(22): E2564 Undefined type 'TRec<T>'

I just wanted once to nest the Spring.Nullable<> types and that did not work. After that I quickly reproduced that with a simple generic record.

Mark Rotteveel
  • 82,132
  • 136
  • 114
  • 158
Z.B.
  • 1,070
  • 9
  • 17
  • Is this working for you: `TCls = class private type Trec1 = record Value : T1; end; public Rec: TRec>; end;` – LU RD Apr 18 '18 at 08:45
  • @LURD: Nope. Same error. – Z.B. Apr 18 '18 at 09:01
  • Hmm, at least it compiles in my Delphi 10.2 Tokyo update 2. – LU RD Apr 18 '18 at 09:05
  • 2
    @LURD That does compile fine, in all Delphi versions. Pretty sure that asker didn't compile your code, and misread your comment. I also found that same workaround independently. – David Heffernan Apr 18 '18 at 09:09
  • @DavidHeffernan: program Project1; {$APPTYPE CONSOLE} {$R *.res} type TCls = class private type Trec1 = record Value : T1; end; public Rec: TRec>; end; begin end. Error: [dcc32 Error] Project84.dpr(8): E2003 Undeclared identifier: 'TRec<>' – Z.B. Apr 18 '18 at 09:17
  • on Berlin 10.1 update 2. 24.0.25048.9432. – Z.B. Apr 18 '18 at 09:18
  • @Z.B. Well, you didn't define `TRec` did you! LURD is proposing the exact same workaround as can be found in my answer. – David Heffernan Apr 18 '18 at 09:20
  • Same on Tokyo 10.2 Update 3 (25.0.29899.2631) – Z.B. Apr 18 '18 at 09:20
  • oh.. I see :) I just copied LURD code and tried and by error assumed it's the same issue. Excuse me both please. – Z.B. Apr 18 '18 at 09:21
  • Does it work with an intermediate `TRecInteger = TRec;` and later, in the class: `Rec: TRec;`? I can't test this (no Delphi here), but I guess it compiles. That is the first thing I would try. – Rudy Velthuis Apr 18 '18 at 09:56
  • No, that does not compile either. Not even using an intermediate `TRecRecInteger = TRec;` does, nor does making the class generic `TCls = class`. – Rudy Velthuis Apr 18 '18 at 14:30

1 Answers1

9

This is a compiler bug, and you should submit a bug report. Consider the following:

type
  TRec<T> = record
    Value: T;
  end;

var
  Rec: TRec<TRec<Integer>>; // compiles successfully
  RecArray: TArray<TRec<TRec<Integer>>>; // compiles successfully

procedure foo;
var
  Rec: TRec<TRec<Integer>>; // compiles successfully
begin
end;

type
  TContainingClass = class
    Rec: TRec<TRec<Integer>>; // E2564 Undefined type 'TRec<T>'
  end;

  TContainingRecord = record
    Rec: TRec<TRec<Integer>>; // E2564 Undefined type 'TRec<T>'
  end;

  TContainingObject = object
    Rec: TRec<TRec<Integer>>; // E2564 Undefined type 'TRec<T>'
  end;

The defect appears to arise when using the type inside an aggregate compound type.

It's somewhat lame, but this is the only workaround I can find:

type
  TRec<T> = record
    Value: T;
  end;

  TRecRec<T> = record
    Value: TRec<T>;
  end;

  TContainingClass = class
    Rec: TRecRec<Integer>;
  end;

But that's not going to be at all useful in any real world scenario.

David Heffernan
  • 572,264
  • 40
  • 974
  • 1,389