Search code examples
delphiinlineanonymous-functiondelphi-2009

Delphi anonymous function passed to inlined function


I have stumbled upon an unexpected Delphi 2009 behaviour. After investigating a strange bug in my code, I managed to narrow down the issue and create a minimal example which I present below.

Of course, the following code prints the value 1:

program Example1;

{$APPTYPE CONSOLE}

type
  TIntFcn = reference to function(const X: integer): integer;

function fcn(AFunction: TIntFcn; a: integer): integer; inline;
begin
  result := AFunction(a);
end;

begin

  writeln(fcn(function(const X: integer): integer
    begin
      result := 1;
    end, 0));

end.

Similarly, this program prints the value 2:

program Example2;

{$APPTYPE CONSOLE}

type
  TIntFcn = reference to function(const X: integer): integer;

function fcn(AFunction: TIntFcn; a: integer): integer; inline;
begin
  result := AFunction(a);
end;

begin

  writeln(fcn(function(const X: integer): integer
    begin
      result := 2;
    end, 0));

end.

"Obviously", this third program prints the same value as the first one, namely, 1:

program Produce;

{$APPTYPE CONSOLE}

type
  TIntFcn = reference to function(const X: integer): integer;

function fcn(AFunction: TIntFcn; a: integer): integer; inline;
begin
  result := AFunction(a);
end;

begin

  writeln(fcn(function(const X: integer): integer
    begin
      result := 1;
    end, 0));

  fcn(function(const X: integer): integer
    begin
      result := 2;
    end, 0); // discard the output

end.

However, the output isn't 1, but 2. It seems like the compiler uses the second anonymous function in the call to fcn in writeln.

To me this seems like a bug in the Delphi 2009 compiler, but it could also simply be my not understanding the more subtle details about anonymous functions in Delphi. What do you think?


Solution

  • This certainly appears to be a bug, and according to the comments received on the question, this has been fixed in Delphi XE. Probably the simplest workaround is to skip asking for inlining if the compiler cannot handle it correctly:

    program Solve;
    
    {$APPTYPE CONSOLE}
    
    type
      TIntFcn = reference to function(const X: integer): integer;
    
    function fcn(AFunction: TIntFcn; a: integer): integer;
      {$IF CompilerVersion >= 22}inline;{$IFEND} {Warning: Horrible bug in Delphi 2009}
    begin
      result := AFunction(a);
    end;
    
    begin
    
      writeln(fcn(function(const X: integer): integer
        begin
          result := 1;
        end, 0));
    
      fcn(function(const X: integer): integer
        begin
          result := 2;
        end, 0); // discard the output
    
    end.
    

    In most cases, the loss of performance should be negligible in Delphi 2009, and you do request inlining in XE and later. Of course, if you don't think inlining is important at all, you can simply remove the request altogether.