Skip to content

Commit b9922ce

Browse files
author
EnzeXing
committed
Rewrite resolveEnv and resolveThis
1 parent 248435c commit b9922ce

File tree

5 files changed

+101
-89
lines changed

5 files changed

+101
-89
lines changed

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

Lines changed: 71 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,6 @@ class Objects(using Context @constructorOnly):
129129

130130
def owner: ClassSymbol
131131

132-
def level: Int
133-
134132
def show(using Context): String
135133

136134
def outer(using Heap.MutableData): ScopeSet
@@ -168,8 +166,6 @@ class Objects(using Context @constructorOnly):
168166
case class ObjectRef private (klass: ClassSymbol)(using Trace) extends Ref:
169167
def owner = klass
170168

171-
def level = 1
172-
173169
def show(using Context) = "ObjectRef(" + klass.show + ")"
174170

175171
object ObjectRef:
@@ -184,7 +180,7 @@ class Objects(using Context @constructorOnly):
184180
* Note that the 2nd parameter block does not take part in the definition of equality.
185181
*/
186182
case class OfClass private (
187-
klass: ClassSymbol, owner: ClassSymbol, ctor: Symbol, level: Int, regions: Regions.Data)(using Trace)
183+
klass: ClassSymbol, owner: ClassSymbol, ctor: Symbol, regions: Regions.Data)(using Trace)
188184
extends Ref:
189185
def show(using Context) =
190186
"OfClass(" + klass.show + ", ctor = " + ctor.show + ", owner = " + owner + ")"
@@ -195,7 +191,7 @@ class Objects(using Context @constructorOnly):
195191
using Context, Heap.MutableData, State.Data, Regions.Data, Trace
196192
): OfClass =
197193
val owner = State.currentObject
198-
val instance = new OfClass(klass, owner, ctor, outerScope.level + 1, summon[Regions.Data])
194+
val instance = new OfClass(klass, owner, ctor, summon[Regions.Data])
199195
instance.initOuter(klass, outerScope)
200196
instance
201197

@@ -216,8 +212,6 @@ class Objects(using Context @constructorOnly):
216212

217213
def klass: ClassSymbol = defn.ArrayClass
218214

219-
def level = 1
220-
221215
def show(using Context) = "OfArray(owner = " + owner.show + ")"
222216

223217
def readElement(using Heap.MutableData) = valValue(elementSymbol)
@@ -294,7 +288,6 @@ class Objects(using Context @constructorOnly):
294288

295289
case class ScopeSet(scopes: Set[Scope]):
296290
assert(scopes.forall(_.isRef) || scopes.forall(_.isEnv), "All scopes should have the same type!")
297-
def level: Int = if scopes.isEmpty then 0 else scopes.head.level
298291

299292
def show(using Context) = scopes.map(_.show).mkString("[", ",", "]")
300293

@@ -365,7 +358,7 @@ class Objects(using Context @constructorOnly):
365358
obj
366359
end doCheckObject
367360

368-
def checkObjectAccess(clazz: ClassSymbol)(using data: Data, ctx: Context, pendingTrace: Trace, heap: Heap.MutableData): ObjectRef | Bottom.type =
361+
def checkObjectAccess(clazz: ClassSymbol)(using data: Data, ctx: Context, pendingTrace: Trace, heap: Heap.MutableData): ObjectRef =
369362
val index = data.checkingObjects.indexWhere(_.klass == clazz)
370363

371364
if index != -1 then
@@ -376,7 +369,6 @@ class Objects(using Context @constructorOnly):
376369
val cycle = data.checkingObjects.slice(index, data.checkingObjects.size)
377370
val pos = clazz.defTree.sourcePos.focus
378371
report.warning("Cyclic initialization: " + cycle.map(_.klass.show).mkString(" -> ") + " -> " + clazz.show + ". " + callTrace, pos)
379-
// Bottom
380372
end if
381373
data.checkingObjects(index)
382374
else
@@ -398,10 +390,7 @@ class Objects(using Context @constructorOnly):
398390
*
399391
* For local variables in rhs of class field definitions, the `meth` is the primary constructor.
400392
*/
401-
case class LocalEnv(meth: Symbol, owner: ClassSymbol, level: Int)(using Trace) extends Scope:
402-
if (level > 3)
403-
report.warning("[Internal error] Deeply nested environment, level = " + level + ", " + meth.show + " in " + meth.enclosingClass.show, meth.defTree)
404-
393+
case class LocalEnv(meth: Symbol, owner: ClassSymbol)(using Trace) extends Scope:
405394
def show(using Context) =
406395
"meth: " + meth.show + "\n" +
407396
"owner: " + owner.show
@@ -428,18 +417,24 @@ class Objects(using Context @constructorOnly):
428417

