Skip to content

Commit 855572e

Browse files
committed
Do not load enclosing objects when accessing inner static classes and objects.
This restores parity with the run-time semantics of Scala 2. When accessing an inner static class or object, we avoid loading the enclosing objects as values. The discrepancy in run-time semantics was introduced as a by-product of #1445 The change in `t4859.check` restores it to its previous state, before that PR.
1 parent a319876 commit 855572e

File tree

4 files changed

+73
-3
lines changed

4 files changed

+73
-3
lines changed

compiler/src/dotty/tools/dotc/transform/SelectStatic.scala

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,23 @@ class SelectStatic extends MiniPhase with IdentityDenotTransformer {
2929
sym.isScalaStatic
3030
val isStaticRef = !sym.is(Package) && !sym.maybeOwner.is(Package) && isStaticMember
3131
val tree1 =
32-
if (isStaticRef && !tree.qualifier.symbol.isAllOf(JavaModule) && !tree.qualifier.isType)
33-
Block(List(tree.qualifier), ref(sym))
32+
if isStaticRef && !tree.qualifier.symbol.isAllOf(JavaModule) && !tree.qualifier.isType then
33+
if isStaticOwnerRef(tree.qualifier) then ref(sym)
34+
else Block(List(tree.qualifier), ref(sym))
3435
else tree
3536

3637
normalize(tree1)
3738
}
3839

40+
private def isStaticOwnerRef(tree: Tree)(using Context): Boolean = tree match {
41+
case Ident(_) =>
42+
tree.symbol.is(Module) && tree.symbol.moduleClass.isStaticOwner
43+
case Select(qual, _) =>
44+
isStaticOwnerRef(qual) && tree.symbol.is(Module) && tree.symbol.moduleClass.isStaticOwner
45+
case _ =>
46+
false
47+
}
48+
3949
private def normalize(t: Tree)(using Context) = t match {
4050
case Select(Block(stats, qual), nm) =>
4151
Block(stats, cpy.Select(t)(qual, nm))
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
InnerObject constructor
2+
42
3+
InnerClass constructor
4+
5
5+
42
6+
InnerClass constructor
7+
5
8+
getEnclosing2()
9+
Enclosing2 constructor
10+
Enclosing2.InnerObject constructor
11+
65
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
object Enclosing1 {
2+
println("must not be executed")
3+
4+
object InnerObject {
5+
val foo: Int = 42
6+
println("InnerObject constructor")
7+
}
8+
9+
class InnerClass(val foo: Int) {
10+
println("InnerClass constructor")
11+
}
12+
}
13+
14+
object Enclosing2 {
15+
println("Enclosing2 constructor")
16+
17+
object InnerObject {
18+
val foo: Int = 65
19+
println("Enclosing2.InnerObject constructor")
20+
}
21+
}
22+
23+
object Test {
24+
def main(args: Array[String]): Unit = {
25+
testExplicit()
26+
testThroughImport()
27+
testThroughFunction()
28+
}
29+
30+
def testExplicit(): Unit = {
31+
println(Enclosing1.InnerObject.foo)
32+
println(new Enclosing1.InnerClass(5).foo)
33+
}
34+
35+
def testThroughImport(): Unit = {
36+
import Enclosing1._
37+
println(InnerObject.foo)
38+
println(new InnerClass(5).foo)
39+
}
40+
41+
def testThroughFunction(): Unit = {
42+
println(getEnclosing2().InnerObject.foo)
43+
}
44+
45+
def getEnclosing2(): Enclosing2.type = {
46+
println("getEnclosing2()")
47+
Enclosing2
48+
}
49+
}

tests/run/t4859.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
Outer
21
Inner
32
Inner.i
43
About to reference Inner.i
4+
Outer
55
Inner.i
66
About to reference O.N
77
About to reference O.N

0 commit comments

Comments
 (0)