Skip to content

Commit 17b1995

Browse files
committed
Break out by-name-closure creation from ElimByName
1 parent 195fe4a commit 17b1995

File tree

2 files changed

+85
-64
lines changed

2 files changed

+85
-64
lines changed
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package dotty.tools.dotc
2+
package transform
3+
4+
import TreeTransforms._
5+
import core._
6+
import Symbols._
7+
import SymDenotations._
8+
import Contexts._
9+
import Types._
10+
import Flags._
11+
import Decorators._
12+
import core.StdNames.nme
13+
import ast.Trees._
14+
15+
/** This phase translates arguments to call-by-name parameters, using the rules
16+
*
17+
* x ==> x if x is a => parameter
18+
* e.apply() ==> DummyApply(e) if e is pure
19+
* e ==> DummyApply(() => e) for all other arguments
20+
*
21+
* where
22+
*
23+
* DummyApply: [T](() => T): T
24+
*
25+
* is a synthetic method defined in Definitions. Erasure will later strip these DummyApply wrappers.
26+
*/
27+
class ByNameClosures extends MiniPhaseTransform { thisTransformer =>
28+
import ast.tpd._
29+
30+
override def phaseName: String = "bynameClosures"
31+
32+
/** The info of the tree's symbol at phase Nullarify (i.e. before transformation) */
33+
private def originalDenotation(tree: Tree)(implicit ctx: Context) =
34+
tree.symbol.denot(ctx.withPhase(thisTransformer))
35+
36+
/** If denotation had an ExprType before, it now gets a function type */
37+
protected def exprBecomesFunction(symd: SymDenotation)(implicit ctx: Context) =
38+
(symd is Param) || (symd is (ParamAccessor, butNot = Method))
39+
40+
protected def isByNameRef(tree: Tree)(implicit ctx: Context) = {
41+
val origDenot = originalDenotation(tree)
42+
origDenot.info.isInstanceOf[ExprType] && exprBecomesFunction(origDenot)
43+
}
44+
45+
override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree =
46+
ctx.traceIndented(s"transforming ${tree.show} at phase ${ctx.phase}", show = true) {
47+
48+
def transformArg(arg: Tree, formal: Type): Tree = formal.dealias match {
49+
case formalExpr: ExprType =>
50+
var argType = arg.tpe.widenIfUnstable
51+
if (defn.isBottomType(argType)) argType = formal.widenExpr
52+
def wrap(arg: Tree) = ref(defn.dummyApply).appliedToType(argType).appliedTo(arg)
53+
arg match {
54+
case Apply(Select(qual, nme.apply), Nil)
55+
if qual.tpe.derivesFrom(defn.FunctionClass(0)) && isPureExpr(qual) =>
56+
wrap(qual)
57+
case _ =>
58+
if (isByNameRef(arg) || arg.symbol == defn.dummyApply) arg
59+
else {
60+
val inSuper = if (ctx.mode.is(Mode.InSuperCall)) InSuperCall else EmptyFlags
61+
val meth = ctx.newSymbol(
62+
ctx.owner, nme.ANON_FUN, Synthetic | Method | inSuper, MethodType(Nil, Nil, argType))
63+
wrap(Closure(meth, _ =>
64+
atGroupEnd { implicit ctx: Context =>
65+
arg.changeOwner(ctx.owner, meth)
66+
}
67+
))
68+
}
69+
}
70+
case _ =>
71+
arg
72+
}
73+
74+
val mt @ MethodType(_) = tree.fun.tpe.widen
75+
val args1 = tree.args.zipWithConserve(mt.paramInfos)(transformArg)
76+
cpy.Apply(tree)(tree.fun, args1)
77+
}
78+
}

compiler/src/dotty/tools/dotc/transform/ElimByName.scala

