@@ -93,10 +93,11 @@ class Objects(using Context @constructorOnly):
9393 * | OfClass(class, vs[outer], ctor, args, env) // instance of a class
9494 * | OfArray(object[owner], regions)
9595 * | Fun(..., env) // value elements that can be contained in ValueSet
96- * | SafeValue // values on which method calls and fields won't cause warnings. Int, String, etc.
96+ * | SafeValue // values on which method calls and field accesses won't cause warnings. Int, String, etc.
97+ * | UnknownValue
9798 * vs ::= ValueSet(ve) // set of abstract values
9899 * Bottom ::= ValueSet(Empty)
99- * val ::= ve | UnknownValue | vs | Package // all possible abstract values in domain
100+ * val ::= ve | TopWidenedValue | vs | Package // all possible abstract values in domain
100101 * Ref ::= ObjectRef | OfClass // values that represent a reference to some (global or instance) object
101102 * ThisValue ::= Ref | UnknownValue // possible values for 'this'
102103 *
@@ -190,7 +191,7 @@ class Objects(using Context @constructorOnly):
190191
191192 def show (using Context ) =
192193 val valFields = vals.map(_.show + " -> " + _.show)
193- " OfClass(" + klass.show + " , outer = " + outer + " , args = " + args.map(_.show) + " , vals = " + valFields + " )"
194+ " OfClass(" + klass.show + " , outer = " + outer + " , args = " + args.map(_.show) + " env = " + env.show + " , vals = " + valFields + " )"
194195
195196 object OfClass :
196197 def apply (
@@ -229,7 +230,8 @@ class Objects(using Context @constructorOnly):
229230
230231 /**
231232 * Represents common base values like Int, String, etc.
232- * Assumption: all methods calls on such values should be pure (no side effects)
233+ * Assumption: all methods calls on such values should not trigger initialization of global objects
234+ * or read/write mutable fields
233235 */
234236 case class SafeValue (tpe : Type ) extends ValueElement :
235237 // tpe could be a AppliedType(java.lang.Class, T)
@@ -253,15 +255,21 @@ class Objects(using Context @constructorOnly):
253255 def show (using Context ): String = " Package(" + packageSym.show + " )"
254256
255257 /** Represents values unknown to the checker, such as values loaded without source
258+ */
259+ case object UnknownValue extends ValueElement :
260+ def show (using Context ): String = " UnknownValue"
261+
262+ /** Represents values lost due to widening
256263 *
257264 * This is the top of the abstract domain lattice, which should not
258265 * be used during initialization.
259266 *
260- * UnknownValue is not ValueElement since RefSet containing UnknownValue
261- * is equivalent to UnknownValue
262- */
263- case object UnknownValue extends Value :
264- def show (using Context ): String = " UnknownValue"
267+ * TopWidenedValue is not ValueElement since RefSet containing TopWidenedValue
268+ * is equivalent to TopWidenedValue
269+ */
270+
271+ case object TopWidenedValue extends Value :
272+ def show (using Context ): String = " TopWidenedValue"
265273
266274 val Bottom = ValueSet (ListSet .empty)
267275
@@ -483,8 +491,8 @@ class Objects(using Context @constructorOnly):
483491 thisV match
484492 case ref : OfClass =>
485493 ref.outer match
486- case outer : ThisValue =>
487- resolveEnv(meth, outer, ref .env)
494+ case outer : OfClass =>
495+ resolveEnv(meth, outer, outer .env)
488496 case _ =>
489497 // TODO: properly handle the case where ref.outer is ValueSet
490498 None
@@ -623,8 +631,8 @@ class Objects(using Context @constructorOnly):
623631 extension (a : Value )
624632 def join (b : Value ): Value =
625633 (a, b) match
626- case (UnknownValue , _) => UnknownValue
627- case (_, UnknownValue ) => UnknownValue
634+ case (TopWidenedValue , _) => TopWidenedValue
635+ case (_, TopWidenedValue ) => TopWidenedValue
628636 case (Package (_), _) => UnknownValue // should not happen
629637 case (_, Package (_)) => UnknownValue
630638 case (Bottom , b) => b
@@ -640,8 +648,8 @@ class Objects(using Context @constructorOnly):
640648 case (a : Ref , b : Ref ) if a.equals(b) => Bottom
641649 case _ => a
642650
643- def widen (height : Int )(using Context ): Value =
644- if height == 0 then UnknownValue
651+ def widen (height : Int )(using Context ): Value = log( " widening value " + a.show + " down to height " + height, printer, ( _ : Value ).show) {
652+ if height == 0 then TopWidenedValue
645653 else
646654 a match
647655 case Bottom => Bottom
@@ -659,6 +667,7 @@ class Objects(using Context @constructorOnly):
659667 ref.widenedCopy(outer2, args2, env2)
660668
661669 case _ => a
670+ }
662671
663672 def filterType (tpe : Type )(using Context ): Value =
664673 tpe match
@@ -670,19 +679,21 @@ class Objects(using Context @constructorOnly):
670679
671680 // Filter the value according to a class symbol, and only leaves the sub-values
672681 // which could represent an object of the given class
673- def filterClass (sym : Symbol )(using Context ): Value =
674- if ! sym.isClass then a
675- else
676- val klass = sym.asClass
677- a match
678- case UnknownValue => UnknownValue
679- case Package (_) => a
680- case SafeValue (_) => a
681- case ref : Ref => if ref.klass.isSubClass(klass) then ref else Bottom
682- case ValueSet (values) => values.map(v => v.filterClass(klass)).join
683- case arr : OfArray => if defn.ArrayClass .isSubClass(klass) then arr else Bottom
684- case fun : Fun =>
685- if klass.isOneOf(AbstractOrTrait ) && klass.baseClasses.exists(defn.isFunctionClass) then fun else Bottom
682+ def filterClass (sym : Symbol )(using Context ): Value = log(" filtering value " + a.show + " through class " + sym.show, printer, (_ : Value ).show) {
683+ if ! sym.isClass then a
684+ else
685+ val klass = sym.asClass
686+ a match
687+ case UnknownValue | TopWidenedValue => a
688+ case Package (packageSym) =>
689+ if packageSym.moduleClass.equals(sym) || (klass.denot.isPackageObject && klass.owner.equals(sym)) then a else Bottom
690+ case v : SafeValue => if v.typeref.symbol.asClass.isSubClass(klass) then a else Bottom
691+ case ref : Ref => if ref.klass.isSubClass(klass) then ref else Bottom
692+ case ValueSet (values) => values.map(v => v.filterClass(klass)).join
693+ case arr : OfArray => if defn.ArrayClass .isSubClass(klass) then arr else Bottom
694+ case fun : Fun =>
695+ if klass.isOneOf(AbstractOrTrait ) && klass.baseClasses.exists(defn.isFunctionClass) then fun else Bottom
696+ }
686697
687698 extension (value : Ref | UnknownValue .type )
688699 def widenRefOrCold (height : Int )(using Context ) : Ref | UnknownValue .type = value.widen(height).asInstanceOf [ThisValue ]
@@ -708,6 +719,9 @@ class Objects(using Context @constructorOnly):
708719 */
709720 def call (value : Value , meth : Symbol , args : List [ArgInfo ], receiver : Type , superType : Type , needResolve : Boolean = true ): Contextual [Value ] = log(" call " + meth.show + " , this = " + value.show + " , args = " + args.map(_.value.show), printer, (_ : Value ).show) {
710721 value.filterClass(meth.owner) match
722+ case TopWidenedValue =>
723+ report.warning(" Value is unknown to the checker due to widening. " + Trace .show, Trace .position)
724+ Bottom
711725 case UnknownValue =>
712726 if reportUnknown then
713727 report.warning(" Using unknown value. " + Trace .show, Trace .position)
@@ -898,6 +912,9 @@ class Objects(using Context @constructorOnly):
898912 */
899913 def select (value : Value , field : Symbol , receiver : Type , needResolve : Boolean = true ): Contextual [Value ] = log(" select " + field.show + " , this = " + value.show, printer, (_ : Value ).show) {
900914 value.filterClass(field.owner) match
915+ case TopWidenedValue =>
916+ report.warning(" Value is unknown to the checker due to widening. " + Trace .show, Trace .position)
917+ Bottom
901918 case UnknownValue =>
902919 if reportUnknown then
903920 report.warning(" Using unknown value. " + Trace .show, Trace .position)
@@ -942,15 +959,15 @@ class Objects(using Context @constructorOnly):
942959 Bottom
943960 else
944961 // initialization error, reported by the initialization checker
945- UnknownValue
962+ Bottom
946963 else if ref.hasVal(target) then
947964 ref.valValue(target)
948965 else if ref.isObjectRef && ref.klass.hasSource then
949966 report.warning(" Access uninitialized field " + field.show + " . " + Trace .show, Trace .position)
950967 Bottom
951968 else
952969 // initialization error, reported by the initialization checker
953- UnknownValue
970+ Bottom
954971
955972 else
956973 if ref.klass.isSubClass(receiver.widenSingleton.classSymbol) then
@@ -984,15 +1001,21 @@ class Objects(using Context @constructorOnly):
9841001 */
9851002 def assign (lhs : Value , field : Symbol , rhs : Value , rhsTyp : Type ): Contextual [Value ] = log(" Assign" + field.show + " of " + lhs.show + " , rhs = " + rhs.show, printer, (_ : Value ).show) {
9861003 lhs.filterClass(field.owner) match
1004+ case TopWidenedValue =>
1005+ report.warning(" Value is unknown to the checker due to widening. " + Trace .show, Trace .position)
1006+ case UnknownValue =>
1007+ if reportUnknown then
1008+ report.warning(" Assigning to unknown value. " + Trace .show, Trace .position)
1009+ end if
9871010 case p : Package =>
9881011 report.warning(" [Internal error] unexpected tree in assignment, package = " + p.packageSym.show + Trace .show, Trace .position)
9891012 case fun : Fun =>
9901013 report.warning(" [Internal error] unexpected tree in assignment, fun = " + fun.code.show + Trace .show, Trace .position)
9911014 case arr : OfArray =>
9921015 report.warning(" [Internal error] unexpected tree in assignment, array = " + arr.show + " field = " + field + Trace .show, Trace .position)
9931016
994- case SafeValue (_) | UnknownValue =>
995- report.warning(" Assigning to base or unknown value is forbidden. " + Trace .show, Trace .position)
1017+ case SafeValue (_) =>
1018+ report.warning(" Assigning to base value is forbidden. " + Trace .show, Trace .position)
9961019
9971020 case ValueSet (values) =>
9981021 values.foreach(ref => assign(ref, field, rhs, rhsTyp))
@@ -1027,8 +1050,16 @@ class Objects(using Context @constructorOnly):
10271050 report.warning(" [Internal error] unexpected outer in instantiating a class, outer = " + outer.show + " , class = " + klass.show + " , " + Trace .show, Trace .position)
10281051 Bottom
10291052
1053+ case TopWidenedValue =>
1054+ report.warning(" Value is unknown to the checker due to widening. " + Trace .show, Trace .position)
1055+ Bottom
1056+
10301057 case UnknownValue =>
1031- UnknownValue
1058+ if reportUnknown then
1059+ report.warning(" Assigning to unknown value. " + Trace .show, Trace .position)
1060+ Bottom
1061+ else
1062+ UnknownValue
10321063
10331064 case outer : (Ref | UnknownValue .type | Package ) =>
10341065 if klass == defn.ArrayClass then
@@ -1115,7 +1146,7 @@ class Objects(using Context @constructorOnly):
11151146 case fun : Fun =>
11161147 given Env .Data = Env .ofByName(sym, fun.env)
11171148 eval(fun.code, fun.thisV, fun.klass)
1118- case UnknownValue =>
1149+ case UnknownValue | TopWidenedValue =>
11191150 report.warning(" Calling on unknown value. " + Trace .show, Trace .position)
11201151 Bottom
11211152 case _ : ValueSet | _ : Ref | _ : OfArray | _ : Package | SafeValue (_) =>
@@ -1891,6 +1922,7 @@ class Objects(using Context @constructorOnly):
18911922 thisV match
18921923 case Bottom => Bottom
18931924 case UnknownValue => UnknownValue
1925+ case TopWidenedValue => TopWidenedValue
18941926 case ref : Ref =>
18951927 val outerCls = klass.owner.lexicallyEnclosingClass.asClass
18961928 if ! ref.hasOuter(klass) then
0 commit comments