Class RetainReleaseBalanceVerifier
OwnershipLowering on every
construct it lowers (EK9_IR_OWNERSHIP_MODEL.md §11.2, stage 3). The ownership form itself is
OwnershipVerifier's (IR_ANALYSIS). Use this — do NOT re-derive a scope-balance walk elsewhere.
Once main is on the +1 convention it has no native backend and no runtime leak gate, so
nothing else structurally checks that a RETAIN was dropped correctly during IR generation.
The JVM backend treats RETAIN/RELEASE as no-ops and SCOPE_* as debug-only, so
a mis-balanced scope compiles and runs fine on the JVM yet is a native leak or use-after-free. This
verifier makes the +1 invariant STRUCTURAL and backend-independent: it runs at compile time on every
construct (not just corpus runtime paths), so "if it compiles, it's good" extends to ARC balance.
Invariant checked (report-only for now — see IRAnalysis): within a single method body,
a scope that is SCOPE_EXIT'd or SCOPE_REGISTER'd into must first be SCOPE_ENTER'd,
and a scope that is entered must be exited on at least one path. These are branch-independent: an orphan
exit is a double-release and an enter with no exit anywhere is an outright leak. Deliberately NOT flagged
is SCOPE_ENTER count != SCOPE_EXIT count — a method with multiple return paths correctly emits one
SCOPE_EXIT per path for a single SCOPE_ENTER (e.g. _isSet enters once and exits on
both the return-false and return-true branches), so counts diverge legitimately and only one exit runs on
any path. Detecting the true per-path leak (a scope not exited on SOME path) needs control-flow-graph path
analysis and is the prerequisite for promoting this phase to enforcing.
Per-activation ownership (LoopActivationOwnership, structural): a value made in every iteration of a
loop is registered with a scope of that iteration (A11), and a slot rebound in a loop has an owner for its last
value (A10) - the two stage-0 rules of EK9_IR_OWNERSHIP_MODEL.md.
The retain heuristic (a RETAIN of a non-return, non-field temp with no matching
SCOPE_REGISTER) is reported as informational only — it flags likely leaks but has known
false-positive shapes (escaping results, argument ownership transfer) that the report-only corpus run
is meant to characterise before any promotion to enforcing.
Collection is IRStructureWalk - every nested list of every composite - so a scope entered in one nested
list and exited in another still balances. The return variable (ownership
transferred to the caller) is whatever the operation's own RETURN names - rtn by convention, but
a generated operator in a dynamic class that captures a variable named rtn returns _rtn (D37),
so it is read from the IR, never assumed - and field paths contain '.' (store-site release/retain pair):
both are excluded from the retain heuristic.
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic final recordA single balance finding for one construct/operation. -
Field Summary
FieldsModifier and TypeFieldDescription(package private) static final StringThe conventional return variable, excluded as well as whatever the operation'sRETURNs name. -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionverify(IRConstruct construct) Verify every operation body in the construct and return all findings (empty when balanced).(package private) List<RetainReleaseBalanceVerifier.Finding> verify(IRConstruct construct, Function<OperationInstr, Set<String>> movedOut) Verify a LOWERED construct: asverify(IRConstruct), told for each operation the variables its ownership form moved out of (MovedOutOf).
-
Field Details
-
RETURN_VARIABLE
The conventional return variable, excluded as well as whatever the operation'sRETURNs name.- See Also:
-
-
Constructor Details
-
RetainReleaseBalanceVerifier
public RetainReleaseBalanceVerifier()
-
-
Method Details
-
verify
Verify every operation body in the construct and return all findings (empty when balanced). -
verify
List<RetainReleaseBalanceVerifier.Finding> verify(IRConstruct construct, Function<OperationInstr, Set<String>> movedOut) Verify a LOWERED construct: asverify(IRConstruct), told for each operation the variables its ownership form moved out of (MovedOutOf). Their last value left them by a move the lowering turned into a bareSTORE, already judged by A10 on the ownership form; like a return variable, A10 does not ask about them again. Used byOwnershipLowering's V-L check.
-