2

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?

Arioch 'The
  • 15,799
  • 35
  • 62
TheHorse
  • 2,787
  • 1
  • 23
  • 32
  • It works as expected if compare function out of btn1Click – TheHorse Jul 22 '13 at 08:52
  • do not post comments - edit the quesiton instead. – Arioch 'The Jul 22 '13 at 09:12
  • 2
    I'm sure this is a dupe a few times over – David Heffernan Jul 22 '13 at 10:09
  • @DavidHeffernan the problem with such dupes is that novices attribute the fault to anything including RTL bugs but the real cause. They just do not see the cause, do see nothing outstanding in their code. The exotic feature that only happens in Pascal and that is missed in other mainstream languages for reason does not alert them at all. Perhaps we should introduce "nested-functions" tag ? – Arioch 'The Jul 22 '13 at 10:17
  • @DavidHeffernan hardly a dupe, because people having AV in `TStringList` would not read about Win32 callbacks - they do not use them and has zero reasons to read of them. IF merging - that question should be very generalized! – Arioch 'The Jul 22 '13 at 14:34
  • @Arioch 100% cast iron dupe! – David Heffernan Jul 22 '13 at 14:39
  • @DavidHeffernan you know it after the fact. And maybe after my hastely edit. Again, the newb only knows he gots AV in TList, period. He does not use win32 callbacks. He has no reason to look into topics discussing weird cornercases of Win32 APi – Arioch 'The Jul 22 '13 at 14:41
  • @Arioch For sure the asker can easily not find the dupe. But we regular answerers know it. We had the same dupe last week. Remember? – David Heffernan Jul 22 '13 at 16:36
  • @DavidHeffernan but why do we close dupes ? to make the answer disappear. If it cannot be found and creates dupes - then we're just depleting SO from chances of being ultimate help resource. – Arioch 'The Jul 22 '13 at 17:00
  • @Arioch Don't ask me. Ask on meta. – David Heffernan Jul 22 '13 at 17:05
  • 1
    @Arioch, but if we close a question as a duplicate, it's even better. The question appears in the `Linked` list on the right side (as happens even if you post just a comment) and it doesn't automatically mean we will delete it. So it might link that newbie as you say also to the next source of information. – TLama Jul 22 '13 at 17:23
  • 1
    So, given what @TLama says, in order to make SO better you should cast the close vote. – David Heffernan Jul 22 '13 at 20:35
  • Okay, on assumption this question would not be deleted and still be able to be googled. But i seem to never come to question closed and not deleted in google. @TLama ain't closed questions automatically or semi-automatically purged after some time gap ? are there real precedents when Google points to closed but not yet purged question for at least a year ? PS. Ah.. well... since i already retracted my vote i cannot re-aply it. But perhaps someone else would consider it – Arioch 'The Jul 23 '13 at 06:02

1 Answers1

4

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.

Arioch 'The
  • 15,799
  • 35
  • 62
  • 6
    It worked in Win32 because the compiler was smart enough to remove the hidden extra stackframe pointer parameter (EBP) if the local procedure doesn't access anything from the outer procedure's stack. The Win64 compiler doesn't have this logic anymore and always adds the hidden parameter. – Andreas Hausladen Jul 22 '13 at 10:13