E01094: 'close' is not a statement in EK9 - closing is generated: declare 'default operator close' (or write 'operator close' for extra work); a local closes through try or its statement

Category: E01xxx Parsing and excluded constructs · Reported in phase: SYMBOL_DEFINITION · Classification: CLOSE_STATEMENT_NOT_SUPPORTED · All error codes

What went wrong

'close' is not a statement in EK9. Every close is generated by the compiler, so a close written by hand could only close a value a second time, or close it while its owner is still using it:

  • a try-with-resources resource is closed when its try ends;
  • a closeable value a statement makes but never names is closed when that statement finishes;
  • a type's own closeable properties are closed by its close operator (the generated one, or one you write for extra work), which closes them after its body, last acquired first, then calls its super class's close.

The grammar keeps 'close x' only to give this error, as it keeps 'break' and 'return'.

Worked examples

Compilable examples demonstrating this error and the correct fix:

  • ek9 -q 1600 (Who closes a closeable value, and can I close one myself?)
  • ek9 -q 1880 (How do I release the resources my class holds - and what does 'default operator close' close for me?)
  • ek9 -q 802 (Why does EK9 reject manual close of local resources?)
  • ek9 -q 1902 (What happens when a close throws an exception - are the other resources still closed?)

Rationale

A manual close() call is skipped whenever an exception is thrown before it is reached, and the opposite mistake is just as common: a value closed by code that does not own it - a double close, or a use after close. C++ and Swift never close a member by hand: the destructor runs, then the members and the base are released automatically. EK9 applies that rule to every close.

How to fix it

Who owns the value decides what replaces the 'close'.

A local: declare it as a try-with-resources resource ('try' with '-> conn <- ...'), and the try closes it when it ends (good_local.ek9 in the [EXAMPLE] section below). Or, when one statement is all you need, do not name it at all - 'use(Connection(a))'

  • and the statement closes it.

Your own property: delete the 'close'. A close that only closed properties becomes 'default operator close'; keep a written 'operator close' only for extra work, which runs first with every property still open. The [EXAMPLE] section below shows 'close conn' in a Holder's close, and the Holder with 'default operator close' instead (good.ek9).

A parameter, a capture, the returning variable or a call result: delete the 'close' - its owner (the caller's try, the scope that made the dynamic construct, the caller, the statement) closes it.

To stop work early (a loop, a server), end it with a condition or call the server's stop() - never close the value under its owner.

Common causes

  • Java, C# or Python habit: closing at the end of a block.
  • Code written before closing was generated, closing each property in its type's own close.
  • A 'release', 'disconnect' or 'reset' method that closes a held resource. Make a new owning object (or acquire afresh in a new try) instead.
  • A handler closing a captured server or connection to stop a loop.

See also

  • E01094: 'close' is not a statement (this error)
  • E81060: Close operator called as a method (never, 'super.close()' included)
  • E81056: Closeable held in a named local (must be a try-with-resources resource)
  • E81062: Closeable slot given a value that is not new (who owns what)
  • E81067: A type that holds a closeable must declare its own operator close
  • E01070: 'break' not supported
  • E01072: 'return' not supported

https://ek9.io/flowControl.html#try_catch https://ek9.io/forAI.json#$.excludedFeatures.features[?(@.keyword=="close")]

Examples

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

Fails with E01094: 'close' is not a statement in EK9 - closing is generated: declare 'default operator close' (or write 'operator close' for extra work); a local closes through try or its statement

#!ek9
defines module error.e01094.bad

  defines class

    Connection
      address as String?

      default private Connection()

      Connection()
        -> address as String
        this.address: address

      default operator ?

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

    Holder
      conn as Connection?

      default private Holder()

      Holder()
        -> address as String
        conn: Connection(address)

      default operator ?

      operator close as pure
        close conn

Fixed: Let the generated close do it: default operator close closes conn

#!ek9
defines module error.e01094.good

  defines class

    Connection
      address as String?

      default private Connection()

      Connection()
        -> address as String
        this.address: address

      default operator ?

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

    Holder
      conn as Connection?

      default private Holder()

      Holder()
        -> address as String
        conn: Connection(address)

      default operator ?

      default operator close

Fixed: A local closes through its try: declare it as the try's resource

#!ek9
defines module error.e01094.good.local

  defines function

    readFirst()
      -> path as FileSystemPath
      stdout <- Stdout()
      try
        -> input <- TextFile(path).input()
        stdout.println(input.next())

Questions and answers

These Q&A entries show E01094 and its fix: