E81064: closeable properties are closed in reverse declaration order, so a constructor must acquire them in declaration order

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

What went wrong

A constructor sets a closeable property while a closeable property declared AFTER it already holds a resource: the constructor acquires them in a different order from the one they are declared in.

An object's closeable properties are released in REVERSE declaration order, which is reverse acquisition order - last made, first closed - only when they are acquired in declaration order. That matters as soon as one resource depends on another: a buffered writer over a file must be closed (and flushed) BEFORE the file. Acquired out of order, the file would be closed first and the writer's last flush would be lost.

Property initialisers already run in declaration order, before the constructor body, so a property with an initialiser counts as acquired before anything the body sets.

Worked examples

Compilable examples demonstrating this error and the correct fix:

  • ek9 -q 1842 (In what order are the resources my object holds closed?)

How to fix it

Declare the properties in the order the constructor acquires them - the foundation first, what depends on it after - or acquire them in the constructor in the order they are declared. The [EXAMPLE] section below shows a constructor setting 'dependent' before 'foundation' when they are declared the other way round, then the two in the same order.

Common causes

  • Adding a new resource property at the top of the type while the constructor acquires it last.
  • Mixing an initialised closeable property, declared after another, with a constructor that sets the earlier one.

See also

  • E81064: Closeable properties acquired out of declaration order (this error)
  • E81063: Closeable slot re-pointed (a property, returning variable or try resource)
  • E81062: Closeable slot given a value that is not new

Examples

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

Fails with E81064: closeable properties are closed in reverse declaration order, so a constructor must acquire them in declaration order

#!ek9
defines module error.e81064.bad

  defines class

    Res
      name as String?

      default private Res()

      Res()
        -> label as String
        name: label

      default operator ?

      operator close as pure
        require name?

    <?- BAD: 'dependent' is declared first but acquired second -?>
    BodyOrder
      dependent as Res?
      foundation as Res?

      BodyOrder()
        foundation: Res("foundation")
        dependent: Res("dependent")

      default operator ?

      default operator close

    <?- BAD: 'late' is initialised before the constructor body sets 'early', which is declared before it -?>
    InitialiserFirst
      early as Res?
      late as Res: Res("late")

      InitialiserFirst()
        early: Res("early")

      default operator ?

      default operator close

    <?- BAD: out of order on one path is enough -?>
    OnOnePath
      first as Res?
      second as Res?

      default private OnOnePath()

      OnOnePath()
        -> secondFirst as Boolean
        if secondFirst
          second: Res("second")
          first: Res("first")
        else
          first: Res("first")
          second: Res("second")

      default operator ?

      default operator close

Fixed: Declare the closeable properties in the order they are acquired

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

    BodyOrder
      foundation as Res?
      dependent as Res?

      BodyOrder()
        foundation: Res("foundation")
        dependent: Res("dependent")

      default operator ?

      default operator close

Questions and answers

These Q&A entries show E81064 and its fix: