Skip to content

Commit a6beba4

Browse files
committed
Use EtaExpansion.liftApp for impure phantom arguments
1 parent 4afa04e commit a6beba4

File tree

5 files changed

+82
-108
lines changed

5 files changed

+82
-108
lines changed

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ class Compiler {
6969
new ShortcutImplicits, // Allow implicit functions without creating closures
7070
new CrossCastAnd, // Normalize selections involving intersection types.
7171
new Splitter), // Expand selections involving union types into conditionals
72-
List(new PhantomArgumentEval, // Extracts the evaluation of phantom arguments placing them before the call.
72+
List(new PhantomArgLift, // Extracts the evaluation of phantom arguments placing them before the call.
7373
new VCInlineMethods, // Inlines calls to value class methods
7474
new SeqLiterals, // Express vararg arguments as arrays
7575
new InterceptedMethods, // Special handling of `==`, `|=`, `getClass` methods

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import dotty.tools.dotc.core.Types.Type
77

88
/** Phantom erasure erases:
99
*
10-
* - Parameters/arguments are removed from the function definition/call in `PhantomArgumentEval`.
10+
* - Parameters/arguments are removed from the function definition/call in `PhantomArgLift`.
1111
* If the evaluation of the phantom arguments may produce a side effect, these are evaluated and stored in
1212
* local `val`s and then the non phantoms are used in the Apply. Phantom `val`s are then erased to
1313
* `val ev$i: ErasedPhantom = myPhantom` intended to be optimized away by local optimizations. `myPhantom` could be
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package dotty.tools.dotc.transform
2+
3+
import dotty.tools.dotc.ast.tpd
4+
import dotty.tools.dotc.core.Contexts._
5+
import dotty.tools.dotc.core.NameKinds._
6+
import dotty.tools.dotc.core.Types._
7+
import dotty.tools.dotc.transform.TreeTransforms.{MiniPhaseTransform, TransformerInfo}
8+
import dotty.tools.dotc.typer.EtaExpansion
9+
10+
import scala.collection.mutable.ListBuffer
11+
12+
/** This phase extracts the arguments of phantom type before the application to avoid losing any
13+
* effects in the argument tree. This trivializes the removal of parameter in the Erasure phase.
14+
*
15+
* `f(x1,...)(y1,...)...(...)` with at least one phantom argument
16+
*
17+
* -->
18+
*
19+
* `val ev$f = f` // if `f` is some expression that needs evaluation
20+
* `val ev$x1 = x1`
21+
* ...
22+
* `val ev$y1 = y1`
23+
* ...
24+
* `ev$f(ev$x1,...)(ev$y1,...)...(...)`
25+
*
26+
*/
27+
class PhantomArgLift extends MiniPhaseTransform {
28+
import tpd._
29+
30+
override def phaseName: String = "phantomArgLift"
31+
32+
/** Check what the phase achieves, to be called at any point after it is finished. */
33+
override def checkPostCondition(tree: Tree)(implicit ctx: Context): Unit = tree match {
34+
case tree: Apply =>
35+
tree.args.foreach { arg =>
36+
assert(!arg.tpe.isPhantom || isPureExpr(arg))
37+
}
38+
case _ =>
39+
}
40+
41+
/* Tree transform */
42+
43+
override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = tree.tpe.widen match {
44+
case _: MethodType => tree // Do the transformation higher in the tree if needed
45+
case _ =>
46+
if (!hasImpurePhantomArgs(tree)) tree
47+
else {
48+
val buffer = ListBuffer.empty[Tree]
49+
val app = EtaExpansion.liftApp(buffer, tree)
50+
if (buffer.isEmpty) app
51+
else Block(buffer.result(), app)
52+
}
53+
}
54+
55+
override def transformAssign(tree: Assign)(implicit ctx: Context, info: TransformerInfo): Tree = {
56+
if (!tree.rhs.tpe.isPhantom) super.transformAssign(tree)
57+
else {
58+
// Apply the same transformation to setters before their creation.
59+
val synthVal = SyntheticValDef(TempResultName.fresh(), tree.rhs)
60+
val synthValRef = ref(synthVal.symbol)
61+
Block(List(synthVal), Assign(tree.lhs, synthValRef))
62+
}
63+
}
64+
65+
/* private methods */
66+
67+
/** Returns true if at least on of the arguments is an impure phantom.
68+
* Inner applies are also checked in case of multiple parameter list.
69+
*/
70+
private def hasImpurePhantomArgs(tree: Apply)(implicit ctx: Context): Boolean = {
71+
tree.args.exists(arg => arg.tpe.isPhantom && !isPureExpr(arg)) || {
72+
tree.fun match {
73+
case fun: Apply => hasImpurePhantomArgs(fun)
74+
case _ => false
75+
}
76+
}
77+
}
78+
79+
}

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

-105
This file was deleted.

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ class VCInlineMethods extends MiniPhaseTransform with IdentityDenotTransformer {
4444
override def phaseName: String = "vcInlineMethods"
4545

4646
override def runsAfter: Set[Class[_ <: Phase]] =
47-
Set(classOf[ExtensionMethods], classOf[PatternMatcher], classOf[PhantomArgumentEval])
47+
Set(classOf[ExtensionMethods], classOf[PatternMatcher], classOf[PhantomArgLift])
4848

4949
/** Replace a value class method call by a call to the corresponding extension method.
5050
*

0 commit comments

Comments
 (0)