Skip to content

Commit 3ae39e3

Browse files
author
EnzeXing
committed
Refactor type hierarchy for abstract value
1 parent f1b2c96 commit 3ae39e3

File tree

1 file changed

+62
-46
lines changed

1 file changed

+62
-46
lines changed

compiler/src/dotty/tools/dotc/transform/init/Objects.scala

Lines changed: 62 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,10 @@ object Objects:
6969
/** Syntax for the data structure abstraction used in abstract domain:
7070
*
7171
* ve ::= ObjectRef(class)
72-
* | OfClass(class, vs[outer], ctor, args, env)
73-
* | OfArray(object[owner], regions)
74-
* | Fun(..., env)
75-
* | Cold // abstract values in domain
72+
* | OfClass(class, vs[outer], ctor, args, env)
73+
* | OfArray(object[owner], regions)
74+
* | Fun(..., env)
75+
* | Cold // abstract values in domain
7676
* vs ::= Set(ve) // set of abstract values
7777
*
7878
* refMap = ( ObjectRef | OfClass ) -> ( valsMap, varsMap, outersMap ) // refMap stores field informations of an object or instance
@@ -87,25 +87,29 @@ object Objects:
8787
* env = (valsMap, Option[env]) // stores local variables in the residing method, and possibly outer environments
8888
*
8989
* addr ::= localVarAddr(regions, valsym, owner)
90-
* | fieldVarAddr(regions, valsym, owner) // independent of OfClass/ObjectRef
91-
* | arrayAddr(regions, owner) // independent of array element type
90+
* | fieldVarAddr(regions, valsym, owner) // independent of OfClass/ObjectRef
91+
* | arrayAddr(regions, owner) // independent of array element type
9292
*
9393
* regions ::= List(sourcePosition)
9494
*/
9595

9696
sealed abstract class Value:
9797
def show(using Context): String
9898

99+
/** ValueElement are elements that can be contained in a RefSet */
99100
sealed abstract class ValueElement extends Value
100101

102+
sealed trait ThisValue extends Value:
103+
def widenThisV(height : Int)(using Context) : ThisValue = this
104+
101105
/**
102106
* A reference caches the values for outers and immutable fields.
103107
*/
104108
sealed abstract class Ref(
105109
valsMap: mutable.Map[Symbol, Value],
106110
varsMap: mutable.Map[Symbol, Heap.Addr],
107111
outersMap: mutable.Map[ClassSymbol, Value])
108-
extends ValueElement:
112+
extends ValueElement with ThisValue:
109113
protected val vals: mutable.Map[Symbol, Value] = valsMap
110114
protected val vars: mutable.Map[Symbol, Heap.Addr] = varsMap
111115
protected val outers: mutable.Map[ClassSymbol, Value] = outersMap
@@ -162,6 +166,12 @@ object Objects:
162166
def widenedCopy(outer: Value, args: List[Value], env: Env.Data): OfClass =
163167
new OfClass(klass, outer, ctor, args, env)(this.valsMap, this.varsMap, this.outersMap)
164168

169+
override def widenThisV(height: Int)(using Context): ThisValue =
170+
val outer2 = outer.widen(height - 1)
171+
val args2 = args.map(_.widen(height - 1))
172+
val env2 = env.widen(height - 1)
173+
widenedCopy(outer2, args2, env2)
174+
165175
def show(using Context) =
166176
val valFields = vals.map(_.show + " -> " + _.show)
167177
"OfClass(" + klass.show + ", outer = " + outer + ", args = " + args.map(_.show) + ", vals = " + valFields + ")"
@@ -198,24 +208,29 @@ object Objects:
198208
/**
199209
* Represents a lambda expression
200210
*/
201-
case class Fun(code: Tree, thisV: ValueElement, klass: ClassSymbol, env: Env.Data) extends Value:
211+
case class Fun(code: Tree, thisV: ThisValue, klass: ClassSymbol, env: Env.Data) extends ValueElement:
202212
def show(using Context) = "Fun(" + code.show + ", " + thisV.show + ", " + klass.show + ")"
203213

204214
/**
205215
* Represents a set of values
206216
*
207217
* It comes from `if` expressions.
208218
*/
209-
case class RefSet(refs: ListSet[Value]) extends Value:
210-
assert(refs.forall(!_.isInstanceOf[RefSet]))
219+
case class RefSet(refs: ListSet[ValueElement]) extends Value:
211220
def show(using Context) = refs.map(_.show).mkString("[", ",", "]")
212221

213-
/** A cold alias which should not be used during initialization. */
214-
case object Cold extends ValueElement:
222+
/** A cold alias which should not be used during initialization.
223+
*
224+
* Cold is not ValueElement since RefSet containing Cold is equivalent to Cold
225+
*/
226+
case object Cold extends Value with ThisValue:
215227
def show(using Context) = "Cold"
216228

