1
+ package dotty .tools
2
+ package dotc
3
+ package transform
4
+
5
+ import core ._
6
+ import Contexts ._
7
+ import Symbols ._
8
+ import Types ._
9
+ import Flags ._
10
+ import SymDenotations .*
11
+ import DenotTransformers .InfoTransformer
12
+ import NameKinds .SuperArgName
13
+ import core .StdNames .nme
14
+ import MegaPhase .*
15
+ import Decorators .*
16
+ import reporting .trace
17
+
18
+ /** This phase translates arguments to call-by-name parameters, using the rules
19
+ *
20
+ * x ==> x if x is a => parameter
21
+ * e.apply() ==> <cbn-arg>(e) if e is pure
22
+ * e ==> <cbn-arg>(() => e) for all other arguments
23
+ *
24
+ * where
25
+ *
26
+ * <cbn-arg>: [T](() => T): T
27
+ *
28
+ * is a synthetic method defined in Definitions. Erasure will later strip the <cbn-arg> wrappers.
29
+ */
30
+ class ElimByNameParams extends MiniPhase , InfoTransformer :
31
+ thisPhase =>
32
+
33
+ import ast .tpd ._
34
+
35
+ override def phaseName : String = ElimByNameParams .name
36
+
37
+ override def runsAfterGroupsOf : Set [String ] = Set (ExpandSAMs .name, ElimRepeated .name)
38
+ // - ExpanSAMs applied to partial functions creates methods that need
39
+ // to be fully defined before converting. Test case is pos/i9391.scala.
40
+ // - ByNameLambda needs to run in a group after ElimRepeated since ElimRepeated
41
+ // works on simple arguments but not converted closures, and it sees the arguments
42
+ // after transformations by subsequent miniphases in the same group.
43
+
44
+ override def changesParents : Boolean = true
45
+ // Expr types in parent type arguments are changed to function types.
46
+
47
+ /** If denotation had an ExprType before, it now gets a function type */
48
+ private def exprBecomesFunction (symd : SymDenotation )(using Context ): Boolean =
49
+ symd.is(Param ) || symd.is(ParamAccessor , butNot = Method )
50
+
51
+ def transformInfo (tp : Type , sym : Symbol )(using Context ): Type = tp match {
52
+ case ExprType (rt) if exprBecomesFunction(sym) =>
53
+ defn.ByNameFunction (rt)
54
+ case tp : MethodType =>
55
+ def exprToFun (tp : Type ) = tp match
56
+ case ExprType (rt) => defn.ByNameFunction (rt)
57
+ case tp => tp
58
+ tp.derivedLambdaType(
59
+ paramInfos = tp.paramInfos.mapConserve(exprToFun),
60
+ resType = transformInfo(tp.resType, sym))
61
+ case tp : PolyType =>
62
+ tp.derivedLambdaType(resType = transformInfo(tp.resType, sym))
63
+ case _ => tp
64
+ }
65
+
66
+ override def infoMayChange (sym : Symbol )(using Context ): Boolean =
67
+ sym.is(Method ) || exprBecomesFunction(sym)
68
+
69
+ def byNameClosure (arg : Tree , argType : Type )(using Context ): Tree =
70
+ val meth = newAnonFun(ctx.owner, MethodType (Nil , argType), coord = arg.span)
71
+ Closure (meth,
72
+ _ => arg.changeOwnerAfter(ctx.owner, meth, thisPhase),
73
+ targetType = defn.ByNameFunction (argType)
74
+ ).withSpan(arg.span)
75
+
76
+ private def isByNameRef (tree : Tree )(using Context ): Boolean =
77
+ defn.isByNameFunction(tree.tpe.widen)
78
+
79
+ /** Map `tree` to `tree.apply()` is `tree` is of type `() ?=> T` */
80
+ private def applyIfFunction (tree : Tree )(using Context ) =
81
+ if isByNameRef(tree) then
82
+ val tree0 = transformFollowing(tree)
83
+ atPhase(next) { tree0.select(defn.ContextFunction0_apply ).appliedToNone }
84
+ else tree
85
+
86
+ override def transformIdent (tree : Ident )(using Context ): Tree =
87
+ applyIfFunction(tree)
88
+
89
+ override def transformSelect (tree : Select )(using Context ): Tree =
90
+ applyIfFunction(tree)
91
+
92
+ override def transformTypeApply (tree : TypeApply )(using Context ): Tree = tree match {
93
+ case TypeApply (Select (_, nme.asInstanceOf_), arg :: Nil ) =>
94
+ // tree might be of form e.asInstanceOf[x.type] where x becomes a function.
95
+ // See pos/t296.scala
96
+ applyIfFunction(tree)
97
+ case _ => tree
98
+ }
99
+
100
+ override def transformApply (tree : Apply )(using Context ): Tree =
101
+ trace(s " transforming ${tree.show} at phase ${ctx.phase}" , show = true ) {
102
+
103
+ def transformArg (arg : Tree , formal : Type ): Tree = formal match
104
+ case defn.ByNameFunction (formalResult) =>
105
+ def stripTyped (t : Tree ): Tree = t match
106
+ case Typed (expr, _) => stripTyped(expr)
107
+ case _ => t
108
+ stripTyped(arg) match
109
+ case Apply (Select (qual, nme.apply), Nil )
110
+ if isByNameRef(qual) && (isPureExpr(qual) || qual.symbol.isAllOf(InlineParam )) =>
111
+ qual
112
+ case _ =>
113
+ if isByNameRef(arg) || arg.symbol.name.is(SuperArgName )
114
+ then arg
115
+ else
116
+ var argType = arg.tpe.widenIfUnstable
117
+ if argType.isBottomType then argType = formalResult
118
+ byNameClosure(arg, argType)
119
+ case _ =>
120
+ arg
121
+
122
+ val mt @ MethodType (_) = tree.fun.tpe.widen
123
+ val args1 = tree.args.zipWithConserve(mt.paramInfos)(transformArg)
124
+ cpy.Apply (tree)(tree.fun, args1)
125
+ }
126
+
127
+ override def transformValDef (tree : ValDef )(using Context ): Tree =
128
+ atPhase(next) {
129
+ if exprBecomesFunction(tree.symbol) then
130
+ cpy.ValDef (tree)(tpt = tree.tpt.withType(tree.symbol.info))
131
+ else tree
132
+ }
133
+
134
+ object ElimByNameParams :
135
+ val name : String = " elimByNameParams"
0 commit comments