Primož Gabrijelčič 223 Posted March 7, 2019 Yes, they work 🙂 (and why wouldn't they?). program RecursiveAnon; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils; function MakeFib: TFunc<integer,int64>; var fib: TFunc<integer,int64>; begin fib := function (value: integer): int64 begin if value < 3 then Result := 1 else Result := fib(value - 1) + fib(value -2); end; Result := fib; end; begin var fib := MakeFib; for var i := 1 to 10 do Write(fib(i), ' '); Readln; end. 2 1 Share this post Link to post
Sherlock 663 Posted March 7, 2019 I see what you did there, @Lars Fosdal 😉 2 1 Share this post Link to post
Primož Gabrijelčič 223 Posted March 7, 2019 (edited) 10 minutes ago, Sherlock said: But is it still anonymous though? Of course it is! You can do this: for i := 1 to 10 do Writeln(MakeFib()(i), ' '); As Stefan noted on a side-channel, calling MakeFib causes a memory leak because the anonfunc interface now contains a cyclic reference to itself. Edited March 7, 2019 by Primož Gabrijelčič 1 Share this post Link to post
Dalija Prasnikar 1393 Posted March 7, 2019 21 minutes ago, Primož Gabrijelčič said: As Stefan noted on a side-channel, calling MakeFib causes a memory leak because the anonfunc interface now contains a cyclic reference to itself. var [weak] fib: TFunc<integer,int64>; Marking it as weak breaks the cycle. However, there was a bug with weak that is only recently fixed (not sure whether in 10.3 or 10.3.1) 5 1 Share this post Link to post
terran 0 Posted August 21 Can anyone explain about memory leak? Is it not deleted when exiting function? Need more examples. Share this post Link to post
terran 0 Posted August 21 Quote Note: The use of weak and unsafe references was originally introduced as part of the ARC memory management support for mobile platforms. As ARC is now phased out, this feature remains available only for interface references. Share this post Link to post
Stefan Glienke 1996 Posted August 21 To understand this, you need to understand how anonymous methods are implemented. The compiler implements them by creating a class that inherits from TInterfacedObject. The anonymous method type is a special interface with one method called Invoke, which has the signature of the anonymous method type. When you add ReportMemoryLeaksOnShutdown := True to the code above you will see that it reports one instance of a class called MakeFib$ActRec - that is the class name of the compiler-generated class for the anonymous method. The class also contains fields for every captured variable - in this case the local variable fib is being captured with holds the reference to itself so this is the circular interface reference that causes the memory leak. To demonstrate here is the code as the compiler implements it: type MakeFibAnonClass = class(TInterfacedObject, TFunc<integer,int64>) fib: TFunc<integer,int64>; function Invoke(value: integer): int64; end; function MakeFibAnonClass.Invoke(value: integer): int64; begin if value < 3 then Result := 1 else Result := fib(value - 1) + fib(value -2); end; function MakeFib_Impl: TFunc<integer,int64>; var obj: MakeFibAnonClass; begin obj := MakeFibAnonClass.Create; obj.fib := obj; Result := obj.fib; end; 2 Share this post Link to post
Remy Lebeau 1385 Posted August 21 3 hours ago, Stefan Glienke said: To demonstrate here is the code as the compiler implements it: Inside MakeFib_Impl(), the object reference should be an interface reference instead, to better match what the compiler actually produces: function MakeFib_Impl: TFunc<integer,int64>; var fib: TFunc<integer,int64>; begin fib := MakeFibAnonClass.Create; // refcnt is 1 obj.fib := fib; // refcnt is now 2 Result := fib; end; Although this is a gross oversimplification, as the actual compiler implementation is much more complex (see Variable Binding Mechanism for details). The local fib variable inside of the calling function, and the captured fib variable inside the anonymous method, are actually the same variable in memory. But yes, the self-referencing issue still applies (just on a separate frame object that holds the fib variable). This self-referencing issue is even mentioned in the documentation: Quote It is possible to create a cycle in the method reference/frame link chains that causes a memory leak. For example, storing an anonymous method directly or indirectly in a variable that the anonymous method itself captures creates a cycle, causing a memory leak. 1 Share this post Link to post
Stefan Glienke 1996 Posted August 21 3 hours ago, Remy Lebeau said: The local fib variable inside of the calling function, and the captured fib variable inside the anonymous method, are actually the same variable in memory. There is no local variable - on the source code level, yes there is, but implementation wise they live inside the heap memory of the compiler generated instance. That's why captured variables never appeared in the local variables view until recently when the kinda implemented support for it. Share this post Link to post