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
+
14
+ /** This phase eliminates ExprTypes `=> T` and PolyTypes over value types `[X]T`.
15
+ * They are expressed in terms of nullary method or function types. More precisely:
16
+ *
17
+ * For types:
18
+ *
19
+ * => T ==> () => T if T is the type of a parameter
20
+ * ==> ()T otherwise
21
+ * [X]T ==> [X]()T
22
+ *
23
+ * For definitions:
24
+ *
25
+ * def f: R ==> def f(): R
26
+ * def f[X]: R ==> def f[X](): R
27
+ * (x: => T) ==> (x: () => T)
28
+ *
29
+ * For terms:
30
+ *
31
+ * f ==> f() if f had type => T and is not a parameter
32
+ * x ==> x.apply() if x is a parameter that had type => T
33
+ * e.apply() ==> e if e.apply() is an argument to a call-by-name parameter
34
+ * expr ==> () => expr if other expr is an argument to a call-by-name parameter
35
+ *
36
+ */
37
+ class Nullarify extends TreeTransform with InfoTransformer {
38
+ import ast .tpd ._
39
+
40
+ override def name : String = " nullarify"
41
+
42
+ override def runsAfterGroupsOf : Set [String ] = Set (" splitter" )
43
+ // assumes idents and selects have symbols; interferes with splitter distribution
44
+ // that's why it's "after group".
45
+
46
+ override def transformApply (tree : Apply )(implicit ctx : Context , info : TransformerInfo ): Tree =
47
+ ctx.traceIndented(s " transforming ${tree.show} at phase ${ctx.phase}" , show = true ) {
48
+
49
+ def transformArg (arg : Tree , formal : Type ): Tree = formal match {
50
+ case _ : ExprType =>
51
+ arg match {
52
+ case Apply (Select (qual, nme.apply), Nil ) => qual
53
+ case _ =>
54
+ val meth = ctx.newSymbol(ctx.owner, nme.ANON_FUN , Synthetic ,
55
+ MethodType (Nil , Nil , arg.tpe.widen))
56
+ Closure (meth, _ => arg)
57
+ }
58
+ case _ =>
59
+ arg
60
+ }
61
+
62
+ // Compute the method type tree had before this phase is run.
63
+ // This is needed to find out which parameters are by-name.
64
+ val funType = tree.fun.symbol.info match {
65
+ case info : PolyType => info.resultType
66
+ case info => info
67
+ }
68
+ def methType (info : Type , tree : Tree ): Type = tree match {
69
+ case Apply (fn, args) => methType(info.resultType, fn)
70
+ case _ => info
71
+ }
72
+ val MethodType (_, formals) = methType(funType, tree.fun)
73
+
74
+ val args1 = tree.args.zipWithConserve(formals)(transformArg)
75
+ cpy.Apply (tree, tree.fun, args1) withType nullarify(tree.tpe)
76
+ }
77
+
78
+ /** Insert () or .apply() if the term refers to something that was converted to a
79
+ * nullary method. Also, transform its type.
80
+ */
81
+ def insertParens (tree : Tree )(implicit ctx : Context ): Tree = {
82
+ val tp1 = transformInfo(tree.tpe, tree.symbol)
83
+ val tree1 = tree.withType(tp1)
84
+ val origType = tree.tpe.widenSingleton
85
+ def result (implicit ctx : Context ) = {
86
+ tp1.widen match {
87
+ case MethodType (Nil , _) if origType.widenExpr.isInstanceOf [ValueType ] =>
88
+ Apply (tree1, Nil )
89
+ case _ =>
90
+ origType match {
91
+ case _ : ExprType => // it's a by-name parameter
92
+ Apply (Select (tree1, defn.Function0_apply ), Nil )
93
+ case _ =>
94
+ tree1
95
+ }
96
+ }
97
+ }
98
+ result(ctx.withPhase(ctx.phase.next))
99
+ }
100
+
101
+ override def transformIdent (tree : Ident )(implicit ctx : Context , info : TransformerInfo ): Tree =
102
+ insertParens(tree)
103
+
104
+ override def transformSelect (tree : Select )(implicit ctx : Context , info : TransformerInfo ): Tree =
105
+ insertParens(tree)
106
+
107
+ override def transformTypeApply (tree : TypeApply )(implicit ctx : Context , info : TransformerInfo ): Tree =
108
+ insertParens(tree)
109
+
110
+ override def transformDefDef (tree : DefDef )(implicit ctx : Context , info : TransformerInfo ): Tree = {
111
+ val DefDef (mods, name, tparams, vparamss, tpt, rhs) = tree
112
+ val vparamss1 =
113
+ if (vparamss.isEmpty) Nil :: Nil
114
+ else vparamss nestedMap { vparam =>
115
+ val tp = vparam.tpt.tpe
116
+ val tp1 = nullarifyParam(tp)
117
+ if (tp eq tp1) vparam
118
+ else cpy.ValDef (vparam, vparam.mods, vparam.name, vparam.tpt.withType(tp1), vparam.rhs)
119
+ }
120
+ cpy.DefDef (tree, mods, name, tparams, vparamss1, tpt, rhs)
121
+ }
122
+
123
+ def nullarify (tp : Type )(implicit ctx : Context ): Type = tp match {
124
+ case ExprType (rt) =>
125
+ MethodType (Nil , Nil , rt)
126
+ case pt : PolyType =>
127
+ val rt = pt.resultType match {
128
+ case mt : MethodType => nullarify(mt)
129
+ case rt => MethodType (Nil , Nil , rt)
130
+ }
131
+ pt.derivedPolyType(pt.paramNames, pt.paramBounds, rt)
132
+ case mt : MethodType =>
133
+ mt.derivedMethodType(mt.paramNames, mt.paramTypes mapConserve nullarifyParam,
134
+ nullarify(mt.resultType))
135
+ case _ =>
136
+ tp
137
+ }
138
+
139
+ def nullarifyParam (tp : Type )(implicit ctx : Context ) = tp match {
140
+ case ExprType (rt) => defn.FunctionType (Nil , rt)
141
+ case _ => tp
142
+ }
143
+
144
+ def transformInfo (tp : Type , sym : Symbol )(implicit ctx : Context ): Type =
145
+ if (defn.typeTestsOrCasts contains sym) tp
146
+ else if (sym is Param ) nullarifyParam(tp)
147
+ else nullarify(tp)
148
+ }
0 commit comments