Class ObjectUsedBeforeConstructedOrError


final class ObjectUsedBeforeConstructedOrError extends TypedSymbolAccess
An object cannot be used before it is fully constructed (E08061).

Construction runs the super constructor, then this type's property initialisers in declaration order, then the constructor body. Until every property has a value, calling a method on the object - or handing out 'this' - can reach a property that is still unset, and it failed with a NullPointerException at run time (front-end audit FE32). This is the two-phase rule: the object may not be used until it is complete.

  • A property initialiser runs before the constructor body, so the object is never complete there: no method call on it (even an inherited one - an inherited method can call back into one this type overrides) and no 'this' at all - not as a value, not in a capture list, and not to reach a property (this.first + 1 is written first + 1; Steve, 2026-10-08, F4 rule 1).
  • A constructor may use the object once every property is set on the current path (the flow tracking E08060 already does) - counting what a 'this(...)' call set, which is only what the constructor it delegates to sets (DelegatedConstructorInitialisation): a private one may set nothing. Never inside the arguments of 'this(...)' or 'super(...)': nothing has been constructed yet.
  • In an open or abstract type, a constructor can never call a method a subtype may override, nor hand out 'this': the subtype's own properties are only set after this constructor returns.

The last rule holds at the other end of the object's life too (E81068): in an open or abstract type, the 'operator close' body can never call a method a subtype may override, nor hand out 'this' - a subtype's own properties are closed before its super's close runs (C++'s destructor order without its devirtualisation; Core Guidelines C.82). Same predicate, same walk; only the enclosing operation differs.

Inside the arguments of 'this(...)' or 'super(...)' even reading one of the object's own properties is a use (acceptPropertyRead(EK9Parser.IdentifierContext), decision 3 of the absence work, family F): nothing has been constructed yet - the super constructor has not run, nor this type's property initialisers, which run after it (ConstructionSteps). So a property with an initialiser has no value there either; the JVM verifier rejected the read as a 'getfield' on 'uninitializedThis' at class load, and on a back end with no verifier it is a null read. Java's rule (JLS 8.8.7.1) is the same.

Anywhere else, reading and writing a property (this.count) is not a use of the object: E08060 checks each property. Nor is storing the object in one of its own properties (me: this, SelfReferenceStore): that completes the property like any other assignment. The property is then the object under another name, so any other appearance of it in that constructor (me.twice(), other <- me) is a use of the object.

THE PARENT LINK (Steve, 2026-10-08, F4 - strict, lenient only for capture-construction). A statement giving one of the type's OWN properties a dynamic function or class that captures 'this' (child :=? (owner: this) with trait of Child as class ..., also := or :) is the same kind of store (SelfReferenceStore.capturedInto(EK9Parser.AssignmentStatementContext, SymbolsAndScopes)), so it is not a use of the object - provided every OTHER property is set on that path or is itself the target of a later capture-assignment (checkCaptureAssignment(EK9Parser.PrimaryReferenceContext, EK9Parser.AssignmentStatementContext)). This is how a parent hands each child a reference back to itself: the object becomes complete when its last such property is set. Until then:

  • the property holding the capture is the object under another name, so calling it, passing it or capturing it again is E08061 (the alias rule above, through SelfReferenceStore.propertiesStoredIn(EK9Parser.MethodDeclarationContext, SymbolsAndScopes));
  • a dynamic class created that way runs its own property initialisers during the capturing statement, while the object is incomplete, so using the captured 'this' there is E08061 (checkIncompleteOwnerUse(ParserRuleContext, ISymbol)); its methods, and a dynamic function's body, run only when called, so they fall under the alias rule.

Unchanged: never in the arguments of this(...) or super(...), and never in an open or abstract type, whose subtype's properties are only set after this constructor returns. No new analysis: the path state is E08060's, the delegated-constructor state is D9's (DelegatedConstructorInitialisation), and the stores are SelfReferenceStore's.

  • Constructor Details

  • Method Details

    • acceptCall

      void acceptCall(EK9Parser.CallContext ctx)
      A method called with no receiver - on the object itself - such as twice().
    • acceptPrimaryReference

      void acceptPrimaryReference(EK9Parser.PrimaryReferenceContext ctx)
      A use of 'this' or 'super': a call through it, or 'this' as a value. Reaching a property is not a use.
    • acceptPropertyRead

      boolean acceptPropertyRead(EK9Parser.IdentifierContext ctx)
      A read of one of the object's own properties - count, this.count, count.length() - inside the arguments of 'this(...)' or 'super(...)', however deeply nested in an expression or another call. A property of another object (other.count), a nested dynamic function's or class's capture label, and an argument label are not reads of this object's properties.
      Returns:
      true if the read was reported, so the per-property check (E08060) need not also report it
    • acceptIdentifierReference

      void acceptIdentifierReference(EK9Parser.IdentifierReferenceContext ctx)
      A property that refers back to the object, used as a value (other <- me).
    • acceptObjectAccessStart

      void acceptObjectAccessStart(EK9Parser.ObjectAccessStartContext ctx)
      A property that refers back to the object, used to reach it (me.twice()).