autofree Ownership Types
autofree T is an owning form of an object-reference or pointer type.
It has the same runtime representation as T, but records that its storage is
responsible for releasing the referenced heap allocation. This page defines the
ownership transfer and cleanup rules. See Objects and Properties,
Structures, Pointers, and Function References,
and Arrays and Slices for the underlying types.
Declaration and Construction
autofree may decorate an object type, a typed pointer type, or pointer:
var object_owner : autofree OThing = new OThing()
var buffer_owner : autofree ^byte = ^byte(MemAlloc(256))
var generic_owner : autofree pointer = MemAlloc(256)
It cannot decorate a primitive, pointer, structure, union, function-reference,
or array type directly. It may instead be the element type of a fixed or dynamic
array:
var fixed : [2]autofree OThing
var dynamic : [*]autofree OThing
Use new autofree T to declare an owning variable directly:
var owner = new autofree OThing()
Ordinary new T still produces T. Assigning that normal result to an
autofree T destination claims it.
Bare :? inference always produces the normal, non-owning type. It cannot be
combined with new autofree T; omit :? as above, or write autofree ? when
the destination's ownership must be explicit. The same rule applies to inferred
for ... in variables.
Type aliases may name an autofree type, but all placement restrictions still
apply where the alias is used.
Cleanup
When an autofree value is destroyed or overwritten, its current value is
released if it is non-nil:
autofree OcallsO.*Destroy()and then releases the object's heap storage.autofree ^Treleases the pointed-to allocation without a destructor call.
The owner storage is set to nil after cleanup. Local owners are initialized to
nil before their initializer is evaluated and are cleaned up on scope exit,
early control-flow exit, and exception unwinding. Object fields are initialized
the same way and are cleaned up in reverse declaration order when their object
is destroyed.
Global owners are zero-initialized. Reassignment cleans up their previous value, but DQ does not currently run automatic global/module de-initialization at program shutdown.
delete is valid for an autofree object or pointer and always leaves
that owner nil:
delete object_owner
if object_owner == nil:
// true
endif
For ordinary object and pointer values, use the existing delete value = nil
form when the source variable must also be cleared.
Assignment and Moves
Assignment to an autofree T first cleans up its previous value. The source
then determines whether the assignment claims, moves, or borrows:
| Source | Assignment to autofree T |
Assignment to normal T |
|---|---|---|
normal T |
claims the reference; the normal source remains an ordinary alias | ordinary copy/borrow |
autofree T lvalue |
moves the reference and clears the source | borrows/copies the reference; the source stays owner |
For example:
var first : autofree OThing = new OThing()
var second : autofree OThing = first
// first is nil; second owns the allocation
Assigning an owner to itself is a no-op. A normal alias whose value has been
claimed by an owner must not later be deleted or otherwise treated as an owner;
it becomes invalid once the autofree destination releases the allocation.
Placement Restrictions
autofree is allowed for variables, object fields, fixed-array elements,
dynamic-array elements, and ref parameters. It is not allowed for:
- embedded (
<-) object fields; - structure or union members;
- properties or property indices;
- function return types;
refinorrefoutparameters.
These restrictions prevent ownership cleanup from being hidden in storage forms that do not have a unique, well-defined destruction path.
Functions
A normal value parameter borrows both normal and autofree object/pointer
arguments. Passing an owner to such a parameter does not transfer or clear it.
A ref parameter preserves ownership qualification exactly: ref autofree T
requires an autofree T argument, while ref T requires normal T storage.
autofree return types, refin autofree T, and refout autofree T are rejected.
Arrays
For [N]autofree T, whole-array assignment moves every element: each target
element is cleaned up, each source owner is transferred, and the source cells
are cleared. Self-assignment is a no-op.
[*]autofree T keeps normal dynamic-array manager sharing: assigning the array
shares its manager, and its elements are destroyed exactly once when the final
manager reference is released. Element assignment uses the normal owner rules.
Single-element Append, Prepend, and Insert claim a normal source or move
an autofree lvalue source. They also support a source element from the same
destination array, including when the operation reallocates or shifts elements.
Clone(), Append/Prepend/Insert with a range source, and the corresponding
slice insertion operations are rejected for [*]autofree T, because copying an
array range would duplicate ownership. Borrowed []autofree T views remain
usable for indexing, iteration, and read-only APIs.
Use for item : ref in owners to replace an owning array element in place. The
reference preserves the element's ownership qualification, so assigning a new
value cleans up the previous element and leaves the array as the owner.
Pop() and PopFirst() are ownership extraction operations for an
[*]autofree T: they remove an element without destroying it and return normal
T. The caller must explicitly delete the result or assign it to an
autofree T destination.