Class StatementOwnedCloseables

java.lang.Object
org.ek9lang.compiler.phase7.generator.AbstractGenerator
org.ek9lang.compiler.phase7.generator.StatementOwnedCloseables

public final class StatementOwnedCloseables extends AbstractGenerator
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; a closeable value you NAME must be declared as a try-with-resources resource." The named half is phase 5's E81056 (CloseableLocalOrError).

This class only EMITS. Which calls make a closeable the statement owns is decided in phase 5, on the call's own resolved symbol (phase5/StatementOwnedCloseableCalls), and arrives here on the CALL (CallInstr.isStatementOwned()). For each such call the statement always makes it declares an owner variable before the statement, stores the temporary into it right after the call, and makes the statement the body of THE owned region (ResourceCloser.closingRegion(List, List, ResourceCloser.FirstFailure, DebugInfo)) that closes the owners that are present, last created first - the same region, and the same failure rule (shape B), try-with-resources uses. So cat TextFile(path).input() > lines closes the input on normal completion, when a stage ends the stream early, and when an exception leaves the statement.

A marked call is looked for at the evaluation's own level and in the setup of a string interpolation, a List literal or a Dict literal (all evaluated unconditionally, before the instruction that uses them). Calls inside a nested block belong to that block's statements (each wrapped on its own). A variable the evaluation declares is declared BEFORE the wrapping so it is still in reach after it.

🔑 The owning region is wherever the evaluation is that phase 5 found the call in (Steve, 2026-10-06, lane initialiser-owned-closeables): the region is THE same one, only its body differs.

  • a block statement - apply(ParserRuleContext, List), from BlockStmtInstrGenerator;
  • a returning variable's declaration - apply(ParserRuleContext, List), from OperationDfnGenerator and ReturningParamProcessor (so the expression forms' returning variables too);
  • a property's initialiser, inside _i_init - apply(ParserRuleContext, List), from PropertyInitialisers;
  • an evaluation that is nested INSIDE another instruction and hands one value on - an if or loop condition, a ternary's control, the left of a coalescing or logical operator, the operand of ?, a range bound - ownedEvaluation(ParserRuleContext, List, String, ISymbol). A statement's own level never reaches into those instructions, and a loop condition runs again on every iteration, so each closes what it made as it finishes.
  • an operand evaluated only CONDITIONALLY (Steve, 2026-10-06, lane conditional-closeables: "an unnamed closeable is closed as soon as the evaluation that made it finishes") - the right of and/or and of ??/?:/<?..., a ternary arm, a condition after a guard, a case value - ownedEvaluation(ParserRuleContext, List, String, ISymbol); the right of :=? - apply(ParserRuleContext, List). Its region is inside the operand's own instructions, so it runs, and closes, only when the operand does. (A later else if condition already went through the condition site above.)

🔑 A value that IS a conditional's result is DEFERRED: an arm whose value is a closeable cannot close it when it finishes, so ownedEvaluation(ParserRuleContext, List, String, ISymbol) stores each marked temporary it makes into an owner as it is made and leaves the owner to the nearest enclosing evaluation that owns its temporaries (own(List, DebugInfo) finds the store however deeply it is nested, declares and registers the owner before its region and closes it if present). Only the arm that ran is closed, and a named value the same conditional chose is never closed by it.

A marked call nested anywhere else (a structure none of these sites wraps) stays open - a possible leak, never a wrong close.

  • Constructor Details

  • Method Details

    • apply

      public List<IRInstr> apply(org.antlr.v4.runtime.ParserRuleContext statementCtx, List<IRInstr> statement)
      The statement's (or initialiser's) instructions, wrapped so it closes the closeable temporaries it created - or unchanged when it creates none.
    • ownedEvaluation

      public StatementOwnedCloseables.OwnedEvaluation ownedEvaluation(org.antlr.v4.runtime.ParserRuleContext ctx, List<IRInstr> evaluation, String resultVariable, ISymbol resultType)
      An evaluation nested inside another instruction (a condition, a control, an operand) that hands ONE value on: the evaluation wrapped, like a statement, in THE owned region that closes the closeable temporaries it made - and the variable that now holds its value after the region. That variable is declared before the region (null, so it is definitely assigned on every path to the code after it) and the value is stored into it as the region's last act. Unchanged - the same instructions and resultVariable - when the evaluation owns no temporary.

      The value must not be closed here when it is the temporary itself: a value of a closeable type is used after the evaluation (a ternary arm, either side of a coalescing operator, the operand of ?). Its temporaries are then DEFERRED (deferToEnclosing(List)): each is stored, as it is made, into an owner that the nearest enclosing evaluation which owns its temporaries declares and closes, if present, when it finishes.

      Parameters:
      ctx - the evaluated expression, for debug information
      evaluation - its instructions, leaving the value in resultVariable
      resultVariable - the variable the evaluation leaves its value in
      resultType - that value's type
    • own

      public StatementOwnedCloseables.OwnedTemporaries own(List<IRInstr> instructions, DebugInfo debugInfo)
      The closeable temporaries these instructions make that phase 5 marked as owned: the owner declarations (emit them before the region that closes them), the same instructions with each temporary handed over to its owner right after the call that made it, and one ResourceCloser.Closing per owner, in the order the temporaries are made. apply(ParserRuleContext, List) closes them around a statement; try-with-resources closes the temporaries a resource's initialiser makes in its own region, beside the resources.