Initialize() / Finalize() standard procedures (managed-type intrinsics)
- Type: feature (Pascal frontend — Track P parse; lowering touches the managed-type init/final machinery = Track A gate)
- Status: backlog — unclaimed
- Opened: 2026-07-18, out of the FPC-compiler gap analysis
- Blocks: [[goal-compile-fpc-compiler]] — used by FPC's
cclasses.pas:2394(TCmdStrListItem.GetCopy) and common in low-level FPC/Delphi library code that raw-moves objects containing managed fields.
Problem
FPC/Delphi define Initialize(v) / Finalize(v[, count]) standard procs:
treat v (a variable of a managed type, or a record/array containing
managed fields) as raw memory and set it to the initialized-empty state
(Initialize), or release its managed contents (Finalize). Canonical use:
{ TLinkedListItem.GetCopy raw-Moves all instance data -> the ansistring
field now aliases the original without a refcount bump. Reinit the field
as if it were fresh memory, then assign properly: }
Initialize(TCmdStrListItem(Result).FPStr);
TCmdStrListItem(Result).FPStr := FPstr;
pxx has the underlying machinery (managed locals get init/final generated by
the compiler) but does not expose it as callable intrinsics — grep for an
Initialize builtin comes up empty.
Fix shape
Two intrinsics resolving against the argument's static type:
Initialize(v)→ zero/nil-fill the managed slots ofvWITHOUT finalizing previous contents (the point: memory is assumed garbage).Finalize(v)→ run the finalization the compiler would run at scope exit, then leave the slot nil/empty. Optionalcountarg for arrays-via-pointer can be a later slice. Reuse the existing per-type init/final generation; the intrinsic is a call into it with an explicit address.
Gate
make test + self-host byte-identical. Regressions: Move-then-Initialize
refcount pattern (mirror the cclasses idiom) leaks nothing and double-frees
nothing under the ansistring refcount checks; Finalize on a record with
mixed managed/unmanaged fields.