E81069: a dynamic function has no 'operator close', so it can never close a NEW closeable it captures - capture one the enclosing scope owns (a try resource), or capture it in a dynamic class that closes it

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

What went wrong

A dynamic function captures a NEW closeable value - a constructor call such as '(res: Res("x"))', or a call that returns a new closeable. A capture of a new value is a transfer: the dynamic construct becomes its only owner. A dynamic function has no 'operator close', so nothing could ever close what it owns, and the resource would leak.

A capture that is not new - a named variable '(res)', a try resource, a property - is a borrow: the scope that made the dynamic function still owns it and closes it, so it is not this error.

Worked examples

Compilable examples demonstrating this error and the correct fix:

  • ek9 -q 1880 (How do I release the resources my class holds - and what does 'default operator close' close for me?)
  • ek9 -q 1520 (When does EK9 close a closeable value, and where may I keep one?)

How to fix it

Acquire the resource where it is used and capture it by name, so the enclosing try owns it and the dynamic function only borrows it (good.ek9 in the [EXAMPLE] section below). Or capture the new value in a dynamic CLASS, which can own it - it declares its own generated close, which closes the capture, and is itself the try's resource:

try
  -> reader <- (res: Res("journal")) with trait of Readable as class
    override text()
      <- rtn as String: res.read()
    default operator close
    default operator ?
  rtn: reader.text()

Common causes

  • Creating the resource inline in the capture list for brevity.
  • Porting a Java lambda that closed over a stream it opened itself.

See also

  • E81069: A dynamic function may not capture a new closeable (this error)
  • E81067: A type that holds a closeable must declare its own operator close
  • E81056: Closeable held in a named local (must be a try-with-resources resource)
  • E81062: A closeable slot takes only a new value

Examples

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

Fails with E81069: a dynamic function has no 'operator close', so it can never close a NEW closeable it captures - capture one the enclosing scope owns (a try resource), or capture it in a dynamic class that closes it

#!ek9
defines module error.e81069.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}`)

  defines function

    Reader() as abstract
      <- text as String?

    makeReader()
      <- rtn as Boolean?
      reader <- (res: Res("journal")) is Reader as function
        text: res.read()
      rtn: reader?

Fixed: Acquire it in a try and capture it by name - a borrow; the try closes it

#!ek9
defines module error.e81069.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}`)

  defines function

    Reader() as abstract
      <- text as String?

    readJournal()
      <- rtn as String?
      try
        -> res <- Res("journal")
        reader <- (res) is Reader as function
          text: res.read()
        rtn: reader()

Fixed: Or capture the new value in a dynamic CLASS, whose generated close closes it

#!ek9
defines module error.e81069.good.class

  defines class
    Res
      name as String?
      default private Res()
      Res()
        -> label as String
        name: label
      read() as pure
        <- rtn as String: `read ${name}`
      operator close as pure
        stdout <- Stdout()
        stdout.println(`closed ${name}`)
      default operator ?

  defines trait
    Readable
      text() as abstract
        <- rtn as String?

  defines function
    readJournal()
      <- rtn as String?
      try
        -> reader <- (res: Res("journal")) with trait of Readable as class
          override text()
            <- rtn as String: res.read()
          default operator close
          default operator ?
        rtn: reader.text()

Questions and answers

These Q&A entries show E81069 and its fix: