Class ResourceCloser
close: the one place IR for closing a resource is built, so every route that
closes something names the same owner, the same method, the same metadata and the same failure rule.
closeCall(ResourceCloser.Closing)- a singleCALL value._close(); every generated close is built on it.closingRegion(List, List, ResourceCloser.FirstFailure, DebugInfo)- THE owned region (shape B, Steve's D1, 2026-10-06, Contract 3): a body followed, on every path, by the close of each value that is present, last first. Try-with-resources (TryCatchStatementGenerator), the closeable temporaries a statement owns (StatementOwnedCloseables) and the generated chain of a type'soperator close(CloseChain) all build it - never a second sequence.failureRegion(List, List, ResourceCloser.FirstFailure, DebugInfo)- the same closes, run ONLY when the body fails with an EK9 Exception, which is then rethrown: a constructor that throws after acquiring part of its object closes that part (D10,CloseChain.constructionFailure(List, IAggregateSymbol, DebugInfo)).
The failure rule (shape B). Each _close() runs in its own protected region (the JLS 14.20.3.1
nested-try shape), so one close that throws never stops the others. The FIRST EK9 Exception is the one
that propagates once every close has run: the body's when the body failed (it is caught into the region's
first-failure variable, never replaced), otherwise the first close's. Every later close failure is attached to
it as SUPPRESSED (Ek9CloseFailures.suppress). A Panic is not an EK9 Exception: no
region here catches it, so a Panic in a close aborts the closes that remain, and a Panic in the body still runs
them (they are a finally) and then propagates unchanged - the first failure is only rethrown AFTER the
region, on a path a pending Panic never reaches, so a close failure cannot replace a Panic.
The null guard is what makes a partial acquisition safe: a value that was never produced (an earlier call threw) is still null and is skipped (Contract 3).
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic final recordA value to close.static final recordThe region's first-failure variable, declared (null) BEFORE the region bydeclareFirstFailure(ResourceCloser.FirstFailure, DebugInfo)- it must be definitely assigned where the region's handlers start. -
Field Summary
Fields inherited from class AbstractGenerator
constantCopyOnAccessLoader, debugInfoCreator, instructionBuilder, MAX_MESSAGE_LENGTH, stackContext, synthesisedCallMetaData, typeNameOrException -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptioncloseCall(ResourceCloser.Closing closing) CALL variable._close()on the type that declaresclose.closeEach(List<ResourceCloser.Closing> closings, ResourceCloser.FirstFailure firstFailure, DebugInfo debugInfo) closingRegion(List, List, ResourceCloser.FirstFailure, DebugInfo)with no body: every value that is present closed, last first, each in its own protected region, then the first close failure rethrown with the later ones suppressed onto it.closingRegion(List<IRInstr> body, List<ResourceCloser.Closing> closings, ResourceCloser.FirstFailure firstFailure, DebugInfo debugInfo) THE owned region:body, then - on the normal path, after an EK9 Exception, and while a Panic unwinds - every value inclosingsthat is present is closed, last first, each in its own protected region; then the first EK9 Exception (the body's, else the first close's) is rethrown with the later close failures suppressed onto it.declareFirstFailure(ResourceCloser.FirstFailure firstFailure, DebugInfo debugInfo) REFERENCE firstFailure, org.ek9.lang::Exception- null until a failure is recorded.failureRegion(List<IRInstr> body, List<ResourceCloser.Closing> closings, ResourceCloser.FirstFailure firstFailure, DebugInfo debugInfo) The closes ofclosingRegion(List, List, ResourceCloser.FirstFailure, DebugInfo), run only whenbodyfails with an EK9 Exception - which is then rethrown with any close failure suppressed onto it.A new first-failure variable for one region.
-
Constructor Details
-
ResourceCloser
ResourceCloser(IRGenerationContext stackContext)
-
-
Method Details
-
newFirstFailure
A new first-failure variable for one region. Declare it (declareFirstFailure(ResourceCloser.FirstFailure, DebugInfo)) before the region - for try-with-resources before the resource scope, beside the resources' own declarations. -
declareFirstFailure
REFERENCE firstFailure, org.ek9.lang::Exception- null until a failure is recorded. -
closeCall
CALL variable._close()on the type that declaresclose. -
closingRegion
public List<IRInstr> closingRegion(List<IRInstr> body, List<ResourceCloser.Closing> closings, ResourceCloser.FirstFailure firstFailure, DebugInfo debugInfo) THE owned region:body, then - on the normal path, after an EK9 Exception, and while a Panic unwinds - every value inclosingsthat is present is closed, last first, each in its own protected region; then the first EK9 Exception (the body's, else the first close's) is rethrown with the later close failures suppressed onto it.SCOPE_ENTER chain_scope CONTROL_FLOW_CHAIN TRY_CATCH_FINALLY try: SCOPE_ENTER try_scope, body..., SCOPE_EXIT try_scope catch: -> _t as org.ek9.lang::Exception (never a Panic) STORE firstFailure, _t finally: per closing, last first: load..., IS_NULL / BRANCH_TRUE skip TRY_CATCH_FINALLY try: CALL value._close() catch -> _ex: record _ex (see below) LABEL skip SCOPE_EXIT chain_scope IS_NULL firstFailure / BRANCH_TRUE done / THROW firstFailure / LABEL doneRecording a close failure: the first one is stored intofirstFailure; a later one isCALL_STATIC Ek9CloseFailures.suppress(firstFailure, _ex). The rethrow sits AFTER the region: a pending Panic leaves through the finally's own rethrow and never reaches it.The caller declares
firstFailurebeforehand (declareFirstFailure(ResourceCloser.FirstFailure, DebugInfo)); the region registers it to the scope that is current when this is called.bodymust not contain the try scope's own enter/exit - the region adds them. -
closeEach
public List<IRInstr> closeEach(List<ResourceCloser.Closing> closings, ResourceCloser.FirstFailure firstFailure, DebugInfo debugInfo) closingRegion(List, List, ResourceCloser.FirstFailure, DebugInfo)with no body: every value that is present closed, last first, each in its own protected region, then the first close failure rethrown with the later ones suppressed onto it. With nothing to protect there is no outer region (adefault operator close). -
failureRegion
public List<IRInstr> failureRegion(List<IRInstr> body, List<ResourceCloser.Closing> closings, ResourceCloser.FirstFailure firstFailure, DebugInfo debugInfo) The closes ofclosingRegion(List, List, ResourceCloser.FirstFailure, DebugInfo), run only whenbodyfails with an EK9 Exception - which is then rethrown with any close failure suppressed onto it. A Panic is not caught: it leaves with nothing closed.SCOPE_ENTER chain_scope CONTROL_FLOW_CHAIN TRY_CATCH_FINALLY try: SCOPE_ENTER try_scope, body..., SCOPE_EXIT try_scope catch: -> _t as org.ek9.lang::Exception STORE firstFailure, _t SCOPE_EXIT chain_scope IS_NULL firstFailure / BRANCH_TRUE done per closing, last first: load..., IS_NULL / BRANCH_TRUE skip, protected CALL value._close(), LABEL skip THROW firstFailure LABEL doneThe caller declaresfirstFailurebeforehand (declareFirstFailure(ResourceCloser.FirstFailure, DebugInfo)).
-