Skip to content

Commit d65c0d9

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 cbb05c8 commit d65c0d9

File tree

4 files changed

+104
-7
lines changed

4 files changed

+104
-7
lines changed

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

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,37 @@ import dotty.tools.dotc.core._
1111
import dotty.tools.dotc.transform.MegaPhase._
1212
import dotty.tools.dotc.transform.SymUtils._
1313

14-
/** Removes selects that would be compiled into GetStatic
15-
* otherwise backend needs to be aware that some qualifiers need to be dropped.
16-
* Similar transformation seems to be performed by flatten in nsc
17-
* @author Dmytro Petrashko
14+
/** Removes `Select`s that would be compiled into `GetStatic`.
15+
*
16+
* Otherwise, the backend needs to be aware that some qualifiers need to be
17+
* dropped.
18+
*
19+
* A tranformation similar to what this phase does seems to be performed by
20+
* flatten in nsc.
21+
*
22+
* The side effects of the qualifier of a dropped `Select` is normally
23+
* retained. As an exception, the qualifier is completely dropped if it is
24+
* a reference to a static owner (see `isStaticOwnerRef`). Concretely, this
25+
* means that in
26+
*
27+
* {{{
28+
* object Foo {
29+
* println("side effects")
30+
*
31+
* object Bar
32+
* class Baz
33+
* }
34+
*
35+
* Foo.Bar
36+
* new Foo.Baz()
37+
* }}}
38+
*
39+
* the `Foo` qualifiers will be dropped, since it is a static object. The
40+
* `println("side effects")` will therefore not be executed.
41+
*
42+
* This intended behavior is equivalent to what scalac does.
43+
*
44+
* @author Dmytro Petrashko
1845
*/
1946
class SelectStatic extends MiniPhase with IdentityDenotTransformer {
2047
import ast.tpd._
@@ -29,13 +56,23 @@ class SelectStatic extends MiniPhase with IdentityDenotTransformer {
2956
sym.isScalaStatic
3057
val isStaticRef = !sym.is(Package) && !sym.maybeOwner.is(Package) && isStaticMember
3158
val tree1 =
32-
if (isStaticRef && !tree.qualifier.symbol.isAllOf(JavaModule) && !tree.qualifier.isType)
33-
Block(List(tree.qualifier), ref(sym))
59+
if isStaticRef && !tree.qualifier.symbol.isAllOf(JavaModule) && !tree.qualifier.isType then
60+
if isStaticOwnerRef(tree.qualifier) then ref(sym)
61+
else Block(List(tree.qualifier), ref(sym))
3462
else tree
3563

3664
normalize(tree1)
3765
}
3866

67+
private def isStaticOwnerRef(tree: Tree)(using Context): Boolean = tree match {
68+
case Ident(_) =>
69+
tree.symbol.is(Module) && tree.symbol.moduleClass.isStaticOwner
70+
case Select(qual, _) =>
71+
isStaticOwnerRef(qual) && tree.symbol.is(Module) && tree.symbol.moduleClass.isStaticOwner
72+
case _ =>
73+
false
74+
}
75+
3976
private def normalize(t: Tree)(using Context) = t match {
4077
case Select(Block(stats, qual), nm) =>
4178
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)