429418
private[Env] def _of(argMap: Map[Symbol, Value], meth: Symbol, outerSet: ScopeSet)
430419
(using State.Data, Heap.MutableData, Trace): LocalEnv =
431-
val env = LocalEnv(meth, State.currentObject, outerSet.level + 1)
420+
val env = LocalEnv(meth, State.currentObject)
432421
argMap.foreach(env.initVal(_, _))
433422
env.initOuter(meth, outerSet)
434423
env
435424

425+
/**
426+
* The main procedure for searching through the outer chain
427+
* @param target The symbol to search for if `bySymbol = true`; otherwise the method symbol of the target environment
428+
* @param scopeSet The set of scopes as starting point
429+
* @return The scopes that contains symbol `target` or whose method is `target`,
430+
* and the value for `C.this` where C is the enclosing class of the result scopes
431+
*/
436432
private[Env] def resolveEnvRecur(
437-
target: Symbol, thisV: ThisValue, scopeSet: ScopeSet, bySymbol: Boolean = true)
433+
target: Symbol, scopeSet: ScopeSet, bySymbol: Boolean = true)
438434
: Contextual[Option[(ThisValue, ScopeSet)]] =
439-
val targetClass = target.owner.lexicallyEnclosingClass.asClass
440-
if scopeSet.level == 0 then // all scopes are NoEnv
441-
None
435+
if scopeSet == Env.NoEnv then None
442436
else
437+
val targetClass = target.owner.lexicallyEnclosingClass.asClass
443438
val head = scopeSet.scopes.head
444439
val filter =
445440
if bySymbol then
@@ -449,20 +444,12 @@ class Objects(using Context @constructorOnly):
449444

450445
assert(filter.isEmpty || filter.size == scopeSet.scopes.size, "Either all scopes or no scopes contain " + target)
451446
if (!filter.isEmpty) then
452-
Some(thisV, ScopeSet(filter))
453-
else if head.isRef then
454-
val currentClass = head.asInstanceOf[Ref].klass
455-
if currentClass == targetClass then
456-
// We have reached the owner class of target but still couldn't find target
457-
None
458-
else
459-
val outerClass = currentClass.owner.lexicallyEnclosingClass.asClass
460-
val outerThis = resolveThis(outerClass, thisV, currentClass)
461-
val outerScopes = scopeSet.scopes.map(_.outer).join
462-
resolveEnvRecur(target, outerThis, outerScopes, bySymbol)
447+
val resultSet = ScopeSet(filter)
448+
val outerThis = resolveThisRecur(targetClass, resultSet)
449+
Some((outerThis, resultSet))
463450
else
464451
val outerScopes = scopeSet.scopes.map(_.outer).join
465-
resolveEnvRecur(target, thisV, outerScopes, bySymbol)
452+
resolveEnvRecur(target, outerScopes, bySymbol)
466453

467454

468455
def ofDefDef(ddef: DefDef, args: List[Value], outer: ScopeSet)
@@ -508,7 +495,7 @@ class Objects(using Context @constructorOnly):
508495
*/
509496
def resolveEnvByValue(target: Symbol, thisV: ThisValue, scope: Scope)
510497
(using Context, Heap.MutableData): Contextual[Option[(ThisValue, ScopeSet)]] = log("Resolving env by value for " + target.show + ", this = " + thisV.show + ", scope = " + scope.show, printer) {
511-
resolveEnvRecur(target, thisV, ScopeSet(Set(scope)))
498+
resolveEnvRecur(target, ScopeSet(Set(scope)))
512499
}
513500

