Class ConstructorThisEscapeOrError

All Implemented Interfaces:
BiConsumer<EK9Parser.ParamExpressionContext, ISymbol>

final class ConstructorThisEscapeOrError extends TypedSymbolAccess implements BiConsumer<EK9Parser.ParamExpressionContext, ISymbol>
Inside a constructor body, 'this' must not be passed as an argument to any NON-PURE call.

Eliminates publication-during-construction races: if 'this' escapes to a thread-spawning receiver (or any receiver that retains the reference) before the constructor has finished initialising fields, another thread can observe partially-initialised state.

Passing 'this' to a PURE receiver is permitted — pure functions have no observable side effects (no global writes, no thread spawning, no retained references), so they cannot leak the reference. This allows the Consumer-based delegated-initialisation pattern (e.g., a pure constructor accepting a Consumer of T as pure and calling initializer(this)).

A local that refers to 'this' (other <- this, or other := this) is 'this' under another name, so passing it is the same escape: recordReference(ReferenceAssignment) marks such a local (REFERS_TO_THIS, followed through further locals) and the argument check treats it as 'this'.

A property the constructor stores 'this' into (me: this, SelfReferenceStore) is 'this' too, in that constructor: passing me is the same escape as passing 'this'.

A dynamic function or dynamic class that captures 'this' (or such a local) holds the half-built object, so it is 'this' too: passed directly (reg.watch((me: this) is Lazy ...)), through a local it is declared into (recordReference(ReferenceAssignment) for a dynamic function, recordDynamicClass(ISymbol, EK9Parser.AssignmentExpressionContext) for a dynamic class), or captured again by another dynamic function.

Any of these STORED into another object (checkStore(EK9Parser.AssignmentStatementContext)) is the same escape without a call: the other object holds the half-built one and can call back into it. Storing into this object's own property is not.

Part of EK9's compile-time data-race detection (slice 1, model rule §3.5). Error E08250 THIS_ESCAPES_CONSTRUCTOR.

  • Constructor Details

  • Method Details

    • accept

      public void accept(EK9Parser.ParamExpressionContext paramExpression, ISymbol resolvedTarget)
      Specified by:
      accept in interface BiConsumer<EK9Parser.ParamExpressionContext, ISymbol>
    • recordReference

      void recordReference(ReferenceAssignment reference)
      Inside a constructor, mark a local that a declaration or assignment makes refer to 'this'.
    • recordDynamicClass

      void recordDynamicClass(ISymbol target, EK9Parser.AssignmentExpressionContext assignment)
      Inside a constructor, mark a local that is given a dynamic class capturing 'this'.
    • checkStore

      Inside a constructor, 'this' (or an alias, or a dynamic function/class capturing either) must not be STORED into another object: a property of another instance (p.parent: this), a collection another object holds or returns (p.kids += this, reg.members() += this), or another object's mutating operator (reg += this, others += this). The other object can then call back into the half-built one - the hazard of passing 'this' as an argument, reached without a call.

      Storing into this object's OWN property is not an escape: re-pointing a property or local (me: this), an in-place operator on its own property (kids += this), or this.prop directly. A property of this object that refers to ANOTHER object is reached by a chain (this.me.parent: this) and is an escape.