From 411d5be477cc862b14d8938c591524d8bf37d4cd Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Sat, 4 Apr 2015 01:15:16 +0200 Subject: New phase: VCInline which inlines value classes calls This corresponds roughly to step 2 of SIP-15 and to the peephole optimizations of step 3. The extractors in TreeExtractors are copied or inspired from src/compiler/scala/tools/nsc/ast/TreeInfo.scala in scalac. --- src/dotty/tools/dotc/Compiler.scala | 1 + .../tools/dotc/transform/InterceptedMethods.scala | 1 + .../tools/dotc/transform/TreeExtractors.scala | 48 ++++++++++++++++++ src/dotty/tools/dotc/transform/VCInline.scala | 59 ++++++++++++++++++++++ 4 files changed, 109 insertions(+) create mode 100644 src/dotty/tools/dotc/transform/TreeExtractors.scala create mode 100644 src/dotty/tools/dotc/transform/VCInline.scala diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index dc92187db..2b5748229 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -57,6 +57,7 @@ class Compiler { new ResolveSuper), List(new Erasure), List(new ElimErasedValueType, + new VCInline, new Mixin, new LazyVals, new Memoize, diff --git a/src/dotty/tools/dotc/transform/InterceptedMethods.scala b/src/dotty/tools/dotc/transform/InterceptedMethods.scala index 725910949..ff354a54c 100644 --- a/src/dotty/tools/dotc/transform/InterceptedMethods.scala +++ b/src/dotty/tools/dotc/transform/InterceptedMethods.scala @@ -27,6 +27,7 @@ import scala.collection.mutable.ListBuffer import dotty.tools.dotc.core.Denotations.SingleDenotation import dotty.tools.dotc.core.SymDenotations.SymDenotation import StdNames._ +import Phases.Phase /** Replace member references as follows: * diff --git a/src/dotty/tools/dotc/transform/TreeExtractors.scala b/src/dotty/tools/dotc/transform/TreeExtractors.scala new file mode 100644 index 000000000..7a5c5df9d --- /dev/null +++ b/src/dotty/tools/dotc/transform/TreeExtractors.scala @@ -0,0 +1,48 @@ +package dotty.tools.dotc +package transform + +import ast.{Trees, tpd} +import core._, core.Decorators._ +import Contexts._, Flags._, Trees._, Types._, StdNames._, Symbols._ +import ValueClasses._ + +object TreeExtractors { + import tpd._ + + /** Match arg1.op(arg2) and extract (arg1, op.symbol, arg2) */ + object BinaryOp { + def unapply(t: Tree)(implicit ctx: Context): Option[(Tree, Symbol, Tree)] = t match { + case Apply(sel @ Select(arg1, _), List(arg2)) => + Some((arg1, sel.symbol, arg2)) + case _ => + None + } + } + + /** Match new C(args) and extract (C, args) */ + object NewWithArgs { + def unapply(t: Tree)(implicit ctx: Context): Option[(Type, List[Tree])] = t match { + case Apply(Select(New(_), nme.CONSTRUCTOR), args) => + Some((t.tpe, args)) + case _ => + None + } + } + + /** For an instance v of a value class like: + * class V(val underlying: X) extends AnyVal + * Match v.underlying() and extract v + */ + object ValueClassUnbox { + def unapply(t: Tree)(implicit ctx: Context): Option[Tree] = t match { + case Apply(sel @ Select(ref, _), Nil) => + val d = ref.tpe.widenDealias.typeSymbol.denot + if (isDerivedValueClass(d) && (sel.symbol eq valueClassUnbox(d.asClass))) { + Some(ref) + } else + None + case _ => + None + } + } +} diff --git a/src/dotty/tools/dotc/transform/VCInline.scala b/src/dotty/tools/dotc/transform/VCInline.scala new file mode 100644 index 000000000..e7b16f59e --- /dev/null +++ b/src/dotty/tools/dotc/transform/VCInline.scala @@ -0,0 +1,59 @@ +package dotty.tools.dotc +package transform + +import ast.{Trees, tpd} +import core._, core.Decorators._ +import Contexts._, Trees._, StdNames._, Symbols._ +import DenotTransformers._, TreeTransforms._, Phases.Phase +import ExtensionMethods._, TreeExtractors._, ValueClasses._ + +/** This phase inlines calls to methods and fields of value classes. + * + * For a value class V defined as: + * case class V(val underlying: U) extends AnyVal + * We replace method calls by calls to the corresponding extension method: + * v.foo(args) => V.foo$extension(v.underlying(), args) + * And we avoid unnecessary allocations: + * new V(u1) == new V(u2) => u1 == u2 + * (new V(u)).underlying() => u + */ +class VCInline extends MiniPhaseTransform with IdentityDenotTransformer { + import tpd._ + + override def phaseName: String = "vcInline" + + override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[ElimErasedValueType]) + + override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = + tree match { + // new V(u1) == new V(u2) => u1 == u2 + // (We don't handle != because it has been eliminated by InterceptedMethods) + case BinaryOp(NewWithArgs(tp1, List(u1)), op, NewWithArgs(tp2, List(u2))) + if (tp1 eq tp2) && (op eq defn.Any_==) && isDerivedValueClass(tp1.typeSymbol) => + // == is overloaded in primitive classes + applyOverloaded(u1, nme.EQ, List(u2), Nil, defn.BooleanType) + + // (new V(u)).underlying() => u + case ValueClassUnbox(NewWithArgs(_, List(u))) => + u + + // (new V(u)).foo(args) => V.foo$extension(u, args) + // v.foo(args) => V.foo$extension(v.underlying(), args) + case Apply(sel @ Select(receiver, _), args) => + val classMeth = sel.symbol + if (isMethodWithExtension(classMeth)) { + val classSym = receiver.tpe.widenDealias.typeSymbol.asClass + val unboxedReceiver = receiver match { + case NewWithArgs(_, List(u)) => + u + case _ => + receiver.select(valueClassUnbox(classSym)).appliedToNone + } + val extensionMeth = extensionMethod(classMeth) + ref(extensionMeth).appliedToArgs(unboxedReceiver :: args) + } else tree + + case _ => + tree + } +} -- cgit v1.2.3