1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
|
package dotty.tools.dotc
package transform
import TreeTransforms._
import core.DenotTransformers._
import core.Symbols._
import core.Contexts._
import core.Types._
import core.Flags._
import core.Decorators._
import SymUtils._
import core.StdNames.nme
import ast.Trees._
/** This phase eliminates ExprTypes `=> T` and PolyTypes over value types `[X]T`.
* They are expressed in terms of nullary method or function types. More precisely:
*
* For types:
*
* => T ==> () => T if T is the type of a parameter
* ==> ()T otherwise
* [X]T ==> [X]()T
*
* For definitions:
*
* def f: R ==> def f(): R
* def f[X]: R ==> def f[X](): R
* (x: => T) ==> (x: () => T)
*
* For terms:
*
* f ==> f() if f had type => T and is not a parameter
* x ==> x.apply() if x is a parameter that had type => T
* e.apply() ==> e if e.apply() is an argument to a call-by-name parameter
* expr ==> () => expr if other expr is an argument to a call-by-name parameter
*
*/
class ElimByName extends MiniPhaseTransform with InfoTransformer { thisTransformer =>
import ast.tpd._
override def phaseName: String = "elimByName"
override def runsAfterGroupsOf = Set(classOf[Splitter])
// assumes idents and selects have symbols; interferes with splitter distribution
// that's why it's "after group".
override def treeTransformPhase = thisTransformer.next
/** The info of the tree's symbol at phase Nullarify (i.e. before transformation) */
private def originalDenotation(tree: Tree)(implicit ctx: Context) =
tree.symbol.denot(ctx.withPhase(thisTransformer))
override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree =
ctx.traceIndented(s"transforming ${tree.show} at phase ${ctx.phase}", show = true) {
def transformArg(arg: Tree, formal: Type): Tree = formal match {
case _: ExprType =>
arg match {
case Apply(Select(qual, nme.apply), Nil) if qual.tpe derivesFrom defn.FunctionClass(0) =>
qual
case _ =>
val meth = ctx.newSymbol(
ctx.owner, nme.ANON_FUN, Synthetic | Method, MethodType(Nil, Nil, arg.tpe.widen))
Closure(meth, _ => arg.changeOwner(ctx.owner, meth))
}
case _ =>
arg
}
/** Given that `info` is the possibly curried) method type of the
* tree's symbol, the method type that corresponds to the current application.
*/
def matchingMethType(info: Type, tree: Tree): Type = tree match {
case Apply(fn, _) => matchingMethType(info.resultType, fn)
case _ => info
}
val origMethType = originalDenotation(tree.fun).info match {
case pt: PolyType => pt.resultType
case mt => mt
}
val MethodType(_, formals) = matchingMethType(origMethType, tree.fun)
val args1 = tree.args.zipWithConserve(formals)(transformArg)
cpy.Apply(tree)(tree.fun, args1)
}
override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo): Tree = {
val origDenot = originalDenotation(tree)
if ((origDenot is Param) && (origDenot.info.isInstanceOf[ExprType]))
tree.select(defn.Function0_apply).appliedToNone
else tree
}
def elimByNameParams(tp: Type)(implicit ctx: Context): Type = tp match {
case tp: PolyType =>
tp.derivedPolyType(tp.paramNames, tp.paramBounds, elimByNameParams(tp.resultType))
case tp: MethodType =>
tp.derivedMethodType(tp.paramNames, tp.paramTypes mapConserve transformParamInfo,
elimByNameParams(tp.resultType))
case _ =>
tp
}
def transformParamInfo(tp: Type)(implicit ctx: Context) = tp match {
case ExprType(rt) => defn.FunctionType(Nil, rt)
case _ => tp
}
def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type =
if (sym is Param) transformParamInfo(tp)
else elimByNameParams(tp)
}
|