Class ObjectSharing

java.lang.Object
org.ek9lang.compiler.support.ObjectSharing

public final class ObjectSharing extends Object
THE reading of what one occurrence of a name does to the OBJECT the name refers to - for every analysis that remembers what a variable holds (the phase-5 value tracker behind E08086, CompileTimeKnownText behind E08275). Use this; never a second list of "the ways a value can change".

An EK9 variable refers to an object, and most mutation happens IN PLACE: total += 5, total++, cat items > total change the object and store nothing. So a value can change through any name that refers to the same object, and through any code that is given it (D-1 / C-2 / NEW-2, 2026-10-19: the value tracker saw only assignment statements, and four routes each gave a false "condition is always true").

  • ObjectSharing.Kind.IN_PLACE_WRITE - a write that changes the object rather than storing a new one: a mutating operator (+=, ++, :=:, ...), or the target of a stream (>, >>, tee). Which occurrences write is SymbolWrites.writingOperator(ParserRuleContext), never re-derived here.
  • ObjectSharing.Kind.SHARED - the occurrence makes another name refer to the SAME object: it is the whole right-hand side of a declaration or a plain store (alias <- base, alias := (base)), or an argument of a capture list ((base) is action as function, whose copy shares the object).
  • ObjectSharing.Kind.HANDED_TO_CALL - code that may change the object is given it: a bare argument of a callee that is not pure (except a concrete built-in, which changes only its receiver - the RETAINS declarations say what a built-in KEEPS, never that it changes an argument), or the receiver of a method that is not pure.

