Skip to content

Commit 0aa3f4c

Browse files
oderskyDarkDimius
authored andcommitted
New phase: Nullarify
Eliminates ExprTypes and PolyTypes over value types.
1 parent 2a681ec commit 0aa3f4c

File tree

7 files changed

+175
-8
lines changed

7 files changed

+175
-8
lines changed

src/dotty/tools/dotc/Compiler.scala

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,14 @@ class Compiler {
2020
def phases: List[List[Phase]] =
2121
List(
2222
List(new FrontEnd),
23-
List(new LazyValsCreateCompanionObjects, new TailRec), //force separataion between lazyVals and LVCreateCO
24-
List(new PatternMatcher, new LazyValTranformContext().transformer,
25-
new Splitter, new TypeTestsCasts, new InterceptedMethods),
23+
List(new LazyValsCreateCompanionObjects,
24+
new TailRec), //force separataion between lazyVals and LVCreateCO
25+
List(new PatternMatcher,
26+
new LazyValTranformContext().transformer,
27+
new Splitter),
28+
List(new Nullarify,
29+
new TypeTestsCasts,
30+
new InterceptedMethods),
2631
List(new Erasure),
2732
List(new UncurryTreeTransform)
2833
)

src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,8 @@ class Definitions {
341341

342342
lazy val AbstractFunctionClass = mkArityArray("scala.runtime.AbstractFunction", MaxFunctionArity, 0)
343343
lazy val FunctionClass = mkArityArray("scala.Function", MaxFunctionArity, 0)
344+
lazy val Function0_apply = FunctionClass(0).requiredMethod(nme.apply)
345+
344346
lazy val TupleClass = mkArityArray("scala.Tuple", MaxTupleArity, 2)
345347
lazy val ProductNClass = mkArityArray("scala.Product", MaxTupleArity, 2)
346348

@@ -357,6 +359,7 @@ class Definitions {
357359

358360
lazy val asInstanceOfMethods = Set[Symbol](Any_asInstanceOf, Object_asInstanceOf)
359361
lazy val isInstanceOfMethods = Set[Symbol](Any_isInstanceOf, Object_isInstanceOf)
362+
lazy val typeTestsOrCasts = asInstanceOfMethods ++ isInstanceOfMethods
360363

361364
lazy val RootImports = List[Symbol](JavaLangPackageVal, ScalaPackageVal, ScalaPredefModule, DottyPredefModule)
362365

@@ -440,7 +443,7 @@ class Definitions {
440443
LongClass,
441444
FloatClass,
442445
DoubleClass)
443-
446+
444447
lazy val ScalaValueClasses: collection.Set[Symbol] = ScalaNumericValueClasses + UnitClass + BooleanClass
445448

446449
lazy val ScalaBoxedClasses = ScalaValueClasses map boxedClass

src/dotty/tools/dotc/transform/Erasure.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class Erasure extends Phase with DenotTransformer {
2828
override def name: String = "erasure"
2929

3030
/** List of names of phases that should precede this phase */
31-
override def runsAfter: Set[String] = Set("typeTestsCasts", "intercepted", "splitter")
31+
override def runsAfter: Set[String] = Set("typeTestsCasts", "intercepted", "splitter", "nullarify")
3232

3333
def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = ref match {
3434
case ref: SymDenotation =>

src/dotty/tools/dotc/transform/LazyVals.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ class LazyValTranformContext {
6868
override def runsAfterGroupsOf: Set[String] = Set("lazyValsModules")
6969
/** List of names of phases that should have finished their processing of all compilation units
7070
* before this phase starts */
71-
override def runsAfter: Set[String] = Set("lazyValsModules")
7271

7372
/** List of names of phases that should have finished processing of tree
7473
* before this phase starts processing same tree */
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
package dotty.tools.dotc
2+
package transform
3+
4+
import TreeTransforms._
5+
import core.DenotTransformers._
6+
import core.Symbols._
7+
import core.Contexts._
8+
import core.Types._
9+
import core.Flags._
10+
import core.Decorators._
11+
import core.StdNames.nme
12+
import ast.Trees._
13+
14+
/** This phase eliminates ExprTypes `=> T` and PolyTypes over value types `[X]T`.
15+
* They are expressed in terms of nullary method or function types. More precisely:
16+
*
17+
* For types:
18+
*
19+
* => T ==> () => T if T is the type of a parameter
20+
* ==> ()T otherwise
21+
* [X]T ==> [X]()T
22+
*
23+
* For definitions:
24+
*
25+
* def f: R ==> def f(): R
26+
* def f[X]: R ==> def f[X](): R
27+
* (x: => T) ==> (x: () => T)
28+
*
29+
* For terms:
30+
*
31+
* f ==> f() if f had type => T and is not a parameter
32+
* x ==> x.apply() if x is a parameter that had type => T
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+
*/
37+
class Nullarify extends TreeTransform with InfoTransformer {
38+
import ast.tpd._
39+
40+
override def name: String = "nullarify"
41+
42+
override def runsAfterGroupsOf: Set[String] = Set("splitter")
43+
// assumes idents and selects have symbols; interferes with splitter distribution
44+
// that's why it's "after group".
45+
46+
override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree =
47+
ctx.traceIndented(s"transforming ${tree.show} at phase ${ctx.phase}", show = true) {
48+
49+
def transformArg(arg: Tree, formal: Type): Tree = formal match {
50+
case _: ExprType =>
51+
arg match {
52+
case Apply(Select(qual, nme.apply), Nil) => qual
53+
case _ =>
54+
val meth = ctx.newSymbol(ctx.owner, nme.ANON_FUN, Synthetic,
55+
MethodType(Nil, Nil, arg.tpe.widen))
56+
Closure(meth, _ => arg)
57+
}
58+
case _ =>
59+
arg
60+
}
61+
62+
// Compute the method type tree had before this phase is run.
63+
// This is needed to find out which parameters are by-name.
64+
val funType = tree.fun.symbol.info match {
65+
case info: PolyType => info.resultType
66+
case info => info
67+
}
68+
def methType(info: Type, tree: Tree): Type = tree match {
69+
case Apply(fn, args) => methType(info.resultType, fn)
70+
case _ => info
71+
}
72+
val MethodType(_, formals) = methType(funType, tree.fun)
73+
74+
val args1 = tree.args.zipWithConserve(formals)(transformArg)
75+
cpy.Apply(tree, tree.fun, args1) withType nullarify(tree.tpe)
76+
}
77+
78+
/** Insert () or .apply() if the term refers to something that was converted to a
79+
* nullary method. Also, transform its type.
80+
*/
81+
def insertParens(tree: Tree)(implicit ctx: Context): Tree = {
82+
val tp1 = transformInfo(tree.tpe, tree.symbol)
83+
val tree1 = tree.withType(tp1)
84+
val origType = tree.tpe.widenSingleton
85+
def result(implicit ctx: Context) = {
86+
tp1.widen match {
87+
case MethodType(Nil, _) if origType.widenExpr.isInstanceOf[ValueType] =>
88+
Apply(tree1, Nil)
89+
case _ =>
90+
origType match {
91+
case _: ExprType => // it's a by-name parameter
92+
Apply(Select(tree1, defn.Function0_apply), Nil)
93+
case _ =>
94+
tree1
95+
}
96+
}
97+
}
98+
result(ctx.withPhase(ctx.phase.next))
99+
}
100+
101+
override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo): Tree =
102+
insertParens(tree)
103+
104+
override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo): Tree =
105+
insertParens(tree)
106+
107+
override def transformTypeApply(tree: TypeApply)(implicit ctx: Context, info: TransformerInfo): Tree =
108+
insertParens(tree)
109+
110+
override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = {
111+
val DefDef(mods, name, tparams, vparamss, tpt, rhs) = tree
112+
val vparamss1 =
113+
if (vparamss.isEmpty) Nil :: Nil
114+
else vparamss nestedMap { vparam =>
115+
val tp = vparam.tpt.tpe
116+
val tp1 = nullarifyParam(tp)
117+
if (tp eq tp1) vparam
118+
else cpy.ValDef(vparam, vparam.mods, vparam.name, vparam.tpt.withType(tp1), vparam.rhs)
119+
}
120+
cpy.DefDef(tree, mods, name, tparams, vparamss1, tpt, rhs)
121+
}
122+
123+
def nullarify(tp: Type)(implicit ctx: Context): Type = tp match {
124+
case ExprType(rt) =>
125+
MethodType(Nil, Nil, rt)
126+
case pt: PolyType =>
127+
val rt = pt.resultType match {
128+
case mt: MethodType => nullarify(mt)
129+
case rt => MethodType(Nil, Nil, rt)
130+
}
131+
pt.derivedPolyType(pt.paramNames, pt.paramBounds, rt)
132+
case mt: MethodType =>
133+
mt.derivedMethodType(mt.paramNames, mt.paramTypes mapConserve nullarifyParam,
134+
nullarify(mt.resultType))
135+
case _ =>
136+
tp
137+
}
138+
139+
def nullarifyParam(tp: Type)(implicit ctx: Context) = tp match {
140+
case ExprType(rt) => defn.FunctionType(Nil, rt)
141+
case _ => tp
142+
}
143+
144+
def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type =
145+
if (defn.typeTestsOrCasts contains sym) tp
146+
else if (sym is Param) nullarifyParam(tp)
147+
else nullarify(tp)
148+
}

test/dotc/tests.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,7 @@ class tests extends CompilerTest {
4848
@Test def pos_overloadedAccess = compileFile(posDir, "overloadedAccess", doErase)
4949
@Test def pos_approximateUnion = compileFile(posDir, "approximateUnion", doErase)
5050
@Test def pos_tailcall = compileDir(posDir + "tailcall/", doErase)
51-
52-
51+
@Test def pos_nullarify = compileFile(posDir, "nullarify", "-Ycheck:nullarify" :: doErase)
5352

5453
@Test def pos_all = compileFiles(posDir, twice)
5554
@Test def new_all = compileFiles(newDir, twice)

tests/pos/nullarify.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
object Test {
2+
3+
def foo: Int = 2
4+
5+
println(foo)
6+
7+
def bar(x: => Int) = x + baz(x)
8+
9+
def baz(y: => Int) = y
10+
11+
bar(foo)
12+
13+
}

0 commit comments

Comments
 (0)