Skip to content

Commit 662da71

Browse files
committed
More fine-grained analysis for inner classes of static objects
1 parent 8af6f21 commit 662da71

File tree

7 files changed

+68
-13
lines changed

7 files changed

+68
-13
lines changed

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

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,14 @@ object Checking {
8989
} error.issue
9090
}
9191

92+
/** Whether it's known to be safe to access an object */
93+
private def isObjectAccessSafe(obj: Global)(using state: State): Boolean =
94+
obj.moduleClass == state.thisClass
95+
&& (obj.enclosingClass == state.thisClass
96+
|| obj.appearsInside(state.thisClass) && state.superConstrCalled)
97+
98+
// ------- checking construtor ---------------------------
99+
92100
/** Check that the given concrete class may be initialized safely
93101
*
94102
* It assumes that all definitions are properly summarized before-hand.
@@ -197,6 +205,8 @@ object Checking {
197205
tpl.body.foreach { checkClassBodyStat(_) }
198206
}
199207

208+
// ------- checking effects ---------------------------
209+
200210
private def checkMethodCall(eff: MethodCall)(using state: State): Errors =
201211
val MethodCall(pot, sym) = eff
202212
pot match {
@@ -234,12 +244,18 @@ object Checking {
234244

235245
case hot: Hot =>
236246
val target = resolve(hot.classSymbol, sym)
237-
state.dependencies += StaticCall(hot.classSymbol, target)(state.path)
238-
Errors.empty
247+
if (target.hasSource && hot.classSymbol.owner == state.thisClass) { // for inner classes of objects
248+
val effs = hot.effectsOf(target).toList
249+
effs.flatMap { check(_) }
250+
}
251+
else {
252+
state.dependencies += StaticCall(hot.classSymbol, target)(state.path)
253+
Errors.empty
254+
}
239255

240256
case obj: Global =>
241257
val target = resolve(obj.moduleClass, sym)
242-
if obj.enclosingClass == state.thisClass && obj.moduleClass == state.thisClass then
258+
if isObjectAccessSafe(obj) then
243259
check(MethodCall(ThisRef()(obj.source), target)(eff.source))
244260
else
245261
state.dependencies += StaticCall(obj.moduleClass, target)(state.path)
@@ -291,7 +307,7 @@ object Checking {
291307
// or all fields are already initialized
292308
val target = resolve(obj.moduleClass, field)
293309
if (target.is(Flags.Lazy)) check(MethodCall(obj, target)(eff.source))
294-
else if obj.enclosingClass == state.thisClass && obj.moduleClass == state.thisClass then
310+
else if isObjectAccessSafe(obj) then
295311
check(FieldAccess(ThisRef()(obj.source), target)(eff.source))
296312
else Errors.empty
297313

@@ -390,12 +406,12 @@ object Checking {
390406

391407
private def checkAccessGlobal(eff: AccessGlobal)(using state: State): Errors =
392408
val obj = eff.potential
393-
if obj.moduleClass != state.thisClass
394-
|| obj.enclosingClass != state.thisClass && !state.superConstrCalled
395-
then
409+
if !isObjectAccessSafe(obj) then
396410
state.dependencies += ObjectAccess(obj.symbol)(state.path)
397411
Errors.empty
398412

413+
// ------- expansion of potentials ---------------------------
414+
399415
private def expand(pot: Potential)(using state: State): Summary = trace("expand " + pot.show, init, _.asInstanceOf[Summary].show) {
400416
pot match {
401417
case MethodReturn(pot1, sym) =>

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import reporting._
1313
import config.Printers.init
1414
import ast.tpd._
1515

16-
import Errors._, Potentials._, Effects._
16+
import Errors._, Potentials._, Effects._, Util._
1717

1818
import scala.collection.mutable
1919

@@ -99,6 +99,8 @@ class CycleChecker(cache: Cache) {
9999
val res = op(using state2)
100100
res
101101

102+
def stackTrace: Vector[Tree] = trace.flatMap(_.source)
103+
102104
}
103105

104106
def state(using ev: State) = ev
@@ -160,6 +162,8 @@ class CycleChecker(cache: Cache) {
160162
if !classesInCurrentRun.contains(dep.cls) || !classesInCurrentRun.contains(dep.symbol.owner) then
161163
Util.traceIndented("skip " + dep.show + " which is not in current run ", init)
162164
Nil
165+
else if !dep.symbol.hasSource then
166+
CallUnknown(dep.symbol, dep.source.last, state.stackTrace) :: Nil
163167
else {
164168
val deps = methodDependencies(dep)
165169
deps.flatMap(check(_))
@@ -169,6 +173,8 @@ class CycleChecker(cache: Cache) {
169173
if !classesInCurrentRun.contains(dep.cls) || !classesInCurrentRun.contains(dep.symbol.owner) then
170174
Util.traceIndented("skip " + dep.show + " which is not in current run ", init)
171175
Nil
176+
else if !dep.symbol.hasSource then
177+
CallUnknown(dep.symbol, dep.source.last, state.stackTrace) :: Nil
172178
else {
173179
val deps = proxyDependencies(dep)
174180
deps.flatMap(check(_))

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,9 @@ object Potentials {
167167
def show(using Context): String = symbol.show
168168

169169
def moduleClass(using Context): ClassSymbol = symbol.moduleClass.asClass
170+
171+
def appearsInside(sym: Symbol)(using Context): Boolean =
172+
enclosingClass.ownersIterator.exists(_ == sym)
170173
}
171174

172175
/** The potential of a hot object

tests/init/neg/features-trees2.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
object Trees { // error
1+
object Trees {
22
class ValDef { counter += 1 }
33
class EmptyValDef extends ValDef
44
val theEmptyValDef = new EmptyValDef
5-
private var counter = 0
5+
private var counter = 0 // error
66
}

tests/init/neg/global-cycle11.scala

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import scala.collection.mutable
2+
3+
object NameKinds {
4+
private val qualifiedNameKinds = mutable.HashMap[Int, QualifiedNameKind]()
5+
6+
val QualifiedName: QualifiedNameKind = new QualifiedNameKind(2, ".")
7+
8+
abstract class NameKind(val tag: Int)
9+
10+
class QualifiedNameKind(tag: Int, val separator: String)
11+
extends NameKind(tag) {
12+
qualifiedNameKinds(tag) = this
13+
}
14+
}
15+
16+
object A { // error
17+
val n: Int = B.m
18+
}
19+
20+
object B {
21+
val m: Int = A.n
22+
}

tests/init/neg/global-cycle9.scala

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
object Names { // error
1+
object Names {
22
abstract class Name
33

44
abstract class TermName extends Name:
@@ -18,3 +18,11 @@ object Names { // error
1818

1919
val nameTable = NameTable()
2020
}
21+
22+
object A { // error
23+
val n: Int = B.m
24+
}
25+
26+
object B {
27+
val m: Int = A.n
28+
}

tests/init/neg/inner9.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
object Flags { // error
1+
object Flags {
22
class Inner {
33
println(b)
44
}
55

66
new Flags.Inner
77

8-
val b = 5
8+
val b = 5 // error
99
}
1010

1111
object Flags2 {

0 commit comments

Comments
 (0)