E81059: a closeable value may only be stored where the declared type is closeable
What went wrong
A closeable value (its type has an 'operator close') is stored into a variable, a property or the returning variable whose DECLARED type has no 'close' - a trait such as IO or StringInput, or Any. Closeability cannot be erased by a store.
Nothing holding the value through that name can ever close it. The statement that created it sees it stored, so it does not close it either, and E81056 does not apply because the declared type is not closeable. It would leak.
Worked examples
Compilable examples demonstrating this error and the correct fix:
- ek9 -q 1640 (Can I keep a closeable in a variable of a trait or Any type?)
- ek9 -q 1520 (When does EK9 close a closeable value, and where may I keep one?)
How to fix it
Keep the closeable type, in a try-with-resources resource (good.ek9 in the [EXAMPLE] section below). Or, when the code only needs the narrower type, do not store it - pass it as an argument. A parameter of a non-closeable type borrows a view, and the statement (or the try) that owns the value still closes it (good_not_a_store.ek9 below shows that, and each other form that is not a store).
What is not a store
- An ARGUMENT: passing a CloseableStringInput to a StringInput parameter lends a view; the owner still closes it.
- A slot declared as the Closeable trait, or as the closeable type itself.
- A standard stream (Stdin, Stdout, Stderr): borrowed, never closed.
Common causes
- Declaring a variable, property or returning variable with the trait the code reads through (StringInput, IO, a trait of your own) instead of the closeable type.
- Aliasing a try resource into a variable declared outside the try.
See also
Examples
Every example is compiled by the EK9 compiler: the first fails exactly as captioned, the others fix it.
Fails with E81059: a closeable value may only be stored where the declared type is closeable
#!ek9 defines module error.e81059.bad defines trait Labelled label() as pure <- rtn as String? defines class LabelledResource with trait of Labelled text as String? default private LabelledResource() LabelledResource() -> initialText as String text: initialText override label() as pure <- rtn as String: text default operator ? operator close as pure require true <?- BAD: a property declared as the non-closeable trait, initialised with a closeable -?> TraitHolder held as Labelled: LabelledResource("held") default operator ? <?- BAD: a property re-pointed at a closeable, by name and through 'this' -?> LateHolder held as Labelled? LateHolder() held: LabelledResource("late") replaceHeld() this.held := LabelledResource("replaced") default operator ? defines function <?- BAD: a local declared as the non-closeable trait -?> storedAsTrait() labelled as Labelled: LabelledResource("trait") require labelled? <?- BAD: a local declared as Any -?> storedAsAny() anything as Any: LabelledResource("any") require anything? <?- BAD: a built-in closeable input stored as the non-closeable StringInput -?> storedAsStringInput() input as StringInput: TextFile("lines.txt").input() require input? <?- BAD: the returning variable declared as the non-closeable trait - the caller could never close it -?> returnedAsTrait() <- rtn as Labelled: LabelledResource("returned") <?- BAD: a declared, unset local later assigned a closeable -?> assignedLater() labelled as Labelled? labelled := LabelledResource("later") require labelled? <?- BAD: an assign-if-unset is a store too -?> assignedIfUnset() labelled as Labelled? labelled :=? LabelledResource("guarded") require labelled? <?- BAD: a try resource aliased into a non-closeable local outlives its try -?> aliasOfTryResource() outer as Labelled? try -> resource <- LabelledResource("aliased") outer := resource require outer?
Fixed: Keep the closeable type - in a try
#!ek9 defines module error.e81059.good defines function firstLine() -> path as FileSystemPath <- rtn as String: String() try -> input <- TextFile(path).input() if input.hasNext() rtn: input.next()
Fixed: What is not a store: a lent argument, a Closeable-typed try resource or return, an owning property
#!ek9 defines module error.e81059.good.not.a.store defines trait Described description() as pure <- rtn as String? defines class DescribedResource with trait of Described, Closeable text as String? default private DescribedResource() DescribedResource() -> initialText as String text: initialText override description() as pure <- rtn as String: text override operator close as pure require true default operator ? PlainDescription with trait of Described text as String? default private PlainDescription() PlainDescription() -> initialText as String text: initialText override description() as pure <- rtn as String: text default operator ? <?- OK: a property declared as the closeable type is owned by its object, and closed by it -?> ResourceOwner owned as DescribedResource? ResourceOwner() owned: DescribedResource("owned") default operator close default operator ? defines function describe() as pure -> described as Described <- rtn as String: described.description() <?- OK: an unnamed closeable passed as an argument - lent, the statement still closes it -?> unnamedArgument() require describe(DescribedResource("argument"))? <?- OK: a try resource passed as an argument - lent, the try still closes it -?> tryResourceArgument() try -> resource <- DescribedResource("try") require describe(resource)? <?- OK: a try resource declared as the Closeable trait keeps its close -?> tryResourceAsCloseable() try -> resource as Closeable: DescribedResource("closeable") require resource? <?- OK: returning as the closeable type hands ownership to the caller -?> returnedAsResource() <- rtn as DescribedResource: DescribedResource("returned") <?- OK: returning as the Closeable trait keeps the close operator -?> returnedAsCloseable() <- rtn as Closeable: DescribedResource("closeable") <?- OK: a non-closeable value stored as the trait -?> plainAsTrait() described as Described: PlainDescription("plain") require described? <?- OK: a standard stream is borrowed, not owned, so it may be held as StringInput -?> standardInputAsStringInput() input as StringInput: Stdin() require input?
Questions and answers
These Q&A entries show E81059 and its fix: