Skip to content

Commit b41732c

Browse files
committed
New minipahse: AttachOuter
This keeps type info needed for explicit outer in attachments so that it survives erasure. ExplicitOuter should run after erasure, for several reasons. (1) Java generic signatures do not include the outer parameter (2) Pre-erasure typings become incorrect after erasure. In particular, if we have a class class C { type T class Inner { type U = C.this.T } val inner: Inner } after explicit outer the equality of T and inner.U is no longer provable.
1 parent 15c4fec commit b41732c

File tree

2 files changed

+67
-1
lines changed

2 files changed

+67
-1
lines changed

src/dotty/tools/dotc/Compiler.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ class Compiler {
6161
List(new ElimByName,
6262
new TypeTestsCasts,
6363
new InterceptedMethods,
64-
new Literalize),
64+
new Literalize,
65+
new AttachOuter),
6566
List(new Erasure)
6667
)
6768

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package dotty.tools.dotc
2+
package transform
3+
4+
import TreeTransforms._
5+
import core.DenotTransformers._
6+
import core.Symbols._
7+
import core.Contexts._
8+
import core.Types._
9+
import core.Flags._
10+
import core.Decorators._
11+
import core.StdNames.nme
12+
import ast.Trees._
13+
import util.Attachment
14+
15+
/** This phase decorates News and parent constructors of non-static inner classes
16+
* with an attachment indicating the outer reference as a tree. This is necessary because
17+
* outer prefixes are erased, and explicit outer runs only after erasure.
18+
*/
19+
class AttachOuter extends MiniPhaseTransform {
20+
import ast.tpd._
21+
22+
val Outer = new Attachment.Key[Tree]
23+
24+
override def phaseName: String = "attachOuter"
25+
26+
private def outerPrefix(tpe: Type)(implicit ctx: Context): Type = tpe match {
27+
case tpe: TypeRef =>
28+
tpe.symbol match {
29+
case cls: ClassSymbol =>
30+
if (cls.owner.isStaticOwner || cls.is(Interface)) NoPrefix
31+
else if (tpe.prefix eq NoPrefix) cls.owner.enclosingClass.thisType
32+
else tpe.prefix
33+
case _ =>
34+
outerPrefix(tpe.underlying)
35+
}
36+
case tpe: TypeProxy =>
37+
outerPrefix(tpe.underlying)
38+
}
39+
40+
override def transformNew(tree: New)(implicit ctx: Context, info: TransformerInfo): Tree = {
41+
val pre = outerPrefix(tree.tpt.tpe)
42+
pre match {
43+
case pre: SingletonType =>
44+
tree.putAttachment(Outer, singleton(pre)) match {
45+
case Some(outer) => assert(outer.tpe =:= pre)
46+
case none =>
47+
}
48+
case NoPrefix =>
49+
}
50+
tree
51+
}
52+
53+
override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo): Tree = {
54+
def transformParent(tree: Tree): Tree = tree match {
55+
case tree: TypeTree if outerPrefix(tree.tpe) != NoPrefix =>
56+
val constr = New(tree.tpe, Nil).withPos(tree.pos)
57+
val Select(nu: New, _) = methPart(constr)
58+
transformNew(nu)
59+
constr
60+
case _ =>
61+
tree
62+
}
63+
cpy.Template(tree)(parents = tree.parents mapconserve transformParent)
64+
}
65+
}

0 commit comments

Comments
 (0)