aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2016-12-13 14:58:26 +0100
committerMartin Odersky <odersky@gmail.com>2016-12-17 18:34:27 +0100
commit6eb1a72bce041d4fd8519713031894dac3192025 (patch)
tree61909b6fcab49b310b9d4f3f82e66fe82f772798
parent5e2f7d18d711814cecce62a4bd7315a26a5efb87 (diff)
downloaddotty-6eb1a72bce041d4fd8519713031894dac3192025.tar.gz
dotty-6eb1a72bce041d4fd8519713031894dac3192025.tar.bz2
dotty-6eb1a72bce041d4fd8519713031894dac3192025.zip
New ShortcutImplicits phase
Optimizes implicit closures by avoiding closure creation where possible.
-rw-r--r--compiler/src/dotty/tools/dotc/Compiler.scala1
-rw-r--r--compiler/src/dotty/tools/dotc/config/JavaPlatform.scala1
-rw-r--r--compiler/src/dotty/tools/dotc/core/NameOps.scala2
-rw-r--r--compiler/src/dotty/tools/dotc/core/StdNames.scala1
-rw-r--r--compiler/src/dotty/tools/dotc/core/Types.scala4
-rw-r--r--compiler/src/dotty/tools/dotc/transform/ShortcutImplicits.scala143
-rw-r--r--tests/run/implicitFuns.scala44
7 files changed, 195 insertions, 1 deletions
diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala
index ad3249be2..900d2b0e3 100644
--- a/compiler/src/dotty/tools/dotc/Compiler.scala
+++ b/compiler/src/dotty/tools/dotc/Compiler.scala
@@ -61,6 +61,7 @@ class Compiler {
new PatternMatcher, // Compile pattern matches
new ExplicitOuter, // Add accessors to outer classes from nested ones.
new ExplicitSelf, // Make references to non-trivial self types explicit as casts
+ new ShortcutImplicits, // Allow implicit functions without creating closures
new CrossCastAnd, // Normalize selections involving intersection types.
new Splitter), // Expand selections involving union types into conditionals
List(new VCInlineMethods, // Inlines calls to value class methods
diff --git a/compiler/src/dotty/tools/dotc/config/JavaPlatform.scala b/compiler/src/dotty/tools/dotc/config/JavaPlatform.scala
index a695202d3..b5bfbb39f 100644
--- a/compiler/src/dotty/tools/dotc/config/JavaPlatform.scala
+++ b/compiler/src/dotty/tools/dotc/config/JavaPlatform.scala
@@ -18,6 +18,7 @@ class JavaPlatform extends Platform {
currentClassPath = Some(new PathResolver().result)
val cp = currentClassPath.get
//println(cp)
+ //println("------------------")
cp
}
diff --git a/compiler/src/dotty/tools/dotc/core/NameOps.scala b/compiler/src/dotty/tools/dotc/core/NameOps.scala
index f40915528..c037d1ce7 100644
--- a/compiler/src/dotty/tools/dotc/core/NameOps.scala
+++ b/compiler/src/dotty/tools/dotc/core/NameOps.scala
@@ -188,6 +188,8 @@ object NameOps {
def errorName: N = likeTyped(name ++ nme.ERROR)
+ def directName: N = likeTyped(name ++ DIRECT_SUFFIX)
+
def freshened(implicit ctx: Context): N =
likeTyped(
if (name.isModuleClassName) name.stripModuleClassSuffix.freshened.moduleClassName
diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala
index adcb15242..716959648 100644
--- a/compiler/src/dotty/tools/dotc/core/StdNames.scala
+++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala
@@ -129,6 +129,7 @@ object StdNames {
val COMPANION_MODULE_METHOD: N = "companion$module"
val COMPANION_CLASS_METHOD: N = "companion$class"
val TRAIT_SETTER_SEPARATOR: N = "$_setter_$"
+ val DIRECT_SUFFIX: N = "$direct"
// value types (and AnyRef) are all used as terms as well
// as (at least) arguments to the @specialize annotation.
diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala
index 01a331e86..344227299 100644
--- a/compiler/src/dotty/tools/dotc/core/Types.scala
+++ b/compiler/src/dotty/tools/dotc/core/Types.scala
@@ -2376,7 +2376,9 @@ object Types {
protected def computeSignature(implicit ctx: Context): Signature =
resultSignature.prepend(paramTypes, isJava)
- def derivedMethodType(paramNames: List[TermName], paramTypes: List[Type], resType: Type)(implicit ctx: Context) =
+ def derivedMethodType(paramNames: List[TermName] = this.paramNames,
+ paramTypes: List[Type] = this.paramTypes,
+ resType: Type = this.resType)(implicit ctx: Context) =
if ((paramNames eq this.paramNames) && (paramTypes eq this.paramTypes) && (resType eq this.resType)) this
else {
val resTypeFn = (x: MethodType) => resType.subst(this, x)
diff --git a/compiler/src/dotty/tools/dotc/transform/ShortcutImplicits.scala b/compiler/src/dotty/tools/dotc/transform/ShortcutImplicits.scala
new file mode 100644
index 000000000..bf72b89c0
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/transform/ShortcutImplicits.scala
@@ -0,0 +1,143 @@
+package dotty.tools.dotc
+package transform
+
+import TreeTransforms._
+import core.DenotTransformers.IdentityDenotTransformer
+import core.Symbols._
+import core.Contexts._
+import core.Types._
+import core.Flags._
+import core.Decorators._
+import core.StdNames.nme
+import core.Names._
+import core.NameOps._
+import ast.Trees._
+import ast.tpd
+import collection.mutable
+
+/** This phase optimizes code using implicit function types, by applying two rewrite rules.
+ * Let IF be the implicit function type
+ *
+ * implicit Us => R
+ *
+ * (1) A method definition
+ *
+ * def m(xs: Ts): IF = implicit (ys: Us) => E
+ *
+ * is expanded to two methods:
+ *
+ * def m(xs: Ts): IF = implicit (ys: Us) => m$direct(xs)(ys)
+ * def m$direct(xs: Ts)(ys: Us): R = E
+ *
+ * (and equivalently for methods with type parameters or a different number of value parameter lists).
+ * An abstract method definition
+ *
+ * def m(xs: Ts): IF
+ *
+ * is expanded to:
+ *
+ * def m(xs: Ts): IF
+ * def m$direct(xs: Ts, ys: Us): R
+ *
+ * (2) A reference `qual.apply` where `qual` has implicit function type and
+ * `qual` refers to a method `m` is rewritten to a reference to `m$direct`,
+ * keeping the same type and value arguments as they are found in `qual`.
+ */
+class ShortcutImplicits extends MiniPhase with IdentityDenotTransformer { thisTransform =>
+ import tpd._
+
+ override def phaseName: String = "shortcutImplicits"
+ val treeTransform = new Transform
+
+ class Transform extends TreeTransform {
+ def phase = thisTransform
+
+ override def prepareForUnit(tree: Tree)(implicit ctx: Context) = new Transform
+
+ /** A map to cache mapping local methods to their direct counterparts.
+ * A fresh map is created for each unit.
+ */
+ private val directMeth = new mutable.HashMap[Symbol, Symbol]
+
+ /** @pre The type's final result type is an implicit function type `implicit Ts => R`.
+ * @return The type of the `apply` member of `implicit Ts => R`.
+ */
+ private def directInfo(info: Type)(implicit ctx: Context): Type = info match {
+ case info: PolyType => info.derivedPolyType(resType = directInfo(info.resultType))
+ case info: MethodType => info.derivedMethodType(resType = directInfo(info.resultType))
+ case info: ExprType => directInfo(info.resultType)
+ case info => info.member(nme.apply).info
+ }
+
+ /** A new `m$direct` method to accompany the given method `m` */
+ private def newDirectMethod(sym: Symbol)(implicit ctx: Context): Symbol =
+ sym.copy(
+ name = sym.name.directName,
+ flags = sym.flags | Synthetic,
+ info = directInfo(sym.info))
+
+ /** The direct method `m$direct` that accompanies the given method `m`.
+ * Create one if it does not exist already.
+ */
+ private def directMethod(sym: Symbol)(implicit ctx: Context): Symbol =
+ if (sym.owner.isClass) {
+ val direct = sym.owner.info.member(sym.name.directName)
+ .suchThat(_.info matches directInfo(sym.info)).symbol
+ if (direct.maybeOwner == sym.owner) direct
+ else newDirectMethod(sym).enteredAfter(thisTransform)
+ }
+ else directMeth.getOrElseUpdate(sym, newDirectMethod(sym))
+
+
+ /** Transform `qual.apply` occurrences according to rewrite rule (2) above */
+ override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo) =
+ if (tree.name == nme.apply &&
+ defn.isImplicitFunctionType(tree.qualifier.tpe.widen) &&
+ tree.qualifier.symbol.is(Method, butNot = Accessor)) {
+ def directQual(tree: Tree): Tree = tree match {
+ case Apply(fn, args) => cpy.Apply(tree)(directQual(fn), args)
+ case TypeApply(fn, args) => cpy.TypeApply(tree)(directQual(fn), args)
+ case Block(stats, expr) => cpy.Block(tree)(stats, directQual(expr))
+ case tree: RefTree =>
+ cpy.Ref(tree)(tree.name.directName)
+ .withType(directMethod(tree.symbol).termRef)
+ }
+ directQual(tree.qualifier)
+ } else tree
+
+ /** Transform methods with implicit function type result according to rewrite rule (1) above */
+ override def transformDefDef(mdef: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = {
+ val original = mdef.symbol
+ if (defn.isImplicitFunctionType(original.info.finalResultType)) {
+ val direct = directMethod(original)
+
+ def splitClosure(tree: Tree): (List[Type] => List[List[Tree]] => Tree, Tree) = tree match {
+ case Block(Nil, expr) => splitClosure(expr)
+ case Block((meth @ DefDef(nme.ANON_FUN, Nil, clparams :: Nil, _, _)) :: Nil, cl: Closure) =>
+ val tparamSyms = mdef.tparams.map(_.symbol)
+ val vparamSymss = mdef.vparamss.map(_.map(_.symbol))
+ val clparamSyms = clparams.map(_.symbol)
+ val remappedCore = (ts: List[Type]) => (prefss: List[List[Tree]]) =>
+ meth.rhs
+ .subst(tparamSyms ::: (vparamSymss.flatten ++ clparamSyms),
+ ts.map(_.typeSymbol) ::: prefss.flatten.map(_.symbol))
+ .changeOwnerAfter(original, direct, thisTransform)
+ .changeOwnerAfter(meth.symbol, direct, thisTransform)
+ val forwarder = ref(direct)
+ .appliedToTypeTrees(tparamSyms.map(ref(_)))
+ .appliedToArgss(vparamSymss.map(_.map(ref(_))) :+ clparamSyms.map(ref(_)))
+ val fwdClosure = cpy.Block(tree)(cpy.DefDef(meth)(rhs = forwarder) :: Nil, cl)
+ (remappedCore, fwdClosure)
+ case EmptyTree =>
+ (_ => _ => EmptyTree, EmptyTree)
+ }
+
+ val (remappedCore, fwdClosure) = splitClosure(mdef.rhs)
+ val originalDef = cpy.DefDef(mdef)(rhs = fwdClosure)
+ val directDef = polyDefDef(direct.asTerm, remappedCore)
+ Thicket(originalDef, directDef)
+ }
+ else mdef
+ }
+ }
+}
diff --git a/tests/run/implicitFuns.scala b/tests/run/implicitFuns.scala
index ee1d37256..1bc5a2f6b 100644
--- a/tests/run/implicitFuns.scala
+++ b/tests/run/implicitFuns.scala
@@ -211,3 +211,47 @@ object TransactionalExpansion {
}
}
+object TransactionalAbstracted {
+ type Transactional[T] = implicit Transaction => T
+
+ trait TransOps {
+ def thisTransaction: Transactional[Transaction]
+ def f1(x: Int): Transactional[Int]
+ def f2(x: Int): Transactional[Int]
+ def f3(x: Int): Transactional[Int]
+ }
+
+ object TransOpsObj extends TransOps {
+
+ def thisTransaction: Transactional[Transaction] = implicitly[Transaction]
+
+ def f1(x: Int): Transactional[Int] = {
+ thisTransaction.println(s"first step: $x")
+ f2(x + 1)
+ }
+ def f2(x: Int): Transactional[Int] = {
+ thisTransaction.println(s"second step: $x")
+ f3(x * x)
+ }
+ def f3(x: Int): Transactional[Int] = {
+ thisTransaction.println(s"third step: $x")
+ if (x % 2 != 0) thisTransaction.abort()
+ x
+ }
+ }
+
+ val transOps: TransOps = TransOpsObj
+
+ def transaction[T](op: Transactional[T]) = {
+ implicit val trans: Transaction = new Transaction
+ op
+ trans.commit()
+ }
+
+ def main(args: Array[String]) = {
+ transaction {
+ val res = transOps.f1(args.length)
+ println(if (transOps.thisTransaction.isAborted) "aborted" else s"result: $res")
+ }
+ }
+}