aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc/transform/VCInlineMethods.scala
diff options
context:
space:
mode:
authorGuillaume Martres <smarter@ubuntu.com>2015-07-04 18:10:59 +0200
committerGuillaume Martres <smarter@ubuntu.com>2015-07-07 21:44:46 +0200
commit3bf22d31148ab077d34fa0a94b3f9e926719e81b (patch)
tree461c86d50f034de48f132a0a3a4a7a89ef716c15 /src/dotty/tools/dotc/transform/VCInlineMethods.scala
parent58e2c9b429dc53a865fdcfd60459966513110058 (diff)
downloaddotty-3bf22d31148ab077d34fa0a94b3f9e926719e81b.tar.gz
dotty-3bf22d31148ab077d34fa0a94b3f9e926719e81b.tar.bz2
dotty-3bf22d31148ab077d34fa0a94b3f9e926719e81b.zip
Move the inlining of value class methods before Erasure
VCInline is split into two phases: - VCInlineMethods (before Erasure) replaces value class method calls by calls to extension methods - VCElideAllocations (after Erasure) handles == and optimizing the unboxing of a boxed value class, as VCInline did before. This should not affect anything currently, but in the future we will have phases before Erasure that mangle names (like TypeSpecializer, see #630), being able to put these phases after VCInlineMethods means that VCInlineMethods does not need to know anything about how these phases mangle names, this reduces the coupling between phases. The trade-off is that VCInlineMethods needs to deal with type parameters and multiple parameter lists whereas VCInline didn't.
Diffstat (limited to 'src/dotty/tools/dotc/transform/VCInlineMethods.scala')
-rw-r--r--src/dotty/tools/dotc/transform/VCInlineMethods.scala104
1 files changed, 104 insertions, 0 deletions
diff --git a/src/dotty/tools/dotc/transform/VCInlineMethods.scala b/src/dotty/tools/dotc/transform/VCInlineMethods.scala
new file mode 100644
index 000000000..1c2b015a1
--- /dev/null
+++ b/src/dotty/tools/dotc/transform/VCInlineMethods.scala
@@ -0,0 +1,104 @@
+package dotty.tools.dotc
+package transform
+
+import ast.{Trees, tpd}
+import core._, core.Decorators._
+import Contexts._, Trees._, Types._
+import DenotTransformers._, TreeTransforms._, Phases.Phase
+import ExtensionMethods._, ValueClasses._
+
+import collection.mutable.ListBuffer
+
+/** This phase inlines calls to methods of value classes.
+ *
+ * A value class V after [[ExtensionMethods]] will look like:
+ * class V[A, B, ...](val underlying: U) extends AnyVal {
+ * def foo[T, S, ...](arg1: A1, arg2: A2, ...) =
+ * V.foo$extension[T, S, ..., A, B, ...](this)(arg1, arg2, ...)
+ *
+ * ...
+ * }
+ *
+ * Let e have type V, if e is a stable prefix or if V does not have any class
+ * type parameter, then we can rewrite:
+ * e.foo[X, Y, ...](args)
+ * as:
+ * V.foo$extension[X, Y, ..., e.A, e.B, ...](e)(args)
+ * Otherwise, we need to evaluate e first:
+ * {
+ * val ev = e
+ * V.foo$extension[X, Y, ..., ev.A, ev.B, ...](ev)(args)
+ * }
+ *
+ * This phase needs to be placed after phases which may introduce calls to
+ * value class methods (like [[PatternMatcher]]). This phase uses name mangling
+ * to find the correct extension method corresponding to a value class method
+ * (see [[ExtensionMethods.extensionMethod]]), therefore we choose to place it
+ * before phases which may perform their own name mangling on value class
+ * methods (like [[TypeSpecializer]]), this way [[VCInlineMethods]] does not
+ * need to have any knowledge of the name mangling done by other phases.
+ */
+class VCInlineMethods extends MiniPhaseTransform with IdentityDenotTransformer {
+ import tpd._
+
+ override def phaseName: String = "vcInlineMethods"
+
+ override def runsAfter: Set[Class[_ <: Phase]] =
+ Set(classOf[ExtensionMethods], classOf[PatternMatcher])
+
+ /** Replace a value class method call by a call to the corresponding extension method.
+ *
+ * @param tree The tree corresponding to the method call
+ * @param mtArgs Type arguments for the method call not present in `tree`
+ * @param mArgss Arguments for the method call not present in `tree`
+ * @return A tree for the extension method call
+ */
+ private def rewire(tree: Tree, mtArgs: List[Tree] = Nil, mArgss: List[List[Tree]] = Nil)
+ (implicit ctx: Context): Tree =
+ tree match {
+ case Apply(qual, mArgs) =>
+ rewire(qual, mtArgs, mArgs :: mArgss)
+ case TypeApply(qual, mtArgs2) =>
+ assert(mtArgs == Nil)
+ rewire(qual, mtArgs2, mArgss)
+ case sel @ Select(qual, _) =>
+ val origMeth = sel.symbol
+ val ctParams = origMeth.enclosingClass.typeParams
+ val extensionMeth = extensionMethod(origMeth)
+
+ if (!ctParams.isEmpty) {
+ evalOnce(qual) { ev =>
+ val ctArgs = ctParams map (ev.select(_))
+ ref(extensionMeth)
+ .appliedToTypeTrees(mtArgs ++ ctArgs)
+ .appliedTo(ev)
+ .appliedToArgss(mArgss)
+ }
+ } else {
+ ref(extensionMeth)
+ .appliedToTypeTrees(mtArgs)
+ .appliedTo(qual)
+ .appliedToArgss(mArgss)
+ }
+ }
+
+ /** If this tree corresponds to a fully-applied value class method call, replace it
+ * by a call to the corresponding extension method, otherwise return it as is.
+ */
+ private def rewireIfNeeded(tree: Tree)(implicit ctx: Context) = tree.tpe.widen match {
+ case tp: MethodOrPoly =>
+ tree // The rewiring will be handled by a fully-applied parent node
+ case _ =>
+ if (isMethodWithExtension(tree.symbol))
+ rewire(tree)
+ else
+ tree
+ }
+
+ override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo): Tree =
+ rewireIfNeeded(tree)
+ override def transformTypeApply(tree: TypeApply)(implicit ctx: Context, info: TransformerInfo): Tree =
+ rewireIfNeeded(tree)
+ override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree =
+ rewireIfNeeded(tree)
+}