217229
val Bottom = RefSet(ListSet.empty)
218230

231+
// type ThisValue = Ref | Cold // used for typing thisV, whose value can only be Ref or Cold
232+
// type VEOrCold = ValueElement | Cold
233+
219234
/** Checking state */
220235
object State:
221236
class Data:
@@ -411,7 +426,7 @@ object Objects:
411426
*
412427
* @return the environment and value for `this` owned by the given method.
413428
*/
414-
def resolveEnv(meth: Symbol, thisV: Value, env: Data)(using Context): Option[(Value, Data)] = log("Resolving env for " + meth.show + ", this = " + thisV.show + ", env = " + env.show, printer) {
429+
def resolveEnv(meth: Symbol, thisV: ThisValue, env: Data)(using Context): Option[(ThisValue, Data)] = log("Resolving env for " + meth.show + ", this = " + thisV.show + ", env = " + env.show, printer) {
415430
env match
416431
case localEnv: LocalEnv =>
417432
if localEnv.meth == meth then Some(thisV -> env)
@@ -420,7 +435,11 @@ object Objects:
420435
// TODO: handle RefSet
421436
thisV match
422437
case ref: OfClass =>
423-
resolveEnv(meth, ref.outer, ref.env)
438+
ref.outer match
439+
case outer : ThisValue =>
440+
resolveEnv(meth, outer, ref.env)
441+
case _ => // This is the case for top-level classes and local classes
442+
None
424443
case _ =>
425444
None
426445
}
@@ -494,7 +513,7 @@ object Objects:
494513
val config = Config(thisV, summon[Env.Data], Heap.getHeapData())
495514
super.get(config, expr).map(_.value)
496515

497-
def cachedEval(thisV: ValueElement, expr: Tree, cacheResult: Boolean)(fun: Tree => Value)(using Heap.MutableData, Env.Data): Value =
516+
def cachedEval(thisV: ThisValue, expr: Tree, cacheResult: Boolean)(fun: Tree => Value)(using Heap.MutableData, Env.Data): Value =
498517
val config = Config(thisV, summon[Env.Data], Heap.getHeapData())
499518
val result = super.cachedEval(config, expr, cacheResult, default = Res(Bottom, Heap.getHeapData())) { expr =>
500519
Res(fun(expr), Heap.getHeapData())
@@ -551,14 +570,14 @@ object Objects:
551570
extension (a: Value)
552571
def join(b: Value): Value =
553572
(a, b) match
554-
case (Cold, b) => Cold
555-
case (a, Cold) => Cold
556-
case (Bottom, b) => b
557-
case (a, Bottom) => a
558-
case (RefSet(refs1), RefSet(refs2)) => RefSet(refs1 ++ refs2)
559-
case (a, RefSet(refs)) => RefSet(refs + a)
560-
case (RefSet(refs), b) => RefSet(refs + b)
561-
case (a, b) => RefSet(ListSet(a, b))
573+
case (Cold, b) => Cold
574+
case (a, Cold) => Cold
575+
case (Bottom, b) => b
576+
case (a, Bottom) => a
577+
case (RefSet(refs1), RefSet(refs2)) => RefSet(refs1 ++ refs2)
578+
case (a : ValueElement, RefSet(refs)) => RefSet(refs + a)
579+
case (RefSet(refs), b : ValueElement) => RefSet(refs + b)
580+
case (a : ValueElement, b : ValueElement) => RefSet(ListSet(a, b))
562581

563582
def widen(height: Int)(using Context): Value =
564583
if height == 0 then Cold
@@ -570,13 +589,10 @@ object Objects:
570589
refs.map(ref => ref.widen(height)).join
571590

572591
case Fun(code, thisV, klass, env) =>
573-
Fun(code, thisV.widen(height).asInstanceOf[ValueElement], klass, env.widen(height))
592+
Fun(code, thisV.widenThisV(height), klass, env.widen(height))
574593

575-
case ref @ OfClass(klass, outer, _, args, env) =>
576-
val outer2 = outer.widen(height - 1)
577-
val args2 = args.map(_.widen(height - 1))
578-
val env2 = env.widen(height - 1)
579-
ref.widenedCopy(outer2, args2, env2)
594+
case ref : Ref =>
595+
ref.widenThisV(height)
580596

581597
case _ => a
582598

@@ -641,7 +657,7 @@ object Objects:
641657
val ddef = target.defTree.asInstanceOf[DefDef]
642658
val meth = ddef.symbol
643659

644-
val (thisV, outerEnv) =
660+
val (thisV : ThisValue, outerEnv) =
645661
if meth.owner.isClass then
646662
(ref, Env.NoEnv)
647663
else
@@ -653,7 +669,7 @@ object Objects:
653669
// eval(ddef.rhs, ref, cls, cacheResult = true)
654670
cache.cachedEval(ref, ddef.rhs, cacheResult = true) { expr =>
655671
Returns.installHandler(meth)
656-
val res = cases(expr, thisV.asInstanceOf[ValueElement], cls)
672+
val res = cases(expr, thisV, cls)
657673
val returns = Returns.popHandler(meth)
658674
res.join(returns)
659675
}
@@ -692,13 +708,13 @@ object Objects:
692708

693709
/** Handle constructor calls `<init>(args)`.
694710
*
695-
* @param thisV The value for the receiver.
711+
* @param value The value for the receiver.
696712
* @param ctor The symbol of the target method.
697713
* @param args Arguments of the constructor call (all parameter blocks flatten to a list).
698714
*/
699-
def callConstructor(thisV: Value, ctor: Symbol, args: List[ArgInfo]): Contextual[Value] = log("call " + ctor.show + ", args = " + args.map(_.value.show), printer, (_: Value).show) {
715+
def callConstructor(value: Value, ctor: Symbol, args: List[ArgInfo]): Contextual[Value] = log("call " + ctor.show + ", args = " + args.map(_.value.show), printer, (_: Value).show) {
700716

701-
thisV match
717+
value match
702718
case ref: Ref =>
703719
if ctor.hasSource then
704720
val cls = ctor.owner.enclosingClass.asClass
@@ -720,7 +736,7 @@ object Objects:
720736
Bottom
721737

722738
case _ =>
723-
report.warning("[Internal error] unexpected constructor call, meth = " + ctor + ", this = " + thisV + Trace.show, Trace.position)
739+
report.warning("[Internal error] unexpected constructor call, meth = " + ctor + ", this = " + value + Trace.show, Trace.position)
724740
Bottom
725741
}
726742

@@ -850,7 +866,7 @@ object Objects:
850866
(outer.widen(1), Env.NoEnv)
851867
else
852868
// klass.enclosingMethod returns its primary constructor
853-
Env.resolveEnv(klass.owner.enclosingMethod, outer.asInstanceOf[ValueElement], summon[Env.Data]).getOrElse(Cold -> Env.NoEnv)
869+
Env.resolveEnv(klass.owner.enclosingMethod, outer.asInstanceOf[ThisValue], summon[Env.Data]).getOrElse(Cold -> Env.NoEnv)
854870

855871
val instance = OfClass(klass, outerWidened, ctor, args.map(_.value), envWidened)
856872
callConstructor(instance, ctor, args)
@@ -880,7 +896,7 @@ object Objects:
880896
* @param thisV The value for `this` where the variable is used.
881897
* @param sym The symbol of the variable.
882898
*/
883-
def readLocal(thisV: ValueElement, sym: Symbol): Contextual[Value] = log("reading local " + sym.show, printer, (_: Value).show) {
899+
def readLocal(thisV: ThisValue, sym: Symbol): Contextual[Value] = log("reading local " + sym.show, printer, (_: Value).show) {
884900
def isByNameParam(sym: Symbol) = sym.is(Flags.Param) && sym.info.isInstanceOf[ExprType]
885901
Env.resolveEnv(sym.enclosingMethod, thisV, summon[Env.Data]) match
886902
case Some(thisV -> env) =>
@@ -934,7 +950,7 @@ object Objects:
934950
* @param sym The symbol of the variable.
935951
* @param value The value of the rhs of the assignment.
936952
*/
937-
def writeLocal(thisV: ValueElement, sym: Symbol, value: Value): Contextual[Value] = log("write local " + sym.show + " with " + value.show, printer, (_: Value).show) {
953+
def writeLocal(thisV: ThisValue, sym: Symbol, value: Value): Contextual[Value] = log("write local " + sym.show + " with " + value.show, printer, (_: Value).show) {
938954

939955
assert(sym.is(Flags.Mutable), "Writing to immutable variable " + sym.show)
940956
Env.resolveEnv(sym.enclosingMethod, thisV, summon[Env.Data]) match
@@ -955,7 +971,7 @@ object Objects:
955971
// -------------------------------- algorithm --------------------------------
956972

957973
/** Check an individual object */
958-
private def accessObject(classSym: ClassSymbol)(using Context, State.Data, Trace): Value = log("accessing " + classSym.show, printer, (_: Value).show) {
974+
private def accessObject(classSym: ClassSymbol)(using Context, State.Data, Trace): ObjectRef = log("accessing " + classSym.show, printer, (_: Value).show) {
959975
if classSym.hasSource then
960976
State.checkObjectAccess(classSym)
961977
else
@@ -992,13 +1008,13 @@ object Objects:
9921008
* @param klass The enclosing class where the expression is located.
9931009
* @param cacheResult It is used to reduce the size of the cache.
9941010
*/
995-
def eval(expr: Tree, thisV: ValueElement, klass: ClassSymbol, cacheResult: Boolean = false): Contextual[Value] = log("evaluating " + expr.show + ", this = " + thisV.show + ", regions = " + Regions.show + " in " + klass.show, printer, (_: Value).show) {
1011+
def eval(expr: Tree, thisV: ThisValue, klass: ClassSymbol, cacheResult: Boolean = false): Contextual[Value] = log("evaluating " + expr.show + ", this = " + thisV.show + ", regions = " + Regions.show + " in " + klass.show, printer, (_: Value).show) {
9961012
cache.cachedEval(thisV, expr, cacheResult) { expr => cases(expr, thisV, klass) }
9971013
}
9981014

9991015

10001016
/** Evaluate a list of expressions */
1001-
def evalExprs(exprs: List[Tree], thisV: ValueElement, klass: ClassSymbol): Contextual[List[Value]] =
1017+
def evalExprs(exprs: List[Tree], thisV: ThisValue, klass: ClassSymbol): Contextual[List[Value]] =
10021018
exprs.map { expr => eval(expr, thisV, klass) }
10031019

10041020
/** Handles the evaluation of different expressions
@@ -1009,7 +1025,7 @@ object Objects:
10091025
* @param thisV The value for `C.this` where `C` is represented by the parameter `klass`.
10101026
* @param klass The enclosing class where the expression `expr` is located.
10111027
*/
1012-
def cases(expr: Tree, thisV: ValueElement, klass: ClassSymbol): Contextual[Value] = log("evaluating " + expr.show + ", this = " + thisV.show + " in " + klass.show, printer, (_: Value).show) {
1028+
def cases(expr: Tree, thisV: ThisValue, klass: ClassSymbol): Contextual[Value] = log("evaluating " + expr.show + ", this = " + thisV.show + " in " + klass.show, printer, (_: Value).show) {
10131029
val trace2 = trace.add(expr)
10141030

10151031
expr match
@@ -1211,7 +1227,7 @@ object Objects:
12111227
* Object access elission happens when the object access is used as a prefix
12121228
* in `new o.C` and `C` does not need an outer.
12131229
*/
1214-
def evalType(tp: Type, thisV: ValueElement, klass: ClassSymbol, elideObjectAccess: Boolean = false): Contextual[Value] = log("evaluating " + tp.show, printer, (_: Value).show) {
1230+
def evalType(tp: Type, thisV: ThisValue, klass: ClassSymbol, elideObjectAccess: Boolean = false): Contextual[Value] = log("evaluating " + tp.show, printer, (_: Value).show) {
12151231
tp match
12161232
case _: ConstantType =>
12171233
Bottom
@@ -1261,7 +1277,7 @@ object Objects:
12611277
}
12621278

12631279
/** Evaluate arguments of methods and constructors */
1264-
def evalArgs(args: List[Arg], thisV: ValueElement, klass: ClassSymbol): Contextual[List[ArgInfo]] =
1280+
def evalArgs(args: List[Arg], thisV: ThisValue, klass: ClassSymbol): Contextual[List[ArgInfo]] =
12651281
val argInfos = new mutable.ArrayBuffer[ArgInfo]
12661282
args.foreach { arg =>
12671283
val res =
@@ -1297,7 +1313,7 @@ object Objects:
12971313
* @param thisV The value of the current object to be initialized.
12981314
* @param klass The class to which the template belongs.
12991315
*/
1300-
def init(tpl: Template, thisV: Ref, klass: ClassSymbol): Contextual[Value] = log("init " + klass.show, printer, (_: Value).show) {
1316+
def init(tpl: Template, thisV: Ref, klass: ClassSymbol): Contextual[Ref] = log("init " + klass.show, printer, (_: Value).show) {
13011317
val paramsMap = tpl.constr.termParamss.flatten.map { vdef =>
13021318
vdef.name -> Env.valValue(vdef.symbol)
13031319
}.toMap
@@ -1461,7 +1477,7 @@ object Objects:
14611477
* @param thisV The value for `C.this` where `C` is represented by the parameter `klass`.
14621478
* @param klass The enclosing class where the type `tref` is located.
14631479
*/
1464-
def outerValue(tref: TypeRef, thisV: ValueElement, klass: ClassSymbol): Contextual[Value] =
1480+
def outerValue(tref: TypeRef, thisV: ThisValue, klass: ClassSymbol): Contextual[Value] =
14651481
val cls = tref.classSymbol.asClass
14661482
if tref.prefix == NoPrefix then
14671483
val enclosing = cls.owner.lexicallyEnclosingClass.asClass

0 commit comments

Comments
 (0)