Skip to content

Commit 6b4fd1b

Browse files
committed
Handle static objects as outer
1 parent 8c28e9b commit 6b4fd1b

File tree

3 files changed

+68
-27
lines changed

3 files changed

+68
-27
lines changed

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

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@ import printing.SyntaxHighlighting
1212
import reporting.trace
1313
import config.Printers.init
1414

15-
import ast.Trees._
16-
import ast.tpd
15+
import ast.tpd._
1716

1817
import scala.collection.mutable
1918

@@ -37,10 +36,10 @@ import scala.collection.mutable
3736
* compiled projects.
3837
*/
3938
class CheckGlobal {
40-
case class Dependency(sym: Symbol, source: tpd.Tree)
39+
case class Dependency(sym: Symbol, source: Tree)
4140

4241
/** Checking state */
43-
case class State(var visited: Set[Symbol], path: Vector[tpd.Tree], obj: Symbol) {
42+
case class State(visited: mutable.Set[Symbol], path: Vector[Tree], obj: Symbol) {
4443
def cyclicPath(using Context): String = if (path.isEmpty) "" else " Cyclic path:\n" + {
4544
var indentCount = 0
4645
var last: String = ""
@@ -74,7 +73,7 @@ class CheckGlobal {
7473
private val summaryCache = mutable.Map.empty[Symbol, List[Dependency]]
7574

7675
def check(obj: Symbol)(using Context): Unit = trace("checking " + obj.show, init) {
77-
checkDependencies(obj, State(visited = Set.empty, path = Vector.empty, obj)) match
76+
checkDependencies(obj, State(visited = mutable.Set.empty, path = Vector.empty, obj)) match
7877
case Some(err) => err.issue
7978
case _ =>
8079
}
@@ -85,7 +84,7 @@ class CheckGlobal {
8584
else if state.visited.contains(sym) then
8685
None
8786
else
88-
state.visited = state.visited + sym
87+
state.visited += sym
8988
checkDependencies(sym, state)
9089
}
9190

@@ -96,8 +95,9 @@ class CheckGlobal {
9695
var res: Option[Error] = None
9796
// TODO: stop early
9897
deps.foreach { dep =>
99-
val state2: State = state.copy(path = state.path :+ dep.source)
100-
if res.isEmpty then res = check(dep.sym, state2)
98+
if res.isEmpty then
99+
val state2: State = state.copy(path = state.path :+ dep.source)
100+
res = check(dep.sym, state2)
101101
}
102102
res
103103
}
@@ -110,29 +110,50 @@ class CheckGlobal {
110110
if (cls.defTree.isEmpty) Nil
111111
else if (summaryCache.contains(cls)) summaryCache(cls)
112112
else {
113-
val cdef = cls.defTree.asInstanceOf[tpd.TypeDef]
114-
val tpl = cdef.rhs.asInstanceOf[tpd.Template]
113+
val cdef = cls.defTree.asInstanceOf[TypeDef]
114+
val tpl = cdef.rhs.asInstanceOf[Template]
115115
var dependencies: List[Dependency] = Nil
116-
val traverser = new tpd.TreeTraverser {
117-
override def traverse(tree: tpd.Tree)(using Context): Unit =
116+
val traverser = new TreeTraverser {
117+
override def traverse(tree: Tree)(using Context): Unit =
118118
tree match {
119-
case tree: tpd.RefTree if isStaticObjectRef(tree.symbol) =>
119+
case tree: RefTree if isStaticObjectRef(tree.symbol) =>
120120
dependencies = Dependency(tree.symbol, tree) :: dependencies
121121

122-
case tdef: tpd.TypeDef =>
122+
case tdef: TypeDef =>
123123
// don't go into nested classes
124124

125-
case tree: tpd.New =>
125+
case tree: New =>
126126
dependencies = Dependency(tree.tpe.classSymbol, tree) :: dependencies
127127

128128
case _ =>
129129
traverseChildren(tree)
130130
}
131131
}
132132

133+
def typeRefOf(tp: Type): TypeRef = tp.dealias.typeConstructor match {
134+
case tref: TypeRef => tref
135+
case hklambda: HKTypeLambda => typeRefOf(hklambda.resType)
136+
}
137+
138+
def addStaticOuterDep(tp: Type, source: Tree): Unit =
139+
tp match
140+
case NoPrefix =>
141+
case tmref: TermRef =>
142+
if isStaticObjectRef(tmref.symbol) then
143+
dependencies = Dependency(tmref.symbol, source) :: dependencies
144+
case ThisType(tref) =>
145+
val obj = tref.symbol.sourceModule
146+
if isStaticObjectRef(obj) then
147+
dependencies = Dependency(obj, source) :: dependencies
148+
case _ =>
149+
throw new Exception("unexpected type: " + tp)
150+
133151
// TODO: the traverser might create duplicate entries for parents
134152
tpl.parents.foreach { tree =>
135-
dependencies = Dependency(tree.tpe.classSymbol, tree) :: dependencies
153+
val tp = tree.tpe
154+
val tref = typeRefOf(tp)
155+
dependencies = Dependency(tp.classSymbol, tree) :: dependencies
156+
addStaticOuterDep(tref.prefix, tree)
136157
}
137158

138159
traverser.traverse(tpl)

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

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,18 @@ object Checking {
3939
safePromoted: mutable.Set[Potential], // Potentials that can be safely promoted
4040
env: Env
4141
) {
42-
def withOwner(sym: Symbol): State = copy(env = env.withOwner(sym))
42+
def withOwner[T](sym: Symbol)(op: State ?=> T): T =
43+
val state = this.copy(env = env.withOwner(sym))
44+
val res = op(using state)
45+
this.visited = state.visited
46+
res
47+
48+
49+
def withStep[T](step: Tree)(op: State ?=> T): T =
50+
val state: State = this.copy(path = path :+ step)
51+
val res = op(using state)
52+
this.visited = state.visited
53+
res
4354

4455
def test(op: State ?=> Errors): Errors = {
4556
val savedVisited = visited
@@ -60,11 +71,12 @@ object Checking {
6071
}
6172
else {
6273
state.visited = state.visited + eff
63-
val state2: State = state.copy(path = state.path :+ eff.source)
64-
eff match {
65-
case eff: Promote => Checking.checkPromote(eff)(using state2)
66-
case eff: FieldAccess => Checking.checkFieldAccess(eff)(using state2)
67-
case eff: MethodCall => Checking.checkMethodCall(eff)(using state2)
74+
state.withStep(eff.source) {
75+
eff match {
76+
case eff: Promote => Checking.checkPromote(eff)
77+
case eff: FieldAccess => Checking.checkFieldAccess(eff)
78+
case eff: MethodCall => Checking.checkMethodCall(eff)
79+
}
6880
}
6981
}
7082
}
@@ -118,11 +130,11 @@ object Checking {
118130
def checkConstructor(ctor: Symbol, tp: Type, source: Tree)(using state: State): Unit = traceOp("checking " + ctor.show, init) {
119131
val cls = ctor.owner
120132
val classDef = cls.defTree
121-
if (!classDef.isEmpty) {
122-
given State = state.withOwner(cls)
123-
if (ctor.isPrimaryConstructor) checkClassBody(classDef.asInstanceOf[TypeDef])
124-
else checkSecondaryConstructor(ctor)
125-
}
133+
if (!classDef.isEmpty)
134+
state.withOwner(cls) {
135+
if (ctor.isPrimaryConstructor) checkClassBody(classDef.asInstanceOf[TypeDef])
136+
else checkSecondaryConstructor(ctor)
137+
}
126138
}
127139

128140
def checkSecondaryConstructor(ctor: Symbol)(using state: State): Unit = traceOp("checking " + ctor.show, init) {

tests/init/neg/t9115.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
object D { // error
2+
def aaa = 1 //that’s the reason
3+
class Z (depends: Any)
4+
case object D1 extends Z(aaa) // 'null' when calling D.D1 first time // error
5+
case object D2 extends Z(aaa) // 'null' when calling D.D2 first time // error
6+
println(D1)
7+
println(D2)
8+
}

0 commit comments

Comments
 (0)