Skip to content

Commit 8436d52

Browse files
committed
Erase arguments of phantom type.
This commit removes arguments and parameter of phantom type from all applications. Arguments are evaluated before the application in case they have side effects. ``` def foo(i: Int, p: SomePhantom) = ??? foo(42, { println("hello"); getSomePhantom }) ``` becomes ``` def foo(i: Int) = ??? val x$0 = 42 val x$1 = { println("hello"); getSomePhantom } foo(x$0) ``` Note that now `def foo(i: Int)` and def `foo(i: Int, p: SomePhantom)` erase to the same signature. Tests for this commit where added in `Extra tests for Phantoms 1`, they where back ported as the semantics and runtime is not affected.
1 parent 1071067 commit 8436d52

File tree

6 files changed

+123
-2
lines changed

6 files changed

+123
-2
lines changed

compiler/src/dotty/tools/dotc/Compiler.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,9 @@ class Compiler {
7474
new AugmentScala2Traits, // Expand traits defined in Scala 2.11 to simulate old-style rewritings
7575
new ResolveSuper, // Implement super accessors and add forwarders to trait methods
7676
new PrimitiveForwarders, // Add forwarders to trait methods that have a mismatch between generic and primitives
77-
new ArrayConstructors), // Intercept creation of (non-generic) arrays and intrinsify.
77+
new ArrayConstructors, // Intercept creation of (non-generic) arrays and intrinsify.
78+
new PhantomTermEval), // Extracts the evaluation of phantom arguments placing them before the call.
79+
List(new PhantomTermErasure), // Erases phantom parameters and arguments
7880
List(new PhantomTypeErasure), // Erases phantom types to ErasedPhantom
7981
List(new Erasure), // Rewrite types to JVM model, erasing all type parameters, abstract types and refinements.
8082
List(new ElimErasedValueType, // Expand erased value types to their underlying implmementation types

compiler/src/dotty/tools/dotc/core/Phases.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import config.Printers.config
1313
import scala.collection.mutable.{ListBuffer, ArrayBuffer}
1414
import dotty.tools.dotc.transform.TreeTransforms.{TreeTransformer, MiniPhase, TreeTransform}
1515
import dotty.tools.dotc.transform._
16+
import dotty.tools.dotc.transform.phantom._
1617
import Periods._
1718
import typer.{FrontEnd, RefChecks}
1819
import ast.tpd
@@ -309,6 +310,7 @@ object Phases {
309310

310311
private var myPeriod: Period = Periods.InvalidPeriod
311312
private var myBase: ContextBase = null
313+
private var myErasedPhantomTerms = false
312314
private var myErasedTypes = false
313315
private var myFlatClasses = false
314316
private var myRefChecked = false
@@ -326,6 +328,7 @@ object Phases {
326328
def start = myPeriod.firstPhaseId
327329
def end = myPeriod.lastPhaseId
328330

331+
final def erasedPhantomTerms = myErasedPhantomTerms // Phase is after phantom terms
329332
final def erasedTypes = myErasedTypes // Phase is after erasure
330333
final def flatClasses = myFlatClasses // Phase is after flatten
331334
final def refChecked = myRefChecked // Phase is after RefChecks
@@ -337,6 +340,7 @@ object Phases {
337340
assert(myPeriod == Periods.InvalidPeriod, s"phase $this has already been used once; cannot be reused")
338341
myBase = base
339342
myPeriod = Period(NoRunId, start, end)
343+
myErasedPhantomTerms = prev.getClass == classOf[PhantomTermErasure] || prev.erasedPhantomTerms
340344
myErasedTypes = prev.getClass == classOf[Erasure] || prev.erasedTypes
341345
myFlatClasses = prev.getClass == classOf[Flatten] || prev.flatClasses
342346
myRefChecked = prev.getClass == classOf[RefChecks] || prev.refChecked
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package dotty.tools.dotc.transform.phantom
2+
3+
import dotty.tools.dotc.ast.Trees._
4+
import dotty.tools.dotc.ast.tpd
5+
import dotty.tools.dotc.core.Constants._
6+
import dotty.tools.dotc.core.Contexts._
7+
import dotty.tools.dotc.core.DenotTransformers._
8+
import dotty.tools.dotc.core.Flags._
9+
import dotty.tools.dotc.core.Symbols._
10+
import dotty.tools.dotc.core.Types._
11+
import dotty.tools.dotc.transform.TreeTransforms.{MiniPhaseTransform, TransformerInfo}
12+
13+
class PhantomTermErasure extends MiniPhaseTransform with InfoTransformer {
14+
import tpd._
15+
16+
override def phaseName: String = "phantomTermErasure"
17+
18+
/** Check what the phase achieves, to be called at any point after it is finished. */
19+
override def checkPostCondition(tree: Tree)(implicit ctx: Context): Unit = {
20+
def assertNotPhantom(tree: Tree): Unit =
21+
assert(!tree.tpe.isPhantom, "All phantom type values should be erased in " + tree)
22+
tree match {
23+
case Apply(_, args) => args.foreach(assertNotPhantom)
24+
case DefDef(_, _, vparamss, tpt, _) => vparamss.foreach(_.foreach(assertNotPhantom))
25+
case _ =>
26+
}
27+
}
28+
29+
/* Tree transform */
30+
31+
override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree =
32+
cpy.Apply(tree)(tree.fun, tree.args.filter(!_.tpe.isPhantom))
33+
34+
override def transformDefDef(ddef: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree =
35+
cpy.DefDef(ddef)(vparamss = ddef.vparamss.map(_.filter(!_.tpt.typeOpt.isPhantom)))
36+
37+
override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo): Tree =
38+
if (tree.symbol.is(Param) && tree.tpe.isPhantom) Literal(Constant(null)).withType(tree.tpe) else tree
39+
40+
/* Symbol transform */
41+
42+
def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = erasedPhantomParameters(tp)
43+
44+
/* private methods */
45+
46+
private def erasedPhantomParameters(tp: Type)(implicit ctx: Context): Type = tp match {
47+
case tp: JavaMethodType => tp
48+
case tp: MethodType =>
49+
val methodType = if (tp.isImplicit) ImplicitMethodType else MethodType
50+
val (erasedParamNames, erasedParamTypes) =
51+
tp.paramNames.zip(tp.paramTypes).filterNot(_._2.isPhantom).unzip
52+
val erasedReturnType = erasedPhantomParameters(tp.resultType)
53+
methodType(erasedParamNames, erasedParamTypes, erasedReturnType)
54+
case tp: PolyType =>
55+
val erasedReturnType = erasedPhantomParameters(tp.resultType)
56+
tp.derivedPolyType(tp.paramNames, tp.paramBounds, erasedReturnType)
57+
case _ => tp
58+
}
59+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package dotty.tools.dotc.transform.phantom
2+
3+
import dotty.tools.dotc.ast.tpd
4+
import dotty.tools.dotc.core.Contexts._
5+
import dotty.tools.dotc.core.Decorators._
6+
import dotty.tools.dotc.core.Types._
7+
import dotty.tools.dotc.transform.TreeTransforms.{MiniPhaseTransform, TransformerInfo}
8+
9+
class PhantomTermEval extends MiniPhaseTransform {
10+
import tpd._
11+
12+
override def phaseName: String = "phantomTermEval"
13+
14+
/** Check what the phase achieves, to be called at any point after it is finished. */
15+
override def checkPostCondition(tree: Tree)(implicit ctx: Context): Unit = {
16+
// TODO
17+
}
18+
19+
/* Tree transform */
20+
21+
override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = tree.tpe match {
22+
case _: MethodType => tree
23+
case _ if tree.args.forall(!_.tpe.isPhantom) => tree
24+
case _ =>
25+
val argsVals = tree.args.map(toSynthVal)
26+
27+
def evalArgsInBlock() = Block(argsVals, cpy.Apply(tree)(tree.fun, refs(argsVals)))
28+
def evalFunAndArgsInBlock(fun: Select) = {
29+
val qualVal = toSynthVal(fun.qualifier)
30+
val app = cpy.Apply(tree)(Select(ref(qualVal.symbol), fun.name), refs(argsVals))
31+
Block(qualVal :: argsVals, app)
32+
}
33+
34+
tree.fun match {
35+
case fun: Select =>
36+
if (fun.qualifier.isInstanceOf[New]) evalArgsInBlock()
37+
else evalFunAndArgsInBlock(fun)
38+
case _ => evalArgsInBlock()
39+
}
40+
}
41+
42+
/* private methods */
43+
44+
private def toSynthVal(t: Tree)(implicit ctx: Context) =
45+
SyntheticValDef(ctx.freshName("ev$").toTermName, t)
46+
47+
private def refs(vals: List[Tree])(implicit ctx: Context) =
48+
vals.map(x => ref(x.symbol))
49+
50+
}

compiler/src/dotty/tools/dotc/transform/phantom/PhantomTypeErasure.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import dotty.tools.dotc.ast.tpd
44
import dotty.tools.dotc.core.Constants.Constant
55
import dotty.tools.dotc.core.Contexts._
66
import dotty.tools.dotc.core.DenotTransformers._
7+
import dotty.tools.dotc.core.Phases._
78
import dotty.tools.dotc.core.Symbols._
89
import dotty.tools.dotc.core.Types._
910
import dotty.tools.dotc.transform.TreeTransforms.{MiniPhaseTransform, TransformerInfo}
@@ -14,6 +15,9 @@ class PhantomTypeErasure extends MiniPhaseTransform with InfoTransformer {
1415

1516
override def phaseName: String = "phantomTypeErasure"
1617

18+
/** List of names of phases that should precede this phase */
19+
override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[PhantomTermErasure])
20+
1721
/** Check what the phase achieves, to be called at any point after it is finished. */
1822
override def checkPostCondition(tree: tpd.Tree)(implicit ctx: Context): Unit = {
1923
assert(!tree.tpe.isPhantom, tree.tpe + " should be erased in " + tree)

compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,9 @@ trait TypeAssigner {
318318
def assignType(tree: untpd.Apply, fn: Tree, args: List[Tree])(implicit ctx: Context) = {
319319
val ownType = fn.tpe.widen match {
320320
case fntpe: MethodType =>
321-
if (sameLength(fntpe.paramTypes, args) || ctx.phase.prev.relaxedTyping) fntpe.instantiate(args.tpes)
321+
def sameLengthAfterPhantomErasure =
322+
ctx.phase.erasedPhantomTerms && sameLength(fntpe.paramTypes, args.filterNot(arg => arg.typeOpt.isPhantom))
323+
if (sameLength(fntpe.paramTypes, args) || ctx.phase.prev.relaxedTyping|| sameLengthAfterPhantomErasure) fntpe.instantiate(args.tpes)
322324
else
323325
errorType(i"wrong number of arguments for $fntpe: ${fn.tpe}, expected: ${fntpe.paramTypes.length}, found: ${args.length}", tree.pos)
324326
case t =>

0 commit comments

Comments
 (0)