Skip to content

Commit 959676b

Browse files
committed
New phase: HoistSuperArgs
Hoist complex supercall arguments out of the class they appear in. This avoids a lot of complexity later on in other transformation phases.
1 parent 12ca2fe commit 959676b

File tree

3 files changed

+173
-0
lines changed

3 files changed

+173
-0
lines changed

compiler/src/dotty/tools/dotc/Compiler.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ class Compiler {
5656
new ExpandSAMs, // Expand single abstract method closures to anonymous classes
5757
new TailRec, // Rewrite tail recursion to loops
5858
new LiftTry, // Put try expressions that might execute on non-empty stacks into their own methods
59+
new HoistSuperArgs, // Hoist complex arguments of supercalls to enclosing scope
5960
new ClassOf), // Expand `Predef.classOf` calls.
6061
List(new TryCatchPatterns, // Compile cases in try/catch
6162
new PatternMatcher, // Compile pattern matches

compiler/src/dotty/tools/dotc/core/NameKinds.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ object NameKinds {
221221
val ExceptionBinderName = new UniqueNameKind("ex")
222222
val SkolemName = new UniqueNameKind("?")
223223
val LiftedTreeName = new UniqueNameKind("liftedTree")
224+
val SuperArgName = new UniqueNameKind("$superArg$")
224225

225226
val UniqueExtMethName = new UniqueNameKind("$extension") {
226227
override def unmangle(name: SimpleTermName): TermName = {
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
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 ast.TreeTypeMap
9+
import core.Types._
10+
import core.Flags._
11+
import core.Decorators._
12+
import collection.mutable
13+
import ast.Trees._
14+
import core.Names.TermName
15+
import core.NameKinds.SuperArgName
16+
import SymUtils._
17+
18+
/** This phase hoists complex arguments to supercalls out of the enclosing class.
19+
* Example:
20+
*
21+
* class B(y: Int) extends A({ def f(x: Int) = x * x; f(y)})
22+
*
23+
* is translated to
24+
*
25+
* class B(y: Int) extends A(B#B$superArg$1(this.y)) {
26+
* private <static> def B$superArg$1(y: Int): Int = {
27+
* def f(x: Int): Int = x.*(x); f(y)
28+
* }
29+
* }
30+
*
31+
* An argument is complex if it contains a method or template definition, a this or a new,
32+
* or it contains an identifier which needs a `this` prefix to be accessed. This is the case
33+
* if the identifer neither a global reference nor a reference to a parameter of the enclosing class.
34+
* @see needsHoist for an implementation.
35+
*
36+
* A hoisted argument definition gets the parameters of the class it is hoisted from
37+
* as method parameters. The definition is installed in the scope enclosing the class,
38+
* or, if that is a package, it is made a static method of the class itself.
39+
*/
40+
class HoistSuperArgs extends MiniPhaseTransform with IdentityDenotTransformer { thisTransform =>
41+
import ast.tpd._
42+
43+
def phaseName = "hoistSuperArgs"
44+
45+
/** Hoist complex arguments in super call `parent` out of the class.
46+
* @return A pair consisting of the transformed super call and a list of super argument
47+
* defininitions.
48+
*/
49+
def hoistSuperArgs(parent: Tree, cls: Symbol)(implicit ctx: Context): (Tree, List[DefDef]) = {
50+
lazy val constr = cls.primaryConstructor
51+
lazy val allParams = // The parameters that can be accessed in the supercall
52+
cls.info.decls.filter(d => d.is(TypeParam) || d.is(TermParamAccessor))
53+
val superArgDefs = new mutable.ListBuffer[DefDef]
54+
55+
/** The parameter references defined by the primary constructor info */
56+
def allParamRefs(tp: Type): List[ParamRef] = tp match {
57+
case tp: LambdaType => tp.paramRefs ++ allParamRefs(tp.resultType)
58+
case _ => Nil
59+
}
60+
61+
/** Splice `restpe` in final result type position of `tp` */
62+
def replaceResult(tp: Type, restpe: Type): Type = tp match {
63+
case tp: LambdaType =>
64+
tp.derivedLambdaType(resType = replaceResult(tp.resultType, restpe))
65+
case _ => restpe
66+
}
67+
68+
/** A method representing a hoisted supercall argument */
69+
def newSuperArgMethod(argType: Type) = {
70+
val (staticFlag, methOwner) =
71+
if (cls.owner.is(Package)) (JavaStatic, cls) else (EmptyFlags, cls.owner)
72+
val argTypeWrtConstr = argType.subst(allParams, allParamRefs(constr.info))
73+
// argType with references to paramRefs of the primary constructor instead of
74+
// local parameter accessors
75+
val meth = ctx.newSymbol(
76+
owner = methOwner,
77+
name = SuperArgName.fresh(cls.name.toTermName),
78+
flags = Synthetic | Private | Method | staticFlag,
79+
info = replaceResult(constr.info, argTypeWrtConstr),
80+
coord = constr.coord)
81+
if (methOwner.isClass) meth.enteredAfter(thisTransform) else meth
82+
}
83+
84+
/** Super call argument is complex, needs to be hoisted */
85+
def needsHoist(tree: Tree) = tree match {
86+
case _: DefDef => true
87+
case _: Template => true
88+
case _: This => !tree.symbol.isStaticOwner
89+
case _: New => !tree.tpe.typeSymbol.isStatic
90+
case _: RefTree =>
91+
var owner = tree.symbol.effectiveOwner
92+
if (owner.isLocalDummy) owner = owner.owner
93+
tree.isTerm && !owner.isStaticOwner && owner != cls
94+
case _ => false
95+
}
96+
97+
/** If argument is complex, hoist it out into its own method and refer to the
98+
* method instead.
99+
* @return The argument after possible hoisting
100+
* Might append a method definition to `superArgs` as a side effect.
101+
*/
102+
def hoistSuperArg(arg: Tree) =
103+
if (arg.existsSubTree(needsHoist)) {
104+
val superMeth = newSuperArgMethod(arg.tpe)
105+
val superArgDef = polyDefDef(superMeth, trefs => vrefss => {
106+
val paramSyms = trefs.map(_.typeSymbol) ::: vrefss.flatten.map(_.symbol)
107+
val tmap = new TreeTypeMap(
108+
typeMap = new TypeMap {
109+
def apply(tp: Type) = tp match {
110+
case tp: NamedType if tp.symbol.owner == cls && tp.symbol.is(ParamOrAccessor) =>
111+
val mappedSym = allParams.zip(paramSyms).toMap.apply(tp.symbol)
112+
if (tp.symbol.isType) mappedSym.typeRef else mappedSym.termRef
113+
case _ =>
114+
mapOver(tp)
115+
}
116+
},
117+
treeMap = {
118+
case tree: RefTree if paramSyms.contains(tree.symbol) =>
119+
cpy.Ident(tree)(tree.name).withType(tree.tpe)
120+
case tree =>
121+
tree
122+
}
123+
)
124+
tmap(arg).changeOwnerAfter(constr, superMeth, thisTransform)
125+
})
126+
superArgDefs += superArgDef
127+
def termParamRefs(tp: Type): List[List[Tree]] = tp match {
128+
case tp: PolyType =>
129+
termParamRefs(tp.resultType)
130+
case tp: MethodType =>
131+
def paramRef(name: TermName) =
132+
ref(cls.info.decl(name).suchThat(_.is(ParamAccessor)).symbol)
133+
tp.paramNames.map(paramRef) :: termParamRefs(tp.resultType)
134+
case _ =>
135+
Nil
136+
}
137+
val res = ref(superMeth)
138+
.appliedToTypes(cls.typeParams.map(_.typeRef))
139+
.appliedToArgss(termParamRefs(constr.info))
140+
ctx.log(i"hoist $arg, cls = $cls = $res")
141+
res
142+
}
143+
else arg
144+
145+
def recur(tree: Tree): Tree = tree match {
146+
case Apply(fn, args) => cpy.Apply(tree)(recur(fn), args.mapconserve(hoistSuperArg))
147+
case _ => tree
148+
}
149+
(recur(parent), superArgDefs.toList)
150+
}
151+
152+
override def transformTypeDef(tdef: TypeDef)(implicit ctx: Context, info: TransformerInfo): Tree =
153+
tdef.rhs match {
154+
case impl @ Template(_, superCall :: others, _, _) =>
155+
val cls = tdef.symbol
156+
val (hoisted, superArgDefs) = hoistSuperArgs(superCall, cls)
157+
if (superArgDefs.isEmpty) tdef
158+
else {
159+
val (staticSuperArgDefs, enclSuperArgDefs) =
160+
superArgDefs.partition(_.symbol.is(JavaStatic))
161+
flatTree(
162+
cpy.TypeDef(tdef)(
163+
rhs = cpy.Template(impl)(
164+
parents = hoisted :: others,
165+
body = impl.body ++ staticSuperArgDefs)) ::
166+
enclSuperArgDefs)
167+
}
168+
case _ =>
169+
tdef
170+
}
171+
}

0 commit comments

Comments
 (0)