Sign in Join

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

Mailing lists / fpc-pascal / 2018-01

_Release call location for unused function results

9 messages · 7 people · –

Reply by email Subscribe to this list Your subscription

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

Zoë Peterson

In the code below Bar creates a reference counted object and returns an interface to it. Foo calls Bar but doesn't assign the result anywhere.

In Delphi, this produces the result:
Foo >
TAutoRelease.Create
Foo <
TAutoRelease.Destroy

But in Free Pascal it produces this:
Foo >
TAutoRelease.Create
TAutoRelease.Destroy
Foo <

If I change Foo to assign the interface to a local variable but don't do anything with that variable, Free Pascal produces the same results as Delphi.

Delphi's behavior is what I expected, and allows tricks with triggering behavior when leaving the current scope. Barry Kelly has one example on his blog where he talks about implementing C++'s RAII using it: http://blog.barrkel.com/2010/01/one-liner-raii-in-delphi.html

Is Free Pascal's behavior intentional? Can it be changed to match Delphi?

Thanks,
Zoë Peterson
Scooter Software

============================================

program AutoRelease;
{$IFDEF FPC}{$MODE DELPHI}{$ENDIF}
{$APPTYPE CONSOLE}

uses
Classes,

   SysUtils;
type
   TAutoRelease = class(TInterfacedObject)
     constructor Create;
     destructor Destroy; override;
   end;
constructor TAutoRelease.Create;
begin
   WriteLn('TAutoRelease.Create');
end;
destructor TAutoRelease.Destroy;
begin
   WriteLn('TAutoRelease.Destroy');
   inherited;
end;
function Bar: IInterface;
begin
   Result := TAutoRelease.Create;
end;
procedure Foo;
begin
   WriteLn('Foo >');
   Bar;
   WriteLn('Foo <');
end;
begin
   Foo;
end.
Graeme Geldenhuys
to Zoë Peterson
Zoë Peterson wrote
Is Free Pascal's behavior intentional? Can it be changed to match Delphi?

Something I've queried with the FPC developers some 10 years ago. The Delphi behaviour is undocumented, so developers are relying on a implementation detail that could change without notice.

FPC, as you noticed, implements such behaviour differently. They can, because once again, the Delphi behaviour in undocumented.

As someone that also works on Delphi code, the "cool interfaces trick" so often used in Delphi (eg: to implement a home-grown call stack etc) doesn't work so nicely with FPC - where you need a interface reference variable.

Anyway, the FPC team had no intention in changing the behaviour to something that is undocumented. So we had to learn to live with it. Maybe you have more luck with them that I did. ;-)

Regards,
Graeme

fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal http://fpgui.sourceforge.net/ My public PGP key: http://tinyurl.com/graeme-pgp
Maciej Izak
to Zoë Peterson: In the code below Bar creates a reference counted object and returns an interface to it. …

2018-01-29 23:32 GMT+01:00 Zoë Peterson :

Is Free Pascal's behavior intentional? Can it be changed to match Delphi?

the topic is well known. I have plan to add new modeswitch for that:

SCOPEDINTERFACEDESTROY

If you like you can try existing patch:

https://bugs.freepascal.org/view.php?id=30409

anyway somehow Delphi (undocumented) way seems bad. SCOPEDINTERFACEDESTROY would be big help for legacy code ported from Delphi...

FPC has more elegant alternative way for the same thing, you can simple use *with*:

procedure Foo;
begin
  WriteLn('Foo >');
  with Bar do
  begin
    WriteLn('Foo <');
  end;
end;
Best regards, Maciej Izak
Michael Van Canneyt
to Zoë Peterson: In the code below Bar creates a reference counted object and returns an interface to it. …
Zoë Peterson wrote 25 lines
In the code below Bar creates a reference counted object and returns an interface to it. Foo calls Bar but doesn't assign the result anywhere. In Delphi, this produces the result: Foo > TAutoRelease.Create Foo < TAutoRelease.Destroy But in Free Pascal it produces this: Foo > TAutoRelease.Create TAutoRelease.Destroy Foo < If I change Foo to assign the interface to a local variable but don't do anything with that variable, Free Pascal produces the same results as Delphi. Delphi's behavior is what I expected, and allows tricks with triggering behavior when leaving the current scope. Barry Kelly has one example on his blog where he talks about implementing C++'s RAII using it: http://blog.barrkel.com/2010/01/one-liner-raii-in-delphi.html Is Free Pascal's behavior intentional? Can it be changed to match Delphi?

The question is whether Delphi's behaviour is intentional. You are relying on undocmented behaviour: when does an intermediate temp variable go out of scope?

This is not defined, and the behaviours of Delphi and FPC regarding temp variables do not match. This is well known since many many years. see e.g.
https://bugs.freepascal.org/view.php?id=30409

As Maciej wrote, it can be changed.

But relying on what is an implementation detail is ill-advised, and certainly not robust programming.

Michael.

Maciej Izak
to Michael Van Canneyt

