|
| 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 | +} |
0 commit comments