@@ -93,10 +93,11 @@ class Objects(using Context @constructorOnly):
93
93
* | OfClass(class, vs[outer], ctor, args, env) // instance of a class
94
94
* | OfArray(object[owner], regions)
95
95
* | 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
97
98
* vs ::= ValueSet(ve) // set of abstract values
98
99
* 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
100
101
* Ref ::= ObjectRef | OfClass // values that represent a reference to some (global or instance) object
101
102
* ThisValue ::= Ref | UnknownValue // possible values for 'this'
102
103
*
@@ -190,7 +191,7 @@ class Objects(using Context @constructorOnly):
190
191
191
192
def show (using Context ) =
192
193
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 + " )"
194
195
195
196
object OfClass :
196
197
def apply (
@@ -229,7 +230,8 @@ class Objects(using Context @constructorOnly):
229
230
230
231
/**
231
232
* 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
233
235
*/
234
236
case class SafeValue (tpe : Type ) extends ValueElement :
235
237
// tpe could be a AppliedType(java.lang.Class, T)
@@ -253,15 +255,21 @@ class Objects(using Context @constructorOnly):
253
255
def show (using Context ): String = " Package(" + packageSym.show + " )"
254
256
255
257
/** 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
256
263
*
257
264
* This is the top of the abstract domain lattice, which should not
258
265
* be used during initialization.
259
266
*
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"
265
273
266
274
val Bottom = ValueSet (ListSet .empty)
267
275
@@ -483,8 +491,8 @@ class Objects(using Context @constructorOnly):
483
491
thisV match
484
492
case ref : OfClass =>
485
493
ref.outer match
486
- case outer : ThisValue =>
487
- resolveEnv(meth, outer, ref .env)
494
+ case outer : OfClass =>
495
+ resolveEnv(meth, outer, outer .env)
488
496
case _ =>
489
497
// TODO: properly handle the case where ref.outer is ValueSet
490
498
None
@@ -623,8 +631,8 @@ class Objects(using Context @constructorOnly):
623
631
extension (a : Value )
624
632
def join (b : Value ): Value =
625
633
(a, b) match
626
- case (UnknownValue , _) => UnknownValue
627
- case (_, UnknownValue ) => UnknownValue
634
+ case (TopWidenedValue , _) => TopWidenedValue
635
+ case (_, TopWidenedValue ) => TopWidenedValue
628
636
case (Package (_), _) => UnknownValue // should not happen
629
637
case (_, Package (_)) => UnknownValue
630
638
case (Bottom , b) => b
@@ -640,8 +648,8 @@ class Objects(using Context @constructorOnly):
640
648
case (a : Ref , b : Ref ) if a.equals(b) => Bottom
641
649
case _ => a
642
650
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
645
653
else
646
654
a match
647
655
case Bottom => Bottom
@@ -659,6 +667,7 @@ class Objects(using Context @constructorOnly):
659
667
ref.widenedCopy(outer2, args2, env2)
660
668
661
669
case _ => a
670
+ }
662
671
663
672
def filterType (tpe : Type )(using Context ): Value =
664
673
tpe match
@@ -670,19 +679,21 @@ class Objects(using Context @constructorOnly):
670
679
671
680
// Filter the value according to a class symbol, and only leaves the sub-values
672
681
// 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
+ }
686
697
687
698
extension (value : Ref | UnknownValue .type )
688
699
def widenRefOrCold (height : Int )(using Context ) : Ref | UnknownValue .type = value.widen(height).asInstanceOf [ThisValue ]
@@ -708,6 +719,9 @@ class Objects(using Context @constructorOnly):
708
719
*/
709
720
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) {
710
721
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
711
725
case UnknownValue =>
712
726
if reportUnknown then
713
727
report.warning(" Using unknown value. " + Trace .show, Trace .position)
@@ -898,6 +912,9 @@ class Objects(using Context @constructorOnly):
898
912
*/
899
913
def select (value : Value , field : Symbol , receiver : Type , needResolve : Boolean = true ): Contextual [Value ] = log(" select " + field.show + " , this = " + value.show, printer, (_ : Value ).show) {
900
914
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
901
918
case UnknownValue =>
902
919
if reportUnknown then
903
920
report.warning(" Using unknown value. " + Trace .show, Trace .position)
@@ -984,15 +1001,21 @@ class Objects(using Context @constructorOnly):
984
1001
*/
985
1002
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) {
986
1003
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
987
1010
case p : Package =>
988
1011
report.warning(" [Internal error] unexpected tree in assignment, package = " + p.packageSym.show + Trace .show, Trace .position)
989
1012
case fun : Fun =>
990
1013
report.warning(" [Internal error] unexpected tree in assignment, fun = " + fun.code.show + Trace .show, Trace .position)
991
1014
case arr : OfArray =>
992
1015
report.warning(" [Internal error] unexpected tree in assignment, array = " + arr.show + " field = " + field + Trace .show, Trace .position)
993
1016
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)
996
1019
997
1020
case ValueSet (values) =>
998
1021
values.foreach(ref => assign(ref, field, rhs, rhsTyp))
@@ -1027,8 +1050,16 @@ class Objects(using Context @constructorOnly):
1027
1050
report.warning(" [Internal error] unexpected outer in instantiating a class, outer = " + outer.show + " , class = " + klass.show + " , " + Trace .show, Trace .position)
1028
1051
Bottom
1029
1052
1053
+ case TopWidenedValue =>
1054
+ report.warning(" Value is unknown to the checker due to widening. " + Trace .show, Trace .position)
1055
+ Bottom
1056
+
1030
1057
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
1032
1063
1033
1064
case outer : (Ref | UnknownValue .type | Package ) =>
1034
1065
if klass == defn.ArrayClass then
@@ -1115,7 +1146,7 @@ class Objects(using Context @constructorOnly):
1115
1146
case fun : Fun =>
1116
1147
given Env .Data = Env .ofByName(sym, fun.env)
1117
1148
eval(fun.code, fun.thisV, fun.klass)
1118
- case UnknownValue =>
1149
+ case UnknownValue | TopWidenedValue =>
1119
1150
report.warning(" Calling on unknown value. " + Trace .show, Trace .position)
1120
1151
Bottom
1121
1152
case _ : ValueSet | _ : Ref | _ : OfArray | _ : Package | SafeValue (_) =>
@@ -1891,6 +1922,7 @@ class Objects(using Context @constructorOnly):
1891
1922
thisV match
1892
1923
case Bottom => Bottom
1893
1924
case UnknownValue => UnknownValue
1925
+ case TopWidenedValue => TopWidenedValue
1894
1926
case ref : Ref =>
1895
1927
val outerCls = klass.owner.lexicallyEnclosingClass.asClass
1896
1928
if ! ref.hasOuter(klass) then
0 commit comments