Class ObjectSharing
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 isSymbolWrites.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 notpure(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 notpure.
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 ClassesModifier and TypeClassDescriptionstatic enumWhat an occurrence does to the object.static final recordWhat a call can REACH (D66, Steve 2026-10-21): the objects it is given.static final recordOne occurrence's effect. -
Method Summary
Modifier and TypeMethodDescriptionstatic List<ObjectSharing.Use> all(EK9Parser.IdentifierContext occurrence, Function<org.antlr.v4.runtime.tree.ParseTree, ISymbol> recorded) EVERY effect of one occurrence: whatof(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 afor: 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.static List<ObjectSharing.Use> allOfSelf(EK9Parser.PrimaryReferenceContext self, Function<org.antlr.v4.runtime.tree.ParseTree, ISymbol> recorded) Whatthisorsuper- 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 booleancompoundTargetMayKeep(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 bareT) 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 booleanTHE coalescing test:?:,<?,>?(??,<=?/>=?keep their grammar tokens only so phase 1 can report E01096/E01095 - they never reach here).static booleanD66 gap (Steve, 2026-10-21): does afor ... inloop over a value of this type run code of the program's own? THE test: the loop callsiterator()and thenhasNext()/next()(or those two on the value itself), so it is a call that is not pure when the type - or the iterator itsiterator()declares to return - has one of those methods that is notpureand 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 booleanmayBeExistingObject(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 routeall(EK9Parser.IdentifierContext, Function)follows for the operand itself).static booleanmayBeExistingObjectByShape(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 onlymayBeExistingObject(EK9Parser.ExpressionContext, Function), once phase 5 has derived the RETAINS of the program's own callables, can say whether it is.static booleanmayBeObjectFromOutside(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 booleanmayRunProgramCode(org.antlr.v4.runtime.tree.ParseTree call, Function<org.antlr.v4.runtime.tree.ParseTree, ISymbol> recorded) May the call (or stream stage operand)callrun code of the program's own - something that is notpureand not a concrete built-in? Such code may change any object it can reach, an object kept where the caller cannot name it included.static Optional<ObjectSharing.Use> of(EK9Parser.IdentifierContext occurrence, Function<org.antlr.v4.runtime.tree.ParseTree, ISymbol> recorded) Whatoccurrence- 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 booleanrangeRunsProgramCode(ISymbol type) D66 gap (verifier, 2026-10-21): does aforrange 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 notpureruns that code, which may change what it can reach (a copy shares its fields).static ObjectSharing.ReachreachOf(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 fromObjectSharing.Reach, which the value tracker turns into what it forgets (ValueTrackingAnalyzer.recordCallThatMayChange).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.static ObjectSharing.ReachreachOfIteration(EK9Parser.ExpressionContext iterated, Function<org.antlr.v4.runtime.tree.ParseTree, ISymbol> recorded) What aforloop overiteratedcan reach (D66, the same reading asreachOf(ParseTree, Function)gives a call): the value iterated - a name,this, or something no name is given to - and what it is made from.
-
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: whatof(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 afor: 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) Whatthisorsuper- 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 storesthiswas read as keeping nothing, becausethisis not an identifier. -
compoundTargetMayKeep
May a compound assignment (target += item,target :~: item) to a value of this type keep its operand? A container (a parameterised type, a bareT) 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)) - aList of Stringor 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) Whatoccurrence- 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 (asRecordedSymbols.Reference.node())recorded- the recorded-symbol lookup of the phase asking (symbolsAndScopes or parsedModule)
-
isCoalescingOperator
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 onlymayBeExistingObject(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 routeall(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 <- baseisObjectSharing.Kind.SHARED, so the alias is outside exactly whenbaseis.- Parameters:
value- the right-hand side of a declaration or store (anassignmentExpression'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)callrun code of the program's own - something that is notpureand 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
D66 gap (Steve, 2026-10-21): does afor ... inloop over a value of this type run code of the program's own? THE test: the loop callsiterator()and thenhasNext()/next()(or those two on the value itself), so it is a call that is not pure when the type - or the iterator itsiterator()declares to return - has one of those methods that is notpureand 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 bareTmay be anything. -
rangeRunsProgramCode
D66 gap (verifier, 2026-10-21): does aforrange 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 notpureruns 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 bareTmay be anything. -
reachOfIteration
public static ObjectSharing.Reach reachOfIteration(EK9Parser.ExpressionContext iterated, Function<org.antlr.v4.runtime.tree.ParseTree, ISymbol> recorded) What aforloop overiteratedcan reach (D66, the same reading asreachOf(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 fromObjectSharing.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, orthisas the target of an assignmentwritten- the variable written, or null forthistype- the type of the object written
-