2.24. Finalizer¶
Finalizer is a special function, which is called in exactly two cases
delete
is called explicitly on a data type:
var f <- [{int 1;2;3;4}]
delete f
Lambda based iterator or generator is sequenced out:
var src <- [{int 1;2;3;4}]
var gen <- generator<int&> [[<-src]] () <| $ ()
for w in src
yield w
return false
for t in gen
print("t = {t}\n")
// finalizer is called on captured version of src
By default finalizers are called recursively on subtypes.
If memory models allows deallocation, standard finalizers will also free the memory:
options persistent_heap = true
var src <- [{int 1;2;3;4}]
delete src // memory of src will be freed here
Custom finalizer can be defined for any type by overriding finalize
function.
Generic custom finalizers are also allowed:
struct Foo
a : int
def finalize ( var foo : Foo )
print("we kill foo\n")
var f = [[Foo a = 5]]
delete f // prints 'we kill foo' here
2.24.1. Rules and implementation details¶
Finalizers obey the following rules.
If custom finalize
is available, its called instead of default one.
Pointer finalizer expands to calling finalize
on dereferenced pointer,
and then calling native memory finalizer on the result:
var pf = new Foo
unsafe
delete pf
expands to:
def finalize ( var __this:Foo?& explicit -const )
if __this != null
_::finalize(deref(__this))
delete /*native*/ __this
__this = null
Static array calls finalize_dim
generic, which finalizes all its values:
var f : Foo[5]
delete f
expands to:
def builtin`finalize_dim ( var a:Foo aka TT[5] explicit )
for aV in a
_::finalize(aV)
Dynamic array calls finalize
generic, which finalizes all its values:
var f : array<Foo>
delete f
expands to:
def builtin`finalize ( var a:array<Foo aka TT> explicit )
for aV in a
_::finalize(aV)
__builtin_array_free(a,4,__context__)
Table calls finalize
generic, which finalizes all its values, but not keys:
var f : table<string;Foo>
delete f
expands to:
def builtin`finalize ( var a:table<string aka TK;Foo aka TV> explicit )
for aV in values(a)
_::finalize(aV)
__builtin_table_free(a,8,4,__context__)
Custom finalizer is generated for structure. Fields annotated as [[do_not_delete]] are ignored.
memzero
clears structure memory at the end:
struct Goo
a : Foo
[[do_not_delete]] b : array<int>
var g <- [[Goo]]
delete g
expands to:
def finalize ( var __this:Goo explicit )
_::finalize(__this.a)
__::builtin`finalize(__this.b)
memzero(__this)
Tuple behaves similar to structure. There is no way to ignore individual fields:
var t : tuple<Foo; int>
delete t
expands to:
def finalize ( var __this:tuple<Foo;int> explicit -const )
_::finalize(__this._0)
memzero(__this)
Variant behaves similar to tuple. Only currently active variant is finalized:
var t : variant<f:Foo; i:int; ai:array<int>>
delete t
epxands to:
def finalize ( var __this:variant<f:Foo;i:int;ai:array<int>> explicit -const )
if __this is f
_::finalize(__this.f)
else if __this is ai
__::builtin`finalize(__this.ai)
memzero(__this)
Lambdas and generators have their capture structure finalized. Lambda can have custom finalizer defined as well (see Lambdas).
Classes can define custom finalizer inside the class body (see Classes).