Recent

Author Topic: Compiler Generated Temporary Interface References  (Read 5576 times)

Coxy

  • New Member
  • *
  • Posts: 32
Compiler Generated Temporary Interface References
« on: January 26, 2021, 02:12:25 pm »
I'm having problems with temporary interface references created by the compiler. Here's a simplified example,

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls;
  9.  
  10. type
  11.  
  12.   TMyInterface = interface
  13.     procedure DoSometing;
  14.   end;
  15.  
  16.   TMyOtherIntf = interface(TMyInterface)
  17.   end;
  18.  
  19.   { TMyClass }
  20.  
  21.   TMyClass = class(TInterfacedObject,TMyOtherIntf)
  22.   public
  23.     destructor Destroy;override;
  24.     procedure DoSometing;
  25.   end;
  26.  
  27.   { TForm1 }
  28.  
  29.   TForm1 = class(TForm)
  30.     Button1: TButton;
  31.     procedure Button1Click(Sender: TObject);
  32.   private
  33.     function GetMyInterface:TMyOtherIntf;
  34.   public
  35.  
  36.   end;
  37.  
  38. var
  39.   Form1: TForm1;
  40.  
  41. implementation
  42.  
  43. {$R *.lfm}
  44.  
  45. { TForm1 }
  46.  
  47. procedure TForm1.Button1Click(Sender: TObject);
  48. var
  49.   myIntf : TMyInterface;
  50. begin
  51.   MyIntf:=GetMyInterface;
  52.  
  53.   MyIntf:=nil;    // Should cause destructor of TMyClass to be called.
  54. end;        // Place breakpoint here
  55.  
  56. function TForm1.GetMyInterface: TMyOtherIntf;
  57. begin
  58.   result := TMyClass.Create;
  59. end;
  60.  
  61. { TMyClass }
  62.  
  63. destructor TMyClass.Destroy;
  64. begin
  65.   inherited Destroy;        // Place breakpoint here
  66. end;
  67.  
  68. procedure TMyClass.DoSometing;
  69. begin
  70.  
  71. end;
  72.  
  73. end.
  74.  

If you place breakpoints where indicated and run this example you'll find the "end;" breakpoint is triggered before the one in the destructor. This is probably caused by compiler creating temporary variable to hold "GetMyInterface" interface value "TMyOtherIntf" which is then converted to "TMyInterface", it being tidied up in the routines exit code.

Now I'm implementing non reference counted interfaces with my own "_AddRef", "_Release" and "QueryInterface" routines, and have a "FreeInterface" routine similar to "FreeAndNil" that free the underlying TObject. The tidying up of temporary interface references in routine exit code causes an access violation. Example,

Code: Pascal  [Select][+][-]
  1. procedure ClearArray;
  2. var
  3.   i : integer;
  4.   intf : TMyInterface;
  5. begin
  6.   for i:=0 to FIntfCount do
  7.   begin
  8.     intf:=GetInterface(i);
  9.     FreeInterface(intf);
  10.   end;
  11. end;
  12.  

Is there a way to have compiler tidy temporary interfaces references immediately after assignment to "intf" rather than in routine exit code.

I know about using "{$interfaces CORBA}", but I wish to mix both reference counted and non reference counted interfaces in the same unit.

Free Pascal: 3.2.0
OS: Windows 10

« Last Edit: January 26, 2021, 02:30:25 pm by Coxy »

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11382
  • FPC developer.
Re: Compiler Generated Temporary Interface References
« Reply #1 on: January 26, 2021, 02:29:58 pm »
Easiest is to look at the generated assembly code for the method (compile with -al)

If you handtuned your interface handling in Delphi, beware that doesn't mean it is safe if it "works".
Read this: https://stackoverflow.com/questions/9592654/what-are-the-differences-between-implementation-of-interfaces-in-delphi-and-laza/9623502#9623502

Coxy

  • New Member
  • *
  • Posts: 32
Re: Compiler Generated Temporary Interface References
« Reply #2 on: January 26, 2021, 02:50:59 pm »
I stepped through assembler code in debugger to identify use of temporary interface variable and clean up in routine exit code.

For me it works in Delphi and fails in FPC.

Coding routines like "FreeAndNil" and "FreeInterface" become problematic because of the compilers creation and use of temporary variables.

One workaround in code is to do the following,

