Class StatementOwnedCloseableCalls

java.lang.Object
org.ek9lang.compiler.common.RuleSupport
org.ek9lang.compiler.phase5.StatementOwnedCloseableCalls
All Implemented Interfaces:
Consumer<org.antlr.v4.runtime.tree.ParseTree>

final class StatementOwnedCloseableCalls extends RuleSupport implements Consumer<org.antlr.v4.runtime.tree.ParseTree>
THE decision of which calls make a closeable value that the block statement making it OWNS - the unnamed half of the closeable ownership rule (Steve, 2026-10-05): "A closeable value you never NAME is owned by the statement that created it and closed when that statement finishes." The named half is CloseableLocalOrError (E81056).

Given a call site (a call, an operationCall or an operator expression) and the call phase 3 resolved for it - the exact overload - the result is owned by its statement, and the call is marked through MarkStatementOwned, when all of these hold:

  • its type is closeable (CloseableResourceToBeChecked.isManagedCloseableType(ISymbol));
  • it runs every time the evaluation that owns it runs (MustExecutePosition.Scope.EVALUATION): its statement - or, for code in no statement, its returning variable's or property's initialiser, or its text's string - or the nearest CONDITIONALLY evaluated operand it is part of (the right of and/or/xor or of a coalescing operator, a ternary arm, a later else if condition, a condition after a guard, the right of :=?, a case expression; Steve, 2026-10-06, lane conditional-closeables: "an unnamed closeable is closed as soon as the evaluation that made it finishes"). When the value IS the result of such a conditional (a ternary arm, either side of a coalescing operator), the test starts from the outermost conditional instead: the arm runs at most once each time it does, and the evaluation that uses the result owns the value, closing it only if it was made;
  • it is not BORROWED: a constructor's result is always new; otherwise the callee's RETAINS must not say the result may be its receiver's interior or an argument (MayAlias), and a callee that cannot be found counts as borrowed;
  • it is not HANDED ON: stored (declared, assigned, returned, put in a property, a guard or a try resource), captured by a dynamic function or class, or passed to any constructor (the new object may keep it - a capture is a constructor argument), or passed as an argument the callee's RETAINS says it keeps (RetainsArgument), or to a callee that cannot be found. A value the statement only CALLS something on - a receiver, an operator's left operand, an interpolated part, a stream source, a for loop's collection, a switch's subject, a stream's sink or a tee's side target - is not handed on.

The route matrix (lane closeable-matrix, 2026-10-21) is docs/language-design/EK9_CLOSEABLE_OWNERSHIP_MATRIX.md: every position an unnamed closeable can be written in, what decides it here and the fixture that pins it. Positions with no recorded call are read through the operator the type serves (OperatorTargetMethodResolver), exactly as a call's callee is: the element of a membership test (x in c is c.contains(x), x in a ... b is a <=> x and b <=> x) and the operand of a compound assignment. A case value is the argument of its case's comparison. An argument that reaches its callee only as its #^ promotion is a loan whatever the callee keeps (reachesCalleeOnlyAsPromotion(ScopedSymbol, int, ISymbol)). An element of a List or Dict literal is KEPT by the new collection (decision A, E81072), as is the receiver of an operator that keeps or hands back its receiver.

The direction is deliberately conservative: anything not recognised counts as handed on, because a value wrongly kept open is a possible leak and a value wrongly closed is a use-after-close. IR generation only reads the mark (phase7/generator/StatementOwnedCloseables) and emits the owner and the close; it never decides.

One owner can never close what it owns: the ARGUMENTS of an explicit super(...) or this(...) (D20, Steve 2026-10-20). A construction call must be the first statement (E05050), so nothing - not even a try - can surround it, and the JVM rejects a protected region that covers it. A closeable owned there - super(describe(Res())) - is E81071 (UNNAMED_CLOSEABLE_IN_CONSTRUCTION_CALL) instead of being marked; the fix is to make the value in a function and pass the result (super(freshDescription())).

It is asked by the call-graph sweep (callgraph/CallGraphCollector, D22), after ReceiverInteriorReturns has derived the RETAINS of user methods, which the borrowed test reads; the collector then reads the mark and records the edge to the owned value's operator close.

  • Constructor Details

  • Method Details

    • accept

      public void accept(org.antlr.v4.runtime.tree.ParseTree callSite)
      Mark the call made at callSite when its statement owns the closeable value it makes - or, when that owner is the arguments of an explicit construction call, report E81071.
      Specified by:
      accept in interface Consumer<org.antlr.v4.runtime.tree.ParseTree>
      Parameters:
      callSite - a call, an operationCall or an operator expression