Recent

Author Topic: Why does the call via function pointer result in an access violation? [Solved]  (Read 1060 times)

peter.dell

  • New Member
  • *
  • Posts: 24
Hello,
It the first time I'm using function pointer in FPC and I wonder why the access to "global" in " Result := (i > global);" results in an access violation here. I would have expected that the variable is visible, not matter if I call the function directly or via the pointer.

Code: Pascal  [Select][+][-]
  1.  procedure TestLanguage();
  2.  
  3.     var global: Integer;
  4.  
  5.     function TestFunction(const i: Integer): Boolean;
  6.     begin
  7.       Result := (i > global);
  8.     end;
  9.  
  10.  
  11.     procedure TestFunctionPointer;
  12.     type
  13.       TFunction = function(const i: Integer): Boolean;
  14.     var
  15.       f: TFunction;
  16.     begin
  17.  
  18.       f := @TestFunction;
  19.       global:=0;
  20.       Assert(f(1)= True);
  21.     end;  
  22.  
  23. end;

[Window Title]
Error

[Content]
Project TestUnits raised exception class 'External: ACCESS VIOLATION' with message:
Access violation reading from address $FFFFFFFFFFFFFFFD.

 In file 'TestUnits.pas' at line 146:
Result := (i > global);
« Last Edit: December 20, 2025, 11:12:29 pm by peter.dell »

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 12021
  • Debugger - SynEdit - and more
    • wiki
Re: Why does the call via function pointer result in an access violation?
« Reply #1 on: December 14, 2025, 01:59:35 am »
My settings don't even compile it. But probably there is a setting to turn checks off...

"TestFunction" is a nested procedure.

That means it actually has a hidden extra parameter. Which is a reference to the outer function stackframe.

The "global" var, in your case is a local var of the "TestLanguage" function, and on that stackframe.

When you call the TestFunction via the incorrect variable, then that ref to the stackframe is not initialized, so when it gets accessed....




In fpc 3.3.1 you can use
Code: Pascal  [Select][+][-]
  1. { $ModeSwitch nestedprocvars}
  2.  type
  3.       TFunction = function(const i: Integer): Boolean is nested;
  4.  

or "reference to function... " (with another modeswitch)

jamie

  • Hero Member
  • *****
  • Posts: 7493
Re: Why does the call via function pointer result in an access violation?
« Reply #2 on: December 14, 2025, 03:19:59 pm »
That's an issue I found when porting code from Delphi, the encapsulation of a callback does not generate a true callback frame when specifying "StdCall" at the end.

 In the end I had to move that callback outside for it to work, In Delphi it works within with no issues which was nice because I could keep all the code enclosed for that operation.

Jamie

The only true wisdom is knowing you know nothing

Thaddy

  • Hero Member
  • *****
  • Posts: 18700
  • To Europe: simply sell USA bonds: dollar collapses
Re: Why does the call via function pointer result in an access violation?
« Reply #3 on: December 15, 2025, 08:57:45 am »
https://gitlab.com/freepascal.org/fpc/source/-/issues/41500
The example I added to the report is not a work-around, but the correct syntax.
That example also works in fpc 3.2.3.:
Code: Pascal  [Select][+][-]
  1. {$mode DELPHI}{$modeswitch nestedprocvars}
  2. uses
  3.   SysUtils, Classes;
  4.  
  5. var
  6.   MyList: TList;
  7.   i: Integer;
  8.   Item: TComponent;
  9.  
  10. function CompareNormal(Item1, Item2: Pointer): Integer;
  11. begin
  12.   Result := CompareText(TComponent(Item1).Name, TComponent(Item2).Name);
  13. end;
  14.  
  15. procedure SortMyList();
  16.  
  17.   function CompareNested(Item1, Item2: Pointer): Integer;
  18.   begin
  19.     Result := CompareText(TComponent(Item1).Name, TComponent(Item2).Name);  // crashes here because Item2 pointer is wrong
  20.   end;
  21.  
  22. begin
  23.   MyList.Sort(@CompareNormal); // ok
  24.   MyList.Sort(@CompareNested); // ok
  25. end;
  26.  
  27. begin
  28.   MyList := TList.Create;
  29.   for i := 1 to 65 do
  30.   begin
  31.     Item := TComponent.Create(nil);
  32.     Item.Name := 'Item' + IntToStr(i);
  33.     MyList.Add(Item);
  34.   end;
  35.  
  36.   SortMyList();
  37.   for I := 1 to 65 do
  38.     TComponent(MyList[i-1]).Free;
  39.   MyList.Free;
  40.   readln;
  41. end.
