Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-devel / 2012-07

nestedprocvar questions

5 messages · 4 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

David Jenkins

Some questions about 'nestedprocvars' mode switch

We have a simplified version of the nested procedure calling demonstrated in tests/test/tmaclocalprocparam3d.pp. It looks more or less like:

{$mode DELPHI}
{$modeswitch nestedprocvars}
type
   tnestedprocvar = function(AArg: string): boolean;
   procedure p1(AArg: string; pp: tnestedprocvar);
   begin
     if pp(AArg) then
       writeln('true');
   end;
   procedure q;
     function r(AArg: string): boolean;
     begin
       writeln(AArg)
       Result := true;
     end;
   begin
      p1('test string', @r);
   end;

Notice that it is $MODE DELPHI, that the proc var is a function, and that we do not access any external variables in the nested function.

Question #1:
There seems to be an interaction between $MODE DELPHI and the 'is nested' construct. I.e. I cannot put the 'is nested' after the tnestedprocvar type and do the function passing with '@r'. If I do the compiler complains about getting a pointer (@r) when it was expecting a function (..): boolean is nested. So either I remove the'@' and just pass 'r' or I do not include the 'is nested'.

Is this correct?

Question #2:
This code is being compiled both with Delphi compiler and with FPC. Delphi handles the nesting with no problem and with no 'is nested' directive.

Should nestedprocvar be included automatically with $MODE DELPHI for compatibility?

Question #3:
If we set up our code as above can we expect the compiler to be consistent in parameter set up for the next while?

Question #4:
If we set up our code as above (with no 'is nested') would it be safe to include access of an external variable (internal to procedure q) to the nested function?

Reply by email

Sven Barth
to David Jenkins
David Jenkins wrote 41 lines
Some questions about 'nestedprocvars' mode switch We have a simplified version of the nested procedure calling demonstrated in tests/test/tmaclocalprocparam3d.pp. It looks more or less like: {$mode DELPHI} {$modeswitch nestedprocvars} type tnestedprocvar = function(AArg: string): boolean; procedure p1(AArg: string; pp: tnestedprocvar); begin if pp(AArg) then writeln('true'); end; procedure q; function r(AArg: string): boolean; begin writeln(AArg) Result := true; end; begin p1('test string', @r); end; Notice that it is $MODE DELPHI, that the proc var is a function, and that we do not access any external variables in the nested function. Question #1: There seems to be an interaction between $MODE DELPHI and the 'is nested' construct. I.e. I cannot put the 'is nested' after the tnestedprocvar type and do the function passing with '@r'. If I do the compiler complains about getting a pointer (@r) when it was expecting a function (..): boolean is nested. So either I remove the'@' and just pass 'r' or I do not include the 'is nested'.

Delphi does not use the "@" to designate the function address, so this isn't to take the address of a function/method. In Delphi you just use "r" (in your example).

Show quoted text 10 lines
Is this correct? Question #2: This code is being compiled both with Delphi compiler and with FPC. Delphi handles the nesting with no problem and with no 'is nested' directive. Should nestedprocvar be included automatically with $MODE DELPHI for compatibility?

Adding "nestedprocvar" automatically wouldn't change anything as you'd still need "is nested" to have a procvar to which nested functions can be passed to.

Question #3: If we set up our code as above can we expect the compiler to be consistent in parameter set up for the next while?

How exactly do you mean this?

Question #4: If we set up our code as above (with no 'is nested') would it be safe to include access of an external variable (internal to procedure q) to the nested function?

The whole purpose of the "is nested" is to allow access to such variables of the parent frame, so without it accessing the nested function will result in problems.

Regards,
Sven

Reply by email

Craig Peterson
to Sven Barth

Hi Sven,

Sven Barth wrote
Delphi does not use the "@" to designate the function address, so this isn't to take the address of a function/method. In Delphi you just use "r" (in your example).

Normally Delphi doesn't allow assigning local functions to procedure variables or arguments (just like FPC without 'nestedprocvar'). If the nested function doesn't access arguments or variables in the outer scope it generates the stack frame differently and the resulting function is identical to a standalone non-nested one. You can then use @ to bypass the typechecking.

What David posted is working Delphi code. We use that style in a few places to better group and encapsulate procedures for callbacks without polluting the unit namespace.

Adding "nestedprocvar" automatically wouldn't change anything as you'd still need "is nested" to have a procvar to which nested functions can be passed to.

The posted code crashes with an EAccessViolation if you run it using FreePascal's default behavior. Adding {$modeswitch nestedprocvar} and nothing else makes it work correctly, so there's some change in compiler behavior even if "is nested" isn't used.

How exactly do you mean this?

Is the behavior described above (the $modeswitch without "is nested") intentional behavior we can rely on, or an unexpected side effect? Can it be added to {$MODE DELPHI} to increase Delphi compatibility?

Regards,
Craig Peterson
Scooter Software

Reply by email

Sven Barth
to Craig Peterson
Craig Peterson wrote 14 lines
Hi Sven, On 7/31/2012 3:11 PM, Sven Barth wrote: > Delphi does not use the "@" to designate the function address, so this > isn't to take the address of a function/method. In Delphi you just use > "r" (in your example). Normally Delphi doesn't allow assigning local functions to procedure variables or arguments (just like FPC without 'nestedprocvar'). If the nested function doesn't access arguments or variables in the outer scope it generates the stack frame differently and the resulting function is identical to a standalone non-nested one. You can then use @ to bypass the typechecking.

