Johan
Johan

Reputation: 76597

Why does TFunc<T> deference properly, but array [0..1] of TFunc<T> not?

If I have a TFunc<T: class> a variable of that type will allow me to do dereference the class members directly.
Even class completion supports it.

enter image description here

If however I have an array of TFunc<T>'s then this does not compile.
Why not? Is this a compiler error or is there some underlying reason?

program Project33;
{$APPTYPE CONSOLE}
uses
  System.SysUtils;

type
  TTestObject = class(TInterfacedObject, IInterface)
    procedure Test;
  end;

procedure TTestObject.Test;
begin
  WriteLn('Test');
end;

procedure Test;
var
  A: IInterface;
  TestObject: array [0..4] of TFunc<TTestObject>;
  SingleObject: TFunc<TTestObject>;
  i: integer;
begin
  for i:= 0 to 4 do begin
    a:= TTestObject.Create;
    TestObject[i]:= function: TTestObject
    begin
      Result:= a as TTestObject;
    end;
    TestObject[i].Test;   //<<-- does not compile
    SingleObject:= TestObject[i];
    SingleObject.Test;   // <<-- works.
  end;
end;

begin
  Test; ReadLn;
end.

Upvotes: 4

Views: 324

Answers (1)

David Heffernan
David Heffernan

Reputation: 613003

Pascal allows the parens to be omitted when calling a function or procedure that has no parameters. In your code SingleObject is a function, and

SingleObject.Test

is equivalent to

SingleObject().Test

There is an ambiguity here. When the parser encounters SingleObject, it has to decide whether you mean to refer to the function, or to call it. The parser resolves that ambiguity by taking account of the context. In the example above, it decides that since you used the . operator, you must have intended for the function to be called, and then have the . operator applied to the value returned from that function call.

Now, for your array example, the parser appears to have trouble disambiguating. Personally I think that is an oversight. It is perfectly possible to disambiguate, so far as I can see.

Consider this program:

type
  TRec = class
    procedure Foo; virtual; abstract;
  end;

  TFunc = function: TRec;
  TFuncOfObj = function: TRec of object;
  TRefFunc = reference to function: TRec;

var
  ScalarFunc: TFunc;
  ArrFunc: array [0..0] of TFunc;
  ScalarFuncOfObj: TFuncOfObj;
  ArrFuncOfObj: array [0..0] of TFuncOfObj;
  ScalarRefFunc: TRefFunc;
  ArrRefFunc: array [0..0] of TRefFunc;

begin
  ScalarFunc.Foo; // fine
  ArrFunc[0].Foo; // fine
  ScalarFuncOfObj.Foo; // fine
  ArrFuncOfObj[0].Foo; // fine
  ScalarRefFunc.Foo; // fine
  ArrRefFunc[0].Foo; // E2003 Undeclared identifier: 'Foo'
end.

As you can see, only the array of reference function suffers the problem. My suspicion is that the compiler developers missed whatever steps are needed to handle this disambiguation when they added support for reference function types.

The work around is easy enough. You need to explicitly disambiguate:

ArrRefFunc[0]().Foo;

This entire issue steps from the decision in Pascal to allow function call parens to be omitted when the function has no parameters. In my opinion, given the issues with ambiguity that arise with modern programming styles, it would be nice to go back in time and change that decision.

Upvotes: 6

Related Questions