« Last Edit: December 15, 2025, 09:50:31 am by Thaddy »
If Europe sells their USA bonds the USD will collapse. Europe can affort that given average state debts. The USA can't affort that. Just an advice...

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1570
    • Lebeau Software
Re: Why does the call via function pointer result in an access violation?
« Reply #4 on: December 15, 2025, 05:49:24 pm »
In Delphi it works within with no issues which was nice because I could keep all the code enclosed for that operation.

Not really.  For instance, lots of people would use nested procedures as Win32 API callbacks in 32bit builds, and then their code would break when they later migrated their code to 64bit builds.  Nested procedures are handled special by the compiler, so you simply can't safely form function pointers to them, it's undefined behavior.  So just don't do it.
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

ASerge

  • Hero Member
  • *****
  • Posts: 2475
Re: Why does the call via function pointer result in an access violation?
« Reply #5 on: December 15, 2025, 10:06:19 pm »
In fpc 3.3.1 you can use
This also works in FPC 3.2.2.
Code: Pascal  [Select][+][-]
  1. {$MODE OBJFPC}
  2. {$IFDEF WINDOWS}
  3.   {$APPTYPE CONSOLE}
  4. {$ENDIF}
  5. {$MODESWITCH NESTEDPROCVARS}
  6.  
  7. procedure TestLanguage;
  8.   var
  9.     global: Integer;
  10.  
  11.   function TestFunction(const i: Integer): Boolean;
  12.   begin
  13.     Result := (i > global);
  14.   end;
  15.  
  16.   procedure TestFunctionPointer;
  17.   type
  18.     TFunction = function(const i: Integer): Boolean is nested;
  19.   var
  20.     f: TFunction;
  21.   begin
  22.     f := @TestFunction;
  23.     global := 0;
  24.     Writeln(f(1));
  25.   end;
  26.  
  27. begin
  28.   TestFunctionPointer;
  29. end;
  30.  
  31. begin
  32.   TestLanguage;
  33.   Readln;
  34. end.

Warfley

  • Hero Member
  • *****
  • Posts: 2037
Re: Why does the call via function pointer result in an access violation?
« Reply #6 on: December 16, 2025, 02:17:38 pm »
On main (3.3+) you can also use function references. They capture local variables and make them also usable outside the parent function (but require dynamic allocations and have therefore a performance and memory penalty)

PascalDragon

  • Hero Member
  • *****
  • Posts: 6283
  • Compiler Developer
Re: Why does the call via function pointer result in an access violation?
« Reply #7 on: December 18, 2025, 09:34:26 pm »
On main (3.3+) you can also use function references. They capture local variables and make them also usable outside the parent function (but require dynamic allocations and have therefore a performance and memory penalty)

The (dynamic) memory penalty is essentially only the size of the reference count for the TInterfacedObject, because the local variables are moved from the stack to the capturer object instance.
And the performance penalty for the allocation is reduced in impact the more the generated function reference is used, cause then it's essentially only a virtual method call.

peter.dell

  • New Member
  • *
  • Posts: 24
Re: Why does the call via function pointer result in an access violation?
« Reply #8 on: December 20, 2025, 11:12:00 pm »
Thank you all a ton for the many replies. I've added the mode switch, and it appears to work. FPC is really great!

 

TinyPortal © 2005-2018