From caa0a6bcfcc31c3f4d863372f430b08ea4cd383a Mon Sep 17 00:00:00 2001 From: Felix Mulder Date: Fri, 2 Dec 2016 17:47:44 +0100 Subject: [PATCH 01/32] Add phases and initial replacement for super --- compiler/src/dotty/tools/dotc/Compiler.scala | 4 +- .../DispatchToSpecializedApply.scala | 16 ++++++ .../SpecializeExtendsFunction1.scala | 55 +++++++++++++++++++ 3 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 compiler/src/dotty/tools/dotc/transform/DispatchToSpecializedApply.scala create mode 100644 compiler/src/dotty/tools/dotc/transform/SpecializeExtendsFunction1.scala diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index e318332826f6..b4df243d9ce8 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -80,7 +80,9 @@ class Compiler { new Simplify, // Perform local optimizations, simplified versions of what linker does. new PrimitiveForwarders, // Add forwarders to trait methods that have a mismatch between generic and primitives new FunctionXXLForwarders, // Add forwarders for FunctionXXL apply method - new ArrayConstructors), // Intercept creation of (non-generic) arrays and intrinsify. + new ArrayConstructors, // Intercept creation of (non-generic) arrays and intrinsify. + new SpecializeExtendsFunction1, // <- what he said + new DispatchToSpecializedApply), // <- what she said List(new Erasure), // Rewrite types to JVM model, erasing all type parameters, abstract types and refinements. List(new ElimErasedValueType, // Expand erased value types to their underlying implmementation types new VCElideAllocations, // Peep-hole optimization to eliminate unnecessary value class allocations diff --git a/compiler/src/dotty/tools/dotc/transform/DispatchToSpecializedApply.scala b/compiler/src/dotty/tools/dotc/transform/DispatchToSpecializedApply.scala new file mode 100644 index 000000000000..12295675b2ab --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/DispatchToSpecializedApply.scala @@ -0,0 +1,16 @@ +package dotty.tools +package dotc +package transform + +import dotty.tools.dotc.util.Positions._ +import TreeTransforms.{MiniPhaseTransform, TransformerInfo} +import core._ +import Contexts.Context, Types._, Constants._, Decorators._, Symbols._ +import TypeUtils._, TypeErasure._, Flags._ + +class DispatchToSpecializedApply extends MiniPhaseTransform { + val phaseName = "specializeExtendsFunction1" + + import ast.tpd._ +} + diff --git a/compiler/src/dotty/tools/dotc/transform/SpecializeExtendsFunction1.scala b/compiler/src/dotty/tools/dotc/transform/SpecializeExtendsFunction1.scala new file mode 100644 index 000000000000..c7a02383e205 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/SpecializeExtendsFunction1.scala @@ -0,0 +1,55 @@ +package dotty.tools +package dotc +package transform + +import TreeTransforms.{ MiniPhaseTransform, TransformerInfo } +import core._ +import Contexts.Context, Types._, Decorators._, Symbols._ + +class SpecializeExtendsFunction1 extends MiniPhaseTransform { + import ast.tpd._ + + val phaseName = "specializeExtendsFunction1" + + override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo) = { + val parentAndTypes = + tree.parents + // map to a tuple of type and tree + .map { t => (t.tpe, t) } + // collect the parent that corresponds to Function1 and its type params + .collect { + case (RefinedType(RefinedType(parent, _, t1), _, r), function1) + if parent.typeSymbol.showFullName == "scala.Function1" => + (function1, t1, r) + } + .headOption + + def replace(parent: Tree, in: List[Tree], withTree: Tree): List[Tree] = + in.foldRight(List.empty[Tree]) { (t, trees) => + if (parent eq t) withTree :: trees + else t :: trees + } + + def specialized(t1: Type, r: Type, orig: Tree): Tree = { + val t1Name = t1.typeSymbol.showFullName + val rName = r.typeSymbol.showFullName + + val requiredClass = (t1Name, rName) match { + case ("scala.Int", "scala.Int") => + ctx.requiredClassRef("scala.Function1$mcII$sp") + case ("scala.Char", "scala.Int") => + ctx.requiredClassRef("scala.Function1$mcCI$sp") + case _ => orig.tpe + } + + TypeTree(requiredClass) + } + + parentAndTypes match { + case Some((parent, t1, r)) => + val newParents = replace(parent, in = tree.parents, withTree = specialized(t1, r, parent)) + cpy.Template(tree)(parents = newParents) + case _ => tree + } + } +} From 04024a152fe07892dce651c440df800c8b62b222 Mon Sep 17 00:00:00 2001 From: Felix Mulder Date: Fri, 2 Dec 2016 18:07:19 +0100 Subject: [PATCH 02/32] Replace all existing combinations of Function1 with specialized version --- .../SpecializeExtendsFunction1.scala | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/SpecializeExtendsFunction1.scala b/compiler/src/dotty/tools/dotc/transform/SpecializeExtendsFunction1.scala index c7a02383e205..1c71b8d8f448 100644 --- a/compiler/src/dotty/tools/dotc/transform/SpecializeExtendsFunction1.scala +++ b/compiler/src/dotty/tools/dotc/transform/SpecializeExtendsFunction1.scala @@ -11,6 +11,16 @@ class SpecializeExtendsFunction1 extends MiniPhaseTransform { val phaseName = "specializeExtendsFunction1" + // types as abbreviated in scalac specialized class file names + private[this] val typeAbbr = Map( + "scala.Double" -> "D", + "scala.Float" -> "F", + "scala.Int" -> "I", + "scala.Long" -> "J", + "scala.Unit" -> "V", + "scala.Boolean" -> "Z" + ) + override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo) = { val parentAndTypes = tree.parents @@ -34,11 +44,11 @@ class SpecializeExtendsFunction1 extends MiniPhaseTransform { val t1Name = t1.typeSymbol.showFullName val rName = r.typeSymbol.showFullName - val requiredClass = (t1Name, rName) match { - case ("scala.Int", "scala.Int") => - ctx.requiredClassRef("scala.Function1$mcII$sp") - case ("scala.Char", "scala.Int") => - ctx.requiredClassRef("scala.Function1$mcCI$sp") + // get the required class via the abbreviations in `typeAbbr`, if they + // don't both exist there, there is no specialization for the combination + val requiredClass = (typeAbbr.get(t1Name), typeAbbr.get(rName)) match { + case (Some(t1Abbr), Some(rAbbr)) => + ctx.requiredClassRef("scala.Function1$mc" + rAbbr + t1Abbr + "$sp") case _ => orig.tpe } From c5a5a7eab080ec04f61ced6228f8ca3cc7d33969 Mon Sep 17 00:00:00 2001 From: Felix Mulder Date: Tue, 13 Dec 2016 17:41:29 +0100 Subject: [PATCH 03/32] Do transformations on symbol level too --- compiler/src/dotty/tools/dotc/Compiler.scala | 3 +- .../DispatchToSpecializedApply.scala | 16 -- .../SpecializeExtendsFunction1.scala | 65 ------ .../dotc/transform/SpecializeFunction1.scala | 189 ++++++++++++++++++ .../transform/SpecializeFunction1Tests.scala | 38 ++++ 5 files changed, 229 insertions(+), 82 deletions(-) delete mode 100644 compiler/src/dotty/tools/dotc/transform/DispatchToSpecializedApply.scala delete mode 100644 compiler/src/dotty/tools/dotc/transform/SpecializeExtendsFunction1.scala create mode 100644 compiler/src/dotty/tools/dotc/transform/SpecializeFunction1.scala create mode 100644 compiler/test/dotty/tools/dotc/transform/SpecializeFunction1Tests.scala diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index b4df243d9ce8..d2e15cad3aed 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -67,7 +67,8 @@ class Compiler { 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 + new Splitter, // Expand selections involving union types into conditionals + new SpecializeFunction1), // Specialized Function1 by replacing super with specialized super List(new PhantomArgLift, // Extracts the evaluation of phantom arguments placing them before the call. new VCInlineMethods, // Inlines calls to value class methods new SeqLiterals, // Express vararg arguments as arrays diff --git a/compiler/src/dotty/tools/dotc/transform/DispatchToSpecializedApply.scala b/compiler/src/dotty/tools/dotc/transform/DispatchToSpecializedApply.scala deleted file mode 100644 index 12295675b2ab..000000000000 --- a/compiler/src/dotty/tools/dotc/transform/DispatchToSpecializedApply.scala +++ /dev/null @@ -1,16 +0,0 @@ -package dotty.tools -package dotc -package transform - -import dotty.tools.dotc.util.Positions._ -import TreeTransforms.{MiniPhaseTransform, TransformerInfo} -import core._ -import Contexts.Context, Types._, Constants._, Decorators._, Symbols._ -import TypeUtils._, TypeErasure._, Flags._ - -class DispatchToSpecializedApply extends MiniPhaseTransform { - val phaseName = "specializeExtendsFunction1" - - import ast.tpd._ -} - diff --git a/compiler/src/dotty/tools/dotc/transform/SpecializeExtendsFunction1.scala b/compiler/src/dotty/tools/dotc/transform/SpecializeExtendsFunction1.scala deleted file mode 100644 index 1c71b8d8f448..000000000000 --- a/compiler/src/dotty/tools/dotc/transform/SpecializeExtendsFunction1.scala +++ /dev/null @@ -1,65 +0,0 @@ -package dotty.tools -package dotc -package transform - -import TreeTransforms.{ MiniPhaseTransform, TransformerInfo } -import core._ -import Contexts.Context, Types._, Decorators._, Symbols._ - -class SpecializeExtendsFunction1 extends MiniPhaseTransform { - import ast.tpd._ - - val phaseName = "specializeExtendsFunction1" - - // types as abbreviated in scalac specialized class file names - private[this] val typeAbbr = Map( - "scala.Double" -> "D", - "scala.Float" -> "F", - "scala.Int" -> "I", - "scala.Long" -> "J", - "scala.Unit" -> "V", - "scala.Boolean" -> "Z" - ) - - override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo) = { - val parentAndTypes = - tree.parents - // map to a tuple of type and tree - .map { t => (t.tpe, t) } - // collect the parent that corresponds to Function1 and its type params - .collect { - case (RefinedType(RefinedType(parent, _, t1), _, r), function1) - if parent.typeSymbol.showFullName == "scala.Function1" => - (function1, t1, r) - } - .headOption - - def replace(parent: Tree, in: List[Tree], withTree: Tree): List[Tree] = - in.foldRight(List.empty[Tree]) { (t, trees) => - if (parent eq t) withTree :: trees - else t :: trees - } - - def specialized(t1: Type, r: Type, orig: Tree): Tree = { - val t1Name = t1.typeSymbol.showFullName - val rName = r.typeSymbol.showFullName - - // get the required class via the abbreviations in `typeAbbr`, if they - // don't both exist there, there is no specialization for the combination - val requiredClass = (typeAbbr.get(t1Name), typeAbbr.get(rName)) match { - case (Some(t1Abbr), Some(rAbbr)) => - ctx.requiredClassRef("scala.Function1$mc" + rAbbr + t1Abbr + "$sp") - case _ => orig.tpe - } - - TypeTree(requiredClass) - } - - parentAndTypes match { - case Some((parent, t1, r)) => - val newParents = replace(parent, in = tree.parents, withTree = specialized(t1, r, parent)) - cpy.Template(tree)(parents = newParents) - case _ => tree - } - } -} diff --git a/compiler/src/dotty/tools/dotc/transform/SpecializeFunction1.scala b/compiler/src/dotty/tools/dotc/transform/SpecializeFunction1.scala new file mode 100644 index 000000000000..f85ae67c6c39 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/SpecializeFunction1.scala @@ -0,0 +1,189 @@ +package dotty.tools +package dotc +package transform + +import TreeTransforms.{ MiniPhaseTransform, TransformerInfo } +import core._ +import Contexts.Context, Types._, Decorators._, Symbols._, DenotTransformers._ +import Denotations._, SymDenotations._, Scopes._, StdNames._ + +class SpecializeFunction1 extends MiniPhaseTransform with DenotTransformer { + import ast.tpd._ + + val phaseName = "specializeFunction1" + + // TODO: remove me, just used for debug during dev + def debug[A](str: A*) = { + str.foreach(println) + System.exit(1) + } + + /** Transforms all classes extending `Function1[-T1, +R]` so that + * they instead extend the specialized version `JFunction...` + */ + def transform(ref: SingleDenotation)(implicit ctx: Context) = { + def containsFunction1(xs: List[TypeRef]): Boolean = + xs.map(_.typeSymbol.showFullName).contains("scala.Function1") + + // TODO: go away **please** + def blacklisted(cref: ClassDenotation): Boolean = { + "JFunction1" :: "AbstractFunction1" :: "PartialFunction" :: "AbstractPartialFunction" :: Nil + } contains (cref.name.show) + + + ref match { + case cref: ClassDenotation if containsFunction1(cref.classParents) && !blacklisted(cref) => + specializeFunction1(cref) + case ref => ref + } + } + + def specializeFunction1(cref: ClassDenotation)(implicit ctx: Context): SingleDenotation = { + def JFunction(t1: String, r: String): TypeRef = ctx.requiredClassRef { + "scala.compat.java8.JFunction1$mc" + s"$r$t1" + "$sp" + } + + def typeMember(name: String): ClassSymbol = + cref.classInfo.decls.lookup(name.toTypeName).info.typeSymbol.asClass + + def t1Member = typeMember("scala$Function1$$T1") + def rMember = typeMember("scala$Function1$$R") + + // This Symbol -> String map contains the equaivalent types for + // scalac-specialized classes + val typeAbbr = Map( + defn.DoubleClass -> "D", + defn.FloatClass -> "F", + defn.IntClass -> "I", + defn.LongClass -> "J", + defn.UnitClass -> "V", + defn.BooleanClass -> "Z" + ) + + def replaceFunction1(in: List[TypeRef]): List[TypeRef] = + in.foldRight(List.empty[TypeRef]) { (tp, acc) => + val curr = + // TODO: remove .showFullName + if (tp.typeSymbol.showFullName == "scala.Function1") { + // If there exists a specialization, both type members will be in + // `typeAbbr` + typeAbbr.get(t1Member) -> typeAbbr.get(rMember) match { + case (Some(t1), Some(r)) => JFunction(t1, r) + case _ => tp + } + } + else tp + + curr :: acc + } + + def specializeApply(scope: Scope): Scope = { + import core.StdNames._ + def specializeApply(sym: Symbol, t1: String, r: String): Symbol = { + val specializedMethodName = ("apply$mc" + t1 + r + "$sp").toTermName + val specializedApply = JFunction(t1, r).info.decls.lookup(specializedMethodName) + + val specSym = ctx.newSymbol( + cref.symbol.owner, + specializedMethodName, + Flags.Override | Flags.Method, + specializedApply.info + ) + + specSym.enteredAfter(this) + } + + typeAbbr.get(t1Member) -> typeAbbr.get(rMember) match { + case (Some(t1), Some(r)) => + scope.foldRight(newScope) { (sym, newScope) => + newScope.enter { + if (sym.name eq nme.apply) specializeApply(sym, t1, r) else sym + } + newScope + } + case _ => scope + } + } + + val ClassInfo(prefix, cls, parents, decls, info) = cref.info + val res = cref.copySymDenotation( + info = ClassInfo(prefix, cls, replaceFunction1(in = parents), specializeApply(decls), info) + ) + + res + } + + override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo) = { + // types as abbreviated in scalac specialized class file names + val typeAbbr = Map( + defn.DoubleClass -> "D", + defn.FloatClass -> "F", + defn.IntClass -> "I", + defn.LongClass -> "J", + defn.UnitClass -> "V", + defn.BooleanClass -> "Z" + ) + + def typeMember(name: String): ClassSymbol = + ctx.owner.info.asInstanceOf[ClassInfo].decls.lookup(name.toTypeName).info.typeSymbol.asClass + + def t1Member = typeMember("scala$Function1$$T1") + def rMember = typeMember("scala$Function1$$R") + + val parentAndTypes = + tree.parents + // map super-classes to a tuple of type and tree + .map { t => (t.tpe, t) } + // collect the parent that corresponds to Function1 and its type params + .collect { + case (RefinedType(RefinedType(parent, _, t1), _, r), function1) + if parent.typeSymbol.showFullName == "scala.Function1" => + (function1, t1, r) + } + .headOption + + def replaceParent(parent: Tree, in: List[Tree], withTree: Tree) = + in.foldRight(List.empty[Tree]) { (t, trees) => + (if (parent eq t) withTree else t) :: trees + } + + def specializedParent(t1: Type, r: Type, orig: Tree): Option[Tree] = + // get the required class via the abbreviations in `typeAbbr`, if they + // don't both exist there, there is no specialization for the combination + typeAbbr.get(t1Member) -> typeAbbr.get(rMember) match { + case (Some(t1Abbr), Some(rAbbr)) => + Some(TypeTree(ctx.requiredClassRef("scala.compat.java8.JFunction1$mc" + rAbbr + t1Abbr + "$sp"))) + case _ => None + } + + def withRenamedApply(parent: Tree): List[Tree] = { + (typeAbbr.get(t1Member), typeAbbr.get(rMember)) match { + case (Some(t1Abbr), Some(rAbbr)) => + tree.body.foldRight(List.empty[Tree]) { + case (tree: DefDef, acc) if tree.name == nme.apply => + val specializedMethodName = ("apply$mc" + t1Abbr + rAbbr + "$sp").toTermName + val specializedApply = ctx.owner.info.decls.lookup(specializedMethodName).asTerm + + val newDefDef = polyDefDef(specializedApply, trefs => vrefss => { + tree.rhs + .changeOwner(tree.symbol, specializedApply) + .subst(tree.vparamss.flatten.map(_.symbol), vrefss.flatten.map(_.symbol)) + }) + + newDefDef :: acc + case (tree, acc) => + tree :: acc + } + case x => tree.body + } + } + + parentAndTypes.flatMap { case (parent, t1, r) => + specializedParent(t1, r, parent).map { newPar => + val parents = replaceParent(parent, in = tree.parents, withTree = newPar) + val body = withRenamedApply(newPar) + cpy.Template(tree)(parents = parents, body = body) + } + } getOrElse (tree) + } +} diff --git a/compiler/test/dotty/tools/dotc/transform/SpecializeFunction1Tests.scala b/compiler/test/dotty/tools/dotc/transform/SpecializeFunction1Tests.scala new file mode 100644 index 000000000000..9104b936b082 --- /dev/null +++ b/compiler/test/dotty/tools/dotc/transform/SpecializeFunction1Tests.scala @@ -0,0 +1,38 @@ +package dotty.tools +package dotc +package transform + +import org.junit.Assert._ +import org.junit.Test + +import dotty.tools.backend.jvm.DottyBytecodeTest + +class SpecializeFunction1Tests extends DottyBytecodeTest { + + import dotty.tools.backend.jvm.ASMConverters._ + import dotty.tools.backend.jvm.AsmNode._ + + @Test def specializeParentIntToInt = { + val source = """ + |class Foo extends Function1[Int, Int] { + | def apply(i: Int) = i + |} + """.stripMargin + + checkBCode(source) { dir => + import scala.collection.JavaConverters._ + val clsIn = dir.lookupName("Foo.class", directory = false).input + val clsNode = loadClassNode(clsIn) + assert(clsNode.name == "Foo", s"inspecting wrong class: ${clsNode.name}") + val methods = clsNode.methods.asScala + + val applys = methods + .collect { + case m if m.name == "apply$mcII$sp" => m + } + .toList + + assert(applys.length == 1, "Wrong number of specialized applys") + } + } +} From 34836c7298c1a71dda80eda3ca2ae0a786a2cbfb Mon Sep 17 00:00:00 2001 From: Felix Mulder Date: Wed, 14 Dec 2016 15:23:45 +0100 Subject: [PATCH 04/32] Refactor transformations to be more idiomatic --- .../src/dotty/tools/dotc/core/NameOps.scala | 2 +- .../dotc/transform/SpecializeFunction1.scala | 250 ++++++++---------- 2 files changed, 109 insertions(+), 143 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/NameOps.scala b/compiler/src/dotty/tools/dotc/core/NameOps.scala index 4e568861e401..1940a8be9266 100644 --- a/compiler/src/dotty/tools/dotc/core/NameOps.scala +++ b/compiler/src/dotty/tools/dotc/core/NameOps.scala @@ -227,7 +227,7 @@ object NameOps { case nme.clone_ => nme.clone_ } - def specializedFor(classTargs: List[Types.Type], classTargsNames: List[Name], methodTargs: List[Types.Type], methodTarsNames: List[Name])(implicit ctx: Context): name.ThisName = { + def specializedFor(classTargs: List[Types.Type], classTargsNames: List[Name], methodTargs: List[Types.Type] = scala.Nil, methodTarsNames: List[Name] = scala.Nil)(implicit ctx: Context): name.ThisName = { val methodTags: Seq[Name] = (methodTargs zip methodTarsNames).sortBy(_._2).map(x => defn.typeTag(x._1)) val classTags: Seq[Name] = (classTargs zip classTargsNames).sortBy(_._2).map(x => defn.typeTag(x._1)) diff --git a/compiler/src/dotty/tools/dotc/transform/SpecializeFunction1.scala b/compiler/src/dotty/tools/dotc/transform/SpecializeFunction1.scala index f85ae67c6c39..0ea053cbd641 100644 --- a/compiler/src/dotty/tools/dotc/transform/SpecializeFunction1.scala +++ b/compiler/src/dotty/tools/dotc/transform/SpecializeFunction1.scala @@ -5,185 +5,151 @@ package transform import TreeTransforms.{ MiniPhaseTransform, TransformerInfo } import core._ import Contexts.Context, Types._, Decorators._, Symbols._, DenotTransformers._ -import Denotations._, SymDenotations._, Scopes._, StdNames._ +import Denotations._, SymDenotations._, Scopes._, StdNames._, NameOps._, Names._ class SpecializeFunction1 extends MiniPhaseTransform with DenotTransformer { import ast.tpd._ val phaseName = "specializeFunction1" - // TODO: remove me, just used for debug during dev - def debug[A](str: A*) = { - str.foreach(println) - System.exit(1) - } - /** Transforms all classes extending `Function1[-T1, +R]` so that - * they instead extend the specialized version `JFunction...` + * they instead extend the specialized version `JFunction$mp...` */ - def transform(ref: SingleDenotation)(implicit ctx: Context) = { - def containsFunction1(xs: List[TypeRef]): Boolean = - xs.map(_.typeSymbol.showFullName).contains("scala.Function1") - - // TODO: go away **please** - def blacklisted(cref: ClassDenotation): Boolean = { - "JFunction1" :: "AbstractFunction1" :: "PartialFunction" :: "AbstractPartialFunction" :: Nil - } contains (cref.name.show) - + def transform(ref: SingleDenotation)(implicit ctx: Context) = ref match { + case ShouldTransformDenot(cref, t1, r, func1) => + transformDenot(cref, t1, r, func1) + case _ => ref + } - ref match { - case cref: ClassDenotation if containsFunction1(cref.classParents) && !blacklisted(cref) => - specializeFunction1(cref) - case ref => ref + override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo) = + tree match { + case ShouldTransformTree(func1, t1, r) => transformTree(tree, func1, t1, r) + case _ => tree } + + private[this] val functionName = "JFunction1".toTermName + private[this] val functionPkg = "scala.compat.java8.".toTermName + private[this] var Function1: Symbol = _ + private[this] var argTypes: Set[Symbol] = _ + private[this] var returnTypes: Set[Symbol] = _ + private[this] var blacklisted: Set[Symbol] = _ + + /** Do setup of `argTypes` and `returnTypes` */ + override def prepareForUnit(tree: Tree)(implicit ctx: Context) = { + argTypes = Set(defn.DoubleClass, + defn.FloatClass, + defn.IntClass, + defn.LongClass, + defn.UnitClass, + defn.BooleanClass) + + returnTypes = Set(defn.DoubleClass, + defn.FloatClass, + defn.IntClass, + defn.LongClass) + + Function1 = ctx.requiredClass("scala.Function1") + + blacklisted = Set( + "scala.compat.java8.JFunction1", + "scala.runtime.AbstractFunction1", + "scala.PartialFunction", + "scala.runtime.AbstractPartialFunction" + ).map(ctx.requiredClass(_)) + + this } - def specializeFunction1(cref: ClassDenotation)(implicit ctx: Context): SingleDenotation = { - def JFunction(t1: String, r: String): TypeRef = ctx.requiredClassRef { - "scala.compat.java8.JFunction1$mc" + s"$r$t1" + "$sp" + private object ShouldTransformDenot { + def unapply(cref: ClassDenotation)(implicit ctx: Context): Option[(ClassDenotation, Type, Type, Type)] = { + def collectFunc1(xs: List[Type])(implicit ctx: Context): Option[(Type, Type, Type)] = + xs.collect { + case func1 @ RefinedType(RefinedType(parent, _, t1), _, r) + if func1.isRef(Function1) => (t1, r, func1) + }.headOption + + collectFunc1(cref.info.parentsWithArgs).flatMap { case (t1, r, func1) => + if ( + !argTypes.contains(t1.typeSymbol) || + !returnTypes.contains(r.typeSymbol) || + blacklisted.exists(sym => cref.symbol.derivesFrom(sym)) + ) None + else Some((cref, t1, r, func1)) + } } + } - def typeMember(name: String): ClassSymbol = - cref.classInfo.decls.lookup(name.toTypeName).info.typeSymbol.asClass + private object ShouldTransformTree { + def unapply(tree: Template)(implicit ctx: Context): Option[(Tree, Type, Type)] = + tree.parents + .map { t => (t.tpe, t) } + .collect { + case (tp @ RefinedType(RefinedType(parent, _, t1), _, r), func1) + if tp.isRef(Function1) => (func1, t1, r) + } + .headOption + } - def t1Member = typeMember("scala$Function1$$T1") - def rMember = typeMember("scala$Function1$$R") + private def specializedName(name: Name, t1: Type, r: Type)(implicit ctx: Context): Name = + name.specializedFor(List(t1, r), List(t1, r).map(_.typeSymbol.name)) - // This Symbol -> String map contains the equaivalent types for - // scalac-specialized classes - val typeAbbr = Map( - defn.DoubleClass -> "D", - defn.FloatClass -> "F", - defn.IntClass -> "I", - defn.LongClass -> "J", - defn.UnitClass -> "V", - defn.BooleanClass -> "Z" - ) + def transformDenot(cref: ClassDenotation, t1: Type, r: Type, func1: Type)(implicit ctx: Context): SingleDenotation = { + val specializedFunction: TypeRef = + ctx.requiredClassRef(functionPkg ++ specializedName(functionName, t1, r)) def replaceFunction1(in: List[TypeRef]): List[TypeRef] = in.foldRight(List.empty[TypeRef]) { (tp, acc) => - val curr = - // TODO: remove .showFullName - if (tp.typeSymbol.showFullName == "scala.Function1") { - // If there exists a specialization, both type members will be in - // `typeAbbr` - typeAbbr.get(t1Member) -> typeAbbr.get(rMember) match { - case (Some(t1), Some(r)) => JFunction(t1, r) - case _ => tp - } - } + val newTp = + if (tp.isRef(Function1)) specializedFunction else tp - - curr :: acc + newTp :: acc } - def specializeApply(scope: Scope): Scope = { - import core.StdNames._ - def specializeApply(sym: Symbol, t1: String, r: String): Symbol = { - val specializedMethodName = ("apply$mc" + t1 + r + "$sp").toTermName - val specializedApply = JFunction(t1, r).info.decls.lookup(specializedMethodName) - - val specSym = ctx.newSymbol( + def specializeApply(scope: Scope): Scope = { + def specializedApply: Symbol = { + val specializedMethodName = specializedName(nme.apply, t1, r) + ctx.newSymbol( cref.symbol.owner, specializedMethodName, Flags.Override | Flags.Method, - specializedApply.info - ) - - specSym.enteredAfter(this) + specializedFunction.info.decls.lookup(specializedMethodName).info + ).enteredAfter(this) } - typeAbbr.get(t1Member) -> typeAbbr.get(rMember) match { - case (Some(t1), Some(r)) => - scope.foldRight(newScope) { (sym, newScope) => - newScope.enter { - if (sym.name eq nme.apply) specializeApply(sym, t1, r) else sym - } - newScope - } - case _ => scope + val alteredScope = newScope + scope.foreach { sym => + alteredScope.enter(if (sym.name eq nme.apply) specializedApply else sym) } + alteredScope } - val ClassInfo(prefix, cls, parents, decls, info) = cref.info - val res = cref.copySymDenotation( - info = ClassInfo(prefix, cls, replaceFunction1(in = parents), specializeApply(decls), info) - ) - - res + val ClassInfo(prefix, cls, parents, decls, info) = cref.classInfo + val newInfo = ClassInfo(prefix, cls, replaceFunction1(in = parents), specializeApply(decls), info) + cref.copySymDenotation(info = newInfo) } - override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo) = { - // types as abbreviated in scalac specialized class file names - val typeAbbr = Map( - defn.DoubleClass -> "D", - defn.FloatClass -> "F", - defn.IntClass -> "I", - defn.LongClass -> "J", - defn.UnitClass -> "V", - defn.BooleanClass -> "Z" - ) - - def typeMember(name: String): ClassSymbol = - ctx.owner.info.asInstanceOf[ClassInfo].decls.lookup(name.toTypeName).info.typeSymbol.asClass + private def transformTree(tmpl: Template, func1: Tree, t1: Type, r: Type)(implicit ctx: Context) = { + val specializedFunc1 = + TypeTree(ctx.requiredClassRef(functionPkg ++ specializedName(functionName, t1, r))) - def t1Member = typeMember("scala$Function1$$T1") - def rMember = typeMember("scala$Function1$$R") - - val parentAndTypes = - tree.parents - // map super-classes to a tuple of type and tree - .map { t => (t.tpe, t) } - // collect the parent that corresponds to Function1 and its type params - .collect { - case (RefinedType(RefinedType(parent, _, t1), _, r), function1) - if parent.typeSymbol.showFullName == "scala.Function1" => - (function1, t1, r) - } - .headOption - - def replaceParent(parent: Tree, in: List[Tree], withTree: Tree) = - in.foldRight(List.empty[Tree]) { (t, trees) => - (if (parent eq t) withTree else t) :: trees - } + val parents = tmpl.parents.foldRight(List.empty[Tree]) { (t, trees) => + (if (func1 eq t) specializedFunc1 else t) :: trees + } - def specializedParent(t1: Type, r: Type, orig: Tree): Option[Tree] = - // get the required class via the abbreviations in `typeAbbr`, if they - // don't both exist there, there is no specialization for the combination - typeAbbr.get(t1Member) -> typeAbbr.get(rMember) match { - case (Some(t1Abbr), Some(rAbbr)) => - Some(TypeTree(ctx.requiredClassRef("scala.compat.java8.JFunction1$mc" + rAbbr + t1Abbr + "$sp"))) - case _ => None - } + val body = tmpl.body.foldRight(List.empty[Tree]) { + case (tree: DefDef, acc) if tree.name == nme.apply => { + val specializedMethodName = specializedName(nme.apply, t1, r) + val specializedApply = ctx.owner.info.decls.lookup(specializedMethodName).asTerm - def withRenamedApply(parent: Tree): List[Tree] = { - (typeAbbr.get(t1Member), typeAbbr.get(rMember)) match { - case (Some(t1Abbr), Some(rAbbr)) => - tree.body.foldRight(List.empty[Tree]) { - case (tree: DefDef, acc) if tree.name == nme.apply => - val specializedMethodName = ("apply$mc" + t1Abbr + rAbbr + "$sp").toTermName - val specializedApply = ctx.owner.info.decls.lookup(specializedMethodName).asTerm - - val newDefDef = polyDefDef(specializedApply, trefs => vrefss => { - tree.rhs - .changeOwner(tree.symbol, specializedApply) - .subst(tree.vparamss.flatten.map(_.symbol), vrefss.flatten.map(_.symbol)) - }) - - newDefDef :: acc - case (tree, acc) => - tree :: acc - } - case x => tree.body + polyDefDef(specializedApply, trefs => vrefss => { + tree.rhs + .changeOwner(tree.symbol, specializedApply) + .subst(tree.vparamss.flatten.map(_.symbol), vrefss.flatten.map(_.symbol)) + }) :: acc } + case (tree, acc) => tree :: acc } - parentAndTypes.flatMap { case (parent, t1, r) => - specializedParent(t1, r, parent).map { newPar => - val parents = replaceParent(parent, in = tree.parents, withTree = newPar) - val body = withRenamedApply(newPar) - cpy.Template(tree)(parents = parents, body = body) - } - } getOrElse (tree) + cpy.Template(tmpl)(parents = parents, body = body) } } From f315b8dd301e2a83accea6f0af8aa17d193589be Mon Sep 17 00:00:00 2001 From: Felix Mulder Date: Wed, 14 Dec 2016 16:48:38 +0100 Subject: [PATCH 05/32] Add dispatch to specialized applys --- compiler/src/dotty/tools/dotc/Compiler.scala | 1 + .../DispatchToSpecializedApply.scala | 29 +++++++++++++++++++ .../dotc/transform/SpecializeFunction1.scala | 4 +-- 3 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 compiler/src/dotty/tools/dotc/transform/DispatchToSpecializedApply.scala diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index d2e15cad3aed..35622b588938 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -70,6 +70,7 @@ class Compiler { new Splitter, // Expand selections involving union types into conditionals new SpecializeFunction1), // Specialized Function1 by replacing super with specialized super List(new PhantomArgLift, // Extracts the evaluation of phantom arguments placing them before the call. + new DispatchToSpecializedApply, // Dispatch to the specialized apply by `SpecializeFunction1` new VCInlineMethods, // Inlines calls to value class methods new SeqLiterals, // Express vararg arguments as arrays new InterceptedMethods, // Special handling of `==`, `|=`, `getClass` methods diff --git a/compiler/src/dotty/tools/dotc/transform/DispatchToSpecializedApply.scala b/compiler/src/dotty/tools/dotc/transform/DispatchToSpecializedApply.scala new file mode 100644 index 000000000000..b3b612bd84fa --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/DispatchToSpecializedApply.scala @@ -0,0 +1,29 @@ +package dotty.tools +package dotc +package transform + +import TreeTransforms.{ MiniPhaseTransform, TransformerInfo } +import core._ +import Contexts.Context, Types._, Decorators._, Symbols._, DenotTransformers._ +import Denotations._, SymDenotations._, Scopes._, StdNames._, NameOps._, Names._ + +class DispatchToSpecializedApply extends MiniPhaseTransform { + import ast.Trees._ + import ast.tpd + + val phaseName = "dispatchToSpecializedApply" + + override def transformApply(tree: tpd.Apply)(implicit ctx: Context, info: TransformerInfo) = + tree match { + case Apply(select @ Select(id, nme.apply), arg :: Nil) => + val params = List(arg.tpe, tree.tpe) + val specializedApply = nme.apply.specializedFor(params, params.map(_.typeSymbol.name)) + val hasOverridenSpecializedApply = id.tpe.decls.iterator.exists { sym => + sym.is(Flags.Override) && (sym.name eq specializedApply) + } + + if (hasOverridenSpecializedApply) tpd.Apply(tpd.Select(id, specializedApply), arg :: Nil) + else tree + case _ => tree + } +} diff --git a/compiler/src/dotty/tools/dotc/transform/SpecializeFunction1.scala b/compiler/src/dotty/tools/dotc/transform/SpecializeFunction1.scala index 0ea053cbd641..9380c1fe7f37 100644 --- a/compiler/src/dotty/tools/dotc/transform/SpecializeFunction1.scala +++ b/compiler/src/dotty/tools/dotc/transform/SpecializeFunction1.scala @@ -109,11 +109,11 @@ class SpecializeFunction1 extends MiniPhaseTransform with DenotTransformer { def specializedApply: Symbol = { val specializedMethodName = specializedName(nme.apply, t1, r) ctx.newSymbol( - cref.symbol.owner, + cref.symbol, specializedMethodName, Flags.Override | Flags.Method, specializedFunction.info.decls.lookup(specializedMethodName).info - ).enteredAfter(this) + ) } val alteredScope = newScope From 1d82065734548b69966ea44e26b4363112509a25 Mon Sep 17 00:00:00 2001 From: Felix Mulder Date: Wed, 14 Dec 2016 18:11:22 +0100 Subject: [PATCH 06/32] Add forwarding method for generic case --- .../dotc/transform/SpecializeFunction1.scala | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/SpecializeFunction1.scala b/compiler/src/dotty/tools/dotc/transform/SpecializeFunction1.scala index 9380c1fe7f37..15f9efb9ef32 100644 --- a/compiler/src/dotty/tools/dotc/transform/SpecializeFunction1.scala +++ b/compiler/src/dotty/tools/dotc/transform/SpecializeFunction1.scala @@ -6,6 +6,7 @@ import TreeTransforms.{ MiniPhaseTransform, TransformerInfo } import core._ import Contexts.Context, Types._, Decorators._, Symbols._, DenotTransformers._ import Denotations._, SymDenotations._, Scopes._, StdNames._, NameOps._, Names._ +import ast.tpd class SpecializeFunction1 extends MiniPhaseTransform with DenotTransformer { import ast.tpd._ @@ -116,10 +117,8 @@ class SpecializeFunction1 extends MiniPhaseTransform with DenotTransformer { ) } - val alteredScope = newScope - scope.foreach { sym => - alteredScope.enter(if (sym.name eq nme.apply) specializedApply else sym) - } + val alteredScope = scope.cloneScope + alteredScope.enter(specializedApply) alteredScope } @@ -141,11 +140,19 @@ class SpecializeFunction1 extends MiniPhaseTransform with DenotTransformer { val specializedMethodName = specializedName(nme.apply, t1, r) val specializedApply = ctx.owner.info.decls.lookup(specializedMethodName).asTerm - polyDefDef(specializedApply, trefs => vrefss => { + val forwardingBody = + tpd.ref(specializedApply) + .appliedToArgs(tree.vparamss.head.map(vparam => ref(vparam.symbol))) + + val applyWithForwarding = cpy.DefDef(tree)(rhs = forwardingBody) + + val specializedApplyDefDef = polyDefDef(specializedApply, trefs => vrefss => { tree.rhs .changeOwner(tree.symbol, specializedApply) .subst(tree.vparamss.flatten.map(_.symbol), vrefss.flatten.map(_.symbol)) - }) :: acc + }) + + applyWithForwarding :: specializedApplyDefDef :: acc } case (tree, acc) => tree :: acc } From a932097ae4900014d1fbdc6e4a5f381bb6ea48bf Mon Sep 17 00:00:00 2001 From: Felix Mulder Date: Wed, 14 Dec 2016 19:20:56 +0100 Subject: [PATCH 07/32] Don't specialize Function1 tree when invalid to --- .../src/dotty/tools/dotc/transform/SpecializeFunction1.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/SpecializeFunction1.scala b/compiler/src/dotty/tools/dotc/transform/SpecializeFunction1.scala index 15f9efb9ef32..96f65c8c4016 100644 --- a/compiler/src/dotty/tools/dotc/transform/SpecializeFunction1.scala +++ b/compiler/src/dotty/tools/dotc/transform/SpecializeFunction1.scala @@ -86,7 +86,9 @@ class SpecializeFunction1 extends MiniPhaseTransform with DenotTransformer { .map { t => (t.tpe, t) } .collect { case (tp @ RefinedType(RefinedType(parent, _, t1), _, r), func1) - if tp.isRef(Function1) => (func1, t1, r) + if tp.isRef(Function1) && + argTypes.contains(t1.typeSymbol) && + returnTypes.contains(r.typeSymbol) => (func1, t1, r) } .headOption } From 1016194052324ef5dad0a90e9d4ace39cded6628 Mon Sep 17 00:00:00 2001 From: Felix Mulder Date: Wed, 14 Dec 2016 19:22:09 +0100 Subject: [PATCH 08/32] Write test to check for specialized apply --- .../transform/SpecializeFunction1Tests.scala | 58 ++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/compiler/test/dotty/tools/dotc/transform/SpecializeFunction1Tests.scala b/compiler/test/dotty/tools/dotc/transform/SpecializeFunction1Tests.scala index 9104b936b082..f9205fa8def6 100644 --- a/compiler/test/dotty/tools/dotc/transform/SpecializeFunction1Tests.scala +++ b/compiler/test/dotty/tools/dotc/transform/SpecializeFunction1Tests.scala @@ -12,6 +12,21 @@ class SpecializeFunction1Tests extends DottyBytecodeTest { import dotty.tools.backend.jvm.ASMConverters._ import dotty.tools.backend.jvm.AsmNode._ + protected def checkForBoxing(ins: List[Instruction], source: String): Unit = ins.foreach { + case Invoke(op, owner, name, desc, itf) => + def error = + s"""|---------------------------------- + |${ins.mkString("\n")} + |---------------------------------- + |From code: + |$source + |----------------------------------""".stripMargin + + assert(!owner.toLowerCase.contains("box"), s"Boxing instruction discovered in:\n$error") + assert(!name.toLowerCase.contains("box"), s"Boxing instruction discovered in:\n$error") + case _ => () + } + @Test def specializeParentIntToInt = { val source = """ |class Foo extends Function1[Int, Int] { @@ -29,10 +44,51 @@ class SpecializeFunction1Tests extends DottyBytecodeTest { val applys = methods .collect { case m if m.name == "apply$mcII$sp" => m + case m if m.name == "apply" => m } + .map(_.name) .toList - assert(applys.length == 1, "Wrong number of specialized applys") + assert( + // there should be two "apply", one generic and the one overwritten and + // then the specialized one + applys.length == 3, + s"Wrong number of specialized applys, actual length: ${applys.length} $applys" + ) + assert( + applys.contains("apply"), + "Foo did not contain `apply` forwarder method" + ) + assert( + applys.contains("apply$mcII$sp"), + "Foo did not contain specialized apply" + ) } } + + @Test def checkBoxingIntToInt = { + val source = + """|object Test { + | class Func1 extends Function1[Int, Int] { + | def apply(i: Int) = i + 1 + | } + | + | (new Func1)(1) + |}""".stripMargin + + checkBCode(source) { dir => + import scala.collection.JavaConverters._ + val clsIn = dir.lookupName("Test$.class", directory = false).input + val clsNode = loadClassNode(clsIn) + assert(clsNode.name == "Test$", s"inspecting wrong class: ${clsNode.name}") + + clsNode.methods.asScala + .find(_.name == "") + .map { m => + checkForBoxing(instructionsFromMethod(m), source) + } + .getOrElse(assert(false, "Could not find constructor for object `Test`")) + } + + } } From 55a35c4f60fc25a973ab8c0799cc30089a5d13f9 Mon Sep 17 00:00:00 2001 From: Felix Mulder Date: Fri, 23 Dec 2016 13:29:27 +0100 Subject: [PATCH 09/32] Remove `DispatchToSpecializedApply` phase --- compiler/src/dotty/tools/dotc/Compiler.scala | 1 - .../DispatchToSpecializedApply.scala | 29 ------------------- .../dotc/transform/SpecializeFunction1.scala | 16 ++++++++++ 3 files changed, 16 insertions(+), 30 deletions(-) delete mode 100644 compiler/src/dotty/tools/dotc/transform/DispatchToSpecializedApply.scala diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index 35622b588938..d2e15cad3aed 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -70,7 +70,6 @@ class Compiler { new Splitter, // Expand selections involving union types into conditionals new SpecializeFunction1), // Specialized Function1 by replacing super with specialized super List(new PhantomArgLift, // Extracts the evaluation of phantom arguments placing them before the call. - new DispatchToSpecializedApply, // Dispatch to the specialized apply by `SpecializeFunction1` new VCInlineMethods, // Inlines calls to value class methods new SeqLiterals, // Express vararg arguments as arrays new InterceptedMethods, // Special handling of `==`, `|=`, `getClass` methods diff --git a/compiler/src/dotty/tools/dotc/transform/DispatchToSpecializedApply.scala b/compiler/src/dotty/tools/dotc/transform/DispatchToSpecializedApply.scala deleted file mode 100644 index b3b612bd84fa..000000000000 --- a/compiler/src/dotty/tools/dotc/transform/DispatchToSpecializedApply.scala +++ /dev/null @@ -1,29 +0,0 @@ -package dotty.tools -package dotc -package transform - -import TreeTransforms.{ MiniPhaseTransform, TransformerInfo } -import core._ -import Contexts.Context, Types._, Decorators._, Symbols._, DenotTransformers._ -import Denotations._, SymDenotations._, Scopes._, StdNames._, NameOps._, Names._ - -class DispatchToSpecializedApply extends MiniPhaseTransform { - import ast.Trees._ - import ast.tpd - - val phaseName = "dispatchToSpecializedApply" - - override def transformApply(tree: tpd.Apply)(implicit ctx: Context, info: TransformerInfo) = - tree match { - case Apply(select @ Select(id, nme.apply), arg :: Nil) => - val params = List(arg.tpe, tree.tpe) - val specializedApply = nme.apply.specializedFor(params, params.map(_.typeSymbol.name)) - val hasOverridenSpecializedApply = id.tpe.decls.iterator.exists { sym => - sym.is(Flags.Override) && (sym.name eq specializedApply) - } - - if (hasOverridenSpecializedApply) tpd.Apply(tpd.Select(id, specializedApply), arg :: Nil) - else tree - case _ => tree - } -} diff --git a/compiler/src/dotty/tools/dotc/transform/SpecializeFunction1.scala b/compiler/src/dotty/tools/dotc/transform/SpecializeFunction1.scala index 96f65c8c4016..73afd42915cc 100644 --- a/compiler/src/dotty/tools/dotc/transform/SpecializeFunction1.scala +++ b/compiler/src/dotty/tools/dotc/transform/SpecializeFunction1.scala @@ -28,6 +28,22 @@ class SpecializeFunction1 extends MiniPhaseTransform with DenotTransformer { case _ => tree } + override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo) = { + import ast.Trees._ + tree match { + case Apply(select @ Select(id, nme.apply), arg :: Nil) => + val params = List(arg.tpe, tree.tpe) + val specializedApply = nme.apply.specializedFor(params, params.map(_.typeSymbol.name)) + val hasOverridenSpecializedApply = id.tpe.decls.iterator.exists { sym => + sym.is(Flags.Override) && (sym.name eq specializedApply) + } + + if (hasOverridenSpecializedApply) tpd.Apply(tpd.Select(id, specializedApply), arg :: Nil) + else tree + case _ => tree + } + } + private[this] val functionName = "JFunction1".toTermName private[this] val functionPkg = "scala.compat.java8.".toTermName private[this] var Function1: Symbol = _ From 9db4d27fefcdb19f734c395135c94bfebbcb1729 Mon Sep 17 00:00:00 2001 From: Felix Mulder Date: Fri, 30 Dec 2016 16:49:40 +0100 Subject: [PATCH 10/32] SpecializeFunction1: don't roll over parents, use mapConserve --- .../src/dotty/tools/dotc/core/NameOps.scala | 2 +- .../dotc/transform/SpecializeFunction1.scala | 239 ++++++++---------- 2 files changed, 112 insertions(+), 129 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/NameOps.scala b/compiler/src/dotty/tools/dotc/core/NameOps.scala index 1940a8be9266..4e568861e401 100644 --- a/compiler/src/dotty/tools/dotc/core/NameOps.scala +++ b/compiler/src/dotty/tools/dotc/core/NameOps.scala @@ -227,7 +227,7 @@ object NameOps { case nme.clone_ => nme.clone_ } - def specializedFor(classTargs: List[Types.Type], classTargsNames: List[Name], methodTargs: List[Types.Type] = scala.Nil, methodTarsNames: List[Name] = scala.Nil)(implicit ctx: Context): name.ThisName = { + def specializedFor(classTargs: List[Types.Type], classTargsNames: List[Name], methodTargs: List[Types.Type], methodTarsNames: List[Name])(implicit ctx: Context): name.ThisName = { val methodTags: Seq[Name] = (methodTargs zip methodTarsNames).sortBy(_._2).map(x => defn.typeTag(x._1)) val classTags: Seq[Name] = (classTargs zip classTargsNames).sortBy(_._2).map(x => defn.typeTag(x._1)) diff --git a/compiler/src/dotty/tools/dotc/transform/SpecializeFunction1.scala b/compiler/src/dotty/tools/dotc/transform/SpecializeFunction1.scala index 73afd42915cc..6db325ce2e4a 100644 --- a/compiler/src/dotty/tools/dotc/transform/SpecializeFunction1.scala +++ b/compiler/src/dotty/tools/dotc/transform/SpecializeFunction1.scala @@ -13,27 +13,119 @@ class SpecializeFunction1 extends MiniPhaseTransform with DenotTransformer { val phaseName = "specializeFunction1" + // Setup --------------------------------------------------------------------- + private[this] val functionName = "JFunction1".toTermName + private[this] val functionPkg = "scala.compat.java8.".toTermName + private[this] var argTypes: Set[Symbol] = _ + private[this] var retTypes: Set[Symbol] = _ + + override def prepareForUnit(tree: Tree)(implicit ctx: Context) = { + retTypes = Set(defn.BooleanClass, + defn.DoubleClass, + defn.FloatClass, + defn.IntClass, + defn.LongClass, + defn.UnitClass) + + argTypes = Set(defn.DoubleClass, + defn.FloatClass, + defn.IntClass, + defn.LongClass) + this + } + + // Transformations ----------------------------------------------------------- + /** Transforms all classes extending `Function1[-T1, +R]` so that * they instead extend the specialized version `JFunction$mp...` */ def transform(ref: SingleDenotation)(implicit ctx: Context) = ref match { - case ShouldTransformDenot(cref, t1, r, func1) => - transformDenot(cref, t1, r, func1) + case ShouldTransformDenot(cref, t1, r, func1) => { + val specializedFunction: Symbol = + ctx.getClassIfDefined(functionPkg ++ specializedName(functionName, t1, r)) + + def replaceFunction1(in: List[TypeRef]): List[TypeRef] = + in.mapConserve { tp => + if (tp.isRef(defn.FunctionClass(1)) && (specializedFunction ne NoSymbol)) + specializedFunction.typeRef + else tp + } + + def specializeApply(scope: Scope): Scope = + if ((specializedFunction ne NoSymbol) && (scope.lookup(nme.apply) ne NoSymbol)) { + def specializedApply: Symbol = { + val specializedMethodName = specializedName(nme.apply, t1, r) + ctx.newSymbol( + cref.symbol, + specializedMethodName, + Flags.Override | Flags.Method, + specializedFunction.info.decls.lookup(specializedMethodName).info + ) + } + + val alteredScope = scope.cloneScope + alteredScope.enter(specializedApply) + alteredScope + } + else scope + + val ClassInfo(prefix, cls, parents, decls, info) = cref.classInfo + val newInfo = ClassInfo(prefix, cls, replaceFunction1(in = parents), specializeApply(decls), info) + cref.copySymDenotation(info = newInfo) + } case _ => ref } + /** Transform the class definition's `Template`: + * + * - change the tree to have the correct parent + * - add the specialized apply method to the template body + * - forward the old `apply` to the specialized version + */ override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo) = tree match { - case ShouldTransformTree(func1, t1, r) => transformTree(tree, func1, t1, r) + case tmpl @ ShouldTransformTree(func1, t1, r) => { + val specializedFunc1 = + TypeTree(ctx.requiredClassRef(functionPkg ++ specializedName(functionName, t1, r))) + + val parents = tmpl.parents.mapConserve { t => + if (func1.isDefined && (func1.get eq t)) specializedFunc1 else t + } + + val body = tmpl.body.foldRight(List.empty[Tree]) { + case (tree: DefDef, acc) if tree.name == nme.apply => { + val specializedMethodName = specializedName(nme.apply, t1, r) + val specializedApply = ctx.owner.info.decls.lookup(specializedMethodName).asTerm + + val forwardingBody = + tpd.ref(specializedApply) + .appliedToArgs(tree.vparamss.head.map(vparam => ref(vparam.symbol))) + + val applyWithForwarding = cpy.DefDef(tree)(rhs = forwardingBody) + + val specializedApplyDefDef = polyDefDef(specializedApply, trefs => vrefss => { + tree.rhs + .changeOwner(tree.symbol, specializedApply) + .subst(tree.vparamss.flatten.map(_.symbol), vrefss.flatten.map(_.symbol)) + }) + + applyWithForwarding :: specializedApplyDefDef :: acc + } + case (tree, acc) => tree :: acc + } + + cpy.Template(tmpl)(parents = parents, body = body) + } case _ => tree } + /** Dispatch to specialized `apply`s in user code */ override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo) = { import ast.Trees._ tree match { case Apply(select @ Select(id, nme.apply), arg :: Nil) => val params = List(arg.tpe, tree.tpe) - val specializedApply = nme.apply.specializedFor(params, params.map(_.typeSymbol.name)) + val specializedApply = nme.apply.specializedFor(params, params.map(_.typeSymbol.name), Nil, Nil) val hasOverridenSpecializedApply = id.tpe.decls.iterator.exists { sym => sym.is(Flags.Override) && (sym.name eq specializedApply) } @@ -44,137 +136,28 @@ class SpecializeFunction1 extends MiniPhaseTransform with DenotTransformer { } } - private[this] val functionName = "JFunction1".toTermName - private[this] val functionPkg = "scala.compat.java8.".toTermName - private[this] var Function1: Symbol = _ - private[this] var argTypes: Set[Symbol] = _ - private[this] var returnTypes: Set[Symbol] = _ - private[this] var blacklisted: Set[Symbol] = _ - - /** Do setup of `argTypes` and `returnTypes` */ - override def prepareForUnit(tree: Tree)(implicit ctx: Context) = { - argTypes = Set(defn.DoubleClass, - defn.FloatClass, - defn.IntClass, - defn.LongClass, - defn.UnitClass, - defn.BooleanClass) - - returnTypes = Set(defn.DoubleClass, - defn.FloatClass, - defn.IntClass, - defn.LongClass) - - Function1 = ctx.requiredClass("scala.Function1") - - blacklisted = Set( - "scala.compat.java8.JFunction1", - "scala.runtime.AbstractFunction1", - "scala.PartialFunction", - "scala.runtime.AbstractPartialFunction" - ).map(ctx.requiredClass(_)) - - this - } + private def specializedName(name: Name, t1: Type, r: Type)(implicit ctx: Context): Name = + name.specializedFor(List(t1, r), List(t1, r).map(_.typeSymbol.name), Nil, Nil) + // Extractors ---------------------------------------------------------------- private object ShouldTransformDenot { - def unapply(cref: ClassDenotation)(implicit ctx: Context): Option[(ClassDenotation, Type, Type, Type)] = { - def collectFunc1(xs: List[Type])(implicit ctx: Context): Option[(Type, Type, Type)] = - xs.collect { - case func1 @ RefinedType(RefinedType(parent, _, t1), _, r) - if func1.isRef(Function1) => (t1, r, func1) - }.headOption - - collectFunc1(cref.info.parentsWithArgs).flatMap { case (t1, r, func1) => - if ( - !argTypes.contains(t1.typeSymbol) || - !returnTypes.contains(r.typeSymbol) || - blacklisted.exists(sym => cref.symbol.derivesFrom(sym)) - ) None - else Some((cref, t1, r, func1)) - } - } + def unapply(cref: ClassDenotation)(implicit ctx: Context): Option[(ClassDenotation, Type, Type, Type)] = + if (!cref.classParents.exists(_.isRef(defn.FunctionClass(1)))) None + else getFunc1(cref.typeRef).map { case (t1, r, func1) => (cref, t1, r, func1) } } private object ShouldTransformTree { - def unapply(tree: Template)(implicit ctx: Context): Option[(Tree, Type, Type)] = - tree.parents - .map { t => (t.tpe, t) } - .collect { - case (tp @ RefinedType(RefinedType(parent, _, t1), _, r), func1) - if tp.isRef(Function1) && - argTypes.contains(t1.typeSymbol) && - returnTypes.contains(r.typeSymbol) => (func1, t1, r) - } - .headOption - } - - private def specializedName(name: Name, t1: Type, r: Type)(implicit ctx: Context): Name = - name.specializedFor(List(t1, r), List(t1, r).map(_.typeSymbol.name)) - - def transformDenot(cref: ClassDenotation, t1: Type, r: Type, func1: Type)(implicit ctx: Context): SingleDenotation = { - val specializedFunction: TypeRef = - ctx.requiredClassRef(functionPkg ++ specializedName(functionName, t1, r)) - - def replaceFunction1(in: List[TypeRef]): List[TypeRef] = - in.foldRight(List.empty[TypeRef]) { (tp, acc) => - val newTp = - if (tp.isRef(Function1)) specializedFunction - else tp - newTp :: acc - } - - def specializeApply(scope: Scope): Scope = { - def specializedApply: Symbol = { - val specializedMethodName = specializedName(nme.apply, t1, r) - ctx.newSymbol( - cref.symbol, - specializedMethodName, - Flags.Override | Flags.Method, - specializedFunction.info.decls.lookup(specializedMethodName).info - ) + def unapply(tree: Template)(implicit ctx: Context): Option[(Option[Tree], Type, Type)] = + tree.parents.find(_.tpe.isRef(defn.FunctionClass(1))).flatMap { t => + getFunc1(t.tpe).map { case (t1, r, _) => (Some(t), t1, r) } } - - val alteredScope = scope.cloneScope - alteredScope.enter(specializedApply) - alteredScope - } - - val ClassInfo(prefix, cls, parents, decls, info) = cref.classInfo - val newInfo = ClassInfo(prefix, cls, replaceFunction1(in = parents), specializeApply(decls), info) - cref.copySymDenotation(info = newInfo) } - private def transformTree(tmpl: Template, func1: Tree, t1: Type, r: Type)(implicit ctx: Context) = { - val specializedFunc1 = - TypeTree(ctx.requiredClassRef(functionPkg ++ specializedName(functionName, t1, r))) - - val parents = tmpl.parents.foldRight(List.empty[Tree]) { (t, trees) => - (if (func1 eq t) specializedFunc1 else t) :: trees + private def getFunc1(tpe: Type)(implicit ctx: Context): Option[(Type, Type, Type)] = + tpe.baseTypeWithArgs(defn.FunctionClass(1)) match { + case func1 @ RefinedType(RefinedType(parent, _, t1), _, r) if ( + argTypes.contains(t1.typeSymbol) && retTypes.contains(r.typeSymbol) + ) => Some((t1, r, func1)) + case _ => None } - - val body = tmpl.body.foldRight(List.empty[Tree]) { - case (tree: DefDef, acc) if tree.name == nme.apply => { - val specializedMethodName = specializedName(nme.apply, t1, r) - val specializedApply = ctx.owner.info.decls.lookup(specializedMethodName).asTerm - - val forwardingBody = - tpd.ref(specializedApply) - .appliedToArgs(tree.vparamss.head.map(vparam => ref(vparam.symbol))) - - val applyWithForwarding = cpy.DefDef(tree)(rhs = forwardingBody) - - val specializedApplyDefDef = polyDefDef(specializedApply, trefs => vrefss => { - tree.rhs - .changeOwner(tree.symbol, specializedApply) - .subst(tree.vparamss.flatten.map(_.symbol), vrefss.flatten.map(_.symbol)) - }) - - applyWithForwarding :: specializedApplyDefDef :: acc - } - case (tree, acc) => tree :: acc - } - - cpy.Template(tmpl)(parents = parents, body = body) - } } From 71005aec537dc199a583dc25c5b154ece83b0fc5 Mon Sep 17 00:00:00 2001 From: Felix Mulder Date: Tue, 14 Feb 2017 20:53:53 +0100 Subject: [PATCH 11/32] Rewrite to handle all specialized functions --- compiler/src/dotty/tools/dotc/Compiler.scala | 4 +- .../dotc/transform/SpecializeFunction1.scala | 206 +++++++++++------- 2 files changed, 134 insertions(+), 76 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index d2e15cad3aed..76daf1bf144f 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -81,9 +81,7 @@ class Compiler { new Simplify, // Perform local optimizations, simplified versions of what linker does. new PrimitiveForwarders, // Add forwarders to trait methods that have a mismatch between generic and primitives new FunctionXXLForwarders, // Add forwarders for FunctionXXL apply method - new ArrayConstructors, // Intercept creation of (non-generic) arrays and intrinsify. - new SpecializeExtendsFunction1, // <- what he said - new DispatchToSpecializedApply), // <- what she said + new ArrayConstructors), // Intercept creation of (non-generic) arrays and intrinsify. List(new Erasure), // Rewrite types to JVM model, erasing all type parameters, abstract types and refinements. List(new ElimErasedValueType, // Expand erased value types to their underlying implmementation types new VCElideAllocations, // Peep-hole optimization to eliminate unnecessary value class allocations diff --git a/compiler/src/dotty/tools/dotc/transform/SpecializeFunction1.scala b/compiler/src/dotty/tools/dotc/transform/SpecializeFunction1.scala index 6db325ce2e4a..0efce59bb334 100644 --- a/compiler/src/dotty/tools/dotc/transform/SpecializeFunction1.scala +++ b/compiler/src/dotty/tools/dotc/transform/SpecializeFunction1.scala @@ -14,23 +14,28 @@ class SpecializeFunction1 extends MiniPhaseTransform with DenotTransformer { val phaseName = "specializeFunction1" // Setup --------------------------------------------------------------------- - private[this] val functionName = "JFunction1".toTermName + private[this] val functionName = "JFunction".toTermName private[this] val functionPkg = "scala.compat.java8.".toTermName private[this] var argTypes: Set[Symbol] = _ private[this] var retTypes: Set[Symbol] = _ override def prepareForUnit(tree: Tree)(implicit ctx: Context) = { - retTypes = Set(defn.BooleanClass, - defn.DoubleClass, - defn.FloatClass, + retTypes = Set(defn.UnitClass, + defn.BooleanClass, defn.IntClass, + defn.FloatClass, defn.LongClass, - defn.UnitClass) + defn.DoubleClass, + /* only for Function0: */ + defn.ByteClass, + defn.ShortClass, + defn.CharClass) - argTypes = Set(defn.DoubleClass, - defn.FloatClass, - defn.IntClass, - defn.LongClass) + argTypes = Set(defn.IntClass, + defn.LongClass, + defn.DoubleClass, + /* only for Function1: */ + defn.FloatClass) this } @@ -40,37 +45,46 @@ class SpecializeFunction1 extends MiniPhaseTransform with DenotTransformer { * they instead extend the specialized version `JFunction$mp...` */ def transform(ref: SingleDenotation)(implicit ctx: Context) = ref match { - case ShouldTransformDenot(cref, t1, r, func1) => { - val specializedFunction: Symbol = - ctx.getClassIfDefined(functionPkg ++ specializedName(functionName, t1, r)) - - def replaceFunction1(in: List[TypeRef]): List[TypeRef] = - in.mapConserve { tp => - if (tp.isRef(defn.FunctionClass(1)) && (specializedFunction ne NoSymbol)) - specializedFunction.typeRef - else tp + case cref @ ShouldTransformDenot(targets) => { + val specializedSymbols: Map[Symbol, (Symbol, Symbol)] = (for (SpecializationTarget(target, args, ret, original) <- targets) yield { + val arity = args.length + val specializedParent = ctx.getClassIfDefined { + functionPkg ++ specializedName(functionName ++ arity, args, ret) } - def specializeApply(scope: Scope): Scope = - if ((specializedFunction ne NoSymbol) && (scope.lookup(nme.apply) ne NoSymbol)) { - def specializedApply: Symbol = { - val specializedMethodName = specializedName(nme.apply, t1, r) - ctx.newSymbol( - cref.symbol, - specializedMethodName, - Flags.Override | Flags.Method, - specializedFunction.info.decls.lookup(specializedMethodName).info - ) - } + val specializedApply: Symbol = { + val specializedMethodName = specializedName(nme.apply, args, ret) + ctx.newSymbol( + cref.symbol, + specializedMethodName, + Flags.Override | Flags.Method, + specializedParent.info.decls.lookup(specializedMethodName).info + ) + } + + original -> (specializedParent, specializedApply) + }).toMap - val alteredScope = scope.cloneScope - alteredScope.enter(specializedApply) - alteredScope + def specializeApplys(scope: Scope): Scope = { + val alteredScope = scope.cloneScope + specializedSymbols.values.foreach { case (_, apply) => + alteredScope.enter(apply) + } + alteredScope + } + + def replace(in: List[TypeRef]): List[TypeRef] = + in.map { tref => + val sym = tref.symbol + specializedSymbols.get(sym).map { case (specializedParent, _) => + specializedParent.typeRef + } + .getOrElse(tref) } - else scope val ClassInfo(prefix, cls, parents, decls, info) = cref.classInfo - val newInfo = ClassInfo(prefix, cls, replaceFunction1(in = parents), specializeApply(decls), info) + val newParents = replace(parents) + val newInfo = ClassInfo(prefix, cls, newParents, specializeApplys(decls), info) cref.copySymDenotation(info = newInfo) } case _ => ref @@ -84,37 +98,51 @@ class SpecializeFunction1 extends MiniPhaseTransform with DenotTransformer { */ override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo) = tree match { - case tmpl @ ShouldTransformTree(func1, t1, r) => { - val specializedFunc1 = - TypeTree(ctx.requiredClassRef(functionPkg ++ specializedName(functionName, t1, r))) + case tmpl @ ShouldTransformTree(targets) => { + val symbolMap = (for ((tree, SpecializationTarget(target, args, ret, orig)) <- targets) yield { + val arity = args.length + val specializedParent = TypeTree { + ctx.requiredClassRef(functionPkg ++ specializedName(functionName ++ arity, args, ret)) + } + val specializedMethodName = specializedName(nme.apply, args, ret) + val specializedApply = ctx.owner.info.decls.lookup(specializedMethodName).asTerm - val parents = tmpl.parents.mapConserve { t => - if (func1.isDefined && (func1.get eq t)) specializedFunc1 else t - } + orig -> (specializedParent, specializedApply) + }).toMap - val body = tmpl.body.foldRight(List.empty[Tree]) { + val body0 = tmpl.body.foldRight(List.empty[Tree]) { case (tree: DefDef, acc) if tree.name == nme.apply => { - val specializedMethodName = specializedName(nme.apply, t1, r) - val specializedApply = ctx.owner.info.decls.lookup(specializedMethodName).asTerm - - val forwardingBody = - tpd.ref(specializedApply) - .appliedToArgs(tree.vparamss.head.map(vparam => ref(vparam.symbol))) - - val applyWithForwarding = cpy.DefDef(tree)(rhs = forwardingBody) - - val specializedApplyDefDef = polyDefDef(specializedApply, trefs => vrefss => { - tree.rhs - .changeOwner(tree.symbol, specializedApply) - .subst(tree.vparamss.flatten.map(_.symbol), vrefss.flatten.map(_.symbol)) - }) - - applyWithForwarding :: specializedApplyDefDef :: acc + val inheritedFrom = + tree.symbol.allOverriddenSymbols + .map(_.owner) + .map(symbolMap.get) + .flatten + .toList + .headOption + + inheritedFrom.map { case (parent, apply) => + val forwardingBody = tpd + .ref(apply) + .appliedToArgs(tree.vparamss.head.map(vparam => ref(vparam.symbol))) + + val applyWithForwarding = cpy.DefDef(tree)(rhs = forwardingBody) + + val specializedApplyDefDef = + polyDefDef(apply, trefs => vrefss => { + tree.rhs + .changeOwner(tree.symbol, apply) + .subst(tree.vparamss.flatten.map(_.symbol), vrefss.flatten.map(_.symbol)) + }) + + applyWithForwarding :: specializedApplyDefDef :: acc + } + .getOrElse(tree :: acc) } case (tree, acc) => tree :: acc } + val parents = symbolMap.map { case (_, (parent, _)) => parent } - cpy.Template(tmpl)(parents = parents, body = body) + cpy.Template(tmpl)(parents = parents.toList, body = body0) } case _ => tree } @@ -136,28 +164,60 @@ class SpecializeFunction1 extends MiniPhaseTransform with DenotTransformer { } } - private def specializedName(name: Name, t1: Type, r: Type)(implicit ctx: Context): Name = - name.specializedFor(List(t1, r), List(t1, r).map(_.typeSymbol.name), Nil, Nil) + private def specializedName(name: Name, args: List[Type], ret: Type)(implicit ctx: Context): Name = { + val typeParams = args :+ ret + name.specializedFor(typeParams, typeParams.map(_.typeSymbol.name), Nil, Nil) + } // Extractors ---------------------------------------------------------------- private object ShouldTransformDenot { - def unapply(cref: ClassDenotation)(implicit ctx: Context): Option[(ClassDenotation, Type, Type, Type)] = - if (!cref.classParents.exists(_.isRef(defn.FunctionClass(1)))) None - else getFunc1(cref.typeRef).map { case (t1, r, func1) => (cref, t1, r, func1) } + def unapply(cref: ClassDenotation)(implicit ctx: Context): Option[Seq[SpecializationTarget]] = + if (!cref.classParents.map(_.symbol).exists(defn.isFunctionClass)) None + else Some(getSpecTargets(cref.typeRef)) } private object ShouldTransformTree { - def unapply(tree: Template)(implicit ctx: Context): Option[(Option[Tree], Type, Type)] = - tree.parents.find(_.tpe.isRef(defn.FunctionClass(1))).flatMap { t => - getFunc1(t.tpe).map { case (t1, r, _) => (Some(t), t1, r) } - } + def unapply(tree: Template)(implicit ctx: Context): Option[Seq[(Tree, SpecializationTarget)]] = { + val treeToTargets = tree.parents + .map(t => (t, getSpecTargets(t.tpe))) + .filter(_._2.nonEmpty) + .map { case (t, xs) => (t, xs.head) } + + if (treeToTargets.isEmpty) None else Some(treeToTargets) + } } - private def getFunc1(tpe: Type)(implicit ctx: Context): Option[(Type, Type, Type)] = - tpe.baseTypeWithArgs(defn.FunctionClass(1)) match { - case func1 @ RefinedType(RefinedType(parent, _, t1), _, r) if ( - argTypes.contains(t1.typeSymbol) && retTypes.contains(r.typeSymbol) - ) => Some((t1, r, func1)) - case _ => None + private case class SpecializationTarget(target: Symbol, + params: List[Type], + ret: Type, + original: Symbol) + + /** Gets all valid specialization targets on `tpe`, allowing multiple + * implementations of FunctionX traits + */ + private def getSpecTargets(tpe: Type)(implicit ctx: Context): List[SpecializationTarget] = { + val functionParents = + tpe.classSymbols.iterator + .flatMap(_.baseClasses) + .filter(defn.isFunctionClass) + + val tpeCls = tpe.widenDealias + functionParents.map { sym => + val typeParams = tpeCls.baseArgTypes(sym) + val args = typeParams.init + val ret = typeParams.last + + val interfaceName = + (functionName ++ args.length) + .specializedFor(typeParams, typeParams.map(_.typeSymbol.name), Nil, Nil) + + val interface = ctx.getClassIfDefined(functionPkg ++ interfaceName) + + if (interface.exists) Some { + SpecializationTarget(interface, args, ret, sym) + } + else None } + .flatten.toList + } } From 9bab6a9b63e5fbb897f9735f4671a25c99d07273 Mon Sep 17 00:00:00 2001 From: Felix Mulder Date: Thu, 16 Feb 2017 10:58:52 +0100 Subject: [PATCH 12/32] Don't remove parents not being specialized --- .../dotc/transform/SpecializeFunction1.scala | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/SpecializeFunction1.scala b/compiler/src/dotty/tools/dotc/transform/SpecializeFunction1.scala index 0efce59bb334..9a15cc54f76d 100644 --- a/compiler/src/dotty/tools/dotc/transform/SpecializeFunction1.scala +++ b/compiler/src/dotty/tools/dotc/transform/SpecializeFunction1.scala @@ -105,10 +105,12 @@ class SpecializeFunction1 extends MiniPhaseTransform with DenotTransformer { ctx.requiredClassRef(functionPkg ++ specializedName(functionName ++ arity, args, ret)) } val specializedMethodName = specializedName(nme.apply, args, ret) - val specializedApply = ctx.owner.info.decls.lookup(specializedMethodName).asTerm + val specializedApply = ctx.owner.info.decls.lookup(specializedMethodName) - orig -> (specializedParent, specializedApply) - }).toMap + if (specializedApply.exists) + Some(orig -> (specializedParent, specializedApply.asTerm)) + else None + }).flatten.toMap val body0 = tmpl.body.foldRight(List.empty[Tree]) { case (tree: DefDef, acc) if tree.name == nme.apply => { @@ -140,9 +142,15 @@ class SpecializeFunction1 extends MiniPhaseTransform with DenotTransformer { } case (tree, acc) => tree :: acc } - val parents = symbolMap.map { case (_, (parent, _)) => parent } - cpy.Template(tmpl)(parents = parents.toList, body = body0) + val specializedParents = tree.parents.map { t => + symbolMap + .get(t.symbol) + .map { case (newSym, _) => newSym } + .getOrElse(t) + } + + cpy.Template(tmpl)(parents = specializedParents, body = body0) } case _ => tree } From b0e175ac30eaac084f3966b2407420f16e1b3945 Mon Sep 17 00:00:00 2001 From: Felix Mulder Date: Thu, 16 Feb 2017 11:49:12 +0100 Subject: [PATCH 13/32] Add plain function tests to NameOps and Definitions --- compiler/src/dotty/tools/dotc/Compiler.scala | 2 +- compiler/src/dotty/tools/dotc/core/Definitions.scala | 7 ++++++- compiler/src/dotty/tools/dotc/core/NameOps.scala | 8 ++++++-- ...pecializeFunction1.scala => SpecializeFunctions.scala} | 6 +++--- ...unction1Tests.scala => SpecializeFunctionsTests.scala} | 3 +-- 5 files changed, 17 insertions(+), 9 deletions(-) rename compiler/src/dotty/tools/dotc/transform/{SpecializeFunction1.scala => SpecializeFunctions.scala} (97%) rename compiler/test/dotty/tools/dotc/transform/{SpecializeFunction1Tests.scala => SpecializeFunctionsTests.scala} (97%) diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index 76daf1bf144f..1bd239b415a3 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -68,7 +68,7 @@ class Compiler { new ShortcutImplicits, // Allow implicit functions without creating closures new CrossCastAnd, // Normalize selections involving intersection types. new Splitter, // Expand selections involving union types into conditionals - new SpecializeFunction1), // Specialized Function1 by replacing super with specialized super + new SpecializeFunctions), // Specialized Function1 by replacing super with specialized super List(new PhantomArgLift, // Extracts the evaluation of phantom arguments placing them before the call. new VCInlineMethods, // Inlines calls to value class methods new SeqLiterals, // Express vararg arguments as arrays diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 3ecf650f6c89..8706b8946e29 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -801,12 +801,17 @@ class Definitions { def isBottomType(tp: Type) = tp.derivesFrom(NothingClass) || tp.derivesFrom(NullClass) - /** Is a function class. + /** Is any function class that satisfies: * - FunctionN for N >= 0 * - ImplicitFunctionN for N > 0 */ def isFunctionClass(cls: Symbol) = scalaClassName(cls).isFunction + /** Is a function class where + * - FunctionN for N >= 0 + */ + def isPlainFunctionClass(cls: Symbol) = scalaClassName(cls).isPlainFunction + /** Is an implicit function class. * - ImplicitFunctionN for N > 0 */ diff --git a/compiler/src/dotty/tools/dotc/core/NameOps.scala b/compiler/src/dotty/tools/dotc/core/NameOps.scala index 4e568861e401..ed439622215e 100644 --- a/compiler/src/dotty/tools/dotc/core/NameOps.scala +++ b/compiler/src/dotty/tools/dotc/core/NameOps.scala @@ -177,13 +177,17 @@ object NameOps { if (n == 0) -1 else n } - /** Is a function name + /** Is any function name that satisfies * - FunctionN for N >= 0 * - ImplicitFunctionN for N >= 1 - * - false otherwise */ def isFunction: Boolean = functionArity >= 0 + /** Is a function name + * - FunctionN for N >= 0 + */ + def isPlainFunction: Boolean = functionArityFor(tpnme.Function) >= 0 + /** Is a implicit function name * - ImplicitFunctionN for N >= 1 * - false otherwise diff --git a/compiler/src/dotty/tools/dotc/transform/SpecializeFunction1.scala b/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala similarity index 97% rename from compiler/src/dotty/tools/dotc/transform/SpecializeFunction1.scala rename to compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala index 9a15cc54f76d..e03b1357fdfe 100644 --- a/compiler/src/dotty/tools/dotc/transform/SpecializeFunction1.scala +++ b/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala @@ -8,7 +8,7 @@ import Contexts.Context, Types._, Decorators._, Symbols._, DenotTransformers._ import Denotations._, SymDenotations._, Scopes._, StdNames._, NameOps._, Names._ import ast.tpd -class SpecializeFunction1 extends MiniPhaseTransform with DenotTransformer { +class SpecializeFunctions extends MiniPhaseTransform with DenotTransformer { import ast.tpd._ val phaseName = "specializeFunction1" @@ -180,7 +180,7 @@ class SpecializeFunction1 extends MiniPhaseTransform with DenotTransformer { // Extractors ---------------------------------------------------------------- private object ShouldTransformDenot { def unapply(cref: ClassDenotation)(implicit ctx: Context): Option[Seq[SpecializationTarget]] = - if (!cref.classParents.map(_.symbol).exists(defn.isFunctionClass)) None + if (!cref.classParents.map(_.symbol).exists(defn.isPlainFunctionClass)) None else Some(getSpecTargets(cref.typeRef)) } @@ -207,7 +207,7 @@ class SpecializeFunction1 extends MiniPhaseTransform with DenotTransformer { val functionParents = tpe.classSymbols.iterator .flatMap(_.baseClasses) - .filter(defn.isFunctionClass) + .filter(defn.isPlainFunctionClass) val tpeCls = tpe.widenDealias functionParents.map { sym => diff --git a/compiler/test/dotty/tools/dotc/transform/SpecializeFunction1Tests.scala b/compiler/test/dotty/tools/dotc/transform/SpecializeFunctionsTests.scala similarity index 97% rename from compiler/test/dotty/tools/dotc/transform/SpecializeFunction1Tests.scala rename to compiler/test/dotty/tools/dotc/transform/SpecializeFunctionsTests.scala index f9205fa8def6..ca611787aff9 100644 --- a/compiler/test/dotty/tools/dotc/transform/SpecializeFunction1Tests.scala +++ b/compiler/test/dotty/tools/dotc/transform/SpecializeFunctionsTests.scala @@ -7,7 +7,7 @@ import org.junit.Test import dotty.tools.backend.jvm.DottyBytecodeTest -class SpecializeFunction1Tests extends DottyBytecodeTest { +class SpecializeFunctionsTests extends DottyBytecodeTest { import dotty.tools.backend.jvm.ASMConverters._ import dotty.tools.backend.jvm.AsmNode._ @@ -89,6 +89,5 @@ class SpecializeFunction1Tests extends DottyBytecodeTest { } .getOrElse(assert(false, "Could not find constructor for object `Test`")) } - } } From 6a9eabbec355d82efd8fb49bb92dd8d68531a244 Mon Sep 17 00:00:00 2001 From: Felix Mulder Date: Thu, 16 Feb 2017 20:27:27 +0100 Subject: [PATCH 14/32] Rewrite `SpecializeFunctions` from `DenotTransformer` to `InfoTransformer` --- .../dotc/transform/SpecializeFunctions.scala | 305 ++++++------------ .../transform/SpecializeFunctionsTests.scala | 30 ++ 2 files changed, 135 insertions(+), 200 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala b/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala index e03b1357fdfe..106d2767e1bb 100644 --- a/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala +++ b/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala @@ -1,231 +1,136 @@ -package dotty.tools -package dotc +package dotty.tools.dotc package transform import TreeTransforms.{ MiniPhaseTransform, TransformerInfo } -import core._ +import ast.Trees._, ast.tpd, core._ import Contexts.Context, Types._, Decorators._, Symbols._, DenotTransformers._ -import Denotations._, SymDenotations._, Scopes._, StdNames._, NameOps._, Names._ -import ast.tpd +import SymDenotations._, Scopes._, StdNames._, NameOps._, Names._ -class SpecializeFunctions extends MiniPhaseTransform with DenotTransformer { - import ast.tpd._ - - val phaseName = "specializeFunction1" - - // Setup --------------------------------------------------------------------- - private[this] val functionName = "JFunction".toTermName - private[this] val functionPkg = "scala.compat.java8.".toTermName - private[this] var argTypes: Set[Symbol] = _ - private[this] var retTypes: Set[Symbol] = _ - - override def prepareForUnit(tree: Tree)(implicit ctx: Context) = { - retTypes = Set(defn.UnitClass, - defn.BooleanClass, - defn.IntClass, - defn.FloatClass, - defn.LongClass, - defn.DoubleClass, - /* only for Function0: */ - defn.ByteClass, - defn.ShortClass, - defn.CharClass) - - argTypes = Set(defn.IntClass, - defn.LongClass, - defn.DoubleClass, - /* only for Function1: */ - defn.FloatClass) - this - } +import scala.collection.mutable - // Transformations ----------------------------------------------------------- - - /** Transforms all classes extending `Function1[-T1, +R]` so that - * they instead extend the specialized version `JFunction$mp...` - */ - def transform(ref: SingleDenotation)(implicit ctx: Context) = ref match { - case cref @ ShouldTransformDenot(targets) => { - val specializedSymbols: Map[Symbol, (Symbol, Symbol)] = (for (SpecializationTarget(target, args, ret, original) <- targets) yield { - val arity = args.length - val specializedParent = ctx.getClassIfDefined { - functionPkg ++ specializedName(functionName ++ arity, args, ret) - } - - val specializedApply: Symbol = { - val specializedMethodName = specializedName(nme.apply, args, ret) - ctx.newSymbol( - cref.symbol, - specializedMethodName, - Flags.Override | Flags.Method, - specializedParent.info.decls.lookup(specializedMethodName).info - ) - } +/** Specializes classes that inherit from `FunctionN` where there exists a + * specialized form. + */ +class SpecializeFunctions extends MiniPhaseTransform with InfoTransformer { + import ast.tpd._ + val phaseName = "specializeFunctions" - original -> (specializedParent, specializedApply) - }).toMap + /** Transforms the type to include decls for specialized applys and replace + * the class parents with specialized versions. + */ + def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context) = tp match { + case tp: ClassInfo if !sym.is(Flags.Package) => { + var newApplys: List[Symbol] = Nil + + val newParents = tp.parents.mapConserve { parent => + if (defn.isPlainFunctionClass(parent.symbol)) { + val typeParams = tp.typeRef.baseArgTypes(parent.classSymbol) + val interface = specInterface(typeParams) + + if (interface.exists) { + val specializedApply: Symbol = { + val specializedMethodName = specializedName(nme.apply, typeParams) + ctx.newSymbol( + sym, + specializedMethodName, + Flags.Override | Flags.Method, + interface.info.decls.lookup(specializedMethodName).info + ) + } - def specializeApplys(scope: Scope): Scope = { - val alteredScope = scope.cloneScope - specializedSymbols.values.foreach { case (_, apply) => - alteredScope.enter(apply) + newApplys = specializedApply :: newApplys + interface.typeRef + } + else parent } - alteredScope + else parent } - def replace(in: List[TypeRef]): List[TypeRef] = - in.map { tref => - val sym = tref.symbol - specializedSymbols.get(sym).map { case (specializedParent, _) => - specializedParent.typeRef - } - .getOrElse(tref) - } + def newDecls = newApplys.foldLeft(tp.decls.cloneScope) { + (scope, sym) => scope.enter(sym); scope + } - val ClassInfo(prefix, cls, parents, decls, info) = cref.classInfo - val newParents = replace(parents) - val newInfo = ClassInfo(prefix, cls, newParents, specializeApplys(decls), info) - cref.copySymDenotation(info = newInfo) + if (newApplys eq Nil) tp + else tp.derivedClassInfo(classParents = newParents, decls = newDecls) } - case _ => ref - } - /** Transform the class definition's `Template`: - * - * - change the tree to have the correct parent - * - add the specialized apply method to the template body - * - forward the old `apply` to the specialized version - */ - override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo) = - tree match { - case tmpl @ ShouldTransformTree(targets) => { - val symbolMap = (for ((tree, SpecializationTarget(target, args, ret, orig)) <- targets) yield { - val arity = args.length - val specializedParent = TypeTree { - ctx.requiredClassRef(functionPkg ++ specializedName(functionName ++ arity, args, ret)) - } - val specializedMethodName = specializedName(nme.apply, args, ret) - val specializedApply = ctx.owner.info.decls.lookup(specializedMethodName) - - if (specializedApply.exists) - Some(orig -> (specializedParent, specializedApply.asTerm)) - else None - }).flatten.toMap - - val body0 = tmpl.body.foldRight(List.empty[Tree]) { - case (tree: DefDef, acc) if tree.name == nme.apply => { - val inheritedFrom = - tree.symbol.allOverriddenSymbols - .map(_.owner) - .map(symbolMap.get) - .flatten - .toList - .headOption - - inheritedFrom.map { case (parent, apply) => - val forwardingBody = tpd - .ref(apply) - .appliedToArgs(tree.vparamss.head.map(vparam => ref(vparam.symbol))) - - val applyWithForwarding = cpy.DefDef(tree)(rhs = forwardingBody) - - val specializedApplyDefDef = - polyDefDef(apply, trefs => vrefss => { - tree.rhs - .changeOwner(tree.symbol, apply) - .subst(tree.vparamss.flatten.map(_.symbol), vrefss.flatten.map(_.symbol)) - }) - - applyWithForwarding :: specializedApplyDefDef :: acc - } - .getOrElse(tree :: acc) - } - case (tree, acc) => tree :: acc - } + case _ => tp + } - val specializedParents = tree.parents.map { t => - symbolMap - .get(t.symbol) - .map { case (newSym, _) => newSym } - .getOrElse(t) + /** Transforms the `Template` of the classes to contain forwarders from the + * generic applys to the specialized ones. Also replaces parents of the + * class on the tree level and inserts the specialized applys in the + * template body. + */ + override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo) = { + val buf = new mutable.ListBuffer[Tree] + val newBody = tree.body.mapConserve { + case dt: DefDef if dt.name == nme.apply && dt.vparamss.length == 1 => { + val specializedApply = ctx.owner.info.decls.lookup { + specializedName( + nme.apply, + dt.vparamss.head.map(_.symbol.info) :+ dt.tpe.widen.finalResultType + ) } - cpy.Template(tmpl)(parents = specializedParents, body = body0) + if (specializedApply.exists) { + val apply = specializedApply.asTerm + val specializedDecl = + polyDefDef(apply, trefs => vrefss => { + dt.rhs + .changeOwner(dt.symbol, apply) + .subst(dt.vparamss.flatten.map(_.symbol), vrefss.flatten.map(_.symbol)) + }) + + buf += specializedDecl + + // create a forwarding to the specialized apply + cpy.DefDef(dt)(rhs = { + tpd + .ref(apply) + .appliedToArgs(dt.vparamss.head.map(vparam => ref(vparam.symbol))) + }) + } else dt } - case _ => tree + case x => x } - /** Dispatch to specialized `apply`s in user code */ - override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo) = { - import ast.Trees._ - tree match { - case Apply(select @ Select(id, nme.apply), arg :: Nil) => - val params = List(arg.tpe, tree.tpe) - val specializedApply = nme.apply.specializedFor(params, params.map(_.typeSymbol.name), Nil, Nil) - val hasOverridenSpecializedApply = id.tpe.decls.iterator.exists { sym => - sym.is(Flags.Override) && (sym.name eq specializedApply) - } + val newParents = tree.parents.mapConserve { parent => + if (defn.isPlainFunctionClass(parent.symbol)) { + val typeParams = tree.tpe.baseArgTypes(parent.symbol) + val interface = specInterface(typeParams) - if (hasOverridenSpecializedApply) tpd.Apply(tpd.Select(id, specializedApply), arg :: Nil) - else tree - case _ => tree + if (interface.exists) TypeTree(interface.info) + else parent + } + else parent } - } - - private def specializedName(name: Name, args: List[Type], ret: Type)(implicit ctx: Context): Name = { - val typeParams = args :+ ret - name.specializedFor(typeParams, typeParams.map(_.typeSymbol.name), Nil, Nil) - } - // Extractors ---------------------------------------------------------------- - private object ShouldTransformDenot { - def unapply(cref: ClassDenotation)(implicit ctx: Context): Option[Seq[SpecializationTarget]] = - if (!cref.classParents.map(_.symbol).exists(defn.isPlainFunctionClass)) None - else Some(getSpecTargets(cref.typeRef)) + cpy.Template(tree)(parents = newParents, body = buf.toList ++ newBody) } - private object ShouldTransformTree { - def unapply(tree: Template)(implicit ctx: Context): Option[Seq[(Tree, SpecializationTarget)]] = { - val treeToTargets = tree.parents - .map(t => (t, getSpecTargets(t.tpe))) - .filter(_._2.nonEmpty) - .map { case (t, xs) => (t, xs.head) } + /** Dispatch to specialized `apply`s in user code when available */ + override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo) = + tree match { + case Apply(select @ Select(id, nme.apply), args) => { + val params = args.map(_.tpe) :+ tree.tpe + val specializedApply = specializedName(nme.apply, params) - if (treeToTargets.isEmpty) None else Some(treeToTargets) + if (tree.fun.symbol.owner.info.member(specializedApply).exists) + tpd.Apply(tpd.Select(id, specializedApply), args) + else tree + } + case _ => tree } - } - private case class SpecializationTarget(target: Symbol, - params: List[Type], - ret: Type, - original: Symbol) + @inline private def specializedName(name: Name, args: List[Type])(implicit ctx: Context) = + name.specializedFor(args, args.map(_.typeSymbol.name), Nil, Nil) - /** Gets all valid specialization targets on `tpe`, allowing multiple - * implementations of FunctionX traits - */ - private def getSpecTargets(tpe: Type)(implicit ctx: Context): List[SpecializationTarget] = { - val functionParents = - tpe.classSymbols.iterator - .flatMap(_.baseClasses) - .filter(defn.isPlainFunctionClass) - - val tpeCls = tpe.widenDealias - functionParents.map { sym => - val typeParams = tpeCls.baseArgTypes(sym) - val args = typeParams.init - val ret = typeParams.last - - val interfaceName = - (functionName ++ args.length) - .specializedFor(typeParams, typeParams.map(_.typeSymbol.name), Nil, Nil) - - val interface = ctx.getClassIfDefined(functionPkg ++ interfaceName) - - if (interface.exists) Some { - SpecializationTarget(interface, args, ret, sym) - } - else None - } - .flatten.toList + @inline private def specInterface(typeParams: List[Type])(implicit ctx: Context) = { + val specName = + ("JFunction".toTermName ++ (typeParams.length - 1)) + .specializedFor(typeParams, typeParams.map(_.typeSymbol.name), Nil, Nil) + + ctx.getClassIfDefined("scala.compat.java8.".toTermName ++ specName) } } diff --git a/compiler/test/dotty/tools/dotc/transform/SpecializeFunctionsTests.scala b/compiler/test/dotty/tools/dotc/transform/SpecializeFunctionsTests.scala index ca611787aff9..5c425ed8162d 100644 --- a/compiler/test/dotty/tools/dotc/transform/SpecializeFunctionsTests.scala +++ b/compiler/test/dotty/tools/dotc/transform/SpecializeFunctionsTests.scala @@ -90,4 +90,34 @@ class SpecializeFunctionsTests extends DottyBytecodeTest { .getOrElse(assert(false, "Could not find constructor for object `Test`")) } } + + @Test def specializeFunction2 = { + val source = + """|class Func2 extends Function2[Int, Int, Int] { + | def apply(i: Int, j: Int): Int = i + j + |}""".stripMargin + + checkBCode(source) { dir => + import scala.collection.JavaConverters._ + val clsIn = dir.lookupName("Func2.class", directory = false).input + val clsNode = loadClassNode(clsIn) + assert(clsNode.name == "Func2", s"inspecting wrong class: ${clsNode.name}") + val methods = clsNode.methods.asScala + + val apps = methods + .collect { + case m if m.name == "apply$mcIII$sp" => m + case m if m.name == "apply" => m + } + .map(_.name) + .toList + + assert( + apps.length == 3, + s"Wrong number of specialized applys, actual length: ${apps.length} - $apps" + ) + assert(apps.contains("apply"), "Func3 did not contain `apply` forwarder method") + assert(apps.contains("apply$mcIII$sp"), "Func3 did not contain specialized apply") + } + } } From 29986fc1c55cf5194aba712f75e38cf2f1926526 Mon Sep 17 00:00:00 2001 From: Felix Mulder Date: Tue, 21 Feb 2017 14:27:14 +0100 Subject: [PATCH 15/32] Add `MiniPhaseTransform` to add specialized methods to FunctionN --- compiler/src/dotty/tools/dotc/Compiler.scala | 1 + .../transform/SpecializedApplyMethods.scala | 140 ++++++++++++++++ .../tools/backend/jvm/DottyBytecodeTest.scala | 47 ++++++ .../transform/SpecializeFunctionsTests.scala | 149 ++++++++++-------- 4 files changed, 272 insertions(+), 65 deletions(-) create mode 100644 compiler/src/dotty/tools/dotc/transform/SpecializedApplyMethods.scala diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index 1bd239b415a3..fe3636bf772e 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -60,6 +60,7 @@ class Compiler { new LiftTry, // Put try expressions that might execute on non-empty stacks into their own methods new HoistSuperArgs, // Hoist complex arguments of supercalls to enclosing scope new ClassOf, // Expand `Predef.classOf` calls. + new SpecializedApplyMethods, // Adds specialized methods to FunctionN new RefChecks), // Various checks mostly related to abstract members and overriding List(new TryCatchPatterns, // Compile cases in try/catch new PatternMatcher, // Compile pattern matches diff --git a/compiler/src/dotty/tools/dotc/transform/SpecializedApplyMethods.scala b/compiler/src/dotty/tools/dotc/transform/SpecializedApplyMethods.scala new file mode 100644 index 000000000000..d71fcb174861 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/SpecializedApplyMethods.scala @@ -0,0 +1,140 @@ +package dotty.tools.dotc +package transform + +import TreeTransforms.{ MiniPhaseTransform, TransformerInfo } +import ast.Trees._, ast.tpd, core._ +import Contexts.Context, Types._, Decorators._, Symbols._, DenotTransformers._ +import SymDenotations._, Scopes._, StdNames._, NameOps._, Names._ + +/** This phase synthesizes specialized methods for FunctionN, this is done + * since there are no scala signatures in the bytecode for the specialized + * methods. + * + * We know which specializations exist for the different arities, therefore we + * can hardcode them. This should, however be removed once we're using a + * different standard library. + */ +class SpecializedApplyMethods extends MiniPhaseTransform with InfoTransformer { + import ast.tpd._ + + val phaseName = "specializedApplyMethods" + + def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context) = tp match { + case tp: ClassInfo if defn.isFunctionClass(sym) => { + def specApply(ret: Type, args: List[Type])(implicit ctx: Context) = { + val all = args :+ ret + val name = nme.apply.specializedFor(all, all.map(_.typeSymbol.name), Nil, Nil) + ctx.newSymbol(sym, name, Flags.Method, MethodType(args, ret)) + } + + val newDecls = sym.name.functionArity match { + case 0 => + List( + specApply(defn.UnitType, Nil), + specApply(defn.ByteType, Nil), + specApply(defn.ShortType, Nil), + specApply(defn.IntType, Nil), + specApply(defn.LongType, Nil), + specApply(defn.CharType, Nil), + specApply(defn.FloatType, Nil), + specApply(defn.DoubleType, Nil), + specApply(defn.BooleanType, Nil) + ) + .foldLeft(tp.decls.cloneScope){ (decls, sym) => decls.enter(sym); decls } + + case 1 => + List( + specApply(defn.UnitType, List(defn.IntType)), + specApply(defn.IntType, List(defn.IntType)), + specApply(defn.FloatType, List(defn.IntType)), + specApply(defn.LongType, List(defn.IntType)), + specApply(defn.DoubleType, List(defn.IntType)), + specApply(defn.UnitType, List(defn.LongType)), + specApply(defn.BooleanType, List(defn.LongType)), + specApply(defn.IntType, List(defn.LongType)), + specApply(defn.FloatType, List(defn.LongType)), + specApply(defn.LongType, List(defn.LongType)), + specApply(defn.DoubleType, List(defn.LongType)), + specApply(defn.UnitType, List(defn.FloatType)), + specApply(defn.BooleanType, List(defn.FloatType)), + specApply(defn.IntType, List(defn.FloatType)), + specApply(defn.FloatType, List(defn.FloatType)), + specApply(defn.LongType, List(defn.FloatType)), + specApply(defn.DoubleType, List(defn.FloatType)), + specApply(defn.UnitType, List(defn.DoubleType)), + specApply(defn.BooleanType, List(defn.DoubleType)), + specApply(defn.IntType, List(defn.DoubleType)), + specApply(defn.FloatType, List(defn.DoubleType)), + specApply(defn.LongType, List(defn.DoubleType)), + specApply(defn.DoubleType, List(defn.DoubleType)) + ) + .foldLeft(tp.decls.cloneScope){ (decls, sym) => decls.enter(sym); decls } + + case 2 => + List( + specApply(defn.UnitType, List(defn.IntType, defn.IntType)), + specApply(defn.BooleanType, List(defn.IntType, defn.IntType)), + specApply(defn.IntType, List(defn.IntType, defn.IntType)), + specApply(defn.FloatType, List(defn.IntType, defn.IntType)), + specApply(defn.LongType, List(defn.IntType, defn.IntType)), + specApply(defn.DoubleType, List(defn.IntType, defn.IntType)), + specApply(defn.UnitType, List(defn.IntType, defn.LongType)), + specApply(defn.BooleanType, List(defn.IntType, defn.LongType)), + specApply(defn.IntType, List(defn.IntType, defn.LongType)), + specApply(defn.FloatType, List(defn.IntType, defn.LongType)), + specApply(defn.LongType, List(defn.IntType, defn.LongType)), + specApply(defn.DoubleType, List(defn.IntType, defn.LongType)), + specApply(defn.UnitType, List(defn.IntType, defn.DoubleType)), + specApply(defn.BooleanType, List(defn.IntType, defn.DoubleType)), + specApply(defn.IntType, List(defn.IntType, defn.DoubleType)), + specApply(defn.FloatType, List(defn.IntType, defn.DoubleType)), + specApply(defn.LongType, List(defn.IntType, defn.DoubleType)), + specApply(defn.DoubleType, List(defn.IntType, defn.DoubleType)), + specApply(defn.UnitType, List(defn.LongType, defn.IntType)), + specApply(defn.BooleanType, List(defn.LongType, defn.IntType)), + specApply(defn.IntType, List(defn.LongType, defn.IntType)), + specApply(defn.FloatType, List(defn.LongType, defn.IntType)), + specApply(defn.LongType, List(defn.LongType, defn.IntType)), + specApply(defn.DoubleType, List(defn.LongType, defn.IntType)), + specApply(defn.UnitType, List(defn.LongType, defn.LongType)), + specApply(defn.BooleanType, List(defn.LongType, defn.LongType)), + specApply(defn.IntType, List(defn.LongType, defn.LongType)), + specApply(defn.FloatType, List(defn.LongType, defn.LongType)), + specApply(defn.LongType, List(defn.LongType, defn.LongType)), + specApply(defn.DoubleType, List(defn.LongType, defn.LongType)), + specApply(defn.UnitType, List(defn.LongType, defn.DoubleType)), + specApply(defn.BooleanType, List(defn.LongType, defn.DoubleType)), + specApply(defn.IntType, List(defn.LongType, defn.DoubleType)), + specApply(defn.FloatType, List(defn.LongType, defn.DoubleType)), + specApply(defn.LongType, List(defn.LongType, defn.DoubleType)), + specApply(defn.DoubleType, List(defn.LongType, defn.DoubleType)), + specApply(defn.UnitType, List(defn.DoubleType, defn.IntType)), + specApply(defn.BooleanType, List(defn.DoubleType, defn.IntType)), + specApply(defn.IntType, List(defn.DoubleType, defn.IntType)), + specApply(defn.FloatType, List(defn.DoubleType, defn.IntType)), + specApply(defn.LongType, List(defn.DoubleType, defn.IntType)), + specApply(defn.DoubleType, List(defn.DoubleType, defn.IntType)), + specApply(defn.UnitType, List(defn.DoubleType, defn.LongType)), + specApply(defn.BooleanType, List(defn.DoubleType, defn.LongType)), + specApply(defn.IntType, List(defn.DoubleType, defn.LongType)), + specApply(defn.FloatType, List(defn.DoubleType, defn.LongType)), + specApply(defn.LongType, List(defn.DoubleType, defn.LongType)), + specApply(defn.DoubleType, List(defn.DoubleType, defn.LongType)), + specApply(defn.UnitType, List(defn.DoubleType, defn.DoubleType)), + specApply(defn.BooleanType, List(defn.DoubleType, defn.DoubleType)), + specApply(defn.IntType, List(defn.DoubleType, defn.DoubleType)), + specApply(defn.FloatType, List(defn.DoubleType, defn.DoubleType)), + specApply(defn.LongType, List(defn.DoubleType, defn.DoubleType)), + specApply(defn.DoubleType, List(defn.DoubleType, defn.DoubleType)) + ) + .foldLeft(tp.decls.cloneScope){ (decls, sym) => decls.enter(sym); decls } + + case _ => + tp.decls + } + + tp.derivedClassInfo(decls = newDecls) + } + case _ => tp + } +} diff --git a/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTest.scala b/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTest.scala index 4542db0d54a0..5923f1c7695c 100644 --- a/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTest.scala +++ b/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTest.scala @@ -17,6 +17,8 @@ import scala.tools.asm.{ClassWriter, ClassReader} import scala.tools.asm.tree._ import java.io.{File => JFile, InputStream} +import org.junit.Assert._ + class TestGenBCode(val outDir: String) extends GenBCode { override def phaseName: String = "testGenBCode" val virtualDir = new Directory(outDir, None) @@ -87,6 +89,14 @@ trait DottyBytecodeTest extends DottyTest { cn } + /** Finds a class with `cls` as name in `dir`, throws if it can't find it */ + def findClass(cls: String, dir: Directory) = { + val clsIn = dir.lookupName(s"$cls.class", directory = false).input + val clsNode = loadClassNode(clsIn) + assert(clsNode.name == cls, s"inspecting wrong class: ${clsNode.name}") + clsNode + } + protected def getMethod(classNode: ClassNode, name: String): MethodNode = classNode.methods.asScala.find(_.name == name) getOrElse sys.error(s"Didn't find method '$name' in class '${classNode.name}'") @@ -203,4 +213,41 @@ trait DottyBytecodeTest extends DottyTest { s"Wrong number of null checks ($actualChecks), expected: $expectedChecks" ) } + + def assertBoxing(nodeName: String, methods: java.lang.Iterable[MethodNode])(implicit source: String): Unit = + methods.asScala.find(_.name == nodeName) + .map { node => + val (ins, boxed) = boxingInstructions(node) + if (!boxed) fail("No boxing in:\n" + boxingError(ins, source)) + } + .getOrElse(fail("Could not find constructor for object `Test`")) + + private def boxingError(ins: List[_], source: String) = + s"""|---------------------------------- + |${ins.mkString("\n")} + |---------------------------------- + |From code: + |$source + |----------------------------------""".stripMargin + + + protected def assertNoBoxing(nodeName: String, methods: java.lang.Iterable[MethodNode])(implicit source: String): Unit = + methods.asScala.find(_.name == nodeName) + .map { node => + val (ins, boxed) = boxingInstructions(node) + if (boxed) fail(boxingError(ins, source)) + } + .getOrElse(fail("Could not find constructor for object `Test`")) + + protected def boxingInstructions(method: MethodNode): (List[_], Boolean) = { + val ins = instructionsFromMethod(method) + val boxed = ins.exists { + case Invoke(op, owner, name, desc, itf) => + owner.toLowerCase.contains("box") || name.toLowerCase.contains("box") + case _ => false + } + + (ins, boxed) + } + } diff --git a/compiler/test/dotty/tools/dotc/transform/SpecializeFunctionsTests.scala b/compiler/test/dotty/tools/dotc/transform/SpecializeFunctionsTests.scala index 5c425ed8162d..9605c8fbfdd7 100644 --- a/compiler/test/dotty/tools/dotc/transform/SpecializeFunctionsTests.scala +++ b/compiler/test/dotty/tools/dotc/transform/SpecializeFunctionsTests.scala @@ -11,21 +11,8 @@ class SpecializeFunctionsTests extends DottyBytecodeTest { import dotty.tools.backend.jvm.ASMConverters._ import dotty.tools.backend.jvm.AsmNode._ - - protected def checkForBoxing(ins: List[Instruction], source: String): Unit = ins.foreach { - case Invoke(op, owner, name, desc, itf) => - def error = - s"""|---------------------------------- - |${ins.mkString("\n")} - |---------------------------------- - |From code: - |$source - |----------------------------------""".stripMargin - - assert(!owner.toLowerCase.contains("box"), s"Boxing instruction discovered in:\n$error") - assert(!name.toLowerCase.contains("box"), s"Boxing instruction discovered in:\n$error") - case _ => () - } + import scala.collection.JavaConverters._ + import scala.tools.asm.tree.MethodNode @Test def specializeParentIntToInt = { val source = """ @@ -35,14 +22,8 @@ class SpecializeFunctionsTests extends DottyBytecodeTest { """.stripMargin checkBCode(source) { dir => - import scala.collection.JavaConverters._ - val clsIn = dir.lookupName("Foo.class", directory = false).input - val clsNode = loadClassNode(clsIn) - assert(clsNode.name == "Foo", s"inspecting wrong class: ${clsNode.name}") - val methods = clsNode.methods.asScala - - val applys = methods - .collect { + val applys = + findClass("Foo", dir).methods.asScala.collect { case m if m.name == "apply$mcII$sp" => m case m if m.name == "apply" => m } @@ -55,57 +36,20 @@ class SpecializeFunctionsTests extends DottyBytecodeTest { applys.length == 3, s"Wrong number of specialized applys, actual length: ${applys.length} $applys" ) - assert( - applys.contains("apply"), - "Foo did not contain `apply` forwarder method" - ) - assert( - applys.contains("apply$mcII$sp"), - "Foo did not contain specialized apply" - ) + assert(applys.contains("apply"), "Foo did not contain `apply` forwarder method") + assert(applys.contains("apply$mcII$sp"), "Foo did not contain specialized apply") } } - @Test def checkBoxingIntToInt = { - val source = - """|object Test { - | class Func1 extends Function1[Int, Int] { - | def apply(i: Int) = i + 1 - | } - | - | (new Func1)(1) - |}""".stripMargin - - checkBCode(source) { dir => - import scala.collection.JavaConverters._ - val clsIn = dir.lookupName("Test$.class", directory = false).input - val clsNode = loadClassNode(clsIn) - assert(clsNode.name == "Test$", s"inspecting wrong class: ${clsNode.name}") - - clsNode.methods.asScala - .find(_.name == "") - .map { m => - checkForBoxing(instructionsFromMethod(m), source) - } - .getOrElse(assert(false, "Could not find constructor for object `Test`")) - } - } - - @Test def specializeFunction2 = { + @Test def specializeFunction2Applys = { val source = """|class Func2 extends Function2[Int, Int, Int] { | def apply(i: Int, j: Int): Int = i + j |}""".stripMargin checkBCode(source) { dir => - import scala.collection.JavaConverters._ - val clsIn = dir.lookupName("Func2.class", directory = false).input - val clsNode = loadClassNode(clsIn) - assert(clsNode.name == "Func2", s"inspecting wrong class: ${clsNode.name}") - val methods = clsNode.methods.asScala - - val apps = methods - .collect { + val apps = + findClass("Func2", dir).methods.asScala.collect { case m if m.name == "apply$mcIII$sp" => m case m if m.name == "apply" => m } @@ -120,4 +64,79 @@ class SpecializeFunctionsTests extends DottyBytecodeTest { assert(apps.contains("apply$mcIII$sp"), "Func3 did not contain specialized apply") } } + + @Test def noBoxingSpecFunction0 = { + implicit val source: String = + """|object Test { + | class Func0 extends Function0[Int] { + | def apply() = 1337 + | } + | + | (new Func0: Function0[Int])() + |}""".stripMargin + + checkBCode(source) { dir => + assertNoBoxing("", findClass("Test$", dir).methods) + } + } + + @Test def boxingFunction1 = { + implicit val source: String = + """|object Test { + | class Func1 extends Function1[Char, Int] { + | def apply(c: Char) = c.toInt + | } + | + | (new Func1: Function1[Char, Int])('c') + |}""".stripMargin + + checkBCode(source) { dir => + assertBoxing("", findClass("Test$", dir).methods) + } + } + + @Test def noBoxingSpecFunction1 = { + implicit val source: String = + """|object Test { + | class Func1 extends Function1[Int, Int] { + | def apply(i: Int) = i + 1 + | } + | + | (new Func1: Function1[Int, Int])(1) + |}""".stripMargin + + checkBCode(source) { dir => + assertNoBoxing("", findClass("Test$", dir).methods) + } + } + + @Test def noBoxingSpecFunction2 = { + implicit val source: String = + """|object Test { + | class Func2 extends Function2[Int, Int, Int] { + | def apply(i: Int, j: Int) = i + j + | } + | + | (new Func2: Function2[Int, Int, Int])(1300, 37) + |}""".stripMargin + + checkBCode(source) { dir => + assertNoBoxing("", findClass("Test$", dir).methods) + } + } + + @Test def boxingFunction2 = { + implicit val source: String = + """|object Test { + | class Func2 extends Function2[Char, Char, Char] { + | def apply(c1: Char, c2: Char) = c1 + | } + | + | (new Func2: Function2[Char, Char, Char])('c', 'd') + |}""".stripMargin + + checkBCode(source) { dir => + assertBoxing("", findClass("Test$", dir).methods) + } + } } From 4e845a4105cd1b905f794c6b1bdac0f460f96ec8 Mon Sep 17 00:00:00 2001 From: Felix Mulder Date: Tue, 21 Feb 2017 16:02:17 +0100 Subject: [PATCH 16/32] Add synthetic bridge when compiling FunctionN --- .../transform/SpecializedApplyMethods.scala | 257 ++++++++++-------- 1 file changed, 150 insertions(+), 107 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/SpecializedApplyMethods.scala b/compiler/src/dotty/tools/dotc/transform/SpecializedApplyMethods.scala index d71fcb174861..b51ccfc8b4d7 100644 --- a/compiler/src/dotty/tools/dotc/transform/SpecializedApplyMethods.scala +++ b/compiler/src/dotty/tools/dotc/transform/SpecializedApplyMethods.scala @@ -6,6 +6,8 @@ import ast.Trees._, ast.tpd, core._ import Contexts.Context, Types._, Decorators._, Symbols._, DenotTransformers._ import SymDenotations._, Scopes._, StdNames._, NameOps._, Names._ +import scala.reflect.internal.util.Collections + /** This phase synthesizes specialized methods for FunctionN, this is done * since there are no scala signatures in the bytecode for the specialized * methods. @@ -19,122 +21,163 @@ class SpecializedApplyMethods extends MiniPhaseTransform with InfoTransformer { val phaseName = "specializedApplyMethods" + private[this] var func0Applys: List[Symbol] = _ + private[this] var func1Applys: List[Symbol] = _ + private[this] var func2Applys: List[Symbol] = _ + private[this] var func0: Symbol = _ + private[this] var func1: Symbol = _ + private[this] var func2: Symbol = _ + + private def init()(implicit ctx: Context): Unit = if (func0Applys eq null) { + def specApply(sym: Symbol, ret: Type, args: List[Type])(implicit ctx: Context) = { + val all = args :+ ret + val name = nme.apply.specializedFor(all, all.map(_.typeSymbol.name), Nil, Nil) + ctx.newSymbol(sym, name, Flags.Method, MethodType(args, ret)) + } + + func0 = defn.FunctionClass(0) + func0Applys = List( + specApply(func0, defn.UnitType, Nil), + specApply(func0, defn.ByteType, Nil), + specApply(func0, defn.ShortType, Nil), + specApply(func0, defn.IntType, Nil), + specApply(func0, defn.LongType, Nil), + specApply(func0, defn.CharType, Nil), + specApply(func0, defn.FloatType, Nil), + specApply(func0, defn.DoubleType, Nil), + specApply(func0, defn.BooleanType, Nil) + ) + func1 = defn.FunctionClass(1) + func1Applys = List( + specApply(func1, defn.UnitType, List(defn.IntType)), + specApply(func1, defn.IntType, List(defn.IntType)), + specApply(func1, defn.FloatType, List(defn.IntType)), + specApply(func1, defn.LongType, List(defn.IntType)), + specApply(func1, defn.DoubleType, List(defn.IntType)), + specApply(func1, defn.UnitType, List(defn.LongType)), + specApply(func1, defn.BooleanType, List(defn.LongType)), + specApply(func1, defn.IntType, List(defn.LongType)), + specApply(func1, defn.FloatType, List(defn.LongType)), + specApply(func1, defn.LongType, List(defn.LongType)), + specApply(func1, defn.DoubleType, List(defn.LongType)), + specApply(func1, defn.UnitType, List(defn.FloatType)), + specApply(func1, defn.BooleanType, List(defn.FloatType)), + specApply(func1, defn.IntType, List(defn.FloatType)), + specApply(func1, defn.FloatType, List(defn.FloatType)), + specApply(func1, defn.LongType, List(defn.FloatType)), + specApply(func1, defn.DoubleType, List(defn.FloatType)), + specApply(func1, defn.UnitType, List(defn.DoubleType)), + specApply(func1, defn.BooleanType, List(defn.DoubleType)), + specApply(func1, defn.IntType, List(defn.DoubleType)), + specApply(func1, defn.FloatType, List(defn.DoubleType)), + specApply(func1, defn.LongType, List(defn.DoubleType)), + specApply(func1, defn.DoubleType, List(defn.DoubleType)) + ) + func2 = defn.FunctionClass(2) + func2Applys = List( + specApply(func2, defn.UnitType, List(defn.IntType, defn.IntType)), + specApply(func2, defn.BooleanType, List(defn.IntType, defn.IntType)), + specApply(func2, defn.IntType, List(defn.IntType, defn.IntType)), + specApply(func2, defn.FloatType, List(defn.IntType, defn.IntType)), + specApply(func2, defn.LongType, List(defn.IntType, defn.IntType)), + specApply(func2, defn.DoubleType, List(defn.IntType, defn.IntType)), + specApply(func2, defn.UnitType, List(defn.IntType, defn.LongType)), + specApply(func2, defn.BooleanType, List(defn.IntType, defn.LongType)), + specApply(func2, defn.IntType, List(defn.IntType, defn.LongType)), + specApply(func2, defn.FloatType, List(defn.IntType, defn.LongType)), + specApply(func2, defn.LongType, List(defn.IntType, defn.LongType)), + specApply(func2, defn.DoubleType, List(defn.IntType, defn.LongType)), + specApply(func2, defn.UnitType, List(defn.IntType, defn.DoubleType)), + specApply(func2, defn.BooleanType, List(defn.IntType, defn.DoubleType)), + specApply(func2, defn.IntType, List(defn.IntType, defn.DoubleType)), + specApply(func2, defn.FloatType, List(defn.IntType, defn.DoubleType)), + specApply(func2, defn.LongType, List(defn.IntType, defn.DoubleType)), + specApply(func2, defn.DoubleType, List(defn.IntType, defn.DoubleType)), + specApply(func2, defn.UnitType, List(defn.LongType, defn.IntType)), + specApply(func2, defn.BooleanType, List(defn.LongType, defn.IntType)), + specApply(func2, defn.IntType, List(defn.LongType, defn.IntType)), + specApply(func2, defn.FloatType, List(defn.LongType, defn.IntType)), + specApply(func2, defn.LongType, List(defn.LongType, defn.IntType)), + specApply(func2, defn.DoubleType, List(defn.LongType, defn.IntType)), + specApply(func2, defn.UnitType, List(defn.LongType, defn.LongType)), + specApply(func2, defn.BooleanType, List(defn.LongType, defn.LongType)), + specApply(func2, defn.IntType, List(defn.LongType, defn.LongType)), + specApply(func2, defn.FloatType, List(defn.LongType, defn.LongType)), + specApply(func2, defn.LongType, List(defn.LongType, defn.LongType)), + specApply(func2, defn.DoubleType, List(defn.LongType, defn.LongType)), + specApply(func2, defn.UnitType, List(defn.LongType, defn.DoubleType)), + specApply(func2, defn.BooleanType, List(defn.LongType, defn.DoubleType)), + specApply(func2, defn.IntType, List(defn.LongType, defn.DoubleType)), + specApply(func2, defn.FloatType, List(defn.LongType, defn.DoubleType)), + specApply(func2, defn.LongType, List(defn.LongType, defn.DoubleType)), + specApply(func2, defn.DoubleType, List(defn.LongType, defn.DoubleType)), + specApply(func2, defn.UnitType, List(defn.DoubleType, defn.IntType)), + specApply(func2, defn.BooleanType, List(defn.DoubleType, defn.IntType)), + specApply(func2, defn.IntType, List(defn.DoubleType, defn.IntType)), + specApply(func2, defn.FloatType, List(defn.DoubleType, defn.IntType)), + specApply(func2, defn.LongType, List(defn.DoubleType, defn.IntType)), + specApply(func2, defn.DoubleType, List(defn.DoubleType, defn.IntType)), + specApply(func2, defn.UnitType, List(defn.DoubleType, defn.LongType)), + specApply(func2, defn.BooleanType, List(defn.DoubleType, defn.LongType)), + specApply(func2, defn.IntType, List(defn.DoubleType, defn.LongType)), + specApply(func2, defn.FloatType, List(defn.DoubleType, defn.LongType)), + specApply(func2, defn.LongType, List(defn.DoubleType, defn.LongType)), + specApply(func2, defn.DoubleType, List(defn.DoubleType, defn.LongType)), + specApply(func2, defn.UnitType, List(defn.DoubleType, defn.DoubleType)), + specApply(func2, defn.BooleanType, List(defn.DoubleType, defn.DoubleType)), + specApply(func2, defn.IntType, List(defn.DoubleType, defn.DoubleType)), + specApply(func2, defn.FloatType, List(defn.DoubleType, defn.DoubleType)), + specApply(func2, defn.LongType, List(defn.DoubleType, defn.DoubleType)), + specApply(func2, defn.DoubleType, List(defn.DoubleType, defn.DoubleType)) + ) + } + + /** Add symbols for specialized methods to FunctionN */ def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context) = tp match { case tp: ClassInfo if defn.isFunctionClass(sym) => { - def specApply(ret: Type, args: List[Type])(implicit ctx: Context) = { - val all = args :+ ret - val name = nme.apply.specializedFor(all, all.map(_.typeSymbol.name), Nil, Nil) - ctx.newSymbol(sym, name, Flags.Method, MethodType(args, ret)) + init() + val newDecls = sym.name.functionArity match { + case 0 => func0Applys.foldLeft(tp.decls.cloneScope) { + (decls, sym) => decls.enter(sym); decls + } + case 1 => func1Applys.foldLeft(tp.decls.cloneScope) { + (decls, sym) => decls.enter(sym); decls + } + case 2 => func2Applys.foldLeft(tp.decls.cloneScope) { + (decls, sym) => decls.enter(sym); decls + } + case _ => tp.decls } - val newDecls = sym.name.functionArity match { - case 0 => - List( - specApply(defn.UnitType, Nil), - specApply(defn.ByteType, Nil), - specApply(defn.ShortType, Nil), - specApply(defn.IntType, Nil), - specApply(defn.LongType, Nil), - specApply(defn.CharType, Nil), - specApply(defn.FloatType, Nil), - specApply(defn.DoubleType, Nil), - specApply(defn.BooleanType, Nil) - ) - .foldLeft(tp.decls.cloneScope){ (decls, sym) => decls.enter(sym); decls } + tp.derivedClassInfo(decls = newDecls) + } + case _ => tp + } - case 1 => - List( - specApply(defn.UnitType, List(defn.IntType)), - specApply(defn.IntType, List(defn.IntType)), - specApply(defn.FloatType, List(defn.IntType)), - specApply(defn.LongType, List(defn.IntType)), - specApply(defn.DoubleType, List(defn.IntType)), - specApply(defn.UnitType, List(defn.LongType)), - specApply(defn.BooleanType, List(defn.LongType)), - specApply(defn.IntType, List(defn.LongType)), - specApply(defn.FloatType, List(defn.LongType)), - specApply(defn.LongType, List(defn.LongType)), - specApply(defn.DoubleType, List(defn.LongType)), - specApply(defn.UnitType, List(defn.FloatType)), - specApply(defn.BooleanType, List(defn.FloatType)), - specApply(defn.IntType, List(defn.FloatType)), - specApply(defn.FloatType, List(defn.FloatType)), - specApply(defn.LongType, List(defn.FloatType)), - specApply(defn.DoubleType, List(defn.FloatType)), - specApply(defn.UnitType, List(defn.DoubleType)), - specApply(defn.BooleanType, List(defn.DoubleType)), - specApply(defn.IntType, List(defn.DoubleType)), - specApply(defn.FloatType, List(defn.DoubleType)), - specApply(defn.LongType, List(defn.DoubleType)), - specApply(defn.DoubleType, List(defn.DoubleType)) - ) - .foldLeft(tp.decls.cloneScope){ (decls, sym) => decls.enter(sym); decls } + /** Create bridge methods for FunctionN with specialized applys */ + override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo) = { + val owner = tree.symbol.owner + val additionalSymbols = + if (owner eq func0) func0Applys + else if (owner eq func1) func1Applys + else if (owner eq func2) func2Applys + else Nil - case 2 => - List( - specApply(defn.UnitType, List(defn.IntType, defn.IntType)), - specApply(defn.BooleanType, List(defn.IntType, defn.IntType)), - specApply(defn.IntType, List(defn.IntType, defn.IntType)), - specApply(defn.FloatType, List(defn.IntType, defn.IntType)), - specApply(defn.LongType, List(defn.IntType, defn.IntType)), - specApply(defn.DoubleType, List(defn.IntType, defn.IntType)), - specApply(defn.UnitType, List(defn.IntType, defn.LongType)), - specApply(defn.BooleanType, List(defn.IntType, defn.LongType)), - specApply(defn.IntType, List(defn.IntType, defn.LongType)), - specApply(defn.FloatType, List(defn.IntType, defn.LongType)), - specApply(defn.LongType, List(defn.IntType, defn.LongType)), - specApply(defn.DoubleType, List(defn.IntType, defn.LongType)), - specApply(defn.UnitType, List(defn.IntType, defn.DoubleType)), - specApply(defn.BooleanType, List(defn.IntType, defn.DoubleType)), - specApply(defn.IntType, List(defn.IntType, defn.DoubleType)), - specApply(defn.FloatType, List(defn.IntType, defn.DoubleType)), - specApply(defn.LongType, List(defn.IntType, defn.DoubleType)), - specApply(defn.DoubleType, List(defn.IntType, defn.DoubleType)), - specApply(defn.UnitType, List(defn.LongType, defn.IntType)), - specApply(defn.BooleanType, List(defn.LongType, defn.IntType)), - specApply(defn.IntType, List(defn.LongType, defn.IntType)), - specApply(defn.FloatType, List(defn.LongType, defn.IntType)), - specApply(defn.LongType, List(defn.LongType, defn.IntType)), - specApply(defn.DoubleType, List(defn.LongType, defn.IntType)), - specApply(defn.UnitType, List(defn.LongType, defn.LongType)), - specApply(defn.BooleanType, List(defn.LongType, defn.LongType)), - specApply(defn.IntType, List(defn.LongType, defn.LongType)), - specApply(defn.FloatType, List(defn.LongType, defn.LongType)), - specApply(defn.LongType, List(defn.LongType, defn.LongType)), - specApply(defn.DoubleType, List(defn.LongType, defn.LongType)), - specApply(defn.UnitType, List(defn.LongType, defn.DoubleType)), - specApply(defn.BooleanType, List(defn.LongType, defn.DoubleType)), - specApply(defn.IntType, List(defn.LongType, defn.DoubleType)), - specApply(defn.FloatType, List(defn.LongType, defn.DoubleType)), - specApply(defn.LongType, List(defn.LongType, defn.DoubleType)), - specApply(defn.DoubleType, List(defn.LongType, defn.DoubleType)), - specApply(defn.UnitType, List(defn.DoubleType, defn.IntType)), - specApply(defn.BooleanType, List(defn.DoubleType, defn.IntType)), - specApply(defn.IntType, List(defn.DoubleType, defn.IntType)), - specApply(defn.FloatType, List(defn.DoubleType, defn.IntType)), - specApply(defn.LongType, List(defn.DoubleType, defn.IntType)), - specApply(defn.DoubleType, List(defn.DoubleType, defn.IntType)), - specApply(defn.UnitType, List(defn.DoubleType, defn.LongType)), - specApply(defn.BooleanType, List(defn.DoubleType, defn.LongType)), - specApply(defn.IntType, List(defn.DoubleType, defn.LongType)), - specApply(defn.FloatType, List(defn.DoubleType, defn.LongType)), - specApply(defn.LongType, List(defn.DoubleType, defn.LongType)), - specApply(defn.DoubleType, List(defn.DoubleType, defn.LongType)), - specApply(defn.UnitType, List(defn.DoubleType, defn.DoubleType)), - specApply(defn.BooleanType, List(defn.DoubleType, defn.DoubleType)), - specApply(defn.IntType, List(defn.DoubleType, defn.DoubleType)), - specApply(defn.FloatType, List(defn.DoubleType, defn.DoubleType)), - specApply(defn.LongType, List(defn.DoubleType, defn.DoubleType)), - specApply(defn.DoubleType, List(defn.DoubleType, defn.DoubleType)) - ) - .foldLeft(tp.decls.cloneScope){ (decls, sym) => decls.enter(sym); decls } + if (additionalSymbols eq Nil) tree + else { + val newBody: List[Tree] = tree.body ++ additionalSymbols.map { applySym => + polyDefDef(applySym.asTerm, tparams => vparamss => { + val prefix = This(owner.asClass).select(nme.apply).appliedToTypes(vparamss.head.map(_.tpe)) + val argTypess = prefix.tpe.widen.paramTypess - case _ => - tp.decls + val argss = Collections.map2(vparamss, argTypess) { (vparams, argTypes) => + Collections.map2(vparams, argTypes) { (vparam, argType) => vparam.ensureConforms(argType) } + } + prefix.appliedToArgss(argss).ensureConforms(applySym.info.finalResultType) + }) } - tp.derivedClassInfo(decls = newDecls) + cpy.Template(tree)(body = newBody) } - case _ => tp } } From 3e32c22522677711c0a63ee37bacdf985fcd3bdf Mon Sep 17 00:00:00 2001 From: Felix Mulder Date: Tue, 21 Feb 2017 17:28:35 +0100 Subject: [PATCH 17/32] Fix ordering of specialized names and type parameterized apply --- .../dotty/tools/dotc/core/Definitions.scala | 6 +- .../src/dotty/tools/dotc/core/NameOps.scala | 35 ++++- .../transform/SpecializedApplyMethods.scala | 141 ++++-------------- 3 files changed, 61 insertions(+), 121 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 8706b8946e29..6e5ad3e66405 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -992,9 +992,9 @@ class Definitions { lazy val ScalaNumericValueTypeList = List( ByteType, ShortType, CharType, IntType, LongType, FloatType, DoubleType) - private lazy val ScalaNumericValueTypes: collection.Set[TypeRef] = ScalaNumericValueTypeList.toSet - private lazy val ScalaValueTypes: collection.Set[TypeRef] = ScalaNumericValueTypes + UnitType + BooleanType - private lazy val ScalaBoxedTypes = ScalaValueTypes map (t => boxedTypes(t.name)) + lazy val ScalaNumericValueTypes: collection.Set[TypeRef] = ScalaNumericValueTypeList.toSet + lazy val ScalaValueTypes: collection.Set[TypeRef] = ScalaNumericValueTypes + UnitType + BooleanType + lazy val ScalaBoxedTypes = ScalaValueTypes map (t => boxedTypes(t.name)) val ScalaNumericValueClasses = new PerRun[collection.Set[Symbol]](implicit ctx => ScalaNumericValueTypes.map(_.symbol)) val ScalaValueClasses = new PerRun[collection.Set[Symbol]](implicit ctx => ScalaValueTypes.map(_.symbol)) diff --git a/compiler/src/dotty/tools/dotc/core/NameOps.scala b/compiler/src/dotty/tools/dotc/core/NameOps.scala index ed439622215e..345891ae5c14 100644 --- a/compiler/src/dotty/tools/dotc/core/NameOps.scala +++ b/compiler/src/dotty/tools/dotc/core/NameOps.scala @@ -186,7 +186,7 @@ object NameOps { /** Is a function name * - FunctionN for N >= 0 */ - def isPlainFunction: Boolean = functionArityFor(tpnme.Function) >= 0 + def isPlainFunction: Boolean = functionArityFor(str.Function) >= 0 /** Is a implicit function name * - ImplicitFunctionN for N >= 1 @@ -231,16 +231,43 @@ object NameOps { case nme.clone_ => nme.clone_ } - def specializedFor(classTargs: List[Types.Type], classTargsNames: List[Name], methodTargs: List[Types.Type], methodTarsNames: List[Name])(implicit ctx: Context): name.ThisName = { + private def typeToTag(tp: Types.Type)(implicit ctx: Context): Name = + tp.classSymbol match { + case t if t eq defn.IntClass => nme.specializedTypeNames.Int + case t if t eq defn.BooleanClass => nme.specializedTypeNames.Boolean + case t if t eq defn.ByteClass => nme.specializedTypeNames.Byte + case t if t eq defn.LongClass => nme.specializedTypeNames.Long + case t if t eq defn.ShortClass => nme.specializedTypeNames.Short + case t if t eq defn.FloatClass => nme.specializedTypeNames.Float + case t if t eq defn.UnitClass => nme.specializedTypeNames.Void + case t if t eq defn.DoubleClass => nme.specializedTypeNames.Double + case t if t eq defn.CharClass => nme.specializedTypeNames.Char + case _ => nme.specializedTypeNames.Object + } - val methodTags: Seq[Name] = (methodTargs zip methodTarsNames).sortBy(_._2).map(x => defn.typeTag(x._1)) - val classTags: Seq[Name] = (classTargs zip classTargsNames).sortBy(_._2).map(x => defn.typeTag(x._1)) + /** This method is to be used on **type parameters** from a class, since + * this method does sorting based on their names + */ + def specializedFor(classTargs: List[Types.Type], classTargsNames: List[Name], methodTargs: List[Types.Type], methodTarsNames: List[Name])(implicit ctx: Context): name.ThisName = { + val methodTags: Seq[Name] = (methodTargs zip methodTarsNames).sortBy(_._2).map(x => typeToTag(x._1)) + val classTags: Seq[Name] = (classTargs zip classTargsNames).sortBy(_._2).map(x => typeToTag(x._1)) name.likeSpaced(name ++ nme.specializedTypeNames.prefix ++ methodTags.fold(nme.EMPTY)(_ ++ _) ++ nme.specializedTypeNames.separator ++ classTags.fold(nme.EMPTY)(_ ++ _) ++ nme.specializedTypeNames.suffix) } + /** Use for specializing function names ONLY and use it if you are **not** + * creating specialized name from type parameters. The order of names will + * be: + * + * `<...>` + */ + def specializedFunction(ret: Types.Type, args: List[Types.Type])(implicit ctx: Context): name.ThisName = + name ++ nme.specializedTypeNames.prefix ++ + nme.specializedTypeNames.separator ++ typeToTag(ret) ++ + args.map(typeToTag).fold(nme.EMPTY)(_ ++ _) ++ nme.specializedTypeNames.suffix + /** If name length exceeds allowable limit, replace part of it by hash */ def compactified(implicit ctx: Context): TermName = termName(compactify(name.toString)) diff --git a/compiler/src/dotty/tools/dotc/transform/SpecializedApplyMethods.scala b/compiler/src/dotty/tools/dotc/transform/SpecializedApplyMethods.scala index b51ccfc8b4d7..fdd8ead26caf 100644 --- a/compiler/src/dotty/tools/dotc/transform/SpecializedApplyMethods.scala +++ b/compiler/src/dotty/tools/dotc/transform/SpecializedApplyMethods.scala @@ -6,8 +6,6 @@ import ast.Trees._, ast.tpd, core._ import Contexts.Context, Types._, Decorators._, Symbols._, DenotTransformers._ import SymDenotations._, Scopes._, StdNames._, NameOps._, Names._ -import scala.reflect.internal.util.Collections - /** This phase synthesizes specialized methods for FunctionN, this is done * since there are no scala signatures in the bytecode for the specialized * methods. @@ -29,107 +27,29 @@ class SpecializedApplyMethods extends MiniPhaseTransform with InfoTransformer { private[this] var func2: Symbol = _ private def init()(implicit ctx: Context): Unit = if (func0Applys eq null) { - def specApply(sym: Symbol, ret: Type, args: List[Type])(implicit ctx: Context) = { - val all = args :+ ret - val name = nme.apply.specializedFor(all, all.map(_.typeSymbol.name), Nil, Nil) + val definitions = ctx.definitions + import definitions._ + + def specApply(sym: Symbol, args: List[Type], ret: Type)(implicit ctx: Context) = { + val name = nme.apply.specializedFunction(ret, args) ctx.newSymbol(sym, name, Flags.Method, MethodType(args, ret)) } - func0 = defn.FunctionClass(0) - func0Applys = List( - specApply(func0, defn.UnitType, Nil), - specApply(func0, defn.ByteType, Nil), - specApply(func0, defn.ShortType, Nil), - specApply(func0, defn.IntType, Nil), - specApply(func0, defn.LongType, Nil), - specApply(func0, defn.CharType, Nil), - specApply(func0, defn.FloatType, Nil), - specApply(func0, defn.DoubleType, Nil), - specApply(func0, defn.BooleanType, Nil) - ) - func1 = defn.FunctionClass(1) - func1Applys = List( - specApply(func1, defn.UnitType, List(defn.IntType)), - specApply(func1, defn.IntType, List(defn.IntType)), - specApply(func1, defn.FloatType, List(defn.IntType)), - specApply(func1, defn.LongType, List(defn.IntType)), - specApply(func1, defn.DoubleType, List(defn.IntType)), - specApply(func1, defn.UnitType, List(defn.LongType)), - specApply(func1, defn.BooleanType, List(defn.LongType)), - specApply(func1, defn.IntType, List(defn.LongType)), - specApply(func1, defn.FloatType, List(defn.LongType)), - specApply(func1, defn.LongType, List(defn.LongType)), - specApply(func1, defn.DoubleType, List(defn.LongType)), - specApply(func1, defn.UnitType, List(defn.FloatType)), - specApply(func1, defn.BooleanType, List(defn.FloatType)), - specApply(func1, defn.IntType, List(defn.FloatType)), - specApply(func1, defn.FloatType, List(defn.FloatType)), - specApply(func1, defn.LongType, List(defn.FloatType)), - specApply(func1, defn.DoubleType, List(defn.FloatType)), - specApply(func1, defn.UnitType, List(defn.DoubleType)), - specApply(func1, defn.BooleanType, List(defn.DoubleType)), - specApply(func1, defn.IntType, List(defn.DoubleType)), - specApply(func1, defn.FloatType, List(defn.DoubleType)), - specApply(func1, defn.LongType, List(defn.DoubleType)), - specApply(func1, defn.DoubleType, List(defn.DoubleType)) - ) + func0 = FunctionClass(0) + func0Applys = for (r <- ScalaValueTypes.toList) yield specApply(func0, Nil, r) + + func1 = FunctionClass(1) + func1Applys = for { + r <- List(UnitType, BooleanType, IntType, FloatType, LongType, DoubleType) + t1 <- List(IntType, LongType, FloatType, DoubleType) + } yield specApply(func1, List(t1), r) + func2 = defn.FunctionClass(2) - func2Applys = List( - specApply(func2, defn.UnitType, List(defn.IntType, defn.IntType)), - specApply(func2, defn.BooleanType, List(defn.IntType, defn.IntType)), - specApply(func2, defn.IntType, List(defn.IntType, defn.IntType)), - specApply(func2, defn.FloatType, List(defn.IntType, defn.IntType)), - specApply(func2, defn.LongType, List(defn.IntType, defn.IntType)), - specApply(func2, defn.DoubleType, List(defn.IntType, defn.IntType)), - specApply(func2, defn.UnitType, List(defn.IntType, defn.LongType)), - specApply(func2, defn.BooleanType, List(defn.IntType, defn.LongType)), - specApply(func2, defn.IntType, List(defn.IntType, defn.LongType)), - specApply(func2, defn.FloatType, List(defn.IntType, defn.LongType)), - specApply(func2, defn.LongType, List(defn.IntType, defn.LongType)), - specApply(func2, defn.DoubleType, List(defn.IntType, defn.LongType)), - specApply(func2, defn.UnitType, List(defn.IntType, defn.DoubleType)), - specApply(func2, defn.BooleanType, List(defn.IntType, defn.DoubleType)), - specApply(func2, defn.IntType, List(defn.IntType, defn.DoubleType)), - specApply(func2, defn.FloatType, List(defn.IntType, defn.DoubleType)), - specApply(func2, defn.LongType, List(defn.IntType, defn.DoubleType)), - specApply(func2, defn.DoubleType, List(defn.IntType, defn.DoubleType)), - specApply(func2, defn.UnitType, List(defn.LongType, defn.IntType)), - specApply(func2, defn.BooleanType, List(defn.LongType, defn.IntType)), - specApply(func2, defn.IntType, List(defn.LongType, defn.IntType)), - specApply(func2, defn.FloatType, List(defn.LongType, defn.IntType)), - specApply(func2, defn.LongType, List(defn.LongType, defn.IntType)), - specApply(func2, defn.DoubleType, List(defn.LongType, defn.IntType)), - specApply(func2, defn.UnitType, List(defn.LongType, defn.LongType)), - specApply(func2, defn.BooleanType, List(defn.LongType, defn.LongType)), - specApply(func2, defn.IntType, List(defn.LongType, defn.LongType)), - specApply(func2, defn.FloatType, List(defn.LongType, defn.LongType)), - specApply(func2, defn.LongType, List(defn.LongType, defn.LongType)), - specApply(func2, defn.DoubleType, List(defn.LongType, defn.LongType)), - specApply(func2, defn.UnitType, List(defn.LongType, defn.DoubleType)), - specApply(func2, defn.BooleanType, List(defn.LongType, defn.DoubleType)), - specApply(func2, defn.IntType, List(defn.LongType, defn.DoubleType)), - specApply(func2, defn.FloatType, List(defn.LongType, defn.DoubleType)), - specApply(func2, defn.LongType, List(defn.LongType, defn.DoubleType)), - specApply(func2, defn.DoubleType, List(defn.LongType, defn.DoubleType)), - specApply(func2, defn.UnitType, List(defn.DoubleType, defn.IntType)), - specApply(func2, defn.BooleanType, List(defn.DoubleType, defn.IntType)), - specApply(func2, defn.IntType, List(defn.DoubleType, defn.IntType)), - specApply(func2, defn.FloatType, List(defn.DoubleType, defn.IntType)), - specApply(func2, defn.LongType, List(defn.DoubleType, defn.IntType)), - specApply(func2, defn.DoubleType, List(defn.DoubleType, defn.IntType)), - specApply(func2, defn.UnitType, List(defn.DoubleType, defn.LongType)), - specApply(func2, defn.BooleanType, List(defn.DoubleType, defn.LongType)), - specApply(func2, defn.IntType, List(defn.DoubleType, defn.LongType)), - specApply(func2, defn.FloatType, List(defn.DoubleType, defn.LongType)), - specApply(func2, defn.LongType, List(defn.DoubleType, defn.LongType)), - specApply(func2, defn.DoubleType, List(defn.DoubleType, defn.LongType)), - specApply(func2, defn.UnitType, List(defn.DoubleType, defn.DoubleType)), - specApply(func2, defn.BooleanType, List(defn.DoubleType, defn.DoubleType)), - specApply(func2, defn.IntType, List(defn.DoubleType, defn.DoubleType)), - specApply(func2, defn.FloatType, List(defn.DoubleType, defn.DoubleType)), - specApply(func2, defn.LongType, List(defn.DoubleType, defn.DoubleType)), - specApply(func2, defn.DoubleType, List(defn.DoubleType, defn.DoubleType)) - ) + func2Applys = for { + r <- List(UnitType, BooleanType, IntType, FloatType, LongType, DoubleType) + t1 <- List(IntType, LongType, DoubleType) + t2 <- List(IntType, LongType, DoubleType) + } yield specApply(func2, List(t1, t2), r) } /** Add symbols for specialized methods to FunctionN */ @@ -164,20 +84,13 @@ class SpecializedApplyMethods extends MiniPhaseTransform with InfoTransformer { else Nil if (additionalSymbols eq Nil) tree - else { - val newBody: List[Tree] = tree.body ++ additionalSymbols.map { applySym => - polyDefDef(applySym.asTerm, tparams => vparamss => { - val prefix = This(owner.asClass).select(nme.apply).appliedToTypes(vparamss.head.map(_.tpe)) - val argTypess = prefix.tpe.widen.paramTypess - - val argss = Collections.map2(vparamss, argTypess) { (vparams, argTypes) => - Collections.map2(vparams, argTypes) { (vparam, argType) => vparam.ensureConforms(argType) } - } - prefix.appliedToArgss(argss).ensureConforms(applySym.info.finalResultType) - }) - } - - cpy.Template(tree)(body = newBody) - } + else cpy.Template(tree)(body = tree.body ++ additionalSymbols.map { apply => + DefDef(apply.asTerm, { vparamss => + This(owner.asClass) + .select(nme.apply) + .appliedToArgss(vparamss) + .ensureConforms(apply.info.finalResultType) + }) + }) } } From d4f83d0f1672d1f7218fd6ffeb1929dad57adfc1 Mon Sep 17 00:00:00 2001 From: Felix Mulder Date: Wed, 22 Feb 2017 18:26:56 +0100 Subject: [PATCH 18/32] Add parent types explicitly when specializing When a class directly extends a specialized function class, we need to replace the parent with the specialized interface. In other cases we don't replace it, even if the parent of a parent has a specialized apply - the symbols would propagate anyway. --- .../dotc/transform/SpecializeFunctions.scala | 129 ++++++++++++------ .../transform/SpecializedApplyMethods.scala | 4 +- tests/run/t2857.scala | 2 - 3 files changed, 89 insertions(+), 46 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala b/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala index 106d2767e1bb..381c2777e870 100644 --- a/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala +++ b/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala @@ -15,43 +15,65 @@ class SpecializeFunctions extends MiniPhaseTransform with InfoTransformer { import ast.tpd._ val phaseName = "specializeFunctions" + private[this] var _blacklistedSymbols: List[Symbol] = _ + + private def blacklistedSymbols(implicit ctx: Context): List[Symbol] = { + if (_blacklistedSymbols eq null) _blacklistedSymbols = List( + ctx.getClassIfDefined("scala.math.Ordering").asClass.membersNamed("Ops".toTypeName).first.symbol + ) + + _blacklistedSymbols + } + /** Transforms the type to include decls for specialized applys and replace * the class parents with specialized versions. */ def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context) = tp match { - case tp: ClassInfo if !sym.is(Flags.Package) => { + case tp: ClassInfo + if !sym.is(Flags.Package) && + !blacklistedSymbols.contains(sym) && + (tp.decls ne EmptyScope) + => { var newApplys: List[Symbol] = Nil val newParents = tp.parents.mapConserve { parent => - if (defn.isPlainFunctionClass(parent.symbol)) { - val typeParams = tp.typeRef.baseArgTypes(parent.classSymbol) - val interface = specInterface(typeParams) - - if (interface.exists) { - val specializedApply: Symbol = { - val specializedMethodName = specializedName(nme.apply, typeParams) - ctx.newSymbol( - sym, - specializedMethodName, - Flags.Override | Flags.Method, - interface.info.decls.lookup(specializedMethodName).info - ) + List(0, 1, 2, 3).flatMap { arity => + val func = defn.FunctionClass(arity) + if (!parent.isRef(func)) Nil + else { + val typeParams = tp.typeRef.baseArgInfos(func) + val interface = specInterface(typeParams) + + if (interface.exists) { + val specializedApply = { + val specializedMethodName = specializedName(nme.apply, typeParams) + ctx.newSymbol( + sym, + specializedMethodName, + Flags.Override | Flags.Method, + interface.info.decls.lookup(specializedMethodName).info + ) + } + newApplys = specializedApply :: newApplys + List(interface.typeRef) } - - newApplys = specializedApply :: newApplys - interface.typeRef + else Nil } - else parent } - else parent + .headOption + .getOrElse(parent) } - def newDecls = newApplys.foldLeft(tp.decls.cloneScope) { - (scope, sym) => scope.enter(sym); scope - } + def newDecls = + if (newApplys.isEmpty) tp.decls + else newApplys.foldLeft(tp.decls.cloneScope) { + (scope, sym) => scope.enter(sym); scope + } - if (newApplys eq Nil) tp - else tp.derivedClassInfo(classParents = newParents, decls = newDecls) + tp.derivedClassInfo( + classParents = newParents, + decls = newDecls + ) } case _ => tp @@ -63,10 +85,10 @@ class SpecializeFunctions extends MiniPhaseTransform with InfoTransformer { * template body. */ override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo) = { - val buf = new mutable.ListBuffer[Tree] + val applyBuf = new mutable.ListBuffer[Tree] val newBody = tree.body.mapConserve { case dt: DefDef if dt.name == nme.apply && dt.vparamss.length == 1 => { - val specializedApply = ctx.owner.info.decls.lookup { + val specializedApply = tree.symbol.enclosingClass.info.decls.lookup { specializedName( nme.apply, dt.vparamss.head.map(_.symbol.info) :+ dt.tpe.widen.finalResultType @@ -82,7 +104,7 @@ class SpecializeFunctions extends MiniPhaseTransform with InfoTransformer { .subst(dt.vparamss.flatten.map(_.symbol), vrefss.flatten.map(_.symbol)) }) - buf += specializedDecl + applyBuf += specializedDecl // create a forwarding to the specialized apply cpy.DefDef(dt)(rhs = { @@ -95,29 +117,52 @@ class SpecializeFunctions extends MiniPhaseTransform with InfoTransformer { case x => x } - val newParents = tree.parents.mapConserve { parent => - if (defn.isPlainFunctionClass(parent.symbol)) { - val typeParams = tree.tpe.baseArgTypes(parent.symbol) - val interface = specInterface(typeParams) + val missing: List[TypeTree] = List(0, 1, 2, 3).flatMap { arity => + val func = defn.FunctionClass(arity) + val tr = tree.symbol.enclosingClass.typeRef - if (interface.exists) TypeTree(interface.info) - else parent - } - else parent - } + if (!tr.parents.exists(_.isRef(func))) Nil + else { + val typeParams = tr.baseArgInfos(func) + val interface = specInterface(typeParams) + + if (interface.exists) List(interface.info) + else Nil + } + }.map(TypeTree) - cpy.Template(tree)(parents = newParents, body = buf.toList ++ newBody) + cpy.Template(tree)( + parents = tree.parents ++ missing, + body = applyBuf.toList ++ newBody + ) } /** Dispatch to specialized `apply`s in user code when available */ override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo) = tree match { - case Apply(select @ Select(id, nme.apply), args) => { - val params = args.map(_.tpe) :+ tree.tpe + case app @ Apply(fun, args) + if fun.symbol.name == nme.apply && + fun.symbol.owner.derivesFrom(defn.FunctionClass(args.length)) + => { + val params = (fun.tpe.widen.firstParamTypes :+ tree.tpe).map(_.widenSingleton.dealias) val specializedApply = specializedName(nme.apply, params) - if (tree.fun.symbol.owner.info.member(specializedApply).exists) - tpd.Apply(tpd.Select(id, specializedApply), args) + if (!params.exists(_.isInstanceOf[ExprType]) && defn.FunctionClass(args.length).info.member(specializedApply).exists) { + val newSel = fun match { + case Select(qual, _) => + qual.select(specializedApply) + case _ => { + (fun.tpe: @unchecked) match { + case TermRef(prefix: ThisType, name) => + tpd.This(prefix.cls).select(specializedApply) + case TermRef(prefix: NamedType, name) => + tpd.ref(prefix).select(specializedApply) + } + } + } + + newSel.appliedToArgs(args) + } else tree } case _ => tree @@ -128,7 +173,7 @@ class SpecializeFunctions extends MiniPhaseTransform with InfoTransformer { @inline private def specInterface(typeParams: List[Type])(implicit ctx: Context) = { val specName = - ("JFunction".toTermName ++ (typeParams.length - 1)) + ("JFunction" + (typeParams.length - 1)).toTermName .specializedFor(typeParams, typeParams.map(_.typeSymbol.name), Nil, Nil) ctx.getClassIfDefined("scala.compat.java8.".toTermName ++ specName) diff --git a/compiler/src/dotty/tools/dotc/transform/SpecializedApplyMethods.scala b/compiler/src/dotty/tools/dotc/transform/SpecializedApplyMethods.scala index fdd8ead26caf..2afee5a397ec 100644 --- a/compiler/src/dotty/tools/dotc/transform/SpecializedApplyMethods.scala +++ b/compiler/src/dotty/tools/dotc/transform/SpecializedApplyMethods.scala @@ -44,7 +44,7 @@ class SpecializedApplyMethods extends MiniPhaseTransform with InfoTransformer { t1 <- List(IntType, LongType, FloatType, DoubleType) } yield specApply(func1, List(t1), r) - func2 = defn.FunctionClass(2) + func2 = FunctionClass(2) func2Applys = for { r <- List(UnitType, BooleanType, IntType, FloatType, LongType, DoubleType) t1 <- List(IntType, LongType, DoubleType) @@ -54,7 +54,7 @@ class SpecializedApplyMethods extends MiniPhaseTransform with InfoTransformer { /** Add symbols for specialized methods to FunctionN */ def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context) = tp match { - case tp: ClassInfo if defn.isFunctionClass(sym) => { + case tp: ClassInfo if defn.isPlainFunctionClass(sym) => { init() val newDecls = sym.name.functionArity match { case 0 => func0Applys.foldLeft(tp.decls.cloneScope) { diff --git a/tests/run/t2857.scala b/tests/run/t2857.scala index 5df2d440eac5..794e651ec3d1 100644 --- a/tests/run/t2857.scala +++ b/tests/run/t2857.scala @@ -5,5 +5,3 @@ object Test extends dotty.runtime.LegacyApp { m.removeBinding(6, "Foo") println(m.contains(6)) } - - From 6e79132e8970f5013511efef33a55d36c7026a5a Mon Sep 17 00:00:00 2001 From: Felix Mulder Date: Wed, 12 Apr 2017 16:48:49 +0200 Subject: [PATCH 19/32] Make `ThisName` recursive on `self.ThisName` --- compiler/src/dotty/tools/dotc/core/Names.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Names.scala b/compiler/src/dotty/tools/dotc/core/Names.scala index af92bbb4ecdb..d7ad373588e6 100644 --- a/compiler/src/dotty/tools/dotc/core/Names.scala +++ b/compiler/src/dotty/tools/dotc/core/Names.scala @@ -35,10 +35,10 @@ object Names { * in a name table. A derived term name adds a tag, and possibly a number * or a further simple name to some other name. */ - abstract class Name extends Designator with PreName { + abstract class Name extends Designator with PreName { self => /** A type for names of the same kind as this name */ - type ThisName <: Name + type ThisName <: Name { type ThisName = self.ThisName } /** Is this name a type name? */ def isTypeName: Boolean From bcf83cf8e45972a34750f3d999dcf2659ba3a38b Mon Sep 17 00:00:00 2001 From: Felix Mulder Date: Wed, 12 Apr 2017 21:57:05 +0200 Subject: [PATCH 20/32] Make sure specialized functions get the correct name --- .../dotc/transform/SpecializeFunctions.scala | 65 +++++++++++-------- 1 file changed, 37 insertions(+), 28 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala b/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala index 381c2777e870..d41772067cbd 100644 --- a/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala +++ b/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala @@ -29,33 +29,25 @@ class SpecializeFunctions extends MiniPhaseTransform with InfoTransformer { * the class parents with specialized versions. */ def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context) = tp match { - case tp: ClassInfo - if !sym.is(Flags.Package) && - !blacklistedSymbols.contains(sym) && - (tp.decls ne EmptyScope) - => { - var newApplys: List[Symbol] = Nil + case tp: ClassInfo if !sym.is(Flags.Package) && (tp.decls ne EmptyScope) => { + var newApplys = Map.empty[Name, Symbol] val newParents = tp.parents.mapConserve { parent => List(0, 1, 2, 3).flatMap { arity => val func = defn.FunctionClass(arity) - if (!parent.isRef(func)) Nil + if (!parent.derivesFrom(func)) Nil else { val typeParams = tp.typeRef.baseArgInfos(func) val interface = specInterface(typeParams) if (interface.exists) { - val specializedApply = { - val specializedMethodName = specializedName(nme.apply, typeParams) - ctx.newSymbol( - sym, - specializedMethodName, - Flags.Override | Flags.Method, - interface.info.decls.lookup(specializedMethodName).info - ) + if (tp.decls.lookup(nme.apply).exists) { + val specializedMethodName = nme.apply.specializedFunction(typeParams.last, typeParams.init) + newApplys = newApplys + (specializedMethodName -> interface) } - newApplys = specializedApply :: newApplys - List(interface.typeRef) + + if (parent.isRef(func)) List(interface.typeRef) + else Nil } else Nil } @@ -66,9 +58,18 @@ class SpecializeFunctions extends MiniPhaseTransform with InfoTransformer { def newDecls = if (newApplys.isEmpty) tp.decls - else newApplys.foldLeft(tp.decls.cloneScope) { - (scope, sym) => scope.enter(sym); scope - } + else + newApplys.toList.map { case (name, interface) => + ctx.newSymbol( + sym, + name, + Flags.Override | Flags.Method, + interface.info.decls.lookup(name).info + ) + } + .foldLeft(tp.decls.cloneScope) { + (scope, sym) => scope.enter(sym); scope + } tp.derivedClassInfo( classParents = newParents, @@ -88,13 +89,22 @@ class SpecializeFunctions extends MiniPhaseTransform with InfoTransformer { val applyBuf = new mutable.ListBuffer[Tree] val newBody = tree.body.mapConserve { case dt: DefDef if dt.name == nme.apply && dt.vparamss.length == 1 => { - val specializedApply = tree.symbol.enclosingClass.info.decls.lookup { - specializedName( - nme.apply, - dt.vparamss.head.map(_.symbol.info) :+ dt.tpe.widen.finalResultType - ) + val specName = nme.apply.specializedFunction( + dt.tpe.widen.finalResultType, + dt.vparamss.head.map(_.symbol.info) + ) + + val specializedApply = tree.symbol.enclosingClass.info.decls.lookup(specName)//member(specName).symbol + //val specializedApply = tree.symbol.enclosingClass.info.member(specName).symbol + + if (false) { + println(tree.symbol.enclosingClass.show) + println("'" + specName.show + "'") + println(specializedApply) + println(specializedApply.exists) } + if (specializedApply.exists) { val apply = specializedApply.asTerm val specializedDecl = @@ -103,7 +113,6 @@ class SpecializeFunctions extends MiniPhaseTransform with InfoTransformer { .changeOwner(dt.symbol, apply) .subst(dt.vparamss.flatten.map(_.symbol), vrefss.flatten.map(_.symbol)) }) - applyBuf += specializedDecl // create a forwarding to the specialized apply @@ -147,7 +156,7 @@ class SpecializeFunctions extends MiniPhaseTransform with InfoTransformer { val params = (fun.tpe.widen.firstParamTypes :+ tree.tpe).map(_.widenSingleton.dealias) val specializedApply = specializedName(nme.apply, params) - if (!params.exists(_.isInstanceOf[ExprType]) && defn.FunctionClass(args.length).info.member(specializedApply).exists) { + if (!params.exists(_.isInstanceOf[ExprType]) && fun.symbol.owner.info.decls.lookup(specializedApply).exists) { val newSel = fun match { case Select(qual, _) => qual.select(specializedApply) @@ -174,7 +183,7 @@ class SpecializeFunctions extends MiniPhaseTransform with InfoTransformer { @inline private def specInterface(typeParams: List[Type])(implicit ctx: Context) = { val specName = ("JFunction" + (typeParams.length - 1)).toTermName - .specializedFor(typeParams, typeParams.map(_.typeSymbol.name), Nil, Nil) + .specializedFunction(typeParams.last, typeParams.init) ctx.getClassIfDefined("scala.compat.java8.".toTermName ++ specName) } From 8b6bb63e4fb992ff56ba91d2ac0fc56e06131fe6 Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Wed, 4 Oct 2017 07:39:03 +0200 Subject: [PATCH 21/32] Fix compilation errors --- .../dotty/tools/dotc/transform/SpecializeFunctions.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala b/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala index d41772067cbd..948ab7c2ed7d 100644 --- a/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala +++ b/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala @@ -37,7 +37,7 @@ class SpecializeFunctions extends MiniPhaseTransform with InfoTransformer { val func = defn.FunctionClass(arity) if (!parent.derivesFrom(func)) Nil else { - val typeParams = tp.typeRef.baseArgInfos(func) + val typeParams = tp.cls.typeRef.baseType(func).argInfos val interface = specInterface(typeParams) if (interface.exists) { @@ -132,13 +132,13 @@ class SpecializeFunctions extends MiniPhaseTransform with InfoTransformer { if (!tr.parents.exists(_.isRef(func))) Nil else { - val typeParams = tr.baseArgInfos(func) + val typeParams = tr.baseType(func).argInfos val interface = specInterface(typeParams) if (interface.exists) List(interface.info) else Nil } - }.map(TypeTree) + }.map(TypeTree _) cpy.Template(tree)( parents = tree.parents ++ missing, From de7b5f4809c9e1e4bab6974117ba3187c2806725 Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Fri, 6 Oct 2017 15:46:12 +0200 Subject: [PATCH 22/32] Don't change parents in `specializeFunctions` --- .../src/dotty/tools/dotc/core/NameOps.scala | 4 +- .../src/dotty/tools/dotc/core/Names.scala | 2 +- .../dotc/transform/SpecializeFunctions.scala | 68 +++++-------------- 3 files changed, 20 insertions(+), 54 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/NameOps.scala b/compiler/src/dotty/tools/dotc/core/NameOps.scala index 345891ae5c14..362788947a2e 100644 --- a/compiler/src/dotty/tools/dotc/core/NameOps.scala +++ b/compiler/src/dotty/tools/dotc/core/NameOps.scala @@ -248,7 +248,7 @@ object NameOps { /** This method is to be used on **type parameters** from a class, since * this method does sorting based on their names */ - def specializedFor(classTargs: List[Types.Type], classTargsNames: List[Name], methodTargs: List[Types.Type], methodTarsNames: List[Name])(implicit ctx: Context): name.ThisName = { + def specializedFor(classTargs: List[Types.Type], classTargsNames: List[Name], methodTargs: List[Types.Type], methodTarsNames: List[Name])(implicit ctx: Context): Name = { val methodTags: Seq[Name] = (methodTargs zip methodTarsNames).sortBy(_._2).map(x => typeToTag(x._1)) val classTags: Seq[Name] = (classTargs zip classTargsNames).sortBy(_._2).map(x => typeToTag(x._1)) @@ -263,7 +263,7 @@ object NameOps { * * `<...>` */ - def specializedFunction(ret: Types.Type, args: List[Types.Type])(implicit ctx: Context): name.ThisName = + def specializedFunction(ret: Types.Type, args: List[Types.Type])(implicit ctx: Context): Name = name ++ nme.specializedTypeNames.prefix ++ nme.specializedTypeNames.separator ++ typeToTag(ret) ++ args.map(typeToTag).fold(nme.EMPTY)(_ ++ _) ++ nme.specializedTypeNames.suffix diff --git a/compiler/src/dotty/tools/dotc/core/Names.scala b/compiler/src/dotty/tools/dotc/core/Names.scala index d7ad373588e6..1ff7de8d472d 100644 --- a/compiler/src/dotty/tools/dotc/core/Names.scala +++ b/compiler/src/dotty/tools/dotc/core/Names.scala @@ -38,7 +38,7 @@ object Names { abstract class Name extends Designator with PreName { self => /** A type for names of the same kind as this name */ - type ThisName <: Name { type ThisName = self.ThisName } + type ThisName <: Name /** Is this name a type name? */ def isTypeName: Boolean diff --git a/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala b/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala index 948ab7c2ed7d..a08545c4df1e 100644 --- a/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala +++ b/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala @@ -32,49 +32,39 @@ class SpecializeFunctions extends MiniPhaseTransform with InfoTransformer { case tp: ClassInfo if !sym.is(Flags.Package) && (tp.decls ne EmptyScope) => { var newApplys = Map.empty[Name, Symbol] - val newParents = tp.parents.mapConserve { parent => - List(0, 1, 2, 3).flatMap { arity => + tp.parents.foreach { parent => + List(0, 1, 2, 3).foreach { arity => val func = defn.FunctionClass(arity) if (!parent.derivesFrom(func)) Nil else { val typeParams = tp.cls.typeRef.baseType(func).argInfos - val interface = specInterface(typeParams) + val interface = specInterface(typeParams) if (interface.exists) { if (tp.decls.lookup(nme.apply).exists) { val specializedMethodName = nme.apply.specializedFunction(typeParams.last, typeParams.init) - newApplys = newApplys + (specializedMethodName -> interface) + newApplys += (specializedMethodName -> interface) } - - if (parent.isRef(func)) List(interface.typeRef) - else Nil } - else Nil } } - .headOption - .getOrElse(parent) } def newDecls = - if (newApplys.isEmpty) tp.decls - else - newApplys.toList.map { case (name, interface) => - ctx.newSymbol( - sym, - name, - Flags.Override | Flags.Method, - interface.info.decls.lookup(name).info - ) - } - .foldLeft(tp.decls.cloneScope) { - (scope, sym) => scope.enter(sym); scope - } + newApplys.toList.map { case (name, interface) => + ctx.newSymbol( + sym, + name, + Flags.Override | Flags.Method, + interface.info.decls.lookup(name).info + ) + } + .foldLeft(tp.decls.cloneScope) { + (scope, sym) => scope.enter(sym); scope + } - tp.derivedClassInfo( - classParents = newParents, - decls = newDecls - ) + if (newApplys.isEmpty) tp + else tp.derivedClassInfo(decls = newDecls) } case _ => tp @@ -95,15 +85,6 @@ class SpecializeFunctions extends MiniPhaseTransform with InfoTransformer { ) val specializedApply = tree.symbol.enclosingClass.info.decls.lookup(specName)//member(specName).symbol - //val specializedApply = tree.symbol.enclosingClass.info.member(specName).symbol - - if (false) { - println(tree.symbol.enclosingClass.show) - println("'" + specName.show + "'") - println(specializedApply) - println(specializedApply.exists) - } - if (specializedApply.exists) { val apply = specializedApply.asTerm @@ -126,22 +107,7 @@ class SpecializeFunctions extends MiniPhaseTransform with InfoTransformer { case x => x } - val missing: List[TypeTree] = List(0, 1, 2, 3).flatMap { arity => - val func = defn.FunctionClass(arity) - val tr = tree.symbol.enclosingClass.typeRef - - if (!tr.parents.exists(_.isRef(func))) Nil - else { - val typeParams = tr.baseType(func).argInfos - val interface = specInterface(typeParams) - - if (interface.exists) List(interface.info) - else Nil - } - }.map(TypeTree _) - cpy.Template(tree)( - parents = tree.parents ++ missing, body = applyBuf.toList ++ newBody ) } From aa3f58885824c7bb31fc7a913fd1e1737159dfa1 Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Mon, 9 Oct 2017 07:20:37 +0200 Subject: [PATCH 23/32] Cleanup --- .../tools/dotc/transform/SpecializeFunctions.scala | 12 +----------- .../dotc/transform/SpecializeFunctionsTests.scala | 6 ++++-- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala b/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala index a08545c4df1e..0bdd2bc97cc0 100644 --- a/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala +++ b/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala @@ -15,16 +15,6 @@ class SpecializeFunctions extends MiniPhaseTransform with InfoTransformer { import ast.tpd._ val phaseName = "specializeFunctions" - private[this] var _blacklistedSymbols: List[Symbol] = _ - - private def blacklistedSymbols(implicit ctx: Context): List[Symbol] = { - if (_blacklistedSymbols eq null) _blacklistedSymbols = List( - ctx.getClassIfDefined("scala.math.Ordering").asClass.membersNamed("Ops".toTypeName).first.symbol - ) - - _blacklistedSymbols - } - /** Transforms the type to include decls for specialized applys and replace * the class parents with specialized versions. */ @@ -84,7 +74,7 @@ class SpecializeFunctions extends MiniPhaseTransform with InfoTransformer { dt.vparamss.head.map(_.symbol.info) ) - val specializedApply = tree.symbol.enclosingClass.info.decls.lookup(specName)//member(specName).symbol + val specializedApply = tree.symbol.enclosingClass.info.decls.lookup(specName) if (specializedApply.exists) { val apply = specializedApply.asTerm diff --git a/compiler/test/dotty/tools/dotc/transform/SpecializeFunctionsTests.scala b/compiler/test/dotty/tools/dotc/transform/SpecializeFunctionsTests.scala index 9605c8fbfdd7..5d5c42e93d98 100644 --- a/compiler/test/dotty/tools/dotc/transform/SpecializeFunctionsTests.scala +++ b/compiler/test/dotty/tools/dotc/transform/SpecializeFunctionsTests.scala @@ -60,8 +60,8 @@ class SpecializeFunctionsTests extends DottyBytecodeTest { apps.length == 3, s"Wrong number of specialized applys, actual length: ${apps.length} - $apps" ) - assert(apps.contains("apply"), "Func3 did not contain `apply` forwarder method") - assert(apps.contains("apply$mcIII$sp"), "Func3 did not contain specialized apply") + assert(apps.contains("apply"), "Func2 did not contain `apply` forwarder method") + assert(apps.contains("apply$mcIII$sp"), "Func2 did not contain specialized apply") } } @@ -91,6 +91,7 @@ class SpecializeFunctionsTests extends DottyBytecodeTest { |}""".stripMargin checkBCode(source) { dir => + // No specialization for Function1[Char, Int] assertBoxing("", findClass("Test$", dir).methods) } } @@ -136,6 +137,7 @@ class SpecializeFunctionsTests extends DottyBytecodeTest { |}""".stripMargin checkBCode(source) { dir => + // No specialization for Function2[Char, Char, Char] assertBoxing("", findClass("Test$", dir).methods) } } From b9187d2cd5dc3041f55946aa031975c33a2be4c1 Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Tue, 10 Oct 2017 10:54:49 +0200 Subject: [PATCH 24/32] Move type to JVM tag conversion to Definitions --- .../src/dotty/tools/dotc/core/NameOps.scala | 22 ++++--------------- 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/NameOps.scala b/compiler/src/dotty/tools/dotc/core/NameOps.scala index 362788947a2e..c8e00391bf7e 100644 --- a/compiler/src/dotty/tools/dotc/core/NameOps.scala +++ b/compiler/src/dotty/tools/dotc/core/NameOps.scala @@ -231,26 +231,12 @@ object NameOps { case nme.clone_ => nme.clone_ } - private def typeToTag(tp: Types.Type)(implicit ctx: Context): Name = - tp.classSymbol match { - case t if t eq defn.IntClass => nme.specializedTypeNames.Int - case t if t eq defn.BooleanClass => nme.specializedTypeNames.Boolean - case t if t eq defn.ByteClass => nme.specializedTypeNames.Byte - case t if t eq defn.LongClass => nme.specializedTypeNames.Long - case t if t eq defn.ShortClass => nme.specializedTypeNames.Short - case t if t eq defn.FloatClass => nme.specializedTypeNames.Float - case t if t eq defn.UnitClass => nme.specializedTypeNames.Void - case t if t eq defn.DoubleClass => nme.specializedTypeNames.Double - case t if t eq defn.CharClass => nme.specializedTypeNames.Char - case _ => nme.specializedTypeNames.Object - } - /** This method is to be used on **type parameters** from a class, since * this method does sorting based on their names */ def specializedFor(classTargs: List[Types.Type], classTargsNames: List[Name], methodTargs: List[Types.Type], methodTarsNames: List[Name])(implicit ctx: Context): Name = { - val methodTags: Seq[Name] = (methodTargs zip methodTarsNames).sortBy(_._2).map(x => typeToTag(x._1)) - val classTags: Seq[Name] = (classTargs zip classTargsNames).sortBy(_._2).map(x => typeToTag(x._1)) + val methodTags: Seq[Name] = (methodTargs zip methodTarsNames).sortBy(_._2).map(x => defn.typeTag(x._1)) + val classTags: Seq[Name] = (classTargs zip classTargsNames).sortBy(_._2).map(x => defn.typeTag(x._1)) name.likeSpaced(name ++ nme.specializedTypeNames.prefix ++ methodTags.fold(nme.EMPTY)(_ ++ _) ++ nme.specializedTypeNames.separator ++ @@ -265,8 +251,8 @@ object NameOps { */ def specializedFunction(ret: Types.Type, args: List[Types.Type])(implicit ctx: Context): Name = name ++ nme.specializedTypeNames.prefix ++ - nme.specializedTypeNames.separator ++ typeToTag(ret) ++ - args.map(typeToTag).fold(nme.EMPTY)(_ ++ _) ++ nme.specializedTypeNames.suffix + nme.specializedTypeNames.separator ++ defn.typeTag(ret) ++ + args.map(defn.typeTag).fold(nme.EMPTY)(_ ++ _) ++ nme.specializedTypeNames.suffix /** If name length exceeds allowable limit, replace part of it by hash */ def compactified(implicit ctx: Context): TermName = termName(compactify(name.toString)) From a607938efa8940bdcbddf71ee42c9548ec95596d Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Wed, 11 Oct 2017 11:05:55 +0200 Subject: [PATCH 25/32] Add more tests for function specialization --- .../transform/SpecializeFunctionsTests.scala | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/compiler/test/dotty/tools/dotc/transform/SpecializeFunctionsTests.scala b/compiler/test/dotty/tools/dotc/transform/SpecializeFunctionsTests.scala index 5d5c42e93d98..69300985938d 100644 --- a/compiler/test/dotty/tools/dotc/transform/SpecializeFunctionsTests.scala +++ b/compiler/test/dotty/tools/dotc/transform/SpecializeFunctionsTests.scala @@ -141,4 +141,77 @@ class SpecializeFunctionsTests extends DottyBytecodeTest { assertBoxing("", findClass("Test$", dir).methods) } } + + @Test def multipleParentsNoBoxing = { + implicit val source: String = + """|object Test { + | class Func01 extends Function0[Int] with Function1[Int, Int] { + | def apply(): Int = 0 + | def apply(x: Int): Int = x + | } + | (new Func01: Function0[Int])() + | (new Func01: Function1[Int, Int])(1) + |}""".stripMargin + + checkBCode(source) { dir => + assertNoBoxing("", findClass("Test$", dir).methods) + } + } + + @Test def multipleLevelInheritanceNoBoxing = { + implicit val source: String = + """|object Test { + | class Func1[T](fn: T => Int) extends Function1[T, Int] { + | def apply(x: T): Int = fn(x) + | } + | class Fn extends Func1(identity[Int]) + | (new Fn: Function1[Int, Int])(123) + |}""".stripMargin + + checkBCode(source) { dir => + assertNoBoxing("", findClass("Test$", dir).methods) + } + } + + @Test def lambdaNoBoxing1 = { + implicit val source: String = + """|object Test { + | val fn = (x: Int) => x + 1 + | fn(2) + |}""".stripMargin + + checkBCode(source) { dir => + assertNoBoxing("", findClass("Test$", dir).methods) + } + } + + @Test def lambdaNoBoxing2 = { + implicit val source: String = + """|object Test { + | def fn[T, U, V](op0: T => U, op1: U => V): T => V = (x: T) => op1(op0(x)) + | val f0: Int => Double = _.toDouble + | val f1: Double => Int = _.toInt + | val id = fn(f0, f1) + | id(2) + |}""".stripMargin + + checkBCode(source) { dir => + assertNoBoxing("", findClass("Test$", dir).methods) + } + } + + @Test def classWithFieldBoxing = { + implicit val source: String = + """|object Test { + | class Func0[T](x: T) extends Function0[T] { + | def apply(): T = x + | } + | (new Func0(2): Function0[Int])() + |}""".stripMargin + + checkBCode(source) { dir => + // Boxing happens because of the field of `Func0`. + assertBoxing("", findClass("Test$", dir).methods) + } + } } From efdc516220374cdc2ceb9029363e7179b97bb6fe Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Wed, 11 Oct 2017 11:07:06 +0200 Subject: [PATCH 26/32] Pass by name should not introduce boxing --- compiler/src/dotty/tools/dotc/Compiler.scala | 6 +++--- .../dotc/transform/SpecializeFunctionsTests.scala | 12 ++++++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index fe3636bf772e..e20fc671af41 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -68,14 +68,14 @@ class Compiler { 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 - new SpecializeFunctions), // Specialized Function1 by replacing super with specialized super + new Splitter), // Expand selections involving union types into conditionals List(new PhantomArgLift, // Extracts the evaluation of phantom arguments placing them before the call. new VCInlineMethods, // Inlines calls to value class methods new SeqLiterals, // Express vararg arguments as arrays new InterceptedMethods, // Special handling of `==`, `|=`, `getClass` methods new Getters, // Replace non-private vals and vars with getter defs (fields are added later) - new ElimByName, // Expand by-name parameter references + new ElimByName), // Expand by-name parameter references + List(new SpecializeFunctions, // Specialized Function{0,1,2} by replacing super with specialized super new ElimOuterSelect, // Expand outer selections new AugmentScala2Traits, // Expand traits defined in Scala 2.x to simulate old-style rewritings new ResolveSuper, // Implement super accessors and add forwarders to trait methods diff --git a/compiler/test/dotty/tools/dotc/transform/SpecializeFunctionsTests.scala b/compiler/test/dotty/tools/dotc/transform/SpecializeFunctionsTests.scala index 69300985938d..503d5198aae4 100644 --- a/compiler/test/dotty/tools/dotc/transform/SpecializeFunctionsTests.scala +++ b/compiler/test/dotty/tools/dotc/transform/SpecializeFunctionsTests.scala @@ -214,4 +214,16 @@ class SpecializeFunctionsTests extends DottyBytecodeTest { assertBoxing("", findClass("Test$", dir).methods) } } + + @Test def passByNameNoBoxing = { + implicit val source: String = + """|object Test { + | def fn(x: => Int): Int = x + | fn(2) + |}""".stripMargin + + checkBCode(source) { dir => + assertNoBoxing("fn", findClass("Test$", dir).methods) + } + } } From 654fb1425c53772af5a8f504838dd451a65b1abe Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Wed, 11 Oct 2017 14:39:13 +0200 Subject: [PATCH 27/32] Cleanup --- .../src/dotty/tools/dotc/core/Names.scala | 2 +- .../dotc/transform/SpecializeFunctions.scala | 39 ++++++++----------- tests/run/t2857.scala | 2 + 3 files changed, 19 insertions(+), 24 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Names.scala b/compiler/src/dotty/tools/dotc/core/Names.scala index 1ff7de8d472d..af92bbb4ecdb 100644 --- a/compiler/src/dotty/tools/dotc/core/Names.scala +++ b/compiler/src/dotty/tools/dotc/core/Names.scala @@ -35,7 +35,7 @@ object Names { * in a name table. A derived term name adds a tag, and possibly a number * or a further simple name to some other name. */ - abstract class Name extends Designator with PreName { self => + abstract class Name extends Designator with PreName { /** A type for names of the same kind as this name */ type ThisName <: Name diff --git a/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala b/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala index 0bdd2bc97cc0..8f69d29f07a0 100644 --- a/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala +++ b/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala @@ -14,27 +14,23 @@ import scala.collection.mutable class SpecializeFunctions extends MiniPhaseTransform with InfoTransformer { import ast.tpd._ val phaseName = "specializeFunctions" + override def runsAfter = Set(classOf[ElimByName]) - /** Transforms the type to include decls for specialized applys and replace - * the class parents with specialized versions. - */ + /** Transforms the type to include decls for specialized applys */ def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context) = tp match { - case tp: ClassInfo if !sym.is(Flags.Package) && (tp.decls ne EmptyScope) => { + case tp: ClassInfo if !sym.is(Flags.Package) && (tp.decls ne EmptyScope) => var newApplys = Map.empty[Name, Symbol] tp.parents.foreach { parent => List(0, 1, 2, 3).foreach { arity => val func = defn.FunctionClass(arity) - if (!parent.derivesFrom(func)) Nil - else { + if (parent.derivesFrom(func)) { val typeParams = tp.cls.typeRef.baseType(func).argInfos val interface = specInterface(typeParams) - if (interface.exists) { - if (tp.decls.lookup(nme.apply).exists) { - val specializedMethodName = nme.apply.specializedFunction(typeParams.last, typeParams.init) - newApplys += (specializedMethodName -> interface) - } + if (interface.exists && tp.decls.lookup(nme.apply).exists) { + val specializedMethodName = nme.apply.specializedFunction(typeParams.last, typeParams.init) + newApplys += (specializedMethodName -> interface) } } } @@ -45,7 +41,7 @@ class SpecializeFunctions extends MiniPhaseTransform with InfoTransformer { ctx.newSymbol( sym, name, - Flags.Override | Flags.Method, + Flags.Override | Flags.Method | Flags.Synthetic, interface.info.decls.lookup(name).info ) } @@ -55,20 +51,18 @@ class SpecializeFunctions extends MiniPhaseTransform with InfoTransformer { if (newApplys.isEmpty) tp else tp.derivedClassInfo(decls = newDecls) - } case _ => tp } /** Transforms the `Template` of the classes to contain forwarders from the - * generic applys to the specialized ones. Also replaces parents of the - * class on the tree level and inserts the specialized applys in the - * template body. + * generic applys to the specialized ones. Also inserts the specialized applys + * in the template body. */ override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo) = { val applyBuf = new mutable.ListBuffer[Tree] val newBody = tree.body.mapConserve { - case dt: DefDef if dt.name == nme.apply && dt.vparamss.length == 1 => { + case dt: DefDef if dt.name == nme.apply && dt.vparamss.length == 1 => val specName = nme.apply.specializedFunction( dt.tpe.widen.finalResultType, dt.vparamss.head.map(_.symbol.info) @@ -93,7 +87,7 @@ class SpecializeFunctions extends MiniPhaseTransform with InfoTransformer { .appliedToArgs(dt.vparamss.head.map(vparam => ref(vparam.symbol))) }) } else dt - } + case x => x } @@ -105,10 +99,10 @@ class SpecializeFunctions extends MiniPhaseTransform with InfoTransformer { /** Dispatch to specialized `apply`s in user code when available */ override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo) = tree match { - case app @ Apply(fun, args) + case Apply(fun, args) if fun.symbol.name == nme.apply && fun.symbol.owner.derivesFrom(defn.FunctionClass(args.length)) - => { + => val params = (fun.tpe.widen.firstParamTypes :+ tree.tpe).map(_.widenSingleton.dealias) val specializedApply = specializedName(nme.apply, params) @@ -116,20 +110,19 @@ class SpecializeFunctions extends MiniPhaseTransform with InfoTransformer { val newSel = fun match { case Select(qual, _) => qual.select(specializedApply) - case _ => { + case _ => (fun.tpe: @unchecked) match { case TermRef(prefix: ThisType, name) => tpd.This(prefix.cls).select(specializedApply) case TermRef(prefix: NamedType, name) => tpd.ref(prefix).select(specializedApply) } - } } newSel.appliedToArgs(args) } else tree - } + case _ => tree } diff --git a/tests/run/t2857.scala b/tests/run/t2857.scala index 794e651ec3d1..5df2d440eac5 100644 --- a/tests/run/t2857.scala +++ b/tests/run/t2857.scala @@ -5,3 +5,5 @@ object Test extends dotty.runtime.LegacyApp { m.removeBinding(6, "Foo") println(m.contains(6)) } + + From ed751bd35dd909a2bf6a54c1583631d224970dc0 Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Wed, 11 Oct 2017 14:43:20 +0200 Subject: [PATCH 28/32] No specialization for Function3 --- .../src/dotty/tools/dotc/transform/SpecializeFunctions.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala b/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala index 8f69d29f07a0..f48610c50c24 100644 --- a/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala +++ b/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala @@ -22,7 +22,7 @@ class SpecializeFunctions extends MiniPhaseTransform with InfoTransformer { var newApplys = Map.empty[Name, Symbol] tp.parents.foreach { parent => - List(0, 1, 2, 3).foreach { arity => + List(0, 1, 2).foreach { arity => val func = defn.FunctionClass(arity) if (parent.derivesFrom(func)) { val typeParams = tp.cls.typeRef.baseType(func).argInfos From a1c7109fdeac2e89d070eee630eba18d2e5c1cbd Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Sat, 28 Oct 2017 15:16:13 +0200 Subject: [PATCH 29/32] Adapt to recent changes in transformers and phases --- .../tools/dotc/transform/SpecializeFunctions.scala | 10 +++++----- .../tools/dotc/transform/SpecializedApplyMethods.scala | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala b/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala index f48610c50c24..69940386450d 100644 --- a/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala +++ b/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala @@ -1,23 +1,23 @@ package dotty.tools.dotc package transform -import TreeTransforms.{ MiniPhaseTransform, TransformerInfo } import ast.Trees._, ast.tpd, core._ import Contexts.Context, Types._, Decorators._, Symbols._, DenotTransformers._ import SymDenotations._, Scopes._, StdNames._, NameOps._, Names._ +import MegaPhase.MiniPhase import scala.collection.mutable /** Specializes classes that inherit from `FunctionN` where there exists a * specialized form. */ -class SpecializeFunctions extends MiniPhaseTransform with InfoTransformer { +class SpecializeFunctions extends MiniPhase with InfoTransformer { import ast.tpd._ val phaseName = "specializeFunctions" override def runsAfter = Set(classOf[ElimByName]) /** Transforms the type to include decls for specialized applys */ - def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context) = tp match { + override def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context) = tp match { case tp: ClassInfo if !sym.is(Flags.Package) && (tp.decls ne EmptyScope) => var newApplys = Map.empty[Name, Symbol] @@ -59,7 +59,7 @@ class SpecializeFunctions extends MiniPhaseTransform with InfoTransformer { * generic applys to the specialized ones. Also inserts the specialized applys * in the template body. */ - override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo) = { + override def transformTemplate(tree: Template)(implicit ctx: Context) = { val applyBuf = new mutable.ListBuffer[Tree] val newBody = tree.body.mapConserve { case dt: DefDef if dt.name == nme.apply && dt.vparamss.length == 1 => @@ -97,7 +97,7 @@ class SpecializeFunctions extends MiniPhaseTransform with InfoTransformer { } /** Dispatch to specialized `apply`s in user code when available */ - override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo) = + override def transformApply(tree: Apply)(implicit ctx: Context) = tree match { case Apply(fun, args) if fun.symbol.name == nme.apply && diff --git a/compiler/src/dotty/tools/dotc/transform/SpecializedApplyMethods.scala b/compiler/src/dotty/tools/dotc/transform/SpecializedApplyMethods.scala index 2afee5a397ec..d5452cfeb4d7 100644 --- a/compiler/src/dotty/tools/dotc/transform/SpecializedApplyMethods.scala +++ b/compiler/src/dotty/tools/dotc/transform/SpecializedApplyMethods.scala @@ -1,10 +1,10 @@ package dotty.tools.dotc package transform -import TreeTransforms.{ MiniPhaseTransform, TransformerInfo } import ast.Trees._, ast.tpd, core._ import Contexts.Context, Types._, Decorators._, Symbols._, DenotTransformers._ import SymDenotations._, Scopes._, StdNames._, NameOps._, Names._ +import MegaPhase.MiniPhase /** This phase synthesizes specialized methods for FunctionN, this is done * since there are no scala signatures in the bytecode for the specialized @@ -14,7 +14,7 @@ import SymDenotations._, Scopes._, StdNames._, NameOps._, Names._ * can hardcode them. This should, however be removed once we're using a * different standard library. */ -class SpecializedApplyMethods extends MiniPhaseTransform with InfoTransformer { +class SpecializedApplyMethods extends MiniPhase with InfoTransformer { import ast.tpd._ val phaseName = "specializedApplyMethods" @@ -53,7 +53,7 @@ class SpecializedApplyMethods extends MiniPhaseTransform with InfoTransformer { } /** Add symbols for specialized methods to FunctionN */ - def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context) = tp match { + override def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context) = tp match { case tp: ClassInfo if defn.isPlainFunctionClass(sym) => { init() val newDecls = sym.name.functionArity match { @@ -75,7 +75,7 @@ class SpecializedApplyMethods extends MiniPhaseTransform with InfoTransformer { } /** Create bridge methods for FunctionN with specialized applys */ - override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo) = { + override def transformTemplate(tree: Template)(implicit ctx: Context) = { val owner = tree.symbol.owner val additionalSymbols = if (owner eq func0) func0Applys From 16b881df2054a5bf8b372c9994f6efd709231105 Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Sat, 28 Oct 2017 15:30:59 +0200 Subject: [PATCH 30/32] Un-split phase group that I previously split --- compiler/src/dotty/tools/dotc/Compiler.scala | 6 +++--- compiler/src/dotty/tools/dotc/transform/ElimByName.scala | 3 --- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index e20fc671af41..8a70a583e0af 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -68,14 +68,14 @@ class Compiler { 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 + new Splitter, // Expand selections involving union types into conditionals + new ElimByName), // Expand by-name parameter references List(new PhantomArgLift, // Extracts the evaluation of phantom arguments placing them before the call. new VCInlineMethods, // Inlines calls to value class methods new SeqLiterals, // Express vararg arguments as arrays new InterceptedMethods, // Special handling of `==`, `|=`, `getClass` methods new Getters, // Replace non-private vals and vars with getter defs (fields are added later) - new ElimByName), // Expand by-name parameter references - List(new SpecializeFunctions, // Specialized Function{0,1,2} by replacing super with specialized super + new SpecializeFunctions, // Specialized Function{0,1,2} by replacing super with specialized super new ElimOuterSelect, // Expand outer selections new AugmentScala2Traits, // Expand traits defined in Scala 2.x to simulate old-style rewritings new ResolveSuper, // Implement super accessors and add forwarders to trait methods diff --git a/compiler/src/dotty/tools/dotc/transform/ElimByName.scala b/compiler/src/dotty/tools/dotc/transform/ElimByName.scala index 36c22f2e2068..758bf8c260f9 100644 --- a/compiler/src/dotty/tools/dotc/transform/ElimByName.scala +++ b/compiler/src/dotty/tools/dotc/transform/ElimByName.scala @@ -42,9 +42,6 @@ class ElimByName extends TransformByNameApply with InfoTransformer { override def phaseName: String = "elimByName" - override def runsAfterGroupsOf = Set(classOf[Splitter]) - // I got errors running this phase in an earlier group, but I did not track them down. - /** Map `tree` to `tree.apply()` is `ftree` was of ExprType and becomes now a function */ private def applyIfFunction(tree: Tree, ftree: Tree)(implicit ctx: Context) = if (isByNameRef(ftree)) From 88513121649819c252c4f738571465007b481377 Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Mon, 6 Nov 2017 08:30:39 +0100 Subject: [PATCH 31/32] Address review comments --- .../dotty/tools/dotc/core/Definitions.scala | 29 ++++++----- .../dotc/transform/SpecializeFunctions.scala | 50 +++++++++++-------- .../transform/SpecializedApplyMethods.scala | 20 ++++---- 3 files changed, 57 insertions(+), 42 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 6e5ad3e66405..a50053507cc7 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -927,28 +927,33 @@ class Definitions { } // Specialized type parameters defined for scala.Function{0,1,2}. - private lazy val Function1SpecializedParams: collection.Set[Type] = + lazy val Function1SpecializedParams: collection.Set[Type] = Set(IntType, LongType, FloatType, DoubleType) - private lazy val Function2SpecializedParams: collection.Set[Type] = + lazy val Function2SpecializedParams: collection.Set[Type] = Set(IntType, LongType, DoubleType) - private lazy val Function0SpecializedReturns: collection.Set[Type] = + lazy val Function0SpecializedReturns: collection.Set[Type] = ScalaNumericValueTypeList.toSet[Type] + UnitType + BooleanType - private lazy val Function1SpecializedReturns: collection.Set[Type] = + lazy val Function1SpecializedReturns: collection.Set[Type] = Set(UnitType, BooleanType, IntType, FloatType, LongType, DoubleType) - private lazy val Function2SpecializedReturns: collection.Set[Type] = + lazy val Function2SpecializedReturns: collection.Set[Type] = Function1SpecializedReturns def isSpecializableFunction(cls: ClassSymbol, paramTypes: List[Type], retType: Type)(implicit ctx: Context) = - isFunctionClass(cls) && (paramTypes match { + cls.derivesFrom(FunctionClass(paramTypes.length)) && (paramTypes match { case Nil => - Function0SpecializedReturns.contains(retType) + val specializedReturns = Function0SpecializedReturns.map(_.typeSymbol) + specializedReturns.contains(retType.typeSymbol) case List(paramType0) => - Function1SpecializedParams.contains(paramType0) && - Function1SpecializedReturns.contains(retType) + val specializedParams = Function1SpecializedParams.map(_.typeSymbol) + lazy val specializedReturns = Function1SpecializedReturns.map(_.typeSymbol) + specializedParams.contains(paramType0.typeSymbol) && + specializedReturns.contains(retType.typeSymbol) case List(paramType0, paramType1) => - Function2SpecializedParams.contains(paramType0) && - Function2SpecializedParams.contains(paramType1) && - Function2SpecializedReturns.contains(retType) + val specializedParams = Function2SpecializedParams.map(_.typeSymbol) + lazy val specializedReturns = Function2SpecializedReturns.map(_.typeSymbol) + specializedParams.contains(paramType0.typeSymbol) && + specializedParams.contains(paramType1.typeSymbol) && + specializedReturns.contains(retType.typeSymbol) case _ => false }) diff --git a/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala b/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala index 69940386450d..4f01567855a8 100644 --- a/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala +++ b/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala @@ -16,6 +16,8 @@ class SpecializeFunctions extends MiniPhase with InfoTransformer { val phaseName = "specializeFunctions" override def runsAfter = Set(classOf[ElimByName]) + private val jFunction = "scala.compat.java8.JFunction".toTermName + /** Transforms the type to include decls for specialized applys */ override def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context) = tp match { case tp: ClassInfo if !sym.is(Flags.Package) && (tp.decls ne EmptyScope) => @@ -26,9 +28,15 @@ class SpecializeFunctions extends MiniPhase with InfoTransformer { val func = defn.FunctionClass(arity) if (parent.derivesFrom(func)) { val typeParams = tp.cls.typeRef.baseType(func).argInfos - val interface = specInterface(typeParams) - - if (interface.exists && tp.decls.lookup(nme.apply).exists) { + val isSpecializable = + defn.isSpecializableFunction( + parent.classSymbol.asClass, + typeParams.init, + typeParams.last + ) + + if (isSpecializable && tp.decls.lookup(nme.apply).exists) { + val interface = specInterface(typeParams) val specializedMethodName = nme.apply.specializedFunction(typeParams.last, typeParams.init) newApplys += (specializedMethodName -> interface) } @@ -63,13 +71,12 @@ class SpecializeFunctions extends MiniPhase with InfoTransformer { val applyBuf = new mutable.ListBuffer[Tree] val newBody = tree.body.mapConserve { case dt: DefDef if dt.name == nme.apply && dt.vparamss.length == 1 => - val specName = nme.apply.specializedFunction( - dt.tpe.widen.finalResultType, - dt.vparamss.head.map(_.symbol.info) - ) - - val specializedApply = tree.symbol.enclosingClass.info.decls.lookup(specName) + val cls = tree.symbol.enclosingClass.asClass + val typeParams = dt.vparamss.head.map(_.symbol.info) + val retType = dt.tpe.widen.finalResultType + val specName = specializedName(nme.apply, typeParams :+ retType) + val specializedApply = cls.info.decls.lookup(specName) if (specializedApply.exists) { val apply = specializedApply.asTerm val specializedDecl = @@ -104,9 +111,14 @@ class SpecializeFunctions extends MiniPhase with InfoTransformer { fun.symbol.owner.derivesFrom(defn.FunctionClass(args.length)) => val params = (fun.tpe.widen.firstParamTypes :+ tree.tpe).map(_.widenSingleton.dealias) - val specializedApply = specializedName(nme.apply, params) - - if (!params.exists(_.isInstanceOf[ExprType]) && fun.symbol.owner.info.decls.lookup(specializedApply).exists) { + val isSpecializable = + defn.isSpecializableFunction( + fun.symbol.owner.asClass, + params.init, + params.last) + + if (isSpecializable && !params.exists(_.isInstanceOf[ExprType])) { + val specializedApply = specializedName(nme.apply, params) val newSel = fun match { case Select(qual, _) => qual.select(specializedApply) @@ -126,14 +138,12 @@ class SpecializeFunctions extends MiniPhase with InfoTransformer { case _ => tree } - @inline private def specializedName(name: Name, args: List[Type])(implicit ctx: Context) = - name.specializedFor(args, args.map(_.typeSymbol.name), Nil, Nil) + private def specializedName(name: Name, args: List[Type])(implicit ctx: Context) = + name.specializedFunction(args.last, args.init) - @inline private def specInterface(typeParams: List[Type])(implicit ctx: Context) = { - val specName = - ("JFunction" + (typeParams.length - 1)).toTermName - .specializedFunction(typeParams.last, typeParams.init) + private def functionName(typeParams: List[Type])(implicit ctx: Context) = + jFunction ++ (typeParams.length - 1).toString - ctx.getClassIfDefined("scala.compat.java8.".toTermName ++ specName) - } + private def specInterface(typeParams: List[Type])(implicit ctx: Context) = + ctx.getClassIfDefined(functionName(typeParams).specializedFunction(typeParams.last, typeParams.init)) } diff --git a/compiler/src/dotty/tools/dotc/transform/SpecializedApplyMethods.scala b/compiler/src/dotty/tools/dotc/transform/SpecializedApplyMethods.scala index d5452cfeb4d7..19c7abacb5fb 100644 --- a/compiler/src/dotty/tools/dotc/transform/SpecializedApplyMethods.scala +++ b/compiler/src/dotty/tools/dotc/transform/SpecializedApplyMethods.scala @@ -19,9 +19,9 @@ class SpecializedApplyMethods extends MiniPhase with InfoTransformer { val phaseName = "specializedApplyMethods" - private[this] var func0Applys: List[Symbol] = _ - private[this] var func1Applys: List[Symbol] = _ - private[this] var func2Applys: List[Symbol] = _ + private[this] var func0Applys: collection.Set[Symbol] = _ + private[this] var func1Applys: collection.Set[Symbol] = _ + private[this] var func2Applys: collection.Set[Symbol] = _ private[this] var func0: Symbol = _ private[this] var func1: Symbol = _ private[this] var func2: Symbol = _ @@ -30,25 +30,25 @@ class SpecializedApplyMethods extends MiniPhase with InfoTransformer { val definitions = ctx.definitions import definitions._ - def specApply(sym: Symbol, args: List[Type], ret: Type)(implicit ctx: Context) = { + def specApply(sym: Symbol, args: List[Type], ret: Type)(implicit ctx: Context): Symbol = { val name = nme.apply.specializedFunction(ret, args) ctx.newSymbol(sym, name, Flags.Method, MethodType(args, ret)) } func0 = FunctionClass(0) - func0Applys = for (r <- ScalaValueTypes.toList) yield specApply(func0, Nil, r) + func0Applys = for (r <- defn.Function0SpecializedReturns) yield specApply(func0, Nil, r) func1 = FunctionClass(1) func1Applys = for { - r <- List(UnitType, BooleanType, IntType, FloatType, LongType, DoubleType) - t1 <- List(IntType, LongType, FloatType, DoubleType) + r <- defn.Function1SpecializedReturns + t1 <- defn.Function1SpecializedParams } yield specApply(func1, List(t1), r) func2 = FunctionClass(2) func2Applys = for { - r <- List(UnitType, BooleanType, IntType, FloatType, LongType, DoubleType) - t1 <- List(IntType, LongType, DoubleType) - t2 <- List(IntType, LongType, DoubleType) + r <- Function2SpecializedReturns + t1 <- Function2SpecializedParams + t2 <- Function2SpecializedReturns } yield specApply(func2, List(t1, t2), r) } From a32b06f26296fbdfdd9b8fa34d71a7f0e701538d Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Sun, 19 Nov 2017 15:32:17 +0100 Subject: [PATCH 32/32] Optimize phase `SpecializeFunctions` - Stop immediately if the type doesn't derive from `Function{0,1,2}` - We don't need to check if any of the parents derives from `Function{0,1,2}`, we can just check if the type derives from it. --- .../dotc/transform/SpecializeFunctions.scala | 103 ++++++++++-------- 1 file changed, 55 insertions(+), 48 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala b/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala index 4f01567855a8..f931339066b5 100644 --- a/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala +++ b/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala @@ -20,28 +20,28 @@ class SpecializeFunctions extends MiniPhase with InfoTransformer { /** Transforms the type to include decls for specialized applys */ override def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context) = tp match { - case tp: ClassInfo if !sym.is(Flags.Package) && (tp.decls ne EmptyScope) => + case tp: ClassInfo if !sym.is(Flags.Package) && (tp.decls ne EmptyScope) && derivesFromFn012(sym) => var newApplys = Map.empty[Name, Symbol] - tp.parents.foreach { parent => - List(0, 1, 2).foreach { arity => - val func = defn.FunctionClass(arity) - if (parent.derivesFrom(func)) { - val typeParams = tp.cls.typeRef.baseType(func).argInfos - val isSpecializable = - defn.isSpecializableFunction( - parent.classSymbol.asClass, - typeParams.init, - typeParams.last - ) - - if (isSpecializable && tp.decls.lookup(nme.apply).exists) { - val interface = specInterface(typeParams) - val specializedMethodName = nme.apply.specializedFunction(typeParams.last, typeParams.init) - newApplys += (specializedMethodName -> interface) - } + var arity = 0 + while (arity < 3) { + val func = defn.FunctionClass(arity) + if (tp.derivesFrom(func)) { + val typeParams = tp.cls.typeRef.baseType(func).argInfos + val isSpecializable = + defn.isSpecializableFunction( + sym.asClass, + typeParams.init, + typeParams.last + ) + + if (isSpecializable && tp.decls.lookup(nme.apply).exists) { + val interface = specInterface(typeParams) + val specializedMethodName = nme.apply.specializedFunction(typeParams.last, typeParams.init) + newApplys += (specializedMethodName -> interface) } } + arity += 1 } def newDecls = @@ -68,39 +68,41 @@ class SpecializeFunctions extends MiniPhase with InfoTransformer { * in the template body. */ override def transformTemplate(tree: Template)(implicit ctx: Context) = { - val applyBuf = new mutable.ListBuffer[Tree] - val newBody = tree.body.mapConserve { - case dt: DefDef if dt.name == nme.apply && dt.vparamss.length == 1 => - val cls = tree.symbol.enclosingClass.asClass - val typeParams = dt.vparamss.head.map(_.symbol.info) - val retType = dt.tpe.widen.finalResultType - - val specName = specializedName(nme.apply, typeParams :+ retType) - val specializedApply = cls.info.decls.lookup(specName) - if (specializedApply.exists) { - val apply = specializedApply.asTerm - val specializedDecl = - polyDefDef(apply, trefs => vrefss => { - dt.rhs - .changeOwner(dt.symbol, apply) - .subst(dt.vparamss.flatten.map(_.symbol), vrefss.flatten.map(_.symbol)) + val cls = tree.symbol.enclosingClass.asClass + if (derivesFromFn012(cls)) { + val applyBuf = new mutable.ListBuffer[Tree] + val newBody = tree.body.mapConserve { + case dt: DefDef if dt.name == nme.apply && dt.vparamss.length == 1 => + val typeParams = dt.vparamss.head.map(_.symbol.info) + val retType = dt.tpe.widen.finalResultType + + val specName = specializedName(nme.apply, typeParams :+ retType) + val specializedApply = cls.info.decls.lookup(specName) + if (specializedApply.exists) { + val apply = specializedApply.asTerm + val specializedDecl = + polyDefDef(apply, trefs => vrefss => { + dt.rhs + .changeOwner(dt.symbol, apply) + .subst(dt.vparamss.flatten.map(_.symbol), vrefss.flatten.map(_.symbol)) + }) + applyBuf += specializedDecl + + // create a forwarding to the specialized apply + cpy.DefDef(dt)(rhs = { + tpd + .ref(apply) + .appliedToArgs(dt.vparamss.head.map(vparam => ref(vparam.symbol))) }) - applyBuf += specializedDecl + } else dt - // create a forwarding to the specialized apply - cpy.DefDef(dt)(rhs = { - tpd - .ref(apply) - .appliedToArgs(dt.vparamss.head.map(vparam => ref(vparam.symbol))) - }) - } else dt - - case x => x - } + case x => x + } - cpy.Template(tree)( - body = applyBuf.toList ++ newBody - ) + cpy.Template(tree)( + body = applyBuf.toList ::: newBody + ) + } else tree } /** Dispatch to specialized `apply`s in user code when available */ @@ -146,4 +148,9 @@ class SpecializeFunctions extends MiniPhase with InfoTransformer { private def specInterface(typeParams: List[Type])(implicit ctx: Context) = ctx.getClassIfDefined(functionName(typeParams).specializedFunction(typeParams.last, typeParams.init)) + + private def derivesFromFn012(sym: Symbol)(implicit ctx: Context): Boolean = + sym.derivesFrom(defn.FunctionClass(0)) || + sym.derivesFrom(defn.FunctionClass(1)) || + sym.derivesFrom(defn.FunctionClass(2)) }