Ok... I now tested myself as well using Delphi XE, FPC 2.7.1 (2.6.0 works also) and the following code:

=== source begin ===

program tnested;
{$ifdef fpc}
   {$mode delphi}
{$endif}
{$APPTYPE CONSOLE}
type
   TFunc = function: LongInt;
procedure DoTest(aFunc: TFunc);
begin
   Writeln(aFunc);
end;
procedure Test;
var
   t: Integer;
   function Test1: LongInt;
   begin
     Result := 42;
   end;
   function Test2: LongInt;
   begin
     Result := t;
   end;
begin
   DoTest(@Test1);
   t := 21;
   DoTest(@Test2);
   t := 42;
   DoTest(@Test2);
end;
begin
   Test;
   Readln;
end.

=== source end ===

Without the "@" the compilation in Delphi indeed fails (it also does in FPC, but with a different error).

The output of the one compiled with Delphi is the following:

=== output Delphi begin ===

42
4230595
4230595

=== output Delphi end ===

The output of the FPC one is this:

=== output FPC begin ===

42
0
0

=== output FPC end ===

In both cases the output printed by Test2 can be considered garbage and thus Delphi and FPC behave identical (the value of "t" in the context of "Test2" is undefined).

Show quoted text 13 lines
What David posted is working Delphi code. We use that style in a few places to better group and encapsulate procedures for callbacks without polluting the unit namespace. > Adding "nestedprocvar" automatically wouldn't change anything as you'd > still need "is nested" to have a procvar to which nested functions can > be passed to. The posted code crashes with an EAccessViolation if you run it using FreePascal's default behavior. Adding {$modeswitch nestedprocvar} and nothing else makes it work correctly, so there's some change in compiler behavior even if "is nested" isn't used.

I now tested exactly David's code and you're right... as far as I can see this is because nested functions are all marked as "delphi style nested call" in case the modeswitch "nestedprocvar" is set (note: the access violation might be, because Strings are used and thus initialization and finalization has to take place).

Show quoted text 5 lines
> How exactly do you mean this? Is the behavior described above (the $modeswitch without "is nested") intentional behavior we can rely on, or an unexpected side effect? Can it be added to {$MODE DELPHI} to increase Delphi compatibility?

I don't know whether it is intentional behavior as Jonas is the one who implemented this, but you can open a feature request issue on the bugtracker where you describe your situation similar to here (with a stress on Delphi compatiblity ;) ) and then Jonas can decide whether he enables it for mode Delphi or not (Note: he is currently on vacation, so it might take a bit longer). And please attach a full compilable example (that compiles in Delphi in should also compile in FPC (using Delphi mode)).

Regards,
Sven

Reply by email

Jonas Maebe
to David Jenkins: Some questions about 'nestedprocvars' mode switch We have a simplified version of the…
David Jenkins wrote 34 lines
Some questions about 'nestedprocvars' mode switch We have a simplified version of the nested procedure calling demonstrated in tests/test/tmaclocalprocparam3d.pp. It looks more or less like: {$mode DELPHI} {$modeswitch nestedprocvars} type tnestedprocvar = function(AArg: string): boolean; procedure p1(AArg: string; pp: tnestedprocvar); begin if pp(AArg) then writeln('true'); end; procedure q; function r(AArg: string): boolean; begin writeln(AArg) Result := true; end; begin p1('test string', @r); end; Notice that it is $MODE DELPHI, that the proc var is a function, and that we do not access any external variables in the nested function. Question #1: There seems to be an interaction between $MODE DELPHI and the 'is nested' construct. I.e. I cannot put the 'is nested' after the tnestedprocvar type and do the function passing with '@r'. If I do the compiler complains about getting a pointer (@r) when it was expecting a function (..): boolean is nested. So either I remove the'@' and just pass 'r' or I do not include the 'is nested'. Is this correct?

@someproc in Delphi (and FPC Delphi mode) evaluates to an untyped pointer, rather than to a procvar type. Delphi (and FPC in Delphi mode) allow implicit type conversions from untyped pointers into regular procedure variables. Neither FPC nor Delphi allows doing this when assigning a value to a "procedure of object" though, because a "procedure of object" internally consists of two pointers and an untyped pointer only defines one of those. Since a "procedure is nested" internally also consists of two pointers, it cannot be done there either.

Question #2: This code is being compiled both with Delphi compiler and with FPC. Delphi handles the nesting with no problem and with no 'is nested' directive. Should nestedprocvar be included automatically with $MODE DELPHI for compatibility?

It is purely a coincidence that {$modeswitch nestedprocvars} currently changes the calling convention in a way that makes it compatible with Delphi as far as calling local functions that do not access parent's local variables or parameters. This is not guaranteed or intentional in any way, and may change in the future or on different platforms.

See also http://bugs.freepascal.org/view.php?id=9432 and the related bugs reports.

Question #3: If we set up our code as above can we expect the compiler to be consistent in parameter set up for the next while?

As mentioned above: no.

Question #4: If we set up our code as above (with no 'is nested') would it be safe to include access of an external variable (internal to procedure q) to the nested function?

It would be guaranteed to fail both in FPC and in Delphi. The code has no access to the nested frame pointer, which would be required for being able to access those variables.

Jonas

Reply by email