Question

I have this simple code

procedure TForm2.btn1Click(Sender: TObject);
var s : TStringList;

  function compare(s : TStringList; i1, i2 : integer) : integer;
  begin
    result := CompareText(s[i1], s[i2]);
  end;

begin
  s := TStringList.Create;
  try
    s.add('s1');
    s.add('s2');
    s.add('s3');
    s.CustomSort(@compare);
  finally
    s.free;
  end;
end;

It works as expected when I compile it as 32-bit, but I get Access Violation when use 64-bit. For 64-bit version in function compare, s = nil. i2 = some random value;

It also works as expected even for Win64 target, if i extract compare function outside of btn1Click function.

Is it bug of System.Classes, is there any way to fix?

Was it helpful?

Solution

Local nested functions SHOULD NOT be assigned to procedural variables (in particular SHOULD NOT be passed as procedural variable parameters).

http://docwiki.embarcadero.com/RADStudio/XE4/en/Procedural_Types - search for "nested".

The reason is simple: local functions should arrange their stack so that they get access to all of their upper functions (parents) stack frames. However those stack frames can not exist when those function were jumped into without prior calling of all the chain of their parent functions one by one. And that "out of the blue" dive is exactly what happens when passing their address outside to executives oblivious to this particular calling chain. It is like calling some object's virtual methods without VMT having link to parent classes and without Self pointing to proper VMT.

The bug is not that such a code does not work in win64.

The bug is that it even compiles in win32 or win64 regardless.

When Delphi would fix the bug, it would no more compile such a code unless you make compare a proper global function like it should be.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top