1- package dotty .tools .dotc
1+ package dotty .tools
2+ package dotc
23package transform
34
45import core ._
5- import DenotTransformers .InfoTransformer
6- import Symbols ._
76import Contexts ._
7+ import Symbols ._
88import Types ._
9+ import Flags ._
10+ import SymDenotations .*
11+ import DenotTransformers .InfoTransformer
12+ import NameKinds .SuperArgName
913import core .StdNames .nme
10- import ast .Trees ._
14+ import MegaPhase .*
15+ import Decorators .*
16+ import reporting .trace
1117
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:
1419 *
15- * For the types of parameter symbols :
20+ * 1. For types of method and class parameters :
1621 *
17- * => T ==> () => T
22+ * => T becomes () ? => T
1823 *
19- * For cbn parameter values
24+ * 2. For references to cbn-parameters:
2025 *
21- * x ==> x ()
26+ * x becomes x.apply ()
2227 *
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
2629 *
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
3031 *
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.
3349 */
34- class ElimByName extends TransformByNameApply with InfoTransformer {
50+ class ElimByName extends MiniPhase , InfoTransformer :
51+ thisPhase =>
52+
3553 import ast .tpd ._
3654
3755 override def phaseName : String = ElimByName .name
3856
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)
4095
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
44102 val tree0 = transformFollowing(tree)
45- atPhase(next) { tree0.select(defn.Function0_apply ).appliedToNone }
46- }
103+ atPhase(next) { tree0.select(defn.ContextFunction0_apply ).appliedToNone }
47104 else tree
48105
49106 override def transformIdent (tree : Ident )(using Context ): Tree =
50- applyIfFunction(tree, tree )
107+ applyIfFunction(tree)
51108
52109 override def transformSelect (tree : Select )(using Context ): Tree =
53- applyIfFunction(tree, tree )
110+ applyIfFunction(tree)
54111
55112 override def transformTypeApply (tree : TypeApply )(using Context ): Tree = tree match {
56113 case TypeApply (Select (_, nme.asInstanceOf_), arg :: Nil ) =>
57114 // tree might be of form e.asInstanceOf[x.type] where x becomes a function.
58115 // See pos/t296.scala
59- applyIfFunction(tree, arg )
116+ applyIfFunction(tree)
60117 case _ => tree
61118 }
62119
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+
63147 override def transformValDef (tree : ValDef )(using Context ): Tree =
64148 atPhase(next) {
65- if ( exprBecomesFunction(tree.symbol))
149+ if exprBecomesFunction(tree.symbol) then
66150 cpy.ValDef (tree)(tpt = tree.tpt.withType(tree.symbol.info))
67151 else tree
68152 }
69153
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