E08061: the object is used before it is fully constructed - set every property before calling its methods

Category: E08xxx Pre-IR checks (flow analysis) · Reported in phase: PRE_IR_CHECKS · Classification: OBJECT_USED_BEFORE_FULLY_CONSTRUCTED · All error codes

What went wrong

The object is used before it is fully constructed.

EK9 builds an object in TWO PHASES. Phase one gives every property a value: the super constructor runs, then this type's property initialisers in declaration order, then the constructor body. Phase two begins only when EVERY property has a value - the object is COMPLETE - and only then may anything use it: call its methods, use 'this' as a value, hand it out, or let something else hold it and call back into it. Using it earlier can read a property that has no value yet. In Java that compiles and fails with a NullPointerException (or sees a 0 that is not the real value); EK9 rejects it at compile time.

When is the object complete?

  • In a property initialiser: NEVER. Initialisers run before the constructor body, so an initialiser may not use 'this' at all - not as a value, not in a capture list ('(owner: this)'), and not even to reach a property ('this.width' - write 'width').
  • In a constructor: once every property is set on the current path - counting what a 'this(...)' call set, which is only what the constructor it delegates to sets (a private one may set nothing, see E08264).
  • In the arguments of 'super(...)' or 'this(...)': never - nothing has been constructed yet, not the super and not this type's properties (their initialisers run AFTER 'super(...)'), so even reading one of the object's own properties there is a use. Java has the same rule (JLS 8.8.7.1).
  • In an open or abstract type's constructor: never for anything a subtype could see - a subtype's own properties are set only after this constructor returns, so it may not call a method a subtype may override, nor hand out 'this'.

Two STORES are allowed before the object is complete, because nothing is read through them:

  • Storing the object in one of its OWN properties: 'next: this', 'this.owner :=? this'.
  • THE PARENT LINK: 'child :=? (owner: this) with trait of Child as class ...' gives one of its OWN properties a dynamic class or function that captures 'this' (any assignment form will do), provided every OTHER property is already set on that path, or is itself the target of a LATER such capture-assignment. This solves the catch-22 of a child that needs its parent while the parent needs the child: the object becomes complete when the last of them is set.

Until the object is complete, a property holding either store IS the object under another name: calling it, passing it (even to a pure function) or capturing it again is a use. And a dynamic class created by the parent link runs its own property initialisers DURING the capturing statement, so the captured owner may be used only in its methods, never in its initialisers. The parent link is not available in an open or abstract type.

Worked examples