Code: Pascal  [Select][+][-]
  1. procedure ClearArray;
  2. var
  3.   i : integer;
  4.   intf : TMyInterface;
  5.   procedure SetIntf;
  6.   begin
  7.      intf:=GetInterface(i);
  8.   end;
  9. begin
  10.   for i:=0 to FIntfCount do
  11.   begin
  12.     SetIntf;
  13.     FreeInterface(intf);
  14.   end;
  15. end;
  16.  

Here the temporary interface variable is tidied up in the sub method exit code, resolving the problem. But this is a workaround not a fix.



marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11382
  • FPC developer.
Re: Compiler Generated Temporary Interface References
« Reply #3 on: January 26, 2021, 03:23:03 pm »
I stepped through assembler code in debugger to identify use of temporary interface variable and clean up in routine exit code.

For me it works in Delphi and fails in FPC.

Coding routines like "FreeAndNil" and "FreeInterface" become problematic because of the compilers creation and use of temporary variables.

Then your routines are wrong. If you do it right, then it works with all cases. If you depend on the lifetime of the variable the code is simply wrong, as the URL I gave explains, working in Delphi is not necessarily correct code.

If you still have questions, please create a self contained (preferably console) example of what goes wrong, and we'll look if it is a coding error or a compiler bug (however, that is unlikely)

Coxy

  • New Member
  • *
  • Posts: 32
Re: Compiler Generated Temporary Interface References
« Reply #4 on: January 26, 2021, 04:04:42 pm »
Here's some example code.

See routine FreeInterface, comment/uncomment highlighted lines to reproduce problem.

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls;
  9.  
  10. type
  11.  
  12.   IMyInterface = interface
  13.     ['{CEC89718-0B2D-4BA5-886F-03FF11955644}']
  14.     function GetSelf:TObject;
  15.     function _ReferenceCounted:boolean;
  16.   end;
  17.  
  18.   { TMyInterfacedObjectNoRefCount }
  19.  
  20.   TMyInterfacedObjectNoRefCount = class(TObject, IUnknown, IMyInterface)
  21.   private
  22.   protected
  23.     function _AddRef:longint;{$IFDEF LINUX}cdecl{$ELSE}stdcall{$ENDIF};
  24.     function _Release:longint;{$IFDEF LINUX}cdecl{$ELSE}stdcall{$ENDIF};
  25.     function QueryInterface({$IFDEF FPC_HAS_CONSTREF}constref{$ELSE}const{$ENDIF} IID: TGUID; out Obj): {$IFDEF LINUX}Longint;cdecl{$ELSE}HResult;stdcall{$ENDIF};
  26.   public
  27.  
  28.     function GetSelf:TObject;virtual;
  29.     function _ReferenceCounted:boolean;virtual;
  30.   end;
  31.  
  32.   TMyInterface = interface
  33.     procedure DoSometing;
  34.   end;
  35.  
  36.   TMyOtherIntf = interface(TMyInterface)
  37.   end;
  38.  
  39.   { TMyClass }
  40.  
  41.   TMyClass = class(TMyInterfacedObjectNoRefCount,TMyOtherIntf)
  42.   public
  43.     destructor Destroy;override;
  44.     procedure DoSometing;
  45.   end;
  46.  
  47.   { TForm1 }
  48.  
  49.   TForm1 = class(TForm)
  50.     Button1: TButton;
  51.     procedure Button1Click(Sender: TObject);
  52.   private
  53.     function GetMyInterface:TMyOtherIntf;
  54.   public
  55.  
  56.   end;
  57.  
  58. var
  59.   Form1: TForm1;
  60.  
  61. implementation
  62.  
  63. {$R *.lfm}
  64.  
  65.  
  66. procedure FreeInterface({$IFDEF FPC_HAS_CONSTREF}constref{$ELSE}const{$ENDIF} X);
  67. {$IFDEF AUTOREFCOUNT}
  68. begin
  69. end;
  70. {$ELSE}
  71. var
  72.   obj : TObject;
  73.   p : ^Pointer;
  74.   a1 : IUnknown;
  75.   a2 : IMyInterface;
  76.   procedure GetIMyInterface;
  77.   begin
  78.     // The following assignment in FPC causes RecCount to be incremented twice
  79.     // while DCC only once. This is because FPC increments with the call to
  80.     // QueryInterface (DCC doesn't). The temp result is released (decremented)
  81.     // on exit from this nested method. So the net effect is only one increment.
  82.     // If this assignment were done in the main routine the temp result would be
  83.     // released in the tidy up for FreeInterface after the object had been freed
  84.     // causing an AV.
  85.     // This method works in both FPC and DCC. Except in routine that call
  86.     // FreeInterface that also have a temporary reference to this interface.
  87.     a2 := a1 as IMyInterface;
  88.   end;
  89. begin
  90.   a1 := IUnknown(X);
  91.   if not assigned(a1) then
  92.     exit;
  93.  
  94.   try
  95. //    a2 := a1 as IMyInterface;     // Uncomment for AV
  96.     GetIMyInterface;              // Uncomment for work
  97.   except
  98.     exit;
  99.   end;
  100.   if not a2._ReferenceCounted then
  101.   begin
  102.     obj := a2.GetSelf;
  103.  
  104.     a1 := nil;
  105.     a2 := nil;
  106.  
  107.     obj.Free;
  108.  
  109.     p := Pointer(@X);
  110.     p^ := nil;
  111.   end
  112.   else
  113.     a1 := nil;
  114. end;
  115.  
  116. { TMyInterfacedObjectNoRefCount }
  117.  
  118. function TMyInterfacedObjectNoRefCount._AddRef: longint; stdcall;
  119. begin
  120.   result := -1
  121. end;
  122.  
  123. function TMyInterfacedObjectNoRefCount._Release: longint; stdcall;
  124. begin
  125.   result := -1
  126. end;
  127.  
  128. function TMyInterfacedObjectNoRefCount.QueryInterface(constref IID: TGUID; out
  129.   Obj): HResult; stdcall;
  130. const
  131.   E_NOINTERFACE = HResult($80004002);
  132. begin
  133.   if GetInterface(IID, Obj) then Result := 0 else Result := E_NOINTERFACE;
  134. end;
  135.  
  136. function TMyInterfacedObjectNoRefCount.GetSelf: TObject;
  137. begin
  138.   result := self;
  139. end;
  140.  
  141. function TMyInterfacedObjectNoRefCount._ReferenceCounted: boolean;
  142. begin
  143.   result := false;
  144. end;
  145.  
  146. {$ENDIF}
  147.  
  148.  
  149. { TForm1 }
  150.  
  151. procedure TForm1.Button1Click(Sender: TObject);
  152. var
  153.   myIntf : TMyInterface;
  154. begin
  155.   MyIntf:=GetMyInterface;
  156.  
  157.   FreeInterface(MyIntf);
  158. end;
  159.  
  160. function TForm1.GetMyInterface: TMyOtherIntf;
  161. begin
  162.   result := TMyClass.Create;
  163. end;
  164.  
  165. { TMyClass }
  166.  
  167. destructor TMyClass.Destroy;
  168. begin
  169.   inherited Destroy;
  170. end;
  171.  
  172. procedure TMyClass.DoSometing;
  173. begin
  174.  
  175. end;
  176.  
  177. end.
  178.  