Lines changed: 7 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -20,27 +20,12 @@ import ast.Trees._
2020
*
2121
* For the types of parameter symbols:
2222
*
23-
* => T ==> () => T
23+
* => T ==> () => T
2424
*
25-
* Note that `=> T` types are not eliminated in MethodTypes. This is done later at erasure.
26-
* Terms are rewritten as follows:
25+
* For cbn parameter values
2726
*
28-
* x ==> x.apply() if x is a parameter that had type => T
29-
*
30-
* Arguments to call-by-name parameters are translated as follows. First, the argument is
31-
* rewritten by the rules
32-
*
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-
* This makes the argument compatible with a parameter type of () => T, which will be the
37-
* formal parameter type at erasure. But to be -Ycheckable until then, any argument
38-
* ARG rewritten by the rules above is again wrapped in an application DummyApply(ARG)
39-
* where
40-
*
41-
* DummyApply: [T](() => T): T
42-
*
43-
* is a synthetic method defined in Definitions. Erasure will later strip these DummyApply wrappers.
27+
* x ==> x()
28+
* CbnArg(x) ==> DummyApply(x)
4429
*
4530
* Note: This scheme to have inconsistent types between method types (whose formal types are still
4631
* ExprTypes and parameter valdefs (which are now FunctionTypes) is not pretty. There are two
@@ -53,7 +38,7 @@ import ast.Trees._
5338
* Option 2: Merge ElimByName with erasure, or have it run immediately before. This has not been
5439
* tried yet.
5540
*/
56-
class ElimByName extends MiniPhaseTransform with InfoTransformer { thisTransformer =>
41+
class ElimByName extends ByNameClosures with InfoTransformer { thisTransformer =>
5742
import ast.tpd._
5843

5944
override def phaseName: String = "elimByName"
@@ -62,52 +47,10 @@ class ElimByName extends MiniPhaseTransform with InfoTransformer { thisTransform
6247
// assumes idents and selects have symbols; interferes with splitter distribution
6348
// that's why it's "after group".
6449

65-
/** The info of the tree's symbol at phase Nullarify (i.e. before transformation) */
66-
private def originalDenotation(tree: Tree)(implicit ctx: Context) =
67-
tree.symbol.denot(ctx.withPhase(thisTransformer))
68-
69-
override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree =
70-
ctx.traceIndented(s"transforming ${tree.show} at phase ${ctx.phase}", show = true) {
71-
72-
def transformArg(arg: Tree, formal: Type): Tree = formal.dealias match {
73-
case formalExpr: ExprType =>
74-
var argType = arg.tpe.widenIfUnstable
75-
if (defn.isBottomType(argType)) argType = formal.widenExpr
76-
val argFun = arg match {
77-
case Apply(Select(qual, nme.apply), Nil)
78-
if qual.tpe.derivesFrom(defn.FunctionClass(0)) && isPureExpr(qual) =>
79-
qual
80-
case _ =>
81-
val inSuper = if (ctx.mode.is(Mode.InSuperCall)) InSuperCall else EmptyFlags
82-
val meth = ctx.newSymbol(
83-
ctx.owner, nme.ANON_FUN, Synthetic | Method | inSuper, MethodType(Nil, Nil, argType))
84-
Closure(meth, _ =>
85-
atGroupEnd { implicit ctx: Context =>
86-
arg.changeOwner(ctx.owner, meth)
87-
}
88-
)
89-
}
90-
ref(defn.dummyApply).appliedToType(argType).appliedTo(argFun)
91-
case _ =>
92-
arg
93-
}
94-
95-
val mt @ MethodType(_) = tree.fun.tpe.widen
96-
val args1 = tree.args.zipWithConserve(mt.paramInfos)(transformArg)
97-
cpy.Apply(tree)(tree.fun, args1)
98-
}
99-
100-
/** If denotation had an ExprType before, it now gets a function type */
101-
private def exprBecomesFunction(symd: SymDenotation)(implicit ctx: Context) =
102-
(symd is Param) || (symd is (ParamAccessor, butNot = Method))
103-
10450
/** Map `tree` to `tree.apply()` is `ftree` was of ExprType and becomes now a function */
105-
private def applyIfFunction(tree: Tree, ftree: Tree)(implicit ctx: Context) = {
106-
val origDenot = originalDenotation(ftree)
107-
if (exprBecomesFunction(origDenot) && (origDenot.info.isInstanceOf[ExprType]))
108-
tree.select(defn.Function0_apply).appliedToNone
51+
private def applyIfFunction(tree: Tree, ftree: Tree)(implicit ctx: Context) =
52+
if (isByNameRef(ftree)) tree.select(defn.Function0_apply).appliedToNone
10953
else tree
110-
}
11154

11255
override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo): Tree =
11356
applyIfFunction(tree, tree)

0 commit comments

Comments
 (0)