Skip to content

Commit b1d5eae

Browse files
author
EnzeXing
committed
Updates on outerEnvs stored in InstanceBody; address comments
1 parent b44185b commit b1d5eae

File tree

3 files changed

+73
-29
lines changed

3 files changed

+73
-29
lines changed

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

Lines changed: 30 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,8 @@ class Objects(using Context @constructorOnly):
9898
* LocalEnv(meth, ownerObject) // represents environments for methods or functions
9999
* EnvSet ::= Set(LocalEnv)
100100
* InstanceBody ::= (valsMap: Map[Symbol, Value],
101-
outersMap: Map[Symbol, Value],
102-
outerEnvsMap: Map[Symbol, EnvSet]) // represents combined information of all instances represented by a ref
101+
outersMap: Map[ClassSymbol, Value],
102+
outerEnv: EnvSet) // represents combined information of all instances represented by a ref
103103
* Heap ::= Ref -> InstanceBody // heap is mutable
104104
* EnvBody ::= (valsMap: Map[Symbol, Value],
105105
* thisV: Value,
@@ -164,16 +164,14 @@ class Objects(using Context @constructorOnly):
164164
Heap.writeJoinOuter(this, sym, outers)
165165
}
166166

167-
def initOuterEnv(sym: Symbol, outerEnvs: Env.EnvSet)(using Context, Heap.MutableData) =
168-
Heap.writeJoinOuterEnv(this, sym, outerEnvs)
167+
def initOuterEnv(outerEnvs: Env.EnvSet)(using Context, Heap.MutableData) =
168+
Heap.writeJoinOuterEnv(this, outerEnvs)
169169

170170
def outerValue(sym: Symbol)(using Heap.MutableData): Value = Heap.readOuter(this, sym)
171171

172172
def outer(using Heap.MutableData): Value = this.outerValue(klass)
173173

174-
def outerEnvValue(sym: Symbol)(using Heap.MutableData): Env.EnvSet = Heap.readOuterEnv(this, sym)
175-
176-
def outerEnv(using Heap.MutableData): Env.EnvSet = this.outerEnvValue(klass)
174+
def outerEnv(using Heap.MutableData): Env.EnvSet = Heap.readOuterEnv(this)
177175
end Ref
178176

179177
/** A reference to a static object */
@@ -186,7 +184,7 @@ class Objects(using Context @constructorOnly):
186184
def apply(klass: ClassSymbol)(using Context, Heap.MutableData, Trace): ObjectRef =
187185
val obj = new ObjectRef(klass)
188186
obj.initOuter(klass, Bottom)
189-
obj.initOuterEnv(klass, Env.NoEnv)
187+
obj.initOuterEnv(Env.NoEnv)
190188
obj
191189

