E81062: a closeable value enters a property or a try resource only when it is NEW (a constructor call, or a call that returns a new closeable) - anything else already has an owner that closes it

Category: E81xxx-E83xxx Test configuration, execution and coverage · Reported in phase: PRE_IR_CHECKS · Classification: CLOSEABLE_SLOT_VALUE_NOT_FRESH · All error codes

What went wrong

A closeable value (its type has an 'operator close') enters a slot that will close it - a property of a closeable type, or a try-with-resources resource - but it is not a NEW value. Something else already owns it, and that owner closes it too: the resource would be closed twice, and used after it was closed.

A closeable has exactly ONE owner. A value is new when it is a constructor call (Res("x")) or a call whose declared return type is closeable and which does not hand back part of its receiver or an argument (TextFile(p).input(), a function that returns a Res it made). Anything else has an owner already:

  • a try resource, or a parameter (the caller owns it);
  • another object's property (rec.held, other.held) or your own (held);
  • a value given to a constructor that keeps it in a closeable property, when the value is a named try resource or any other name - Holder(res) with res a try resource gives res two owners, the try and the holder.

A constructor that stores its own parameter in a closeable property (held: res) takes ownership of it - a transfer - so every construction must hand that parameter a new value, and the parameter may go into only one property.

Who owns a closeable

Every closeable value has exactly one owner, decided by where it lives, and only the owner's generated code closes it:

  • a LOCAL is a try-with-resources resource: the try closes it when it ends.
  • a value that is never NAMED is owned by the statement that made it - a call result or a constructed value - and closed when that statement finishes.
  • a PROPERTY is owned by its object: the type's close (the generated one, or the chain after one you write) closes it.
  • a PARAMETER belongs to the caller, which lends it for the call.
  • a CAPTURE of a named value belongs to the scope that made the dynamic function or class; a NEW captured value is owned by the dynamic class.
  • the RETURNING variable is handed to the caller, who becomes the owner.
  • 'this' is owned by whoever made the object.

Nothing else may close it: there is no 'close' statement (E01094) and the close operator is never called as a method (E81060). That is why a slot that will close its value may only take a NEW one.

Worked examples

Compilable examples demonstrating this error and the correct fix:

  • ek9 -q 1840 (Can I give my object a resource I have already opened?)
  • ek9 -q 1520 (When does EK9 close a closeable value, and where may I keep one?)
  • ek9 -q 1740 (How do I let other code use a resource my object owns without letting it close or keep it?)
  • ek9 -q 1900 (How can my code use a closeable resource it does not own?)

How to fix it

Hand the slot a NEW value, made where it is stored: the try resource is 'Holder(Res("shared"))', not a Holder of a Res the try already names (the [EXAMPLE] section below).

Use a resource someone else owns through a PARAMETER instead of taking it: a function given 'res' uses it directly, never as a resource of a try of its own.

To let other code use a resource your object holds, lend it - pass it to a function or method as a parameter (the loan pattern) - never put it in a second try or a second object.

Common causes

  • 'try -> x <- param' or 'try -> x <- rec.held' to "make sure it is closed": its owner already closes it.
  • Constructing a wrapper around a named try resource: Holder(res).
  • A hand-written ':=:' or ':^:' that copies another object's closeable property into its own.
  • One constructor parameter stored into two properties.

See also

  • E81062: Closeable slot given a value that is not new (this error)
  • E81063: Closeable slot re-pointed (a property, returning variable or try resource)
  • E81065: 'default operator :=:' on a type that holds a closeable
  • E81056: Closeable held in a named local (must be a try resource)
  • E81057: Closeable return is not a new instance
  • E01094: 'close' is not a statement (closing is generated)

Examples

Every example is compiled by the EK9 compiler: the first fails exactly as captioned, the others fix it.

Fails with E81062: a closeable value enters a property or a try resource only when it is NEW (a constructor call, or a call that returns a new closeable) - anything else already has an owner that closes it

#!ek9
defines module error.e81062.bad

  defines class

    Res
      name as String?

      default private Res()

      Res()
        -> label as String
        name: label

      read() as pure
        <- rtn as String: `read ${name}`

      default operator ?

      operator close as pure
        stdout <- Stdout()
        stdout.println(`closed ${name}`)

    Holder
      held as Res?

      default private Holder()

      Holder()
        -> res as Res
        held: res

      default operator ?

      default operator close

  defines function

    share()
      <- rtn as Boolean?
      try
        ->
          res <- Res("shared")
          holder <- Holder(res)
        rtn: holder?

Fixed: Hand the slot a new value - the holder is its only owner

#!ek9
defines module error.e81062.good

  defines class

    Res
      name as String?

      default private Res()

      Res()
        -> label as String
        name: label

      read() as pure
        <- rtn as String: `read ${name}`

      default operator ?

      operator close as pure
        stdout <- Stdout()
        stdout.println(`closed ${name}`)

    Holder
      held as Res?

      default private Holder()

      Holder()
        -> res as Res
        held: res

      default operator ?

      default operator close

  defines function

    share()
      <- rtn as Boolean?
      try
        -> holder <- Holder(Res("shared"))
        rtn: holder?

Questions and answers

These Q&A entries show E81062 and its fix: