1
- package dotty .tools .dotc
1
+ package dotty .tools
2
+ package dotc
2
3
package transform
3
4
4
5
import core ._
5
- import DenotTransformers .InfoTransformer
6
- import Symbols ._
7
6
import Contexts ._
7
+ import Symbols ._
8
8
import Types ._
9
+ import Flags ._
10
+ import SymDenotations .*
11
+ import DenotTransformers .InfoTransformer
12
+ import NameKinds .SuperArgName
9
13
import core .StdNames .nme
10
- import ast .Trees ._
14
+ import MegaPhase .*
15
+ import Decorators .*
16
+ import reporting .trace
11
17
12
- /** This phase eliminates ExprTypes `=> T` as types of method parameter references, and replaces them b
13
- * nullary function types. More precisely:
18
+ /** This phase implements the following transformations:
14
19
*
15
- * For the types of parameter symbols :
20
+ * 1. For types of method and class parameters :
16
21
*
17
- * => T ==> () => T
22
+ * => T becomes () ? => T
18
23
*
19
- * For cbn parameter values
24
+ * 2. For references to cbn-parameters:
20
25
*
21
- * x ==> x ()
26
+ * x becomes x.apply ()
22
27
*
23
- * Note: This scheme to have inconsistent types between method types (whose formal types are still
24
- * ExprTypes and parameter valdefs (which are now FunctionTypes) is not pretty. There are two
25
- * other options which have been abandoned or not yet pursued.
28
+ * 3. For arguments to cbn parameters
26
29
*
27
- * Option 1: Transform => T to () => T also in method and function types. The problem with this is
28
- * that is that it requires to look at every type, and this forces too much, causing
29
- * Cyclic Reference errors. Abandoned for this reason.
30
+ * e becomes () ?=> e
30
31
*
31
- * Option 2: Merge ElimByName with erasure, or have it run immediately before. This has not been
32
- * tried yet.
32
+ * An opimization is applied: If the argument `e` to a cbn parameter is already
33
+ * of type `() ?=> T` and is a pure expression, we avoid (2) and (3), i.e. we
34
+ * pass `e` directly instead of `() ?=> e.apply()`.
35
+ *
36
+ * Note that `() ?=> T` cannot be written in source since user-defined context functions
37
+ * must have at least one parameter. We use the type here as a convenient marker
38
+ * of something that will erase to Function0, and where we know that it came from
39
+ * a by-name parameter.
40
+ *
41
+ * Note also that the transformation applies only to types of parameters, not to other
42
+ * occurrences of ExprTypes. In particular, embedded occurrences in function types
43
+ * such as `(=> T) => U` are left as-is. Trying to convert these as well would mean
44
+ * traversing all the types, and that leads to cyclic reference errors in many cases.
45
+ * This can cause problems in that we might have sometimes a `() ?=> T` where a
46
+ * `=> T` is expected. To compensate, there is a new clause in TypeComparer#subArg that
47
+ * declares `() ?=> T` to be a subtype of `T` for arguments of type applications,
48
+ * after this phase and up to erasure.
33
49
*/
34
- class ElimByName extends TransformByNameApply with InfoTransformer {
50
+ class ElimByName extends MiniPhase , InfoTransformer :
51
+ thisPhase =>
52
+
35
53
import ast .tpd ._
36
54
37
55
override def phaseName : String = ElimByName .name
38
56
39
- override def changesParents : Boolean = true // Only true for by-names
57
+ override def runsAfterGroupsOf : Set [String ] = Set (ExpandSAMs .name, ElimRepeated .name)
58
+ // - ExpanSAMs applied to partial functions creates methods that need
59
+ // to be fully defined before converting. Test case is pos/i9391.scala.
60
+ // - ByNameLambda needs to run in a group after ElimRepeated since ElimRepeated
61
+ // works on simple arguments but not converted closures, and it sees the arguments
62
+ // after transformations by subsequent miniphases in the same group.
63
+
64
+ override def changesParents : Boolean = true
65
+ // Expr types in parent type arguments are changed to function types.
66
+
67
+ /** If denotation had an ExprType before, it now gets a function type */
68
+ private def exprBecomesFunction (symd : SymDenotation )(using Context ): Boolean =
69
+ symd.is(Param ) || symd.is(ParamAccessor , butNot = Method )
70
+
71
+ def transformInfo (tp : Type , sym : Symbol )(using Context ): Type = tp match {
72
+ case ExprType (rt) if exprBecomesFunction(sym) =>
73
+ defn.ByNameFunction (rt)
74
+ case tp : MethodType =>
75
+ def exprToFun (tp : Type ) = tp match
76
+ case ExprType (rt) => defn.ByNameFunction (rt)
77
+ case tp => tp
78
+ tp.derivedLambdaType(
79
+ paramInfos = tp.paramInfos.mapConserve(exprToFun),
80
+ resType = transformInfo(tp.resType, sym))
81
+ case tp : PolyType =>
82
+ tp.derivedLambdaType(resType = transformInfo(tp.resType, sym))
83
+ case _ => tp
84
+ }
85
+
86
+ override def infoMayChange (sym : Symbol )(using Context ): Boolean =
87
+ sym.is(Method ) || exprBecomesFunction(sym)
88
+
89
+ def byNameClosure (arg : Tree , argType : Type )(using Context ): Tree =
90
+ val meth = newAnonFun(ctx.owner, MethodType (Nil , argType), coord = arg.span)
91
+ Closure (meth,
92
+ _ => arg.changeOwnerAfter(ctx.owner, meth, thisPhase),
93
+ targetType = defn.ByNameFunction (argType)
94
+ ).withSpan(arg.span)
40
95
41
- /** Map `tree` to `tree.apply()` is `ftree` was of ExprType and becomes now a function */
42
- private def applyIfFunction (tree : Tree , ftree : Tree )(using Context ) =
43
- if (isByNameRef(ftree)) {
96
+ private def isByNameRef (tree : Tree )(using Context ): Boolean =
97
+ defn.isByNameFunction(tree.tpe.widen)
98
+
99
+ /** Map `tree` to `tree.apply()` is `tree` is of type `() ?=> T` */
100
+ private def applyIfFunction (tree : Tree )(using Context ) =
101
+ if isByNameRef(tree) then
44
102
val tree0 = transformFollowing(tree)
45
- atPhase(next) { tree0.select(defn.Function0_apply ).appliedToNone }
46
- }
103
+ atPhase(next) { tree0.select(defn.ContextFunction0_apply ).appliedToNone }
47
104
else tree
48
105
49
106
override def transformIdent (tree : Ident )(using Context ): Tree =
50
- applyIfFunction(tree, tree )
107
+ applyIfFunction(tree)
51
108
52
109
override def transformSelect (tree : Select )(using Context ): Tree =
53
- applyIfFunction(tree, tree )
110
+ applyIfFunction(tree)
54
111
55
112
override def transformTypeApply (tree : TypeApply )(using Context ): Tree = tree match {
56
113
case TypeApply (Select (_, nme.asInstanceOf_), arg :: Nil ) =>
57
114
// tree might be of form e.asInstanceOf[x.type] where x becomes a function.
58
115
// See pos/t296.scala
59
- applyIfFunction(tree, arg )
116
+ applyIfFunction(tree)
60
117
case _ => tree
61
118
}
62
119
120
+ override def transformApply (tree : Apply )(using Context ): Tree =
121
+ trace(s " transforming ${tree.show} at phase ${ctx.phase}" , show = true ) {
122
+
123
+ def transformArg (arg : Tree , formal : Type ): Tree = formal match
124
+ case defn.ByNameFunction (formalResult) =>
125
+ def stripTyped (t : Tree ): Tree = t match
126
+ case Typed (expr, _) => stripTyped(expr)
127
+ case _ => t
128
+ stripTyped(arg) match
129
+ case Apply (Select (qual, nme.apply), Nil )
130
+ if isByNameRef(qual) && (isPureExpr(qual) || qual.symbol.isAllOf(InlineParam )) =>
131
+ qual
132
+ case _ =>
133
+ if isByNameRef(arg) || arg.symbol.name.is(SuperArgName )
134
+ then arg
135
+ else
136
+ var argType = arg.tpe.widenIfUnstable
137
+ if argType.isBottomType then argType = formalResult
138
+ byNameClosure(arg, argType)
139
+ case _ =>
140
+ arg
141
+
142
+ val mt @ MethodType (_) = tree.fun.tpe.widen
143
+ val args1 = tree.args.zipWithConserve(mt.paramInfos)(transformArg)
144
+ cpy.Apply (tree)(tree.fun, args1)
145
+ }
146
+
63
147
override def transformValDef (tree : ValDef )(using Context ): Tree =
64
148
atPhase(next) {
65
- if ( exprBecomesFunction(tree.symbol))
149
+ if exprBecomesFunction(tree.symbol) then
66
150
cpy.ValDef (tree)(tpt = tree.tpt.withType(tree.symbol.info))
67
151
else tree
68
152
}
69
153
70
- def transformInfo (tp : Type , sym : Symbol )(using Context ): Type = tp match {
71
- case ExprType (rt) => defn.FunctionOf (Nil , rt)
72
- case _ => tp
73
- }
74
-
75
- override def infoMayChange (sym : Symbol )(using Context ): Boolean = sym.isTerm && exprBecomesFunction(sym)
76
- }
77
-
78
- object ElimByName {
79
- val name : String = " elimByName"
80
- }
154
+ object ElimByName :
155
+ val name : String = " elimByName"
0 commit comments