From c1f58425aeffbab6226599a830da3845ac999093 Mon Sep 17 00:00:00 2001 From: AlexSikia Date: Sun, 1 Mar 2015 21:10:46 +0100 Subject: [PATCH 01/37] Add a TypeSpecializer Phase Adds `TypeSpecializer.scala`, a phase which runs just before erasure. --- out/.keep | 0 src/dotty/tools/dotc/Compiler.scala | 1 + src/dotty/tools/dotc/ast/TreeTypeMap.scala | 2 +- .../dotc/transform/FullParameterization.scala | 2 +- .../dotc/transform/TypeSpecializer.scala | 88 +++++++++++++++++++ 5 files changed, 91 insertions(+), 2 deletions(-) delete mode 100644 out/.keep create mode 100644 src/dotty/tools/dotc/transform/TypeSpecializer.scala diff --git a/out/.keep b/out/.keep deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index f753b7614a45..a09faf45d025 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -62,6 +62,7 @@ class Compiler { new ElimByName, new AugmentScala2Traits, new ResolveSuper), + List(new TypeSpecializer), List(new Erasure), List(new ElimErasedValueType, new VCElideAllocations, diff --git a/src/dotty/tools/dotc/ast/TreeTypeMap.scala b/src/dotty/tools/dotc/ast/TreeTypeMap.scala index d714a3d21f40..ea7f3a0f3720 100644 --- a/src/dotty/tools/dotc/ast/TreeTypeMap.scala +++ b/src/dotty/tools/dotc/ast/TreeTypeMap.scala @@ -60,7 +60,7 @@ final class TreeTypeMap( def mapType(tp: Type) = mapOwnerThis(typeMap(tp).substSym(substFrom, substTo)) - + private def updateDecls(prevStats: List[Tree], newStats: List[Tree]): Unit = if (prevStats.isEmpty) assert(newStats.isEmpty) else { diff --git a/src/dotty/tools/dotc/transform/FullParameterization.scala b/src/dotty/tools/dotc/transform/FullParameterization.scala index e9057e885c47..c123daabdb38 100644 --- a/src/dotty/tools/dotc/transform/FullParameterization.scala +++ b/src/dotty/tools/dotc/transform/FullParameterization.scala @@ -139,7 +139,7 @@ trait FullParameterization { * fully parameterized method definition derived from `originalDef`, which * has `derived` as symbol and `fullyParameterizedType(originalDef.symbol.info)` * as info. - * `abstractOverClass` defines weather the DefDef should abstract over type parameters + * `abstractOverClass` defines whether the DefDef should abstract over type parameters * of class that contained original defDef */ def fullyParameterizedDef(derived: TermSymbol, originalDef: DefDef, abstractOverClass: Boolean = true)(implicit ctx: Context): Tree = diff --git a/src/dotty/tools/dotc/transform/TypeSpecializer.scala b/src/dotty/tools/dotc/transform/TypeSpecializer.scala new file mode 100644 index 000000000000..3a1add813844 --- /dev/null +++ b/src/dotty/tools/dotc/transform/TypeSpecializer.scala @@ -0,0 +1,88 @@ +package dotty.tools.dotc.transform + +import dotty.tools.dotc.ast.TreeTypeMap +import dotty.tools.dotc.ast.tpd._ +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.Flags +import dotty.tools.dotc.core.Types._ +import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, MiniPhaseTransform} +import dotty.tools.dotc.core.Decorators._ +import scala.collection.mutable.{ListBuffer, ArrayBuffer} + +class TypeSpecializer extends MiniPhaseTransform { + + override def phaseName = "Type Specializer" + + final val maxTparamsToSpecialize = 2 + + private final def specialisedTypes(implicit ctx: Context) = + Map(ctx.definitions.ByteType -> "$mcB$sp", + ctx.definitions.BooleanType -> "$mcZ$sp", + ctx.definitions.ShortType -> "$mcS$sp", + ctx.definitions.IntType -> "$mcI$sp", + ctx.definitions.LongType -> "$mcJ$sp", + ctx.definitions.FloatType -> "$mcF$sp", + ctx.definitions.DoubleType -> "$mcD$sp", + ctx.definitions.CharType -> "$mcC$sp", + ctx.definitions.UnitType -> "$mcV$sp") + + override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = { + + def rewireType(tpe: Type) = tpe match { + case tpe: TermRef => tpe.widen + case _ => tpe + } + + tree.tpe.widen match { + + case poly: PolyType if !(tree.symbol.isPrimaryConstructor + || (tree.symbol is Flags.Label) + || (tree.tparams.length > maxTparamsToSpecialize)) => { + val origTParams = tree.tparams.map(_.symbol) + val origVParams = tree.vparamss.flatten.map(_.symbol) + println(s"specializing ${tree.symbol} for Tparams: ${origTParams.length}") + + def specialize(instatiations: collection.mutable.ListBuffer[Type], names: collection.mutable.ArrayBuffer[String]): Tree = { + + val newSym = ctx.newSymbol(tree.symbol.owner, (tree.name + names.mkString).toTermName, tree.symbol.flags | Flags.Synthetic, poly.instantiate(instatiations.toList)) + polyDefDef(newSym, { tparams => vparams => { + assert(tparams.isEmpty) + new TreeTypeMap( + typeMap = _ + .substDealias(origTParams, instatiations.toList) + .subst(origVParams, vparams.flatten.map(_.tpe)), + oldOwners = tree.symbol :: Nil, + newOwners = newSym :: Nil + ).transform(tree.rhs) + } + }) + } + + def generateSpecializations(remainingTParams: List[TypeDef]) + (instatiated: ArrayBuffer[TypeDef], instatiations: ListBuffer[Type], + names: ArrayBuffer[String]): Iterable[Tree] = { + if (remainingTParams.nonEmpty) { + val typeToSpecialize = remainingTParams.head + specialisedTypes.flatMap { tpnme => + val tpe = tpnme._1 + val nme = tpnme._2 + instatiated.+=(typeToSpecialize) + instatiations.+=(tpe) + names.+=(nme) + val r = generateSpecializations(remainingTParams.tail)(instatiated, instatiations, names) + instatiated.drop(1) + instatiations.drop(1) + names.drop(1) + r + } + } else + List(specialize(instatiations, names)) + } + + + Thicket(tree :: generateSpecializations(tree.tparams)(ArrayBuffer.empty, ListBuffer.empty, ArrayBuffer.empty).toList) + } + case _ => tree + } + } +} \ No newline at end of file From cd8eae0182d4a21f506f2de5b032dbc5e429e52a Mon Sep 17 00:00:00 2001 From: AlexSikia Date: Tue, 17 Mar 2015 13:29:31 +0100 Subject: [PATCH 02/37] Move TypeSpecializer before ElimByName and implement check for @specialized --- src/dotty/tools/dotc/Compiler.scala | 2 +- .../tools/dotc/config/ScalaSettings.scala | 2 + src/dotty/tools/dotc/core/Definitions.scala | 1 + .../dotc/transform/TypeSpecializer.scala | 64 +++++++++++-------- 4 files changed, 41 insertions(+), 28 deletions(-) diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index a09faf45d025..4e1b34b9b2b4 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -53,6 +53,7 @@ class Compiler { List(new PatternMatcher, new ExplicitOuter, new Splitter), + List(new TypeSpecializer), List(new VCInlineMethods, new SeqLiterals, new InterceptedMethods, @@ -62,7 +63,6 @@ class Compiler { new ElimByName, new AugmentScala2Traits, new ResolveSuper), - List(new TypeSpecializer), List(new Erasure), List(new ElimErasedValueType, new VCElideAllocations, diff --git a/src/dotty/tools/dotc/config/ScalaSettings.scala b/src/dotty/tools/dotc/config/ScalaSettings.scala index 05fefc8b41f4..86cd24f1eec2 100644 --- a/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -152,6 +152,8 @@ class ScalaSettings extends Settings.SettingGroup { val YprintSyms = BooleanSetting("-Yprint-syms", "when printing trees print info in symbols instead of corresponding info in trees.") val YtestPickler = BooleanSetting("-Ytest-pickler", "self-test for pickling functionality; should be used with -Ystop-after:pickler") val YcheckReentrant = BooleanSetting("-Ycheck-reentrant", "check that compiled program does not contain vars that can be accessed from a global root.") + val Yspecialize = StringSetting("-Yspecialize","","Specialize all methods.","") // How should the second and fourth paramerters be initialised ? + def stop = YstopAfter /** Area-specific debug output. diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index fcd9ef224f10..1128be8d3af3 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -338,6 +338,7 @@ class Definitions { lazy val TransientAnnot = ctx.requiredClass("scala.transient") lazy val NativeAnnot = ctx.requiredClass("scala.native") lazy val ScalaStrictFPAnnot = ctx.requiredClass("scala.annotation.strictfp") + lazy val specializedAnnot = ctx.requiredClass("scala.specialized") // Annotation classes lazy val AliasAnnot = ctx.requiredClass("dotty.annotation.internal.Alias") diff --git a/src/dotty/tools/dotc/transform/TypeSpecializer.scala b/src/dotty/tools/dotc/transform/TypeSpecializer.scala index 3a1add813844..fb8d5dc9ab3c 100644 --- a/src/dotty/tools/dotc/transform/TypeSpecializer.scala +++ b/src/dotty/tools/dotc/transform/TypeSpecializer.scala @@ -3,18 +3,26 @@ package dotty.tools.dotc.transform import dotty.tools.dotc.ast.TreeTypeMap import dotty.tools.dotc.ast.tpd._ import dotty.tools.dotc.core.Contexts.Context -import dotty.tools.dotc.core.Flags +import dotty.tools.dotc.core.{Symbols, Flags} import dotty.tools.dotc.core.Types._ import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, MiniPhaseTransform} import dotty.tools.dotc.core.Decorators._ -import scala.collection.mutable.{ListBuffer, ArrayBuffer} +import scala.collection.mutable class TypeSpecializer extends MiniPhaseTransform { - override def phaseName = "Type Specializer" + override def phaseName = "specialize" final val maxTparamsToSpecialize = 2 - + + private val specializationRequests: mutable.HashMap[Symbol, List[List[Type]]] = mutable.HashMap.empty + + def registerSpecializationRequest(method: Symbol)(arguments: List[Type])(implicit ctx: Context) = { + assert(ctx.phaseId <= this.period.phaseId) + val prev = specializationRequests.getOrElse(method, List.empty) + specializationRequests.put(method, arguments :: prev) + } + private final def specialisedTypes(implicit ctx: Context) = Map(ctx.definitions.ByteType -> "$mcB$sp", ctx.definitions.BooleanType -> "$mcZ$sp", @@ -25,24 +33,30 @@ class TypeSpecializer extends MiniPhaseTransform { ctx.definitions.DoubleType -> "$mcD$sp", ctx.definitions.CharType -> "$mcC$sp", ctx.definitions.UnitType -> "$mcV$sp") - + + def shouldSpecializeForAll(sym: Symbols.Symbol)(implicit ctx: Context): Boolean = { + // either -Yspecialize:all is given, or sym has @specialize annotation + sym.denot.hasAnnotation(ctx.definitions.specializedAnnot) || (ctx.settings.Yspecialize.value == "all") + } + + def shouldSpecializeForSome(sym: Symbol)(implicit ctx: Context): List[List[Type]] = { + specializationRequests.getOrElse(sym, Nil) + } + + + + override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = { - def rewireType(tpe: Type) = tpe match { - case tpe: TermRef => tpe.widen - case _ => tpe - } - tree.tpe.widen match { case poly: PolyType if !(tree.symbol.isPrimaryConstructor - || (tree.symbol is Flags.Label) - || (tree.tparams.length > maxTparamsToSpecialize)) => { + || (tree.symbol is Flags.Label)) => { val origTParams = tree.tparams.map(_.symbol) val origVParams = tree.vparamss.flatten.map(_.symbol) println(s"specializing ${tree.symbol} for Tparams: ${origTParams.length}") - def specialize(instatiations: collection.mutable.ListBuffer[Type], names: collection.mutable.ArrayBuffer[String]): Tree = { + def specialize(instatiations: List[Type], names: List[String]): Tree = { val newSym = ctx.newSymbol(tree.symbol.owner, (tree.name + names.mkString).toTermName, tree.symbol.flags | Flags.Synthetic, poly.instantiate(instatiations.toList)) polyDefDef(newSym, { tparams => vparams => { @@ -58,29 +72,25 @@ class TypeSpecializer extends MiniPhaseTransform { }) } - def generateSpecializations(remainingTParams: List[TypeDef]) - (instatiated: ArrayBuffer[TypeDef], instatiations: ListBuffer[Type], - names: ArrayBuffer[String]): Iterable[Tree] = { + def generateSpecializations(remainingTParams: List[TypeDef], remainingBounds: List[TypeBounds]) + (instatiations: List[Type], + names: List[String]): Iterable[Tree] = { if (remainingTParams.nonEmpty) { val typeToSpecialize = remainingTParams.head - specialisedTypes.flatMap { tpnme => + val bounds = remainingBounds.head + specialisedTypes.filter{ tpnme => + bounds.contains(tpnme._1) + }.flatMap { tpnme => val tpe = tpnme._1 val nme = tpnme._2 - instatiated.+=(typeToSpecialize) - instatiations.+=(tpe) - names.+=(nme) - val r = generateSpecializations(remainingTParams.tail)(instatiated, instatiations, names) - instatiated.drop(1) - instatiations.drop(1) - names.drop(1) - r + generateSpecializations(remainingTParams.tail, remainingBounds.tail)(tpe :: instatiations, nme :: names) } } else - List(specialize(instatiations, names)) + List(specialize(instatiations.reverse, names.reverse)) } - Thicket(tree :: generateSpecializations(tree.tparams)(ArrayBuffer.empty, ListBuffer.empty, ArrayBuffer.empty).toList) + Thicket(tree :: generateSpecializations(tree.tparams, poly.paramBounds)(List.empty, List.empty).toList) } case _ => tree } From af2ff527895e432b78122c5a5f283dacd31c7423 Mon Sep 17 00:00:00 2001 From: AlexSikia Date: Wed, 3 Jun 2015 01:50:36 +0200 Subject: [PATCH 03/37] Add method specialization on specified Types. --- src/dotty/tools/dotc/core/Definitions.scala | 8 +-- .../dotc/transform/TypeSpecializer.scala | 60 +++++++++++++------ test/dotc/tests.scala | 6 +- tests/pos/specialization.scala | 6 ++ 4 files changed, 55 insertions(+), 25 deletions(-) create mode 100644 tests/pos/specialization.scala diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index 1128be8d3af3..12e0d1a4577b 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -329,10 +329,10 @@ class Definitions { lazy val NonLocalReturnControlClass = ctx.requiredClass("scala.runtime.NonLocalReturnControl") // Annotation base classes - lazy val AnnotationClass = ctx.requiredClass("scala.annotation.Annotation") - lazy val ClassfileAnnotationClass = ctx.requiredClass("scala.annotation.ClassfileAnnotation") - lazy val StaticAnnotationClass = ctx.requiredClass("scala.annotation.StaticAnnotation") - lazy val TailrecAnnotationClass = ctx.requiredClass("scala.annotation.tailrec") + lazy val AnnotationClass = ctx.requiredClass("scala.annotation.Annotation") + lazy val ClassfileAnnotationClass = ctx.requiredClass("scala.annotation.ClassfileAnnotation") + lazy val StaticAnnotationClass = ctx.requiredClass("scala.annotation.StaticAnnotation") + lazy val TailrecAnnotationClass = ctx.requiredClass("scala.annotation.tailrec") lazy val RemoteAnnot = ctx.requiredClass("scala.remote") lazy val SerialVersionUIDAnnot = ctx.requiredClass("scala.SerialVersionUID") lazy val TransientAnnot = ctx.requiredClass("scala.transient") diff --git a/src/dotty/tools/dotc/transform/TypeSpecializer.scala b/src/dotty/tools/dotc/transform/TypeSpecializer.scala index fb8d5dc9ab3c..580c6388bd0b 100644 --- a/src/dotty/tools/dotc/transform/TypeSpecializer.scala +++ b/src/dotty/tools/dotc/transform/TypeSpecializer.scala @@ -1,7 +1,9 @@ package dotty.tools.dotc.transform import dotty.tools.dotc.ast.TreeTypeMap +import dotty.tools.dotc.ast.Trees.SeqLiteral import dotty.tools.dotc.ast.tpd._ +import dotty.tools.dotc.core.Annotations.Annotation import dotty.tools.dotc.core.Contexts.Context import dotty.tools.dotc.core.{Symbols, Flags} import dotty.tools.dotc.core.Types._ @@ -15,15 +17,26 @@ class TypeSpecializer extends MiniPhaseTransform { final val maxTparamsToSpecialize = 2 - private val specializationRequests: mutable.HashMap[Symbol, List[List[Type]]] = mutable.HashMap.empty + private val specializationRequests: mutable.HashMap[Symbols.Symbol, List[List[Type]]] = mutable.HashMap.empty - def registerSpecializationRequest(method: Symbol)(arguments: List[Type])(implicit ctx: Context) = { - assert(ctx.phaseId <= this.period.phaseId) + def registerSpecializationRequest(method: Symbols.Symbol)(arguments: List[Type])(implicit ctx: Context) = { + //assert(ctx.phaseId <= this.period.phaseId) // This fails - why ? val prev = specializationRequests.getOrElse(method, List.empty) specializationRequests.put(method, arguments :: prev) } - private final def specialisedTypes(implicit ctx: Context) = + private final def name2SpecialisedType(implicit ctx: Context) = + Map("Byte" -> ctx.definitions.ByteType, + "Boolean" -> ctx.definitions.BooleanType, + "Short" -> ctx.definitions.ShortType, + "Int" -> ctx.definitions.IntType, + "Long" -> ctx.definitions.LongType, + "Float" -> ctx.definitions.FloatType, + "Double" -> ctx.definitions.DoubleType, + "Char" -> ctx.definitions.CharType, + "Unit" -> ctx.definitions.UnitType) + + private final def specialisedType2Suffix(implicit ctx: Context) = Map(ctx.definitions.ByteType -> "$mcB$sp", ctx.definitions.BooleanType -> "$mcZ$sp", ctx.definitions.ShortType -> "$mcS$sp", @@ -34,17 +47,28 @@ class TypeSpecializer extends MiniPhaseTransform { ctx.definitions.CharType -> "$mcC$sp", ctx.definitions.UnitType -> "$mcV$sp") - def shouldSpecializeForAll(sym: Symbols.Symbol)(implicit ctx: Context): Boolean = { - // either -Yspecialize:all is given, or sym has @specialize annotation - sym.denot.hasAnnotation(ctx.definitions.specializedAnnot) || (ctx.settings.Yspecialize.value == "all") + def specializeForAll(sym: Symbols.Symbol)(implicit ctx: Context): List[List[Type]] = { + registerSpecializationRequest(sym)(specialisedType2Suffix.keys.toList) + specializationRequests.getOrElse(sym, Nil) } - def shouldSpecializeForSome(sym: Symbol)(implicit ctx: Context): List[List[Type]] = { + def specializeForSome(sym: Symbols.Symbol)(annotationArgs: List[Type])(implicit ctx: Context): List[List[Type]] = { + registerSpecializationRequest(sym)(annotationArgs) + println(s"specializationRequests : $specializationRequests") specializationRequests.getOrElse(sym, Nil) } - - + def shouldSpecializeFor(sym: Symbols.Symbol)(implicit ctx: Context): List[List[Type]] = { + if (sym.denot.hasAnnotation(ctx.definitions.specializedAnnot)) { + val specAnnotation = sym.denot.getAnnotation(ctx.definitions.specializedAnnot).getOrElse(Nil) + specAnnotation.asInstanceOf[Annotation].arguments match { + case List(SeqLiteral(types)) => specializeForSome(sym)(types.map(tpeTree => name2SpecialisedType(ctx)(tpeTree.tpe.asInstanceOf[TermRef].name.toString()))) + case List() => specializeForAll(sym) + } + } + else if(ctx.settings.Yspecialize.value == "all") specializeForAll(sym) + else Nil + } override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = { @@ -54,7 +78,7 @@ class TypeSpecializer extends MiniPhaseTransform { || (tree.symbol is Flags.Label)) => { val origTParams = tree.tparams.map(_.symbol) val origVParams = tree.vparamss.flatten.map(_.symbol) - println(s"specializing ${tree.symbol} for Tparams: ${origTParams.length}") + println(s"specializing ${tree.symbol} for Tparams: ${origTParams}") def specialize(instatiations: List[Type], names: List[String]): Tree = { @@ -78,18 +102,18 @@ class TypeSpecializer extends MiniPhaseTransform { if (remainingTParams.nonEmpty) { val typeToSpecialize = remainingTParams.head val bounds = remainingBounds.head - specialisedTypes.filter{ tpnme => - bounds.contains(tpnme._1) - }.flatMap { tpnme => - val tpe = tpnme._1 - val nme = tpnme._2 + val specializeFor = shouldSpecializeFor(typeToSpecialize.symbol).flatten + println(s"types to specialize for are : $specializeFor") + + specializeFor.filter{ tpe => + bounds.contains(tpe) + }.flatMap { tpe => + val nme = specialisedType2Suffix(ctx)(tpe) generateSpecializations(remainingTParams.tail, remainingBounds.tail)(tpe :: instatiations, nme :: names) } } else List(specialize(instatiations.reverse, names.reverse)) } - - Thicket(tree :: generateSpecializations(tree.tparams, poly.paramBounds)(List.empty, List.empty).toList) } case _ => tree diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index b39d0e928951..536140446909 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -88,9 +88,9 @@ class tests extends CompilerTest { @Test def pos_nullarify = compileFile(posDir, "nullarify", args = "-Ycheck:nullarify" :: Nil) @Test def pos_subtyping = compileFile(posDir, "subtyping", twice) @Test def pos_t2613 = compileFile(posSpecialDir, "t2613")(allowDeepSubtypes) - @Test def pos_packageObj = compileFile(posDir, "i0239", twice) - @Test def pos_anonClassSubtyping = compileFile(posDir, "anonClassSubtyping", twice) - @Test def pos_extmethods = compileFile(posDir, "extmethods", twice) + @Test def pos_packageObj = compileFile(posDir, "i0239") + @Test def pos_anonClassSubtyping = compileFile(posDir, "anonClassSubtyping") + @Test def pos_specialization = compileFile(posDir, "specialization") @Test def pos_all = compileFiles(posDir) // twice omitted to make tests run faster diff --git a/tests/pos/specialization.scala b/tests/pos/specialization.scala new file mode 100644 index 000000000000..ff5c8052f9b3 --- /dev/null +++ b/tests/pos/specialization.scala @@ -0,0 +1,6 @@ +class specialization { + def printer[@specialized(Int, Long) T](a: T) = { + println(a) + } +} + From 5a1e4918f30c20e81db1bd32ecfc6f0533ef2f4d Mon Sep 17 00:00:00 2001 From: AlexSikia Date: Wed, 3 Jun 2015 01:51:45 +0200 Subject: [PATCH 04/37] Implement type specialization with specified Types. --- .../dotc/transform/TypeSpecializer.scala | 35 ++++++++++--------- test/dotc/tests.scala | 9 +++++ 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/src/dotty/tools/dotc/transform/TypeSpecializer.scala b/src/dotty/tools/dotc/transform/TypeSpecializer.scala index 580c6388bd0b..fccd56d11adc 100644 --- a/src/dotty/tools/dotc/transform/TypeSpecializer.scala +++ b/src/dotty/tools/dotc/transform/TypeSpecializer.scala @@ -20,7 +20,8 @@ class TypeSpecializer extends MiniPhaseTransform { private val specializationRequests: mutable.HashMap[Symbols.Symbol, List[List[Type]]] = mutable.HashMap.empty def registerSpecializationRequest(method: Symbols.Symbol)(arguments: List[Type])(implicit ctx: Context) = { - //assert(ctx.phaseId <= this.period.phaseId) // This fails - why ? + if(ctx.phaseId > this.treeTransformPhase.id) + assert(ctx.phaseId <= this.treeTransformPhase.id) val prev = specializationRequests.getOrElse(method, List.empty) specializationRequests.put(method, arguments :: prev) } @@ -49,6 +50,7 @@ class TypeSpecializer extends MiniPhaseTransform { def specializeForAll(sym: Symbols.Symbol)(implicit ctx: Context): List[List[Type]] = { registerSpecializationRequest(sym)(specialisedType2Suffix.keys.toList) + println("Specializing for all primitive types") specializationRequests.getOrElse(sym, Nil) } @@ -59,29 +61,31 @@ class TypeSpecializer extends MiniPhaseTransform { } def shouldSpecializeFor(sym: Symbols.Symbol)(implicit ctx: Context): List[List[Type]] = { - if (sym.denot.hasAnnotation(ctx.definitions.specializedAnnot)) { - val specAnnotation = sym.denot.getAnnotation(ctx.definitions.specializedAnnot).getOrElse(Nil) - specAnnotation.asInstanceOf[Annotation].arguments match { - case List(SeqLiteral(types)) => specializeForSome(sym)(types.map(tpeTree => name2SpecialisedType(ctx)(tpeTree.tpe.asInstanceOf[TermRef].name.toString()))) - case List() => specializeForAll(sym) + sym.denot.getAnnotation(ctx.definitions.specializedAnnot).getOrElse(Nil) match { + case annot: Annotation => + annot.arguments match { + case List(SeqLiteral(types)) => + specializeForSome(sym)(types.map(tpeTree => + name2SpecialisedType(ctx)(tpeTree.tpe.asInstanceOf[TermRef].name.toString()))) + case List() => specializeForAll(sym) + } + case nil => + if(ctx.settings.Yspecialize.value == "all") specializeForAll(sym) + else Nil } - } - else if(ctx.settings.Yspecialize.value == "all") specializeForAll(sym) - else Nil } override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = { tree.tpe.widen match { - + case poly: PolyType if !(tree.symbol.isPrimaryConstructor || (tree.symbol is Flags.Label)) => { val origTParams = tree.tparams.map(_.symbol) val origVParams = tree.vparamss.flatten.map(_.symbol) - println(s"specializing ${tree.symbol} for Tparams: ${origTParams}") + println(s"specializing ${tree.symbol} for Tparams: $origTParams") def specialize(instatiations: List[Type], names: List[String]): Tree = { - val newSym = ctx.newSymbol(tree.symbol.owner, (tree.name + names.mkString).toTermName, tree.symbol.flags | Flags.Synthetic, poly.instantiate(instatiations.toList)) polyDefDef(newSym, { tparams => vparams => { assert(tparams.isEmpty) @@ -102,10 +106,9 @@ class TypeSpecializer extends MiniPhaseTransform { if (remainingTParams.nonEmpty) { val typeToSpecialize = remainingTParams.head val bounds = remainingBounds.head - val specializeFor = shouldSpecializeFor(typeToSpecialize.symbol).flatten - println(s"types to specialize for are : $specializeFor") - - specializeFor.filter{ tpe => + shouldSpecializeFor(typeToSpecialize.symbol) + .flatten + .filter{ tpe => bounds.contains(tpe) }.flatMap { tpe => val nme = specialisedType2Suffix(ctx)(tpe) diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 536140446909..b30b9f98480a 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -94,6 +94,15 @@ class tests extends CompilerTest { @Test def pos_all = compileFiles(posDir) // twice omitted to make tests run faster + @Test def pos_specialization = compileFile(posDir, "specialization") + +// contains buggy tests + @Test def pos_all = compileFiles(posDir, failedOther) + + @Test def pos_SI7638 = compileFile(posDir, "SI-7638") + @Test def pos_SI7638a = compileFile(posDir, "SI-7638a") + + @Test def new_all = compileFiles(newDir, twice) @Test def neg_blockescapes() = compileFile(negDir, "blockescapesNeg", xerrors = 1) From d76f8bb2ff09c31866b5aa9aaa37e13bae959a14 Mon Sep 17 00:00:00 2001 From: AlexSikia Date: Wed, 3 Jun 2015 01:54:11 +0200 Subject: [PATCH 05/37] Add InfoTransformer Trait to TypeSpecializer Moves the new Symbols generation to the `transformInfo` method call, and builds a map of those new symbols. `transfomDefDef` later uses them to actually generate the specialized methods. --- .../dotc/transform/TypeSpecializer.scala | 153 +++++++++++------- test/dotc/tests.scala | 3 +- tests/pos/specialization.scala | 4 +- 3 files changed, 102 insertions(+), 58 deletions(-) diff --git a/src/dotty/tools/dotc/transform/TypeSpecializer.scala b/src/dotty/tools/dotc/transform/TypeSpecializer.scala index fccd56d11adc..0c6497fa3376 100644 --- a/src/dotty/tools/dotc/transform/TypeSpecializer.scala +++ b/src/dotty/tools/dotc/transform/TypeSpecializer.scala @@ -5,28 +5,22 @@ import dotty.tools.dotc.ast.Trees.SeqLiteral import dotty.tools.dotc.ast.tpd._ import dotty.tools.dotc.core.Annotations.Annotation import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.Decorators.StringDecorator +import dotty.tools.dotc.core.DenotTransformers.InfoTransformer +import dotty.tools.dotc.core.Names.TermName +import dotty.tools.dotc.core.Symbols.Symbol import dotty.tools.dotc.core.{Symbols, Flags} import dotty.tools.dotc.core.Types._ import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, MiniPhaseTransform} -import dotty.tools.dotc.core.Decorators._ import scala.collection.mutable -class TypeSpecializer extends MiniPhaseTransform { +class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { override def phaseName = "specialize" final val maxTparamsToSpecialize = 2 - private val specializationRequests: mutable.HashMap[Symbols.Symbol, List[List[Type]]] = mutable.HashMap.empty - - def registerSpecializationRequest(method: Symbols.Symbol)(arguments: List[Type])(implicit ctx: Context) = { - if(ctx.phaseId > this.treeTransformPhase.id) - assert(ctx.phaseId <= this.treeTransformPhase.id) - val prev = specializationRequests.getOrElse(method, List.empty) - specializationRequests.put(method, arguments :: prev) - } - - private final def name2SpecialisedType(implicit ctx: Context) = + private final def nameToSpecialisedType(implicit ctx: Context) = Map("Byte" -> ctx.definitions.ByteType, "Boolean" -> ctx.definitions.BooleanType, "Short" -> ctx.definitions.ShortType, @@ -37,19 +31,71 @@ class TypeSpecializer extends MiniPhaseTransform { "Char" -> ctx.definitions.CharType, "Unit" -> ctx.definitions.UnitType) - private final def specialisedType2Suffix(implicit ctx: Context) = + private final def specialisedTypeToSuffix(implicit ctx: Context) = Map(ctx.definitions.ByteType -> "$mcB$sp", - ctx.definitions.BooleanType -> "$mcZ$sp", - ctx.definitions.ShortType -> "$mcS$sp", - ctx.definitions.IntType -> "$mcI$sp", - ctx.definitions.LongType -> "$mcJ$sp", - ctx.definitions.FloatType -> "$mcF$sp", - ctx.definitions.DoubleType -> "$mcD$sp", - ctx.definitions.CharType -> "$mcC$sp", - ctx.definitions.UnitType -> "$mcV$sp") + ctx.definitions.BooleanType -> "$mcZ$sp", + ctx.definitions.ShortType -> "$mcS$sp", + ctx.definitions.IntType -> "$mcI$sp", + ctx.definitions.LongType -> "$mcJ$sp", + ctx.definitions.FloatType -> "$mcF$sp", + ctx.definitions.DoubleType -> "$mcD$sp", + ctx.definitions.CharType -> "$mcC$sp", + ctx.definitions.UnitType -> "$mcV$sp") + + private val specializationRequests: mutable.HashMap[Symbols.Symbol, List[List[Type]]] = mutable.HashMap.empty + + private val newSymbolMap: mutable.HashMap[TermName, (List[Symbols.TermSymbol], List[Type])] = mutable.HashMap.empty // Why does the typechecker require TermSymbol ? + + override def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = { + + tp.widen match { + case poly: PolyType if !(sym.isPrimaryConstructor + || (sym is Flags.Label)) => + + def generateSpecializations(remainingTParams: List[Type], remainingBounds: List[TypeBounds]) + (instantiations: List[Type], names: List[String])(implicit ctx: Context): Unit = { + if (remainingTParams.nonEmpty) { + val typeToSpecialize = remainingTParams.head + val bounds = remainingBounds.head + val a = shouldSpecializeFor(typeToSpecialize.typeSymbol) // TODO returns Nil because no annotations are found - elucidate + a.flatten + .filter { tpe => + bounds.contains(tpe) + }.foreach({ tpe => + val nme = specialisedTypeToSuffix(ctx)(tpe) + generateSpecializations(remainingTParams.tail, remainingBounds.tail)(tpe :: instantiations, nme :: names) + }) + } + else { + generateSpecializedSymbols(instantiations.reverse, names.reverse) + } + } + + def generateSpecializedSymbols(instantiations : List[Type], names: List[String])(implicit ctx: Context): Unit = { + val newSym = ctx.newSymbol(sym.owner, (sym.name + names.mkString).toTermName, sym.flags | Flags.Synthetic, poly.instantiate(instantiations.toList)) + ctx.enter(newSym) // TODO check frozen flag ? + val prev = newSymbolMap.getOrElse(sym.name.toTermName, (Nil, Nil)) + val newSyms = newSym :: prev._1 + newSymbolMap.put(sym.name.toTermName, (newSyms, instantiations)) // Could `.put(...)` bring up (mutability) issues ? + } + val origTParams = poly.resType.paramTypess.flatten // Is this really what is needed ? + val bounds = poly.paramBounds + generateSpecializations(origTParams, bounds)(List.empty, List.empty) + tp + case _ => + tp + } + } + + def registerSpecializationRequest(method: Symbols.Symbol)(arguments: List[Type])(implicit ctx: Context) = { + if(ctx.phaseId > this.treeTransformPhase.id) + assert(ctx.phaseId <= this.treeTransformPhase.id) + val prev = specializationRequests.getOrElse(method, List.empty) + specializationRequests.put(method, arguments :: prev) + } def specializeForAll(sym: Symbols.Symbol)(implicit ctx: Context): List[List[Type]] = { - registerSpecializationRequest(sym)(specialisedType2Suffix.keys.toList) + registerSpecializationRequest(sym)(specialisedTypeToSuffix.keys.toList) println("Specializing for all primitive types") specializationRequests.getOrElse(sym, Nil) } @@ -66,7 +112,7 @@ class TypeSpecializer extends MiniPhaseTransform { annot.arguments match { case List(SeqLiteral(types)) => specializeForSome(sym)(types.map(tpeTree => - name2SpecialisedType(ctx)(tpeTree.tpe.asInstanceOf[TermRef].name.toString()))) + nameToSpecialisedType(ctx)(tpeTree.tpe.asInstanceOf[TermRef].name.toString()))) case List() => specializeForAll(sym) } case nil => @@ -80,46 +126,43 @@ class TypeSpecializer extends MiniPhaseTransform { tree.tpe.widen match { case poly: PolyType if !(tree.symbol.isPrimaryConstructor - || (tree.symbol is Flags.Label)) => { + || (tree.symbol is Flags.Label)) => val origTParams = tree.tparams.map(_.symbol) val origVParams = tree.vparamss.flatten.map(_.symbol) println(s"specializing ${tree.symbol} for Tparams: $origTParams") - def specialize(instatiations: List[Type], names: List[String]): Tree = { - val newSym = ctx.newSymbol(tree.symbol.owner, (tree.name + names.mkString).toTermName, tree.symbol.flags | Flags.Synthetic, poly.instantiate(instatiations.toList)) - polyDefDef(newSym, { tparams => vparams => { - assert(tparams.isEmpty) - new TreeTypeMap( - typeMap = _ - .substDealias(origTParams, instatiations.toList) - .subst(origVParams, vparams.flatten.map(_.tpe)), - oldOwners = tree.symbol :: Nil, - newOwners = newSym :: Nil - ).transform(tree.rhs) + def specialize(instantiations: List[Type]): List[Tree] = { + newSymbolMap(tree.name) match { + case newSyms: (List[Symbol], List[Type]) => + newSyms._1.map{newSym => + polyDefDef(newSym, { tparams => vparams => { + assert(tparams.isEmpty) + new TreeTypeMap( + typeMap = _ + .substDealias(origTParams, instantiations.toList) + .subst(origVParams, vparams.flatten.map(_.tpe)), + oldOwners = tree.symbol :: Nil, + newOwners = newSym :: Nil + ).transform(tree.rhs) + } + })} + case nil => + List() } - }) } - def generateSpecializations(remainingTParams: List[TypeDef], remainingBounds: List[TypeBounds]) - (instatiations: List[Type], - names: List[String]): Iterable[Tree] = { - if (remainingTParams.nonEmpty) { - val typeToSpecialize = remainingTParams.head - val bounds = remainingBounds.head - shouldSpecializeFor(typeToSpecialize.symbol) - .flatten - .filter{ tpe => - bounds.contains(tpe) - }.flatMap { tpe => - val nme = specialisedType2Suffix(ctx)(tpe) - generateSpecializations(remainingTParams.tail, remainingBounds.tail)(tpe :: instatiations, nme :: names) - } - } else - List(specialize(instatiations.reverse, names.reverse)) - } - Thicket(tree :: generateSpecializations(tree.tparams, poly.paramBounds)(List.empty, List.empty).toList) - } + val specializedMethods: List[Tree] = (for (inst <- newSymbolMap.keys) yield specialize(newSymbolMap(inst)._2)).flatten.toList + Thicket(tree :: specializedMethods) + case _ => tree } } + + def transformTypeOfTree(tree: Tree): Tree = { + tree + } + + override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo): Tree = transformTypeOfTree(tree) + override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo): Tree = transformTypeOfTree(tree) + } \ No newline at end of file diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index b30b9f98480a..542c69fdfc2a 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -90,13 +90,13 @@ class tests extends CompilerTest { @Test def pos_t2613 = compileFile(posSpecialDir, "t2613")(allowDeepSubtypes) @Test def pos_packageObj = compileFile(posDir, "i0239") @Test def pos_anonClassSubtyping = compileFile(posDir, "anonClassSubtyping") + @Test def pos_specialization = compileFile(posDir, "specialization") @Test def pos_all = compileFiles(posDir) // twice omitted to make tests run faster @Test def pos_specialization = compileFile(posDir, "specialization") -// contains buggy tests @Test def pos_all = compileFiles(posDir, failedOther) @Test def pos_SI7638 = compileFile(posDir, "SI-7638") @@ -207,4 +207,5 @@ class tests extends CompilerTest { val javaDir = "./tests/pos/java-interop/" @Test def java_all = compileFiles(javaDir, twice) //@Test def dotc_compilercommand = compileFile(dotcDir + "config/", "CompilerCommand") + } diff --git a/tests/pos/specialization.scala b/tests/pos/specialization.scala index ff5c8052f9b3..f2fa8afda228 100644 --- a/tests/pos/specialization.scala +++ b/tests/pos/specialization.scala @@ -1,6 +1,6 @@ class specialization { - def printer[@specialized(Int, Long) T](a: T) = { - println(a) + def printer[@specialized(Int, Long) T, U](a: T, b:U) = { + println(a.toString + b.toString) } } From 7817fdeed8a5ad435f4e9b42b0d85d44753a3952 Mon Sep 17 00:00:00 2001 From: AlexSikia Date: Mon, 20 Apr 2015 11:18:16 +0200 Subject: [PATCH 06/37] Add specialized methods dispatch --- .../tools/dotc/config/ScalaSettings.scala | 2 +- .../dotc/transform/TypeSpecializer.scala | 198 +++++++++++------- test/dotc/tests.scala | 12 ++ tests/pos/specialization.scala | 13 +- 4 files changed, 141 insertions(+), 84 deletions(-) diff --git a/src/dotty/tools/dotc/config/ScalaSettings.scala b/src/dotty/tools/dotc/config/ScalaSettings.scala index 86cd24f1eec2..cd80e54ce278 100644 --- a/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -152,7 +152,7 @@ class ScalaSettings extends Settings.SettingGroup { val YprintSyms = BooleanSetting("-Yprint-syms", "when printing trees print info in symbols instead of corresponding info in trees.") val YtestPickler = BooleanSetting("-Ytest-pickler", "self-test for pickling functionality; should be used with -Ystop-after:pickler") val YcheckReentrant = BooleanSetting("-Ycheck-reentrant", "check that compiled program does not contain vars that can be accessed from a global root.") - val Yspecialize = StringSetting("-Yspecialize","","Specialize all methods.","") // How should the second and fourth paramerters be initialised ? + val Yspecialize = StringSetting("-Yspecialize","all","Specialize all methods.", "all") def stop = YstopAfter diff --git a/src/dotty/tools/dotc/transform/TypeSpecializer.scala b/src/dotty/tools/dotc/transform/TypeSpecializer.scala index 0c6497fa3376..7fc2510605fe 100644 --- a/src/dotty/tools/dotc/transform/TypeSpecializer.scala +++ b/src/dotty/tools/dotc/transform/TypeSpecializer.scala @@ -1,13 +1,13 @@ package dotty.tools.dotc.transform -import dotty.tools.dotc.ast.TreeTypeMap -import dotty.tools.dotc.ast.Trees.SeqLiteral +import dotty.tools.dotc.ast.{tpd, TreeTypeMap} +import dotty.tools.dotc.ast.Trees.{TypeApply, SeqLiteral} import dotty.tools.dotc.ast.tpd._ import dotty.tools.dotc.core.Annotations.Annotation import dotty.tools.dotc.core.Contexts.Context import dotty.tools.dotc.core.Decorators.StringDecorator import dotty.tools.dotc.core.DenotTransformers.InfoTransformer -import dotty.tools.dotc.core.Names.TermName +import dotty.tools.dotc.core.Names.Name import dotty.tools.dotc.core.Symbols.Symbol import dotty.tools.dotc.core.{Symbols, Flags} import dotty.tools.dotc.core.Types._ @@ -42,51 +42,82 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { ctx.definitions.CharType -> "$mcC$sp", ctx.definitions.UnitType -> "$mcV$sp") + private def primitiveTypes(implicit ctx: Context) = + List(ctx.definitions.ByteType, + ctx.definitions.BooleanType, + ctx.definitions.ShortType, + ctx.definitions.IntType, + ctx.definitions.LongType, + ctx.definitions.FloatType, + ctx.definitions.DoubleType, + ctx.definitions.CharType, + ctx.definitions.UnitType + ) + private val specializationRequests: mutable.HashMap[Symbols.Symbol, List[List[Type]]] = mutable.HashMap.empty - private val newSymbolMap: mutable.HashMap[TermName, (List[Symbols.TermSymbol], List[Type])] = mutable.HashMap.empty // Why does the typechecker require TermSymbol ? + private val newSymbolMap: mutable.HashMap[Symbol, List[mutable.HashMap[List[Type], Symbols.Symbol]]] = mutable.HashMap.empty override def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = { + def generateSpecializations(remainingTParams: List[Name], remainingBounds: List[TypeBounds]) + (instantiations: List[Type], names: List[String], poly: PolyType, decl: Symbol) + (implicit ctx: Context): List[Symbol] = { + if (remainingTParams.nonEmpty) { + val bounds = remainingBounds.head + val specTypes = primitiveTypes.filter{ tpe => bounds.contains(tpe)} + val specializations = (for (tpe <- specTypes) yield { + generateSpecializations(remainingTParams.tail, remainingBounds.tail)(tpe :: instantiations, specialisedTypeToSuffix(ctx)(tpe) :: names, poly, decl) + }).flatten + specializations + } + else { + generateSpecializedSymbols(instantiations.reverse, names.reverse, poly, decl) + } + } + def generateSpecializedSymbols(instantiations: List[Type], names: List[String], poly: PolyType, decl: Symbol) + (implicit ctx: Context): List[Symbol] = { + val newSym = + ctx.newSymbol(decl.owner, (decl.name + names.mkString).toTermName, + decl.flags | Flags.Synthetic, poly.instantiate(instantiations.toList)) // Who should the owner be ? decl.owner ? sym ? sym.owner ? ctx.owner ? + // TODO I think there might be a bug in the assertion at dotty.tools.dotc.transform.TreeChecker$Checker.dotty$tools$dotc$transform$TreeChecker$Checker$$checkOwner(TreeChecker.scala:244) + // Shouldn't the owner remain the original one ? In this instance, the assertion always expects the owner to be `class specialization` (the test I run), even for methods that aren't + //defined by the test itself, such as `instanceOf` (to which my implementation gives owner `class Any`). + val prevMaps = newSymbolMap.getOrElse(decl, List()).reverse + val newMap: mutable.HashMap[List[Type], Symbols.Symbol] = mutable.HashMap(instantiations -> newSym) + newSymbolMap.put(decl, (newMap :: prevMaps.reverse).reverse) + (newSym :: prevMaps.flatMap(_.values).reverse).reverse // All those reverse are probably useless + } - tp.widen match { - case poly: PolyType if !(sym.isPrimaryConstructor - || (sym is Flags.Label)) => - - def generateSpecializations(remainingTParams: List[Type], remainingBounds: List[TypeBounds]) - (instantiations: List[Type], names: List[String])(implicit ctx: Context): Unit = { - if (remainingTParams.nonEmpty) { - val typeToSpecialize = remainingTParams.head - val bounds = remainingBounds.head - val a = shouldSpecializeFor(typeToSpecialize.typeSymbol) // TODO returns Nil because no annotations are found - elucidate - a.flatten - .filter { tpe => - bounds.contains(tpe) - }.foreach({ tpe => - val nme = specialisedTypeToSuffix(ctx)(tpe) - generateSpecializations(remainingTParams.tail, remainingBounds.tail)(tpe :: instantiations, nme :: names) - }) - } - else { - generateSpecializedSymbols(instantiations.reverse, names.reverse) + if((sym ne ctx.definitions.ScalaPredefModule.moduleClass) && !(sym is Flags.Package) && !sym.isAnonymousClass) { + sym.info match { + case classInfo: ClassInfo => + val newDecls = classInfo.decls.flatMap(decl => { + if (shouldSpecialize(decl)) { + decl.info.widen match { + case poly: PolyType => + if (poly.paramNames.length <= maxTparamsToSpecialize && poly.paramNames.length > 0) + generateSpecializations(poly.paramNames, poly.paramBounds)(List.empty, List.empty, poly, decl) + else Nil + case nil => Nil + } + } else Nil + }) + if (newDecls.nonEmpty) { + val decls = classInfo.decls.cloneScope + newDecls.foreach(decls.enter) + classInfo.derivedClassInfo(decls = decls) } - } - - def generateSpecializedSymbols(instantiations : List[Type], names: List[String])(implicit ctx: Context): Unit = { - val newSym = ctx.newSymbol(sym.owner, (sym.name + names.mkString).toTermName, sym.flags | Flags.Synthetic, poly.instantiate(instantiations.toList)) - ctx.enter(newSym) // TODO check frozen flag ? - val prev = newSymbolMap.getOrElse(sym.name.toTermName, (Nil, Nil)) - val newSyms = newSym :: prev._1 - newSymbolMap.put(sym.name.toTermName, (newSyms, instantiations)) // Could `.put(...)` bring up (mutability) issues ? - } - val origTParams = poly.resType.paramTypess.flatten // Is this really what is needed ? - val bounds = poly.paramBounds - generateSpecializations(origTParams, bounds)(List.empty, List.empty) - tp - case _ => - tp - } + case nil => + } + tp + } else tp } + def shouldSpecialize(decl: Symbol)(implicit ctx: Context): Boolean = + specializationRequests.contains(decl) || + (ctx.settings.Yspecialize.value != "" && decl.name.contains(ctx.settings.Yspecialize.value)) || + ctx.settings.Yspecialize.value == "all" + def registerSpecializationRequest(method: Symbols.Symbol)(arguments: List[Type])(implicit ctx: Context) = { if(ctx.phaseId > this.treeTransformPhase.id) assert(ctx.phaseId <= this.treeTransformPhase.id) @@ -95,7 +126,7 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { } def specializeForAll(sym: Symbols.Symbol)(implicit ctx: Context): List[List[Type]] = { - registerSpecializationRequest(sym)(specialisedTypeToSuffix.keys.toList) + registerSpecializationRequest(sym)(primitiveTypes) println("Specializing for all primitive types") specializationRequests.getOrElse(sym, Nil) } @@ -106,19 +137,19 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { specializationRequests.getOrElse(sym, Nil) } - def shouldSpecializeFor(sym: Symbols.Symbol)(implicit ctx: Context): List[List[Type]] = { - sym.denot.getAnnotation(ctx.definitions.specializedAnnot).getOrElse(Nil) match { - case annot: Annotation => - annot.arguments match { - case List(SeqLiteral(types)) => - specializeForSome(sym)(types.map(tpeTree => - nameToSpecialisedType(ctx)(tpeTree.tpe.asInstanceOf[TermRef].name.toString()))) - case List() => specializeForAll(sym) - } - case nil => - if(ctx.settings.Yspecialize.value == "all") specializeForAll(sym) - else Nil - } + def specializeFor(sym: Symbols.Symbol)(implicit ctx: Context): List[List[Type]] = { + sym.denot.getAnnotation(ctx.definitions.specializedAnnot).getOrElse(Nil) match { + case annot: Annotation => + annot.arguments match { + case List(SeqLiteral(types)) => + specializeForSome(sym)(types.map(tpeTree => //tpeTree.tpe.widen)) + nameToSpecialisedType(ctx)(tpeTree.tpe.asInstanceOf[TermRef].name.toString()))) // Not sure how to match TermRefs rather than types. comment on line above was an attempt. + case List() => specializeForAll(sym) + } + case nil => + if(ctx.settings.Yspecialize.value == "all") {println("Yspecialize set to all"); specializeForAll(sym) } + else Nil + } } override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = { @@ -126,43 +157,48 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { tree.tpe.widen match { case poly: PolyType if !(tree.symbol.isPrimaryConstructor - || (tree.symbol is Flags.Label)) => + || (tree.symbol is Flags.Label)) => val origTParams = tree.tparams.map(_.symbol) val origVParams = tree.vparamss.flatten.map(_.symbol) println(s"specializing ${tree.symbol} for Tparams: $origTParams") - def specialize(instantiations: List[Type]): List[Tree] = { - newSymbolMap(tree.name) match { - case newSyms: (List[Symbol], List[Type]) => - newSyms._1.map{newSym => - polyDefDef(newSym, { tparams => vparams => { - assert(tparams.isEmpty) - new TreeTypeMap( - typeMap = _ - .substDealias(origTParams, instantiations.toList) - .subst(origVParams, vparams.flatten.map(_.tpe)), - oldOwners = tree.symbol :: Nil, - newOwners = newSym :: Nil - ).transform(tree.rhs) - } - })} - case nil => - List() + def specialize(decl : Symbol): List[Tree] = { + val declSpecs = newSymbolMap(decl) + val newSyms = declSpecs.map(_.values).flatten + /*for (newSym <- newSyms) { + println(newSym) + }*/ + val instantiations = declSpecs.flatMap(_.keys).flatten + newSyms.map{newSym => + polyDefDef(newSym.asTerm, { tparams => vparams => { + assert(tparams.isEmpty) + //println(newSym + " ; " + origVParams + " ; " + vparams + " ; " + vparams.flatten + " ; " + vparams.flatten.map(_.tpe)) + new TreeTypeMap( //TODO Figure out what is happening with newSym. Why do some symbols have unmatching vparams and origVParams ? + typeMap = _ + .substDealias(origTParams, instantiations) + .subst(origVParams, vparams.flatten.map(_.tpe)), + oldOwners = tree.symbol :: Nil, + newOwners = newSym :: Nil + ).transform(tree.rhs) + }}) } } - - val specializedMethods: List[Tree] = (for (inst <- newSymbolMap.keys) yield specialize(newSymbolMap(inst)._2)).flatten.toList + //specializeFor(tree.symbol) -> necessary ? This registers specialization requests, but do they still make sense at this point ? Symbols have already been generated + val specializedMethods = newSymbolMap.keys.map(specialize).flatten.toList Thicket(tree :: specializedMethods) - case _ => tree } } - def transformTypeOfTree(tree: Tree): Tree = { + override def transformTypeApply(tree: tpd.TypeApply)(implicit ctx: Context, info: TransformerInfo): Tree = { + val TypeApply(fun,args) = tree + val newSymInfo = newSymbolMap(fun.symbol).flatten.toMap + val specializationType: List[Type] = args.map(_.tpe.asInstanceOf[TypeVar].instanceOpt) + val t = fun.symbol.info.decls + if (t.nonEmpty) { + t.cloneScope.lookupEntry(args.head.symbol.name) + val newSym = newSymInfo(specializationType) + } tree } - - override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo): Tree = transformTypeOfTree(tree) - override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo): Tree = transformTypeOfTree(tree) - -} \ No newline at end of file +} diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 542c69fdfc2a..e3d460c7247e 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -174,6 +174,15 @@ class tests extends CompilerTest { @Test def dotc_transform = compileDir(dotcDir, "transform")// twice omitted to make tests run faster + val javaDir = "./tests/pos/java-interop/" + @Test def java_all = compileFiles(javaDir) + + @Test def pos_specialization = compileFile(posDir, "specialization") + + //@Test def dotc_compilercommand = compileFile(dotcDir + "tools/dotc/config/", "CompilerCommand") +//@ Test def dotc_transform = compileDir(dotcDir + "tools/dotc/transform", failedbyName) + + @Test def dotc_parsing = compileDir(dotcDir, "parsing") // twice omitted to make tests run faster @Test def dotc_printing = compileDir(dotcDir, "printing") // twice omitted to make tests run faster @@ -206,6 +215,9 @@ class tests extends CompilerTest { val javaDir = "./tests/pos/java-interop/" @Test def java_all = compileFiles(javaDir, twice) + + @Test def pos_specialization = compileFile(posDir, "specialization") + //@Test def dotc_compilercommand = compileFile(dotcDir + "config/", "CompilerCommand") } diff --git a/tests/pos/specialization.scala b/tests/pos/specialization.scala index f2fa8afda228..bed233a7d0aa 100644 --- a/tests/pos/specialization.scala +++ b/tests/pos/specialization.scala @@ -1,6 +1,15 @@ class specialization { - def printer[@specialized(Int, Long) T, U](a: T, b:U) = { + def printer1[@specialized(Int, Long) T](a: T) = { + println(a.toString) + } + + def printer2[@specialized(Int, Long) T, U](a: T, b: U) = { println(a.toString + b.toString) } + def print(a: Int) = { + printer1(a) + println(" ---- ") + printer2(a,a) + } + print(9) } - From 3366e4537a16b00a9e15d633faf82b056423da3f Mon Sep 17 00:00:00 2001 From: AlexSikia Date: Wed, 3 Jun 2015 01:59:14 +0200 Subject: [PATCH 07/37] Add specialised method dispatching Implementation of specialized methods dispatching in `transformTypeApply`. If several specialised variants fit (e.g. Nothing is a subtype of all primitive types), the compiler defaults to not specialising. Currently, all specialization is done through the Yspecialize:all setting, as some anotations (including `@specialized`) are lost earlier in the pipeline. Various bugs corrected in generation of specialized method symbols. --- .../tools/dotc/config/ScalaSettings.scala | 2 +- .../dotc/transform/TypeSpecializer.scala | 149 +++++++++++------- test/dotc/tests.scala | 15 +- 3 files changed, 109 insertions(+), 57 deletions(-) diff --git a/src/dotty/tools/dotc/config/ScalaSettings.scala b/src/dotty/tools/dotc/config/ScalaSettings.scala index cd80e54ce278..1baee9732f8f 100644 --- a/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -33,7 +33,7 @@ class ScalaSettings extends Settings.SettingGroup { val usejavacp = BooleanSetting("-usejavacp", "Utilize the java.class.path in classpath resolution.") val verbose = BooleanSetting("-verbose", "Output messages about what the compiler is doing.") val version = BooleanSetting("-version", "Print product version and exit.") - val pageWidth = IntSetting("-pagewidth", "Set page width", 80) + val pageWidth = IntSetting("-pagewidth", "Set page width", 160) val jvmargs = PrefixSetting("-J", "-J", "Pass directly to the runtime system.") val defines = PrefixSetting("-Dproperty=value", "-D", "Pass -Dproperty=value directly to the runtime system.") diff --git a/src/dotty/tools/dotc/transform/TypeSpecializer.scala b/src/dotty/tools/dotc/transform/TypeSpecializer.scala index 7fc2510605fe..9041932122f0 100644 --- a/src/dotty/tools/dotc/transform/TypeSpecializer.scala +++ b/src/dotty/tools/dotc/transform/TypeSpecializer.scala @@ -1,8 +1,7 @@ package dotty.tools.dotc.transform import dotty.tools.dotc.ast.{tpd, TreeTypeMap} -import dotty.tools.dotc.ast.Trees.{TypeApply, SeqLiteral} -import dotty.tools.dotc.ast.tpd._ +import dotty.tools.dotc.ast.Trees._ import dotty.tools.dotc.core.Annotations.Annotation import dotty.tools.dotc.core.Contexts.Context import dotty.tools.dotc.core.Decorators.StringDecorator @@ -13,9 +12,10 @@ import dotty.tools.dotc.core.{Symbols, Flags} import dotty.tools.dotc.core.Types._ import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, MiniPhaseTransform} import scala.collection.mutable +import dotty.tools.dotc.core.StdNames.nme class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { - + import tpd._ override def phaseName = "specialize" final val maxTparamsToSpecialize = 2 @@ -56,17 +56,21 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { private val specializationRequests: mutable.HashMap[Symbols.Symbol, List[List[Type]]] = mutable.HashMap.empty - private val newSymbolMap: mutable.HashMap[Symbol, List[mutable.HashMap[List[Type], Symbols.Symbol]]] = mutable.HashMap.empty + /** + * A map that links symbols to their specialized variants. + * Each symbol maps to another as map, from the list of specialization types to the specialized symbol. + */ + private val newSymbolMap: mutable.HashMap[Symbol, mutable.HashMap[List[Type], Symbols.Symbol]] = mutable.HashMap.empty override def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = { - def generateSpecializations(remainingTParams: List[Name], remainingBounds: List[TypeBounds]) + + def generateSpecializations(remainingTParams: List[Name], remainingBounds: List[TypeBounds], specTypes: List[Type]) (instantiations: List[Type], names: List[String], poly: PolyType, decl: Symbol) (implicit ctx: Context): List[Symbol] = { if (remainingTParams.nonEmpty) { val bounds = remainingBounds.head - val specTypes = primitiveTypes.filter{ tpe => bounds.contains(tpe)} val specializations = (for (tpe <- specTypes) yield { - generateSpecializations(remainingTParams.tail, remainingBounds.tail)(tpe :: instantiations, specialisedTypeToSuffix(ctx)(tpe) :: names, poly, decl) + generateSpecializations(remainingTParams.tail, remainingBounds.tail, specTypes)(tpe :: instantiations, specialisedTypeToSuffix(ctx)(tpe) :: names, poly, decl) }).flatten specializations } @@ -78,25 +82,27 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { (implicit ctx: Context): List[Symbol] = { val newSym = ctx.newSymbol(decl.owner, (decl.name + names.mkString).toTermName, - decl.flags | Flags.Synthetic, poly.instantiate(instantiations.toList)) // Who should the owner be ? decl.owner ? sym ? sym.owner ? ctx.owner ? - // TODO I think there might be a bug in the assertion at dotty.tools.dotc.transform.TreeChecker$Checker.dotty$tools$dotc$transform$TreeChecker$Checker$$checkOwner(TreeChecker.scala:244) - // Shouldn't the owner remain the original one ? In this instance, the assertion always expects the owner to be `class specialization` (the test I run), even for methods that aren't - //defined by the test itself, such as `instanceOf` (to which my implementation gives owner `class Any`). - val prevMaps = newSymbolMap.getOrElse(decl, List()).reverse - val newMap: mutable.HashMap[List[Type], Symbols.Symbol] = mutable.HashMap(instantiations -> newSym) - newSymbolMap.put(decl, (newMap :: prevMaps.reverse).reverse) - (newSym :: prevMaps.flatMap(_.values).reverse).reverse // All those reverse are probably useless + decl.flags | Flags.Synthetic, poly.instantiate(instantiations.toList)) + val map = newSymbolMap.getOrElse(decl, mutable.HashMap.empty) + map.put(instantiations, newSym) + newSymbolMap.put(decl, map) + map.values.toList } - if((sym ne ctx.definitions.ScalaPredefModule.moduleClass) && !(sym is Flags.Package) && !sym.isAnonymousClass) { + if((sym ne ctx.definitions.ScalaPredefModule.moduleClass) && + !(sym is Flags.Package) && + !sym.isAnonymousClass && + !(sym.name == nme.asInstanceOf_)) { sym.info match { case classInfo: ClassInfo => - val newDecls = classInfo.decls.flatMap(decl => { + val newDecls = classInfo.decls.filterNot(_.isConstructor/*isPrimaryConstructor*/).flatMap(decl => { if (shouldSpecialize(decl)) { decl.info.widen match { case poly: PolyType => - if (poly.paramNames.length <= maxTparamsToSpecialize && poly.paramNames.length > 0) - generateSpecializations(poly.paramNames, poly.paramBounds)(List.empty, List.empty, poly, decl) + if (poly.paramNames.length <= maxTparamsToSpecialize && poly.paramNames.length > 0) { + val specTypes = getSpecTypes(sym) + generateSpecializations(poly.paramNames, poly.paramBounds, specTypes)(List.empty, List.empty, poly, decl) + } else Nil case nil => Nil } @@ -113,6 +119,20 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { } else tp } + def getSpecTypes(sym: Symbol)(implicit ctx: Context): List[Type] = { + sym.denot.getAnnotation(ctx.definitions.specializedAnnot).getOrElse(Nil) match { + case annot: Annotation => + annot.arguments match { + case List(SeqLiteral(types)) => + types.map(tpeTree => nameToSpecialisedType(ctx)(tpeTree.tpe.asInstanceOf[TermRef].name.toString())) + case List() => primitiveTypes + } + case nil => + if(ctx.settings.Yspecialize.value == "all") primitiveTypes + else Nil + } + } + def shouldSpecialize(decl: Symbol)(implicit ctx: Context): Boolean = specializationRequests.contains(decl) || (ctx.settings.Yspecialize.value != "" && decl.name.contains(ctx.settings.Yspecialize.value)) || @@ -124,81 +144,104 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { val prev = specializationRequests.getOrElse(method, List.empty) specializationRequests.put(method, arguments :: prev) } - - def specializeForAll(sym: Symbols.Symbol)(implicit ctx: Context): List[List[Type]] = { +/* + def specializeForAll(sym: Symbols.Symbol)(implicit ctx: Context): List[Type] = { registerSpecializationRequest(sym)(primitiveTypes) - println("Specializing for all primitive types") - specializationRequests.getOrElse(sym, Nil) + println(s"Specializing $sym for all primitive types") + specializationRequests.getOrElse(sym, Nil).flatten } - def specializeForSome(sym: Symbols.Symbol)(annotationArgs: List[Type])(implicit ctx: Context): List[List[Type]] = { + def specializeForSome(sym: Symbols.Symbol)(annotationArgs: List[Type])(implicit ctx: Context): List[Type] = { registerSpecializationRequest(sym)(annotationArgs) println(s"specializationRequests : $specializationRequests") - specializationRequests.getOrElse(sym, Nil) + specializationRequests.getOrElse(sym, Nil).flatten } - def specializeFor(sym: Symbols.Symbol)(implicit ctx: Context): List[List[Type]] = { + def specializeFor(sym: Symbols.Symbol)(implicit ctx: Context): List[Type] = { sym.denot.getAnnotation(ctx.definitions.specializedAnnot).getOrElse(Nil) match { case annot: Annotation => annot.arguments match { case List(SeqLiteral(types)) => - specializeForSome(sym)(types.map(tpeTree => //tpeTree.tpe.widen)) - nameToSpecialisedType(ctx)(tpeTree.tpe.asInstanceOf[TermRef].name.toString()))) // Not sure how to match TermRefs rather than types. comment on line above was an attempt. + specializeForSome(sym)(types.map(tpeTree => + nameToSpecialisedType(ctx)(tpeTree.tpe.asInstanceOf[TermRef].name.toString()))) // Not sure how to match TermRefs rather than type names case List() => specializeForAll(sym) } case nil => - if(ctx.settings.Yspecialize.value == "all") {println("Yspecialize set to all"); specializeForAll(sym) } + if(ctx.settings.Yspecialize.value == "all") specializeForAll(sym) else Nil } - } + }*/ override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = { tree.tpe.widen match { - case poly: PolyType if !(tree.symbol.isPrimaryConstructor - || (tree.symbol is Flags.Label)) => + case poly: PolyType if !(tree.symbol.isConstructor//isPrimaryConstructor + || (tree.symbol is Flags.Label)) + || (tree.symbol.name == nme.asInstanceOf_) => val origTParams = tree.tparams.map(_.symbol) val origVParams = tree.vparamss.flatten.map(_.symbol) - println(s"specializing ${tree.symbol} for Tparams: $origTParams") def specialize(decl : Symbol): List[Tree] = { - val declSpecs = newSymbolMap(decl) - val newSyms = declSpecs.map(_.values).flatten - /*for (newSym <- newSyms) { - println(newSym) - }*/ - val instantiations = declSpecs.flatMap(_.keys).flatten - newSyms.map{newSym => + if (newSymbolMap.contains(decl)) { + val declSpecs = newSymbolMap(decl) + val newSyms = declSpecs.values.toList + val instantiations = declSpecs.keys.toArray + var index = -1 + println(s"specializing ${tree.symbol} for $origTParams") + newSyms.map { newSym => + index += 1 polyDefDef(newSym.asTerm, { tparams => vparams => { assert(tparams.isEmpty) - //println(newSym + " ; " + origVParams + " ; " + vparams + " ; " + vparams.flatten + " ; " + vparams.flatten.map(_.tpe)) - new TreeTypeMap( //TODO Figure out what is happening with newSym. Why do some symbols have unmatching vparams and origVParams ? + new TreeTypeMap( typeMap = _ - .substDealias(origTParams, instantiations) + .substDealias(origTParams, instantiations(index)) .subst(origVParams, vparams.flatten.map(_.tpe)), oldOwners = tree.symbol :: Nil, newOwners = newSym :: Nil ).transform(tree.rhs) }}) } + } else Nil } - //specializeFor(tree.symbol) -> necessary ? This registers specialization requests, but do they still make sense at this point ? Symbols have already been generated - val specializedMethods = newSymbolMap.keys.map(specialize).flatten.toList + val specializedMethods = specialize(tree.symbol) Thicket(tree :: specializedMethods) case _ => tree } } override def transformTypeApply(tree: tpd.TypeApply)(implicit ctx: Context, info: TransformerInfo): Tree = { - val TypeApply(fun,args) = tree - val newSymInfo = newSymbolMap(fun.symbol).flatten.toMap - val specializationType: List[Type] = args.map(_.tpe.asInstanceOf[TypeVar].instanceOpt) - val t = fun.symbol.info.decls - if (t.nonEmpty) { - t.cloneScope.lookupEntry(args.head.symbol.name) - val newSym = newSymInfo(specializationType) + + def allowedToSpecialize(sym: Symbol): Boolean = { + sym.name != nme.asInstanceOf_ && + !(sym is Flags.JavaDefined) && + !sym.isConstructor//isPrimaryConstructor } - tree + val TypeApply(fun,args) = tree + if (newSymbolMap.contains(fun.symbol) && allowedToSpecialize(fun.symbol)) { + val newSymInfos = newSymbolMap(fun.symbol) + val betterDefs = newSymInfos.filter(x => (x._1 zip args).forall{a => + val specializedType = a._1 + val argType = a._2 + argType.tpe <:< specializedType + }).toList + assert(betterDefs.length < 2) // TODO: How to select the best if there are several ? + + if (betterDefs.nonEmpty) { + println(s"method $fun rewired to specialozed variant with type (${betterDefs.head._1})") + val prefix = fun match { + case Select(pre, name) => + pre + case t @ Ident(_) if t.tpe.isInstanceOf[TermRef] => + val tp = t.tpe.asInstanceOf[TermRef] + if (tp.prefix ne NoPrefix) + ref(tp.prefix.termSymbol) + else EmptyTree + } + if (prefix ne EmptyTree) + prefix.select(betterDefs.head._2) + else ref(betterDefs.head._2) + } else tree + } else tree } } diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index e3d460c7247e..6c99027e431a 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -16,7 +16,7 @@ class tests extends CompilerTest { // "-Xprompt", // "-explaintypes", // "-Yshow-suppressed-errors", - "-pagewidth", "160") + ) val defaultOutputDir = "./out/" @@ -36,12 +36,16 @@ class tests extends CompilerTest { val allowDeepSubtypes = defaultOptions diff List("-Yno-deep-subtypes") val allowDoubleBindings = defaultOptions diff List("-Yno-double-bindings") + val specialise = List("-Yspecialize:all") + val testsDir = "./tests/" val posDir = testsDir + "pos/" val posSpecialDir = testsDir + "pos-special/" val negDir = testsDir + "neg/" val runDir = testsDir + "run/" val newDir = testsDir + "new/" + val miniMethodDir = testsDir + "method_minibox/" + val miniMoreDir = testsDir + "more_minibox/" val sourceDir = "./src/" val dottyDir = sourceDir + "dotty/" @@ -216,8 +220,13 @@ class tests extends CompilerTest { val javaDir = "./tests/pos/java-interop/" @Test def java_all = compileFiles(javaDir, twice) - @Test def pos_specialization = compileFile(posDir, "specialization") + //@Test def pos_specialization = compileFile(posDir, "specialization")//, specialise) //@Test def dotc_compilercommand = compileFile(dotcDir + "config/", "CompilerCommand") - + + //@Test def test = compileFile(posDir, "t247", List("-Xprint:all")) + @Test def mini_method = compileFiles(miniMethodDir)//, List("-Xprint:all")) + @Test def mini_more = compileFiles(miniMoreDir)//, List("-Xprint:all")) + //@Test def pos_all = compileFiles(posDir)//, List("-Xprint:all")) + } From 30566fc9519ac2c0884e19cc05e88759f54af419 Mon Sep 17 00:00:00 2001 From: AlexSikia Date: Wed, 3 Jun 2015 02:04:10 +0200 Subject: [PATCH 08/37] Add `PreSpecializer` phase Creates a new `PreSpecializer` phase that detects `@specialized` annotations and registers them. This is necessary because annotations are lost further down the pipeline (and before TypeSpecializer runs.) --- src/dotty/tools/dotc/Compiler.scala | 3 +- .../tools/dotc/config/ScalaSettings.scala | 2 +- src/dotty/tools/dotc/core/Phases.scala | 2 + .../tools/dotc/transform/PreSpecializer.scala | 98 +++++++++++++++++++ .../dotc/transform/TypeSpecializer.scala | 72 +++----------- test/dotc/tests.scala | 7 ++ tests/pos/mutual_specialization.scala | 10 ++ tests/pos/return_specialization.scala | 6 ++ 8 files changed, 141 insertions(+), 59 deletions(-) create mode 100644 src/dotty/tools/dotc/transform/PreSpecializer.scala create mode 100644 tests/pos/mutual_specialization.scala create mode 100644 tests/pos/return_specialization.scala diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index 4e1b34b9b2b4..b141971497f8 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -42,7 +42,8 @@ class Compiler { List(new Pickler), List(new FirstTransform, new CheckReentrant), - List(new RefChecks, + List(new PreSpecializer, + new RefChecks, new ElimRepeated, new NormalizeFlags, new ExtensionMethods, diff --git a/src/dotty/tools/dotc/config/ScalaSettings.scala b/src/dotty/tools/dotc/config/ScalaSettings.scala index 1baee9732f8f..9f1f8d835d1e 100644 --- a/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -152,7 +152,7 @@ class ScalaSettings extends Settings.SettingGroup { val YprintSyms = BooleanSetting("-Yprint-syms", "when printing trees print info in symbols instead of corresponding info in trees.") val YtestPickler = BooleanSetting("-Ytest-pickler", "self-test for pickling functionality; should be used with -Ystop-after:pickler") val YcheckReentrant = BooleanSetting("-Ycheck-reentrant", "check that compiled program does not contain vars that can be accessed from a global root.") - val Yspecialize = StringSetting("-Yspecialize","all","Specialize all methods.", "all") + val Yspecialize = StringSetting("-Yspecialize","","Specialize all methods.", "") def stop = YstopAfter diff --git a/src/dotty/tools/dotc/core/Phases.scala b/src/dotty/tools/dotc/core/Phases.scala index 8d5ec08f70af..5fae2626d7b6 100644 --- a/src/dotty/tools/dotc/core/Phases.scala +++ b/src/dotty/tools/dotc/core/Phases.scala @@ -240,6 +240,7 @@ object Phases { private val explicitOuterCache = new PhaseCache(classOf[ExplicitOuter]) private val gettersCache = new PhaseCache(classOf[Getters]) private val genBCodeCache = new PhaseCache(classOf[GenBCode]) + private val specializeCache = new PhaseCache(classOf[TypeSpecializer]) def typerPhase = typerCache.phase def picklerPhase = picklerCache.phase @@ -252,6 +253,7 @@ object Phases { def explicitOuterPhase = explicitOuterCache.phase def gettersPhase = gettersCache.phase def genBCodePhase = genBCodeCache.phase + def specializePhase = specializeCache.phase def isAfterTyper(phase: Phase): Boolean = phase.id > typerPhase.id } diff --git a/src/dotty/tools/dotc/transform/PreSpecializer.scala b/src/dotty/tools/dotc/transform/PreSpecializer.scala new file mode 100644 index 000000000000..732cf99c244d --- /dev/null +++ b/src/dotty/tools/dotc/transform/PreSpecializer.scala @@ -0,0 +1,98 @@ +package dotty.tools.dotc.transform + +import dotty.tools.dotc.ast.Trees.{Select, Ident, SeqLiteral, Typed} +import dotty.tools.dotc.ast.tpd +import dotty.tools.dotc.core.Annotations.Annotation +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.DenotTransformers.InfoTransformer +import dotty.tools.dotc.core.StdNames._ +import dotty.tools.dotc.core.{Flags, Definitions, Symbols} +import dotty.tools.dotc.core.Symbols.Symbol +import dotty.tools.dotc.core.Types.{TermRef, TypeRef, OrType, Type} +import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, MiniPhaseTransform} + +import scala.collection.mutable + +/** + * This phase runs before {what phase ?}, so as to retrieve all `@specialized` + * anotations before they are thrown away, and stores them through a `PhaseCache` + * for the `TypeSpecializer` phase. + */ +class PreSpecializer extends MiniPhaseTransform with InfoTransformer { + + override def phaseName: String = "prespecialize" + + private val specTypes: mutable.HashMap[Symbols.Symbol, List[Type]] = mutable.HashMap.empty + + override def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = { + + def getSpecTypes(sym: Symbol)(implicit ctx: Context): List[Type] = { + + def allowedToSpecialize(sym: Symbol): Boolean = { + sym.name != nme.asInstanceOf_ && + !(sym is Flags.JavaDefined) && + !sym.isConstructor//isPrimaryConstructor + } + + if (allowedToSpecialize(sym)) { + val annotation = sym.denot.getAnnotation(ctx.definitions.specializedAnnot).getOrElse(Nil) + annotation match { + case annot: Annotation => + val args = annot.arguments + if (args.isEmpty) primitiveTypes + else args.head match { + case a@Typed(SeqLiteral(types), _) => types.map(t => nameToType(t.tpe)) + case a@Select(Ident(_), _) => { + println(a) + primitiveTypes + } + case _ => { + println("Nonono") + ctx.error("surprising match on specialized annotation"); Nil + } + } + case nil => Nil + } + } else Nil + } + val st = getSpecTypes(sym) + if (st.nonEmpty) { + specTypes.put(sym, st) + } + tp + } + + private final def nameToType(name: Type)(implicit ctx: Context) = + name.asInstanceOf[TermRef].name.toString match { + case s if s.startsWith("Int") => defn.IntType + case s if s.startsWith("Boolean") => defn.BooleanType + case s if s.startsWith("Byte") => defn.ByteType + case s if s.startsWith("Long") => defn.LongType + case s if s.startsWith("Short") => defn.ShortType + case s if s.startsWith("Float") => defn.FloatType + case s if s.startsWith("Unit") => defn.UnitType + case s if s.startsWith("Double") => defn.DoubleType + case s if s.startsWith("Char") => defn.CharType + } + + def defn(implicit ctx: Context): Definitions = ctx.definitions + + private def primitiveTypes(implicit ctx: Context) = + List(ctx.definitions.ByteType, + ctx.definitions.BooleanType, + ctx.definitions.ShortType, + ctx.definitions.IntType, + ctx.definitions.LongType, + ctx.definitions.FloatType, + ctx.definitions.DoubleType, + ctx.definitions.CharType, + ctx.definitions.UnitType + ) + + override def transformDefDef(tree: tpd.DefDef)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { + specTypes.keys.foreach( + sym => ctx.specializePhase.asInstanceOf[TypeSpecializer].registerSpecializationRequest(tree.symbol)(specTypes(sym)) + ) + tree + } +} diff --git a/src/dotty/tools/dotc/transform/TypeSpecializer.scala b/src/dotty/tools/dotc/transform/TypeSpecializer.scala index 9041932122f0..75ffec23e173 100644 --- a/src/dotty/tools/dotc/transform/TypeSpecializer.scala +++ b/src/dotty/tools/dotc/transform/TypeSpecializer.scala @@ -20,17 +20,6 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { final val maxTparamsToSpecialize = 2 - private final def nameToSpecialisedType(implicit ctx: Context) = - Map("Byte" -> ctx.definitions.ByteType, - "Boolean" -> ctx.definitions.BooleanType, - "Short" -> ctx.definitions.ShortType, - "Int" -> ctx.definitions.IntType, - "Long" -> ctx.definitions.LongType, - "Float" -> ctx.definitions.FloatType, - "Double" -> ctx.definitions.DoubleType, - "Char" -> ctx.definitions.CharType, - "Unit" -> ctx.definitions.UnitType) - private final def specialisedTypeToSuffix(implicit ctx: Context) = Map(ctx.definitions.ByteType -> "$mcB$sp", ctx.definitions.BooleanType -> "$mcZ$sp", @@ -54,7 +43,7 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { ctx.definitions.UnitType ) - private val specializationRequests: mutable.HashMap[Symbols.Symbol, List[List[Type]]] = mutable.HashMap.empty + private val specializationRequests: mutable.HashMap[Symbols.Symbol, List[Type]] = mutable.HashMap.empty /** * A map that links symbols to their specialized variants. @@ -63,14 +52,12 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { private val newSymbolMap: mutable.HashMap[Symbol, mutable.HashMap[List[Type], Symbols.Symbol]] = mutable.HashMap.empty override def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = { - - def generateSpecializations(remainingTParams: List[Name], remainingBounds: List[TypeBounds], specTypes: List[Type]) + def generateSpecializations(remainingTParams: List[Name], specTypes: List[Type]) (instantiations: List[Type], names: List[String], poly: PolyType, decl: Symbol) (implicit ctx: Context): List[Symbol] = { if (remainingTParams.nonEmpty) { - val bounds = remainingBounds.head val specializations = (for (tpe <- specTypes) yield { - generateSpecializations(remainingTParams.tail, remainingBounds.tail, specTypes)(tpe :: instantiations, specialisedTypeToSuffix(ctx)(tpe) :: names, poly, decl) + generateSpecializations(remainingTParams.tail, specTypes)(tpe :: instantiations, specialisedTypeToSuffix(ctx)(tpe) :: names, poly, decl) }).flatten specializations } @@ -96,12 +83,15 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { sym.info match { case classInfo: ClassInfo => val newDecls = classInfo.decls.filterNot(_.isConstructor/*isPrimaryConstructor*/).flatMap(decl => { + if(decl.name.toString.contains("foobar")) { + println("hello") + } if (shouldSpecialize(decl)) { decl.info.widen match { case poly: PolyType => if (poly.paramNames.length <= maxTparamsToSpecialize && poly.paramNames.length > 0) { - val specTypes = getSpecTypes(sym) - generateSpecializations(poly.paramNames, poly.paramBounds, specTypes)(List.empty, List.empty, poly, decl) + val specTypes = getSpecTypes(decl).filter(tpe => poly.paramBounds.forall(_.contains(tpe))) + generateSpecializations(poly.paramNames, specTypes)(List.empty, List.empty, poly, decl) } else Nil case nil => Nil @@ -120,16 +110,11 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { } def getSpecTypes(sym: Symbol)(implicit ctx: Context): List[Type] = { - sym.denot.getAnnotation(ctx.definitions.specializedAnnot).getOrElse(Nil) match { - case annot: Annotation => - annot.arguments match { - case List(SeqLiteral(types)) => - types.map(tpeTree => nameToSpecialisedType(ctx)(tpeTree.tpe.asInstanceOf[TermRef].name.toString())) - case List() => primitiveTypes - } - case nil => - if(ctx.settings.Yspecialize.value == "all") primitiveTypes - else Nil + val requested = specializationRequests.getOrElse(sym, List()) + if (requested.nonEmpty) requested.toList + else { + if(ctx.settings.Yspecialize.value == "all") primitiveTypes + else Nil } } @@ -142,35 +127,8 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { if(ctx.phaseId > this.treeTransformPhase.id) assert(ctx.phaseId <= this.treeTransformPhase.id) val prev = specializationRequests.getOrElse(method, List.empty) - specializationRequests.put(method, arguments :: prev) + specializationRequests.put(method, arguments ::: prev) } -/* - def specializeForAll(sym: Symbols.Symbol)(implicit ctx: Context): List[Type] = { - registerSpecializationRequest(sym)(primitiveTypes) - println(s"Specializing $sym for all primitive types") - specializationRequests.getOrElse(sym, Nil).flatten - } - - def specializeForSome(sym: Symbols.Symbol)(annotationArgs: List[Type])(implicit ctx: Context): List[Type] = { - registerSpecializationRequest(sym)(annotationArgs) - println(s"specializationRequests : $specializationRequests") - specializationRequests.getOrElse(sym, Nil).flatten - } - - def specializeFor(sym: Symbols.Symbol)(implicit ctx: Context): List[Type] = { - sym.denot.getAnnotation(ctx.definitions.specializedAnnot).getOrElse(Nil) match { - case annot: Annotation => - annot.arguments match { - case List(SeqLiteral(types)) => - specializeForSome(sym)(types.map(tpeTree => - nameToSpecialisedType(ctx)(tpeTree.tpe.asInstanceOf[TermRef].name.toString()))) // Not sure how to match TermRefs rather than type names - case List() => specializeForAll(sym) - } - case nil => - if(ctx.settings.Yspecialize.value == "all") specializeForAll(sym) - else Nil - } - }*/ override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = { @@ -228,7 +186,7 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { assert(betterDefs.length < 2) // TODO: How to select the best if there are several ? if (betterDefs.nonEmpty) { - println(s"method $fun rewired to specialozed variant with type (${betterDefs.head._1})") + println(s"method $fun rewired to specialized variant with type (${betterDefs.head._1})") val prefix = fun match { case Select(pre, name) => pre diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 6c99027e431a..094d33ce27b6 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -229,4 +229,11 @@ class tests extends CompilerTest { @Test def mini_more = compileFiles(miniMoreDir)//, List("-Xprint:all")) //@Test def pos_all = compileFiles(posDir)//, List("-Xprint:all")) + @Test def pos_mutual_spec = compileFile(posDir, "mutual_specialization", List("-Xprint:all")) + //@Test def pos_mutual_spec = compileFile(posDir, "mutual_specialization") + //@Test def pos_spec = compileFile(posDir, "specialization") +*/ + @Test def pos_return_spec = compileFile(posDir, "return_specialization") +// @Test def pos_si7638 = compileFile(posDir, "SI-7638", List("-Xprint:all")) + } diff --git a/tests/pos/mutual_specialization.scala b/tests/pos/mutual_specialization.scala new file mode 100644 index 000000000000..cc778e8ccf09 --- /dev/null +++ b/tests/pos/mutual_specialization.scala @@ -0,0 +1,10 @@ +object mutual_specialization { + class A[T] { + def foo[T](b: B[T], n: Int): Unit = if (n > 0) b.bar(this, n-1) + } + class B[T] { + def bar[T](a: A[T], n: Int): Unit = if (n > 0) a.foo(this, n-1) + } + def foobar[@specialized(Int, Float, Double) T](n: T): Unit = new A[T].foo(new B[T], 5) + foobar(5) +} diff --git a/tests/pos/return_specialization.scala b/tests/pos/return_specialization.scala new file mode 100644 index 000000000000..e2424c474aa9 --- /dev/null +++ b/tests/pos/return_specialization.scala @@ -0,0 +1,6 @@ +object return_specialization { + def qwa[@specialized T](a: (String, String) => T, b: T): T = { + if(a ne this) return a("1", "2") + else b + } +} From 0280142a7760c2eaf5b627c8f1ec68675c8529e6 Mon Sep 17 00:00:00 2001 From: AlexSikia Date: Wed, 3 Jun 2015 02:06:05 +0200 Subject: [PATCH 09/37] Specialize methods defined inside of other methods Specialize inner methods. Also adds some tests of specialization. --- .../tools/dotc/transform/PreSpecializer.scala | 25 ++- .../dotc/transform/TypeSpecializer.scala | 143 ++++++++++-------- test/dotc/tests.scala | 33 ++-- .../bounds_specialization.scala | 21 +++ .../method_in_class_specialization.scala | 42 +++++ .../method_in_method_specialization.scala | 13 ++ .../specialization/multi_specialization.scala | 5 + .../mutual_specialization.scala | 0 .../nothing_specialization.scala | 7 + .../return_specialization.scala | 0 .../{ => specialization}/specialization.scala | 0 .../type_check_specialization.scala | 3 + 12 files changed, 194 insertions(+), 98 deletions(-) create mode 100644 tests/pos/specialization/bounds_specialization.scala create mode 100644 tests/pos/specialization/method_in_class_specialization.scala create mode 100644 tests/pos/specialization/method_in_method_specialization.scala create mode 100644 tests/pos/specialization/multi_specialization.scala rename tests/pos/{ => specialization}/mutual_specialization.scala (100%) create mode 100644 tests/pos/specialization/nothing_specialization.scala rename tests/pos/{ => specialization}/return_specialization.scala (100%) rename tests/pos/{ => specialization}/specialization.scala (100%) create mode 100644 tests/pos/specialization/type_check_specialization.scala diff --git a/src/dotty/tools/dotc/transform/PreSpecializer.scala b/src/dotty/tools/dotc/transform/PreSpecializer.scala index 732cf99c244d..6b5ddc5ab7e7 100644 --- a/src/dotty/tools/dotc/transform/PreSpecializer.scala +++ b/src/dotty/tools/dotc/transform/PreSpecializer.scala @@ -14,9 +14,8 @@ import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, MiniPhaseTran import scala.collection.mutable /** - * This phase runs before {what phase ?}, so as to retrieve all `@specialized` - * anotations before they are thrown away, and stores them through a `PhaseCache` - * for the `TypeSpecializer` phase. + * This phase retrieves all `@specialized` anotations before they are thrown away, + * and stores them for the `TypeSpecializer` phase. */ class PreSpecializer extends MiniPhaseTransform with InfoTransformer { @@ -30,6 +29,7 @@ class PreSpecializer extends MiniPhaseTransform with InfoTransformer { def allowedToSpecialize(sym: Symbol): Boolean = { sym.name != nme.asInstanceOf_ && + sym.name != nme.isInstanceOf_ && !(sym is Flags.JavaDefined) && !sym.isConstructor//isPrimaryConstructor } @@ -41,15 +41,9 @@ class PreSpecializer extends MiniPhaseTransform with InfoTransformer { val args = annot.arguments if (args.isEmpty) primitiveTypes else args.head match { - case a@Typed(SeqLiteral(types), _) => types.map(t => nameToType(t.tpe)) - case a@Select(Ident(_), _) => { - println(a) - primitiveTypes - } - case _ => { - println("Nonono") - ctx.error("surprising match on specialized annotation"); Nil - } + case a@Typed(SeqLiteral(types), _) => types.map(t => nameToType(t.tpe)) // Matches the expected `@specialized(...)` annotations + case a@Select(Ident(_), _) => primitiveTypes // Matches `Select(Ident(Specializable), Primitives)` which is used in several instances + case _ => ctx.error("surprising match on specialized annotation"); Nil } case nil => Nil } @@ -57,7 +51,7 @@ class PreSpecializer extends MiniPhaseTransform with InfoTransformer { } val st = getSpecTypes(sym) if (st.nonEmpty) { - specTypes.put(sym, st) + specTypes.put(sym.owner, st) } tp } @@ -90,9 +84,8 @@ class PreSpecializer extends MiniPhaseTransform with InfoTransformer { ) override def transformDefDef(tree: tpd.DefDef)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { - specTypes.keys.foreach( - sym => ctx.specializePhase.asInstanceOf[TypeSpecializer].registerSpecializationRequest(tree.symbol)(specTypes(sym)) - ) + val st = specTypes.getOrElse(tree.symbol, List()) + if (st.nonEmpty) ctx.specializePhase.asInstanceOf[TypeSpecializer].registerSpecializationRequest(tree.symbol)(st) tree } } diff --git a/src/dotty/tools/dotc/transform/TypeSpecializer.scala b/src/dotty/tools/dotc/transform/TypeSpecializer.scala index 75ffec23e173..e40809b39b56 100644 --- a/src/dotty/tools/dotc/transform/TypeSpecializer.scala +++ b/src/dotty/tools/dotc/transform/TypeSpecializer.scala @@ -21,28 +21,30 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { final val maxTparamsToSpecialize = 2 private final def specialisedTypeToSuffix(implicit ctx: Context) = - Map(ctx.definitions.ByteType -> "$mcB$sp", - ctx.definitions.BooleanType -> "$mcZ$sp", - ctx.definitions.ShortType -> "$mcS$sp", - ctx.definitions.IntType -> "$mcI$sp", - ctx.definitions.LongType -> "$mcJ$sp", - ctx.definitions.FloatType -> "$mcF$sp", - ctx.definitions.DoubleType -> "$mcD$sp", - ctx.definitions.CharType -> "$mcC$sp", - ctx.definitions.UnitType -> "$mcV$sp") + Map(defn.ByteType -> "$mcB$sp", + defn.BooleanType -> "$mcZ$sp", + defn.ShortType -> "$mcS$sp", + defn.IntType -> "$mcI$sp", + defn.LongType -> "$mcJ$sp", + defn.FloatType -> "$mcF$sp", + defn.DoubleType -> "$mcD$sp", + defn.CharType -> "$mcC$sp", + defn.UnitType -> "$mcV$sp") private def primitiveTypes(implicit ctx: Context) = - List(ctx.definitions.ByteType, - ctx.definitions.BooleanType, - ctx.definitions.ShortType, - ctx.definitions.IntType, - ctx.definitions.LongType, - ctx.definitions.FloatType, - ctx.definitions.DoubleType, - ctx.definitions.CharType, - ctx.definitions.UnitType + List(defn.ByteType, + defn.BooleanType, + defn.ShortType, + defn.IntType, + defn.LongType, + defn.FloatType, + defn.DoubleType, + defn.CharType, + defn.UnitType ) + private def defn(implicit ctx:Context) = ctx.definitions + private val specializationRequests: mutable.HashMap[Symbols.Symbol, List[Type]] = mutable.HashMap.empty /** @@ -51,15 +53,45 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { */ private val newSymbolMap: mutable.HashMap[Symbol, mutable.HashMap[List[Type], Symbols.Symbol]] = mutable.HashMap.empty + def allowedToSpecialize(sym: Symbol, numOfTypes: Int)(implicit ctx: Context): Boolean = { + numOfTypes <= maxTparamsToSpecialize && + numOfTypes > 0 && + sym.name != nme.asInstanceOf_ && + sym.name != nme.isInstanceOf_ && + !(sym is Flags.JavaDefined) && + !sym.isConstructor && + !sym.name.toString.contains("Function2") + } + + def getSpecTypes(sym: Symbol, poly: PolyType)(implicit ctx: Context): List[Type] = { + val requested = specializationRequests.getOrElse(sym, List()) + if (requested.nonEmpty) requested.toList + else { + if(ctx.settings.Yspecialize.value == "all") primitiveTypes + else Nil + }.filter(tpe => poly.paramBounds.forall(_.contains(tpe))) + } + + def requestedSpecialization(decl: Symbol)(implicit ctx: Context): Boolean = + specializationRequests.contains(decl) || + (ctx.settings.Yspecialize.value != "" && decl.name.contains(ctx.settings.Yspecialize.value)) || + ctx.settings.Yspecialize.value == "all" + + def registerSpecializationRequest(method: Symbols.Symbol)(arguments: List[Type])(implicit ctx: Context) = { + if(ctx.phaseId > this.treeTransformPhase.id) + assert(ctx.phaseId <= this.treeTransformPhase.id) + val prev = specializationRequests.getOrElse(method, List.empty) + specializationRequests.put(method, (arguments ::: prev).toSet.toList) + } + override def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = { def generateSpecializations(remainingTParams: List[Name], specTypes: List[Type]) (instantiations: List[Type], names: List[String], poly: PolyType, decl: Symbol) (implicit ctx: Context): List[Symbol] = { if (remainingTParams.nonEmpty) { - val specializations = (for (tpe <- specTypes) yield { + (for (tpe <- specTypes) yield { generateSpecializations(remainingTParams.tail, specTypes)(tpe :: instantiations, specialisedTypeToSuffix(ctx)(tpe) :: names, poly, decl) }).flatten - specializations } else { generateSpecializedSymbols(instantiations.reverse, names.reverse, poly, decl) @@ -76,65 +108,39 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { map.values.toList } - if((sym ne ctx.definitions.ScalaPredefModule.moduleClass) && + if((sym ne defn.ScalaPredefModule.moduleClass) && !(sym is Flags.Package) && - !sym.isAnonymousClass && - !(sym.name == nme.asInstanceOf_)) { + !sym.isAnonymousClass) { sym.info match { case classInfo: ClassInfo => - val newDecls = classInfo.decls.filterNot(_.isConstructor/*isPrimaryConstructor*/).flatMap(decl => { - if(decl.name.toString.contains("foobar")) { - println("hello") - } - if (shouldSpecialize(decl)) { + val newDecls = classInfo.decls + .filterNot(_.isConstructor) + .filter(requestedSpecialization) + .flatMap(decl => { decl.info.widen match { - case poly: PolyType => - if (poly.paramNames.length <= maxTparamsToSpecialize && poly.paramNames.length > 0) { - val specTypes = getSpecTypes(decl).filter(tpe => poly.paramBounds.forall(_.contains(tpe))) - generateSpecializations(poly.paramNames, specTypes)(List.empty, List.empty, poly, decl) - } - else Nil + case poly: PolyType if allowedToSpecialize(decl.symbol, poly.paramNames.length) => + generateSpecializations(poly.paramNames, getSpecTypes(decl, poly))(List.empty, List.empty, poly, decl) case nil => Nil } - } else Nil }) - if (newDecls.nonEmpty) { val decls = classInfo.decls.cloneScope newDecls.foreach(decls.enter) classInfo.derivedClassInfo(decls = decls) - } + case poly: PolyType if !newSymbolMap.contains(sym) && + requestedSpecialization(sym) && + allowedToSpecialize(sym, poly.paramNames.length)=> + generateSpecializations(poly.paramNames, getSpecTypes(sym, poly))(List.empty, List.empty, poly, sym) case nil => } tp } else tp } - def getSpecTypes(sym: Symbol)(implicit ctx: Context): List[Type] = { - val requested = specializationRequests.getOrElse(sym, List()) - if (requested.nonEmpty) requested.toList - else { - if(ctx.settings.Yspecialize.value == "all") primitiveTypes - else Nil - } - } - - def shouldSpecialize(decl: Symbol)(implicit ctx: Context): Boolean = - specializationRequests.contains(decl) || - (ctx.settings.Yspecialize.value != "" && decl.name.contains(ctx.settings.Yspecialize.value)) || - ctx.settings.Yspecialize.value == "all" - - def registerSpecializationRequest(method: Symbols.Symbol)(arguments: List[Type])(implicit ctx: Context) = { - if(ctx.phaseId > this.treeTransformPhase.id) - assert(ctx.phaseId <= this.treeTransformPhase.id) - val prev = specializationRequests.getOrElse(method, List.empty) - specializationRequests.put(method, arguments ::: prev) - } - override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = { tree.tpe.widen match { - case poly: PolyType if !(tree.symbol.isConstructor//isPrimaryConstructor + case poly: PolyType if !(tree.symbol.isConstructor || (tree.symbol is Flags.Label)) || (tree.symbol.name == nme.asInstanceOf_) => val origTParams = tree.tparams.map(_.symbol) @@ -162,31 +168,35 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { } } else Nil } - val specializedMethods = specialize(tree.symbol) - Thicket(tree :: specializedMethods) + Thicket(tree :: specialize(tree.symbol)) case _ => tree } } override def transformTypeApply(tree: tpd.TypeApply)(implicit ctx: Context, info: TransformerInfo): Tree = { + def allowedToSpecialize(sym: Symbol): Boolean = { sym.name != nme.asInstanceOf_ && !(sym is Flags.JavaDefined) && !sym.isConstructor//isPrimaryConstructor } + val TypeApply(fun,args) = tree - if (newSymbolMap.contains(fun.symbol) && allowedToSpecialize(fun.symbol)) { + if (newSymbolMap.contains(fun.symbol)){ val newSymInfos = newSymbolMap(fun.symbol) val betterDefs = newSymInfos.filter(x => (x._1 zip args).forall{a => val specializedType = a._1 val argType = a._2 argType.tpe <:< specializedType }).toList - assert(betterDefs.length < 2) // TODO: How to select the best if there are several ? + + if (betterDefs.length > 1) ctx.debuglog("Several specialized variants fit.") + //assert(betterDefs.length < 2) // TODO: How to select the best if there are several ? if (betterDefs.nonEmpty) { - println(s"method $fun rewired to specialized variant with type (${betterDefs.head._1})") + val best = betterDefs.head + println(s"method ${fun.symbol.name} of ${fun.symbol.owner} rewired to specialized variant with type (${best._1})") val prefix = fun match { case Select(pre, name) => pre @@ -197,9 +207,10 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { else EmptyTree } if (prefix ne EmptyTree) - prefix.select(betterDefs.head._2) - else ref(betterDefs.head._2) + prefix.select(best._2) + else ref(best._2) } else tree } else tree } } + diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 094d33ce27b6..3038e3cd39d0 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -44,6 +44,7 @@ class tests extends CompilerTest { val negDir = testsDir + "neg/" val runDir = testsDir + "run/" val newDir = testsDir + "new/" + val specialDir = posDir + "specialization/" val miniMethodDir = testsDir + "method_minibox/" val miniMoreDir = testsDir + "more_minibox/" @@ -106,8 +107,7 @@ class tests extends CompilerTest { @Test def pos_SI7638 = compileFile(posDir, "SI-7638") @Test def pos_SI7638a = compileFile(posDir, "SI-7638a") - - @Test def new_all = compileFiles(newDir, twice) + //@Test def new_all = compileFiles(newDir, twice) @Test def neg_blockescapes() = compileFile(negDir, "blockescapesNeg", xerrors = 1) @Test def neg_typedapply() = compileFile(negDir, "typedapply", xerrors = 4) @@ -220,20 +220,21 @@ class tests extends CompilerTest { val javaDir = "./tests/pos/java-interop/" @Test def java_all = compileFiles(javaDir, twice) - //@Test def pos_specialization = compileFile(posDir, "specialization")//, specialise) - - //@Test def dotc_compilercommand = compileFile(dotcDir + "config/", "CompilerCommand") - - //@Test def test = compileFile(posDir, "t247", List("-Xprint:all")) - @Test def mini_method = compileFiles(miniMethodDir)//, List("-Xprint:all")) - @Test def mini_more = compileFiles(miniMoreDir)//, List("-Xprint:all")) + //@Test def specialization = compileFile(specialDir, "specialization")//, specialise) + //@Test def mutual_spec = compileFile(specialDir, "mutual_specialization") + //@Test def return_spec = compileFile(specialDir, "return_specialization") + //@Test def nothing_spec = compileFile(specialDir, "nothing_specialization") + //@Test def method_in_class_spec = compileFile(specialDir, "method_in_class_specialization") + //@Test def method_in_method_spec = compileFile(specialDir, "method_in_method_specialization", List("-Xprint:all")) + @Test def type_check_spec = compileFile(specialDir, "type_check_specialization") + //@Test def bounds_spec = compileFile(specialDir, "bounds_specialization", List("-Xprint:all")) + //@Test def multi_spec = compileFile(specialDir, "multi_specialization", List("-Xprint:all")) + //@Test def pos_spec_all = compileFiles(specialDir) + + //@Test def mini_method = compileFiles(miniMethodDir)//, List("-Xprint:all")) + //@Test def mini_more = compileFiles(miniMoreDir)//, List("-Xprint:all")) //@Test def pos_all = compileFiles(posDir)//, List("-Xprint:all")) - @Test def pos_mutual_spec = compileFile(posDir, "mutual_specialization", List("-Xprint:all")) - //@Test def pos_mutual_spec = compileFile(posDir, "mutual_specialization") - //@Test def pos_spec = compileFile(posDir, "specialization") -*/ - @Test def pos_return_spec = compileFile(posDir, "return_specialization") -// @Test def pos_si7638 = compileFile(posDir, "SI-7638", List("-Xprint:all")) - + //@Test def pos_si7638 = compileFile(posDir, "SI-7638", List("-Xprint:all")) + //@Test def test = compileFile(posDir, "t247", List("-Xprint:all")) } diff --git a/tests/pos/specialization/bounds_specialization.scala b/tests/pos/specialization/bounds_specialization.scala new file mode 100644 index 000000000000..dbb4fac23df6 --- /dev/null +++ b/tests/pos/specialization/bounds_specialization.scala @@ -0,0 +1,21 @@ +object bounds_specialization { + /*class Foo[@specialized K] { + def bar[@specialized U](u: U) { + def dough[@specialized V](v: V) { + println("innerMethod") + } + dough(1.toShort) + dough('c') + } + bar(2.toShort) + bar('d') + } +*/ + def kung[@specialized(Int, Double) T <: AnyRef](t: T): T = { + t + } + + def fu[@specialized(Int, Double) T >: Nothing](t: T): T = { + t + } +} \ No newline at end of file diff --git a/tests/pos/specialization/method_in_class_specialization.scala b/tests/pos/specialization/method_in_class_specialization.scala new file mode 100644 index 000000000000..9b9c74f4fdec --- /dev/null +++ b/tests/pos/specialization/method_in_class_specialization.scala @@ -0,0 +1,42 @@ +object method_in_class_specialization { + class A { + def foo[@specialized(Int, Long) T](a: T) = List() + } + class B[K] { + def foo[@specialized T](b: T) = List() + } + class C extends A { + override def foo[@specialized T](c: T) = super.foo(c) + def bar[@specialized(Float, Char) U](c: U) = super.foo(c) + def man[@specialized V](c: V) = List() + } + class D extends B { + override def foo[@specialized T](d: T) = super.foo(d) + def bar[@specialized U](d: U) = super.foo(d) + def man[@specialized V](d: V) = List() + } + class E[U] extends B { + override def foo[@specialized T](e: T) = super.foo(e) + def bar[@specialized U](e: U) = super.foo(e) + def man[@specialized V](e: V) = List() + } + + val a = new A + val b = new B[Int] + val c = new C + val d = new D + val e = new E[Char] + + a.foo(1) + a.foo(1.toLong) + a.foo("foo") + b.foo(2) + c.foo(3) + d.foo(4) + e.foo(5) + + c.bar('d') + c.bar(6) + e.bar('d') + e.bar(7) +} \ No newline at end of file diff --git a/tests/pos/specialization/method_in_method_specialization.scala b/tests/pos/specialization/method_in_method_specialization.scala new file mode 100644 index 000000000000..467510b24892 --- /dev/null +++ b/tests/pos/specialization/method_in_method_specialization.scala @@ -0,0 +1,13 @@ +object method_in_method_specialization { + def outer[@specialized(Int) O](o: O): O = { + def inner[@specialized(Int, Char) I](i: I): O = i match { + case o2: O => o2 + case _ => o + } + inner(1) + inner('c') + } + + outer(2) + outer('d') +} \ No newline at end of file diff --git a/tests/pos/specialization/multi_specialization.scala b/tests/pos/specialization/multi_specialization.scala new file mode 100644 index 000000000000..19b276fc1c16 --- /dev/null +++ b/tests/pos/specialization/multi_specialization.scala @@ -0,0 +1,5 @@ +object multi_specialization { + def one[@specialized T](n: T): T = n + def two[@specialized T, U](n: T, m: U): (T,U) = (n,m) + def three[@specialized T, U, V](n: T, m: U, o: V): (T,U,V) = (n,m,o) +} \ No newline at end of file diff --git a/tests/pos/mutual_specialization.scala b/tests/pos/specialization/mutual_specialization.scala similarity index 100% rename from tests/pos/mutual_specialization.scala rename to tests/pos/specialization/mutual_specialization.scala diff --git a/tests/pos/specialization/nothing_specialization.scala b/tests/pos/specialization/nothing_specialization.scala new file mode 100644 index 000000000000..a6155da12753 --- /dev/null +++ b/tests/pos/specialization/nothing_specialization.scala @@ -0,0 +1,7 @@ +object nothing_specialization { + def ret_nothing[@specialized T] = { + //val a: List[T] = List[Nothing]() + def apply[@specialized X](xs : X*) : List[X] = List(xs:_*) + def apply6[@specialized X](xs : Nothing*) : List[Nothing] = List(xs: _*) + } +} diff --git a/tests/pos/return_specialization.scala b/tests/pos/specialization/return_specialization.scala similarity index 100% rename from tests/pos/return_specialization.scala rename to tests/pos/specialization/return_specialization.scala diff --git a/tests/pos/specialization.scala b/tests/pos/specialization/specialization.scala similarity index 100% rename from tests/pos/specialization.scala rename to tests/pos/specialization/specialization.scala diff --git a/tests/pos/specialization/type_check_specialization.scala b/tests/pos/specialization/type_check_specialization.scala new file mode 100644 index 000000000000..7c6a9c47f70e --- /dev/null +++ b/tests/pos/specialization/type_check_specialization.scala @@ -0,0 +1,3 @@ +object type_check_specialization { + def inner[@specialized(Char) I](i: I): Unit = i.isInstanceOf[Int] +} \ No newline at end of file From f56c7aeb1312f4815099f5517372e318174d50d0 Mon Sep 17 00:00:00 2001 From: AlexSikia Date: Mon, 18 May 2015 14:10:39 +0200 Subject: [PATCH 10/37] Adapt instance of `TreeTypeMap` to map trees recursively Solves an issue involving return values not being specialized. Test `return_specialization` illustrates this. --- .../dotc/transform/TypeSpecializer.scala | 84 +++++++++++++++---- test/dotc/tests.scala | 20 +++-- .../return_specialization.scala | 4 +- .../specialization/this_specialization.scala | 8 ++ .../type_check_specialization.scala | 3 - tests/pos/specialization/type_test.scala | 3 + 6 files changed, 92 insertions(+), 30 deletions(-) create mode 100644 tests/pos/specialization/this_specialization.scala delete mode 100644 tests/pos/specialization/type_check_specialization.scala create mode 100644 tests/pos/specialization/type_test.scala diff --git a/src/dotty/tools/dotc/transform/TypeSpecializer.scala b/src/dotty/tools/dotc/transform/TypeSpecializer.scala index e40809b39b56..0ec88bf858cc 100644 --- a/src/dotty/tools/dotc/transform/TypeSpecializer.scala +++ b/src/dotty/tools/dotc/transform/TypeSpecializer.scala @@ -101,7 +101,23 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { (implicit ctx: Context): List[Symbol] = { val newSym = ctx.newSymbol(decl.owner, (decl.name + names.mkString).toTermName, - decl.flags | Flags.Synthetic, poly.instantiate(instantiations.toList)) + decl.flags | Flags.Synthetic, poly.instantiate(instantiations.toList)) + + /* The following generated symbols which kept type bounds. It served, as illustrated by the `this_specialization` + * test, as a way of keeping type bounds when instantiating a `this` referring to a generic class. However, + * because type bounds are not transitive, this did not work out and we introduced casts instead. + * + * ctx.newSymbol(decl.owner, (decl.name + names.mkString).toTermName, + * decl.flags | Flags.Synthetic, + * poly.derivedPolyType(poly.paramNames, + * (poly.paramBounds zip instantiations).map + * {case (bounds, instantiation) => + * TypeBounds(bounds.lo, AndType(bounds.hi, instantiation))}, + * poly.instantiate(indices, instantiations) + * ) + * ) + */ + val map = newSymbolMap.getOrElse(decl, mutable.HashMap.empty) map.put(instantiations, newSym) newSymbolMap.put(decl, map) @@ -153,14 +169,22 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { val instantiations = declSpecs.keys.toArray var index = -1 println(s"specializing ${tree.symbol} for $origTParams") - newSyms.map { newSym => + newSyms.map { newSym => index += 1 polyDefDef(newSym.asTerm, { tparams => vparams => { - assert(tparams.isEmpty) + val tmap: (Tree => Tree) = _ match { + case Return(t, from) if from.symbol == tree.symbol => Return(t, ref(newSym)) + case t: TypeApply => transformTypeApply(t) + case t: Apply => transformApply(t) + case t => t + } + new TreeTypeMap( + treeMap = tmap, typeMap = _ - .substDealias(origTParams, instantiations(index)) - .subst(origVParams, vparams.flatten.map(_.tpe)), + .substDealias(origTParams, instantiations(index)) + .subst(origVParams, vparams.flatten.map(_.tpe)) + , oldOwners = tree.symbol :: Nil, newOwners = newSym :: Nil ).transform(tree.rhs) @@ -168,33 +192,62 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { } } else Nil } - Thicket(tree :: specialize(tree.symbol)) + val specialized_trees = specialize(tree.symbol) + Thicket(tree :: specialized_trees) case _ => tree } } override def transformTypeApply(tree: tpd.TypeApply)(implicit ctx: Context, info: TransformerInfo): Tree = { + val TypeApply(fun, _) = tree + if (fun.tpe.isParameterless) rewireTree(tree) + tree + } + override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = { + val Apply(fun, args) = tree + fun match { + case fun: TypeApply => { + println( + s""" + |args -> ${args} - def allowedToSpecialize(sym: Symbol): Boolean = { - sym.name != nme.asInstanceOf_ && - !(sym is Flags.JavaDefined) && - !sym.isConstructor//isPrimaryConstructor + |f.fun -> ${fun.fun.tree} + """.stripMargin) + + val newFun = rewireTree(fun) + if (fun ne newFun) { + val b = (args zip newFun.tpe.firstParamTypes) + val a = b.map{ + case (arg, tpe) => + arg.ensureConforms(tpe) + } + Apply(newFun,a) + /* zip (instantiations zip paramTypes)).map{ + case (argType, (specType, castType)) => argType.ensureConforms(specType)})*/ + } else tree + } + case _ => tree } + } + def rewireTree(tree: Tree)(implicit ctx: Context): Tree = { + assert(tree.isInstanceOf[TypeApply]) val TypeApply(fun,args) = tree if (newSymbolMap.contains(fun.symbol)){ val newSymInfos = newSymbolMap(fun.symbol) val betterDefs = newSymInfos.filter(x => (x._1 zip args).forall{a => - val specializedType = a._1 - val argType = a._2 + val specializedType = a._1 + val argType = a._2 argType.tpe <:< specializedType }).toList - if (betterDefs.length > 1) ctx.debuglog("Several specialized variants fit.") - //assert(betterDefs.length < 2) // TODO: How to select the best if there are several ? + if (betterDefs.length > 1) { + ctx.debuglog("Several specialized variants fit.") + tree + } - if (betterDefs.nonEmpty) { + else if (betterDefs.nonEmpty) { val best = betterDefs.head println(s"method ${fun.symbol.name} of ${fun.symbol.owner} rewired to specialized variant with type (${best._1})") val prefix = fun match { @@ -213,4 +266,3 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { } else tree } } - diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 3038e3cd39d0..9723bda27d62 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -220,16 +220,18 @@ class tests extends CompilerTest { val javaDir = "./tests/pos/java-interop/" @Test def java_all = compileFiles(javaDir, twice) - //@Test def specialization = compileFile(specialDir, "specialization")//, specialise) - //@Test def mutual_spec = compileFile(specialDir, "mutual_specialization") +*/ + //@Test def specialization = compileFile(specialDir, "specialization") + //@Test def mutual_spec = compileFile(specialDir, "mutual_specialization", List("-Xprint:all")) //@Test def return_spec = compileFile(specialDir, "return_specialization") - //@Test def nothing_spec = compileFile(specialDir, "nothing_specialization") - //@Test def method_in_class_spec = compileFile(specialDir, "method_in_class_specialization") - //@Test def method_in_method_spec = compileFile(specialDir, "method_in_method_specialization", List("-Xprint:all")) - @Test def type_check_spec = compileFile(specialDir, "type_check_specialization") - //@Test def bounds_spec = compileFile(specialDir, "bounds_specialization", List("-Xprint:all")) - //@Test def multi_spec = compileFile(specialDir, "multi_specialization", List("-Xprint:all")) - //@Test def pos_spec_all = compileFiles(specialDir) + // @Test def nothing_spec = compileFile(specialDir, "nothing_specialization") + // @Test def method_in_class_spec = compileFile(specialDir, "method_in_class_specialization") + // @Test def method_in_method_spec = compileFile(specialDir, "method_in_method_specialization") + // @Test def pos_type_check = compileFile(specialDir, "type_test") + // @Test def bounds_spec = compileFile(specialDir, "bounds_specialization") + // @Test def multi_spec = compileFile(specialDir, "multi_specialization") + // @Test def pos_spec_all = compileFiles(specialDir) + @Test def pos_this_specialization = compileFile(specialDir, "this_specialization", List("-Xprint:specialize")) //@Test def mini_method = compileFiles(miniMethodDir)//, List("-Xprint:all")) //@Test def mini_more = compileFiles(miniMoreDir)//, List("-Xprint:all")) diff --git a/tests/pos/specialization/return_specialization.scala b/tests/pos/specialization/return_specialization.scala index e2424c474aa9..8cfddbbd1f3d 100644 --- a/tests/pos/specialization/return_specialization.scala +++ b/tests/pos/specialization/return_specialization.scala @@ -1,6 +1,6 @@ object return_specialization { - def qwa[@specialized T](a: (String, String) => T, b: T): T = { - if(a ne this) return a("1", "2") + def qwa[@specialized(Int) T](a: (T, T) => T, b: T): T = { + if(a ne this) return a(b, b) else b } } diff --git a/tests/pos/specialization/this_specialization.scala b/tests/pos/specialization/this_specialization.scala new file mode 100644 index 000000000000..f809f4804a88 --- /dev/null +++ b/tests/pos/specialization/this_specialization.scala @@ -0,0 +1,8 @@ +sealed abstract class Foo[@specialized +A] { + def bop[@specialized B >: A]: Foo[B] = new Bar[B](this) + //def bip[@specialized C >: A, @specialized D >: A]: Foo[D] = new Cho[D, C](new Bar[C](this)) +} + +case class Bar[@specialized a](tl: Foo[a]) extends Foo[a] + +//case class Cho[@specialized c, @specialized d](tl: Bar[d]) extends Foo[c] \ No newline at end of file diff --git a/tests/pos/specialization/type_check_specialization.scala b/tests/pos/specialization/type_check_specialization.scala deleted file mode 100644 index 7c6a9c47f70e..000000000000 --- a/tests/pos/specialization/type_check_specialization.scala +++ /dev/null @@ -1,3 +0,0 @@ -object type_check_specialization { - def inner[@specialized(Char) I](i: I): Unit = i.isInstanceOf[Int] -} \ No newline at end of file diff --git a/tests/pos/specialization/type_test.scala b/tests/pos/specialization/type_test.scala new file mode 100644 index 000000000000..570cfdf33d54 --- /dev/null +++ b/tests/pos/specialization/type_test.scala @@ -0,0 +1,3 @@ +object type_test { + def typeTest(i: Char): Unit = i.isInstanceOf[Int] +} \ No newline at end of file From b6fcb63172b18ac6013a0e1a34e6bd795180d935 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Wed, 20 May 2015 15:29:48 +0200 Subject: [PATCH 11/37] Workaround https://github.com/lampepfl/dotty/issues/592 Insert casts all over the place in specialised methods. The way this fix is structured ensures that if #592 ever gets fixed, no casts will be inserted. --- src/dotty/tools/dotc/ast/TreeTypeMap.scala | 6 ++--- .../dotc/transform/TypeSpecializer.scala | 23 +++++++++++++++++-- .../specialization/this_specialization.scala | 10 ++++++-- 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/dotty/tools/dotc/ast/TreeTypeMap.scala b/src/dotty/tools/dotc/ast/TreeTypeMap.scala index ea7f3a0f3720..b8b5451b9abc 100644 --- a/src/dotty/tools/dotc/ast/TreeTypeMap.scala +++ b/src/dotty/tools/dotc/ast/TreeTypeMap.scala @@ -31,7 +31,7 @@ import dotty.tools.dotc.transform.SymUtils._ * gets two different denotations in the same period. Hence, if -Yno-double-bindings is * set, we would get a data race assertion error. */ -final class TreeTypeMap( +class TreeTypeMap( val typeMap: Type => Type = IdentityTypeMap, val treeMap: tpd.Tree => tpd.Tree = identity _, val oldOwners: List[Symbol] = Nil, @@ -60,7 +60,7 @@ final class TreeTypeMap( def mapType(tp: Type) = mapOwnerThis(typeMap(tp).substSym(substFrom, substTo)) - + private def updateDecls(prevStats: List[Tree], newStats: List[Tree]): Unit = if (prevStats.isEmpty) assert(newStats.isEmpty) else { @@ -75,7 +75,7 @@ final class TreeTypeMap( updateDecls(prevStats.tail, newStats.tail) } - override def transform(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = treeMap(tree) match { + override final def transform(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = treeMap(tree) match { case impl @ Template(constr, parents, self, _) => val tmap = withMappedSyms(localSyms(impl :: self :: Nil)) cpy.Template(impl)( diff --git a/src/dotty/tools/dotc/transform/TypeSpecializer.scala b/src/dotty/tools/dotc/transform/TypeSpecializer.scala index 0ec88bf858cc..23a1f552edec 100644 --- a/src/dotty/tools/dotc/transform/TypeSpecializer.scala +++ b/src/dotty/tools/dotc/transform/TypeSpecializer.scala @@ -175,11 +175,27 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { val tmap: (Tree => Tree) = _ match { case Return(t, from) if from.symbol == tree.symbol => Return(t, ref(newSym)) case t: TypeApply => transformTypeApply(t) - case t: Apply => transformApply(t) + case t: Apply => + transformApply(t) case t => t } + val tp = new TreeMap() { + // needed to workaround https://github.com/lampepfl/dotty/issues/592 + override def transform(t: Tree)(implicit ctx: Context) = super.transform(t) match { + case t @ Apply(fun, args) => + val newArgs = (args zip fun.tpe.firstParamTypes).map{case(t, tpe) => t.ensureConforms(tpe)} + if (sameTypes(args, newArgs)) { + t + } else tpd.Apply(fun, newArgs) + case t: ValDef => + cpy.ValDef(t)(rhs = t.rhs.ensureConforms(t.tpe.widen)) + case t: DefDef => + cpy.DefDef(t)(rhs = t.rhs.ensureConforms(t.tpe.finalResultType)) + case t => t + } + } - new TreeTypeMap( + val typesReplaced = new TreeTypeMap( treeMap = tmap, typeMap = _ .substDealias(origTParams, instantiations(index)) @@ -188,6 +204,9 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { oldOwners = tree.symbol :: Nil, newOwners = newSym :: Nil ).transform(tree.rhs) + + val expectedTypeFixed = tp.transform(typesReplaced) + expectedTypeFixed.ensureConforms(newSym.info.widen.finalResultType) }}) } } else Nil diff --git a/tests/pos/specialization/this_specialization.scala b/tests/pos/specialization/this_specialization.scala index f809f4804a88..40244f661f7a 100644 --- a/tests/pos/specialization/this_specialization.scala +++ b/tests/pos/specialization/this_specialization.scala @@ -1,8 +1,14 @@ -sealed abstract class Foo[@specialized +A] { +trait Foo[@specialized +A] { +// all those examples trigger bugs due to https://github.com/lampepfl/dotty/issues/592 def bop[@specialized B >: A]: Foo[B] = new Bar[B](this) + def gwa[@specialized B >: A]: Foo[B] = this + def gwd[@specialized B >: A]: Foo[B] = { + val d: Foo[B] = this + d + } //def bip[@specialized C >: A, @specialized D >: A]: Foo[D] = new Cho[D, C](new Bar[C](this)) } case class Bar[@specialized a](tl: Foo[a]) extends Foo[a] -//case class Cho[@specialized c, @specialized d](tl: Bar[d]) extends Foo[c] \ No newline at end of file +//case class Cho[@specialized c, @specialized d](tl: Bar[d]) extends Foo[c] From a80c4438f6e2018357cd21020f5a4011b35d3ddb Mon Sep 17 00:00:00 2001 From: AlexSikia Date: Sat, 30 May 2015 12:19:28 +0200 Subject: [PATCH 12/37] Add casts, and debug implementation An issue occurs when trying to specialize certain methods when relying on typer only - this is described by #592 , and occured in test `this_specialization`. # with '#' will be ignored, and an empty message aborts the commit. --- .../dotc/transform/TypeSpecializer.scala | 52 +++++++------------ test/dotc/tests.scala | 23 ++++---- .../method_in_method_specialization.scala | 6 +++ .../specialization/multi_specialization.scala | 4 ++ .../nothing_specialization.scala | 7 +-- tests/pos/specialization/specialization.scala | 9 ++-- 6 files changed, 49 insertions(+), 52 deletions(-) diff --git a/src/dotty/tools/dotc/transform/TypeSpecializer.scala b/src/dotty/tools/dotc/transform/TypeSpecializer.scala index 23a1f552edec..923b2798627e 100644 --- a/src/dotty/tools/dotc/transform/TypeSpecializer.scala +++ b/src/dotty/tools/dotc/transform/TypeSpecializer.scala @@ -2,7 +2,6 @@ package dotty.tools.dotc.transform import dotty.tools.dotc.ast.{tpd, TreeTypeMap} import dotty.tools.dotc.ast.Trees._ -import dotty.tools.dotc.core.Annotations.Annotation import dotty.tools.dotc.core.Contexts.Context import dotty.tools.dotc.core.Decorators.StringDecorator import dotty.tools.dotc.core.DenotTransformers.InfoTransformer @@ -13,6 +12,7 @@ import dotty.tools.dotc.core.Types._ import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, MiniPhaseTransform} import scala.collection.mutable import dotty.tools.dotc.core.StdNames.nme +import dotty.tools._ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { import tpd._ @@ -175,36 +175,34 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { val tmap: (Tree => Tree) = _ match { case Return(t, from) if from.symbol == tree.symbol => Return(t, ref(newSym)) case t: TypeApply => transformTypeApply(t) - case t: Apply => - transformApply(t) + case t: Apply => transformApply(t) case t => t } - val tp = new TreeMap() { - // needed to workaround https://github.com/lampepfl/dotty/issues/592 - override def transform(t: Tree)(implicit ctx: Context) = super.transform(t) match { - case t @ Apply(fun, args) => - val newArgs = (args zip fun.tpe.firstParamTypes).map{case(t, tpe) => t.ensureConforms(tpe)} - if (sameTypes(args, newArgs)) { - t - } else tpd.Apply(fun, newArgs) - case t: ValDef => - cpy.ValDef(t)(rhs = t.rhs.ensureConforms(t.tpe.widen)) - case t: DefDef => - cpy.DefDef(t)(rhs = t.rhs.ensureConforms(t.tpe.finalResultType)) - case t => t - } - } val typesReplaced = new TreeTypeMap( treeMap = tmap, typeMap = _ .substDealias(origTParams, instantiations(index)) - .subst(origVParams, vparams.flatten.map(_.tpe)) - , + .subst(origVParams, vparams.flatten.map(_.tpe)), oldOwners = tree.symbol :: Nil, newOwners = newSym :: Nil ).transform(tree.rhs) + val tp = new TreeMap() { + // needed to workaround https://github.com/lampepfl/dotty/issues/592 + override def transform(t: Tree)(implicit ctx: Context) = super.transform(t) match { + case t @ Apply(fun, args) => + assert(sameLength(args, fun.tpe.widen.firstParamTypes)) + val newArgs = (args zip fun.tpe.widen.firstParamTypes).map{case(t, tpe) => t.ensureConforms(tpe)} + if (sameTypes(args, newArgs)) { + t + } else tpd.Apply(fun, newArgs) + case t: ValDef => + cpy.ValDef(t)(rhs = if(t.rhs.isEmpty) EmptyTree else t.rhs.ensureConforms(t.tpt.tpe)) + case t: DefDef => + cpy.DefDef(t)(rhs = if(t.rhs.isEmpty) EmptyTree else t.rhs.ensureConforms(t.tpt.tpe)) + case t => t + }} val expectedTypeFixed = tp.transform(typesReplaced) expectedTypeFixed.ensureConforms(newSym.info.widen.finalResultType) }}) @@ -227,23 +225,13 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { val Apply(fun, args) = tree fun match { case fun: TypeApply => { - println( - s""" - |args -> ${args} - - |f.fun -> ${fun.fun.tree} - """.stripMargin) - val newFun = rewireTree(fun) if (fun ne newFun) { - val b = (args zip newFun.tpe.firstParamTypes) - val a = b.map{ + val as = (args zip newFun.tpe.widen.firstParamTypes).map{ case (arg, tpe) => arg.ensureConforms(tpe) } - Apply(newFun,a) - /* zip (instantiations zip paramTypes)).map{ - case (argType, (specType, castType)) => argType.ensureConforms(specType)})*/ + Apply(newFun,as) } else tree } case _ => tree diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 9723bda27d62..66313e52c7b4 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -219,19 +219,18 @@ class tests extends CompilerTest { val javaDir = "./tests/pos/java-interop/" @Test def java_all = compileFiles(javaDir, twice) - */ - //@Test def specialization = compileFile(specialDir, "specialization") - //@Test def mutual_spec = compileFile(specialDir, "mutual_specialization", List("-Xprint:all")) - //@Test def return_spec = compileFile(specialDir, "return_specialization") - // @Test def nothing_spec = compileFile(specialDir, "nothing_specialization") - // @Test def method_in_class_spec = compileFile(specialDir, "method_in_class_specialization") - // @Test def method_in_method_spec = compileFile(specialDir, "method_in_method_specialization") - // @Test def pos_type_check = compileFile(specialDir, "type_test") - // @Test def bounds_spec = compileFile(specialDir, "bounds_specialization") - // @Test def multi_spec = compileFile(specialDir, "multi_specialization") - // @Test def pos_spec_all = compileFiles(specialDir) - @Test def pos_this_specialization = compileFile(specialDir, "this_specialization", List("-Xprint:specialize")) + @Test def specialization = compileFile(specialDir, "specialization", List("-Xprint:specialize")) + @Test def mutual_spec = compileFile(specialDir, "mutual_specialization", List("-Xprint:specialize")) + @Test def return_spec = compileFile(specialDir, "return_specialization", List("-Xprint:specialize")) + @Test def nothing_spec = compileFile(specialDir, "nothing_specialization", List("-Xprint:specialize")) + @Test def method_in_class_spec = compileFile(specialDir, "method_in_class_specialization", List("-Xprint:specialize")) + @Test def method_in_method_spec = compileFile(specialDir, "method_in_method_specialization", List("-Xprint:specialize")) + @Test def pos_type_check = compileFile(specialDir, "type_test", List("-Xprint:specialize")) + @Test def bounds_spec = compileFile(specialDir, "bounds_specialization", List("-Xprint:specialize")) + @Test def multi_spec = compileFile(specialDir, "multi_specialization", List("-Xprint:specialize")) + @Test def pos_this_specialization = compileFile(specialDir, "this_specialization", List("-Xprint:specialize")) + @Test def pos_spec_all = compileFiles(specialDir) //@Test def mini_method = compileFiles(miniMethodDir)//, List("-Xprint:all")) //@Test def mini_more = compileFiles(miniMoreDir)//, List("-Xprint:all")) diff --git a/tests/pos/specialization/method_in_method_specialization.scala b/tests/pos/specialization/method_in_method_specialization.scala index 467510b24892..af5e6c535d1d 100644 --- a/tests/pos/specialization/method_in_method_specialization.scala +++ b/tests/pos/specialization/method_in_method_specialization.scala @@ -10,4 +10,10 @@ object method_in_method_specialization { outer(2) outer('d') + + def outer2[@specialized(Int) O](o: O): Int = { + def inner2[@specialized(Int) I] (i: I) = 1 + inner2(42) + } + outer2(1) } \ No newline at end of file diff --git a/tests/pos/specialization/multi_specialization.scala b/tests/pos/specialization/multi_specialization.scala index 19b276fc1c16..ad430050d0bf 100644 --- a/tests/pos/specialization/multi_specialization.scala +++ b/tests/pos/specialization/multi_specialization.scala @@ -2,4 +2,8 @@ object multi_specialization { def one[@specialized T](n: T): T = n def two[@specialized T, U](n: T, m: U): (T,U) = (n,m) def three[@specialized T, U, V](n: T, m: U, o: V): (T,U,V) = (n,m,o) + + one(1) + two(1,2) + two('a', null) } \ No newline at end of file diff --git a/tests/pos/specialization/nothing_specialization.scala b/tests/pos/specialization/nothing_specialization.scala index a6155da12753..a8586a90e1e9 100644 --- a/tests/pos/specialization/nothing_specialization.scala +++ b/tests/pos/specialization/nothing_specialization.scala @@ -1,7 +1,8 @@ object nothing_specialization { - def ret_nothing[@specialized T] = { + def ret_nothing[@specialized(Char) T] = { //val a: List[T] = List[Nothing]() - def apply[@specialized X](xs : X*) : List[X] = List(xs:_*) - def apply6[@specialized X](xs : Nothing*) : List[Nothing] = List(xs: _*) + def apply[@specialized(Char) X](xs : X*) : List[X] = List(xs:_*) + def apply6[@specialized(Char) X](xs : Nothing*) : List[Nothing] = List(xs: _*) + def apply2[@specialized(Long) U] = 1.asInstanceOf[U] } } diff --git a/tests/pos/specialization/specialization.scala b/tests/pos/specialization/specialization.scala index bed233a7d0aa..3fdc13b78c8f 100644 --- a/tests/pos/specialization/specialization.scala +++ b/tests/pos/specialization/specialization.scala @@ -1,15 +1,14 @@ -class specialization { +trait specialization { def printer1[@specialized(Int, Long) T](a: T) = { println(a.toString) } - def printer2[@specialized(Int, Long) T, U](a: T, b: U) = { println(a.toString + b.toString) } - def print(a: Int) = { - printer1(a) + def print(i: Int) = { + printer1(i) println(" ---- ") - printer2(a,a) + printer2(i,i) } print(9) } From 843f5477067d13d45c32a33b2be8933b6aff1f1e Mon Sep 17 00:00:00 2001 From: AlexSikia Date: Wed, 3 Jun 2015 02:09:52 +0200 Subject: [PATCH 13/37] Do not look into scala- or java-defined symbols This caused errors by forcing evaluation on symbols undefined at the current phase or run. --- src/dotty/tools/dotc/Compiler.scala | 46 +++--- .../tools/dotc/transform/PreSpecializer.scala | 98 ++++++------ .../dotc/transform/TypeSpecializer.scala | 145 +++++++++--------- .../tools/dotc/transform/ValueClasses.scala | 1 + test/dotc/tests.scala | 68 ++++---- 5 files changed, 170 insertions(+), 188 deletions(-) diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index b141971497f8..e1bb4a0602bb 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -20,21 +20,21 @@ import dotty.tools.backend.jvm.{LabelDefs, GenBCode} class Compiler { /** Meta-ordering constraint: - * - * DenotTransformers that change the signature of their denotation's info must go - * after erasure. The reason is that denotations are permanently referred to by - * TermRefs which contain a signature. If the signature of a symbol would change, - * all refs to it would become outdated - they could not be dereferenced in the - * new phase. - * - * After erasure, signature changing denot-transformers are OK because erasure - * will make sure that only term refs with fixed SymDenotations survive beyond it. This - * is possible because: - * - * - splitter has run, so every ident or select refers to a unique symbol - * - after erasure, asSeenFrom is the identity, so every reference has a - * plain SymDenotation, as opposed to a UniqueRefDenotation. - */ + * + * DenotTransformers that change the signature of their denotation's info must go + * after erasure. The reason is that denotations are permanently referred to by + * TermRefs which contain a signature. If the signature of a symbol would change, + * all refs to it would become outdated - they could not be dereferenced in the + * new phase. + * + * After erasure, signature changing denot-transformers are OK because erasure + * will make sure that only term refs with fixed SymDenotations survive beyond it. This + * is possible because: + * + * - splitter has run, so every ident or select refers to a unique symbol + * - after erasure, asSeenFrom is the identity, so every reference has a + * plain SymDenotation, as opposed to a UniqueRefDenotation. + */ def phases: List[List[Phase]] = List( List(new FrontEnd), @@ -93,13 +93,13 @@ class Compiler { } /** Produces the following contexts, from outermost to innermost - * - * bootStrap: A context with next available runId and a scope consisting of - * the RootPackage _root_ - * start A context with RootClass as owner and the necessary initializations - * for type checking. - * imports For each element of RootImports, an import context - */ + * + * bootStrap: A context with next available runId and a scope consisting of + * the RootPackage _root_ + * start A context with RootClass as owner and the necessary initializations + * for type checking. + * imports For each element of RootImports, an import context + */ def rootContext(implicit ctx: Context): Context = { ctx.definitions.init(ctx) ctx.setPhasePlan(phases) @@ -130,4 +130,4 @@ class Compiler { reset() new Run(this)(rootContext) } -} +} \ No newline at end of file diff --git a/src/dotty/tools/dotc/transform/PreSpecializer.scala b/src/dotty/tools/dotc/transform/PreSpecializer.scala index 6b5ddc5ab7e7..4a07a1ce5dbd 100644 --- a/src/dotty/tools/dotc/transform/PreSpecializer.scala +++ b/src/dotty/tools/dotc/transform/PreSpecializer.scala @@ -4,69 +4,24 @@ import dotty.tools.dotc.ast.Trees.{Select, Ident, SeqLiteral, Typed} import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.core.Annotations.Annotation import dotty.tools.dotc.core.Contexts.Context -import dotty.tools.dotc.core.DenotTransformers.InfoTransformer import dotty.tools.dotc.core.StdNames._ -import dotty.tools.dotc.core.{Flags, Definitions, Symbols} +import dotty.tools.dotc.core.{Flags, Definitions} import dotty.tools.dotc.core.Symbols.Symbol -import dotty.tools.dotc.core.Types.{TermRef, TypeRef, OrType, Type} +import dotty.tools.dotc.core.Types.Type import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, MiniPhaseTransform} -import scala.collection.mutable - /** * This phase retrieves all `@specialized` anotations before they are thrown away, * and stores them for the `TypeSpecializer` phase. */ -class PreSpecializer extends MiniPhaseTransform with InfoTransformer { +class PreSpecializer extends MiniPhaseTransform { override def phaseName: String = "prespecialize" - private val specTypes: mutable.HashMap[Symbols.Symbol, List[Type]] = mutable.HashMap.empty - - override def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = { - - def getSpecTypes(sym: Symbol)(implicit ctx: Context): List[Type] = { - - def allowedToSpecialize(sym: Symbol): Boolean = { - sym.name != nme.asInstanceOf_ && - sym.name != nme.isInstanceOf_ && - !(sym is Flags.JavaDefined) && - !sym.isConstructor//isPrimaryConstructor - } - - if (allowedToSpecialize(sym)) { - val annotation = sym.denot.getAnnotation(ctx.definitions.specializedAnnot).getOrElse(Nil) - annotation match { - case annot: Annotation => - val args = annot.arguments - if (args.isEmpty) primitiveTypes - else args.head match { - case a@Typed(SeqLiteral(types), _) => types.map(t => nameToType(t.tpe)) // Matches the expected `@specialized(...)` annotations - case a@Select(Ident(_), _) => primitiveTypes // Matches `Select(Ident(Specializable), Primitives)` which is used in several instances - case _ => ctx.error("surprising match on specialized annotation"); Nil - } - case nil => Nil - } - } else Nil - } - val st = getSpecTypes(sym) - if (st.nonEmpty) { - specTypes.put(sym.owner, st) - } - tp - } - - private final def nameToType(name: Type)(implicit ctx: Context) = - name.asInstanceOf[TermRef].name.toString match { - case s if s.startsWith("Int") => defn.IntType - case s if s.startsWith("Boolean") => defn.BooleanType - case s if s.startsWith("Byte") => defn.ByteType - case s if s.startsWith("Long") => defn.LongType - case s if s.startsWith("Short") => defn.ShortType - case s if s.startsWith("Float") => defn.FloatType - case s if s.startsWith("Unit") => defn.UnitType - case s if s.startsWith("Double") => defn.DoubleType - case s if s.startsWith("Char") => defn.CharType + private final def primitiveCompanionToPrimitive(companion: Type)(implicit ctx: Context) = { + val claz = companion.termSymbol.companionClass + assert(ctx.definitions.ScalaValueClasses.contains(claz)) + claz.typeRef } def defn(implicit ctx: Context): Definitions = ctx.definitions @@ -83,9 +38,42 @@ class PreSpecializer extends MiniPhaseTransform with InfoTransformer { ctx.definitions.UnitType ) + def getSpec(sym: Symbol)(implicit ctx: Context): List[Type] = { + + def allowedToSpecialize(sym: Symbol): Boolean = { + sym.name != nme.asInstanceOf_ && + sym.name != nme.isInstanceOf_ && + !(sym is Flags.JavaDefined) && + !sym.isConstructor && + !sym.name.toString.contains("Function2") + } + + if (allowedToSpecialize(sym)) { + val annotation = sym.denot.getAnnotation(ctx.definitions.specializedAnnot).getOrElse(Nil) + annotation match { + case annot: Annotation => + val args = annot.arguments + if (args.isEmpty) primitiveTypes + else args.head match { + case a@Typed(SeqLiteral(types), _) => types.map(t => primitiveCompanionToPrimitive(t.tpe)) // Matches the expected `@specialized(...)` annotations + case a@Select(Ident(_), _) => primitiveTypes // Matches `Select(Ident(Specializable), Primitives)` which is used in several instances + case _ => ctx.error("surprising match on specialized annotation"); Nil + } + case nil => Nil + } + } else Nil + } + override def transformDefDef(tree: tpd.DefDef)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { - val st = specTypes.getOrElse(tree.symbol, List()) - if (st.nonEmpty) ctx.specializePhase.asInstanceOf[TypeSpecializer].registerSpecializationRequest(tree.symbol)(st) + val tparams = tree.tparams.map(_.symbol) + val st = tparams.map(getSpec) + if (st.nonEmpty) { + st.map{ + case (types: List[Type]) if types.nonEmpty => + ctx.specializePhase.asInstanceOf[TypeSpecializer].registerSpecializationRequest(tree.symbol)(types) + case _ => + } + } tree } -} +} \ No newline at end of file diff --git a/src/dotty/tools/dotc/transform/TypeSpecializer.scala b/src/dotty/tools/dotc/transform/TypeSpecializer.scala index 923b2798627e..7ee5bf91243c 100644 --- a/src/dotty/tools/dotc/transform/TypeSpecializer.scala +++ b/src/dotty/tools/dotc/transform/TypeSpecializer.scala @@ -15,21 +15,22 @@ import dotty.tools.dotc.core.StdNames.nme import dotty.tools._ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { + import tpd._ override def phaseName = "specialize" final val maxTparamsToSpecialize = 2 private final def specialisedTypeToSuffix(implicit ctx: Context) = - Map(defn.ByteType -> "$mcB$sp", - defn.BooleanType -> "$mcZ$sp", - defn.ShortType -> "$mcS$sp", - defn.IntType -> "$mcI$sp", - defn.LongType -> "$mcJ$sp", - defn.FloatType -> "$mcF$sp", - defn.DoubleType -> "$mcD$sp", - defn.CharType -> "$mcC$sp", - defn.UnitType -> "$mcV$sp") + Map(defn.ByteType -> "B", + defn.BooleanType -> "Z", + defn.ShortType -> "S", + defn.IntType -> "I", + defn.LongType -> "J", + defn.FloatType -> "F", + defn.DoubleType -> "D", + defn.CharType -> "C", + defn.UnitType -> "V") private def primitiveTypes(implicit ctx: Context) = List(defn.ByteType, @@ -45,7 +46,7 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { private def defn(implicit ctx:Context) = ctx.definitions - private val specializationRequests: mutable.HashMap[Symbols.Symbol, List[Type]] = mutable.HashMap.empty + private var specializationRequests: mutable.HashMap[Symbols.Symbol, List[Type]] = mutable.HashMap.empty /** * A map that links symbols to their specialized variants. @@ -63,13 +64,13 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { !sym.name.toString.contains("Function2") } - def getSpecTypes(sym: Symbol, poly: PolyType)(implicit ctx: Context): List[Type] = { - val requested = specializationRequests.getOrElse(sym, List()) - if (requested.nonEmpty) requested.toList + def getSpecTypes(method: Symbol, poly: PolyType)(implicit ctx: Context): List[Type] = { + val requested = specializationRequests.getOrElse(method, List.empty) + if (requested.nonEmpty) requested else { - if(ctx.settings.Yspecialize.value == "all") primitiveTypes + if(ctx.settings.Yspecialize.value == "all") primitiveTypes.filter(tpe => poly.paramBounds.forall(_.contains(tpe))) else Nil - }.filter(tpe => poly.paramBounds.forall(_.contains(tpe))) + } } def requestedSpecialization(decl: Symbol)(implicit ctx: Context): Boolean = @@ -81,7 +82,7 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { if(ctx.phaseId > this.treeTransformPhase.id) assert(ctx.phaseId <= this.treeTransformPhase.id) val prev = specializationRequests.getOrElse(method, List.empty) - specializationRequests.put(method, (arguments ::: prev).toSet.toList) + specializationRequests.put(method, arguments ::: prev) } override def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = { @@ -100,7 +101,7 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { def generateSpecializedSymbols(instantiations: List[Type], names: List[String], poly: PolyType, decl: Symbol) (implicit ctx: Context): List[Symbol] = { val newSym = - ctx.newSymbol(decl.owner, (decl.name + names.mkString).toTermName, + ctx.newSymbol(decl.owner, (decl.name + "$mc" + names.mkString + "$sp").toTermName, decl.flags | Flags.Synthetic, poly.instantiate(instantiations.toList)) /* The following generated symbols which kept type bounds. It served, as illustrated by the `this_specialization` @@ -125,26 +126,30 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { } if((sym ne defn.ScalaPredefModule.moduleClass) && - !(sym is Flags.Package) && - !sym.isAnonymousClass) { + !(sym is Flags.JavaDefined) && + !(sym is Flags.Scala2x) && + !(sym is Flags.Package) && + !sym.isAnonymousClass) { sym.info match { case classInfo: ClassInfo => val newDecls = classInfo.decls + .filter(_.symbol.isCompleted) // we do not want to force symbols here. + // if there's unforced symbol it means its not used in the source .filterNot(_.isConstructor) .filter(requestedSpecialization) .flatMap(decl => { - decl.info.widen match { - case poly: PolyType if allowedToSpecialize(decl.symbol, poly.paramNames.length) => - generateSpecializations(poly.paramNames, getSpecTypes(decl, poly))(List.empty, List.empty, poly, decl) - case nil => Nil - } + decl.info.widen match { + case poly: PolyType if allowedToSpecialize(decl.symbol, poly.paramNames.length) => + generateSpecializations(poly.paramNames, getSpecTypes(decl, poly))(List.empty, List.empty, poly, decl) + case nil => Nil + } }) - val decls = classInfo.decls.cloneScope - newDecls.foreach(decls.enter) - classInfo.derivedClassInfo(decls = decls) + val decls = classInfo.decls.cloneScope + newDecls.foreach(decls.enter) + classInfo.derivedClassInfo(decls = decls) case poly: PolyType if !newSymbolMap.contains(sym) && - requestedSpecialization(sym) && - allowedToSpecialize(sym, poly.paramNames.length)=> + requestedSpecialization(sym) && + allowedToSpecialize(sym, poly.paramNames.length)=> generateSpecializations(poly.paramNames, getSpecTypes(sym, poly))(List.empty, List.empty, poly, sym) case nil => } @@ -170,44 +175,44 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { var index = -1 println(s"specializing ${tree.symbol} for $origTParams") newSyms.map { newSym => - index += 1 - polyDefDef(newSym.asTerm, { tparams => vparams => { - val tmap: (Tree => Tree) = _ match { - case Return(t, from) if from.symbol == tree.symbol => Return(t, ref(newSym)) - case t: TypeApply => transformTypeApply(t) - case t: Apply => transformApply(t) - case t => t - } + index += 1 + polyDefDef(newSym.asTerm, { tparams => vparams => { + val tmap: (Tree => Tree) = _ match { + case Return(t, from) if from.symbol == tree.symbol => Return(t, ref(newSym)) + case t: TypeApply => transformTypeApply(t) + case t: Apply => transformApply(t) + case t => t + } - val typesReplaced = new TreeTypeMap( - treeMap = tmap, - typeMap = _ + val typesReplaced = new TreeTypeMap( + treeMap = tmap, + typeMap = _ .substDealias(origTParams, instantiations(index)) .subst(origVParams, vparams.flatten.map(_.tpe)), - oldOwners = tree.symbol :: Nil, - newOwners = newSym :: Nil - ).transform(tree.rhs) + oldOwners = tree.symbol :: Nil, + newOwners = newSym :: Nil + ).transform(tree.rhs) - val tp = new TreeMap() { - // needed to workaround https://github.com/lampepfl/dotty/issues/592 - override def transform(t: Tree)(implicit ctx: Context) = super.transform(t) match { - case t @ Apply(fun, args) => - assert(sameLength(args, fun.tpe.widen.firstParamTypes)) - val newArgs = (args zip fun.tpe.widen.firstParamTypes).map{case(t, tpe) => t.ensureConforms(tpe)} - if (sameTypes(args, newArgs)) { - t - } else tpd.Apply(fun, newArgs) - case t: ValDef => - cpy.ValDef(t)(rhs = if(t.rhs.isEmpty) EmptyTree else t.rhs.ensureConforms(t.tpt.tpe)) - case t: DefDef => - cpy.DefDef(t)(rhs = if(t.rhs.isEmpty) EmptyTree else t.rhs.ensureConforms(t.tpt.tpe)) - case t => t - }} - val expectedTypeFixed = tp.transform(typesReplaced) - expectedTypeFixed.ensureConforms(newSym.info.widen.finalResultType) - }}) - } - } else Nil + val tp = new TreeMap() { + // needed to workaround https://github.com/lampepfl/dotty/issues/592 + override def transform(t: Tree)(implicit ctx: Context) = super.transform(t) match { + case t @ Apply(fun, args) => + assert(sameLength(args, fun.tpe.widen.firstParamTypes)) + val newArgs = (args zip fun.tpe.widen.firstParamTypes).map{case(t, tpe) => t.ensureConforms(tpe)} + if (sameTypes(args, newArgs)) { + t + } else tpd.Apply(fun, newArgs) + case t: ValDef => + cpy.ValDef(t)(rhs = if(t.rhs.isEmpty) EmptyTree else t.rhs.ensureConforms(t.tpt.tpe)) + case t: DefDef => + cpy.DefDef(t)(rhs = if(t.rhs.isEmpty) EmptyTree else t.rhs.ensureConforms(t.tpt.tpe)) + case t => t + }} + val expectedTypeFixed = tp.transform(typesReplaced) + expectedTypeFixed.ensureConforms(newSym.info.widen.finalResultType) + }}) + } + } else Nil } val specialized_trees = specialize(tree.symbol) Thicket(tree :: specialized_trees) @@ -227,11 +232,11 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { case fun: TypeApply => { val newFun = rewireTree(fun) if (fun ne newFun) { - val as = (args zip newFun.tpe.widen.firstParamTypes).map{ - case (arg, tpe) => - arg.ensureConforms(tpe) - } - Apply(newFun,as) + val as = (args zip newFun.tpe.widen.firstParamTypes).map{ + case (arg, tpe) => + arg.ensureConforms(tpe) + } + Apply(newFun,as) } else tree } case _ => tree @@ -256,7 +261,7 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { else if (betterDefs.nonEmpty) { val best = betterDefs.head - println(s"method ${fun.symbol.name} of ${fun.symbol.owner} rewired to specialized variant with type (${best._1})") + println(s"method ${fun.symbol.name} of ${fun.symbol.owner} rewired to specialized variant with type(s) : ${best._1.map{case TypeRef(_, name) => name}.mkString(", ")}") val prefix = fun match { case Select(pre, name) => pre @@ -272,4 +277,4 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { } else tree } else tree } -} +} \ No newline at end of file diff --git a/src/dotty/tools/dotc/transform/ValueClasses.scala b/src/dotty/tools/dotc/transform/ValueClasses.scala index 93005c57ae26..f0762b406064 100644 --- a/src/dotty/tools/dotc/transform/ValueClasses.scala +++ b/src/dotty/tools/dotc/transform/ValueClasses.scala @@ -22,6 +22,7 @@ object ValueClasses { def isMethodWithExtension(d: SymDenotation)(implicit ctx: Context) = d.isRealMethod && + !(d.initial.validFor.firstPhaseId > ctx.extensionMethodsPhase.id) && isDerivedValueClass(d.owner) && !d.isConstructor && !d.is(SuperAccessor) && diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 66313e52c7b4..f7751b4ea9a1 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -11,12 +11,12 @@ class tests extends CompilerTest { def isRunByJenkins: Boolean = sys.props.isDefinedAt("dotty.jenkins.build") val noCheckOptions = List( -// "-verbose", -// "-Ylog:frontend", -// "-Xprompt", -// "-explaintypes", -// "-Yshow-suppressed-errors", - ) + // "-verbose", + // "-Ylog:frontend", + // "-Xprompt", + // "-explaintypes", + // "-Yshow-suppressed-errors", + ) val defaultOutputDir = "./out/" @@ -54,9 +54,11 @@ class tests extends CompilerTest { val dotcDir = toolsDir + "dotc/" val coreDir = dotcDir + "core/" +/* + @Test def pickle_pickleOK = compileDir(testsDir, "pickling", testPickling) -// This directory doesn't exist anymore -// @Test def pickle_pickling = compileDir(coreDir, "pickling", testPickling) + // This directory doesn't exist anymore + // @Test def pickle_pickling = compileDir(coreDir, "pickling", testPickling) @Test def pickle_ast = compileDir(dotcDir, "ast", testPickling) //@Test def pickle_core = compileDir(dotcDir, "core", testPickling, xerrors = 2) // two spurious comparison errors in Types and TypeOps @@ -93,21 +95,14 @@ class tests extends CompilerTest { @Test def pos_nullarify = compileFile(posDir, "nullarify", args = "-Ycheck:nullarify" :: Nil) @Test def pos_subtyping = compileFile(posDir, "subtyping", twice) @Test def pos_t2613 = compileFile(posSpecialDir, "t2613")(allowDeepSubtypes) - @Test def pos_packageObj = compileFile(posDir, "i0239") - @Test def pos_anonClassSubtyping = compileFile(posDir, "anonClassSubtyping") - - @Test def pos_specialization = compileFile(posDir, "specialization") + @Test def pos_packageObj = compileFile(posDir, "i0239", twice) + @Test def pos_anonClassSubtyping = compileFile(posDir, "anonClassSubtyping", twice) + @Test def pos_extmethods = compileFile(posDir, "extmethods", twice) + //@Test def pos_specialization = compileFile(posDir, "specialization") @Test def pos_all = compileFiles(posDir) // twice omitted to make tests run faster - @Test def pos_specialization = compileFile(posDir, "specialization") - - @Test def pos_all = compileFiles(posDir, failedOther) - - @Test def pos_SI7638 = compileFile(posDir, "SI-7638") - @Test def pos_SI7638a = compileFile(posDir, "SI-7638a") - - //@Test def new_all = compileFiles(newDir, twice) + @Test def new_all = compileFiles(newDir, twice) @Test def neg_blockescapes() = compileFile(negDir, "blockescapesNeg", xerrors = 1) @Test def neg_typedapply() = compileFile(negDir, "typedapply", xerrors = 4) @@ -178,15 +173,6 @@ class tests extends CompilerTest { @Test def dotc_transform = compileDir(dotcDir, "transform")// twice omitted to make tests run faster - val javaDir = "./tests/pos/java-interop/" - @Test def java_all = compileFiles(javaDir) - - @Test def pos_specialization = compileFile(posDir, "specialization") - - //@Test def dotc_compilercommand = compileFile(dotcDir + "tools/dotc/config/", "CompilerCommand") -//@ Test def dotc_transform = compileDir(dotcDir + "tools/dotc/transform", failedbyName) - - @Test def dotc_parsing = compileDir(dotcDir, "parsing") // twice omitted to make tests run faster @Test def dotc_printing = compileDir(dotcDir, "printing") // twice omitted to make tests run faster @@ -194,9 +180,9 @@ class tests extends CompilerTest { @Test def dotc_reporting = compileDir(dotcDir, "reporting") // twice omitted to make tests run faster @Test def dotc_typer = compileDir(dotcDir, "typer")// twice omitted to make tests run faster - // error: error while loading Checking$$anon$2$, - // class file 'target/scala-2.11/dotty_2.11-0.1-SNAPSHOT.jar(dotty/tools/dotc/typer/Checking$$anon$2.class)' - // has location not matching its contents: contains class $anon + // error: error while loading Checking$$anon$2$, + // class file 'target/scala-2.11/dotty_2.11-0.1-SNAPSHOT.jar(dotty/tools/dotc/typer/Checking$$anon$2.class)' + // has location not matching its contents: contains class $anon @Test def dotc_util = compileDir(dotcDir, "util") // twice omitted to make tests run faster @@ -207,15 +193,15 @@ class tests extends CompilerTest { //@Test def tools = compileDir(dottyDir, "tools", "-deep" :: Nil)(allowDeepSubtypes) @Test def testNonCyclic = compileList("testNonCyclic", List( - dotcDir + "CompilationUnit.scala", - coreDir + "Types.scala", - dotcDir + "ast/Trees.scala" - ), List("-Xprompt") ++ staleSymbolError ++ twice) + dotcDir + "CompilationUnit.scala", + coreDir + "Types.scala", + dotcDir + "ast/Trees.scala" + ), List("-Xprompt") ++ staleSymbolError ++ twice) @Test def testIssue_34 = compileList("testIssue_34", List( - dotcDir + "config/Properties.scala", - dotcDir + "config/PathResolver.scala" - ), List(/* "-Ylog:frontend", */ "-Xprompt") ++ staleSymbolError ++ twice) + dotcDir + "config/Properties.scala", + dotcDir + "config/PathResolver.scala" + ), List(/* "-Ylog:frontend", */ "-Xprompt") ++ staleSymbolError ++ twice) val javaDir = "./tests/pos/java-interop/" @Test def java_all = compileFiles(javaDir, twice) @@ -232,10 +218,12 @@ class tests extends CompilerTest { @Test def pos_this_specialization = compileFile(specialDir, "this_specialization", List("-Xprint:specialize")) @Test def pos_spec_all = compileFiles(specialDir) + //@Test def mini_method = compileFiles(miniMethodDir)//, List("-Xprint:all")) //@Test def mini_more = compileFiles(miniMoreDir)//, List("-Xprint:all")) //@Test def pos_all = compileFiles(posDir)//, List("-Xprint:all")) //@Test def pos_si7638 = compileFile(posDir, "SI-7638", List("-Xprint:all")) //@Test def test = compileFile(posDir, "t247", List("-Xprint:all")) -} + //@Test def dotc_compilercommand = compileFile(dotcDir + "config/", "CompilerCommand") +} \ No newline at end of file From deeea643bc06431e268f35d9a739f192ad644f5e Mon Sep 17 00:00:00 2001 From: AlexSikia Date: Wed, 3 Jun 2015 02:49:31 +0200 Subject: [PATCH 14/37] Add run test for specialisation Adds a simple run test which checks for an expected number of generated specialised methods, and correct parameter types instantiation. --- src/dotty/tools/dotc/Compiler.scala | 44 +++++++++++++------------- test/dotc/tests.scala | 49 +++++++++++++---------------- tests/run/specialization.check | 6 ++++ tests/run/specialization.scala | 41 ++++++++++++++++++++++++ 4 files changed, 90 insertions(+), 50 deletions(-) create mode 100644 tests/run/specialization.check create mode 100644 tests/run/specialization.scala diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index e1bb4a0602bb..5c2ed71cd2f4 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -20,21 +20,21 @@ import dotty.tools.backend.jvm.{LabelDefs, GenBCode} class Compiler { /** Meta-ordering constraint: - * - * DenotTransformers that change the signature of their denotation's info must go - * after erasure. The reason is that denotations are permanently referred to by - * TermRefs which contain a signature. If the signature of a symbol would change, - * all refs to it would become outdated - they could not be dereferenced in the - * new phase. - * - * After erasure, signature changing denot-transformers are OK because erasure - * will make sure that only term refs with fixed SymDenotations survive beyond it. This - * is possible because: - * - * - splitter has run, so every ident or select refers to a unique symbol - * - after erasure, asSeenFrom is the identity, so every reference has a - * plain SymDenotation, as opposed to a UniqueRefDenotation. - */ + * + * DenotTransformers that change the signature of their denotation's info must go + * after erasure. The reason is that denotations are permanently referred to by + * TermRefs which contain a signature. If the signature of a symbol would change, + * all refs to it would become outdated - they could not be dereferenced in the + * new phase. + * + * After erasure, signature changing denot-transformers are OK because erasure + * will make sure that only term refs with fixed SymDenotations survive beyond it. This + * is possible because: + * + * - splitter has run, so every ident or select refers to a unique symbol + * - after erasure, asSeenFrom is the identity, so every reference has a + * plain SymDenotation, as opposed to a UniqueRefDenotation. + */ def phases: List[List[Phase]] = List( List(new FrontEnd), @@ -93,13 +93,13 @@ class Compiler { } /** Produces the following contexts, from outermost to innermost - * - * bootStrap: A context with next available runId and a scope consisting of - * the RootPackage _root_ - * start A context with RootClass as owner and the necessary initializations - * for type checking. - * imports For each element of RootImports, an import context - */ + * + * bootStrap: A context with next available runId and a scope consisting of + * the RootPackage _root_ + * start A context with RootClass as owner and the necessary initializations + * for type checking. + * imports For each element of RootImports, an import context + */ def rootContext(implicit ctx: Context): Context = { ctx.definitions.init(ctx) ctx.setPhasePlan(phases) diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index f7751b4ea9a1..49f12fb046c0 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -11,11 +11,11 @@ class tests extends CompilerTest { def isRunByJenkins: Boolean = sys.props.isDefinedAt("dotty.jenkins.build") val noCheckOptions = List( - // "-verbose", - // "-Ylog:frontend", - // "-Xprompt", - // "-explaintypes", - // "-Yshow-suppressed-errors", +// "-verbose", +// "-Ylog:frontend", +// "-Xprompt", +// "-explaintypes", +// "-Yshow-suppressed-errors", ) val defaultOutputDir = "./out/" @@ -54,11 +54,10 @@ class tests extends CompilerTest { val dotcDir = toolsDir + "dotc/" val coreDir = dotcDir + "core/" -/* @Test def pickle_pickleOK = compileDir(testsDir, "pickling", testPickling) - // This directory doesn't exist anymore - // @Test def pickle_pickling = compileDir(coreDir, "pickling", testPickling) +// This directory doesn't exist anymore +// @Test def pickle_pickling = compileDir(coreDir, "pickling", testPickling) @Test def pickle_ast = compileDir(dotcDir, "ast", testPickling) //@Test def pickle_core = compileDir(dotcDir, "core", testPickling, xerrors = 2) // two spurious comparison errors in Types and TypeOps @@ -98,7 +97,6 @@ class tests extends CompilerTest { @Test def pos_packageObj = compileFile(posDir, "i0239", twice) @Test def pos_anonClassSubtyping = compileFile(posDir, "anonClassSubtyping", twice) @Test def pos_extmethods = compileFile(posDir, "extmethods", twice) - //@Test def pos_specialization = compileFile(posDir, "specialization") @Test def pos_all = compileFiles(posDir) // twice omitted to make tests run faster @@ -180,9 +178,9 @@ class tests extends CompilerTest { @Test def dotc_reporting = compileDir(dotcDir, "reporting") // twice omitted to make tests run faster @Test def dotc_typer = compileDir(dotcDir, "typer")// twice omitted to make tests run faster - // error: error while loading Checking$$anon$2$, - // class file 'target/scala-2.11/dotty_2.11-0.1-SNAPSHOT.jar(dotty/tools/dotc/typer/Checking$$anon$2.class)' - // has location not matching its contents: contains class $anon + // error: error while loading Checking$$anon$2$, + // class file 'target/scala-2.11/dotty_2.11-0.1-SNAPSHOT.jar(dotty/tools/dotc/typer/Checking$$anon$2.class)' + // has location not matching its contents: contains class $anon @Test def dotc_util = compileDir(dotcDir, "util") // twice omitted to make tests run faster @@ -193,19 +191,19 @@ class tests extends CompilerTest { //@Test def tools = compileDir(dottyDir, "tools", "-deep" :: Nil)(allowDeepSubtypes) @Test def testNonCyclic = compileList("testNonCyclic", List( - dotcDir + "CompilationUnit.scala", - coreDir + "Types.scala", - dotcDir + "ast/Trees.scala" - ), List("-Xprompt") ++ staleSymbolError ++ twice) + dotcDir + "CompilationUnit.scala", + coreDir + "Types.scala", + dotcDir + "ast/Trees.scala" + ), List("-Xprompt") ++ staleSymbolError ++ twice) @Test def testIssue_34 = compileList("testIssue_34", List( - dotcDir + "config/Properties.scala", - dotcDir + "config/PathResolver.scala" - ), List(/* "-Ylog:frontend", */ "-Xprompt") ++ staleSymbolError ++ twice) + dotcDir + "config/Properties.scala", + dotcDir + "config/PathResolver.scala" + ), List(/* "-Ylog:frontend", */ "-Xprompt") ++ staleSymbolError ++ twice) val javaDir = "./tests/pos/java-interop/" @Test def java_all = compileFiles(javaDir, twice) -*/ + @Test def specialization = compileFile(specialDir, "specialization", List("-Xprint:specialize")) @Test def mutual_spec = compileFile(specialDir, "mutual_specialization", List("-Xprint:specialize")) @Test def return_spec = compileFile(specialDir, "return_specialization", List("-Xprint:specialize")) @@ -218,12 +216,7 @@ class tests extends CompilerTest { @Test def pos_this_specialization = compileFile(specialDir, "this_specialization", List("-Xprint:specialize")) @Test def pos_spec_all = compileFiles(specialDir) - - //@Test def mini_method = compileFiles(miniMethodDir)//, List("-Xprint:all")) - //@Test def mini_more = compileFiles(miniMoreDir)//, List("-Xprint:all")) - //@Test def pos_all = compileFiles(posDir)//, List("-Xprint:all")) - - //@Test def pos_si7638 = compileFile(posDir, "SI-7638", List("-Xprint:all")) - //@Test def test = compileFile(posDir, "t247", List("-Xprint:all")) + @Test def run_spec = runFile(runDir, "specialization") + //@Test def dotc_compilercommand = compileFile(dotcDir + "config/", "CompilerCommand") -} \ No newline at end of file +} diff --git a/tests/run/specialization.check b/tests/run/specialization.check new file mode 100644 index 000000000000..52f223d0a6f9 --- /dev/null +++ b/tests/run/specialization.check @@ -0,0 +1,6 @@ +10 +82 +3 +int +int,double +double,int \ No newline at end of file diff --git a/tests/run/specialization.scala b/tests/run/specialization.scala new file mode 100644 index 000000000000..760f46093964 --- /dev/null +++ b/tests/run/specialization.scala @@ -0,0 +1,41 @@ +object Test extends dotty.runtime.LegacyApp { + + class Foo { + def foo[@specialized U](u: U) = u + } + class Bar { + def bar[@specialized U, V](u: U, v: V) = v + } + class Baz { + def baz[@specialized(Int, Char) V](v: V): V = v + } + + override def main(args: Array[String]): Unit = { + /** + * Expected output is: + * + * 10 + * 82 + * 3 + * int + * double,int + * int,double + */ + + val a = new Foo + val b = new Bar + val c = new Baz + val foo_methods = a.getClass.getMethods + val bar_methods = b.getClass.getMethods + val baz_methods = c.getClass.getMethods + println(foo_methods.filter(_.toString.contains("foo")).length) + println(bar_methods.filter(_.toString.contains("bar")).length) + println(baz_methods.filter(_.toString.contains("baz")).length) + + val baz_int_param = baz_methods.filter(_.toString.contains("$mcI$sp")).head.getParameterTypes.mkString(",") + val bar_int_double_params = bar_methods.filter(s => s.toString.contains("$mcDI$sp") || s.toString.contains("$mcID$sp")) + println(baz_int_param) + println(bar_int_double_params.head.getParameterTypes.mkString(",")) + println(bar_int_double_params.tail.head.getParameterTypes.mkString(",")) + } +} \ No newline at end of file From d40d01c1b843015674b629b04ce6cf9dba71df2d Mon Sep 17 00:00:00 2001 From: AlexSikia Date: Thu, 4 Jun 2015 02:05:08 +0200 Subject: [PATCH 15/37] Handle `@specialized(AnyRef)` --- src/dotty/tools/dotc/Compiler.scala | 2 +- .../tools/dotc/config/ScalaSettings.scala | 2 +- src/dotty/tools/dotc/core/Definitions.scala | 8 +++---- .../tools/dotc/transform/PreSpecializer.scala | 20 +++++++++++----- .../dotc/transform/TypeSpecializer.scala | 3 ++- test/dotc/tests.scala | 23 +++++++++---------- 6 files changed, 33 insertions(+), 25 deletions(-) diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index 5c2ed71cd2f4..b141971497f8 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -130,4 +130,4 @@ class Compiler { reset() new Run(this)(rootContext) } -} \ No newline at end of file +} diff --git a/src/dotty/tools/dotc/config/ScalaSettings.scala b/src/dotty/tools/dotc/config/ScalaSettings.scala index 9f1f8d835d1e..9abd2b2db676 100644 --- a/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -33,7 +33,7 @@ class ScalaSettings extends Settings.SettingGroup { val usejavacp = BooleanSetting("-usejavacp", "Utilize the java.class.path in classpath resolution.") val verbose = BooleanSetting("-verbose", "Output messages about what the compiler is doing.") val version = BooleanSetting("-version", "Print product version and exit.") - val pageWidth = IntSetting("-pagewidth", "Set page width", 160) + val pageWidth = IntSetting("-pagewidth", "Set page width", 80) val jvmargs = PrefixSetting("-J", "-J", "Pass directly to the runtime system.") val defines = PrefixSetting("-Dproperty=value", "-D", "Pass -Dproperty=value directly to the runtime system.") diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index 12e0d1a4577b..1128be8d3af3 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -329,10 +329,10 @@ class Definitions { lazy val NonLocalReturnControlClass = ctx.requiredClass("scala.runtime.NonLocalReturnControl") // Annotation base classes - lazy val AnnotationClass = ctx.requiredClass("scala.annotation.Annotation") - lazy val ClassfileAnnotationClass = ctx.requiredClass("scala.annotation.ClassfileAnnotation") - lazy val StaticAnnotationClass = ctx.requiredClass("scala.annotation.StaticAnnotation") - lazy val TailrecAnnotationClass = ctx.requiredClass("scala.annotation.tailrec") + lazy val AnnotationClass = ctx.requiredClass("scala.annotation.Annotation") + lazy val ClassfileAnnotationClass = ctx.requiredClass("scala.annotation.ClassfileAnnotation") + lazy val StaticAnnotationClass = ctx.requiredClass("scala.annotation.StaticAnnotation") + lazy val TailrecAnnotationClass = ctx.requiredClass("scala.annotation.tailrec") lazy val RemoteAnnot = ctx.requiredClass("scala.remote") lazy val SerialVersionUIDAnnot = ctx.requiredClass("scala.SerialVersionUID") lazy val TransientAnnot = ctx.requiredClass("scala.transient") diff --git a/src/dotty/tools/dotc/transform/PreSpecializer.scala b/src/dotty/tools/dotc/transform/PreSpecializer.scala index 4a07a1ce5dbd..6a447a1af0af 100644 --- a/src/dotty/tools/dotc/transform/PreSpecializer.scala +++ b/src/dotty/tools/dotc/transform/PreSpecializer.scala @@ -7,7 +7,7 @@ import dotty.tools.dotc.core.Contexts.Context import dotty.tools.dotc.core.StdNames._ import dotty.tools.dotc.core.{Flags, Definitions} import dotty.tools.dotc.core.Symbols.Symbol -import dotty.tools.dotc.core.Types.Type +import dotty.tools.dotc.core.Types.{TermRef, Type} import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, MiniPhaseTransform} /** @@ -19,9 +19,14 @@ class PreSpecializer extends MiniPhaseTransform { override def phaseName: String = "prespecialize" private final def primitiveCompanionToPrimitive(companion: Type)(implicit ctx: Context) = { - val claz = companion.termSymbol.companionClass - assert(ctx.definitions.ScalaValueClasses.contains(claz)) - claz.typeRef + if (companion.asInstanceOf[TermRef].name.toString == "AnyRef") { // Handles `@specialized(AnyRef)` cases + defn.AnyRefType + } + else { + val claz = companion.termSymbol.companionClass + assert(ctx.definitions.ScalaValueClasses.contains(claz)) + claz.typeRef + } } def defn(implicit ctx: Context): Definitions = ctx.definitions @@ -55,8 +60,11 @@ class PreSpecializer extends MiniPhaseTransform { val args = annot.arguments if (args.isEmpty) primitiveTypes else args.head match { - case a@Typed(SeqLiteral(types), _) => types.map(t => primitiveCompanionToPrimitive(t.tpe)) // Matches the expected `@specialized(...)` annotations - case a@Select(Ident(_), _) => primitiveTypes // Matches `Select(Ident(Specializable), Primitives)` which is used in several instances + case a@Typed(SeqLiteral(types), _) => // Matches the expected `@specialized(...)` annotations + types.map(t => primitiveCompanionToPrimitive(t.tpe)) + + case a@Select(Ident(_), _) => primitiveTypes // Matches `Select(Ident(Specializable), Primitives)` + // which is used in several instances in the compiler case _ => ctx.error("surprising match on specialized annotation"); Nil } case nil => Nil diff --git a/src/dotty/tools/dotc/transform/TypeSpecializer.scala b/src/dotty/tools/dotc/transform/TypeSpecializer.scala index 7ee5bf91243c..634336a737b3 100644 --- a/src/dotty/tools/dotc/transform/TypeSpecializer.scala +++ b/src/dotty/tools/dotc/transform/TypeSpecializer.scala @@ -30,7 +30,8 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { defn.FloatType -> "F", defn.DoubleType -> "D", defn.CharType -> "C", - defn.UnitType -> "V") + defn.UnitType -> "V", + defn.AnyRefType -> "A") private def primitiveTypes(implicit ctx: Context) = List(defn.ByteType, diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 49f12fb046c0..8a573fa6824c 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -16,7 +16,7 @@ class tests extends CompilerTest { // "-Xprompt", // "-explaintypes", // "-Yshow-suppressed-errors", - ) + "-pagewidth", "160") val defaultOutputDir = "./out/" @@ -54,7 +54,6 @@ class tests extends CompilerTest { val dotcDir = toolsDir + "dotc/" val coreDir = dotcDir + "core/" - @Test def pickle_pickleOK = compileDir(testsDir, "pickling", testPickling) // This directory doesn't exist anymore // @Test def pickle_pickling = compileDir(coreDir, "pickling", testPickling) @@ -178,9 +177,9 @@ class tests extends CompilerTest { @Test def dotc_reporting = compileDir(dotcDir, "reporting") // twice omitted to make tests run faster @Test def dotc_typer = compileDir(dotcDir, "typer")// twice omitted to make tests run faster - // error: error while loading Checking$$anon$2$, - // class file 'target/scala-2.11/dotty_2.11-0.1-SNAPSHOT.jar(dotty/tools/dotc/typer/Checking$$anon$2.class)' - // has location not matching its contents: contains class $anon + // error: error while loading Checking$$anon$2$, + // class file 'target/scala-2.11/dotty_2.11-0.1-SNAPSHOT.jar(dotty/tools/dotc/typer/Checking$$anon$2.class)' + // has location not matching its contents: contains class $anon @Test def dotc_util = compileDir(dotcDir, "util") // twice omitted to make tests run faster @@ -191,15 +190,15 @@ class tests extends CompilerTest { //@Test def tools = compileDir(dottyDir, "tools", "-deep" :: Nil)(allowDeepSubtypes) @Test def testNonCyclic = compileList("testNonCyclic", List( - dotcDir + "CompilationUnit.scala", - coreDir + "Types.scala", - dotcDir + "ast/Trees.scala" - ), List("-Xprompt") ++ staleSymbolError ++ twice) + dotcDir + "CompilationUnit.scala", + coreDir + "Types.scala", + dotcDir + "ast/Trees.scala" + ), List("-Xprompt") ++ staleSymbolError ++ twice) @Test def testIssue_34 = compileList("testIssue_34", List( - dotcDir + "config/Properties.scala", - dotcDir + "config/PathResolver.scala" - ), List(/* "-Ylog:frontend", */ "-Xprompt") ++ staleSymbolError ++ twice) + dotcDir + "config/Properties.scala", + dotcDir + "config/PathResolver.scala" + ), List(/* "-Ylog:frontend", */ "-Xprompt") ++ staleSymbolError ++ twice) val javaDir = "./tests/pos/java-interop/" @Test def java_all = compileFiles(javaDir, twice) From 4f8b423d0e729156340c152e8c2fe5c2e70b38d4 Mon Sep 17 00:00:00 2001 From: AlexSikia Date: Thu, 4 Jun 2015 11:36:11 +0200 Subject: [PATCH 16/37] Add `out/.keep` file back --- out/.keep | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 out/.keep diff --git a/out/.keep b/out/.keep new file mode 100644 index 000000000000..e69de29bb2d1 From 8df6a1a8ef63eac36fc0172bfb8b75f57c6624ff Mon Sep 17 00:00:00 2001 From: AlexSikia Date: Thu, 4 Jun 2015 12:45:11 +0200 Subject: [PATCH 17/37] Clean up testing for Jenkins --- test/dotc/tests.scala | 4 ++-- .../{specialization.scala => simple_specialization.scala} | 2 +- .../run/{specialization.check => method-specialization.check} | 1 - .../run/{specialization.scala => method-specialization.scala} | 3 +-- 4 files changed, 4 insertions(+), 6 deletions(-) rename tests/pos/specialization/{specialization.scala => simple_specialization.scala} (90%) rename tests/run/{specialization.check => method-specialization.check} (66%) rename tests/run/{specialization.scala => method-specialization.scala} (88%) diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 8a573fa6824c..837f31351fc0 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -203,7 +203,7 @@ class tests extends CompilerTest { val javaDir = "./tests/pos/java-interop/" @Test def java_all = compileFiles(javaDir, twice) - @Test def specialization = compileFile(specialDir, "specialization", List("-Xprint:specialize")) + @Test def simple_specialization = compileFile(specialDir, "simple_specialization", List("-Xprint:specialize")) @Test def mutual_spec = compileFile(specialDir, "mutual_specialization", List("-Xprint:specialize")) @Test def return_spec = compileFile(specialDir, "return_specialization", List("-Xprint:specialize")) @Test def nothing_spec = compileFile(specialDir, "nothing_specialization", List("-Xprint:specialize")) @@ -215,7 +215,7 @@ class tests extends CompilerTest { @Test def pos_this_specialization = compileFile(specialDir, "this_specialization", List("-Xprint:specialize")) @Test def pos_spec_all = compileFiles(specialDir) - @Test def run_spec = runFile(runDir, "specialization") + @Test def run_spec = runFile(runDir, "method-specialization") //@Test def dotc_compilercommand = compileFile(dotcDir + "config/", "CompilerCommand") } diff --git a/tests/pos/specialization/specialization.scala b/tests/pos/specialization/simple_specialization.scala similarity index 90% rename from tests/pos/specialization/specialization.scala rename to tests/pos/specialization/simple_specialization.scala index 3fdc13b78c8f..e835ddee559b 100644 --- a/tests/pos/specialization/specialization.scala +++ b/tests/pos/specialization/simple_specialization.scala @@ -1,4 +1,4 @@ -trait specialization { +trait simple_specialization { def printer1[@specialized(Int, Long) T](a: T) = { println(a.toString) } diff --git a/tests/run/specialization.check b/tests/run/method-specialization.check similarity index 66% rename from tests/run/specialization.check rename to tests/run/method-specialization.check index 52f223d0a6f9..23594baa83ec 100644 --- a/tests/run/specialization.check +++ b/tests/run/method-specialization.check @@ -2,5 +2,4 @@ 82 3 int -int,double double,int \ No newline at end of file diff --git a/tests/run/specialization.scala b/tests/run/method-specialization.scala similarity index 88% rename from tests/run/specialization.scala rename to tests/run/method-specialization.scala index 760f46093964..84d6a10013b6 100644 --- a/tests/run/specialization.scala +++ b/tests/run/method-specialization.scala @@ -33,9 +33,8 @@ object Test extends dotty.runtime.LegacyApp { println(baz_methods.filter(_.toString.contains("baz")).length) val baz_int_param = baz_methods.filter(_.toString.contains("$mcI$sp")).head.getParameterTypes.mkString(",") - val bar_int_double_params = bar_methods.filter(s => s.toString.contains("$mcDI$sp") || s.toString.contains("$mcID$sp")) + val bar_int_double_params = bar_methods.filter(s => s.toString.contains("$mcDI$sp")) println(baz_int_param) println(bar_int_double_params.head.getParameterTypes.mkString(",")) - println(bar_int_double_params.tail.head.getParameterTypes.mkString(",")) } } \ No newline at end of file From aad8578ec2c88cb0f15f50f1263c581cdaa8f8d3 Mon Sep 17 00:00:00 2001 From: AlexSikia Date: Fri, 5 Jun 2015 15:15:49 +0200 Subject: [PATCH 18/37] Clean up scalastyle on several files --- src/dotty/tools/dotc/core/Definitions.scala | 2 +- src/dotty/tools/dotc/core/Symbols.scala | 2 +- .../tools/dotc/transform/PreSpecializer.scala | 9 ++++----- .../dotc/transform/TypeSpecializer.scala | 17 ++++++++-------- test/dotc/tests.scala | 20 +++++++++---------- .../bounds_specialization.scala | 8 ++++---- .../nothing_specialization.scala | 1 - 7 files changed, 28 insertions(+), 31 deletions(-) diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index 1128be8d3af3..29328a828cbd 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -338,7 +338,7 @@ class Definitions { lazy val TransientAnnot = ctx.requiredClass("scala.transient") lazy val NativeAnnot = ctx.requiredClass("scala.native") lazy val ScalaStrictFPAnnot = ctx.requiredClass("scala.annotation.strictfp") - lazy val specializedAnnot = ctx.requiredClass("scala.specialized") + lazy val SpecializedAnnot = ctx.requiredClass("scala.specialized") // Annotation classes lazy val AliasAnnot = ctx.requiredClass("dotty.annotation.internal.Alias") diff --git a/src/dotty/tools/dotc/core/Symbols.scala b/src/dotty/tools/dotc/core/Symbols.scala index 2b4f806dd3ee..2b516a3448fb 100644 --- a/src/dotty/tools/dotc/core/Symbols.scala +++ b/src/dotty/tools/dotc/core/Symbols.scala @@ -404,7 +404,7 @@ object Symbols { (if(isDefinedInCurrentRun) lastDenot else denot).isTerm final def isType(implicit ctx: Context): Boolean = - (if(isDefinedInCurrentRun) lastDenot else denot).isType + (if (isDefinedInCurrentRun) lastDenot else denot).isType final def isClass: Boolean = isInstanceOf[ClassSymbol] diff --git a/src/dotty/tools/dotc/transform/PreSpecializer.scala b/src/dotty/tools/dotc/transform/PreSpecializer.scala index 6a447a1af0af..3e9c19a634a5 100644 --- a/src/dotty/tools/dotc/transform/PreSpecializer.scala +++ b/src/dotty/tools/dotc/transform/PreSpecializer.scala @@ -24,7 +24,7 @@ class PreSpecializer extends MiniPhaseTransform { } else { val claz = companion.termSymbol.companionClass - assert(ctx.definitions.ScalaValueClasses.contains(claz)) + assert(defn.ScalaValueClasses.contains(claz)) claz.typeRef } } @@ -49,12 +49,11 @@ class PreSpecializer extends MiniPhaseTransform { sym.name != nme.asInstanceOf_ && sym.name != nme.isInstanceOf_ && !(sym is Flags.JavaDefined) && - !sym.isConstructor && - !sym.name.toString.contains("Function2") + !sym.isConstructor } if (allowedToSpecialize(sym)) { - val annotation = sym.denot.getAnnotation(ctx.definitions.specializedAnnot).getOrElse(Nil) + val annotation = sym.denot.getAnnotation(defn.specializedAnnot).getOrElse(Nil) annotation match { case annot: Annotation => val args = annot.arguments @@ -84,4 +83,4 @@ class PreSpecializer extends MiniPhaseTransform { } tree } -} \ No newline at end of file +} diff --git a/src/dotty/tools/dotc/transform/TypeSpecializer.scala b/src/dotty/tools/dotc/transform/TypeSpecializer.scala index 634336a737b3..dda0f0b5b9f9 100644 --- a/src/dotty/tools/dotc/transform/TypeSpecializer.scala +++ b/src/dotty/tools/dotc/transform/TypeSpecializer.scala @@ -47,7 +47,7 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { private def defn(implicit ctx:Context) = ctx.definitions - private var specializationRequests: mutable.HashMap[Symbols.Symbol, List[Type]] = mutable.HashMap.empty + private val specializationRequests: mutable.HashMap[Symbols.Symbol, List[Type]] = mutable.HashMap.empty /** * A map that links symbols to their specialized variants. @@ -61,15 +61,14 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { sym.name != nme.asInstanceOf_ && sym.name != nme.isInstanceOf_ && !(sym is Flags.JavaDefined) && - !sym.isConstructor && - !sym.name.toString.contains("Function2") + !sym.isConstructor } def getSpecTypes(method: Symbol, poly: PolyType)(implicit ctx: Context): List[Type] = { val requested = specializationRequests.getOrElse(method, List.empty) if (requested.nonEmpty) requested else { - if(ctx.settings.Yspecialize.value == "all") primitiveTypes.filter(tpe => poly.paramBounds.forall(_.contains(tpe))) + if (ctx.settings.Yspecialize.value == "all") primitiveTypes.filter(tpe => poly.paramBounds.forall(_.contains(tpe))) else Nil } } @@ -80,7 +79,7 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { ctx.settings.Yspecialize.value == "all" def registerSpecializationRequest(method: Symbols.Symbol)(arguments: List[Type])(implicit ctx: Context) = { - if(ctx.phaseId > this.treeTransformPhase.id) + if (ctx.phaseId > this.treeTransformPhase.id) assert(ctx.phaseId <= this.treeTransformPhase.id) val prev = specializationRequests.getOrElse(method, List.empty) specializationRequests.put(method, arguments ::: prev) @@ -126,7 +125,7 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { map.values.toList } - if((sym ne defn.ScalaPredefModule.moduleClass) && + if ((sym ne defn.ScalaPredefModule.moduleClass) && !(sym is Flags.JavaDefined) && !(sym is Flags.Scala2x) && !(sym is Flags.Package) && @@ -204,9 +203,9 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { t } else tpd.Apply(fun, newArgs) case t: ValDef => - cpy.ValDef(t)(rhs = if(t.rhs.isEmpty) EmptyTree else t.rhs.ensureConforms(t.tpt.tpe)) + cpy.ValDef(t)(rhs = if (t.rhs.isEmpty) EmptyTree else t.rhs.ensureConforms(t.tpt.tpe)) case t: DefDef => - cpy.DefDef(t)(rhs = if(t.rhs.isEmpty) EmptyTree else t.rhs.ensureConforms(t.tpt.tpe)) + cpy.DefDef(t)(rhs = if (t.rhs.isEmpty) EmptyTree else t.rhs.ensureConforms(t.tpt.tpe)) case t => t }} val expectedTypeFixed = tp.transform(typesReplaced) @@ -278,4 +277,4 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { } else tree } else tree } -} \ No newline at end of file +} diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 837f31351fc0..ba00c8ab3986 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -203,16 +203,16 @@ class tests extends CompilerTest { val javaDir = "./tests/pos/java-interop/" @Test def java_all = compileFiles(javaDir, twice) - @Test def simple_specialization = compileFile(specialDir, "simple_specialization", List("-Xprint:specialize")) - @Test def mutual_spec = compileFile(specialDir, "mutual_specialization", List("-Xprint:specialize")) - @Test def return_spec = compileFile(specialDir, "return_specialization", List("-Xprint:specialize")) - @Test def nothing_spec = compileFile(specialDir, "nothing_specialization", List("-Xprint:specialize")) - @Test def method_in_class_spec = compileFile(specialDir, "method_in_class_specialization", List("-Xprint:specialize")) - @Test def method_in_method_spec = compileFile(specialDir, "method_in_method_specialization", List("-Xprint:specialize")) - @Test def pos_type_check = compileFile(specialDir, "type_test", List("-Xprint:specialize")) - @Test def bounds_spec = compileFile(specialDir, "bounds_specialization", List("-Xprint:specialize")) - @Test def multi_spec = compileFile(specialDir, "multi_specialization", List("-Xprint:specialize")) - @Test def pos_this_specialization = compileFile(specialDir, "this_specialization", List("-Xprint:specialize")) + @Test def simple_specialization = compileFile(specialDir, "simple_specialization") + @Test def mutual_spec = compileFile(specialDir, "mutual_specialization") + @Test def return_spec = compileFile(specialDir, "return_specialization") + @Test def nothing_spec = compileFile(specialDir, "nothing_specialization") + @Test def method_in_class_spec = compileFile(specialDir, "method_in_class_specialization") + @Test def method_in_method_spec = compileFile(specialDir, "method_in_method_specialization") + @Test def pos_type_check = compileFile(specialDir, "type_test") + @Test def bounds_spec = compileFile(specialDir, "bounds_specialization") + @Test def multi_spec = compileFile(specialDir, "multi_specialization") + @Test def pos_this_specialization = compileFile(specialDir, "this_specialization") @Test def pos_spec_all = compileFiles(specialDir) @Test def run_spec = runFile(runDir, "method-specialization") diff --git a/tests/pos/specialization/bounds_specialization.scala b/tests/pos/specialization/bounds_specialization.scala index dbb4fac23df6..9cc72b7853b3 100644 --- a/tests/pos/specialization/bounds_specialization.scala +++ b/tests/pos/specialization/bounds_specialization.scala @@ -1,7 +1,7 @@ object bounds_specialization { - /*class Foo[@specialized K] { - def bar[@specialized U](u: U) { - def dough[@specialized V](v: V) { + class Foo[@specialized K] { + def bar[@specialized U](u: U) = { + def dough[@specialized V](v: V) = { println("innerMethod") } dough(1.toShort) @@ -10,7 +10,7 @@ object bounds_specialization { bar(2.toShort) bar('d') } -*/ + def kung[@specialized(Int, Double) T <: AnyRef](t: T): T = { t } diff --git a/tests/pos/specialization/nothing_specialization.scala b/tests/pos/specialization/nothing_specialization.scala index a8586a90e1e9..39fb35e273db 100644 --- a/tests/pos/specialization/nothing_specialization.scala +++ b/tests/pos/specialization/nothing_specialization.scala @@ -1,6 +1,5 @@ object nothing_specialization { def ret_nothing[@specialized(Char) T] = { - //val a: List[T] = List[Nothing]() def apply[@specialized(Char) X](xs : X*) : List[X] = List(xs:_*) def apply6[@specialized(Char) X](xs : Nothing*) : List[Nothing] = List(xs: _*) def apply2[@specialized(Long) U] = 1.asInstanceOf[U] From 15f121926fe86c97414d3a712cb10af1aa374185 Mon Sep 17 00:00:00 2001 From: AlexSikia Date: Fri, 5 Jun 2015 15:32:44 +0200 Subject: [PATCH 19/37] Check for `@specialized(AnyRef)`based on symbols instead of `Name`s --- src/dotty/tools/dotc/transform/PreSpecializer.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/transform/PreSpecializer.scala b/src/dotty/tools/dotc/transform/PreSpecializer.scala index 3e9c19a634a5..aca9ce3f63f9 100644 --- a/src/dotty/tools/dotc/transform/PreSpecializer.scala +++ b/src/dotty/tools/dotc/transform/PreSpecializer.scala @@ -9,6 +9,7 @@ import dotty.tools.dotc.core.{Flags, Definitions} import dotty.tools.dotc.core.Symbols.Symbol import dotty.tools.dotc.core.Types.{TermRef, Type} import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, MiniPhaseTransform} +import dotty.tools.dotc.core.Decorators._ /** * This phase retrieves all `@specialized` anotations before they are thrown away, @@ -19,7 +20,7 @@ class PreSpecializer extends MiniPhaseTransform { override def phaseName: String = "prespecialize" private final def primitiveCompanionToPrimitive(companion: Type)(implicit ctx: Context) = { - if (companion.asInstanceOf[TermRef].name.toString == "AnyRef") { // Handles `@specialized(AnyRef)` cases + if (companion.termSymbol eq ctx.requiredModule("scala.package").info.member("AnyRef".toTermName).symbol) { // Handles `@specialized(AnyRef)` cases defn.AnyRefType } else { @@ -53,7 +54,7 @@ class PreSpecializer extends MiniPhaseTransform { } if (allowedToSpecialize(sym)) { - val annotation = sym.denot.getAnnotation(defn.specializedAnnot).getOrElse(Nil) + val annotation = sym.denot.getAnnotation(defn.SpecializedAnnot).getOrElse(Nil) annotation match { case annot: Annotation => val args = annot.arguments From b5c5ced3593e786c15e5c40846dddeeedc7d8277 Mon Sep 17 00:00:00 2001 From: AlexSikia Date: Mon, 8 Jun 2015 23:26:59 +0200 Subject: [PATCH 20/37] Fix specialised method dispatch to recursive calls Fixes dispatching of specialised methods to recursive calls. Also takes care of mutually recursive calls. Adds a few tests for recursive specialisation, and `@specialized` annotations with parameter `AnyRef` or `Specializable.specializable_group` --- .../dotc/transform/TypeSpecializer.scala | 86 ++++++++++--------- .../anyRef_specialization.scala | 0 .../recursive_specialization.scala | 0 .../specializable_specialization.scala | 0 4 files changed, 46 insertions(+), 40 deletions(-) create mode 100644 tests/pos/specialization/anyRef_specialization.scala create mode 100644 tests/pos/specialization/recursive_specialization.scala create mode 100644 tests/pos/specialization/specializable_specialization.scala diff --git a/src/dotty/tools/dotc/transform/TypeSpecializer.scala b/src/dotty/tools/dotc/transform/TypeSpecializer.scala index dda0f0b5b9f9..edb7102b69e9 100644 --- a/src/dotty/tools/dotc/transform/TypeSpecializer.scala +++ b/src/dotty/tools/dotc/transform/TypeSpecializer.scala @@ -31,7 +31,7 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { defn.DoubleType -> "D", defn.CharType -> "C", defn.UnitType -> "V", - defn.AnyRefType -> "A") + defn.AnyRefType -> "L") private def primitiveTypes(implicit ctx: Context) = List(defn.ByteType, @@ -48,10 +48,10 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { private def defn(implicit ctx:Context) = ctx.definitions private val specializationRequests: mutable.HashMap[Symbols.Symbol, List[Type]] = mutable.HashMap.empty - + private val genericToInstantiation: mutable.HashMap[Symbols.Symbol, Type] = mutable.HashMap.empty /** * A map that links symbols to their specialized variants. - * Each symbol maps to another as map, from the list of specialization types to the specialized symbol. + * Each symbol maps to another map, from the list of specialization types to the specialized symbol. */ private val newSymbolMap: mutable.HashMap[Symbol, mutable.HashMap[List[Type], Symbols.Symbol]] = mutable.HashMap.empty @@ -90,7 +90,7 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { (instantiations: List[Type], names: List[String], poly: PolyType, decl: Symbol) (implicit ctx: Context): List[Symbol] = { if (remainingTParams.nonEmpty) { - (for (tpe <- specTypes) yield { + specTypes.map(tpe => { generateSpecializations(remainingTParams.tail, specTypes)(tpe :: instantiations, specialisedTypeToSuffix(ctx)(tpe) :: names, poly, decl) }).flatten } @@ -134,7 +134,7 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { case classInfo: ClassInfo => val newDecls = classInfo.decls .filter(_.symbol.isCompleted) // we do not want to force symbols here. - // if there's unforced symbol it means its not used in the source + // if there's unforced symbol it means its not used in the source .filterNot(_.isConstructor) .filter(requestedSpecialization) .flatMap(decl => { @@ -150,7 +150,7 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { case poly: PolyType if !newSymbolMap.contains(sym) && requestedSpecialization(sym) && allowedToSpecialize(sym, poly.paramNames.length)=> - generateSpecializations(poly.paramNames, getSpecTypes(sym, poly))(List.empty, List.empty, poly, sym) + generateSpecializations(poly.paramNames, getSpecTypes(sym, poly))(List.empty, List.empty, poly, sym) case nil => } tp @@ -173,14 +173,20 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { val newSyms = declSpecs.values.toList val instantiations = declSpecs.keys.toArray var index = -1 - println(s"specializing ${tree.symbol} for $origTParams") + ctx.debuglog(s"specializing ${tree.symbol} for $origTParams") newSyms.map { newSym => index += 1 polyDefDef(newSym.asTerm, { tparams => vparams => { val tmap: (Tree => Tree) = _ match { case Return(t, from) if from.symbol == tree.symbol => Return(t, ref(newSym)) - case t: TypeApply => transformTypeApply(t) - case t: Apply => transformApply(t) + case t: TypeApply => { + (origTParams zip instantiations(index)).foreach(x => genericToInstantiation.put(x._1, x._2)) + transformTypeApply(t) + } + case t: Apply => { + (origTParams zip instantiations(index)).foreach(x => genericToInstantiation.put(x._1, x._2)) + transformApply(t) + } case t => t } @@ -195,7 +201,7 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { val tp = new TreeMap() { // needed to workaround https://github.com/lampepfl/dotty/issues/592 - override def transform(t: Tree)(implicit ctx: Context) = super.transform(t) match { + override def transform(tree1: Tree)(implicit ctx: Context) = super.transform(tree1) match { case t @ Apply(fun, args) => assert(sameLength(args, fun.tpe.widen.firstParamTypes)) val newArgs = (args zip fun.tpe.widen.firstParamTypes).map{case(t, tpe) => t.ensureConforms(tpe)} @@ -220,29 +226,6 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { } } - override def transformTypeApply(tree: tpd.TypeApply)(implicit ctx: Context, info: TransformerInfo): Tree = { - val TypeApply(fun, _) = tree - if (fun.tpe.isParameterless) rewireTree(tree) - tree - } - - override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = { - val Apply(fun, args) = tree - fun match { - case fun: TypeApply => { - val newFun = rewireTree(fun) - if (fun ne newFun) { - val as = (args zip newFun.tpe.widen.firstParamTypes).map{ - case (arg, tpe) => - arg.ensureConforms(tpe) - } - Apply(newFun,as) - } else tree - } - case _ => tree - } - } - def rewireTree(tree: Tree)(implicit ctx: Context): Tree = { assert(tree.isInstanceOf[TypeApply]) val TypeApply(fun,args) = tree @@ -250,18 +233,18 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { val newSymInfos = newSymbolMap(fun.symbol) val betterDefs = newSymInfos.filter(x => (x._1 zip args).forall{a => val specializedType = a._1 - val argType = a._2 - argType.tpe <:< specializedType + val argType = genericToInstantiation.getOrElse(a._2.tpe.typeSymbol, a._2.tpe) + argType <:< specializedType }).toList if (betterDefs.length > 1) { - ctx.debuglog("Several specialized variants fit.") + ctx.debuglog(s"Several specialized variants fit for method ${fun.symbol.name} of ${fun.symbol.owner}. Defaulting to no specialization.") tree } else if (betterDefs.nonEmpty) { - val best = betterDefs.head - println(s"method ${fun.symbol.name} of ${fun.symbol.owner} rewired to specialized variant with type(s) : ${best._1.map{case TypeRef(_, name) => name}.mkString(", ")}") + val bestDef = betterDefs.head + ctx.debuglog(s"method ${fun.symbol.name} of ${fun.symbol.owner} rewired to specialized variant with type(s) : ${bestDef._1.map{case TypeRef(_, name) => name}.mkString(", ")}") val prefix = fun match { case Select(pre, name) => pre @@ -272,9 +255,32 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { else EmptyTree } if (prefix ne EmptyTree) - prefix.select(best._2) - else ref(best._2) + prefix.select(bestDef._2) + else ref(bestDef._2) } else tree } else tree } + + override def transformTypeApply(tree: tpd.TypeApply)(implicit ctx: Context, info: TransformerInfo): Tree = { + val TypeApply(fun, _) = tree + if (fun.tpe.isParameterless) rewireTree(tree) + tree + } + + override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = { + val Apply(fun, args) = tree + fun match { + case fun: TypeApply => + val newFun = rewireTree(fun) + if (fun ne newFun) { + val as = (args zip newFun.tpe.widen.firstParamTypes).map{ + case (arg, tpe) => arg.ensureConforms(tpe) + } + Apply(newFun,as) + } else tree + case fun : Apply => + Apply(transformApply(fun), args) + case _ => tree + } + } } diff --git a/tests/pos/specialization/anyRef_specialization.scala b/tests/pos/specialization/anyRef_specialization.scala new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/pos/specialization/recursive_specialization.scala b/tests/pos/specialization/recursive_specialization.scala new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/pos/specialization/specializable_specialization.scala b/tests/pos/specialization/specializable_specialization.scala new file mode 100644 index 000000000000..e69de29bb2d1 From 013b7c31938776376ed81f7e0c55643996fa902e Mon Sep 17 00:00:00 2001 From: AlexSikia Date: Tue, 9 Jun 2015 00:02:37 +0200 Subject: [PATCH 21/37] Fix catching of specialized annotations Detecting annotations on `Specializable` groups or `AnyRef` was flawed --- .../tools/dotc/transform/PreSpecializer.scala | 58 +++++++++++++++---- 1 file changed, 46 insertions(+), 12 deletions(-) diff --git a/src/dotty/tools/dotc/transform/PreSpecializer.scala b/src/dotty/tools/dotc/transform/PreSpecializer.scala index aca9ce3f63f9..e146abcd72d3 100644 --- a/src/dotty/tools/dotc/transform/PreSpecializer.scala +++ b/src/dotty/tools/dotc/transform/PreSpecializer.scala @@ -1,26 +1,52 @@ package dotty.tools.dotc.transform -import dotty.tools.dotc.ast.Trees.{Select, Ident, SeqLiteral, Typed} +import dotty.tools.dotc.ast.Trees.{Ident, SeqLiteral, Typed} import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.core.Annotations.Annotation import dotty.tools.dotc.core.Contexts.Context -import dotty.tools.dotc.core.StdNames._ -import dotty.tools.dotc.core.{Flags, Definitions} -import dotty.tools.dotc.core.Symbols.Symbol -import dotty.tools.dotc.core.Types.{TermRef, Type} -import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, MiniPhaseTransform} import dotty.tools.dotc.core.Decorators._ +import dotty.tools.dotc.core.Names.Name +import dotty.tools.dotc.core.StdNames._ +import dotty.tools.dotc.core.Symbols.{NoSymbol, Symbol} +import dotty.tools.dotc.core.Types.Type +import dotty.tools.dotc.core.{Definitions, Flags} +import dotty.tools.dotc.transform.TreeTransforms.{TreeTransform, MiniPhaseTransform, TransformerInfo} /** - * This phase retrieves all `@specialized` anotations before they are thrown away, + * This phase retrieves all `@specialized` anotations, * and stores them for the `TypeSpecializer` phase. */ class PreSpecializer extends MiniPhaseTransform { override def phaseName: String = "prespecialize" + private var anyRefModule: Symbol = NoSymbol + private var specializableMapping: Map[Symbol, List[Type]] = _ + private var specializableModule: Symbol = NoSymbol + + + override def prepareForUnit(tree: tpd.Tree)(implicit ctx: Context): TreeTransform = { + specializableModule = ctx.requiredModule("scala.Specializable") + anyRefModule = ctx.requiredModule("scala.package") // Using nme.PACKAGE generated errors on my machine - should be further explored + def specializableField(nm: String) = specializableModule.info.member(nm.toTermName).symbol + + specializableMapping = Map( + specializableField("Primitives") -> List(defn.IntType, defn.LongType, defn.FloatType, defn.ShortType, + defn.DoubleType, defn.BooleanType, defn.UnitType, defn.CharType, defn.ByteType), + specializableField("Everything") -> List(defn.IntType, defn.LongType, defn.FloatType, defn.ShortType, + defn.DoubleType, defn.BooleanType, defn.UnitType, defn.CharType, defn.ByteType, defn.AnyRefType), + specializableField("Bits32AndUp") -> List(defn.IntType, defn.LongType, defn.FloatType, defn.DoubleType), + specializableField("Integral") -> List(defn.ByteType, defn.ShortType, defn.IntType, defn.LongType, defn.CharType), + specializableField("AllNumeric") -> List(defn.ByteType, defn.ShortType, defn.IntType, defn.LongType, + defn.CharType, defn.FloatType, defn.DoubleType), + specializableField("BestOfBreed") -> List(defn.IntType, defn.DoubleType, defn.BooleanType, defn.UnitType, + defn.AnyRefType) + ) + this + } + private final def primitiveCompanionToPrimitive(companion: Type)(implicit ctx: Context) = { - if (companion.termSymbol eq ctx.requiredModule("scala.package").info.member("AnyRef".toTermName).symbol) { // Handles `@specialized(AnyRef)` cases + if (companion.termSymbol eq anyRefModule.info.member(nme.AnyRef.toTermName).symbol) { defn.AnyRefType } else { @@ -30,6 +56,13 @@ class PreSpecializer extends MiniPhaseTransform { } } + private def specializableToPrimitive(specializable: Type, name: Name)(implicit ctx: Context): List[Type] = { + if (specializable.termSymbol eq specializableModule.info.member(name).symbol) { + specializableMapping(specializable.termSymbol) + } + else Nil + } + def defn(implicit ctx: Context): Definitions = ctx.definitions private def primitiveTypes(implicit ctx: Context) = @@ -60,12 +93,13 @@ class PreSpecializer extends MiniPhaseTransform { val args = annot.arguments if (args.isEmpty) primitiveTypes else args.head match { - case a@Typed(SeqLiteral(types), _) => // Matches the expected `@specialized(...)` annotations + case a @ Typed(SeqLiteral(types), _) => types.map(t => primitiveCompanionToPrimitive(t.tpe)) - case a@Select(Ident(_), _) => primitiveTypes // Matches `Select(Ident(Specializable), Primitives)` - // which is used in several instances in the compiler - case _ => ctx.error("surprising match on specialized annotation"); Nil + case a @ Ident(groupName) if a.tpe.isInstanceOf[Type] => // Matches `@specialized` annotations on Specializable Groups + specializableToPrimitive(a.tpe.asInstanceOf[Type], groupName) + + case _ => ctx.error("unexpected match on specialized annotation"); Nil } case nil => Nil } From 03c0c3c98397df1cc00bfd842ffe47b6000b3512 Mon Sep 17 00:00:00 2001 From: AlexSikia Date: Tue, 9 Jun 2015 01:11:33 +0200 Subject: [PATCH 22/37] Adapt specialization tests and clean up --- src/dotty/tools/dotc/transform/TypeSpecializer.scala | 12 +++++------- test/dotc/tests.scala | 6 ++++-- tests/pos/specialization/anyRef_specialization.scala | 3 +++ tests/pos/specialization/mutual_specialization.scala | 8 ++------ .../specialization/recursive_specialization.scala | 7 +++++++ .../specializable_specialization.scala | 10 ++++++++++ 6 files changed, 31 insertions(+), 15 deletions(-) diff --git a/src/dotty/tools/dotc/transform/TypeSpecializer.scala b/src/dotty/tools/dotc/transform/TypeSpecializer.scala index edb7102b69e9..d39a31e6faa3 100644 --- a/src/dotty/tools/dotc/transform/TypeSpecializer.scala +++ b/src/dotty/tools/dotc/transform/TypeSpecializer.scala @@ -19,7 +19,7 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { import tpd._ override def phaseName = "specialize" - final val maxTparamsToSpecialize = 2 + final var maxTparamsToSpecialize = 0 private final def specialisedTypeToSuffix(implicit ctx: Context) = Map(defn.ByteType -> "B", @@ -56,7 +56,7 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { private val newSymbolMap: mutable.HashMap[Symbol, mutable.HashMap[List[Type], Symbols.Symbol]] = mutable.HashMap.empty def allowedToSpecialize(sym: Symbol, numOfTypes: Int)(implicit ctx: Context): Boolean = { - numOfTypes <= maxTparamsToSpecialize && + (maxTparamsToSpecialize == 0 || numOfTypes <= maxTparamsToSpecialize) && numOfTypes > 0 && sym.name != nme.asInstanceOf_ && sym.name != nme.isInstanceOf_ && @@ -179,14 +179,12 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { polyDefDef(newSym.asTerm, { tparams => vparams => { val tmap: (Tree => Tree) = _ match { case Return(t, from) if from.symbol == tree.symbol => Return(t, ref(newSym)) - case t: TypeApply => { + case t: TypeApply => (origTParams zip instantiations(index)).foreach(x => genericToInstantiation.put(x._1, x._2)) transformTypeApply(t) - } - case t: Apply => { + case t: Apply => (origTParams zip instantiations(index)).foreach(x => genericToInstantiation.put(x._1, x._2)) transformApply(t) - } case t => t } @@ -204,7 +202,7 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { override def transform(tree1: Tree)(implicit ctx: Context) = super.transform(tree1) match { case t @ Apply(fun, args) => assert(sameLength(args, fun.tpe.widen.firstParamTypes)) - val newArgs = (args zip fun.tpe.widen.firstParamTypes).map{case(t, tpe) => t.ensureConforms(tpe)} + val newArgs = (args zip fun.tpe.widen.firstParamTypes).map{case(tr, tpe) => tr.ensureConforms(tpe)} if (sameTypes(args, newArgs)) { t } else tpd.Apply(fun, newArgs) diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index ba00c8ab3986..891719fa15f1 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -213,9 +213,11 @@ class tests extends CompilerTest { @Test def bounds_spec = compileFile(specialDir, "bounds_specialization") @Test def multi_spec = compileFile(specialDir, "multi_specialization") @Test def pos_this_specialization = compileFile(specialDir, "this_specialization") - @Test def pos_spec_all = compileFiles(specialDir) + @Test def specializable_spec = runFile(specialDir, "specializable_specialization") + @Test def anyRef_spec = runFile(specialDir, "anyRef_specialization") + @Test def recursive_spec = runFile(specialDir, "recursive_specialization") @Test def run_spec = runFile(runDir, "method-specialization") - + //@Test def dotc_compilercommand = compileFile(dotcDir + "config/", "CompilerCommand") } diff --git a/tests/pos/specialization/anyRef_specialization.scala b/tests/pos/specialization/anyRef_specialization.scala index e69de29bb2d1..e20a3d6f6270 100644 --- a/tests/pos/specialization/anyRef_specialization.scala +++ b/tests/pos/specialization/anyRef_specialization.scala @@ -0,0 +1,3 @@ +object Test { + def foo[@specialized(AnyRef) T](t: T): T = t +} diff --git a/tests/pos/specialization/mutual_specialization.scala b/tests/pos/specialization/mutual_specialization.scala index cc778e8ccf09..9d7394fbd5ee 100644 --- a/tests/pos/specialization/mutual_specialization.scala +++ b/tests/pos/specialization/mutual_specialization.scala @@ -1,10 +1,6 @@ object mutual_specialization { class A[T] { - def foo[T](b: B[T], n: Int): Unit = if (n > 0) b.bar(this, n-1) + def foo[@specialized U](b: U, n: Int): Unit = if (n > 0) bar(b, n-1) + def bar[@specialized V](a: V, n: Int): Unit = if (n > 0) foo(a, n-1) } - class B[T] { - def bar[T](a: A[T], n: Int): Unit = if (n > 0) a.foo(this, n-1) - } - def foobar[@specialized(Int, Float, Double) T](n: T): Unit = new A[T].foo(new B[T], 5) - foobar(5) } diff --git a/tests/pos/specialization/recursive_specialization.scala b/tests/pos/specialization/recursive_specialization.scala index e69de29bb2d1..757b206b0f1e 100644 --- a/tests/pos/specialization/recursive_specialization.scala +++ b/tests/pos/specialization/recursive_specialization.scala @@ -0,0 +1,7 @@ +class Spec { + def plus[@specialized T](a: T, b:T)(ev: Numeric[T]): T = plus(b, a)(ev) +} + +class IntSpec extends Spec { + val res = plus(1,2)(Numeric.IntIsIntegral) +} diff --git a/tests/pos/specialization/specializable_specialization.scala b/tests/pos/specialization/specializable_specialization.scala index e69de29bb2d1..9f0736b7b970 100644 --- a/tests/pos/specialization/specializable_specialization.scala +++ b/tests/pos/specialization/specializable_specialization.scala @@ -0,0 +1,10 @@ +import Specializable._ + +object specializable { + def foo[@specialized(Primitives) T](t: T): T = t + def foo2[@specialized(Everything) T](t: T): T = t + def foo3[@specialized(Bits32AndUp) T](t: T): T = t + def foo4[@specialized(Integral) T](t: T): T = t + def foo5[@specialized(AllNumeric) T](t: T): T = t + def foo6[@specialized(BestOfBreed) T](t: T): T = t +} From ab3d717b32f71c51127f6d7a44b9da5a656c3681 Mon Sep 17 00:00:00 2001 From: AlexSikia Date: Mon, 22 Jun 2015 14:44:16 +0200 Subject: [PATCH 23/37] Correct name mangling Name mangling was incorrect previously. Now uses the `specializedFor` method in `NameOps`. --- src/dotty/tools/dotc/core/NameOps.scala | 5 +- .../tools/dotc/transform/PreSpecializer.scala | 2 +- .../dotc/transform/TypeSpecializer.scala | 64 ++++++++----------- test/dotc/tests.scala | 28 ++++---- .../anyRef_specialization.scala | 3 + .../genericClass_specialization.scala | 6 ++ .../recursive_specialization.scala | 15 +++-- .../specializable_specialization.scala | 7 +- tests/run/method-specialization.scala | 4 +- 9 files changed, 71 insertions(+), 63 deletions(-) create mode 100644 tests/pos/specialization/genericClass_specialization.scala diff --git a/src/dotty/tools/dotc/core/NameOps.scala b/src/dotty/tools/dotc/core/NameOps.scala index 593d5f036197..f86274a3828b 100644 --- a/src/dotty/tools/dotc/core/NameOps.scala +++ b/src/dotty/tools/dotc/core/NameOps.scala @@ -4,7 +4,7 @@ package core import java.security.MessageDigest import scala.annotation.switch import scala.io.Codec -import Names._, StdNames._, Contexts._, Symbols._, Flags._ +import Names._, dotty.tools.dotc.core.StdNames._, Contexts._, Symbols._, Flags._ import Decorators.StringDecorator import util.{Chars, NameTransformer} import Chars.isOperatorPart @@ -244,7 +244,8 @@ object NameOps { def specializedFor(classTargs: List[Types.Type], classTargsNames: List[Name], methodTargs: List[Types.Type], methodTarsNames: List[Name])(implicit ctx: Context): name.ThisName = { def typeToTag(tp: Types.Type): Name = { - tp.classSymbol match { + if (tp eq null) "".toTermName + else 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 diff --git a/src/dotty/tools/dotc/transform/PreSpecializer.scala b/src/dotty/tools/dotc/transform/PreSpecializer.scala index e146abcd72d3..fda2e5aa0f00 100644 --- a/src/dotty/tools/dotc/transform/PreSpecializer.scala +++ b/src/dotty/tools/dotc/transform/PreSpecializer.scala @@ -27,7 +27,7 @@ class PreSpecializer extends MiniPhaseTransform { override def prepareForUnit(tree: tpd.Tree)(implicit ctx: Context): TreeTransform = { specializableModule = ctx.requiredModule("scala.Specializable") - anyRefModule = ctx.requiredModule("scala.package") // Using nme.PACKAGE generated errors on my machine - should be further explored + anyRefModule = ctx.requiredModule("scala.package") def specializableField(nm: String) = specializableModule.info.member(nm.toTermName).symbol specializableMapping = Map( diff --git a/src/dotty/tools/dotc/transform/TypeSpecializer.scala b/src/dotty/tools/dotc/transform/TypeSpecializer.scala index d39a31e6faa3..e889c94e64ee 100644 --- a/src/dotty/tools/dotc/transform/TypeSpecializer.scala +++ b/src/dotty/tools/dotc/transform/TypeSpecializer.scala @@ -3,11 +3,10 @@ package dotty.tools.dotc.transform import dotty.tools.dotc.ast.{tpd, TreeTypeMap} import dotty.tools.dotc.ast.Trees._ import dotty.tools.dotc.core.Contexts.Context -import dotty.tools.dotc.core.Decorators.StringDecorator import dotty.tools.dotc.core.DenotTransformers.InfoTransformer import dotty.tools.dotc.core.Names.Name import dotty.tools.dotc.core.Symbols.Symbol -import dotty.tools.dotc.core.{Symbols, Flags} +import dotty.tools.dotc.core.{NameOps, Symbols, Flags} import dotty.tools.dotc.core.Types._ import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, MiniPhaseTransform} import scala.collection.mutable @@ -21,18 +20,6 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { final var maxTparamsToSpecialize = 0 - private final def specialisedTypeToSuffix(implicit ctx: Context) = - Map(defn.ByteType -> "B", - defn.BooleanType -> "Z", - defn.ShortType -> "S", - defn.IntType -> "I", - defn.LongType -> "J", - defn.FloatType -> "F", - defn.DoubleType -> "D", - defn.CharType -> "C", - defn.UnitType -> "V", - defn.AnyRefType -> "L") - private def primitiveTypes(implicit ctx: Context) = List(defn.ByteType, defn.BooleanType, @@ -42,8 +29,7 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { defn.FloatType, defn.DoubleType, defn.CharType, - defn.UnitType - ) + defn.UnitType) private def defn(implicit ctx:Context) = ctx.definitions @@ -87,22 +73,22 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { override def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = { def generateSpecializations(remainingTParams: List[Name], specTypes: List[Type]) - (instantiations: List[Type], names: List[String], poly: PolyType, decl: Symbol) + (instantiations: List[Type], poly: PolyType, decl: Symbol) (implicit ctx: Context): List[Symbol] = { if (remainingTParams.nonEmpty) { specTypes.map(tpe => { - generateSpecializations(remainingTParams.tail, specTypes)(tpe :: instantiations, specialisedTypeToSuffix(ctx)(tpe) :: names, poly, decl) + generateSpecializations(remainingTParams.tail, specTypes)(tpe :: instantiations, poly, decl) }).flatten } else { - generateSpecializedSymbols(instantiations.reverse, names.reverse, poly, decl) + generateSpecializedSymbols(instantiations.reverse, poly, decl) :: Nil } } - def generateSpecializedSymbols(instantiations: List[Type], names: List[String], poly: PolyType, decl: Symbol) - (implicit ctx: Context): List[Symbol] = { + def generateSpecializedSymbols(instantiations: List[Type], poly: PolyType, decl: Symbol) + (implicit ctx: Context): Symbol = { val newSym = - ctx.newSymbol(decl.owner, (decl.name + "$mc" + names.mkString + "$sp").toTermName, - decl.flags | Flags.Synthetic, poly.instantiate(instantiations.toList)) + ctx.newSymbol(decl.owner, NameOps.NameDecorator(decl.name).specializedFor(null, Nil, instantiations), + decl.flags | Flags.Synthetic, poly.instantiate(instantiations.map(_.widen).toList)) /* The following generated symbols which kept type bounds. It served, as illustrated by the `this_specialization` * test, as a way of keeping type bounds when instantiating a `this` referring to a generic class. However, @@ -122,7 +108,7 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { val map = newSymbolMap.getOrElse(decl, mutable.HashMap.empty) map.put(instantiations, newSym) newSymbolMap.put(decl, map) - map.values.toList + newSym } if ((sym ne defn.ScalaPredefModule.moduleClass) && @@ -140,20 +126,24 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { .flatMap(decl => { decl.info.widen match { case poly: PolyType if allowedToSpecialize(decl.symbol, poly.paramNames.length) => - generateSpecializations(poly.paramNames, getSpecTypes(decl, poly))(List.empty, List.empty, poly, decl) - case nil => Nil + generateSpecializations(poly.paramNames, getSpecTypes(decl, poly))(List.empty, poly, decl) + case _ => Nil } }) - val decls = classInfo.decls.cloneScope - newDecls.foreach(decls.enter) - classInfo.derivedClassInfo(decls = decls) - case poly: PolyType if !newSymbolMap.contains(sym) && + + if (newDecls.nonEmpty) { + val decls = classInfo.decls.cloneScope + newDecls.foreach(decls.enter) + classInfo.derivedClassInfo(decls = decls) + } + else tp + case poly: PolyType if !newSymbolMap.contains(sym)&& requestedSpecialization(sym) && - allowedToSpecialize(sym, poly.paramNames.length)=> - generateSpecializations(poly.paramNames, getSpecTypes(sym, poly))(List.empty, List.empty, poly, sym) - case nil => + allowedToSpecialize(sym, poly.paramNames.length) => + generateSpecializations(poly.paramNames, getSpecTypes(sym, poly))(List.empty, poly, sym) + tp + case _ => tp } - tp } else tp } @@ -235,6 +225,7 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { argType <:< specializedType }).toList + if (betterDefs.length > 1) { ctx.debuglog(s"Several specialized variants fit for method ${fun.symbol.name} of ${fun.symbol.owner}. Defaulting to no specialization.") tree @@ -252,8 +243,7 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { ref(tp.prefix.termSymbol) else EmptyTree } - if (prefix ne EmptyTree) - prefix.select(bestDef._2) + if (prefix ne EmptyTree) prefix.select(bestDef._2) else ref(bestDef._2) } else tree } else tree @@ -262,7 +252,7 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { override def transformTypeApply(tree: tpd.TypeApply)(implicit ctx: Context, info: TransformerInfo): Tree = { val TypeApply(fun, _) = tree if (fun.tpe.isParameterless) rewireTree(tree) - tree + else tree } override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = { diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 891719fa15f1..ad2c01c4da4d 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -203,21 +203,19 @@ class tests extends CompilerTest { val javaDir = "./tests/pos/java-interop/" @Test def java_all = compileFiles(javaDir, twice) - @Test def simple_specialization = compileFile(specialDir, "simple_specialization") - @Test def mutual_spec = compileFile(specialDir, "mutual_specialization") - @Test def return_spec = compileFile(specialDir, "return_specialization") - @Test def nothing_spec = compileFile(specialDir, "nothing_specialization") - @Test def method_in_class_spec = compileFile(specialDir, "method_in_class_specialization") - @Test def method_in_method_spec = compileFile(specialDir, "method_in_method_specialization") - @Test def pos_type_check = compileFile(specialDir, "type_test") - @Test def bounds_spec = compileFile(specialDir, "bounds_specialization") - @Test def multi_spec = compileFile(specialDir, "multi_specialization") - @Test def pos_this_specialization = compileFile(specialDir, "this_specialization") - @Test def specializable_spec = runFile(specialDir, "specializable_specialization") - @Test def anyRef_spec = runFile(specialDir, "anyRef_specialization") - @Test def recursive_spec = runFile(specialDir, "recursive_specialization") - - @Test def run_spec = runFile(runDir, "method-specialization") + @Test def specialization = compileDir(posDir, "specialization", twice) + @Test def simple_specialization = compileFile(specialDir, "simple_specialization", twice) + @Test def mutual_spec = compileFile(specialDir, "mutual_specialization", twice) + @Test def return_spec = compileFile(specialDir, "return_specialization", twice) + @Test def nothing_spec = compileFile(specialDir, "nothing_specialization", twice) + @Test def method_in_class_spec = compileFile(specialDir, "method_in_class_specialization", twice) + @Test def method_in_method_spec = compileFile(specialDir, "method_in_method_specialization", twice) + @Test def pos_type_check = compileFile(specialDir, "type_test", twice) + @Test def bounds_spec = compileFile(specialDir, "bounds_specialization", twice) + @Test def multi_spec = compileFile(specialDir, "multi_specialization", twice) + @Test def pos_this_specialization = compileFile(specialDir, "this_specialization", twice) + @Test def anyRef_spec = runFile(specialDir, "anyRef_specialization", twice) + @Test def genClass_spec = compileFile(specialDir, "genericClass_specialization", twice) //@Test def dotc_compilercommand = compileFile(dotcDir + "config/", "CompilerCommand") } diff --git a/tests/pos/specialization/anyRef_specialization.scala b/tests/pos/specialization/anyRef_specialization.scala index e20a3d6f6270..c41d0ba484b8 100644 --- a/tests/pos/specialization/anyRef_specialization.scala +++ b/tests/pos/specialization/anyRef_specialization.scala @@ -1,3 +1,6 @@ object Test { def foo[@specialized(AnyRef) T](t: T): T = t + def main (args: Array[String]) = { + foo(5) + } } diff --git a/tests/pos/specialization/genericClass_specialization.scala b/tests/pos/specialization/genericClass_specialization.scala new file mode 100644 index 000000000000..5c8de766f48c --- /dev/null +++ b/tests/pos/specialization/genericClass_specialization.scala @@ -0,0 +1,6 @@ +object genericClass_specialization { + class A[T] { + def foo[@specialized(Int, Char, Double) U](b: U) = b + } + def foobar[@specialized(Char) X] = new A[X].foo(2) +} diff --git a/tests/pos/specialization/recursive_specialization.scala b/tests/pos/specialization/recursive_specialization.scala index 757b206b0f1e..a48e8a358292 100644 --- a/tests/pos/specialization/recursive_specialization.scala +++ b/tests/pos/specialization/recursive_specialization.scala @@ -1,7 +1,12 @@ -class Spec { - def plus[@specialized T](a: T, b:T)(ev: Numeric[T]): T = plus(b, a)(ev) -} +object recursive_specialization { + class Spec { + def plus[@specialized T](a: T, b:T)(ev: Numeric[T]): T = plus(b, a)(ev) + } + + class IntSpec extends Spec { + lazy val res = plus(1,2)(Numeric.IntIsIntegral) + } -class IntSpec extends Spec { - val res = plus(1,2)(Numeric.IntIsIntegral) + def main(args: Array[String]) = { + } } diff --git a/tests/pos/specialization/specializable_specialization.scala b/tests/pos/specialization/specializable_specialization.scala index 9f0736b7b970..c13b51fc6b0d 100644 --- a/tests/pos/specialization/specializable_specialization.scala +++ b/tests/pos/specialization/specializable_specialization.scala @@ -1,10 +1,15 @@ import Specializable._ -object specializable { +object specializable_specialization { def foo[@specialized(Primitives) T](t: T): T = t def foo2[@specialized(Everything) T](t: T): T = t def foo3[@specialized(Bits32AndUp) T](t: T): T = t def foo4[@specialized(Integral) T](t: T): T = t def foo5[@specialized(AllNumeric) T](t: T): T = t def foo6[@specialized(BestOfBreed) T](t: T): T = t + + def main(args: Array[String]) = { + foo('c') + foo5('c') + } } diff --git a/tests/run/method-specialization.scala b/tests/run/method-specialization.scala index 84d6a10013b6..a1ab0271958d 100644 --- a/tests/run/method-specialization.scala +++ b/tests/run/method-specialization.scala @@ -32,8 +32,8 @@ object Test extends dotty.runtime.LegacyApp { println(bar_methods.filter(_.toString.contains("bar")).length) println(baz_methods.filter(_.toString.contains("baz")).length) - val baz_int_param = baz_methods.filter(_.toString.contains("$mcI$sp")).head.getParameterTypes.mkString(",") - val bar_int_double_params = bar_methods.filter(s => s.toString.contains("$mcDI$sp")) + val baz_int_param = baz_methods.filter(_.toString.contains("$mIc$sp")).head.getParameterTypes.mkString(",") + val bar_int_double_params = bar_methods.filter(s => s.toString.contains("$mDIc$sp")) println(baz_int_param) println(bar_int_double_params.head.getParameterTypes.mkString(",")) } From 07e9ae12d574fa0bbb07012cd78611973e2afeb7 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Thu, 25 Jun 2015 09:39:15 +0200 Subject: [PATCH 24/37] SpecializeNames: Duplicate scalac behaviour, sort tparams --- src/dotty/tools/dotc/core/Names.scala | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/dotty/tools/dotc/core/Names.scala b/src/dotty/tools/dotc/core/Names.scala index 12def107626f..93a080e77e94 100644 --- a/src/dotty/tools/dotc/core/Names.scala +++ b/src/dotty/tools/dotc/core/Names.scala @@ -353,9 +353,9 @@ object Names { def compare(x: Name, y: Name): Int = { if (x.isTermName && y.isTypeName) 1 else if (x.isTypeName && y.isTermName) -1 - else if (x eq y) 0 + else if (x.start == y.start && x.length == y.length) 0 else { - val until = x.length min y.length + val until = Math.min(x.length, y.length) var i = 0 while (i < until && x(i) == y(i)) i = i + 1 @@ -364,7 +364,9 @@ object Names { if (x(i) < y(i)) -1 else /*(x(i) > y(i))*/ 1 } else { - x.length - y.length + if (x.length < y.length) 1 + else if (x.length > y.length) -1 + else 0 // shouldn't happen, but still } } } From 68e4f6e7af3a5f2570362a8c5579865633ed005c Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Mon, 9 Mar 2015 16:14:05 +0100 Subject: [PATCH 25/37] Allow to instantiate only some type params of a PolyType --- src/dotty/tools/dotc/core/Types.scala | 49 +++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 0e9f5d9b217a..3163dadac3b6 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -29,10 +29,12 @@ import Uniques._ import collection.{mutable, Seq, breakOut} import config.Config import config.Printers._ +import dotty.tools.sameLength import annotation.tailrec import Flags.FlagSet import typer.Mode import language.implicitConversions +import scala.collection.mutable.ListBuffer object Types { @@ -2220,9 +2222,10 @@ object Types { protected def computeSignature(implicit ctx: Context) = resultSignature - def instantiate(argTypes: List[Type])(implicit ctx: Context): Type = + def instantiate(argTypes: List[Type])(implicit ctx: Context): Type = { + assert(sameLength(argTypes, paramNames)) resultType.substParams(this, argTypes) - + } def instantiateBounds(argTypes: List[Type])(implicit ctx: Context): List[TypeBounds] = paramBounds.mapConserve(_.substParams(this, argTypes).bounds) @@ -2235,6 +2238,48 @@ object Types { x => paramBounds mapConserve (_.subst(this, x).bounds), x => resType.subst(this, x)) + /** Instantiate only some type parameters. + * @param argNum which parameters should be instantiated + * @param argTypes which types should be used for Instatiation + * @return a PolyType with (this.paramNames - argNum.size) type parameters left abstract + */ + def instantiate(argNum: List[Int], argTypes: List[Type])(implicit ctx: Context) = { + // merge original args list with supplied one + def mergeArgs(pp: PolyType, nxt: Int, id: Int, until: Int, argT: List[Type], argN: List[Int], res: ListBuffer[Type]): List[Type] = + if (id < until && argT.nonEmpty) { + if (argN.head == id) // we replace this poly param by supplied one + mergeArgs(pp, nxt, id + 1, until, argT.tail, argN.tail, res += argT.head) + else { // we create a PolyParam that is still not instantiated + val nw = PolyParam(pp, nxt) + res += nw + mergeArgs(pp, nxt + 1, id + 1, until, argT, argN, res) + } + } else { + res ++= nxt.until(nxt + until - id).map(PolyParam(pp, _)) + res.toList + } + def args(pp: PolyType) = mergeArgs(pp, 0, 0, argTypes.length, argTypes, argNum, ListBuffer.empty) + + def pnames(origPnames: List[TypeName] = paramNames, argN: List[Int] = argNum, id: Int = 0, tmp: ListBuffer[TypeName] = ListBuffer.empty): List[TypeName] = { + if (argN.isEmpty) { + tmp ++= origPnames + tmp.toList + } + else if (id == argN.head) { + pnames(origPnames.tail, argN.tail, id + 1, tmp) + } else { + pnames(origPnames.tail, argN, id + 1, tmp += origPnames.head) + } + } + + PolyType(pnames())( + x => { + val a = args(x) + paramBounds mapConserve (_.substParams(this, a).bounds) + }, + x => resType.substParams(this, args(x))) + } + // need to override hashCode and equals to be object identity // because paramNames by itself is not discriminatory enough override def equals(other: Any) = this eq other.asInstanceOf[AnyRef] From ff9c58384d930ef6b324ccfb0f4f1ac61d415ad2 Mon Sep 17 00:00:00 2001 From: AlexSikia Date: Mon, 3 Aug 2015 11:44:14 +0200 Subject: [PATCH 26/37] Correct typos Conflicts: src/dotty/tools/dotc/core/NameOps.scala Conflicts: src/dotty/tools/dotc/core/NameOps.scala --- src/dotty/tools/dotc/core/NameOps.scala | 4 ++-- src/dotty/tools/dotc/core/Types.scala | 4 ++-- src/dotty/tools/dotc/typer/Applications.scala | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/dotty/tools/dotc/core/NameOps.scala b/src/dotty/tools/dotc/core/NameOps.scala index f86274a3828b..025b47a25818 100644 --- a/src/dotty/tools/dotc/core/NameOps.scala +++ b/src/dotty/tools/dotc/core/NameOps.scala @@ -241,7 +241,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], methodTargsNames: List[Name])(implicit ctx: Context): name.ThisName = { def typeToTag(tp: Types.Type): Name = { if (tp eq null) "".toTermName @@ -259,7 +259,7 @@ object NameOps { } } - val methodTags: Seq[Name] = (methodTargs zip methodTarsNames).sortBy(_._2).map(x => typeToTag(x._1)) + val methodTags: Seq[Name] = (methodTargs zip methodTargsNames).sortBy(_._2).map(x => typeToTag(x._1)) val classTags: Seq[Name] = (classTargs zip classTargsNames).sortBy(_._2).map(x => typeToTag(x._1)) name.fromName(name ++ nme.specializedTypeNames.prefix ++ diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 3163dadac3b6..e6cd3aac8ff0 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -2423,9 +2423,9 @@ object Types { * * @param origin The parameter that's tracked by the type variable. * @param creatorState The typer state in which the variable was created. - * @param owningTree The function part of the TypeApply tree tree that introduces + * @param owningTree The function part of the TypeApply tree that introduces * the type variable. - * @paran owner The current owner if the context where the variable was created. + * @param owner The current owner if the context where the variable was created. * * `owningTree` and `owner` are used to determine whether a type-variable can be instantiated * at some given point. See `Inferencing#interpolateUndetVars`. diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index c45db4ccc827..3a4374843d11 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -206,7 +206,7 @@ trait Applications extends Compatibility { self: Typer => /** @param pnames The list of parameter names that are missing arguments * @param args The list of arguments that are not yet passed, or that are waiting to be dropped * @param nameToArg A map from as yet unseen names to named arguments - * @param toDrop A set of names that have already be passed as named arguments + * @param toDrop A set of names that have already been passed as named arguments * * For a well-typed application we have the invariants * From 266194f79b0cbb9ae608ce1399f1ec0ef43da00e Mon Sep 17 00:00:00 2001 From: AlexSikia Date: Thu, 23 Jul 2015 14:23:29 +0200 Subject: [PATCH 27/37] Fix partial instantiation of PolyTypes `until` argument of `mergeArgs(...)` was passed the value of `argTypes.length` (only types which are used for instantiation) by `args(...)`, and therefore did not account for remaining generics. --- src/dotty/tools/dotc/core/Types.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index e6cd3aac8ff0..279c240b03c7 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -2258,7 +2258,7 @@ object Types { res ++= nxt.until(nxt + until - id).map(PolyParam(pp, _)) res.toList } - def args(pp: PolyType) = mergeArgs(pp, 0, 0, argTypes.length, argTypes, argNum, ListBuffer.empty) + def args(pp: PolyType) = mergeArgs(pp, 0, 0, argTypes.length + pp.paramNames.length, argTypes, argNum, ListBuffer.empty) def pnames(origPnames: List[TypeName] = paramNames, argN: List[Int] = argNum, id: Int = 0, tmp: ListBuffer[TypeName] = ListBuffer.empty): List[TypeName] = { if (argN.isEmpty) { From 6599f59ab9487238eba1390827b432d6cdc96002 Mon Sep 17 00:00:00 2001 From: AlexSikia Date: Sun, 26 Jul 2015 12:49:20 +0200 Subject: [PATCH 28/37] Implement Partial Specialisation Only type params directly preceded by an `@specialized` annotation will be specialised. --- .../tools/dotc/transform/PreSpecializer.scala | 15 +- .../dotc/transform/TypeSpecializer.scala | 216 ++++++++++++------ 2 files changed, 154 insertions(+), 77 deletions(-) diff --git a/src/dotty/tools/dotc/transform/PreSpecializer.scala b/src/dotty/tools/dotc/transform/PreSpecializer.scala index fda2e5aa0f00..6381158275b9 100644 --- a/src/dotty/tools/dotc/transform/PreSpecializer.scala +++ b/src/dotty/tools/dotc/transform/PreSpecializer.scala @@ -5,10 +5,11 @@ import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.core.Annotations.Annotation import dotty.tools.dotc.core.Contexts.Context import dotty.tools.dotc.core.Decorators._ +import dotty.tools.dotc.core.DenotTransformers.InfoTransformer import dotty.tools.dotc.core.Names.Name import dotty.tools.dotc.core.StdNames._ -import dotty.tools.dotc.core.Symbols.{NoSymbol, Symbol} -import dotty.tools.dotc.core.Types.Type +import dotty.tools.dotc.core.Symbols.{ClassSymbol, NoSymbol, Symbol} +import dotty.tools.dotc.core.Types.{ClassInfo, Type} import dotty.tools.dotc.core.{Definitions, Flags} import dotty.tools.dotc.transform.TreeTransforms.{TreeTransform, MiniPhaseTransform, TransformerInfo} @@ -95,10 +96,8 @@ class PreSpecializer extends MiniPhaseTransform { else args.head match { case a @ Typed(SeqLiteral(types), _) => types.map(t => primitiveCompanionToPrimitive(t.tpe)) - - case a @ Ident(groupName) if a.tpe.isInstanceOf[Type] => // Matches `@specialized` annotations on Specializable Groups + case a @ Ident(groupName) => // Matches `@specialized` annotations on Specializable Groups specializableToPrimitive(a.tpe.asInstanceOf[Type], groupName) - case _ => ctx.error("unexpected match on specialized annotation"); Nil } case nil => Nil @@ -108,11 +107,11 @@ class PreSpecializer extends MiniPhaseTransform { override def transformDefDef(tree: tpd.DefDef)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { val tparams = tree.tparams.map(_.symbol) - val st = tparams.map(getSpec) + val st = tparams.zipWithIndex.map{case(sym, i) => (i, getSpec(sym))} if (st.nonEmpty) { st.map{ - case (types: List[Type]) if types.nonEmpty => - ctx.specializePhase.asInstanceOf[TypeSpecializer].registerSpecializationRequest(tree.symbol)(types) + case (index: Int, types: List[Type]) if types.nonEmpty => + ctx.specializePhase.asInstanceOf[TypeSpecializer].registerSpecializationRequest(tree.symbol)(index, types) case _ => } } diff --git a/src/dotty/tools/dotc/transform/TypeSpecializer.scala b/src/dotty/tools/dotc/transform/TypeSpecializer.scala index e889c94e64ee..a00e7f984998 100644 --- a/src/dotty/tools/dotc/transform/TypeSpecializer.scala +++ b/src/dotty/tools/dotc/transform/TypeSpecializer.scala @@ -4,7 +4,6 @@ import dotty.tools.dotc.ast.{tpd, TreeTypeMap} import dotty.tools.dotc.ast.Trees._ import dotty.tools.dotc.core.Contexts.Context import dotty.tools.dotc.core.DenotTransformers.InfoTransformer -import dotty.tools.dotc.core.Names.Name import dotty.tools.dotc.core.Symbols.Symbol import dotty.tools.dotc.core.{NameOps, Symbols, Flags} import dotty.tools.dotc.core.Types._ @@ -13,13 +12,13 @@ import scala.collection.mutable import dotty.tools.dotc.core.StdNames.nme import dotty.tools._ +import scala.collection.mutable.ListBuffer + class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { import tpd._ override def phaseName = "specialize" - final var maxTparamsToSpecialize = 0 - private def primitiveTypes(implicit ctx: Context) = List(defn.ByteType, defn.BooleanType, @@ -33,28 +32,55 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { private def defn(implicit ctx:Context) = ctx.definitions - private val specializationRequests: mutable.HashMap[Symbols.Symbol, List[Type]] = mutable.HashMap.empty + /** + * Methods requested for specialization + * Generic Symbol => List[ (position in type args list, specialized type requested) ] + */ + private val specializationRequests: mutable.HashMap[Symbols.Symbol, List[(Int, List[Type])]] = mutable.HashMap.empty + + /** + * A list of instantiation values of generics (helps with recursive polymorphic methods) + */ private val genericToInstantiation: mutable.HashMap[Symbols.Symbol, Type] = mutable.HashMap.empty + /** * A map that links symbols to their specialized variants. * Each symbol maps to another map, from the list of specialization types to the specialized symbol. + * Generic symbol => Map[ Tuple(position in type args list, specialized Type) => Specialized Symbol ] */ - private val newSymbolMap: mutable.HashMap[Symbol, mutable.HashMap[List[Type], Symbols.Symbol]] = mutable.HashMap.empty + private val newSymbolMap: mutable.HashMap[Symbol, mutable.HashMap[List[(Int, Type)], Symbols.Symbol]] = mutable.HashMap.empty - def allowedToSpecialize(sym: Symbol, numOfTypes: Int)(implicit ctx: Context): Boolean = { - (maxTparamsToSpecialize == 0 || numOfTypes <= maxTparamsToSpecialize) && - numOfTypes > 0 && + /** + * A map from specialised symbols to the indices of their remaining generic types + */ + private val newSymbolsGenericIndices: mutable.HashMap[Symbol, List[Int]] = mutable.HashMap.empty + + /** + * A list of symbols gone through specialisation + * Is used to make calls to transformInfo idempotent + */ + private val specialized: ListBuffer[Symbol] = ListBuffer.empty + + def allowedToSpecialize(sym: Symbol, numOfTypes: Int)(implicit ctx: Context) = + numOfTypes > 0 && sym.name != nme.asInstanceOf_ && sym.name != nme.isInstanceOf_ && + !newSymbolMap.contains(sym) && !(sym is Flags.JavaDefined) && !sym.isConstructor - } - def getSpecTypes(method: Symbol, poly: PolyType)(implicit ctx: Context): List[Type] = { - val requested = specializationRequests.getOrElse(method, List.empty) - if (requested.nonEmpty) requested + + def getSpecTypes(method: Symbol, poly: PolyType)(implicit ctx: Context): List[(Int, List[Type])] = { + + val requested = specializationRequests.getOrElse(method, List.empty).toMap + if (requested.nonEmpty) { + poly.paramNames.zipWithIndex.map{case(name, i) => (i, requested.getOrElse(i, Nil))} + } else { - if (ctx.settings.Yspecialize.value == "all") primitiveTypes.filter(tpe => poly.paramBounds.forall(_.contains(tpe))) + if (ctx.settings.Yspecialize.value == "all") { + val filteredPrims = primitiveTypes.filter(tpe => poly.paramBounds.forall(_.contains(tpe))) + List.range(0, poly.paramNames.length).map(i => (i, filteredPrims)) + } else Nil } } @@ -64,69 +90,71 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { (ctx.settings.Yspecialize.value != "" && decl.name.contains(ctx.settings.Yspecialize.value)) || ctx.settings.Yspecialize.value == "all" - def registerSpecializationRequest(method: Symbols.Symbol)(arguments: List[Type])(implicit ctx: Context) = { + def registerSpecializationRequest(method: Symbols.Symbol)(index: Int, arguments: List[Type])(implicit ctx: Context) = { if (ctx.phaseId > this.treeTransformPhase.id) assert(ctx.phaseId <= this.treeTransformPhase.id) val prev = specializationRequests.getOrElse(method, List.empty) - specializationRequests.put(method, arguments ::: prev) + specializationRequests.put(method, (index, arguments) :: prev) } override def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = { - def generateSpecializations(remainingTParams: List[Name], specTypes: List[Type]) - (instantiations: List[Type], poly: PolyType, decl: Symbol) - (implicit ctx: Context): List[Symbol] = { - if (remainingTParams.nonEmpty) { - specTypes.map(tpe => { - generateSpecializations(remainingTParams.tail, specTypes)(tpe :: instantiations, poly, decl) - }).flatten + + def generateMethodSpecializations(specTypes: List[(Int, List[Type])]) + (instantiations: List[(Int, Type)], poly: PolyType, decl: Symbol) + (implicit ctx: Context): List[Symbol] = { + if (specTypes.nonEmpty) { + specTypes.head match{ + case (i, tpes) if tpes.nonEmpty => + tpes.flatMap(tpe => + generateMethodSpecializations(specTypes.tail)((i, tpe) :: instantiations, poly, decl) + ) + case (i, nil) => + generateMethodSpecializations(specTypes.tail)(instantiations, poly, decl) + } } else { - generateSpecializedSymbols(instantiations.reverse, poly, decl) :: Nil + if (instantiations.isEmpty) Nil + else generateSpecializedSymbol(instantiations.reverse, poly, decl) :: Nil } } - def generateSpecializedSymbols(instantiations: List[Type], poly: PolyType, decl: Symbol) - (implicit ctx: Context): Symbol = { - val newSym = - ctx.newSymbol(decl.owner, NameOps.NameDecorator(decl.name).specializedFor(null, Nil, instantiations), - decl.flags | Flags.Synthetic, poly.instantiate(instantiations.map(_.widen).toList)) - - /* The following generated symbols which kept type bounds. It served, as illustrated by the `this_specialization` - * test, as a way of keeping type bounds when instantiating a `this` referring to a generic class. However, - * because type bounds are not transitive, this did not work out and we introduced casts instead. - * - * ctx.newSymbol(decl.owner, (decl.name + names.mkString).toTermName, - * decl.flags | Flags.Synthetic, - * poly.derivedPolyType(poly.paramNames, - * (poly.paramBounds zip instantiations).map - * {case (bounds, instantiation) => - * TypeBounds(bounds.lo, AndType(bounds.hi, instantiation))}, - * poly.instantiate(indices, instantiations) - * ) - * ) - */ + def generateSpecializedSymbol(instantiations: List[(Int, Type)], poly: PolyType, decl: Symbol) + (implicit ctx: Context): Symbol = { + val indices = instantiations.map(_._1) + val instanceTypes = instantiations.map(_._2) + val newSym = ctx.newSymbol(decl.owner, NameOps.NameDecorator(decl.name).specializedFor(Nil, Nil, instanceTypes, instanceTypes.map(_.asInstanceOf[NamedType].name)), + decl.flags | Flags.Synthetic, { + if (indices.length != poly.paramNames.length) // Partial Specialisation case + poly.instantiate(indices, instanceTypes) // Returns a PolyType with uninstantiated types kept generic + else + poly.instantiate(instanceTypes) // Returns a MethodType, as no polymorphic types remains + }) val map = newSymbolMap.getOrElse(decl, mutable.HashMap.empty) map.put(instantiations, newSym) newSymbolMap.put(decl, map) + + newSymbolsGenericIndices.put(newSym, indices) + newSym } - if ((sym ne defn.ScalaPredefModule.moduleClass) && + if (!specialized.contains(sym) && + (sym ne defn.ScalaPredefModule.moduleClass) && !(sym is Flags.JavaDefined) && !(sym is Flags.Scala2x) && !(sym is Flags.Package) && !sym.isAnonymousClass) { + specialized += sym sym.info match { case classInfo: ClassInfo => val newDecls = classInfo.decls - .filter(_.symbol.isCompleted) // we do not want to force symbols here. - // if there's unforced symbol it means its not used in the source + .filter(_.symbol.isCompleted) // We do not want to force symbols. Unforced symbol are not used in the source .filterNot(_.isConstructor) .filter(requestedSpecialization) .flatMap(decl => { decl.info.widen match { case poly: PolyType if allowedToSpecialize(decl.symbol, poly.paramNames.length) => - generateSpecializations(poly.paramNames, getSpecTypes(decl, poly))(List.empty, poly, decl) + generateMethodSpecializations(getSpecTypes(decl, poly))(List.empty, poly, decl) case _ => Nil } }) @@ -137,11 +165,17 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { classInfo.derivedClassInfo(decls = decls) } else tp - case poly: PolyType if !newSymbolMap.contains(sym)&& - requestedSpecialization(sym) && - allowedToSpecialize(sym, poly.paramNames.length) => - generateSpecializations(poly.paramNames, getSpecTypes(sym, poly))(List.empty, poly, sym) + case poly: PolyType if allowedToSpecialize(sym, poly.paramNames.length) => + if (sym.owner.info.isInstanceOf[ClassInfo]) { + transformInfo(sym.owner.info, sym.owner) + tp + } + else if (requestedSpecialization(sym) && + allowedToSpecialize(sym, poly.paramNames.length)) { + generateMethodSpecializations(getSpecTypes(sym, poly))(List.empty, poly, sym) tp + } + else tp case _ => tp } } else tp @@ -158,6 +192,18 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { val origVParams = tree.vparamss.flatten.map(_.symbol) def specialize(decl : Symbol): List[Tree] = { + + def makeTypesList(origTSyms: List[Symbol], instantiation: Map[Int, Type], pt: PolyType): List[Type] = { + var holePos = -1 + origTSyms.zipWithIndex.map { + case (_, i) => instantiation.getOrElse(i, { + holePos += 1 + PolyParam(pt, holePos) + } + ).widen + } + } + if (newSymbolMap.contains(decl)) { val declSpecs = newSymbolMap(decl) val newSyms = declSpecs.values.toList @@ -166,14 +212,20 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { ctx.debuglog(s"specializing ${tree.symbol} for $origTParams") newSyms.map { newSym => index += 1 + val newSymType = newSym.info.widenDealias polyDefDef(newSym.asTerm, { tparams => vparams => { + val instTypes = newSymType match { + case pt: PolyType => makeTypesList(origTParams, instantiations(index).toMap, pt) + case _ => instantiations(index).map(_._2) + } + val tmap: (Tree => Tree) = _ match { case Return(t, from) if from.symbol == tree.symbol => Return(t, ref(newSym)) case t: TypeApply => - (origTParams zip instantiations(index)).foreach(x => genericToInstantiation.put(x._1, x._2)) + (origTParams zip instTypes).foreach(x => genericToInstantiation.put(x._1, x._2)) transformTypeApply(t) case t: Apply => - (origTParams zip instantiations(index)).foreach(x => genericToInstantiation.put(x._1, x._2)) + (origTParams zip instTypes).foreach(x => genericToInstantiation.put(x._1, x._2)) transformApply(t) case t => t } @@ -181,7 +233,7 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { val typesReplaced = new TreeTypeMap( treeMap = tmap, typeMap = _ - .substDealias(origTParams, instantiations(index)) + .substDealias(origTParams, instTypes) .subst(origVParams, vparams.flatten.map(_.tpe)), oldOwners = tree.symbol :: Nil, newOwners = newSym :: Nil @@ -191,19 +243,36 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { // needed to workaround https://github.com/lampepfl/dotty/issues/592 override def transform(tree1: Tree)(implicit ctx: Context) = super.transform(tree1) match { case t @ Apply(fun, args) => - assert(sameLength(args, fun.tpe.widen.firstParamTypes)) - val newArgs = (args zip fun.tpe.widen.firstParamTypes).map{case(tr, tpe) => tr.ensureConforms(tpe)} + assert(sameLength(args, fun.tpe.widen.firstParamTypes), + s"Wrong number of parameters. Expected: ${fun.tpe.widen.firstParamTypes.length}. Found: ${args.length}") + val newArgs = (args zip fun.tpe.widen.firstParamTypes).map{ + case(tr, tpe) => + assert(tpe.widen ne NoType, "Bad cast when specializing") + tr.ensureConforms(tpe.widen) + } if (sameTypes(args, newArgs)) { t - } else tpd.Apply(fun, newArgs) + } + else tpd.Apply(fun, newArgs) case t: ValDef => - cpy.ValDef(t)(rhs = if (t.rhs.isEmpty) EmptyTree else t.rhs.ensureConforms(t.tpt.tpe)) + cpy.ValDef(t)(rhs = if (t.rhs.isEmpty) EmptyTree else + t.rhs.ensureConforms(t.tpt.tpe)) case t: DefDef => - cpy.DefDef(t)(rhs = if (t.rhs.isEmpty) EmptyTree else t.rhs.ensureConforms(t.tpt.tpe)) + cpy.DefDef(t)(rhs = if (t.rhs.isEmpty) EmptyTree else + t.rhs.ensureConforms(t.tpt.tpe)) + case t: TypeTree => + t.tpe match { + case pp: PolyParam => + TypeTree(tparams(pp.paramNum)) + case _ => t + } case t => t }} val expectedTypeFixed = tp.transform(typesReplaced) - expectedTypeFixed.ensureConforms(newSym.info.widen.finalResultType) + if (expectedTypeFixed ne EmptyTree) { + expectedTypeFixed.ensureConforms(newSym.info.widen.finalResultType.widenDealias) + } + else expectedTypeFixed }}) } } else Nil @@ -219,11 +288,14 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { val TypeApply(fun,args) = tree if (newSymbolMap.contains(fun.symbol)){ val newSymInfos = newSymbolMap(fun.symbol) - val betterDefs = newSymInfos.filter(x => (x._1 zip args).forall{a => - val specializedType = a._1 - val argType = genericToInstantiation.getOrElse(a._2.tpe.typeSymbol, a._2.tpe) - argType <:< specializedType - }).toList + val betterDefs = newSymInfos.filter( + x => { + val instantiation = x._1 + instantiation.forall { x => + val ord = x._1 + val tp = x._2 + args(ord).tpe <:< tp + }}).toList if (betterDefs.length > 1) { @@ -233,7 +305,7 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { else if (betterDefs.nonEmpty) { val bestDef = betterDefs.head - ctx.debuglog(s"method ${fun.symbol.name} of ${fun.symbol.owner} rewired to specialized variant with type(s) : ${bestDef._1.map{case TypeRef(_, name) => name}.mkString(", ")}") + ctx.debuglog(s"method ${fun.symbol.name} of ${fun.symbol.owner} rewired to specialized variant") val prefix = fun match { case Select(pre, name) => pre @@ -242,6 +314,7 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { if (tp.prefix ne NoPrefix) ref(tp.prefix.termSymbol) else EmptyTree + case _ => EmptyTree } if (prefix ne EmptyTree) prefix.select(bestDef._2) else ref(bestDef._2) @@ -259,12 +332,17 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { val Apply(fun, args) = tree fun match { case fun: TypeApply => + val TypeApply(_, typeArgs) = fun val newFun = rewireTree(fun) if (fun ne newFun) { - val as = (args zip newFun.tpe.widen.firstParamTypes).map{ - case (arg, tpe) => arg.ensureConforms(tpe) + newFun.symbol.info.widenDealias match { + case pt: PolyType => // Need to apply types to the remaining generics first + val tpeOfRemainingGenerics = typeArgs.zipWithIndex.filterNot(x => newSymbolsGenericIndices(newFun.symbol).contains(x._2)).map(_._1) + assert(tpeOfRemainingGenerics.nonEmpty, s"Remaining generics on ${newFun.symbol.name} not properly instantiated: missing types") + Apply(TypeApply(newFun, tpeOfRemainingGenerics), args) + case _ => + Apply(newFun, args) } - Apply(newFun,as) } else tree case fun : Apply => Apply(transformApply(fun), args) From ce2b5612a866f59b2581bccba0aab3b2ef603a83 Mon Sep 17 00:00:00 2001 From: AlexSikia Date: Sun, 26 Jul 2015 13:02:18 +0200 Subject: [PATCH 29/37] Simplify code and clean up --- src/dotty/tools/dotc/core/NameOps.scala | 2 +- .../tools/dotc/transform/PreSpecializer.scala | 12 ++- .../dotc/transform/TypeSpecializer.scala | 81 ++++++++++--------- 3 files changed, 52 insertions(+), 43 deletions(-) diff --git a/src/dotty/tools/dotc/core/NameOps.scala b/src/dotty/tools/dotc/core/NameOps.scala index 025b47a25818..d3a35e7337e6 100644 --- a/src/dotty/tools/dotc/core/NameOps.scala +++ b/src/dotty/tools/dotc/core/NameOps.scala @@ -244,7 +244,7 @@ object NameOps { def specializedFor(classTargs: List[Types.Type], classTargsNames: List[Name], methodTargs: List[Types.Type], methodTargsNames: List[Name])(implicit ctx: Context): name.ThisName = { def typeToTag(tp: Types.Type): Name = { - if (tp eq null) "".toTermName + if (tp eq null) nme.EMPTY else tp.classSymbol match { case t if t eq defn.IntClass => nme.specializedTypeNames.Int case t if t eq defn.BooleanClass => nme.specializedTypeNames.Boolean diff --git a/src/dotty/tools/dotc/transform/PreSpecializer.scala b/src/dotty/tools/dotc/transform/PreSpecializer.scala index 6381158275b9..4d7980031a7a 100644 --- a/src/dotty/tools/dotc/transform/PreSpecializer.scala +++ b/src/dotty/tools/dotc/transform/PreSpecializer.scala @@ -108,13 +108,17 @@ class PreSpecializer extends MiniPhaseTransform { override def transformDefDef(tree: tpd.DefDef)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { val tparams = tree.tparams.map(_.symbol) val st = tparams.zipWithIndex.map{case(sym, i) => (i, getSpec(sym))} - if (st.nonEmpty) { - st.map{ - case (index: Int, types: List[Type]) if types.nonEmpty => + sendRequests(st, tree) + tree + } + + def sendRequests(requests: List[(Int, List[Type])], tree: tpd.Tree)(implicit ctx: Context): Unit = { + if (requests.nonEmpty) { + requests.map{ + case (index, types) if types.nonEmpty => ctx.specializePhase.asInstanceOf[TypeSpecializer].registerSpecializationRequest(tree.symbol)(index, types) case _ => } } - tree } } diff --git a/src/dotty/tools/dotc/transform/TypeSpecializer.scala b/src/dotty/tools/dotc/transform/TypeSpecializer.scala index a00e7f984998..c309830c92c9 100644 --- a/src/dotty/tools/dotc/transform/TypeSpecializer.scala +++ b/src/dotty/tools/dotc/transform/TypeSpecializer.scala @@ -99,6 +99,45 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { override def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = { + def enterNewSyms(newDecls: List[Symbol], classInfo: ClassInfo) = { + val decls = classInfo.decls.cloneScope + newDecls.foreach(decls.enter) + classInfo.derivedClassInfo(decls = decls) + } + + def specializeMethods(sym: Symbol) = { + specialized += sym + sym.info match { + case classInfo: ClassInfo => + val newDecls = classInfo.decls + .filter(_.symbol.isCompleted) // We do not want to force symbols. Unforced symbol are not used in the source + .filterNot(_.isConstructor) + .filter(requestedSpecialization) + .flatMap(decl => { + decl.info.widen match { + case poly: PolyType if allowedToSpecialize(decl.symbol, poly.paramNames.length) => + generateMethodSpecializations(getSpecTypes(decl, poly))(List.empty, poly, decl) + case _ => Nil + } + }) + + if (newDecls.nonEmpty) enterNewSyms(newDecls.toList, classInfo) + else tp + case poly: PolyType if allowedToSpecialize(sym, poly.paramNames.length) => + if (sym.owner.info.isInstanceOf[ClassInfo]) { + transformInfo(sym.owner.info, sym.owner) + tp + } + else if (requestedSpecialization(sym) && + allowedToSpecialize(sym, poly.paramNames.length)) { + generateMethodSpecializations(getSpecTypes(sym, poly))(List.empty, poly, sym) + tp + } + else tp + case _ => tp + } + } + def generateMethodSpecializations(specTypes: List[(Int, List[Type])]) (instantiations: List[(Int, Type)], poly: PolyType, decl: Symbol) (implicit ctx: Context): List[Symbol] = { @@ -144,40 +183,7 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { !(sym is Flags.Scala2x) && !(sym is Flags.Package) && !sym.isAnonymousClass) { - specialized += sym - sym.info match { - case classInfo: ClassInfo => - val newDecls = classInfo.decls - .filter(_.symbol.isCompleted) // We do not want to force symbols. Unforced symbol are not used in the source - .filterNot(_.isConstructor) - .filter(requestedSpecialization) - .flatMap(decl => { - decl.info.widen match { - case poly: PolyType if allowedToSpecialize(decl.symbol, poly.paramNames.length) => - generateMethodSpecializations(getSpecTypes(decl, poly))(List.empty, poly, decl) - case _ => Nil - } - }) - - if (newDecls.nonEmpty) { - val decls = classInfo.decls.cloneScope - newDecls.foreach(decls.enter) - classInfo.derivedClassInfo(decls = decls) - } - else tp - case poly: PolyType if allowedToSpecialize(sym, poly.paramNames.length) => - if (sym.owner.info.isInstanceOf[ClassInfo]) { - transformInfo(sym.owner.info, sym.owner) - tp - } - else if (requestedSpecialization(sym) && - allowedToSpecialize(sym, poly.paramNames.length)) { - generateMethodSpecializations(getSpecTypes(sym, poly))(List.empty, poly, sym) - tp - } - else tp - case _ => tp - } + specializeMethods(sym) } else tp } @@ -277,8 +283,8 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { } } else Nil } - val specialized_trees = specialize(tree.symbol) - Thicket(tree :: specialized_trees) + val specializedTrees = specialize(tree.symbol) + Thicket(tree :: specializedTrees) case _ => tree } } @@ -297,9 +303,8 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { args(ord).tpe <:< tp }}).toList - if (betterDefs.length > 1) { - ctx.debuglog(s"Several specialized variants fit for method ${fun.symbol.name} of ${fun.symbol.owner}. Defaulting to no specialization.") + ctx.debuglog(s"Several specialized variants fit for ${fun.symbol.name} of ${fun.symbol.owner}. Defaulting to no specialization.") tree } From 4f7f3a785a53e159a7aeafcfe43a059764114bb2 Mon Sep 17 00:00:00 2001 From: AlexSikia Date: Sun, 26 Jul 2015 13:05:35 +0200 Subject: [PATCH 30/37] Change Yspecialize behaviour Changes type from `StringSetting` to `IntSetting`. Value x passed to the setting is the maximum number of parameters to specialize. Any method with type parameters will see the x first (or all if there are not enough) of them specialized. Conflicts: src/dotty/tools/dotc/config/ScalaSettings.scala --- src/dotty/tools/dotc/config/ScalaSettings.scala | 2 +- src/dotty/tools/dotc/transform/TypeSpecializer.scala | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/dotty/tools/dotc/config/ScalaSettings.scala b/src/dotty/tools/dotc/config/ScalaSettings.scala index 9abd2b2db676..fa32a138f5cc 100644 --- a/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -152,7 +152,7 @@ class ScalaSettings extends Settings.SettingGroup { val YprintSyms = BooleanSetting("-Yprint-syms", "when printing trees print info in symbols instead of corresponding info in trees.") val YtestPickler = BooleanSetting("-Ytest-pickler", "self-test for pickling functionality; should be used with -Ystop-after:pickler") val YcheckReentrant = BooleanSetting("-Ycheck-reentrant", "check that compiled program does not contain vars that can be accessed from a global root.") - val Yspecialize = StringSetting("-Yspecialize","","Specialize all methods.", "") + val Yspecialize = IntSetting("-Yspecialize","Specialize methods with maximum this amount of polymorphic types.", 0, 0 to 10) def stop = YstopAfter diff --git a/src/dotty/tools/dotc/transform/TypeSpecializer.scala b/src/dotty/tools/dotc/transform/TypeSpecializer.scala index c309830c92c9..f2d0be05bf51 100644 --- a/src/dotty/tools/dotc/transform/TypeSpecializer.scala +++ b/src/dotty/tools/dotc/transform/TypeSpecializer.scala @@ -77,18 +77,16 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { poly.paramNames.zipWithIndex.map{case(name, i) => (i, requested.getOrElse(i, Nil))} } else { - if (ctx.settings.Yspecialize.value == "all") { + if (ctx.settings.Yspecialize.value > 0) { val filteredPrims = primitiveTypes.filter(tpe => poly.paramBounds.forall(_.contains(tpe))) - List.range(0, poly.paramNames.length).map(i => (i, filteredPrims)) + List.range(0, Math.min(poly.paramNames.length, ctx.settings.Yspecialize.value)).map(i => (i, filteredPrims)) } else Nil } } def requestedSpecialization(decl: Symbol)(implicit ctx: Context): Boolean = - specializationRequests.contains(decl) || - (ctx.settings.Yspecialize.value != "" && decl.name.contains(ctx.settings.Yspecialize.value)) || - ctx.settings.Yspecialize.value == "all" + ctx.settings.Yspecialize.value != 0 || specializationRequests.contains(decl) def registerSpecializationRequest(method: Symbols.Symbol)(index: Int, arguments: List[Type])(implicit ctx: Context) = { if (ctx.phaseId > this.treeTransformPhase.id) From 87d59fa4ab4113c5cac8fe25c8fb22c36afa48a0 Mon Sep 17 00:00:00 2001 From: AlexSikia Date: Sun, 26 Jul 2015 13:19:42 +0200 Subject: [PATCH 31/37] Reduce restrictions on methods allowed for specialization --- src/dotty/tools/dotc/transform/PreSpecializer.scala | 3 +-- src/dotty/tools/dotc/transform/TypeSpecializer.scala | 11 ++++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/dotty/tools/dotc/transform/PreSpecializer.scala b/src/dotty/tools/dotc/transform/PreSpecializer.scala index 4d7980031a7a..6212d71c417e 100644 --- a/src/dotty/tools/dotc/transform/PreSpecializer.scala +++ b/src/dotty/tools/dotc/transform/PreSpecializer.scala @@ -82,9 +82,8 @@ class PreSpecializer extends MiniPhaseTransform { def allowedToSpecialize(sym: Symbol): Boolean = { sym.name != nme.asInstanceOf_ && - sym.name != nme.isInstanceOf_ && !(sym is Flags.JavaDefined) && - !sym.isConstructor + !sym.isPrimaryConstructor } if (allowedToSpecialize(sym)) { diff --git a/src/dotty/tools/dotc/transform/TypeSpecializer.scala b/src/dotty/tools/dotc/transform/TypeSpecializer.scala index f2d0be05bf51..51d599961cd5 100644 --- a/src/dotty/tools/dotc/transform/TypeSpecializer.scala +++ b/src/dotty/tools/dotc/transform/TypeSpecializer.scala @@ -64,10 +64,10 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { def allowedToSpecialize(sym: Symbol, numOfTypes: Int)(implicit ctx: Context) = numOfTypes > 0 && sym.name != nme.asInstanceOf_ && - sym.name != nme.isInstanceOf_ && !newSymbolMap.contains(sym) && + !sym.name.toString.contains("$sp") && !(sym is Flags.JavaDefined) && - !sym.isConstructor + !sym.isPrimaryConstructor def getSpecTypes(method: Symbol, poly: PolyType)(implicit ctx: Context): List[(Int, List[Type])] = { @@ -189,9 +189,10 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { tree.tpe.widen match { - case poly: PolyType if !(tree.symbol.isConstructor - || (tree.symbol is Flags.Label)) - || (tree.symbol.name == nme.asInstanceOf_) => + case poly: PolyType + if !(tree.symbol.isPrimaryConstructor + || (tree.symbol is Flags.Label) + ) => val origTParams = tree.tparams.map(_.symbol) val origVParams = tree.vparamss.flatten.map(_.symbol) From cc653e42084a6043271f425eea0122e790e169a8 Mon Sep 17 00:00:00 2001 From: AlexSikia Date: Sun, 26 Jul 2015 13:23:41 +0200 Subject: [PATCH 32/37] Use a typemap when transforming DefDef's Makes for a more complete handling of different elements --- .../dotc/transform/TypeSpecializer.scala | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/src/dotty/tools/dotc/transform/TypeSpecializer.scala b/src/dotty/tools/dotc/transform/TypeSpecializer.scala index 51d599961cd5..21bd17c5669c 100644 --- a/src/dotty/tools/dotc/transform/TypeSpecializer.scala +++ b/src/dotty/tools/dotc/transform/TypeSpecializer.scala @@ -56,7 +56,7 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { private val newSymbolsGenericIndices: mutable.HashMap[Symbol, List[Int]] = mutable.HashMap.empty /** - * A list of symbols gone through specialisation + * A list of symbols gone through the specialisation pipeline * Is used to make calls to transformInfo idempotent */ private val specialized: ListBuffer[Symbol] = ListBuffer.empty @@ -224,7 +224,7 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { case _ => instantiations(index).map(_._2) } - val tmap: (Tree => Tree) = _ match { + val treemap: (Tree => Tree) = _ match { case Return(t, from) if from.symbol == tree.symbol => Return(t, ref(newSym)) case t: TypeApply => (origTParams zip instTypes).foreach(x => genericToInstantiation.put(x._1, x._2)) @@ -235,11 +235,27 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { case t => t } + val abstractPolyType = tree.symbol.info.widenDealias.asInstanceOf[PolyType] + val typemap = new TypeMap { + override def apply(tp: Type): Type = { + val t = mapOver(tp) + .substDealias(origTParams, instTypes) + .substParams(abstractPolyType, instTypes) + .subst(origVParams, vparams.flatten.map(_.tpe)) + newSymType match { + case mt: MethodType if tparams.isEmpty => + t.substParams(newSymType.asInstanceOf[MethodType], vparams.flatten.map(_.tpe)) + case pt: PolyType => + t.substParams(newSymType.asInstanceOf[PolyType], tparams) + .substParams(newSymType.resultType.asInstanceOf[MethodType], vparams.flatten.map(_.tpe)) + case _ => t + } + } + } + val typesReplaced = new TreeTypeMap( - treeMap = tmap, - typeMap = _ - .substDealias(origTParams, instTypes) - .subst(origVParams, vparams.flatten.map(_.tpe)), + treeMap = treemap, + typeMap = typemap, oldOwners = tree.symbol :: Nil, newOwners = newSym :: Nil ).transform(tree.rhs) @@ -253,7 +269,7 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { val newArgs = (args zip fun.tpe.widen.firstParamTypes).map{ case(tr, tpe) => assert(tpe.widen ne NoType, "Bad cast when specializing") - tr.ensureConforms(tpe.widen) + tr.ensureConforms(typemap(tpe.widen)) } if (sameTypes(args, newArgs)) { t @@ -275,7 +291,7 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { }} val expectedTypeFixed = tp.transform(typesReplaced) if (expectedTypeFixed ne EmptyTree) { - expectedTypeFixed.ensureConforms(newSym.info.widen.finalResultType.widenDealias) + expectedTypeFixed.ensureConforms(typemap(newSym.info.widen.finalResultType.widenDealias)) } else expectedTypeFixed }}) From 096466386dae58faf61b959f44b635bb731dd285 Mon Sep 17 00:00:00 2001 From: AlexSikia Date: Tue, 28 Jul 2015 15:45:54 +0200 Subject: [PATCH 33/37] Add test cases for specialisation --- .../specialization/multi_specialization.scala | 4 ++-- .../mutual_specialization.scala | 4 ++-- .../partial_specialization.scala | 16 ++++++++++++++++ .../subtype_specialization.scala | 13 +++++++++++++ tests/run/method-specialization.check | 4 +++- tests/run/method-specialization.scala | 19 +++++++++++++++++-- 6 files changed, 53 insertions(+), 7 deletions(-) create mode 100644 tests/pos/specialization/partial_specialization.scala create mode 100644 tests/pos/specialization/subtype_specialization.scala diff --git a/tests/pos/specialization/multi_specialization.scala b/tests/pos/specialization/multi_specialization.scala index ad430050d0bf..7ad357253850 100644 --- a/tests/pos/specialization/multi_specialization.scala +++ b/tests/pos/specialization/multi_specialization.scala @@ -1,7 +1,7 @@ object multi_specialization { def one[@specialized T](n: T): T = n - def two[@specialized T, U](n: T, m: U): (T,U) = (n,m) - def three[@specialized T, U, V](n: T, m: U, o: V): (T,U,V) = (n,m,o) + def two[@specialized(Int, Double) T,@specialized(Double, Int) U](n: T, m: U): (T,U) = (n,m) + def three[@specialized(Int, Double) T,@specialized(Double, Int) U, V](n: T, m: U, o: V): (T,U,V) = (n,m,o) one(1) two(1,2) diff --git a/tests/pos/specialization/mutual_specialization.scala b/tests/pos/specialization/mutual_specialization.scala index 9d7394fbd5ee..849aaba2b459 100644 --- a/tests/pos/specialization/mutual_specialization.scala +++ b/tests/pos/specialization/mutual_specialization.scala @@ -1,6 +1,6 @@ object mutual_specialization { class A[T] { - def foo[@specialized U](b: U, n: Int): Unit = if (n > 0) bar(b, n-1) - def bar[@specialized V](a: V, n: Int): Unit = if (n > 0) foo(a, n-1) + def foo[@specialized(Double) U](b: U, n: Int): Unit = if (n > 0) bar(b, n-1) + def bar[@specialized(Double) V](a: V, n: Int): Unit = if (n > 0) foo(a, n-1) } } diff --git a/tests/pos/specialization/partial_specialization.scala b/tests/pos/specialization/partial_specialization.scala new file mode 100644 index 000000000000..989db1f9df01 --- /dev/null +++ b/tests/pos/specialization/partial_specialization.scala @@ -0,0 +1,16 @@ +trait partial_specialization { + def foo1stOutOf1[@specialized(Int, Char) T](t: T) = ??? + def foo1stOutOf2[@specialized(Int, Char) T, U](t: T, u: U): T = t + def foo2ndOutOf2[T, @specialized(Int, Char) U](t: T, u: U) = ??? + def fooAllOutOf2[@specialized(Int, Char) T, @specialized(Int, Char) U](t: T, u: U) = ??? + def foo1st3rdOutOf3[@specialized(Int, Char) T, U, @specialized(Int, Char) V](t: T, u: U, v: V) = ??? + + def main(args: Array[String]) = { + foo1stOutOf2(1, 2.0) + foo1stOutOf2(1, 2.0) + foo1st3rdOutOf3(1, 2, 'c') + foo2ndOutOf2(1, 'c') + fooAllOutOf2('a','b') + fooAllOutOf2(1.0,1.0) + } +} \ No newline at end of file diff --git a/tests/pos/specialization/subtype_specialization.scala b/tests/pos/specialization/subtype_specialization.scala new file mode 100644 index 000000000000..1ce6d570f6b8 --- /dev/null +++ b/tests/pos/specialization/subtype_specialization.scala @@ -0,0 +1,13 @@ +object subtype_specialization { + + class Seq[+A] + + case class FirstName[T](name: String) extends Seq[Char] {} + + def foo[@specialized(Char) A](stuff: Seq[A]): Seq[A] = { + stuff + } + + val s: Seq[FirstName] = foo[FirstName](new Seq[FirstName]) + +} diff --git a/tests/run/method-specialization.check b/tests/run/method-specialization.check index 23594baa83ec..46894d008a35 100644 --- a/tests/run/method-specialization.check +++ b/tests/run/method-specialization.check @@ -1,5 +1,7 @@ 10 82 3 +10 int -double,int \ No newline at end of file +double,int +int,class java.lang.Object \ No newline at end of file diff --git a/tests/run/method-specialization.scala b/tests/run/method-specialization.scala index a1ab0271958d..ba95ac51f585 100644 --- a/tests/run/method-specialization.scala +++ b/tests/run/method-specialization.scala @@ -4,11 +4,14 @@ object Test extends dotty.runtime.LegacyApp { def foo[@specialized U](u: U) = u } class Bar { - def bar[@specialized U, V](u: U, v: V) = v + def bar[@specialized U,@specialized V](u: U, v: V) = v } class Baz { def baz[@specialized(Int, Char) V](v: V): V = v } + class Kung { + def kung[@specialized U, V](u: U, v: V) = println(u.getClass) + } override def main(args: Array[String]): Unit = { /** @@ -17,24 +20,36 @@ object Test extends dotty.runtime.LegacyApp { * 10 * 82 * 3 + * 10 * int * double,int - * int,double + * int,class java.lang.Object */ val a = new Foo val b = new Bar val c = new Baz + val d = new Kung val foo_methods = a.getClass.getMethods val bar_methods = b.getClass.getMethods val baz_methods = c.getClass.getMethods + val kung_methods = d.getClass.getMethods println(foo_methods.filter(_.toString.contains("foo")).length) println(bar_methods.filter(_.toString.contains("bar")).length) println(baz_methods.filter(_.toString.contains("baz")).length) + println(kung_methods.filter(_.toString.contains("kung")).length) val baz_int_param = baz_methods.filter(_.toString.contains("$mIc$sp")).head.getParameterTypes.mkString(",") val bar_int_double_params = bar_methods.filter(s => s.toString.contains("$mDIc$sp")) + val kung_int_gen_params = kung_methods.filter(s => s.toString.contains("$mIc$sp")) println(baz_int_param) println(bar_int_double_params.head.getParameterTypes.mkString(",")) + println(kung_int_gen_params.head.getParameterTypes.mkString(",")) + + def genericKung[A](a: A) = d.kung(a, a) + genericKung(1) + + d.kung(1, 1) + d.kung(1.0, 1.0) } } \ No newline at end of file From d48c520d49e5a37f8e3aa434119200d9203202bc Mon Sep 17 00:00:00 2001 From: AlexSikia Date: Mon, 10 Aug 2015 11:45:57 +0200 Subject: [PATCH 34/37] Fix transformation of TypeApply in the specialised case Widening the type of the function is necessary before checking for parameterlessness of the methods. --- src/dotty/tools/dotc/transform/TypeSpecializer.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/transform/TypeSpecializer.scala b/src/dotty/tools/dotc/transform/TypeSpecializer.scala index 21bd17c5669c..bdd3d8d553c5 100644 --- a/src/dotty/tools/dotc/transform/TypeSpecializer.scala +++ b/src/dotty/tools/dotc/transform/TypeSpecializer.scala @@ -344,7 +344,7 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { override def transformTypeApply(tree: tpd.TypeApply)(implicit ctx: Context, info: TransformerInfo): Tree = { val TypeApply(fun, _) = tree - if (fun.tpe.isParameterless) rewireTree(tree) + if (fun.tpe.widenDealias.isParameterless) rewireTree(tree) else tree } From 6579d82eb748b3197d7b221c005cca59d565715f Mon Sep 17 00:00:00 2001 From: AlexSikia Date: Tue, 11 Aug 2015 16:44:27 +0200 Subject: [PATCH 35/37] Remove sorting of method args names when mangling for specialisation --- src/dotty/tools/dotc/core/NameOps.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/core/NameOps.scala b/src/dotty/tools/dotc/core/NameOps.scala index d3a35e7337e6..8d3982a33d7d 100644 --- a/src/dotty/tools/dotc/core/NameOps.scala +++ b/src/dotty/tools/dotc/core/NameOps.scala @@ -259,7 +259,7 @@ object NameOps { } } - val methodTags: Seq[Name] = (methodTargs zip methodTargsNames).sortBy(_._2).map(x => typeToTag(x._1)) + val methodTags: Seq[Name] = (methodTargs zip methodTargsNames).map(x => typeToTag(x._1)) val classTags: Seq[Name] = (classTargs zip classTargsNames).sortBy(_._2).map(x => typeToTag(x._1)) name.fromName(name ++ nme.specializedTypeNames.prefix ++ From 1cc3c83663808ac7605d970abfef4abab70bea04 Mon Sep 17 00:00:00 2001 From: AlexSikia Date: Thu, 20 Aug 2015 15:38:23 +0200 Subject: [PATCH 36/37] Clean up code Refines names of variables and refactors small (outdated) parts of the code. --- .../tools/dotc/transform/PreSpecializer.scala | 16 ++- .../dotc/transform/TypeSpecializer.scala | 118 ++++++++++-------- test/dotc/tests.scala | 21 +--- 3 files changed, 73 insertions(+), 82 deletions(-) diff --git a/src/dotty/tools/dotc/transform/PreSpecializer.scala b/src/dotty/tools/dotc/transform/PreSpecializer.scala index 6212d71c417e..c91ece37eb4c 100644 --- a/src/dotty/tools/dotc/transform/PreSpecializer.scala +++ b/src/dotty/tools/dotc/transform/PreSpecializer.scala @@ -93,7 +93,7 @@ class PreSpecializer extends MiniPhaseTransform { val args = annot.arguments if (args.isEmpty) primitiveTypes else args.head match { - case a @ Typed(SeqLiteral(types), _) => + case _ @ Typed(SeqLiteral(types), _) => types.map(t => primitiveCompanionToPrimitive(t.tpe)) case a @ Ident(groupName) => // Matches `@specialized` annotations on Specializable Groups specializableToPrimitive(a.tpe.asInstanceOf[Type], groupName) @@ -106,18 +106,16 @@ class PreSpecializer extends MiniPhaseTransform { override def transformDefDef(tree: tpd.DefDef)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { val tparams = tree.tparams.map(_.symbol) - val st = tparams.zipWithIndex.map{case(sym, i) => (i, getSpec(sym))} - sendRequests(st, tree) + val requests = tparams.zipWithIndex.map{case(sym, i) => (i, getSpec(sym))} + if (requests.nonEmpty) sendRequests(requests, tree) tree } def sendRequests(requests: List[(Int, List[Type])], tree: tpd.Tree)(implicit ctx: Context): Unit = { - if (requests.nonEmpty) { - requests.map{ - case (index, types) if types.nonEmpty => - ctx.specializePhase.asInstanceOf[TypeSpecializer].registerSpecializationRequest(tree.symbol)(index, types) - case _ => - } + requests.map { + case (index, types) if types.nonEmpty => + ctx.specializePhase.asInstanceOf[TypeSpecializer].registerSpecializationRequest(tree.symbol)(index, types) + case _ => } } } diff --git a/src/dotty/tools/dotc/transform/TypeSpecializer.scala b/src/dotty/tools/dotc/transform/TypeSpecializer.scala index bdd3d8d553c5..4c69b73935df 100644 --- a/src/dotty/tools/dotc/transform/TypeSpecializer.scala +++ b/src/dotty/tools/dotc/transform/TypeSpecializer.scala @@ -34,38 +34,38 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { /** * Methods requested for specialization - * Generic Symbol => List[ (position in type args list, specialized type requested) ] + * Generic Symbol => List[ (position in list of args, specialized type requested) ] */ private val specializationRequests: mutable.HashMap[Symbols.Symbol, List[(Int, List[Type])]] = mutable.HashMap.empty /** - * A list of instantiation values of generics (helps with recursive polymorphic methods) + * A list of instantiation values of generics (for recursive polymorphic methods) */ private val genericToInstantiation: mutable.HashMap[Symbols.Symbol, Type] = mutable.HashMap.empty /** * A map that links symbols to their specialized variants. * Each symbol maps to another map, from the list of specialization types to the specialized symbol. - * Generic symbol => Map[ Tuple(position in type args list, specialized Type) => Specialized Symbol ] + * Generic symbol => + * Map{ List of [ Tuple(position in list of args, specialized Type) ] for each variant => Specialized Symbol } */ private val newSymbolMap: mutable.HashMap[Symbol, mutable.HashMap[List[(Int, Type)], Symbols.Symbol]] = mutable.HashMap.empty /** * A map from specialised symbols to the indices of their remaining generic types */ - private val newSymbolsGenericIndices: mutable.HashMap[Symbol, List[Int]] = mutable.HashMap.empty + private val newSymToGenIndices: mutable.HashMap[Symbol, List[Int]] = mutable.HashMap.empty /** * A list of symbols gone through the specialisation pipeline * Is used to make calls to transformInfo idempotent */ - private val specialized: ListBuffer[Symbol] = ListBuffer.empty + private val processed: ListBuffer[Symbol] = ListBuffer.empty def allowedToSpecialize(sym: Symbol, numOfTypes: Int)(implicit ctx: Context) = numOfTypes > 0 && sym.name != nme.asInstanceOf_ && !newSymbolMap.contains(sym) && - !sym.name.toString.contains("$sp") && !(sym is Flags.JavaDefined) && !sym.isPrimaryConstructor @@ -88,7 +88,8 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { def requestedSpecialization(decl: Symbol)(implicit ctx: Context): Boolean = ctx.settings.Yspecialize.value != 0 || specializationRequests.contains(decl) - def registerSpecializationRequest(method: Symbols.Symbol)(index: Int, arguments: List[Type])(implicit ctx: Context) = { + def registerSpecializationRequest(method: Symbols.Symbol)(index: Int, arguments: List[Type]) + (implicit ctx: Context) = { if (ctx.phaseId > this.treeTransformPhase.id) assert(ctx.phaseId <= this.treeTransformPhase.id) val prev = specializationRequests.getOrElse(method, List.empty) @@ -104,7 +105,7 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { } def specializeMethods(sym: Symbol) = { - specialized += sym + processed += sym sym.info match { case classInfo: ClassInfo => val newDecls = classInfo.decls @@ -114,7 +115,7 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { .flatMap(decl => { decl.info.widen match { case poly: PolyType if allowedToSpecialize(decl.symbol, poly.paramNames.length) => - generateMethodSpecializations(getSpecTypes(decl, poly))(List.empty, poly, decl) + generateMethodSpecializations(getSpecTypes(decl, poly), List.empty)(poly, decl) case _ => Nil } }) @@ -128,7 +129,7 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { } else if (requestedSpecialization(sym) && allowedToSpecialize(sym, poly.paramNames.length)) { - generateMethodSpecializations(getSpecTypes(sym, poly))(List.empty, poly, sym) + generateMethodSpecializations(getSpecTypes(sym, poly), List.empty)(poly, sym) tp } else tp @@ -136,17 +137,17 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { } } - def generateMethodSpecializations(specTypes: List[(Int, List[Type])]) - (instantiations: List[(Int, Type)], poly: PolyType, decl: Symbol) + def generateMethodSpecializations(specTypes: List[(Int, List[Type])], instantiations: List[(Int, Type)]) + (poly: PolyType, decl: Symbol) (implicit ctx: Context): List[Symbol] = { if (specTypes.nonEmpty) { specTypes.head match{ case (i, tpes) if tpes.nonEmpty => tpes.flatMap(tpe => - generateMethodSpecializations(specTypes.tail)((i, tpe) :: instantiations, poly, decl) + generateMethodSpecializations(specTypes.tail, (i, tpe) :: instantiations)(poly, decl) ) case (i, nil) => - generateMethodSpecializations(specTypes.tail)(instantiations, poly, decl) + generateMethodSpecializations(specTypes.tail, instantiations)(poly, decl) } } else { @@ -154,28 +155,33 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { else generateSpecializedSymbol(instantiations.reverse, poly, decl) :: Nil } } + def generateSpecializedSymbol(instantiations: List[(Int, Type)], poly: PolyType, decl: Symbol) (implicit ctx: Context): Symbol = { val indices = instantiations.map(_._1) val instanceTypes = instantiations.map(_._2) - val newSym = ctx.newSymbol(decl.owner, NameOps.NameDecorator(decl.name).specializedFor(Nil, Nil, instanceTypes, instanceTypes.map(_.asInstanceOf[NamedType].name)), - decl.flags | Flags.Synthetic, { - if (indices.length != poly.paramNames.length) // Partial Specialisation case - poly.instantiate(indices, instanceTypes) // Returns a PolyType with uninstantiated types kept generic - else - poly.instantiate(instanceTypes) // Returns a MethodType, as no polymorphic types remains - }) + val newSym = ctx.newSymbol( + decl.owner, + NameOps.NameDecorator(decl.name) + .specializedFor(Nil, Nil, instanceTypes, instanceTypes.map(_.asInstanceOf[NamedType].name)), + decl.flags | Flags.Synthetic, + { if (indices.length != poly.paramNames.length) // Partial Specialisation case + poly.instantiate(indices, instanceTypes) // Returns a PolyType with uninstantiated types kept generic + else + poly.instantiate(instanceTypes) // Returns a MethodType, no polymorphic type remains + } + ) val map = newSymbolMap.getOrElse(decl, mutable.HashMap.empty) map.put(instantiations, newSym) newSymbolMap.put(decl, map) - newSymbolsGenericIndices.put(newSym, indices) + newSymToGenIndices.put(newSym, indices) newSym } - if (!specialized.contains(sym) && + if (!processed.contains(sym) && (sym ne defn.ScalaPredefModule.moduleClass) && !(sym is Flags.JavaDefined) && !(sym is Flags.Scala2x) && @@ -204,15 +210,14 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { case (_, i) => instantiation.getOrElse(i, { holePos += 1 PolyParam(pt, holePos) - } - ).widen + }).widen } } if (newSymbolMap.contains(decl)) { - val declSpecs = newSymbolMap(decl) - val newSyms = declSpecs.values.toList - val instantiations = declSpecs.keys.toArray + val specInfo = newSymbolMap(decl) + val newSyms = specInfo.values.toList + val instantiationss = specInfo.keys.toArray var index = -1 ctx.debuglog(s"specializing ${tree.symbol} for $origTParams") newSyms.map { newSym => @@ -220,34 +225,38 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { val newSymType = newSym.info.widenDealias polyDefDef(newSym.asTerm, { tparams => vparams => { val instTypes = newSymType match { - case pt: PolyType => makeTypesList(origTParams, instantiations(index).toMap, pt) - case _ => instantiations(index).map(_._2) + case pt: PolyType => + makeTypesList(origTParams, instantiationss(index).toMap, pt) // Will add missing PolyParams + case _ => instantiationss(index).map(_._2) } val treemap: (Tree => Tree) = _ match { case Return(t, from) if from.symbol == tree.symbol => Return(t, ref(newSym)) case t: TypeApply => - (origTParams zip instTypes).foreach(x => genericToInstantiation.put(x._1, x._2)) + (origTParams zip instTypes) + .foreach{case (genTpe, instTpe) => genericToInstantiation.put(genTpe, instTpe)} transformTypeApply(t) case t: Apply => - (origTParams zip instTypes).foreach(x => genericToInstantiation.put(x._1, x._2)) + (origTParams zip instTypes) + .foreach{case (genTpe, instTpe) => genericToInstantiation.put(genTpe, instTpe)} transformApply(t) case t => t } val abstractPolyType = tree.symbol.info.widenDealias.asInstanceOf[PolyType] + val vparamTpes = vparams.flatten.map(_.tpe) val typemap = new TypeMap { override def apply(tp: Type): Type = { val t = mapOver(tp) .substDealias(origTParams, instTypes) .substParams(abstractPolyType, instTypes) - .subst(origVParams, vparams.flatten.map(_.tpe)) + .subst(origVParams, vparamTpes) newSymType match { case mt: MethodType if tparams.isEmpty => - t.substParams(newSymType.asInstanceOf[MethodType], vparams.flatten.map(_.tpe)) + t.substParams(newSymType.asInstanceOf[MethodType], vparamTpes) case pt: PolyType => t.substParams(newSymType.asInstanceOf[PolyType], tparams) - .substParams(newSymType.resultType.asInstanceOf[MethodType], vparams.flatten.map(_.tpe)) + .substParams(newSymType.resultType.asInstanceOf[MethodType], vparamTpes) case _ => t } } @@ -265,11 +274,13 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { override def transform(tree1: Tree)(implicit ctx: Context) = super.transform(tree1) match { case t @ Apply(fun, args) => assert(sameLength(args, fun.tpe.widen.firstParamTypes), - s"Wrong number of parameters. Expected: ${fun.tpe.widen.firstParamTypes.length}. Found: ${args.length}") + s"Wrong number of parameters." + + s"Expected: ${fun.tpe.widen.firstParamTypes.length}." + + s"Found: ${args.length}") val newArgs = (args zip fun.tpe.widen.firstParamTypes).map{ - case(tr, tpe) => + case(arg, tpe) => assert(tpe.widen ne NoType, "Bad cast when specializing") - tr.ensureConforms(typemap(tpe.widen)) + arg.ensureConforms(typemap(tpe.widen)) } if (sameTypes(args, newArgs)) { t @@ -308,23 +319,22 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { assert(tree.isInstanceOf[TypeApply]) val TypeApply(fun,args) = tree if (newSymbolMap.contains(fun.symbol)){ - val newSymInfos = newSymbolMap(fun.symbol) - val betterDefs = newSymInfos.filter( - x => { - val instantiation = x._1 - instantiation.forall { x => - val ord = x._1 - val tp = x._2 - args(ord).tpe <:< tp - }}).toList + val newSymInfo = newSymbolMap(fun.symbol) + val betterDefs = newSymInfo.filter{ + case (instantiations, symbol) => { + instantiations.forall { + case (ord, specTpe) => + args(ord).tpe <:< specTpe + }}}.toList if (betterDefs.length > 1) { - ctx.debuglog(s"Several specialized variants fit for ${fun.symbol.name} of ${fun.symbol.owner}. Defaulting to no specialization.") + ctx.debuglog(s"Several specialized variants fit for ${fun.symbol.name} of ${fun.symbol.owner}." + + s" Defaulting to no specialization.") tree } else if (betterDefs.nonEmpty) { - val bestDef = betterDefs.head + val newFunSym = betterDefs.head._2 ctx.debuglog(s"method ${fun.symbol.name} of ${fun.symbol.owner} rewired to specialized variant") val prefix = fun match { case Select(pre, name) => @@ -336,8 +346,8 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { else EmptyTree case _ => EmptyTree } - if (prefix ne EmptyTree) prefix.select(bestDef._2) - else ref(bestDef._2) + if (prefix ne EmptyTree) prefix.select(newFunSym) + else ref(newFunSym) } else tree } else tree } @@ -357,8 +367,10 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { if (fun ne newFun) { newFun.symbol.info.widenDealias match { case pt: PolyType => // Need to apply types to the remaining generics first - val tpeOfRemainingGenerics = typeArgs.zipWithIndex.filterNot(x => newSymbolsGenericIndices(newFun.symbol).contains(x._2)).map(_._1) - assert(tpeOfRemainingGenerics.nonEmpty, s"Remaining generics on ${newFun.symbol.name} not properly instantiated: missing types") + val tpeOfRemainingGenerics = + typeArgs.zipWithIndex.filterNot(x => newSymToGenIndices(newFun.symbol).contains(x._2)).map(_._1) + assert(tpeOfRemainingGenerics.nonEmpty, + s"Remaining generics on ${newFun.symbol.name} not properly instantiated: missing types") Apply(TypeApply(newFun, tpeOfRemainingGenerics), args) case _ => Apply(newFun, args) diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index ad2c01c4da4d..db8775b59e00 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -36,17 +36,12 @@ class tests extends CompilerTest { val allowDeepSubtypes = defaultOptions diff List("-Yno-deep-subtypes") val allowDoubleBindings = defaultOptions diff List("-Yno-double-bindings") - val specialise = List("-Yspecialize:all") - val testsDir = "./tests/" val posDir = testsDir + "pos/" val posSpecialDir = testsDir + "pos-special/" val negDir = testsDir + "neg/" val runDir = testsDir + "run/" val newDir = testsDir + "new/" - val specialDir = posDir + "specialization/" - val miniMethodDir = testsDir + "method_minibox/" - val miniMoreDir = testsDir + "more_minibox/" val sourceDir = "./src/" val dottyDir = sourceDir + "dotty/" @@ -96,6 +91,7 @@ class tests extends CompilerTest { @Test def pos_packageObj = compileFile(posDir, "i0239", twice) @Test def pos_anonClassSubtyping = compileFile(posDir, "anonClassSubtyping", twice) @Test def pos_extmethods = compileFile(posDir, "extmethods", twice) + @Test def pos_specialization = compileDir(posDir, "specialization", twice) @Test def pos_all = compileFiles(posDir) // twice omitted to make tests run faster @@ -202,20 +198,5 @@ class tests extends CompilerTest { val javaDir = "./tests/pos/java-interop/" @Test def java_all = compileFiles(javaDir, twice) - - @Test def specialization = compileDir(posDir, "specialization", twice) - @Test def simple_specialization = compileFile(specialDir, "simple_specialization", twice) - @Test def mutual_spec = compileFile(specialDir, "mutual_specialization", twice) - @Test def return_spec = compileFile(specialDir, "return_specialization", twice) - @Test def nothing_spec = compileFile(specialDir, "nothing_specialization", twice) - @Test def method_in_class_spec = compileFile(specialDir, "method_in_class_specialization", twice) - @Test def method_in_method_spec = compileFile(specialDir, "method_in_method_specialization", twice) - @Test def pos_type_check = compileFile(specialDir, "type_test", twice) - @Test def bounds_spec = compileFile(specialDir, "bounds_specialization", twice) - @Test def multi_spec = compileFile(specialDir, "multi_specialization", twice) - @Test def pos_this_specialization = compileFile(specialDir, "this_specialization", twice) - @Test def anyRef_spec = runFile(specialDir, "anyRef_specialization", twice) - @Test def genClass_spec = compileFile(specialDir, "genericClass_specialization", twice) - //@Test def dotc_compilercommand = compileFile(dotcDir + "config/", "CompilerCommand") } From 417fc9611881272383bccfd2d77f2671d36bcfc2 Mon Sep 17 00:00:00 2001 From: AlexSikia Date: Fri, 21 Aug 2015 14:11:53 +0200 Subject: [PATCH 37/37] Cast return value of `specializedFor` to `TermName` --- src/dotty/tools/dotc/transform/TypeSpecializer.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/transform/TypeSpecializer.scala b/src/dotty/tools/dotc/transform/TypeSpecializer.scala index 4c69b73935df..af52f6552a1f 100644 --- a/src/dotty/tools/dotc/transform/TypeSpecializer.scala +++ b/src/dotty/tools/dotc/transform/TypeSpecializer.scala @@ -4,6 +4,7 @@ import dotty.tools.dotc.ast.{tpd, TreeTypeMap} import dotty.tools.dotc.ast.Trees._ import dotty.tools.dotc.core.Contexts.Context import dotty.tools.dotc.core.DenotTransformers.InfoTransformer +import dotty.tools.dotc.core.Names.TermName import dotty.tools.dotc.core.Symbols.Symbol import dotty.tools.dotc.core.{NameOps, Symbols, Flags} import dotty.tools.dotc.core.Types._ @@ -163,7 +164,8 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer { val newSym = ctx.newSymbol( decl.owner, NameOps.NameDecorator(decl.name) - .specializedFor(Nil, Nil, instanceTypes, instanceTypes.map(_.asInstanceOf[NamedType].name)), + .specializedFor(Nil, Nil, instanceTypes, instanceTypes.map(_.asInstanceOf[NamedType].name)) + .asInstanceOf[TermName], decl.flags | Flags.Synthetic, { if (indices.length != poly.paramNames.length) // Partial Specialisation case poly.instantiate(indices, instanceTypes) // Returns a PolyType with uninstantiated types kept generic