Search code examples
delphigenericsdelphi-10-seattle

Why am I getting E2531 "Method requires explicit type argument"


The code below relies pretty heavily on generics, and I think exposes a bug in the generics handling. But maybe there is simply something I don't understand.

The compiler raises the error:

E2531 Method 'CreateBaseItem' requires explicit type arguments

on the line:

foo3 := TFactory.CreateBaseItem<TDescendentFunctionsGroup2.Select>;

yet as far as I can tell, instantiating foo1 through foo4 should all be essentially identical. This complete program highlights the issue:

program SO53568763;

type
  TBaseItem = class(TInterfacedObject);

  IListableItem<T> = interface
    ['{6FD07ACB-04BB-4BFC-A38C-9B98F86DBC25}']
  end;

  TSomeDescendent = class(TBaseItem, IListableItem<TSomeDescendent>)
  end;

  TSelectFunctionsGenerator<T: TBaseItem, IListableItem<T>> = class(TBaseItem)
  end;

  TFunctionsGroup<T: TBaseItem, IListableItem<T>> = class
  public
    type
      Select = TSelectFunctionsGenerator<T>;
  end;

  TDescendentFunctionsGroup1 = class(TFunctionsGroup<TSomeDescendent>);
  TDescendentFunctionsGroup2 = TFunctionsGroup<TSomeDescendent>;

  TFactory = class
  public
    class function CreateBaseItem<T: TBaseItem>: T;
  end;

class function TFactory.CreateBaseItem<T>;
begin
end;

procedure Foo;
var
  foo: TSelectFunctionsGenerator<TSomeDescendent>;
  foo1: TFunctionsGroup<TSomeDescendent>.Select;
  foo2: TDescendentFunctionsGroup1.Select;
  foo3: TDescendentFunctionsGroup2.Select;
begin
  foo := TFactory.CreateBaseItem<TSelectFunctionsGenerator<TSomeDescendent>>;
  foo1 := TFactory.CreateBaseItem<TFunctionsGroup<TSomeDescendent>.Select>;
  foo2 := TFactory.CreateBaseItem<TDescendentFunctionsGroup1.Select>;
  foo3 := TFactory.CreateBaseItem<TDescendentFunctionsGroup2.Select>;
end;

begin
end.

It is strange that TDescendentFunctionsGroup2.Select is explicit enough to declare a variable of that type, but not explicit enough to use as the generic argument to CreateBaseItem.


Solution

  • This does appear to be a compiler bug. The difference between TDescendentFunctionsGroup1 and TDescendentFunctionsGroup2 is that the former is a new class derived from TFunctionsGroup<TSomeDescendent> and the latter is an alias to TFunctionsGroup<TSomeDescendent>.

    So my guess is that the parser or compiler has an issue with aliases to generic types.

    I'm really not sure what benefit the alias gives you, in any case, so I'd simply write it like this:

    var
      foo3: TFunctionsGroup<TSomeDescendent>.Select;
    ...
    foo3 := TFactory.CreateBaseItem<TFunctionsGroup<TSomeDescendent>.Select>;