192190
/**
@@ -208,7 +206,7 @@ class Objects(using Context @constructorOnly):
208206
val owner = State.currentObject
209207
val instance = new OfClass(klass, owner, ctor, summon[Regions.Data])
210208
instance.initOuter(klass, outer)
211-
instance.initOuterEnv(klass, outerEnv)
209+
instance.initOuterEnv(outerEnv)
212210
instance
213211

214212
/**
@@ -239,7 +237,7 @@ class Objects(using Context @constructorOnly):
239237
val arr = new OfArray(owner, regions)
240238
arr.initVal(arr.elementSymbol, Bottom)
241239
arr.initOuter(arr.klass, Bottom)
242-
arr.initOuterEnv(arr.klass, Env.NoEnv)
240+
arr.initOuterEnv(Env.NoEnv)
243241
arr
244242

245243
/**
@@ -499,13 +497,16 @@ class Objects(using Context @constructorOnly):
499497
val outerEnvs = envSet.joinOuterEnvs
500498
if outerEnvs != NoEnv then // Search for the outerEnvs of the current envSet
501499
resolveEnvRecur(target, outerEnvs, bySymbol)
502-
else // Search through the outerEnvs of the instances represented by `this`
500+
else
501+
// Search through the outerEnvs of the instances represented by `this`
502+
// in case that `target` is in outer methods separated by local class definitions
503+
// See `tests/init-global/warn/local-class.scala`
503504
val thisV = envSet.joinThisV
504505
val outerEnvsOfThis = thisV match {
505506
case ref: Ref => ref.outerEnv
506507
case refSet: RefSet => refSet.joinOuterEnvs
507508
}
508-
resolveEnvRecur(target, outerEnvs, bySymbol)
509+
resolveEnvRecur(target, outerEnvsOfThis, bySymbol)
509510
}
510511

511512

@@ -589,13 +590,13 @@ class Objects(using Context @constructorOnly):
589590
private case class InstanceBody(
590591
valsMap: Map[Symbol, Value],
591592
outersMap: Map[Symbol, Value],
592-
outerEnvsMap: Map[Symbol, Env.EnvSet]
593+
outerEnvs: Env.EnvSet
593594
)
594595

595596
private def emptyInstanceBody(): InstanceBody = InstanceBody(
596597
valsMap = Map.empty,
597598
outersMap = Map.empty,
598-
outerEnvsMap = Map.empty
599+
outerEnvs = Env.NoEnv
599600
)
600601

601602
/** Immutable heap data used in the cache.
@@ -619,7 +620,7 @@ class Objects(using Context @constructorOnly):
619620
heap = heap.updated(ref, new InstanceBody(
620621
valsMap = newValsMap,
621622
outersMap = current.outersMap,
622-
outerEnvsMap = current.outerEnvsMap
623+
outerEnvs = current.outerEnvs
623624
))
624625

625626
private[Heap] def writeJoinOuter(ref: Ref, parentSymbol: Symbol, outers: Value): Unit =
@@ -633,21 +634,21 @@ class Objects(using Context @constructorOnly):
633634
heap = heap.updated(ref, new InstanceBody(
634635
valsMap = current.valsMap,
635636
outersMap = newOutersMap,
636-
outerEnvsMap = current.outerEnvsMap
637+
outerEnvs = current.outerEnvs
637638
))
638639

639-
private[Heap] def writeJoinOuterEnv(ref: Ref, parentSymbol: Symbol, outerEnvs: Env.EnvSet): Unit =
640+
private[Heap] def writeJoinOuterEnv(ref: Ref, outerEnvs: Env.EnvSet): Unit =
640641
heap.get(ref) match
641642
case None =>
642643
heap = heap.updated(ref, Heap.emptyInstanceBody())
643-
writeJoinOuterEnv(ref, parentSymbol, outerEnvs)
644+
writeJoinOuterEnv(ref, outerEnvs)
644645

645646
case Some(current) =>
646-
val newOuterEnvsMap = current.outerEnvsMap.join(parentSymbol, outerEnvs)
647+
val newOuterEnvs = current.outerEnvs.join(outerEnvs)
647648
heap = heap.updated(ref, new InstanceBody(
648649
valsMap = current.valsMap,
649650
outersMap = current.outersMap,
650-
outerEnvsMap = newOuterEnvsMap
651+
outerEnvs = newOuterEnvs
651652
))
652653
end MutableData
653654

@@ -668,17 +669,17 @@ class Objects(using Context @constructorOnly):
668669
def readOuter(ref: Ref, parent: Symbol)(using mutable: MutableData): Value =
669670
mutable.heap(ref).outersMap(parent)
670671

671-
def readOuterEnv(ref: Ref, parent: Symbol)(using mutable: MutableData): Env.EnvSet =
672-
mutable.heap(ref).outerEnvsMap(parent)
672+
def readOuterEnv(ref: Ref)(using mutable: MutableData): Env.EnvSet =
673+
mutable.heap(ref).outerEnvs
673674

674675
def writeJoinVal(ref: Ref, valSymbol: Symbol, value: Value)(using mutable: MutableData): Unit =
675676
mutable.writeJoinVal(ref, valSymbol, value)
676677

677678
def writeJoinOuter(ref: Ref, outer: Symbol, outers: Value)(using mutable: MutableData): Unit =
678679
mutable.writeJoinOuter(ref, outer, outers)
679680

680-
def writeJoinOuterEnv(ref: Ref, outer: Symbol, outerEnvs: Env.EnvSet)(using mutable: MutableData): Unit =
681-
mutable.writeJoinOuterEnv(ref, outer, outerEnvs)
681+
def writeJoinOuterEnv(ref: Ref, outerEnvs: Env.EnvSet)(using mutable: MutableData): Unit =
682+
mutable.writeJoinOuterEnv(ref, outerEnvs)
682683

683684
def getHeapData()(using mutable: MutableData): Data = mutable.heap
684685

@@ -1085,7 +1086,7 @@ class Objects(using Context @constructorOnly):
10851086
// See tests/init/pos/Type.scala
10861087
Bottom
10871088

1088-
case Fun(code, thisV, klass, scope) =>
1089+
case Fun(code, thisVOfClosure, klass, scope) =>
10891090
// meth == NoSymbol for poly functions
10901091
if meth.name == nme.tupled then
10911092
value // a call like `fun.tupled`
@@ -1094,11 +1095,11 @@ class Objects(using Context @constructorOnly):
10941095
case ddef: DefDef =>
10951096
if meth.name == nme.apply then
10961097
val funEnv = scope match {
1097-
case ref: Ref => Env.ofDefDef(ddef, args.map(_.value), thisV, Env.NoEnv)
1098-
case env: Env.LocalEnv => Env.ofDefDef(ddef, args.map(_.value), thisV, Env.EnvSet(Set(env)))
1098+
case ref: Ref => Env.ofDefDef(ddef, args.map(_.value), thisVOfClosure, Env.NoEnv)
1099+
case env: Env.LocalEnv => Env.ofDefDef(ddef, args.map(_.value), thisVOfClosure, Env.EnvSet(Set(env)))
10991100
}
11001101
given Scope = funEnv
1101-
extendTrace(code) { eval(ddef.rhs, thisV, klass, cacheResult = true) }
1102+
extendTrace(code) { eval(ddef.rhs, thisVOfClosure, klass, cacheResult = true) }
11021103
else
11031104
// The methods defined in `Any` and `AnyRef` are trivial and don't affect initialization.
11041105
if meth.owner == defn.AnyClass || meth.owner == defn.ObjectClass then
@@ -2121,7 +2122,7 @@ class Objects(using Context @constructorOnly):
21212122
*
21222123
* @param target The class symbol for `C` for which `C.this` is to be resolved.
21232124
* @param thisV The value for `D.this`.
2124-
* @param klass The enclosing class of where `C.this` appears
2125+
* @param klass The enclosing class `D` where `C.this` appears
21252126
* @param elideObjectAccess Whether object access should be omitted.
21262127
*
21272128
* Object access elision happens when the object access is used as a prefix
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
-- Warning: tests/init-global/warn/local-class.scala:2:14 --------------------------------------------------------------
2+
2 | def m() = O.f2 // warn
3+
| ^^^^
4+
| Access uninitialized field value f2. Calling trace:
5+
| ├── object O { [ local-class.scala:5 ]
6+
| │ ^
7+
| ├── val f1 = foo() [ local-class.scala:19 ]
8+
| │ ^^^^^
9+
| ├── def foo(): Int = { [ local-class.scala:6 ]
10+
| │ ^
11+
| ├── val d = new D [ local-class.scala:15 ]
12+
| │ ^^^^^
13+
| ├── class D { [ local-class.scala:8 ]
14+
| │ ^
15+
| ├── val f = bar() [ local-class.scala:13 ]
16+
| │ ^^^^^
17+
| ├── def bar() = { [ local-class.scala:9 ]
18+
| │ ^
19+
| ├── c.m() [ local-class.scala:10 ]
20+
| │ ^^^^^
21+
| └── def m() = O.f2 // warn [ local-class.scala:2 ]
22+
| ^^^^
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
class C {
2+
def m() = O.f2 // warn
3+
}
4+
5+
object O {
6+
def foo(): Int = {
7+
val c = new C
8+
class D {
9+
def bar() = {
10+
c.m()
11+
}
12+
13+
val f = bar()
14+
}
15+
val d = new D
16+
d.f
17+
}
18+
19+
val f1 = foo()
20+
val f2: Int = 5
21+
}

0 commit comments

Comments
 (0)