@@ -27,9 +27,7 @@ class Semantic {
2727
2828 /** Abstract values
2929 *
30- * Value = Hot | Cold | Addr | Fun | RefSet
31- *
32- * `Warm` and `This` will be addresses refer to the abstract heap
30+ * Value = Hot | Cold | Warm | ThisRef | Fun | RefSet
3331 */
3432 trait Value {
3533 def show : String = this .toString()
@@ -41,47 +39,30 @@ class Semantic {
4139 /** An object with unknown initialization status */
4240 case object Cold extends Value
4341
44- /** Addresses to the abstract heap
45- *
46- * Addresses determine abstractions of objects. Objects created
47- * with same address are represented with the same abstraction.
48- *
49- * Nested addresses may lead to infinite domain, thus widen is
50- * needed to finitize addresses. E.g. OOPSLA 2020 paper restricts
51- * args to be either `Hot` or `Cold`
52- */
53- case class Addr (klass : ClassSymbol , outer : Value ) extends Value
54-
55- /** A function value */
56- case class Fun (expr : Tree , thisV : Addr , klass : ClassSymbol ) extends Value
57-
58- /** A value which represents a set of addresses
42+ /** Object referred by `this` which stores abstract values for all fields
5943 *
60- * It comes from `if` expressions.
44+ * Note: the mutable `fields` plays the role of heap. Thanks to monotonicity
45+ * of the heap, we may handle it in a simple way.
6146 */
62- case class RefSet (refs : List [Addr | Fun ]) extends Value
47+ case class ThisRef (klass : ClassSymbol )(val fields : mutable.Map [Symbol , Value ]) extends Value {
48+ def updateField (field : Symbol , value : Value ): Unit =
49+ fields(field) = value
50+ }
6351
64- /** Object stores abstract values for all fields and the outer.
52+ /** An object with all fields initialized but reaches objects under initialization
6553 *
66- * Theoretically we only need to store the outer for the concrete class,
67- * as all other outers are determined.
68- *
69- * From performance reasons, we cache the immediate outer for all classes
70- * in the inheritance hierarchy.
54+ * We need to restrict nesting levels of `outer` to finitize the domain.
7155 */
72- case class Objekt (klass : ClassSymbol , fields : Map [ Symbol , Value ], outers : Map [ ClassSymbol , Value ])
56+ case class Warm (klass : ClassSymbol , outer : Value ) extends Value
7357
74- /** Abstract heap stores abstract objects
75- *
76- * As in the OOPSLA paper, the abstract heap is monotonistic
77- */
78- type Heap = mutable.Map [Addr , Objekt ]
58+ /** A function value */
59+ case class Fun (expr : Tree , thisV : ThisRef | Warm , klass : ClassSymbol ) extends Value
7960
80- /** The heap for abstract objects
61+ /** A value which represents a set of addresses
8162 *
82- * As the heap is monotonistic, we can avoid passing it around .
63+ * It comes from `if` expressions .
8364 */
84- val heap : Heap = mutable. Map .empty[ Addr , Objekt ]
65+ case class RefSet ( refs : List [ Warm | Fun | ThisRef ]) extends Value
8566
8667 /** Interpreter configuration
8768 *
@@ -154,39 +135,50 @@ class Semantic {
154135 case (Cold , _) => Cold
155136 case (_, Cold ) => Cold
156137
157- case (a : (Fun | Addr ), b : (Fun | Addr )) => RefSet (a :: b :: Nil )
138+ case (a : (Fun | Warm | ThisRef ), b : (Fun | Warm | ThisRef )) => RefSet (a :: b :: Nil )
158139
159- case (a : (Fun | Addr ), RefSet (refs)) => RefSet (a :: refs)
160- case (RefSet (refs), b : (Fun | Addr )) => RefSet (b :: refs)
140+ case (a : (Fun | Warm | ThisRef ), RefSet (refs)) => RefSet (a :: refs)
141+ case (RefSet (refs), b : (Fun | Warm | ThisRef )) => RefSet (b :: refs)
161142
162143 case (RefSet (refs1), RefSet (refs2)) => RefSet (refs1 ++ refs2)
163144
164145 extension (values : Seq [Value ])
165146 def join : Value = values.reduce { (v1, v2) => v1.join(v2) }
166147
167148 extension (value : Value )
168- def select (f : Symbol , source : Tree )(using Context , Trace ): Result =
149+ def select (field : Symbol , source : Tree )(using Context , Trace ): Result =
169150 value match {
170151 case Hot =>
171152 Result (Hot , noErrors)
172153
173154 case Cold =>
174- val error = AccessCold (f , source, trace)
155+ val error = AccessCold (field , source, trace)
175156 Result (Hot , error :: Nil )
176157
177- case addr : Addr =>
178- val obj = heap(addr)
179- if obj.fields.contains(f) then
180- Result (obj.fields(f), Nil )
158+ case thisRef : ThisRef =>
159+ val target = resolve(thisRef.klass, field)
160+ if target.is(Flags .Lazy ) then value.call(target, superType = NoType , source)
161+ else if thisRef.fields.contains(target) then
162+ Result (thisRef.fields(target), Nil )
181163 else
182- val error = AccessNonInit (f, trace.add(source))
164+ val error = AccessNonInit (target, trace.add(source))
165+ Result (Hot , error :: Nil )
166+
167+ case warm : Warm =>
168+ val target = resolve(warm.klass, field)
169+ if target.is(Flags .Lazy ) then value.call(target, superType = NoType , source)
170+ else if target.hasSource then
171+ val rhs = target.defTree.asInstanceOf [ValDef ].rhs
172+ eval(rhs, warm, target.owner.asClass)
173+ else
174+ val error = CallUnknown (field, source, trace)
183175 Result (Hot , error :: Nil )
184176
185177 case _ : Fun =>
186178 ???
187179
188180 case RefSet (refs) =>
189- val resList = refs.map(_.select(f , source))
181+ val resList = refs.map(_.select(field , source))
190182 val value2 = resList.map(_.value).join
191183 val errors = resList.flatMap(_.errors)
192184 Result (value2, errors)
@@ -201,29 +193,48 @@ class Semantic {
201193 val error = CallCold (meth, source, trace)
202194 Result (Hot , error :: Nil )
203195
204- case addr : Addr =>
205- val obj = heap(addr)
196+ case thisRef : ThisRef =>
206197 val target =
207198 if superType.exists then
208199 // TODO: superType could be A & B when there is self-annotation
209- resolveSuper(obj .klass, superType.classSymbol.asClass, meth)
200+ resolveSuper(thisRef .klass, superType.classSymbol.asClass, meth)
210201 else
211- resolve(obj .klass, meth)
202+ resolve(thisRef .klass, meth)
212203 if target.isPrimaryConstructor then
213- init(target.owner.asClass, addr )
204+ init(target.owner.asClass, thisRef )
214205 else if target.isOneOf(Flags .Method | Flags .Lazy ) then
215206 if target.hasSource then
216207 val rhs = target.defTree.asInstanceOf [DefDef ].rhs
217- eval(rhs, addr , target.owner.asClass)
208+ eval(rhs, thisRef , target.owner.asClass)
218209 else
219210 val error = CallUnknown (target, source, trace)
220211 Result (Hot , error :: Nil )
212+ else if thisRef.fields.contains(target) then
213+ Result (thisRef.fields(target), Nil )
221214 else
222- if obj.fields.contains(target) then
223- Result (obj.fields(target), Nil )
215+ val error = AccessNonInit (target, trace.add(source))
216+ Result (Hot , error :: Nil )
217+
218+ case warm : Warm =>
219+ val target =
220+ if superType.exists then
221+ // TODO: superType could be A & B when there is self-annotation
222+ resolveSuper(warm.klass, superType.classSymbol.asClass, meth)
223+ else
224+ resolve(warm.klass, meth)
225+ if target.isOneOf(Flags .Method | Flags .Lazy ) then
226+ if target.hasSource then
227+ val rhs = target.defTree.asInstanceOf [DefDef ].rhs
228+ eval(rhs, warm, target.owner.asClass)
224229 else
225- val error = AccessNonInit (target, trace.add( source) )
230+ val error = CallUnknown (target, source, trace )
226231 Result (Hot , error :: Nil )
232+ else if target.hasSource then
233+ val rhs = target.defTree.asInstanceOf [ValDef ].rhs
234+ eval(rhs, warm, target.owner.asClass)
235+ else
236+ val error = CallUnknown (target, source, trace)
237+ Result (Hot , error :: Nil )
227238
228239 case Fun (body, thisV, klass) =>
229240 if meth.name == nme.apply then eval(body, thisV, klass)
@@ -246,13 +257,13 @@ class Semantic {
246257 val error = CallCold (ctor, source, trace)
247258 Result (Hot , error :: Nil )
248259
249- case addr : Addr =>
260+ case thisRef : ThisRef =>
261+ Result (Warm (klass, outer = thisRef), noErrors)
262+
263+ case warm : Warm =>
250264 // widen the outer to finitize addresses
251- val outer = if addr.outer.isInstanceOf [Addr ] then addr.copy(outer = Cold ) else addr
252- val addr2 = Addr (klass, outer)
253- if ! heap.contains(addr2) then
254- heap(addr2) = Objekt (klass, Map .empty, Map (klass -> outer))
255- addr2.call(ctor, superType = NoType , source)
265+ val outer = if warm.outer.isInstanceOf [Warm ] then warm.copy(outer = Cold ) else warm
266+ Result (Warm (klass, outer), noErrors)
256267
257268 case Fun (body, thisV, klass) =>
258269 ??? // impossible
@@ -265,19 +276,6 @@ class Semantic {
265276 }
266277 end extension
267278
268- extension (addr : Addr )
269- def updateOuter (klass : ClassSymbol , value : Value ): Unit =
270- val obj = heap(addr)
271- val obj2 = obj.copy(outers = obj.outers.updated(klass, value))
272- heap(addr) = obj2
273-
274- def updateField (field : Symbol , value : Value ): Unit =
275- val obj = heap(addr)
276- val obj2 = obj.copy(fields = obj.fields.updated(field, value))
277- heap(addr) = obj2
278- end extension
279-
280-
281279// ----- Semantic definition --------------------------------
282280
283281 /** Evaluate an expression with the given value for `this` in a given class `klass`
@@ -388,8 +386,8 @@ class Semantic {
388386
389387 case closureDef(ddef) =>
390388 thisV match
391- case addr : Addr =>
392- val value = Fun (ddef.rhs, addr , klass)
389+ case obj : ( ThisRef | Warm ) =>
390+ val value = Fun (ddef.rhs, obj , klass)
393391 Result (value, Nil )
394392 case _ =>
395393 ??? // impossible
@@ -485,12 +483,7 @@ class Semantic {
485483 case tp @ ThisType (tref) =>
486484 if tref.symbol.is(Flags .Package ) then Result (Hot , noErrors)
487485 else
488- val value =
489- thisV match
490- case Hot => Hot
491- case addr : Addr =>
492- resolveThis(tp.classSymbol.asClass, addr, klass)
493- case _ => ???
486+ val value = resolveThis(tp.classSymbol.asClass, thisV, klass)
494487 Result (value, noErrors)
495488
496489 case _ : TermParamRef | _ : RecThis =>
@@ -507,46 +500,50 @@ class Semantic {
507500 if target == klass then thisV
508501 else
509502 thisV match
510- case Hot => Hot
511- case thisV : Addr =>
512- val outer = heap(thisV).outers.getOrElse(klass, Hot )
513- val outerCls = klass.owner.enclosingClass.asClass
514- resolveThis(target, outer, outerCls)
503+ case Hot | _ : ThisRef => Hot
504+ case warm : Warm =>
505+ // use existing type information as a shortcut
506+ val tref = typeRefOf(warm.klass.typeRef.baseType(klass))
507+ if tref.prefix == NoPrefix then
508+ // Current class is local, in the enclosing scope of `warm.klass`
509+ val outerCls = warm.klass.owner.enclosingClass.asClass
510+ resolveThis(target, warm.outer, outerCls)
511+ else
512+ val outerCls = klass.owner.enclosingClass.asClass
513+ val res = cases(tref.prefix, warm.outer, warm.klass.owner.asClass, EmptyTree )
514+ assert(res.errors.isEmpty, " unexpected error " + res)
515+ resolveThis(target, res.value, outerCls)
515516 case _ => ???
516517 }
517518
518519 /** Compute the outer value that correspond to `tref.prefix` */
519520 def outerValue (tref : TypeRef , thisV : Value , klass : ClassSymbol , source : Tree )(using Context , Trace ): Result =
520521 val cls = tref.classSymbol.asClass
521- if ( tref.prefix == NoPrefix ) then
522+ if tref.prefix == NoPrefix then
522523 val enclosing = cls.owner.lexicallyEnclosingClass.asClass
523524 val outerV = resolveThis(enclosing, thisV, klass)
524525 Result (outerV, noErrors)
525526 else
526527 cases(tref.prefix, thisV, klass, source)
527528
528529 /** Initialize part of an abstract object in `klass` of the inheritance chain */
529- def init (klass : ClassSymbol , thisV : Addr )(using Context , Trace ): Result = log(" init " + klass.show, printer, res => res.asInstanceOf [Result ].show) {
530+ def init (klass : ClassSymbol , thisV : ThisRef )(using Context , Trace ): Result = log(" init " + klass.show, printer, res => res.asInstanceOf [Result ].show) {
530531 val errorBuffer = new mutable.ArrayBuffer [Error ]
531532
532533 val tpl = klass.defTree.asInstanceOf [TypeDef ].rhs.asInstanceOf [Template ]
533534
534535 // init param fields
535- var obj = heap(thisV)
536536 klass.paramAccessors.foreach { acc =>
537537 if (! acc.is(Flags .Method )) {
538538 traceIndented(acc.show + " initialized" , printer)
539- obj = obj.copy(fields = obj.fields.updated( acc, Hot ) )
539+ thisV.updateField( acc, Hot )
540540 }
541541 }
542- heap(thisV) = obj
543542
544543 def superCall (tref : TypeRef , ctor : Symbol , source : Tree ): Unit =
545- // update outer for super class
546544 val cls = tref.classSymbol.asClass
547- val res = outerValue(tref, thisV, klass, source)
548- errorBuffer ++= res.errors
549- thisV.updateOuter(cls, res.value)
545+ // update outer for super class
546+ // ignored as they are all hot
550547
551548 // follow constructor
552549 if ! cls.defTree.isEmpty then
0 commit comments