Compilable examples demonstrating this error and the correct fix:

  • ek9 -q 1396 (Why can't my constructor call a method before every property is set?)
  • ek9 -q 1471 (How can an object hold a reference to itself, like a doubly linked node that starts as a ring of one?)
  • ek9 -q 2180 (Why can't a property initialiser use 'this', not even 'this.width'?)
  • ek9 -q 2181 (How does a child object hold a reference back to its parent when the parent creates it in its constructor?)
  • ek9 -q 2182 (How does a constructor give two children...)
  • ek9 -q 2183 (What mistakes does EK9 reject when a constructor hands 'this' to a child object or callback?)

How to fix it

Each mistake has one fix:

  1. A constructor calls a method before a property it reads is set: set every property first, then call the object's methods.
  2. A property initialiser calls a method of the object, or uses 'this' ('this.width', '$this', '(owner: this)'): read an earlier initialised property by its plain name ('width'), compute the value with a function, or take it from a constructor argument - and build anything that needs the object itself in the constructor.
  3. An open or abstract type's constructor calls an overridable method or hands out 'this': make the helper 'private' (it cannot be overridden) or compute the value with a function; keep a type with a parent link closed, or create the child in a method after construction.
  4. 'super(...)' or 'this(...)' arguments use the object: pass a constructor parameter, a literal or a function's result.
  5. A child needs a reference back to its parent: declare the child property unset ('child as Child?'), set every other property, then give the child its parent link as above - with several children, assign each the same way, one after another (good_parent_link.ek9 in the [EXAMPLE] section below).
  6. 'this', captured into a property, is captured before another property is set: set that other property first (or make it a capture-assignment too, after this one).
  7. A child, callback or self reference is called, passed or captured before the object is complete: move that use after the last property (the last capture-assignment) is set.
  8. A dynamic class created by the parent link uses its captured owner in a property initialiser: give the initialiser a value that does not need the owner, and use the owner in a method.

Common causes

  • A constructor calling a helper method before assigning a property that helper reads
  • A property initialiser calling a method of the object, even an inherited one
  • 'this.width' in a property initialiser where 'width' was meant
  • A base-type constructor calling a method a subtype overrides
  • 'super(helper())' or 'this(helper())' - no object exists yet when the arguments are evaluated
  • 'super(count)' where 'count as Integer: 5' - the initialiser has not run yet either
  • A call after 'this()' to a 'default private' constructor that sets nothing
  • Calling a method through a property set to 'this' ('me.describe()') before the rest are set
  • Creating the child ('child :=? (owner: this) ...') before the parent's other properties
  • Calling the first child, or a callback registered in the constructor, before the last child is created - its methods may call back into the parent and read the property still missing
  • 'label as String: owner.name()' inside a dynamic class created with '(owner: this)'
  • A parent link in an 'open' or 'abstract' type

See also

  • E08061: object used before it is fully constructed (this error)
  • E08060: property is/may not be initialized before use
  • E08010: used before definition (a local initialiser reading itself)
  • E08264: a construction call reaches a private constructor that leaves properties unset
  • E08250: 'this' cannot be passed as an argument to, or stored into, another object inside a constructor body

Examples

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

Fails with E08061: the object is used before it is fully constructed - set every property before calling its methods

#!ek9
defines module error.e08061.bad

  defines trait

    Part
      describe() as pure
        <- rtn as String?

  defines class

    Gauge
      reading as Integer?
      doubled as Integer?

      Gauge()
        doubled: twice()
        reading: 21

      twice()
        <- rtn as Integer: reading * 2

      default operator ?

    Document
      title as String?
      section as Part?

      default private Document()

      Document()
        -> text as String
        section :=? (owner: this) with trait of Part as class
          override describe() as pure
            <- rtn as String: `a section of ${owner.name()}`
          default operator ?
        title: text

      name() as pure
        <- rtn as String: title

      default operator ?

Fixed: Set every property before the code that uses this

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

  defines trait

    Part
      describe() as pure
        <- rtn as String?

  defines class

    Gauge
      reading as Integer?
      doubled as Integer: 0

      Gauge()
        reading: 21
        doubled: twice()

      twice()
        <- rtn as Integer: reading * 2

      default operator ?

    Document
      title as String?
      section as Part?

      default private Document()

      Document()
        -> text as String
        title: text
        section :=? (owner: this) with trait of Part as class
          override describe() as pure
            <- rtn as String: `a section of ${owner.name()}`
          default operator ?

      name() as pure
        <- rtn as String: title

      default operator ?

Fixed: Set every property before calling a method on the object

#!ek9
defines module error.e08061.good

  defines class

    Gauge
      reading as Integer?
      doubled as Integer: 0

      Gauge()
        reading: 21
        doubled: twice()

      twice()
        <- rtn as Integer: reading * 2

      default operator ?

Fixed: The parent link: capture this only once every other property is set

#!ek9
defines module error.e08061.good.parent.link

  defines class

    Document
      title as String?
      section as Part?

      default private Document()

      Document()
        -> text as String
        title: text
        section :=? (owner: this) with trait of Part as class
          override describe() as pure
            <- rtn as String: `a section of ${owner.name()}`
          default operator ?

      name() as pure
        <- rtn as String: title

      default operator ?

  defines trait

    Part
      describe() as pure
        <- rtn as String?

Questions and answers

These Q&A entries show E08061 and its fix: