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