PascalDragon

  • Hero Member
  • *****
  • Posts: 5446
  • Compiler Developer
Re: Compiler Generated Temporary Interface References
« Reply #5 on: January 27, 2021, 09:18:06 am »
See routine FreeInterface, comment/uncomment highlighted lines to reproduce problem.

Mixing class and interface references like that is always a slippery slope and thus the general advice is: don't. Just don't. You're essentially relying on implementation defined behaviour that can for example change between compiler versions or depending on the optimization settings.

I know about using "{$interfaces CORBA}", but I wish to mix both reference counted and non reference counted interfaces in the same unit.

The {$Interfaces xxx} directive is local so you can decide for each interface declaration inside a unit whether it should be a reference counted one or not. What is however not possible is to inherit a reference counted interface from a non reference counted one and vice versa.

Coxy

  • New Member
  • *
  • Posts: 32
Re: Compiler Generated Temporary Interface References
« Reply #6 on: January 27, 2021, 10:39:02 am »
Quote
The {$Interfaces xxx} directive is local so you can decide for each interface declaration inside a unit whether it should be a reference counted one or not. What is however not possible is to inherit a reference counted interface from a non reference counted one and vice versa.

Thanks for pointing this out, I didn't know this.

Quote
Mixing class and interface references like that is always a slippery slope and thus the general advice is: don't. Just don't. You're essentially relying on implementation defined behaviour that can for example change between compiler versions or depending on the optimization settings.


My point about the compilers use of temporary interface references, possible having unintended consequences, is not confined to example "FreeInterface". I've updated my original example to highlight the issue.

Place breakpoint on "end;" of "Button1Click" and also in destructor of "TMyClass", and run code.

Code: Pascal  [Select][+][-]
  1. unit Unit2;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls;
  9.  
  10. type
  11.  
  12.   IMyInterface = interface
  13.     ['{7E741756-BA4F-4BB2-8AFF-5805586D3B53}']
  14.     procedure DoSometing;
  15.   end;
  16.  
  17.   IMyOtherIntf = interface(IMyInterface)
  18.     ['{50897EE1-6BBE-431B-920A-EA3CFDCDF763}']
  19.   end;
  20.  
  21.   { TMyClass }
  22.  
  23.   TMyClass = class(TInterfacedObject,IMyOtherIntf,IMyInterface)
  24.   protected
  25.     FId : string;
  26.   public
  27.     constructor Create(pId:string);
  28.     destructor Destroy;override;
  29.  
  30.     procedure DoSometing;
  31.   end;
  32.  
  33.   { TForm2 }
  34.  
  35.   TForm2 = class(TForm)
  36.     Button1: TButton;
  37.     procedure Button1Click(Sender: TObject);
  38.   private
  39.     function GetMyInterface:IMyInterface;
  40.     function GetMyOtherIntf:IMyInterface;
  41.   public
  42.  
  43.   end;
  44.  
  45. var
  46.   Form2: TForm2;
  47.  
  48. implementation
  49.  
  50. {$R *.lfm}
  51.  
  52. { TForm1 }
  53.  
  54. procedure TForm2.Button1Click(Sender: TObject);
  55. var
  56.   myInterface : IMyInterface;
  57.   myOtherIntf : IMyOtherIntf;
  58. begin
  59.   myInterface:=GetMyInterface;
  60.   myOtherIntf:=GetMyOtherIntf as IMyOtherIntf;
  61.  
  62.   myInterface:=nil;    // Should cause destructor of TMyClass to be called.
  63.   myOtherIntf:=nil;    // Should cause destructor of TMyClass to be called.
  64.  
  65.   // Something that writes out application state.
  66.  
  67. end;        // Place breakpoint here
  68.  
  69. function TForm2.GetMyInterface: IMyInterface;
  70. begin
  71.   result := TMyClass.Create('IMyInterface');
  72. end;
  73.  
  74. function TForm2.GetMyOtherIntf: IMyInterface;
  75. begin
  76.   result := TMyClass.Create('IMyOtherIntf');
  77. end;
  78.  
  79. { TMyClass }
  80.  
  81. constructor TMyClass.Create(pId: string);
  82. begin
  83.   FId:=pId;
  84. end;
  85.  
  86. destructor TMyClass.Destroy;
  87. begin
  88.   // Do something that changes application state.
  89.  
  90.   inherited Destroy;        // Place breakpoint here
  91. end;
  92.  
  93. procedure TMyClass.DoSometing;
  94. begin
  95.  
  96. end;
  97.  
  98. end.
  99.  


First breakpoint reached is in destructor with item id "IMyInterface", next breakpoint is "end;" of button click handler, final breakpoint is destructor called for "IMyOtherIntf". This would lead to "IMyOtherIntf" application state update being lost.

I know this is a contrived example, but it illustrates what could happen in a concrete application.

Given this code and knowledge they are the final references to their interfaces,

Code: Pascal  [Select][+][-]
  1.   myInterface:=nil;
  2.   myOtherIntf:=nil;
  3.  

I would expect outcomes and timing of side effects, calling of destructors, for both assignments to be consistent and not depend on build type or optimization.






marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11382
  • FPC developer.
Re: Compiler Generated Temporary Interface References
« Reply #7 on: January 27, 2021, 11:10:52 am »
The two interfaces are two references to the same object. So the object gets refcounted twice, and then with the :=nil decreased twice and finally deallocated.

The exact moment is implementation defined, and you shouldn't rely on it. I see no problem.

Keep in mind that the interfaces don't hold state, the underlying object does.

Or do I understand you wrong?

Coxy

  • New Member
  • *
  • Posts: 32
Re: Compiler Generated Temporary Interface References
« Reply #8 on: January 27, 2021, 11:52:33 am »
Quote
The two interfaces are two references to the same object. So the object gets refcounted twice, and then with the :=nil decreased twice and finally deallocated.