514501
/**
@@ -527,9 +514,11 @@ class Objects(using Context @constructorOnly):
527514
*
528515
* @return the environment and value for `this` owned by the given method.
529516
*/
530-
def resolveEnvByMethod(enclosing: Symbol, thisV: ThisValue, scope: Scope)(using Context, Heap.MutableData): Contextual[Option[(ThisValue, ScopeSet)]] = log("Resolving env which corresponds to method " + enclosing.show + ", this = " + thisV.show + ", scope = " + scope.show, printer) {
517+
def resolveEnvByMethod(enclosing: Symbol, thisV: ThisValue, scope: Scope)(using Context, Heap.MutableData): Contextual[(ThisValue, ScopeSet)] = log("Resolving env which corresponds to method " + enclosing.show + ", this = " + thisV.show + ", scope = " + scope.show, printer) {
531518
assert(enclosing.is(Flags.Method), "Only method symbols allows, got " + enclosing.show)
532-
resolveEnvRecur(enclosing, thisV, ScopeSet(Set(scope)), bySymbol = false)
519+
val result = resolveEnvRecur(enclosing, ScopeSet(Set(scope)), bySymbol = false)
520+
assert(!result.isEmpty, "Failed to find environment for " + enclosing + "!")
521+
result.get
533522
}
534523

535524
def withEnv[T](env: LocalEnv)(fn: LocalEnv ?=> T): T = fn(using env)
@@ -737,9 +726,7 @@ class Objects(using Context @constructorOnly):
737726

738727
given Join[ScopeSet] with
739728
extension (a: ScopeSet)
740-
def join(b: ScopeSet): ScopeSet =
741-
assert(a.level == b.level, "Invalid join on scopes!")
742-
ScopeSet(a.scopes ++ b.scopes)
729+
def join(b: ScopeSet): ScopeSet = ScopeSet(a.scopes ++ b.scopes)
743730

744731
extension (values: Iterable[Value])
745732
def join: Value =
@@ -857,7 +844,6 @@ class Objects(using Context @constructorOnly):
857844
SafeValue(defn.IntType)
858845

859846
case ref: Ref =>
860-
val isLocal = !meth.owner.isClass
861847
val target =
862848
if !needResolve then
863849
meth
@@ -880,14 +866,13 @@ class Objects(using Context @constructorOnly):
880866
val cls = target.owner.enclosingClass.asClass
881867
val ddef = target.defTree.asInstanceOf[DefDef]
882868
val meth = ddef.symbol
883-
val enclosingMethod = meth.owner.enclosingMethod
884-
val resolveResult =
885-
if enclosingMethod == cls.primaryConstructor then // meth is top-level method, outer is a ref
886-
Some(ref, ScopeSet(Set(ref)))
869+
val (thisV : ThisValue, outerEnv) =
870+
if meth.owner.enclosingMethod == cls.primaryConstructor then
871+
// meth is top-level method, outer is a ref
872+
(ref, ScopeSet(Set(ref)))
887873
else
874+
val enclosingMethod = meth.owner.enclosingMethod
888875
Env.resolveEnvByMethod(enclosingMethod, ref, summon[Scope])
889-
assert(!resolveResult.isEmpty, "Cannot find environment for method " + meth.show + "!" + Trace.show)
890-
val (thisV : ThisValue, outerEnv) = resolveResult.get
891876

892877
val env2 = Env.ofDefDef(ddef, args.map(_.value), outerEnv)
893878
extendTrace(ddef) {
@@ -1132,7 +1117,7 @@ class Objects(using Context @constructorOnly):
11321117
arr
11331118
else
11341119
// Widen the outer to finitize the domain. Arguments already widened in `evalArgs`.
1135-
val envWidened =
1120+
val envWidened: ScopeSet =
11361121
outer match
11371122
case Package(_) => // For top-level classes
11381123
Env.NoEnv
@@ -1141,13 +1126,15 @@ class Objects(using Context @constructorOnly):
11411126
report.warning("[Internal error] top-level class should have `Package` as outer, class = " + klass.show + ", outer = " + outer.show + ", " + Trace.show, Trace.position)
11421127
Env.NoEnv
11431128
else
1144-
val enclosingMethod = klass.owner.enclosingMethod
1145-
val outerCls = outer.asInstanceOf[Ref].klass
1129+
val outerCls = klass.owner.enclosingClass.asClass
11461130
// When `klass` is directly nested in `outerCls`, `outerCls`.enclosingMethod returns its primary constructor
1147-
if enclosingMethod == outerCls.primaryConstructor then
1148-
ScopeSet(Set(outer.asInstanceOf[Ref]))
1131+
if klass.owner.enclosingMethod == outerCls.primaryConstructor then
1132+
// Don't use the parameter `outer` as the outer value, but uses `outerCls.this`
1133+
// This eliminates infinite outer chain caused by inner classes extending outer classes.
1134+
// See `inner-extends-outer.scala`
1135+
resolveThis(outerCls, outer).toScopeSet
11491136
else
1150-
Env.resolveEnvByMethod(klass.owner.enclosingMethod, outer, summon[Scope]).getOrElse(UnknownValue -> Env.NoEnv)._2
1137+
Env.resolveEnvByMethod(klass.owner.enclosingMethod, outer, summon[Scope])._2
11511138

11521139
val instance = OfClass(klass, envWidened, ctor)
11531140
callConstructor(instance, ctor, args)
@@ -1249,7 +1236,7 @@ class Objects(using Context @constructorOnly):
12491236
// -------------------------------- algorithm --------------------------------
12501237

12511238
/** Check an individual object */
1252-
private def accessObject(classSym: ClassSymbol)(using Context, State.Data, Trace, Heap.MutableData): ObjectRef | Bottom.type = log("accessing " + classSym.show, printer, (_: Value).show) {
1239+
private def accessObject(classSym: ClassSymbol)(using Context, State.Data, Trace, Heap.MutableData): ObjectRef = log("accessing " + classSym.show, printer, (_: Value).show) {
12531240
if classSym.hasSource then
12541241
State.checkObjectAccess(classSym)
12551242
else
@@ -1369,7 +1356,7 @@ class Objects(using Context @constructorOnly):
13691356
case TermRef(NoPrefix, _) =>
13701357
// resolve this for the local method
13711358
val enclosingClass = id.symbol.owner.enclosingClass.asClass
1372-
val thisValue2 = extendTrace(ref) { resolveThis(enclosingClass, thisV, klass) }
1359+
val thisValue2 = extendTrace(ref) { resolveThis(enclosingClass, thisV) }
13731360
// local methods are not a member, but we can reuse the method `call`
13741361
withTrace(trace2) { call(thisValue2, id.symbol, args, receiver = NoType, superType = NoType, needResolve = false) }
13751362
case TermRef(prefix, _) =>
@@ -1386,7 +1373,7 @@ class Objects(using Context @constructorOnly):
13861373
case OuterSelectName(_, _) =>
13871374
val current = qualifier.tpe.classSymbol
13881375
val target = expr.tpe.widenSingleton.classSymbol.asClass
1389-
withTrace(trace2) { resolveThis(target, qual, current.asClass) }
1376+
withTrace(trace2) { resolveThis(target, qual) }
13901377
case _ =>
13911378
withTrace(trace2) { select(qual, expr.symbol, receiver = qualifier.tpe) }
13921379

@@ -1779,7 +1766,7 @@ class Objects(using Context @constructorOnly):
17791766
accessObject(sym.moduleClass.asClass)
17801767

17811768
else
1782-
resolveThis(tref.classSymbol.asClass, thisV, klass)
1769+
resolveThis(tref.classSymbol.asClass, thisV)
17831770

17841771
case _ =>
17851772
throw new Exception("unexpected type: " + tp + ", Trace:\n" + Trace.show)
@@ -1931,50 +1918,52 @@ class Objects(using Context @constructorOnly):
19311918
}
19321919

19331920

1934-
/** Resolve C.this that appear in `klass`
1921+
/** Resolve C.this by recursively searching through the outer chain
1922+
* @param target The class symbol for `C` for which `C.this` is to be resolved.
1923+
* @param scopeSet The scopes as the starting point.
1924+
*/
1925+
def resolveThisRecur(target: ClassSymbol, scopeSet: ScopeSet): Contextual[ValueSet] =
1926+
if scopeSet == Env.NoEnv then
1927+
Bottom
1928+
else
1929+
val head = scopeSet.scopes.head
1930+
if head.isInstanceOf[Ref] then
1931+
val klass = head.asInstanceOf[Ref].klass
1932+
assert(scopeSet.scopes.forall(_.asInstanceOf[Ref].klass == klass), "Multiple possible outer class?")
1933+
if klass == target then
1934+
scopeSet.toValueSet
1935+
else
1936+
resolveThisRecur(target, scopeSet.scopes.map(_.outer).join)
1937+
else
1938+
resolveThisRecur(target, scopeSet.scopes.map(_.outer).join)
1939+
1940+
/** Resolve C.this that appear in `D.this`
19351941
*
19361942
* @param target The class symbol for `C` for which `C.this` is to be resolved.
1937-
* @param thisV The value for `D.this` where `D` is represented by the parameter `klass`.
1938-
* @param klass The enclosing class where the type `C.this` is located.
1943+
* @param thisV The value for `D.this`.
19391944
* @param elideObjectAccess Whether object access should be omitted.
19401945
*
19411946
* Object access elision happens when the object access is used as a prefix
19421947
* in `new o.C` and `C` does not need an outer.
19431948
*/
1944-
def resolveThis(target: ClassSymbol, thisV: Value, klass: ClassSymbol, elideObjectAccess: Boolean = false): Contextual[ThisValue] = log("resolveThis target = " + target.show + ", this = " + thisV.show, printer, (_: Value).show) {
1945-
def recur(scopeSet: ScopeSet): ThisValue =
1946-
if scopeSet == Env.NoEnv then
1947-
Bottom
1948-
else
1949-
val head = scopeSet.scopes.head
1950-
if head.isInstanceOf[Ref] then
1951-
val klass = head.asInstanceOf[Ref].klass
1952-
assert(scopeSet.scopes.forall(_.asInstanceOf[Ref].klass == klass), "Multiple possible outer class?")
1953-
if klass == target then
1954-
scopeSet.toValueSet
1955-
else
1956-
recur(scopeSet.scopes.map(_.outer).join)
1957-
else
1958-
recur(scopeSet.scopes.map(_.outer).join)
1959-
end recur
1960-
1949+
def resolveThis(target: ClassSymbol, thisV: Value, elideObjectAccess: Boolean = false): Contextual[ValueSet] = log("resolveThis target = " + target.show + ", this = " + thisV.show, printer, (_: Value).show) {
19611950
if target.is(Flags.Package) then
1962-
val error = "[Internal error] target cannot be packages, target = " + target + ", klass = " + klass + Trace.show
1951+
val error = "[Internal error] target cannot be packages, target = " + target + Trace.show
19631952
report.warning(error, Trace.position)
19641953
Bottom
19651954
else if target.isStaticObject then
19661955
val res = ObjectRef(target.moduleClass.asClass)
1967-
if elideObjectAccess then res
1968-
else accessObject(target)
1956+
if elideObjectAccess then ValueSet(Set(res))
1957+
else ValueSet(Set(accessObject(target)))
19691958
else
19701959
thisV match
19711960
case Bottom => Bottom
19721961
case ref: Ref =>
1973-
recur(ScopeSet(Set(ref)))
1962+
resolveThisRecur(target, ScopeSet(Set(ref)))
19741963
case vs: ValueSet if vs.isRefSet =>
1975-
recur(vs.toScopeSet)
1964+
resolveThisRecur(target, vs.toScopeSet)
19761965
case _ =>
1977-
report.warning("[Internal error] unexpected thisV = " + thisV + ", target = " + target.show + ", klass = " + klass.show + Trace.show, Trace.position)
1966+
report.warning("[Internal error] unexpected thisV = " + thisV + ", target = " + target.show + Trace.show, Trace.position)
19781967
Bottom
19791968
}
19801969

@@ -1988,7 +1977,7 @@ class Objects(using Context @constructorOnly):
19881977
val cls = tref.classSymbol.asClass
19891978
if tref.prefix == NoPrefix then
19901979
val enclosing = cls.owner.lexicallyEnclosingClass.asClass
1991-
resolveThis(enclosing, thisV, klass, elideObjectAccess = cls.isStatic)
1980+
resolveThis(enclosing, thisV, elideObjectAccess = cls.isStatic)
19921981
else
19931982
if cls.isAllOf(Flags.JavaInterface) then Bottom
19941983
else evalType(tref.prefix, thisV, klass, elideObjectAccess = cls.isStatic)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
class Outer {
2+
val f = 5
3+
class Inner extends Outer {
4+
val g = Outer.this.f
5+
}
6+
}
7+
8+
object O {
9+
def foo(i: Outer): Unit =
10+
val i2 = new i.Inner // i2.outer should always be OfClass(Outer)
11+
foo(i2)
12+
13+
foo(new Outer)
14+
}

tests/init-global/pos/local-class.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,5 @@ class Outer {
1818

1919
object O {
2020
val c = new Outer
21+
val d: Object = c.foo
2122
}

0 commit comments

Comments
 (0)