fix(semantic): break analyser<->symbol-table cycle that leaked the AST
Compiling even a tiny program leaked ~339 heap objects (TSymbol ×129, TObjectList ×156, TTypeDesc ×14, plus the TProgram, TSymbolTable and TSemanticAnalyser singletons), despite the driver correctly calling Prog.Free() and Semantic.Free(). Root cause was a strong reference cycle: TSemanticAnalyser owns its TSymbolTable (FTable), and the table held a plain strong reference back to the analyser via FUsesChainProvider (set in Analyse so Lookup can consult per-unit visibility). Under refcount ARC the two keep each other alive: Semantic.Free() drops the analyser to rc=1 (the table still references it), the analyser destructor never runs, FTable.Free() is never called, and the entire reachable graph — every scope, symbol, builtin type descriptor, and the whole AST — leaks as one unit. Marking FUsesChainProvider [Unretained] breaks the cycle. The provider is the analyser that owns the table and always outlives it (its destructor is what frees the table), so a non-owning reference cannot dangle. Impact: --debug leak count on a tiny compile drops from 339 to 14 (~96% reclaimed). The remaining 14 are a separate, smaller global-scope fragment. Full suite OK (2671 tests), self-compile clean, FIXPOINT_OK. This adjusts TSymbolTable's field layout (one ARC slot becomes non-managed), so it is a layout-affecting change validated via stage-2 fixpoint.
This commit is contained in:
parent
935bd5237d
commit
53ea92da00
|
|
@ -432,8 +432,16 @@ type
|
|||
{ Optional hook into uses-chain lookup. Set by TSemanticAnalyser
|
||||
so Lookup() can consult per-unit visibility after exhausting the
|
||||
pushed scopes but BEFORE falling back to the global scope.
|
||||
Task #44 step 7. }
|
||||
FUsesChainProvider: TUsesChainProvider;
|
||||
Task #44 step 7.
|
||||
|
||||
[Unretained]: the provider is the TSemanticAnalyser that OWNS this
|
||||
symbol table (TSemanticAnalyser.FTable). A strong reference here
|
||||
would form an analyser<->table cycle that refcount ARC cannot
|
||||
collect, so Semantic.Free() would never run the analyser destructor
|
||||
and the whole AST + symbol-table graph (~325 objects) would leak.
|
||||
The analyser always outlives the table — its destructor is what
|
||||
frees the table — so a non-owning reference is safe. }
|
||||
[Unretained] FUsesChainProvider: TUsesChainProvider;
|
||||
|
||||
{ Recursion guard. TSemanticAnalyser.LookupViaUsesChain (which
|
||||
sits behind FUsesChainLookup) calls FTable.Lookup itself to
|
||||
|
|
|
|||
Loading…
Reference in a new issue