E81074: an Exception is a value carried to a handler, not a resource - it has no 'operator close', declared, defaulted or inherited; put the resource in a type of its own and close it there

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

What went wrong

An Exception, or a sub-type of one, has an 'operator close' - one it writes, one it asks for with 'default operator close' (it holds a closeable property), or one it must implement because it takes a trait that carries a close. An exception is a value carried to a handler, not a resource, so it may not be closeable.

A thrown exception has no try that owns it, and a 'catch' is not an owner: the exception is made, thrown, caught and dropped, and its close was never called. Making every handler an owner would close a value that a handler may keep, log or rethrow. So the rule is the simpler one - an exception carries values (a reason, a code, the name of what failed) and never holds a resource. A resource that the failure concerns belongs to a type of its own, which closes it in its 'operator close'.

The error is reported once down a hierarchy, on the first type that is closeable; a sub-type of that exception is not reported again.

Worked examples

Compilable examples demonstrating this error and the correct fix:

  • ek9 -q 1520 (When does EK9 close a closeable value, and where may I keep one?)

How to fix it

Remove the 'operator close' from the exception. Where it closed a resource, hold the resource in a class of your own that owns it (closed by its own 'operator close'), acquire it in a 'try' where it is used, and let the exception carry only a String or a number describing what happened (good.ek9 in the [EXAMPLE] section below).

Common causes

  • Giving an exception a handle - a connection, a file - so the handler can inspect it, and then closing it in the exception.
  • Writing 'default operator close' on an exception with a closeable property.
  • Taking a 'with trait of' a trait that has an 'operator close' on an exception.

See also

  • E81074: An Exception has no operator close (this error)
  • E81067: A type that holds a closeable declares its own operator close
  • E81070: 'default operator close' with nothing to close
  • E81056: Closeable held in a named local (must be a try-with-resources resource)

Examples

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

Fails with E81074: an Exception is a value carried to a handler, not a resource - it has no 'operator close', declared, defaulted or inherited; put the resource in a type of its own and close it there

#!ek9
defines module error.e81074.bad

  defines class

    Failure extends Exception
      Failure()
        -> reason as String
        super(reason)

      default operator ?

      operator close as pure
        stdout <- Stdout()
        stdout.println("failure closed")

  defines function

    failedWork()
      <- rtn as String: "none"
      try
        throw Failure("boom")
      catch
        -> ex as Exception
        rtn: `caught ${ex}`

Fixed: Remove the operator close - an exception is a value, not a resource

#!ek9
defines module error.e81074.good.fix

  defines class

    Failure extends Exception
      Failure()
        -> reason as String
        super(reason)

      default operator ?

  defines function

    failedWork()
      <- rtn as String: "none"
      try
        throw Failure("boom")
      catch
        -> ex as Exception
        rtn: `caught ${ex}`

Fixed: Hold the resource in a type of your own that closes it, and let the exception carry plain text

#!ek9
defines module error.e81074.good

  defines class

    Failure extends Exception
      Failure()
        -> reason as String
        super(reason)

      default operator ?

    Connection
      name as String?

      default private Connection()

      Connection()
        -> label as String
        name: label

      send() as pure
        -> text as String
        <- rtn as String: `${name} sent ${text}`

      default operator ?

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

  defines function

    failedWork()
      <- rtn as String: "none"
      try
        throw Failure("boom")
      catch
        -> ex as Exception
        rtn: `caught ${ex}`

    describeConnection()
      <- rtn as String: String()
      try
        -> connection <- Connection("link")
        rtn: connection.send("hello")