It doesn't matter both interfaces are implemented by the same object type, it could be rewritten to use unrelated and different interfaces and implementing objects to produce same behaviour.

Quote
The exact moment is implementation defined, and you shouldn't rely on it. I see no problem.

The manual https://www.freepascal.org/docs-html/ref/refse50.html says  "When the reference count reaches zero, usually the instance of the class that implements the interface, is freed.". I known no time is specified here but I take it to be immediate and in flow of code, not as soon as is convenient and possibly out of sequence, if the latter is the case then documentation should explicitly state this so application logic can reflect this.

Quote
Or do I understand you wrong?

My purpose here is to improve the compiler. Its possible use of temporary items, of what ever kind, should not be visible to or govern application development. This example shows this not to be the case.

PascalDragon

  • Hero Member
  • *****
  • Posts: 5446
  • Compiler Developer
Re: Compiler Generated Temporary Interface References
« Reply #9 on: January 27, 2021, 01:31:30 pm »
Quote
The exact moment is implementation defined, and you shouldn't rely on it. I see no problem.

The manual https://www.freepascal.org/docs-html/ref/refse50.html says  "When the reference count reaches zero, usually the instance of the class that implements the interface, is freed.". I known no time is specified here but I take it to be immediate and in flow of code, not as soon as is convenient and possibly out of sequence, if the latter is the case then documentation should explicitly state this so application logic can reflect this.

And the information that the object instance is freed when the reference count reaches zero is true (at least for those inheriting from TInterfacedObject or those classes correctly implementing a reference counting scheme similar to TInterfacedObject). What the documentation explicitly does not mention is where additional references might come from (namely temporary variables). This is by choice and thus the behaviour when the class instance will be freed is implementation defined. The only guarantee is that it will be freed as long as it isn't kept artificially alive (e.g. by a global variable or a cycle between interface references).

Quote
Or do I understand you wrong?

My purpose here is to improve the compiler. Its possible use of temporary items, of what ever kind, should not be visible to or govern application development. This example shows this not to be the case.

But they are visible to application development. The same is true for other managed types like strings as well. Though it's indeed best visible with interfaces.

Coxy

  • New Member
  • *
  • Posts: 32
Re: Compiler Generated Temporary Interface References
« Reply #10 on: January 27, 2021, 02:16:36 pm »
Quote
But they are visible to application development.

Not sure what you mean here, temporary variables created by compiler are not visible to application developer.

Quote
And the information that the object instance is freed when the reference count reaches zero is true (at least for those inheriting from TInterfacedObject or those classes correctly implementing a reference counting scheme similar to TInterfacedObject). What the documentation explicitly does not mention is where additional references might come from (namely temporary variables). This is by choice and thus the behaviour when the class instance will be freed is implementation defined. The only guarantee is that it will be freed as long as it isn't kept artificially alive (e.g. by a global variable or a cycle between interface references).

I think you are saying this is a language feature, which I would disagree with. Temporary items are a compiler construct, a means to an end, not part of the language, their use, and side effects of their use, should be hidden from application developers and not impact on application design.

BeniBela

  • Hero Member
  • *****
  • Posts: 905
    • homepage
Re: Compiler Generated Temporary Interface References
« Reply #11 on: January 27, 2021, 10:52:44 pm »
If you depend on the lifetime of the variable the code is simply wrong, as the URL I gave explains, working in Delphi is not necessarily correct code.


Of the variable?

But with temporary interface references, there is no variable

The manual https://www.freepascal.org/docs-html/ref/refse50.html says  "When the reference count reaches zero, usually the instance of the class that implements the interface, is freed.".

It also says "Whenever the variable goes out of scope, the reference count is automatically decreased"

That seems to imply, that is does not decrease the reference count before the variable goes out of scope


In fact, it does not decrease it until the end of the scope:


Code: Pascal  [Select][+][-]
  1. type ti = interface
  2. procedure dop;
  3. end;
  4. type TSMart = class(TInterfacedObject,ti)
  5.   n: string;
  6.   procedure dop;
  7.   constructor Create(an: string);
  8.   destructor Destroy; override;
  9. end;
  10.  
  11. procedure TSMart.dop;
  12. begin
  13.   writeln();
  14. end;
  15.  
  16. constructor TSMart.Create(an: string);
  17. begin
  18.   n := an
  19. end;
  20.  
  21. destructor TSMart.Destroy;
  22. begin
  23.   writeln('destroy ', N);
  24.   inherited Destroy;
  25. end;
  26.  
  27. procedure test;
  28. var a,b,c,d:IUnknown;
  29. begin
  30.   a := TSMart.Create('a');
  31.   (ti(TSMart.Create('0'))).dop;
  32.   b := TSMart.Create('b');
  33.   (ti(TSMart.Create('1'))).dop;
  34.   c := TSMart.Create('c');
  35.   (ti(TSMart.Create('2'))).dop;
  36.   d := TSMart.Create('d');
  37.   (ti(TSMart.Create('3'))).dop;
  38.   writeln('x');
  39. end;
  40.  

Coxy

  • New Member
  • *
  • Posts: 32
Re: Compiler Generated Temporary Interface References
« Reply #12 on: January 28, 2021, 08:33:49 am »
I'm not concerned with ARC life cycle management as such, compiler temporary items behave identically to application ones and in line with the language definition.

Compile generated code is equivalent to this (for example code posted earlier),

Code: Pascal  [Select][+][-]
  1. procedure TForm2.Button1Click(Sender: TObject);
  2. var
  3.   myInterface : IMyInterface;
  4.   myOtherIntf : IMyOtherIntf;
  5.   compilerTemp : IMyInterface;
  6. begin
  7.   myInterface:=GetMyInterface;
  8.   compilerTemp:=GetMyOtherIntf;
  9.   myOtherIntf:=compilerTemp as IMyOtherIntf;
  10.  
  11.   myInterface:=nil;    // Should cause destructor of TMyClass to be called.
  12.   myOtherIntf:=nil;    // Should cause destructor of TMyClass to be called.
  13.  
  14.   // Something that writes out application state.
  15.  
  16.   compilerTemp:=nil;
  17. end;        // Place breakpoint here

This code effects execution follow.

If the compiler, instead, generated this,

Code: Pascal  [Select][+][-]
  1. procedure TForm2.Button1Click(Sender: TObject);
  2. var
  3.   myInterface : IMyInterface;
  4.   myOtherIntf : IMyOtherIntf;
  5.   compilerTemp : IMyInterface;
  6. begin
  7.   myInterface:=GetMyInterface;
  8.   compilerTemp:=GetMyOtherIntf;
  9.   myOtherIntf:=compilerTemp as IMyOtherIntf;
  10.   compilerTemp:=nil;
  11.  
  12.   myInterface:=nil;    // Should cause destructor of TMyClass to be called.
  13.   myOtherIntf:=nil;    // Should cause destructor of TMyClass to be called.
  14.  
  15.   // Something that writes out application state.
  16.  
  17. end;        // Place breakpoint here

Then the code would behave identically with or without the use of temporary items.

If there's a reason why the compiler handles temporary items in this way I willing to accept it, but would like to know what it is.


« Last Edit: January 28, 2021, 08:40:50 am by Coxy »

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11382
  • FPC developer.
Re: Compiler Generated Temporary Interface References
« Reply #13 on: January 28, 2021, 10:02:46 am »
I think you are saying this is a language feature, which I would disagree with. Temporary items are a compiler construct, a means to an end, not part of the language, their use, and side effects of their use, should be hidden from application developers and not impact on application design.

The language typically allows the room for the compiler to use it or not. This is what "implementation defined" means.

And afaik the rule is that the interface reference stays defined for the statement it was last used in. How long it persists after that, is implementation defined, and assumptions on it are by definition wrong.

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11382
  • FPC developer.
Re: Compiler Generated Temporary Interface References
« Reply #14 on: January 28, 2021, 10:12:27 am »
It also says "Whenever the variable goes out of scope, the reference count is automatically decreased"

That is the whole point, the scope is not fixated in detail for the programmer.

In practice it can be everything from after the statement of last use to the end of the procedure/method, depending how the compiler divides the code up into blocks of statements that belong together.   

Some compilers deallocate temps quickly, which is something that dates back to old (16-bit) compilers, to keep pressure of the limited stack. (and keep in mind that with old static (short)string types that was more deeply felt. A recursive procedure with two by-value shortstring parameters and some local variables could run out of 16-bits worth of stack space rather quickly)

Some let them linger longer because it is more efficient to do it all at once, usually at the end of the method. ABI rules also play a role.

 

TinyPortal © 2005-2018