D61 added what an occurrence RETAINS (all(EK9Parser.IdentifierContext, Function)): an object kept in a collection or Dict literal, added with a compound assignment, given to a call or constructor that may keep it, or iterated by a for is the same object as the holder's element, so the holder is ObjectSharing.Kind.SHARED with it - or, where nothing nameable keeps it, ObjectSharing.Kind.ESCAPES. A call chain that may return a VIEW of its receiver is followed the same way, and so are the expressions whose value IS an operand (second ?: other, second <? limit, a ternary arm), the elements of a stream's source (kept by its sink, each tee target and what keeps a collect, given to each stage function), and the returning variable of a switch, try, while or for whose value is assigned (holder <- switch ...). An escaped object stays changeable: the value tracker forgets what it learns about it at every later call of the program's own code (mayRunProgramCode(ParseTree, Function)) and every in-place change. Deliberately NOT covered: a view obtained from a CALL RESULT that is not a chain on a variable (makeBox().item()).

  • Nested Class Summary

    Nested Classes
    Modifier and Type
    Class
    Description
    static enum 
    What an occurrence does to the object.
    static final record 
    What a call can REACH (D66, Steve 2026-10-21): the objects it is given.
    static final record 
    One occurrence's effect.
  • Method Summary

    Modifier and Type
    Method
    Description
    all(EK9Parser.IdentifierContext occurrence, Function<org.antlr.v4.runtime.tree.ParseTree, ISymbol> recorded)
    EVERY effect of one occurrence: what of(EK9Parser.IdentifierContext, Function) says, plus what the occurrence RETAINS (D61) - the object put in a collection literal, a Dict value, given to a call or constructor that keeps its argument, added with a compound assignment, or iterated by a for: whatever holds it afterwards shares the object, so an in-place change through the holder (a loop variable, an element, a field) changes it too.
    allOfSelf(EK9Parser.PrimaryReferenceContext self, Function<org.antlr.v4.runtime.tree.ParseTree, ISymbol> recorded)
    What this or super - the object a method runs on, whose name is no identifier - does to that object, read exactly as the occurrence of a variable is (all(EK9Parser.IdentifierContext, Function)): kept where the whole expression is kept (items += this, box <- Box(this), (this) is ...), or the start of a call chain that may return a view of it (this.me()).
    static boolean
    May a compound assignment (target += item, target :~: item) to a value of this type keep its operand? A container (a parameterised type, a bare T) does; so does a type of the program's own, whose operator is written in EK9 and may store it - as any call of the program's own may keep its argument (keepsArgument(ISymbol, int); the verifier, 2026-10-21: registry += item, Registry's += adding to its list, was read as keeping nothing, so a loaned closeable was closed by its statement and then used).
    static boolean
    THE coalescing test: ?:, <?, >? (??, <=?/>=? keep their grammar tokens only so phase 1 can report E01096/E01095 - they never reach here).
    static boolean
    D66 gap (Steve, 2026-10-21): does a for ... in loop over a value of this type run code of the program's own? THE test: the loop calls iterator() and then hasNext()/next() (or those two on the value itself), so it is a call that is not pure when the type - or the iterator its iterator() declares to return - has one of those methods that is not pure and is supplied by the program: a method written in EK9, or an abstract one of a built-in trait or abstract type (Iterator, Iterable) a program implements.
    static boolean
    mayBeExistingObject(EK9Parser.ExpressionContext expression, Function<org.antlr.v4.runtime.tree.ParseTree, ISymbol> recorded)
    May the value of this expression BE an object that exists already - rather than a fresh one: a variable (not a constant, which is read as a copy), this/super, a call whose RETAINS says its result may be its receiver's interior or an argument (ReturnRetention.mayAlias(ScopedSymbol)), or a coalescing operator's result or a ternary's when EITHER operand may be (the result is one of them: the same route all(EK9Parser.IdentifierContext, Function) follows for the operand itself).
    static boolean
    mayBeExistingObjectByShape(EK9Parser.ExpressionContext expression, Function<org.antlr.v4.runtime.tree.ParseTree, ISymbol> recorded)
    Is the expression, looking through parentheses, of a SHAPE whose value may be an object that already exists - a variable, this/super, a call, or a coalescing operator or ternary (whose result IS one of its operands) - so only mayBeExistingObject(EK9Parser.ExpressionContext, Function), once phase 5 has derived the RETAINS of the program's own callables, can say whether it is.
    static boolean
    mayBeObjectFromOutside(org.antlr.v4.runtime.tree.ParseTree value, Function<org.antlr.v4.runtime.tree.ParseTree, ISymbol> recorded)
    May the value given to a name be, or hold, an object from OUTSIDE the code being analysed (D66) that the name is not recorded as sharing with: this - itself, or held by a list, a Dict, a constructor's or call's arguments or a dynamic function's or class's capture list (box <- Box(this)) - or a result a call, a coalescing operator or a ternary hands back that may be an existing object.
    static boolean
    mayRunProgramCode(org.antlr.v4.runtime.tree.ParseTree call, Function<org.antlr.v4.runtime.tree.ParseTree, ISymbol> recorded)
    May the call (or stream stage operand) call run code of the program's own - something that is not pure and not a concrete built-in? Such code may change any object it can reach, an object kept where the caller cannot name it included.
    of(EK9Parser.IdentifierContext occurrence, Function<org.antlr.v4.runtime.tree.ParseTree, ISymbol> recorded)
    What occurrence - an identifier the compiler bound to a symbol - does to the object it names; empty when it only reads it, or stores a new object in it.
    static boolean
    D66 gap (verifier, 2026-10-21): does a for range over values of this type run code of the program's own? The range builds its counter and each loop value with the type's copy constructor and moves the counter with ++/-- (or +=/-= by a step), comparing with <=>: a program type with any of those not pure runs that code, which may change what it can reach (a copy shares its fields).
    reachOf(org.antlr.v4.runtime.tree.ParseTree call, Function<org.antlr.v4.runtime.tree.ParseTree, ISymbol> recorded)
    THE reading of what a call that may run the program's own code can change: everything reachable from ObjectSharing.Reach, which the value tracker turns into what it forgets (ValueTrackingAnalyzer.recordCallThatMayChange).
    reachOfInPlaceWrite(org.antlr.v4.runtime.ParserRuleContext occurrence, ISymbol written, ISymbol type, Function<org.antlr.v4.runtime.tree.ParseTree, ISymbol> recorded)
    THE reading of an in-place write as a call (D66, Steve 2026-10-21): when the operator that makes it is the program's own code (inPlaceWriteRunsProgramCode(ISymbol, String)), it is a call that is not pure, given the object written and whatever the right-hand side gives it (other -= amount), or - for a stream target - every element of the stream.
    reachOfIteration(EK9Parser.ExpressionContext iterated, Function<org.antlr.v4.runtime.tree.ParseTree, ISymbol> recorded)
    What a for loop over iterated can reach (D66, the same reading as reachOf(ParseTree, Function) gives a call): the value iterated - a name, this, or something no name is given to - and what it is made from.

    Methods inherited from class Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Method Details

    • all

      public static List<ObjectSharing.Use> all(EK9Parser.IdentifierContext occurrence, Function<org.antlr.v4.runtime.tree.ParseTree, ISymbol> recorded)
      EVERY effect of one occurrence: what of(EK9Parser.IdentifierContext, Function) says, plus what the occurrence RETAINS (D61) - the object put in a collection literal, a Dict value, given to a call or constructor that keeps its argument, added with a compound assignment, or iterated by a for: whatever holds it afterwards shares the object, so an in-place change through the holder (a loop variable, an element, a field) changes it too. THE one place that decides this; callers loop over the result.
    • allOfSelf

      public static List<ObjectSharing.Use> allOfSelf(EK9Parser.PrimaryReferenceContext self, Function<org.antlr.v4.runtime.tree.ParseTree, ISymbol> recorded)
      What this or super - the object a method runs on, whose name is no identifier - does to that object, read exactly as the occurrence of a variable is (all(EK9Parser.IdentifierContext, Function)): kept where the whole expression is kept (items += this, box <- Box(this), (this) is ...), or the start of a call chain that may return a view of it (this.me()). Nothing for any other use (a read of its own fields, a call of its own methods that does not keep it - the receiver's own verdict, which the caller asks of the callee). D80 follow-up (2026-10-21): a method that stores this was read as keeping nothing, because this is not an identifier.
    • compoundTargetMayKeep

      public static boolean compoundTargetMayKeep(ISymbol type, ISymbol operandType)
      May a compound assignment (target += item, target :~: item) to a value of this type keep its operand? A container (a parameterised type, a bare T) does; so does a type of the program's own, whose operator is written in EK9 and may store it - as any call of the program's own may keep its argument (keepsArgument(ISymbol, int); the verifier, 2026-10-21: registry += item, Registry's += adding to its list, was read as keeping nothing, so a loaned closeable was closed by its statement and then used). A built-in value (total += 5), a constrained type over one, and an enumeration copy.

      The two-argument form is the statement-ownership decision's FALLBACK (D80 follow-up; since lane closeable-matrix, 2026-10-21, the decision first reads the operator the target's type serves, as it reads a call's callee, and asks this only when no operator can be read - a generic's T): an UNNAMED closeable given to a compound assignment is then the statement's loan unless the target may keep it, where the target must also be able to HOLD an object of the operand's type (BorrowedViewReturn.mayHold(ISymbol)) - a List of String or a type whose properties are all Strings keeps no closeable.

    • of

      public static Optional<ObjectSharing.Use> of(EK9Parser.IdentifierContext occurrence, Function<org.antlr.v4.runtime.tree.ParseTree, ISymbol> recorded)
      What occurrence - an identifier the compiler bound to a symbol - does to the object it names; empty when it only reads it, or stores a new object in it.
      Parameters:
      occurrence - the identifier (as RecordedSymbols.Reference.node())
      recorded - the recorded-symbol lookup of the phase asking (symbolsAndScopes or parsedModule)
    • isCoalescingOperator

      public static boolean isCoalescingOperator(EK9Parser.ExpressionContext binary)
      THE coalescing test: ?:, <?, >? (??, <=?/>=? keep their grammar tokens only so phase 1 can report E01096/E01095 - they never reach here).
    • mayBeExistingObjectByShape

      public static boolean mayBeExistingObjectByShape(EK9Parser.ExpressionContext expression, Function<org.antlr.v4.runtime.tree.ParseTree, ISymbol> recorded)
      Is the expression, looking through parentheses, of a SHAPE whose value may be an object that already exists - a variable, this/super, a call, or a coalescing operator or ternary (whose result IS one of its operands) - so only mayBeExistingObject(EK9Parser.ExpressionContext, Function), once phase 5 has derived the RETAINS of the program's own callables, can say whether it is. Phase 3 defers its E08090 verdict on exactly these shapes; anything else (a literal, a constructor call, a list or dict literal) is fresh and keeps its verdict there.
    • mayBeExistingObject

      public static boolean mayBeExistingObject(EK9Parser.ExpressionContext expression, Function<org.antlr.v4.runtime.tree.ParseTree, ISymbol> recorded)
      May the value of this expression BE an object that exists already - rather than a fresh one: a variable (not a constant, which is read as a copy), this/super, a call whose RETAINS says its result may be its receiver's interior or an argument (ReturnRetention.mayAlias(ScopedSymbol)), or a coalescing operator's result or a ternary's when EITHER operand may be (the result is one of them: the same route all(EK9Parser.IdentifierContext, Function) follows for the operand itself). Parentheses are looked through. A literal, a constructor call and a call that hands back something fresh are not.
    • mayBeObjectFromOutside

      public static boolean mayBeObjectFromOutside(org.antlr.v4.runtime.tree.ParseTree value, Function<org.antlr.v4.runtime.tree.ParseTree, ISymbol> recorded)
      May the value given to a name be, or hold, an object from OUTSIDE the code being analysed (D66) that the name is not recorded as sharing with: this - itself, or held by a list, a Dict, a constructor's or call's arguments or a dynamic function's or class's capture list (box <- Box(this)) - or a result a call, a coalescing operator or a ternary hands back that may be an existing object. A plain variable is not asked here - alias <- base is ObjectSharing.Kind.SHARED, so the alias is outside exactly when base is.
      Parameters:
      value - the right-hand side of a declaration or store (an assignmentExpression's content)
    • mayRunProgramCode

      public static boolean mayRunProgramCode(org.antlr.v4.runtime.tree.ParseTree call, Function<org.antlr.v4.runtime.tree.ParseTree, ISymbol> recorded)
      May the call (or stream stage operand) call run code of the program's own - something that is not pure and not a concrete built-in? Such code may change any object it can reach, an object kept where the caller cannot name it included. A stage operand that is not a function (a sink variable, a count) runs nothing.
    • iterationRunsProgramCode

      public static boolean iterationRunsProgramCode(ISymbol type)
      D66 gap (Steve, 2026-10-21): does a for ... in loop over a value of this type run code of the program's own? THE test: the loop calls iterator() and then hasNext()/next() (or those two on the value itself), so it is a call that is not pure when the type - or the iterator its iterator() declares to return - has one of those methods that is not pure and is supplied by the program: a method written in EK9, or an abstract one of a built-in trait or abstract type (Iterator, Iterable) a program implements. A concrete built-in (List, Dict, String, a range, an enumeration) never calls the program back - its iterator never runs a held element's code. A generic's bare T may be anything.
    • rangeRunsProgramCode

      public static boolean rangeRunsProgramCode(ISymbol type)
      D66 gap (verifier, 2026-10-21): does a for range over values of this type run code of the program's own? The range builds its counter and each loop value with the type's copy constructor and moves the counter with ++/-- (or +=/-= by a step), comparing with <=>: a program type with any of those not pure runs that code, which may change what it can reach (a copy shares its fields). A built-in, a constrained type (it counts in its built-in base) and an enumeration run nothing of the program's; a generic's bare T may be anything.
    • reachOfIteration

      public static ObjectSharing.Reach reachOfIteration(EK9Parser.ExpressionContext iterated, Function<org.antlr.v4.runtime.tree.ParseTree, ISymbol> recorded)
      What a for loop over iterated can reach (D66, the same reading as reachOf(ParseTree, Function) gives a call): the value iterated - a name, this, or something no name is given to - and what it is made from.
    • reachOf

      public static ObjectSharing.Reach reachOf(org.antlr.v4.runtime.tree.ParseTree call, Function<org.antlr.v4.runtime.tree.ParseTree, ISymbol> recorded)
      THE reading of what a call that may run the program's own code can change: everything reachable from ObjectSharing.Reach, which the value tracker turns into what it forgets (ValueTrackingAnalyzer.recordCallThatMayChange). A value that is certainly fresh - a literal, a constructor call with nothing to reach, an operator's new result - reaches nothing.
    • reachOfInPlaceWrite

      public static Optional<ObjectSharing.Reach> reachOfInPlaceWrite(org.antlr.v4.runtime.ParserRuleContext occurrence, ISymbol written, ISymbol type, Function<org.antlr.v4.runtime.tree.ParseTree, ISymbol> recorded)
      THE reading of an in-place write as a call (D66, Steve 2026-10-21): when the operator that makes it is the program's own code (inPlaceWriteRunsProgramCode(ISymbol, String)), it is a call that is not pure, given the object written and whatever the right-hand side gives it (other -= amount), or - for a stream target - every element of the stream. Empty when nothing of the program's runs (total += 5, items += item), or the occurrence is not an in-place write.
      Parameters:
      occurrence - the written occurrence: an identifier, or this as the target of an assignment
      written - the variable written, or null for this
      type - the type of the object written