2018-01-30 8:34 GMT+01:00 Michael Van Canneyt :

The question is whether Delphi's behaviour is intentional.

Sadly it is intentional (but undocumented), the facts:

* Barry Kelly compiler guy post
* Works with all known Delphi versions
* I've checked this for NG compiler - also works like in classic Delphi (especially this point means that the "feature" is intentional )

Seems like the SCOPEDINTERFACEDESTROY mode-switch is necessary. The question is:

would we like to enabling this for all Delphi modes?

the approach with "with" for "scoping" seems more proper even in Delphi mode. Probably SCOPEDINTERFACEDESTROY should stay off for all modes including Delphi modes.

Best regards, Maciej Izak
Marco van de Voort
to Maciej Izak

In our previous episode, Maciej Izak said:

Show quoted text 7 lines
> The question is whether Delphi's behaviour is intentional. Sadly it is intentional (but undocumented), the facts: * Barry Kelly compiler guy post * Works with all known Delphi versions * I've checked this for NG compiler - also works like in classic Delphi (especially this point means that the "feature" is intentional )

Have you tested this with large methods? The trouble is we only get reports/code from people for whom the hack succeeded, not from ones that tried and failed.

I can remember from one of the bugreports or mail threads about very large procedure bodies (where FPC hit virtual register numbers) that Delphi starts to deallocate temps earlier depending on procedure size and maybe used language constructs. (number of structured temps?) That was mostly strings, but might also go for interfaces. I can also imagine that one would like to disable this in case recursion is detected.

Seems like the SCOPEDINTERFACEDESTROY mode-switch is necessary.

No, one could also simply fix the relevant code.

question is: would we like to enabling this for all Delphi modes?

Everything that needs this is essentially feature abuse. I would not enable it by default, but IF for some reason this needs to be added, when enabled add a permanent note/warning that this is a workaround for broken code.

Maciej Izak
to Marco van de Voort

2018-01-30 14:40 GMT+01:00 Marco van de Voort :

Show quoted text 13 lines
Have you tested this with large methods? The trouble is we only get reports/code from people for whom the hack succeeded, not from ones that tried and failed. I can remember from one of the bugreports or mail threads about very large procedure bodies (where FPC hit virtual register numbers) that Delphi starts to deallocate temps earlier depending on procedure size and maybe used language constructs. (number of structured temps?) That was mostly strings, but might also go for interfaces. I can also imagine that one would like to disable this in case recursion is detected.

Yes I am aware of this. In the case of interfaces for large methods all seems works as presented for simple example. I was not able to reproduce this problem for interfaces.

> Seems like the SCOPEDINTERFACEDESTROY mode-switch is necessary. No, one could also simply fix the relevant code.

I am not the fan of SCOPEDINTERFACEDESTROY. My first impression about this was : WTF. In some cases is almost impossible to fix large legacy (and often wrong) code base.

> question is: would we like to enabling this for all Delphi modes? Everything that needs this is essentially feature abuse. I would not enable it by default

+1 . Anyway I am still considering : commit or not :P.

Best regards, Maciej Izak
Sven Barth
to Maciej Izak
"Maciej Izak" wrote
> question is: would we like to enabling this for all Delphi modes? Everything that needs this is essentially feature abuse. I would not enable it by default

+1 . Anyway I am still considering : commit or not :P.

While I still don't want to have this switch no matter the legacy codebase affected by it, if we add it we definitely don't add it enabled anywhere. And please, maybe it "ScopedInterfaceRelease", not "-Destroy" as that is what the functionality does.

Also I remember from some time back that someone (maybe even Jonas) presented an example where Delphi does in fact behave different. I'd need to find that again however. :/

Regards,
Sven

Florian Klämpfl
to Maciej Izak: 2018-01-30 14:40 GMT+01:00 Marco van de Voort : Yes I am aware of this. In the case of…
Maciej Izak wrote 34 lines
2018-01-30 14:40 GMT+01:00 Marco van de Voort < >: Have you tested this with large methods? The trouble is we only get reports/code from people for whom the hack succeeded, not from ones that tried and failed. I can remember from one of the bugreports or mail threads about very large procedure bodies (where FPC hit virtual register numbers) that Delphi starts to deallocate temps earlier depending on procedure size and maybe used language constructs.  (number of structured temps?) That was mostly strings, but might also go for interfaces. I can also imagine that one would like to disable this in case recursion is detected. Yes I am aware of this. In the case of interfaces for large methods all seems works as presented for simple example. I was not able to reproduce this problem for interfaces.   > Seems like the SCOPEDINTERFACEDESTROY mode-switch is necessary. No, one could also simply fix the relevant code. I am not the fan of SCOPEDINTERFACEDESTROY. My first impression about this was : WTF. In some cases is almost impossible to fix large legacy (and often wrong) code base.   > question is: would we like to enabling this for all Delphi modes? Everything that needs this is essentially feature abuse. I would not enable it by default +1 . Anyway I am still considering : commit or not :P.

If is implemented, it needs to be clearly documented how it works.