From 1ee66027681d726eed7887b39850eaaeee9f8250 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Sat, 18 May 2019 09:37:51 +0200 Subject: [PATCH 01/11] Improve performance of generic tuples code generation Improves the state of #6524. Instead of convering all tuples to arrays and performing the operations on them, we take advantage of the fact that those Tuples are products and have productIterators. Implementations of tail, *: and ++ on iterators do not require extra collentions to be created, hence it is also probably more performant at runtime. --- library/src-3.x/scala/Tuple.scala | 64 +++++++++++++++---------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/library/src-3.x/scala/Tuple.scala b/library/src-3.x/scala/Tuple.scala index 2f0b1a9626de..e2848c0b7445 100644 --- a/library/src-3.x/scala/Tuple.scala +++ b/library/src-3.x/scala/Tuple.scala @@ -49,7 +49,7 @@ sealed trait Tuple extends Any { val t = asInstanceOf[Tuple4[_, _, _, _]] Tuple5(x, t._1, t._2, t._3, t._4).asInstanceOf[Result] case Some(n) => - knownTupleFromArray[H *: this.type](DynamicTuple.cons$Array(x, toArray)) + knownTupleFromItrator[H *: this.type](Iterator.single(x) ++ this.asInstanceOf[Product].productIterator) case _ => DynamicTuple.dynamic_*:[This, H](this, x) } @@ -74,7 +74,7 @@ sealed trait Tuple extends Any { val u = that.asInstanceOf[Tuple2[_, _]] Tuple4(t._1, t._2, u._1, u._2).asInstanceOf[Result] case _ => - knownTupleFromArray[Result](this.toArray ++ that.toArray) + knownTupleFromItrator[Result](this.asInstanceOf[Product].productIterator ++ that.asInstanceOf[Product].productIterator) } case Some(3) => val t = asInstanceOf[Tuple3[_, _, _]] @@ -84,11 +84,11 @@ sealed trait Tuple extends Any { val u = that.asInstanceOf[Tuple1[_]] Tuple4(t._1, t._2, t._3, u._1).asInstanceOf[Result] case _ => - knownTupleFromArray[Result](this.toArray ++ that.toArray) - } + knownTupleFromItrator[Result](this.asInstanceOf[Product].productIterator ++ that.asInstanceOf[Product].productIterator) + } case Some(_) => if (constValue[BoundedSize[that.type]] == 0) this.asInstanceOf[Result] - else knownTupleFromArray[Result](this.toArray ++ that.toArray) + else knownTupleFromItrator[Result](this.asInstanceOf[Product].productIterator ++ that.asInstanceOf[Product].productIterator) case None => DynamicTuple.dynamic_++[This, that.type](this, that) } @@ -143,34 +143,32 @@ object Tuple { private[scala] type BoundedSize[X] = BoundedSizeRecur[X, 23] - private[scala] val $emptyArray = Array[Object]() - - private[scala] inline def knownTupleFromArray[T <: Tuple](xs: Array[Object]): T = + private[scala] inline def knownTupleFromItrator[T <: Tuple](it: Iterator[Any]): T = inline constValue[BoundedSize[T]] match { case 0 => ().asInstanceOf[T] - case 1 => Tuple1(xs(0)).asInstanceOf[T] - case 2 => Tuple2(xs(0), xs(1)).asInstanceOf[T] - case 3 => Tuple3(xs(0), xs(1), xs(2)).asInstanceOf[T] - case 4 => Tuple4(xs(0), xs(1), xs(2), xs(3)).asInstanceOf[T] - case 5 => Tuple5(xs(0), xs(1), xs(2), xs(3), xs(4)).asInstanceOf[T] - case 6 => Tuple6(xs(0), xs(1), xs(2), xs(3), xs(4), xs(5)).asInstanceOf[T] - case 7 => Tuple7(xs(0), xs(1), xs(2), xs(3), xs(4), xs(5), xs(6)).asInstanceOf[T] - case 8 => Tuple8(xs(0), xs(1), xs(2), xs(3), xs(4), xs(5), xs(6), xs(7)).asInstanceOf[T] - case 9 => Tuple9(xs(0), xs(1), xs(2), xs(3), xs(4), xs(5), xs(6), xs(7), xs(8)).asInstanceOf[T] - case 10 => Tuple10(xs(0), xs(1), xs(2), xs(3), xs(4), xs(5), xs(6), xs(7), xs(8), xs(9)).asInstanceOf[T] - case 11 => Tuple11(xs(0), xs(1), xs(2), xs(3), xs(4), xs(5), xs(6), xs(7), xs(8), xs(9), xs(10)).asInstanceOf[T] - case 12 => Tuple12(xs(0), xs(1), xs(2), xs(3), xs(4), xs(5), xs(6), xs(7), xs(8), xs(9), xs(10), xs(11)).asInstanceOf[T] - case 13 => Tuple13(xs(0), xs(1), xs(2), xs(3), xs(4), xs(5), xs(6), xs(7), xs(8), xs(9), xs(10), xs(11), xs(12)).asInstanceOf[T] - case 14 => Tuple14(xs(0), xs(1), xs(2), xs(3), xs(4), xs(5), xs(6), xs(7), xs(8), xs(9), xs(10), xs(11), xs(12), xs(13)).asInstanceOf[T] - case 15 => Tuple15(xs(0), xs(1), xs(2), xs(3), xs(4), xs(5), xs(6), xs(7), xs(8), xs(9), xs(10), xs(11), xs(12), xs(13), xs(14)).asInstanceOf[T] - case 16 => Tuple16(xs(0), xs(1), xs(2), xs(3), xs(4), xs(5), xs(6), xs(7), xs(8), xs(9), xs(10), xs(11), xs(12), xs(13), xs(14), xs(15)).asInstanceOf[T] - case 17 => Tuple17(xs(0), xs(1), xs(2), xs(3), xs(4), xs(5), xs(6), xs(7), xs(8), xs(9), xs(10), xs(11), xs(12), xs(13), xs(14), xs(15), xs(16)).asInstanceOf[T] - case 18 => Tuple18(xs(0), xs(1), xs(2), xs(3), xs(4), xs(5), xs(6), xs(7), xs(8), xs(9), xs(10), xs(11), xs(12), xs(13), xs(14), xs(15), xs(16), xs(17)).asInstanceOf[T] - case 19 => Tuple19(xs(0), xs(1), xs(2), xs(3), xs(4), xs(5), xs(6), xs(7), xs(8), xs(9), xs(10), xs(11), xs(12), xs(13), xs(14), xs(15), xs(16), xs(17), xs(18)).asInstanceOf[T] - case 20 => Tuple20(xs(0), xs(1), xs(2), xs(3), xs(4), xs(5), xs(6), xs(7), xs(8), xs(9), xs(10), xs(11), xs(12), xs(13), xs(14), xs(15), xs(16), xs(17), xs(18), xs(19)).asInstanceOf[T] - case 21 => Tuple21(xs(0), xs(1), xs(2), xs(3), xs(4), xs(5), xs(6), xs(7), xs(8), xs(9), xs(10), xs(11), xs(12), xs(13), xs(14), xs(15), xs(16), xs(17), xs(18), xs(19), xs(20)).asInstanceOf[T] - case 22 => Tuple22(xs(0), xs(1), xs(2), xs(3), xs(4), xs(5), xs(6), xs(7), xs(8), xs(9), xs(10), xs(11), xs(12), xs(13), xs(14), xs(15), xs(16), xs(17), xs(18), xs(19), xs(20), xs(21)).asInstanceOf[T] - case _ => TupleXXL(xs).asInstanceOf[T] + case 1 => Tuple1(it.next()).asInstanceOf[T] + case 2 => Tuple2(it.next(), it.next()).asInstanceOf[T] + case 3 => Tuple3(it.next(), it.next(), it.next()).asInstanceOf[T] + case 4 => Tuple4(it.next(), it.next(), it.next(), it.next()).asInstanceOf[T] + case 5 => Tuple5(it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T] + case 6 => Tuple6(it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T] + case 7 => Tuple7(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T] + case 8 => Tuple8(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T] + case 9 => Tuple9(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T] + case 10 => Tuple10(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T] + case 11 => Tuple11(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T] + case 12 => Tuple12(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T] + case 13 => Tuple13(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T] + case 14 => Tuple14(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T] + case 15 => Tuple15(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T] + case 16 => Tuple16(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T] + case 17 => Tuple17(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T] + case 18 => Tuple18(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T] + case 19 => Tuple19(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T] + case 20 => Tuple20(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T] + case 21 => Tuple21(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T] + case 22 => Tuple22(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T] + case _ => TupleXXL(it.asInstanceOf[Iterator[Object]].toArray).asInstanceOf[T] } def fromArray[T](xs: Array[T]): Tuple = { @@ -233,7 +231,9 @@ sealed trait NonEmptyTuple extends Tuple { val t = asInstanceOf[Tuple5[_, _, _, _, _]] Tuple4(t._2, t._3, t._4, t._5).asInstanceOf[Result] case Some(n) if n > 5 => - knownTupleFromArray[Result](toArray.tail) + val it = this.asInstanceOf[Product].productIterator + it.next() + knownTupleFromItrator[Result](it) case None => DynamicTuple.dynamicTail[This](this) } From ce50d3ba5d23c9cacc2d985ca64b6a07c8ca79dc Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Sat, 18 May 2019 12:05:01 +0200 Subject: [PATCH 02/11] Avoid recomputing tuple size from type --- library/src-3.x/scala/Tuple.scala | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/library/src-3.x/scala/Tuple.scala b/library/src-3.x/scala/Tuple.scala index e2848c0b7445..a330a1bd78aa 100644 --- a/library/src-3.x/scala/Tuple.scala +++ b/library/src-3.x/scala/Tuple.scala @@ -49,7 +49,7 @@ sealed trait Tuple extends Any { val t = asInstanceOf[Tuple4[_, _, _, _]] Tuple5(x, t._1, t._2, t._3, t._4).asInstanceOf[Result] case Some(n) => - knownTupleFromItrator[H *: this.type](Iterator.single(x) ++ this.asInstanceOf[Product].productIterator) + knownTupleFromItrator[H *: this.type](n + 1, Iterator.single(x) ++ this.asInstanceOf[Product].productIterator) case _ => DynamicTuple.dynamic_*:[This, H](this, x) } @@ -73,8 +73,8 @@ sealed trait Tuple extends Any { case 2 => val u = that.asInstanceOf[Tuple2[_, _]] Tuple4(t._1, t._2, u._1, u._2).asInstanceOf[Result] - case _ => - knownTupleFromItrator[Result](this.asInstanceOf[Product].productIterator ++ that.asInstanceOf[Product].productIterator) + case m => + knownTupleFromItrator[Result](2 + m, this.asInstanceOf[Product].productIterator ++ that.asInstanceOf[Product].productIterator) } case Some(3) => val t = asInstanceOf[Tuple3[_, _, _]] @@ -83,12 +83,14 @@ sealed trait Tuple extends Any { case 1 => val u = that.asInstanceOf[Tuple1[_]] Tuple4(t._1, t._2, t._3, u._1).asInstanceOf[Result] - case _ => - knownTupleFromItrator[Result](this.asInstanceOf[Product].productIterator ++ that.asInstanceOf[Product].productIterator) + case m => + knownTupleFromItrator[Result](3 + m, this.asInstanceOf[Product].productIterator ++ that.asInstanceOf[Product].productIterator) } - case Some(_) => - if (constValue[BoundedSize[that.type]] == 0) this.asInstanceOf[Result] - else knownTupleFromItrator[Result](this.asInstanceOf[Product].productIterator ++ that.asInstanceOf[Product].productIterator) + case Some(n) => + inline constValue[BoundedSize[that.type]] match { + case 0 => this.asInstanceOf[Result] + case m => knownTupleFromItrator[Result](n + m, this.asInstanceOf[Product].productIterator ++ that.asInstanceOf[Product].productIterator) + } case None => DynamicTuple.dynamic_++[This, that.type](this, that) } @@ -141,10 +143,10 @@ object Tuple { } } - private[scala] type BoundedSize[X] = BoundedSizeRecur[X, 23] + private[scala] type BoundedSize[X] = BoundedSizeRecur[X, 24] - private[scala] inline def knownTupleFromItrator[T <: Tuple](it: Iterator[Any]): T = - inline constValue[BoundedSize[T]] match { + private[scala] inline def knownTupleFromItrator[T <: Tuple](n: Int, it: Iterator[Any]): T = + inline n match { case 0 => ().asInstanceOf[T] case 1 => Tuple1(it.next()).asInstanceOf[T] case 2 => Tuple2(it.next(), it.next()).asInstanceOf[T] @@ -233,7 +235,7 @@ sealed trait NonEmptyTuple extends Tuple { case Some(n) if n > 5 => val it = this.asInstanceOf[Product].productIterator it.next() - knownTupleFromItrator[Result](it) + knownTupleFromItrator[Result](n - 1, it) case None => DynamicTuple.dynamicTail[This](this) } From 7666e034d1a68b51ba5d50c70e584d633e26ee60 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 21 May 2019 09:05:20 +0200 Subject: [PATCH 03/11] Implement Tuple operation directly in the compiler To increase the performance of compiling code with Tuple/NonEmptyTuple operations --- compiler/src/dotty/tools/dotc/Compiler.scala | 1 + .../dotty/tools/dotc/core/Definitions.scala | 21 ++ .../src/dotty/tools/dotc/core/StdNames.scala | 1 + .../tools/dotc/printing/RefinedPrinter.scala | 10 +- .../tools/dotc/transform/GenericTuples.scala | 243 +++++++++++++++++ .../test/dotc/run-test-pickling.blacklist | 3 + library/src-3.x/scala/Tuple.scala | 250 +----------------- .../src-3.x/scala/runtime/DynamicTuple.scala | 51 ++-- library/src/scala/TupleXXL.scala | 1 + tests/run-deep-subtype/Tuple-head.scala | 31 ++- tests/run-deep-subtype/Tuple-size.scala | 31 ++- tests/run-deep-subtype/Tuple-tail.check | 15 ++ tests/run-deep-subtype/Tuple-tail.scala | 31 ++- tests/run-deep-subtype/Tuple-toArray.check | 15 ++ tests/run-deep-subtype/Tuple-toArray.scala | 31 ++- .../staged-tuples/StagedTuple.scala | 10 +- 16 files changed, 405 insertions(+), 340 deletions(-) create mode 100644 compiler/src/dotty/tools/dotc/transform/GenericTuples.scala diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index 53d51077e967..a6607a7fc9db 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -87,6 +87,7 @@ class Compiler { new AugmentScala2Traits, // Augments Scala2 traits with additional members needed for mixin composition. new ResolveSuper, // Implement super accessors new FunctionXXLForwarders, // Add forwarders for FunctionXXL apply method + new GenericTuples, // TODO new ArrayConstructors) :: // Intercept creation of (non-generic) arrays and intrinsify. List(new Erasure) :: // Rewrite types to JVM model, erasing all type parameters, abstract types and refinements. List(new ElimErasedValueType, // Expand erased value types to their underlying implmementation types diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index f7d64fad60bb..baf4df0b9b2e 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -688,6 +688,10 @@ class Definitions { lazy val Product_productPrefixR: TermRef = ProductClass.requiredMethodRef(nme.productPrefix) def Product_productPrefix(implicit ctx: Context): Symbol = Product_productPrefixR.symbol + lazy val IteratorType: TypeRef = ctx.requiredClassRef("scala.collection.Iterator") + def IteratorClass(implicit ctx: Context): ClassSymbol = IteratorType.symbol.asClass + def IteratorModule(implicit ctx: Context): Symbol = IteratorClass.companionModule + lazy val ModuleSerializationProxyType: TypeRef = ctx.requiredClassRef("scala.runtime.ModuleSerializationProxy") def ModuleSerializationProxyClass(implicit ctx: Context): ClassSymbol = ModuleSerializationProxyType.symbol.asClass lazy val ModuleSerializationProxyConstructor: TermSymbol = @@ -792,8 +796,12 @@ class Definitions { lazy val TupleTypeRef: TypeRef = ctx.requiredClassRef("scala.Tuple") def TupleClass(implicit ctx: Context): ClassSymbol = TupleTypeRef.symbol.asClass + lazy val Tuple_consR: TermRef = TupleClass.requiredMethod("*:").termRef + def Tuple_cons: Symbol = Tuple_consR.symbol lazy val NonEmptyTupleTypeRef: TypeRef = ctx.requiredClassRef("scala.NonEmptyTuple") def NonEmptyTupleClass(implicit ctx: Context): ClassSymbol = NonEmptyTupleTypeRef.symbol.asClass + lazy val NonEmptyTuple_tailR: TermRef = NonEmptyTupleClass.requiredMethod("tail").termRef + def NonEmptyTuple_tail: Symbol = NonEmptyTuple_tailR.symbol lazy val PairType: TypeRef = ctx.requiredClassRef("scala.*:") def PairClass(implicit ctx: Context): ClassSymbol = PairType.symbol.asClass @@ -803,6 +811,19 @@ class Definitions { def TupleXXL_apply(implicit ctx: Context): Symbol = TupleXXLModule.info.member(nme.apply).requiredSymbol("method", nme.apply, TupleXXLModule)(_.info.isVarArgsMethod) + def TupleXXL_fromIterator(implicit ctx: Context): Symbol = TupleXXLModule.requiredMethod("fromIterator") + + lazy val DynamicTupleModule: Symbol = ctx.requiredModule("scala.runtime.DynamicTuple") + lazy val DynamicTuple_consIterator: Symbol = DynamicTupleModule.requiredMethod("consIterator") + lazy val DynamicTuple_concatIterator: Symbol = DynamicTupleModule.requiredMethod("concatIterator") + lazy val DynamicTuple_dynamicApply: Symbol = DynamicTupleModule.requiredMethod("dynamicApply") + lazy val DynamicTuple_dynamicCons: Symbol = DynamicTupleModule.requiredMethod("dynamicCons") + lazy val DynamicTuple_dynamicHead: Symbol = DynamicTupleModule.requiredMethod("dynamicHead") + lazy val DynamicTuple_dynamicSize: Symbol = DynamicTupleModule.requiredMethod("dynamicSize") + lazy val DynamicTuple_dynamicTail: Symbol = DynamicTupleModule.requiredMethod("dynamicTail") + lazy val DynamicTuple_dynamicConcat: Symbol = DynamicTupleModule.requiredMethod("dynamicConcat") + lazy val DynamicTuple_dynamicToArray: Symbol = DynamicTupleModule.requiredMethod("dynamicToArray") + lazy val DynamicTuple_productToArray: Symbol = DynamicTupleModule.requiredMethod("productToArray") lazy val TupledFunctionTypeRef: TypeRef = ctx.requiredClassRef("scala.TupledFunction") def TupledFunctionClass(implicit ctx: Context): ClassSymbol = TupledFunctionTypeRef.symbol.asClass diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index 620a13de65e6..d0f359d1d9b8 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -417,6 +417,7 @@ object StdNames { val drop: N = "drop" val dynamics: N = "dynamics" val elem: N = "elem" + val elems: N = "elems" val emptyValDef: N = "emptyValDef" val ensureAccessible : N = "ensureAccessible" val enumTag: N = "enumTag" diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 9a068966f7dd..1788331bbbfa 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -632,14 +632,18 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { if (ctx.settings.XprintTypes.value && tree.hasType) { // add type to term nodes; replace type nodes with their types unless -Yprint-pos is also set. - def tp = tree.typeOpt match { + val tp1 = tree.typeOpt match { case tp: TermRef if tree.isInstanceOf[RefTree] && !tp.denot.isOverloaded => tp.underlying case tp => tp } + val tp2 = { + val tp = tp1.tryNormalize + if (tp != NoType) tp else tp1 + } if (!suppressTypes) - txt = ("<" ~ txt ~ ":" ~ toText(tp) ~ ">").close + txt = ("<" ~ txt ~ ":" ~ toText(tp2) ~ ">").close else if (tree.isType && !homogenizedView) - txt = toText(tp) + txt = toText(tp2) } if (!suppressPositions) { if (printPos) { diff --git a/compiler/src/dotty/tools/dotc/transform/GenericTuples.scala b/compiler/src/dotty/tools/dotc/transform/GenericTuples.scala new file mode 100644 index 000000000000..305a71ba40e6 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/GenericTuples.scala @@ -0,0 +1,243 @@ +package dotty.tools.dotc +package transform + +import core._ +import Constants.Constant +import Contexts.Context +import Decorators._ +import Flags._ +import ast.Trees._ +import Definitions._ +import DenotTransformers._ +import StdNames._ +import Symbols._ +import MegaPhase._ +import Types._ +import dotty.tools.dotc.ast.tpd + +import scala.annotation.tailrec + +/** TODO + */ +class GenericTuples extends MiniPhase with IdentityDenotTransformer { + import tpd._ + + def phaseName: String = "genericTuples" + + override def transformApply(tree: tpd.Apply)(implicit ctx: Context): tpd.Tree = { + if (tree.symbol == defn.DynamicTuple_dynamicCons) transformTupleCons(tree) + else if (tree.symbol == defn.DynamicTuple_dynamicHead) transformTupleHead(tree) + else if (tree.symbol == defn.DynamicTuple_dynamicTail) transformTupleTail(tree) + else if (tree.symbol == defn.DynamicTuple_dynamicSize) transformTupleSize(tree) + else if (tree.symbol == defn.DynamicTuple_dynamicConcat) transformTupleConcat(tree) + else if (tree.symbol == defn.DynamicTuple_dynamicApply) transformTupleApply(tree) + else if (tree.symbol == defn.DynamicTuple_dynamicToArray) transformTupleToArray(tree) + else super.transformApply(tree) + } + + private def transformTupleCons(tree: tpd.Apply)(implicit ctx: Context): Tree = { + val TypeApply(_, headType :: tailType :: Nil) = tree.fun + val tail :: head :: Nil = tree.args + tupleTypes(tree.tpe) match { + case Some(tpes) => + val size = tpes.size + if (size <= 5) { + // val t = tail + // TupleN+1(head, t._1, ..., t._n) + evalOnce(Typed(tail, TypeTree(defn.tupleType(tpes.tail)))) { tup => + val elements = head :: (0 until size - 1).map(i => tup.select(nme.selectorName(i))).toList + knownTupleFromElements(tpes, elements) + } + } else { + // val it = Iterator.single(head) ++ tail.asInstanceOf[Product].productIterator + // TupleN(it.next(), ..., it.next()) + val fullIterator = ref(defn.DynamicTuple_consIterator).appliedToArgs(head :: tail :: Nil) + evalOnce(fullIterator) { it => + knownTupleFromIterator(tpes.length, it).asInstance(tree.tpe) + } + } + case _ => + // DynamicTuple.dynamicCons:(tail, head) + tree + } + } + + private def transformTupleTail(tree: tpd.Apply)(implicit ctx: Context): Tree = { + val Apply(TypeApply(_, tpt :: Nil), tup :: Nil) = tree + tupleTypes(tpt.tpe) match { // TODO tupleBoundedTypes + case Some(tpes) => + val size = tpes.size + assert(size > 0) + if (size == 1) { + // () + Literal(Constant(())) + } + else if (size <= 5) { + // val t = tup.asInstanceOf[TupleN[...]] + // TupleN-1(t._2, ..., t._n) + evalOnce(Typed(tup, TypeTree(defn.tupleType(tpes)))) { tup => + val elements = (1 until size).map(i => tup.select(nme.selectorName(i))).toList + knownTupleFromElements(tpes.tail, elements) + } + } else { + // val it = this.asInstanceOf[Product].productIterator + // it.next() + // TupleN(it.next(), ..., it.next()) + evalOnce(tup.asInstance(defn.ProductType).select(nme.productIterator)) { it => + Block( + it.select(nme.next).ensureApplied :: Nil, + knownTupleFromIterator(size - 1, it).asInstance(tree.tpe) + ) + } + } + case None => + // DynamicTuple.dynamicTail(tup) + tree + } + } + + private def transformTupleHead(tree: tpd.Apply)(implicit ctx: Context): Tree = { + val Apply(TypeApply(_, tpt :: Nil), tup :: Nil) = tree + tupleTypes(tpt.tpe) match { // TODO tupleBoundedTypes + case Some(tpes) => + if (tpes.size <= Definitions.MaxTupleArity) { + // tup._1 + Typed(tup, TypeTree(defn.tupleType(tpes))).select(nme.selectorName(0)) + } else { + // tup.asInstanceOf[TupleXXL].productElement(0) + tup.asInstance(defn.TupleXXLType).select(nme.productElement).appliedTo(Literal(Constant(0))) + } + case None => + // DynamicTuple.dynamicHead(tup) + tree + } + } + + private def transformTupleSize(tree: tpd.Apply)(implicit ctx: Context): Tree = { + tree.tpe.tryNormalize match { + case tp: ConstantType => Literal(tp.value) + case _ => tree + } + } + + private def transformTupleConcat(tree: tpd.Apply)(implicit ctx: Context): Tree = { + val Apply(TypeApply(_, selfTp :: thatTp :: Nil), self :: that :: Nil) = tree + + (tupleTypes(selfTp.tpe), tupleTypes(that.tpe.widenTermRefExpr)) match { + case (Some(tpes1), Some(tpes2)) => + val n = tpes1.size + val m = tpes2.size + if (n == 0) that + else if (m == 0) self + else if (n + m < 5) { + // val t = self + // val u = that + // TupleN+M(t._1,..., t._N, u._1, ..., u._M) + evalOnce(Typed(self, TypeTree(defn.tupleType(tpes1)))) { self => + evalOnce(Typed(that, TypeTree(defn.tupleType(tpes2)))) { that => + val types = tpes1 ::: tpes2 + val elements = { + (0 until n).map(i => self.select(nme.selectorName(i))) ++ + (0 until m).map(i => that.select(nme.selectorName(i))) + }.toList + knownTupleFromElements(types, elements) + } + } + } else { + // val it = self.asInstanceOf[Product].productIterator ++ that.asInstanceOf[Product].productIterator + // TupleN(it.next(), ..., it.next()) + val fullIterator = ref(defn.DynamicTuple_concatIterator).appliedToArgs(tree.args) + evalOnce(fullIterator) { it => + knownTupleFromIterator(n + m, it).asInstance(tree.tpe) + } + } + case _ => + // DynamicTuple.dynamicCons[This, that.type](self, that) + tree + } + } + + private def transformTupleApply(tree: tpd.Apply)(implicit ctx: Context): Tree = { + val Apply(TypeApply(_, tpt :: nTpt :: Nil), tup :: nTree :: Nil) = tree + (tupleTypes(tpt.tpe), nTpt.tpe) match { + case (Some(tpes), nTpe: ConstantType) => + val size = tpes.size + val n = nTpe.value.intValue + if (n < 0 || n >= size) { + ctx.error("index out of bounds: " + n, nTree.underlyingArgument.sourcePos) + tree + } else if (size <= Definitions.MaxTupleArity) { + // tup._n + Typed(tup, TypeTree(defn.tupleType(tpes))).select(nme.selectorName(n)) + } else { + // tup.asInstanceOf[TupleXXL].productElement(n) + tup.asInstance(defn.TupleXXLType).select(nme.productElement).appliedTo(Literal(nTpe.value)) + } + case (None, nTpe: ConstantType) if nTpe.value.intValue < 0 => + ctx.error("index out of bounds: " + nTpe.value.intValue, nTree.sourcePos) + tree + case _ => + // DynamicTuple.dynamicApply(tup, n) + tree + } + } + + private def transformTupleToArray(tree: tpd.Apply)(implicit ctx: Context): Tree = { + val Apply(_, tup :: Nil) = tree + tupleTypes(tup.tpe.widen) match { // TODO tupleBoundedTypes + case Some(tpes) => + val size = tpes.size + if (size == 0) { + // Array.emptyObjectArray + ref(defn.ArrayModule.companionModule).select("emptyObjectArray".toTermName).ensureApplied + } else if (size <= Definitions.MaxTupleArity) { + // DynamicTuple.productToArray(tup.asInstanceOf[Product]) + ref(defn.DynamicTuple_productToArray).appliedTo(tup.asInstance(defn.ProductType)) + } else { + // tup.asInstanceOf[TupleXXL].elems + tup.asInstance(defn.TupleXXLType).select(nme.elems) + } + case None => + // DynamicTuple.dynamicToArray(tup) + tree + } + } + + /** Create a TupleN (1 <= N < 23) from the elements */ + private def knownTupleFromElements(tpes: List[Type], elements: List[Tree])(implicit ctx: Context) = { + val size = elements.size + assert(0 < size && size <= Definitions.MaxTupleArity) + val tupleModule = defn.TupleType(size).classSymbol.companionModule + ref(tupleModule).select(nme.apply).appliedToTypes(tpes).appliedToArgs(elements) + } + + private def knownTupleFromIterator(size: Int, it: Tree)(implicit ctx: Context): Tree = { + if (size == 0) { + // Unit for empty tuple + Literal(Constant(())) // TODO should this code be here? Or assert(size > specializedSize) + } + else if (size <= Definitions.MaxTupleArity) { + // TupleN(it.next(), ..., it.next()) + + // TODO outline this code for the 22 alternatives (or less, may not need the smallest ones)? + // This would yield smaller bytecode at the cost of an extra (easily JIT inlinable) call. + // def dynamicTupleN(it: Iterator[Any]): TupleN[Any, ..., Any] = Tuple(it.next(), ..., it.next()) + val tpes = List.fill(size)(defn.AnyType) + val elements = (0 until size).map(_ => it.select(nme.next)).toList + knownTupleFromElements(tpes, elements) + } else { + // TupleXXL.fromIterator(it) + ref(defn.TupleXXL_fromIterator).appliedTo(it) + } + } + + private def tupleTypes(tp: Type)(implicit ctx: Context): Option[List[Type]] = { + @tailrec def rec(tp: Type, acc: List[Type]): Option[List[Type]] = tp match { + case tp: AppliedType if defn.PairClass == tp.classSymbol => rec(tp.args(1), tp.args(0) :: acc) + case tp: AppliedType if defn.isTupleClass(tp.tycon.classSymbol) => Some(acc.reverse ::: tp.args) + case tp if tp.classSymbol == defn.UnitClass => Some(acc.reverse) + case _ => None + } + rec(tp.stripTypeVar, Nil) + } +} diff --git a/compiler/test/dotc/run-test-pickling.blacklist b/compiler/test/dotc/run-test-pickling.blacklist index 3c59bfe5ad4e..f0f4453a2414 100644 --- a/compiler/test/dotc/run-test-pickling.blacklist +++ b/compiler/test/dotc/run-test-pickling.blacklist @@ -4,6 +4,8 @@ t3452e t3452g t7374 tuples1.scala +tuples1a.scala +tuples1b.scala typeclass-derivation1.scala typeclass-derivation2.scala typeclass-derivation2a.scala @@ -14,3 +16,4 @@ derive-generic.scala mixin-forwarder-overload t8905 t10889 +i5257.scala diff --git a/library/src-3.x/scala/Tuple.scala b/library/src-3.x/scala/Tuple.scala index a330a1bd78aa..1b46429f532f 100644 --- a/library/src-3.x/scala/Tuple.scala +++ b/library/src-3.x/scala/Tuple.scala @@ -9,100 +9,16 @@ sealed trait Tuple extends Any { import Tuple._ inline def toArray: Array[Object] = - inline constValueOpt[BoundedSize[this.type]] match { - case Some(0) => - DynamicTuple.empty$Array - case Some(1) => - val t = asInstanceOf[Tuple1[Object]] - Array(t._1) - case Some(2) => - val t = asInstanceOf[Tuple2[Object, Object]] - Array(t._1, t._2) - case Some(3) => - val t = asInstanceOf[Tuple3[Object, Object, Object]] - Array(t._1, t._2, t._3) - case Some(4) => - val t = asInstanceOf[Tuple4[Object, Object, Object, Object]] - Array(t._1, t._2, t._3, t._4) - case Some(n) if n <= DynamicTuple.MaxSpecialized => - DynamicTuple.to$Array(this, n) - case Some(n) => - asInstanceOf[TupleXXL].elems - case None => - DynamicTuple.dynamicToArray(this) - } + DynamicTuple.dynamicToArray(this) - inline def *: [H, This >: this.type <: Tuple] (x: H): H *: This = { - type Result = H *: This - inline constValueOpt[BoundedSize[this.type]] match { - case Some(0) => - Tuple1(x).asInstanceOf[Result] - case Some(1) => - Tuple2(x, asInstanceOf[Tuple1[_]]._1).asInstanceOf[Result] - case Some(2) => - val t = asInstanceOf[Tuple2[_, _]] - Tuple3(x, t._1, t._2).asInstanceOf[Result] - case Some(3) => - val t = asInstanceOf[Tuple3[_, _, _]] - Tuple4(x, t._1, t._2, t._3).asInstanceOf[Result] - case Some(4) => - val t = asInstanceOf[Tuple4[_, _, _, _]] - Tuple5(x, t._1, t._2, t._3, t._4).asInstanceOf[Result] - case Some(n) => - knownTupleFromItrator[H *: this.type](n + 1, Iterator.single(x) ++ this.asInstanceOf[Product].productIterator) - case _ => - DynamicTuple.dynamic_*:[This, H](this, x) - } - } + inline def *: [H, This >: this.type <: Tuple] (x: H): H *: This = + DynamicTuple.dynamicCons[This, H](this, x) - inline def ++ [This >: this.type <: Tuple](that: Tuple): Concat[This, that.type] = { - type Result = Concat[This, that.type] - inline constValueOpt[BoundedSize[this.type]] match { - case Some(0) => - that.asInstanceOf[Result] - case Some(1) => - if (constValue[BoundedSize[that.type]] == 0) this.asInstanceOf[Result] - else (asInstanceOf[Tuple1[_]]._1 *: that).asInstanceOf[Result] - case Some(2) => - val t = asInstanceOf[Tuple2[_, _]] - inline constValue[BoundedSize[that.type]] match { - case 0 => this.asInstanceOf[Result] - case 1 => - val u = that.asInstanceOf[Tuple1[_]] - Tuple3(t._1, t._2, u._1).asInstanceOf[Result] - case 2 => - val u = that.asInstanceOf[Tuple2[_, _]] - Tuple4(t._1, t._2, u._1, u._2).asInstanceOf[Result] - case m => - knownTupleFromItrator[Result](2 + m, this.asInstanceOf[Product].productIterator ++ that.asInstanceOf[Product].productIterator) - } - case Some(3) => - val t = asInstanceOf[Tuple3[_, _, _]] - inline constValue[BoundedSize[that.type]] match { - case 0 => this.asInstanceOf[Result] - case 1 => - val u = that.asInstanceOf[Tuple1[_]] - Tuple4(t._1, t._2, t._3, u._1).asInstanceOf[Result] - case m => - knownTupleFromItrator[Result](3 + m, this.asInstanceOf[Product].productIterator ++ that.asInstanceOf[Product].productIterator) - } - case Some(n) => - inline constValue[BoundedSize[that.type]] match { - case 0 => this.asInstanceOf[Result] - case m => knownTupleFromItrator[Result](n + m, this.asInstanceOf[Product].productIterator ++ that.asInstanceOf[Product].productIterator) - } - case None => - DynamicTuple.dynamic_++[This, that.type](this, that) - } - } + inline def ++ [This >: this.type <: Tuple](that: Tuple): Concat[This, that.type] = + DynamicTuple.dynamicConcat[This, that.type](this, that) - inline def size[This >: this.type <: Tuple]: Size[This] = { - type Result = Size[This] - inline constValueOpt[BoundedSize[this.type]] match { - case Some(n) => n.asInstanceOf[Result] - case _ => DynamicTuple.dynamicSize(this) - } - } + inline def size[This >: this.type <: Tuple]: Size[This] = + DynamicTuple.dynamicSize(this) } @@ -134,45 +50,6 @@ object Tuple { case x *: xs => S[Size[xs]] } - private[scala] type BoundedSizeRecur[X, L <: Int] <: Int = X match { - case Unit => 0 - case x *: xs => - L match { - case 0 => 0 - case S[n] => S[BoundedSizeRecur[xs, n]] - } - } - - private[scala] type BoundedSize[X] = BoundedSizeRecur[X, 24] - - private[scala] inline def knownTupleFromItrator[T <: Tuple](n: Int, it: Iterator[Any]): T = - inline n match { - case 0 => ().asInstanceOf[T] - case 1 => Tuple1(it.next()).asInstanceOf[T] - case 2 => Tuple2(it.next(), it.next()).asInstanceOf[T] - case 3 => Tuple3(it.next(), it.next(), it.next()).asInstanceOf[T] - case 4 => Tuple4(it.next(), it.next(), it.next(), it.next()).asInstanceOf[T] - case 5 => Tuple5(it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T] - case 6 => Tuple6(it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T] - case 7 => Tuple7(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T] - case 8 => Tuple8(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T] - case 9 => Tuple9(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T] - case 10 => Tuple10(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T] - case 11 => Tuple11(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T] - case 12 => Tuple12(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T] - case 13 => Tuple13(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T] - case 14 => Tuple14(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T] - case 15 => Tuple15(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T] - case 16 => Tuple16(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T] - case 17 => Tuple17(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T] - case 18 => Tuple18(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T] - case 19 => Tuple19(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T] - case 20 => Tuple20(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T] - case 21 => Tuple21(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T] - case 22 => Tuple22(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T] - case _ => TupleXXL(it.asInstanceOf[Iterator[Object]].toArray).asInstanceOf[T] - } - def fromArray[T](xs: Array[T]): Tuple = { val xs2 = xs match { case xs: Array[Object] => xs @@ -189,117 +66,18 @@ object Tuple { sealed trait NonEmptyTuple extends Tuple { import Tuple._ - inline def head[This >: this.type <: NonEmptyTuple]: Head[This] = { - type Result = Head[This] - val resVal = inline constValueOpt[BoundedSize[this.type]] match { - case Some(1) => - val t = asInstanceOf[Tuple1[_]] - t._1 - case Some(2) => - val t = asInstanceOf[Tuple2[_, _]] - t._1 - case Some(3) => - val t = asInstanceOf[Tuple3[_, _, _]] - t._1 - case Some(4) => - val t = asInstanceOf[Tuple4[_, _, _, _]] - t._1 - case Some(n) if n > 4 && n <= DynamicTuple.MaxSpecialized => - asInstanceOf[Product].productElement(0) - case Some(n) if n > DynamicTuple.MaxSpecialized => - val t = asInstanceOf[TupleXXL] - t.elems(0) - case None => - DynamicTuple.dynamicHead[this.type](this) - } - resVal.asInstanceOf[Result] - } - - inline def tail[This >: this.type <: NonEmptyTuple]: Tail[This] = { - type Result = Tail[This] - inline constValueOpt[BoundedSize[this.type]] match { - case Some(1) => - ().asInstanceOf[Result] - case Some(2) => - val t = asInstanceOf[Tuple2[_, _]] - Tuple1(t._2).asInstanceOf[Result] - case Some(3) => - val t = asInstanceOf[Tuple3[_, _, _]] - Tuple2(t._2, t._3).asInstanceOf[Result] - case Some(4) => - val t = asInstanceOf[Tuple4[_, _, _, _]] - Tuple3(t._2, t._3, t._4).asInstanceOf[Result] - case Some(5) => - val t = asInstanceOf[Tuple5[_, _, _, _, _]] - Tuple4(t._2, t._3, t._4, t._5).asInstanceOf[Result] - case Some(n) if n > 5 => - val it = this.asInstanceOf[Product].productIterator - it.next() - knownTupleFromItrator[Result](n - 1, it) - case None => - DynamicTuple.dynamicTail[This](this) - } - } - - inline def fallbackApply(n: Int) = - inline constValueOpt[n.type] match { - case Some(n: Int) => error("index out of bounds: ", n) - case None => DynamicTuple.dynamicApply[this.type, n.type](this, n) - } + inline def head[This >: this.type <: NonEmptyTuple]: Head[This] = + DynamicTuple.dynamicHead[This](this) - inline def apply[This >: this.type <: NonEmptyTuple](n: Int): Elem[This, n.type] = { - type Result = Elem[This, n.type] - inline constValueOpt[Size[this.type]] match { - case Some(1) => - val t = asInstanceOf[Tuple1[_]] - inline constValueOpt[n.type] match { - case Some(0) => t._1.asInstanceOf[Result] - case _ => fallbackApply(n).asInstanceOf[Result] - } - case Some(2) => - val t = asInstanceOf[Tuple2[_, _]] - inline constValueOpt[n.type] match { - case Some(0) => t._1.asInstanceOf[Result] - case Some(1) => t._2.asInstanceOf[Result] - case _ => fallbackApply(n).asInstanceOf[Result] - } - case Some(3) => - val t = asInstanceOf[Tuple3[_, _, _]] - inline constValueOpt[n.type] match { - case Some(0) => t._1.asInstanceOf[Result] - case Some(1) => t._2.asInstanceOf[Result] - case Some(2) => t._3.asInstanceOf[Result] - case _ => fallbackApply(n).asInstanceOf[Result] - } - case Some(4) => - val t = asInstanceOf[Tuple4[_, _, _, _]] - inline constValueOpt[n.type] match { - case Some(0) => t._1.asInstanceOf[Result] - case Some(1) => t._2.asInstanceOf[Result] - case Some(2) => t._3.asInstanceOf[Result] - case Some(3) => t._4.asInstanceOf[Result] - case _ => fallbackApply(n).asInstanceOf[Result] - } - case Some(s) if s > 4 && s <= DynamicTuple.MaxSpecialized => - val t = asInstanceOf[Product] - inline constValueOpt[n.type] match { - case Some(n) if n >= 0 && n < s => t.productElement(n).asInstanceOf[Result] - case _ => fallbackApply(n).asInstanceOf[Result] - } - case Some(s) if s > DynamicTuple.MaxSpecialized => - val t = asInstanceOf[TupleXXL] - inline constValueOpt[n.type] match { - case Some(n) if n >= 0 && n < s => t.elems(n).asInstanceOf[Result] - case _ => fallbackApply(n).asInstanceOf[Result] - } - case _ => fallbackApply(n).asInstanceOf[Result] - } - } + inline def tail[This >: this.type <: NonEmptyTuple]: Tail[This] = + DynamicTuple.dynamicTail[This](this) + inline def apply[This >: this.type <: NonEmptyTuple](n: Int): Elem[This, n.type] = + DynamicTuple.dynamicApply[This, n.type](this, n) } @showAsInfix -sealed class *:[+H, +T <: Tuple] extends NonEmptyTuple +sealed abstract class *:[+H, +T <: Tuple] extends NonEmptyTuple object *: { inline def unapply[H, T <: Tuple](x: H *: T) = (x.head, x.tail) diff --git a/library/src-3.x/scala/runtime/DynamicTuple.scala b/library/src-3.x/scala/runtime/DynamicTuple.scala index ab26ca839fcc..c39bd2aef66c 100644 --- a/library/src-3.x/scala/runtime/DynamicTuple.scala +++ b/library/src-3.x/scala/runtime/DynamicTuple.scala @@ -10,8 +10,6 @@ object DynamicTuple { inline val MaxSpecialized = 22 inline private val XXL = MaxSpecialized + 1 - val empty$Array = Array[Object]() - def to$Array(xs: Tuple, n: Int) = { val arr = new Array[Object](n) var i = 0 @@ -178,28 +176,20 @@ object DynamicTuple { def dynamicToArray(self: Tuple): Array[Object] = (self: Any) match { case self: Unit => - scala.runtime.DynamicTuple.empty$Array - case self: Tuple1[_] => - val t = self.asInstanceOf[Tuple1[Object]] - Array(t._1) - case self: Tuple2[_, _] => - val t = self.asInstanceOf[Tuple2[Object, Object]] - Array(t._1, t._2) - case self: Tuple3[_, _, _] => - val t = self.asInstanceOf[Tuple3[Object, Object, Object]] - Array(t._1, t._2, t._3) - case self: Tuple4[_, _, _, _] => - val t = self.asInstanceOf[Tuple4[Object, Object, Object, Object]] - Array(t._1, t._2, t._3, t._4) + Array.emptyObjectArray case self: TupleXXL => self.elems case self: Product => - val arr = new Array[Object](self.productArity) - for (i <- 0 until arr.length) arr(i) = self.productElement(i).asInstanceOf[Object] - arr + productToArray(self) } - def dynamic_*: [This <: Tuple, H] (self: Tuple, x: H): H *: This = { + def productToArray(self: Product): Array[Object] = { + val arr = new Array[Object](self.productArity) + for (i <- 0 until arr.length) arr(i) = self.productElement(i).asInstanceOf[Object] + arr + } + + def dynamicCons[This <: Tuple, H] (self: Tuple, x: H): H *: This = { type Result = H *: This (self: Any) match { case () => @@ -217,7 +207,7 @@ object DynamicTuple { } } - def dynamic_++[This <: Tuple, That <: Tuple](self: This, that: That): Concat[This, That] = { + def dynamicConcat[This <: Tuple, That <: Tuple](self: This, that: That): Concat[This, That] = { type Result = Concat[This, That] (this: Any) match { case self: Unit => return self.asInstanceOf[Result] @@ -236,18 +226,8 @@ object DynamicTuple { case self: Product => self.productArity.asInstanceOf[Size[This]] } - def dynamicHead[This <: NonEmptyTuple] (self: This): Head[This] = { - type Result = Head[This] - val res = (self: Any) match { - case self: Tuple1[_] => self._1 - case self: Tuple2[_, _] => self._1 - case self: Tuple3[_, _, _] => self._1 - case self: Tuple4[_, _, _, _] => self._1 - case self: TupleXXL => self.elems(0) - case self: Product => self.productElement(0) - } - res.asInstanceOf[Result] - } + def dynamicHead[This <: NonEmptyTuple] (self: This): Head[This] = + self.asInstanceOf[Product].productElement(0).asInstanceOf[Head[This]] def dynamicTail[This <: NonEmptyTuple] (self: This): Tail[This] = { type Result = Tail[This] @@ -264,9 +244,16 @@ object DynamicTuple { def dynamicApply[This <: NonEmptyTuple, N <: Int] (self: This, n: Int): Elem[This, N] = { type Result = Elem[This, N] val res = (self: Any) match { + case self: Unit => throw new IndexOutOfBoundsException(n.toString) case self: TupleXXL => self.elems(n) case self: Product => self.productElement(n) } res.asInstanceOf[Result] } + + def consIterator(head: Any, tail: Tuple): Iterator[Any] = + Iterator.single(head) ++ tail.asInstanceOf[Product].productIterator + + def concatIterator(tup1: Tuple, tup2: Tuple): Iterator[Any] = + tup1.asInstanceOf[Product].productIterator ++ tup2.asInstanceOf[Product].productIterator } diff --git a/library/src/scala/TupleXXL.scala b/library/src/scala/TupleXXL.scala index cb79ff68a4a8..88db06ea4d03 100644 --- a/library/src/scala/TupleXXL.scala +++ b/library/src/scala/TupleXXL.scala @@ -21,6 +21,7 @@ final class TupleXXL private (es: Array[Object]) extends Product { def elems: Array[Object] = es } object TupleXXL { + def fromIterator(elems: Iterator[Any]) = new TupleXXL(elems.map(_.asInstanceOf[Object]).toArray) def apply(elems: Array[Object]) = new TupleXXL(elems.clone) def apply(elems: Any*) = new TupleXXL(elems.asInstanceOf[Seq[Object]].toArray) def unapplySeq(x: TupleXXL): Option[Seq[Any]] = Some(x.elems.toSeq) diff --git a/tests/run-deep-subtype/Tuple-head.scala b/tests/run-deep-subtype/Tuple-head.scala index 8c8788278b62..04a87e18ac65 100644 --- a/tests/run-deep-subtype/Tuple-head.scala +++ b/tests/run-deep-subtype/Tuple-head.scala @@ -47,21 +47,20 @@ object Test { assert(1 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: ()).head) assert(1 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: ()).head) assert(1 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: ()).head) - // FIXME performace -// assert(1 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: ()).head) -// assert(1 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: ()).head) -// assert(1 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: ()).head) -// assert(1 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: ()).head) -// assert(1 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: ()).head) -// assert(1 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: ()).head) -// assert(1 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: ()).head) -// assert(1 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: ()).head) -// assert(1 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: ()).head) -// assert(1 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: ()).head) -// assert(1 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: ()).head) -// assert(1 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: 22 *: ()).head) -// assert(1 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: 22 *: 23 *: ()).head) -// assert(1 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: 22 *: 23 *: 24 *: ()).head) -// assert(1 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: 22 *: 23 *: 24 *: 25 *: ()).head) + assert(1 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: ()).head) + assert(1 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: ()).head) + assert(1 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: ()).head) + assert(1 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: ()).head) + assert(1 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: ()).head) + assert(1 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: ()).head) + assert(1 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: ()).head) + assert(1 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: ()).head) + assert(1 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: ()).head) + assert(1 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: ()).head) + assert(1 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: ()).head) + assert(1 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: 22 *: ()).head) + assert(1 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: 22 *: 23 *: ()).head) + assert(1 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: 22 *: 23 *: 24 *: ()).head) + assert(1 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: 22 *: 23 *: 24 *: 25 *: ()).head) } } diff --git a/tests/run-deep-subtype/Tuple-size.scala b/tests/run-deep-subtype/Tuple-size.scala index 78785dc0a70f..fd3483fee9d2 100644 --- a/tests/run-deep-subtype/Tuple-size.scala +++ b/tests/run-deep-subtype/Tuple-size.scala @@ -47,21 +47,20 @@ object Test { assert(8 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: ()).size) assert(9 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: ()).size) assert(10 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: ()).size) - // FIXME performace -// assert(11 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: ()).size) -// assert(12 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: ()).size) -// assert(13 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: ()).size) -// assert(14 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: ()).size) -// assert(15 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: ()).size) -// assert(16 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: ()).size) -// assert(17 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: ()).size) -// assert(18 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: ()).size) -// assert(19 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: ()).size) -// assert(20 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: ()).size) -// assert(21 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: ()).size) -// assert(22 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: 22 *: ()).size) -// assert(23 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: 22 *: 23 *: ()).size) -// assert(24 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: 22 *: 23 *: 24 *: ()).size) -// assert(25 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: 22 *: 23 *: 24 *: 25 *: ()).size) + assert(11 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: ()).size) + assert(12 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: ()).size) + assert(13 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: ()).size) + assert(14 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: ()).size) + assert(15 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: ()).size) + assert(16 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: ()).size) + assert(17 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: ()).size) + assert(18 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: ()).size) + assert(19 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: ()).size) + assert(20 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: ()).size) + assert(21 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: ()).size) + assert(22 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: 22 *: ()).size) + assert(23 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: 22 *: 23 *: ()).size) + assert(24 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: 22 *: 23 *: 24 *: ()).size) + assert(25 == (1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: 22 *: 23 *: 24 *: 25 *: ()).size) } } diff --git a/tests/run-deep-subtype/Tuple-tail.check b/tests/run-deep-subtype/Tuple-tail.check index b22fd91be7de..f73cefb41134 100644 --- a/tests/run-deep-subtype/Tuple-tail.check +++ b/tests/run-deep-subtype/Tuple-tail.check @@ -59,3 +59,18 @@ (2,3,4,5,6,7,8) (2,3,4,5,6,7,8,9) (2,3,4,5,6,7,8,9,10) +(2,3,4,5,6,7,8,9,10,11) +(2,3,4,5,6,7,8,9,10,11,12) +(2,3,4,5,6,7,8,9,10,11,12,13) +(2,3,4,5,6,7,8,9,10,11,12,13,14) +(2,3,4,5,6,7,8,9,10,11,12,13,14,15) +(2,3,4,5,6,7,8,9,10,11,12,13,14,15,16) +(2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17) +(2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18) +(2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19) +(2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20) +(2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21) +(2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22) +(2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23) +(2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24) +(2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25) diff --git a/tests/run-deep-subtype/Tuple-tail.scala b/tests/run-deep-subtype/Tuple-tail.scala index 896f33b29aa2..d32d7d9567b2 100644 --- a/tests/run-deep-subtype/Tuple-tail.scala +++ b/tests/run-deep-subtype/Tuple-tail.scala @@ -47,21 +47,20 @@ object Test { println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: ()).tail) println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: ()).tail) println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: ()).tail) - // FIXME performace -// println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: ()).tail) -// println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: ()).tail) -// println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: ()).tail) -// println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: ()).tail) -// println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: ()).tail) -// println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: ()).tail) -// println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: ()).tail) -// println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: ()).tail) -// println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: ()).tail) -// println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: ()).tail) -// println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: ()).tail) -// println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: 22 *: ()).tail) -// println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: 22 *: 23 *: ()).tail) -// println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: 22 *: 23 *: 24 *: ()).tail) -// println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: 22 *: 23 *: 24 *: 25 *: ()).tail) + println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: ()).tail) + println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: ()).tail) + println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: ()).tail) + println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: ()).tail) + println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: ()).tail) + println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: ()).tail) + println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: ()).tail) + println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: ()).tail) + println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: ()).tail) + println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: ()).tail) + println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: ()).tail) + println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: 22 *: ()).tail) + println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: 22 *: 23 *: ()).tail) + println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: 22 *: 23 *: 24 *: ()).tail) + println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: 22 *: 23 *: 24 *: 25 *: ()).tail) } } diff --git a/tests/run-deep-subtype/Tuple-toArray.check b/tests/run-deep-subtype/Tuple-toArray.check index 247bee409b31..bd80083ded53 100644 --- a/tests/run-deep-subtype/Tuple-toArray.check +++ b/tests/run-deep-subtype/Tuple-toArray.check @@ -60,3 +60,18 @@ [1, 2, 3, 4, 5, 6, 7, 8] [1, 2, 3, 4, 5, 6, 7, 8, 9] [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] +[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] +[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] +[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] +[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] +[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] +[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] +[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17] +[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18] +[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] +[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20] +[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21] +[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22] +[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23] +[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24] +[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25] diff --git a/tests/run-deep-subtype/Tuple-toArray.scala b/tests/run-deep-subtype/Tuple-toArray.scala index 941121bd4ace..4f7db2546020 100644 --- a/tests/run-deep-subtype/Tuple-toArray.scala +++ b/tests/run-deep-subtype/Tuple-toArray.scala @@ -50,21 +50,20 @@ object Test { printArray((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: ()).toArray) printArray((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: ()).toArray) printArray((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: ()).toArray) - // FIXME performace -// printArray((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: ()).toArray) -// printArray((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: ()).toArray) -// printArray((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: ()).toArray) -// printArray((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: ()).toArray) -// printArray((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: ()).toArray) -// printArray((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: ()).toArray) -// printArray((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: ()).toArray) -// printArray((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: ()).toArray) -// printArray((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: ()).toArray) -// printArray((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: ()).toArray) -// printArray((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: ()).toArray) -// printArray((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: 22 *: ()).toArray) -// printArray((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: 22 *: 23 *: ()).toArray) -// printArray((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: 22 *: 23 *: 24 *: ()).toArray) -// printArray((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: 22 *: 23 *: 24 *: 25 *: ()).toArray) + printArray((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: ()).toArray) + printArray((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: ()).toArray) + printArray((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: ()).toArray) + printArray((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: ()).toArray) + printArray((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: ()).toArray) + printArray((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: ()).toArray) + printArray((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: ()).toArray) + printArray((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: ()).toArray) + printArray((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: ()).toArray) + printArray((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: ()).toArray) + printArray((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: ()).toArray) + printArray((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: 22 *: ()).toArray) + printArray((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: 22 *: 23 *: ()).toArray) + printArray((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: 22 *: 23 *: 24 *: ()).toArray) + printArray((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: 22 *: 23 *: 24 *: 25 *: ()).toArray) } } diff --git a/tests/run-with-compiler/staged-tuples/StagedTuple.scala b/tests/run-with-compiler/staged-tuples/StagedTuple.scala index e18487cef27d..f6e23928883e 100644 --- a/tests/run-with-compiler/staged-tuples/StagedTuple.scala +++ b/tests/run-with-compiler/staged-tuples/StagedTuple.scala @@ -16,7 +16,7 @@ object StagedTuple { if (!specialize) '{dynamicToArray($tup)} else size match { case Some(0) => - '{scala.runtime.DynamicTuple.empty$Array} + '{Array.emptyObjectArray} case Some(1) => tup.as[Tuple1[Object]].bind(t => '{Array($t._1)}) case Some(2) => @@ -184,7 +184,7 @@ object StagedTuple { } def consStaged[T <: Tuple & Singleton : Type, H : Type](self: Expr[T], x: Expr[H], tailSize: Option[Int]): Expr[H *: T] = - if (!specialize) '{dynamic_*:[T, H]($self, $x)} + if (!specialize) '{dynamicCons[T, H]($self, $x)} else { val res = tailSize match { case Some(0) => @@ -200,13 +200,13 @@ object StagedTuple { case Some(n) => fromArrayStaged[H *: T]('{cons$Array($x, ${ toArrayStaged(self, tailSize) })}, Some(n + 1)) case _ => - '{dynamic_*:[T, H]($self, $x)} + '{dynamicCons[T, H]($self, $x)} } res.as[H *: T] } def concatStaged[Self <: Tuple & Singleton : Type, That <: Tuple & Singleton : Type](self: Expr[Self], selfSize: Option[Int], that: Expr[That], thatSize: Option[Int]): Expr[Concat[Self, That]] = { - if (!specialize) '{dynamic_++[Self, That]($self, $that)} + if (!specialize) '{dynamicConcat[Self, That]($self, $that)} else { def genericConcat(xs: Expr[Tuple], ys: Expr[Tuple]): Expr[Tuple] = // TODO remove ascriptions when #6126 is fixed @@ -248,7 +248,7 @@ object StagedTuple { if (thatSize.contains(0)) self else genericConcat(self, that) case None => - '{dynamic_++($self, $that)} + '{dynamicConcat($self, $that)} } res.as[Concat[Self, That]] } From 2d9e6b967d72ce5d70c51d5e1f187bb53e04af79 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 23 May 2019 08:09:54 +0200 Subject: [PATCH 04/11] Implement Tuple.head as Tuple.apply(0) --- .../dotty/tools/dotc/core/Definitions.scala | 1 - .../tools/dotc/transform/GenericTuples.scala | 18 ------------------ library/src-3.x/scala/Tuple.scala | 7 ++++--- .../src-3.x/scala/runtime/DynamicTuple.scala | 4 ---- .../staged-tuples/StagedTuple.scala | 4 ++-- 5 files changed, 6 insertions(+), 28 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index baf4df0b9b2e..369501daf27e 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -818,7 +818,6 @@ class Definitions { lazy val DynamicTuple_concatIterator: Symbol = DynamicTupleModule.requiredMethod("concatIterator") lazy val DynamicTuple_dynamicApply: Symbol = DynamicTupleModule.requiredMethod("dynamicApply") lazy val DynamicTuple_dynamicCons: Symbol = DynamicTupleModule.requiredMethod("dynamicCons") - lazy val DynamicTuple_dynamicHead: Symbol = DynamicTupleModule.requiredMethod("dynamicHead") lazy val DynamicTuple_dynamicSize: Symbol = DynamicTupleModule.requiredMethod("dynamicSize") lazy val DynamicTuple_dynamicTail: Symbol = DynamicTupleModule.requiredMethod("dynamicTail") lazy val DynamicTuple_dynamicConcat: Symbol = DynamicTupleModule.requiredMethod("dynamicConcat") diff --git a/compiler/src/dotty/tools/dotc/transform/GenericTuples.scala b/compiler/src/dotty/tools/dotc/transform/GenericTuples.scala index 305a71ba40e6..68a9e97d15c1 100644 --- a/compiler/src/dotty/tools/dotc/transform/GenericTuples.scala +++ b/compiler/src/dotty/tools/dotc/transform/GenericTuples.scala @@ -26,7 +26,6 @@ class GenericTuples extends MiniPhase with IdentityDenotTransformer { override def transformApply(tree: tpd.Apply)(implicit ctx: Context): tpd.Tree = { if (tree.symbol == defn.DynamicTuple_dynamicCons) transformTupleCons(tree) - else if (tree.symbol == defn.DynamicTuple_dynamicHead) transformTupleHead(tree) else if (tree.symbol == defn.DynamicTuple_dynamicTail) transformTupleTail(tree) else if (tree.symbol == defn.DynamicTuple_dynamicSize) transformTupleSize(tree) else if (tree.symbol == defn.DynamicTuple_dynamicConcat) transformTupleConcat(tree) @@ -96,23 +95,6 @@ class GenericTuples extends MiniPhase with IdentityDenotTransformer { } } - private def transformTupleHead(tree: tpd.Apply)(implicit ctx: Context): Tree = { - val Apply(TypeApply(_, tpt :: Nil), tup :: Nil) = tree - tupleTypes(tpt.tpe) match { // TODO tupleBoundedTypes - case Some(tpes) => - if (tpes.size <= Definitions.MaxTupleArity) { - // tup._1 - Typed(tup, TypeTree(defn.tupleType(tpes))).select(nme.selectorName(0)) - } else { - // tup.asInstanceOf[TupleXXL].productElement(0) - tup.asInstance(defn.TupleXXLType).select(nme.productElement).appliedTo(Literal(Constant(0))) - } - case None => - // DynamicTuple.dynamicHead(tup) - tree - } - } - private def transformTupleSize(tree: tpd.Apply)(implicit ctx: Context): Tree = { tree.tpe.tryNormalize match { case tp: ConstantType => Literal(tp.value) diff --git a/library/src-3.x/scala/Tuple.scala b/library/src-3.x/scala/Tuple.scala index 1b46429f532f..a19e6c56a4c0 100644 --- a/library/src-3.x/scala/Tuple.scala +++ b/library/src-3.x/scala/Tuple.scala @@ -66,14 +66,15 @@ object Tuple { sealed trait NonEmptyTuple extends Tuple { import Tuple._ + inline def apply[This >: this.type <: NonEmptyTuple](n: Int): Elem[This, n.type] = + DynamicTuple.dynamicApply[This, n.type](this, n) + inline def head[This >: this.type <: NonEmptyTuple]: Head[This] = - DynamicTuple.dynamicHead[This](this) + DynamicTuple.dynamicApply[This, 0](this, 0) inline def tail[This >: this.type <: NonEmptyTuple]: Tail[This] = DynamicTuple.dynamicTail[This](this) - inline def apply[This >: this.type <: NonEmptyTuple](n: Int): Elem[This, n.type] = - DynamicTuple.dynamicApply[This, n.type](this, n) } @showAsInfix diff --git a/library/src-3.x/scala/runtime/DynamicTuple.scala b/library/src-3.x/scala/runtime/DynamicTuple.scala index c39bd2aef66c..3520c9be459a 100644 --- a/library/src-3.x/scala/runtime/DynamicTuple.scala +++ b/library/src-3.x/scala/runtime/DynamicTuple.scala @@ -226,9 +226,6 @@ object DynamicTuple { case self: Product => self.productArity.asInstanceOf[Size[This]] } - def dynamicHead[This <: NonEmptyTuple] (self: This): Head[This] = - self.asInstanceOf[Product].productElement(0).asInstanceOf[Head[This]] - def dynamicTail[This <: NonEmptyTuple] (self: This): Tail[This] = { type Result = Tail[This] val res = (self: Any) match { @@ -245,7 +242,6 @@ object DynamicTuple { type Result = Elem[This, N] val res = (self: Any) match { case self: Unit => throw new IndexOutOfBoundsException(n.toString) - case self: TupleXXL => self.elems(n) case self: Product => self.productElement(n) } res.asInstanceOf[Result] diff --git a/tests/run-with-compiler/staged-tuples/StagedTuple.scala b/tests/run-with-compiler/staged-tuples/StagedTuple.scala index f6e23928883e..f8c75ccefa63 100644 --- a/tests/run-with-compiler/staged-tuples/StagedTuple.scala +++ b/tests/run-with-compiler/staged-tuples/StagedTuple.scala @@ -79,7 +79,7 @@ object StagedTuple { } def headStaged[Tup <: NonEmptyTuple : Type](tup: Expr[Tup], size: Option[Int]): Expr[Head[Tup]] = { - if (!specialize) '{dynamicHead($tup)} + if (!specialize) '{dynamicApply($tup, 0)} else { val resVal = size match { case Some(1) => @@ -95,7 +95,7 @@ object StagedTuple { case Some(n) if n > MaxSpecialized => '{${tup.as[TupleXXL] }.elems(0)} case None => - '{dynamicHead($tup)} + '{dynamicApply($tup, 0)} } resVal.as[Head[Tup]] } From a098e32932d2e5871872c2ceda7be8aa822f2eb5 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 23 May 2019 09:17:27 +0200 Subject: [PATCH 05/11] Add more documentation --- compiler/src/dotty/tools/dotc/Compiler.scala | 2 +- ...Tuples.scala => TuplesOptimizations.scala} | 21 +++++++++++++------ 2 files changed, 16 insertions(+), 7 deletions(-) rename compiler/src/dotty/tools/dotc/transform/{GenericTuples.scala => TuplesOptimizations.scala} (92%) diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index a6607a7fc9db..85bcf3bcd605 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -87,7 +87,7 @@ class Compiler { new AugmentScala2Traits, // Augments Scala2 traits with additional members needed for mixin composition. new ResolveSuper, // Implement super accessors new FunctionXXLForwarders, // Add forwarders for FunctionXXL apply method - new GenericTuples, // TODO + new TuplesOptimizations, // Optimize generic operations on tuples new ArrayConstructors) :: // Intercept creation of (non-generic) arrays and intrinsify. List(new Erasure) :: // Rewrite types to JVM model, erasing all type parameters, abstract types and refinements. List(new ElimErasedValueType, // Expand erased value types to their underlying implmementation types diff --git a/compiler/src/dotty/tools/dotc/transform/GenericTuples.scala b/compiler/src/dotty/tools/dotc/transform/TuplesOptimizations.scala similarity index 92% rename from compiler/src/dotty/tools/dotc/transform/GenericTuples.scala rename to compiler/src/dotty/tools/dotc/transform/TuplesOptimizations.scala index 68a9e97d15c1..6c05d059ca8a 100644 --- a/compiler/src/dotty/tools/dotc/transform/GenericTuples.scala +++ b/compiler/src/dotty/tools/dotc/transform/TuplesOptimizations.scala @@ -17,9 +17,8 @@ import dotty.tools.dotc.ast.tpd import scala.annotation.tailrec -/** TODO - */ -class GenericTuples extends MiniPhase with IdentityDenotTransformer { +/** Optimize generic operations on tuples */ +class TuplesOptimizations extends MiniPhase with IdentityDenotTransformer { import tpd._ def phaseName: String = "genericTuples" @@ -39,6 +38,7 @@ class GenericTuples extends MiniPhase with IdentityDenotTransformer { val tail :: head :: Nil = tree.args tupleTypes(tree.tpe) match { case Some(tpes) => + // Generate a the tuple directly with TupleN+1.apply val size = tpes.size if (size <= 5) { // val t = tail @@ -49,13 +49,14 @@ class GenericTuples extends MiniPhase with IdentityDenotTransformer { } } else { // val it = Iterator.single(head) ++ tail.asInstanceOf[Product].productIterator - // TupleN(it.next(), ..., it.next()) + // TupleN+1(it.next(), ..., it.next()) val fullIterator = ref(defn.DynamicTuple_consIterator).appliedToArgs(head :: tail :: Nil) evalOnce(fullIterator) { it => knownTupleFromIterator(tpes.length, it).asInstance(tree.tpe) } } case _ => + // No optimization, keep: // DynamicTuple.dynamicCons:(tail, head) tree } @@ -65,6 +66,7 @@ class GenericTuples extends MiniPhase with IdentityDenotTransformer { val Apply(TypeApply(_, tpt :: Nil), tup :: Nil) = tree tupleTypes(tpt.tpe) match { // TODO tupleBoundedTypes case Some(tpes) => + // Generate a the tuple directly with TupleN-1.apply val size = tpes.size assert(size > 0) if (size == 1) { @@ -81,7 +83,7 @@ class GenericTuples extends MiniPhase with IdentityDenotTransformer { } else { // val it = this.asInstanceOf[Product].productIterator // it.next() - // TupleN(it.next(), ..., it.next()) + // TupleN-1(it.next(), ..., it.next()) evalOnce(tup.asInstance(defn.ProductType).select(nme.productIterator)) { it => Block( it.select(nme.next).ensureApplied :: Nil, @@ -90,6 +92,7 @@ class GenericTuples extends MiniPhase with IdentityDenotTransformer { } } case None => + // No optimization, keep: // DynamicTuple.dynamicTail(tup) tree } @@ -107,6 +110,7 @@ class GenericTuples extends MiniPhase with IdentityDenotTransformer { (tupleTypes(selfTp.tpe), tupleTypes(that.tpe.widenTermRefExpr)) match { case (Some(tpes1), Some(tpes2)) => + // Generate a the tuple directly with TupleN+M.apply val n = tpes1.size val m = tpes2.size if (n == 0) that @@ -127,13 +131,14 @@ class GenericTuples extends MiniPhase with IdentityDenotTransformer { } } else { // val it = self.asInstanceOf[Product].productIterator ++ that.asInstanceOf[Product].productIterator - // TupleN(it.next(), ..., it.next()) + // TupleN+M(it.next(), ..., it.next()) val fullIterator = ref(defn.DynamicTuple_concatIterator).appliedToArgs(tree.args) evalOnce(fullIterator) { it => knownTupleFromIterator(n + m, it).asInstance(tree.tpe) } } case _ => + // No optimization, keep: // DynamicTuple.dynamicCons[This, that.type](self, that) tree } @@ -143,6 +148,7 @@ class GenericTuples extends MiniPhase with IdentityDenotTransformer { val Apply(TypeApply(_, tpt :: nTpt :: Nil), tup :: nTree :: Nil) = tree (tupleTypes(tpt.tpe), nTpt.tpe) match { case (Some(tpes), nTpe: ConstantType) => + // Get the element directly with TupleM._n+1 or TupleXXL.productElement(n) val size = tpes.size val n = nTpe.value.intValue if (n < 0 || n >= size) { @@ -159,6 +165,7 @@ class GenericTuples extends MiniPhase with IdentityDenotTransformer { ctx.error("index out of bounds: " + nTpe.value.intValue, nTree.sourcePos) tree case _ => + // No optimization, keep: // DynamicTuple.dynamicApply(tup, n) tree } @@ -180,6 +187,7 @@ class GenericTuples extends MiniPhase with IdentityDenotTransformer { tup.asInstance(defn.TupleXXLType).select(nme.elems) } case None => + // No optimization, keep: // DynamicTuple.dynamicToArray(tup) tree } @@ -208,6 +216,7 @@ class GenericTuples extends MiniPhase with IdentityDenotTransformer { val elements = (0 until size).map(_ => it.select(nme.next)).toList knownTupleFromElements(tpes, elements) } else { + // No optimization, keep: // TupleXXL.fromIterator(it) ref(defn.TupleXXL_fromIterator).appliedTo(it) } From 3ff85dbc3a213120a714488a5571cebe3267c2d3 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 23 May 2019 09:30:10 +0200 Subject: [PATCH 06/11] Bound tuple sizes when possible --- .../dotc/transform/TuplesOptimizations.scala | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/TuplesOptimizations.scala b/compiler/src/dotty/tools/dotc/transform/TuplesOptimizations.scala index 6c05d059ca8a..cacbd9b35d9d 100644 --- a/compiler/src/dotty/tools/dotc/transform/TuplesOptimizations.scala +++ b/compiler/src/dotty/tools/dotc/transform/TuplesOptimizations.scala @@ -64,7 +64,7 @@ class TuplesOptimizations extends MiniPhase with IdentityDenotTransformer { private def transformTupleTail(tree: tpd.Apply)(implicit ctx: Context): Tree = { val Apply(TypeApply(_, tpt :: Nil), tup :: Nil) = tree - tupleTypes(tpt.tpe) match { // TODO tupleBoundedTypes + tupleTypes(tpt.tpe, MaxTupleArity + 1) match { case Some(tpes) => // Generate a the tuple directly with TupleN-1.apply val size = tpes.size @@ -154,7 +154,7 @@ class TuplesOptimizations extends MiniPhase with IdentityDenotTransformer { if (n < 0 || n >= size) { ctx.error("index out of bounds: " + n, nTree.underlyingArgument.sourcePos) tree - } else if (size <= Definitions.MaxTupleArity) { + } else if (size <= MaxTupleArity) { // tup._n Typed(tup, TypeTree(defn.tupleType(tpes))).select(nme.selectorName(n)) } else { @@ -173,13 +173,13 @@ class TuplesOptimizations extends MiniPhase with IdentityDenotTransformer { private def transformTupleToArray(tree: tpd.Apply)(implicit ctx: Context): Tree = { val Apply(_, tup :: Nil) = tree - tupleTypes(tup.tpe.widen) match { // TODO tupleBoundedTypes + tupleTypes(tup.tpe.widen, MaxTupleArity) match { case Some(tpes) => val size = tpes.size if (size == 0) { // Array.emptyObjectArray ref(defn.ArrayModule.companionModule).select("emptyObjectArray".toTermName).ensureApplied - } else if (size <= Definitions.MaxTupleArity) { + } else if (size <= MaxTupleArity) { // DynamicTuple.productToArray(tup.asInstanceOf[Product]) ref(defn.DynamicTuple_productToArray).appliedTo(tup.asInstance(defn.ProductType)) } else { @@ -196,7 +196,7 @@ class TuplesOptimizations extends MiniPhase with IdentityDenotTransformer { /** Create a TupleN (1 <= N < 23) from the elements */ private def knownTupleFromElements(tpes: List[Type], elements: List[Tree])(implicit ctx: Context) = { val size = elements.size - assert(0 < size && size <= Definitions.MaxTupleArity) + assert(0 < size && size <= MaxTupleArity) val tupleModule = defn.TupleType(size).classSymbol.companionModule ref(tupleModule).select(nme.apply).appliedToTypes(tpes).appliedToArgs(elements) } @@ -206,7 +206,7 @@ class TuplesOptimizations extends MiniPhase with IdentityDenotTransformer { // Unit for empty tuple Literal(Constant(())) // TODO should this code be here? Or assert(size > specializedSize) } - else if (size <= Definitions.MaxTupleArity) { + else if (size <= MaxTupleArity) { // TupleN(it.next(), ..., it.next()) // TODO outline this code for the 22 alternatives (or less, may not need the smallest ones)? @@ -222,13 +222,14 @@ class TuplesOptimizations extends MiniPhase with IdentityDenotTransformer { } } - private def tupleTypes(tp: Type)(implicit ctx: Context): Option[List[Type]] = { - @tailrec def rec(tp: Type, acc: List[Type]): Option[List[Type]] = tp match { - case tp: AppliedType if defn.PairClass == tp.classSymbol => rec(tp.args(1), tp.args(0) :: acc) + private def tupleTypes(tp: Type, bound: Int = Int.MaxValue)(implicit ctx: Context): Option[List[Type]] = { + @tailrec def rec(tp: Type, acc: List[Type], bound: Int): Option[List[Type]] = tp match { + case _ if bound < 0 => Some(acc.reverse) + case tp: AppliedType if defn.PairClass == tp.classSymbol => rec(tp.args(1), tp.args.head :: acc, bound - 1) case tp: AppliedType if defn.isTupleClass(tp.tycon.classSymbol) => Some(acc.reverse ::: tp.args) case tp if tp.classSymbol == defn.UnitClass => Some(acc.reverse) case _ => None } - rec(tp.stripTypeVar, Nil) + rec(tp.stripTypeVar, Nil, bound) } } From d2bc81fe3f3c815acdd7e19994b38d08ec53ab47 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 23 May 2019 17:06:43 +0200 Subject: [PATCH 07/11] Optimize some operations on TupleXXL --- compiler/src/dotty/tools/dotc/Compiler.scala | 2 +- ...esOptimizations.scala => TupleOptimizations.scala} | 11 +++++++---- library/src-3.x/scala/runtime/DynamicTuple.scala | 10 +++------- library/src/scala/TupleXXL.scala | 7 +++++++ 4 files changed, 18 insertions(+), 12 deletions(-) rename compiler/src/dotty/tools/dotc/transform/{TuplesOptimizations.scala => TupleOptimizations.scala} (96%) diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index 85bcf3bcd605..b087f313c33e 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -87,7 +87,7 @@ class Compiler { new AugmentScala2Traits, // Augments Scala2 traits with additional members needed for mixin composition. new ResolveSuper, // Implement super accessors new FunctionXXLForwarders, // Add forwarders for FunctionXXL apply method - new TuplesOptimizations, // Optimize generic operations on tuples + new TupleOptimizations, // Optimize generic operations on tuples new ArrayConstructors) :: // Intercept creation of (non-generic) arrays and intrinsify. List(new Erasure) :: // Rewrite types to JVM model, erasing all type parameters, abstract types and refinements. List(new ElimErasedValueType, // Expand erased value types to their underlying implmementation types diff --git a/compiler/src/dotty/tools/dotc/transform/TuplesOptimizations.scala b/compiler/src/dotty/tools/dotc/transform/TupleOptimizations.scala similarity index 96% rename from compiler/src/dotty/tools/dotc/transform/TuplesOptimizations.scala rename to compiler/src/dotty/tools/dotc/transform/TupleOptimizations.scala index cacbd9b35d9d..82fa30e1f809 100644 --- a/compiler/src/dotty/tools/dotc/transform/TuplesOptimizations.scala +++ b/compiler/src/dotty/tools/dotc/transform/TupleOptimizations.scala @@ -18,7 +18,7 @@ import dotty.tools.dotc.ast.tpd import scala.annotation.tailrec /** Optimize generic operations on tuples */ -class TuplesOptimizations extends MiniPhase with IdentityDenotTransformer { +class TupleOptimizations extends MiniPhase with IdentityDenotTransformer { import tpd._ def phaseName: String = "genericTuples" @@ -80,7 +80,7 @@ class TuplesOptimizations extends MiniPhase with IdentityDenotTransformer { val elements = (1 until size).map(i => tup.select(nme.selectorName(i))).toList knownTupleFromElements(tpes.tail, elements) } - } else { + } else if (size <= MaxTupleArity + 1) { // val it = this.asInstanceOf[Product].productIterator // it.next() // TupleN-1(it.next(), ..., it.next()) @@ -90,6 +90,9 @@ class TuplesOptimizations extends MiniPhase with IdentityDenotTransformer { knownTupleFromIterator(size - 1, it).asInstance(tree.tpe) ) } + } else { + // tup.asInstanceOf[TupleXXL].tailXXL + tup.asInstance(defn.TupleXXLType).select("tailXXL".toTermName) } case None => // No optimization, keep: @@ -183,8 +186,8 @@ class TuplesOptimizations extends MiniPhase with IdentityDenotTransformer { // DynamicTuple.productToArray(tup.asInstanceOf[Product]) ref(defn.DynamicTuple_productToArray).appliedTo(tup.asInstance(defn.ProductType)) } else { - // tup.asInstanceOf[TupleXXL].elems - tup.asInstance(defn.TupleXXLType).select(nme.elems) + // tup.asInstanceOf[TupleXXL].elems.clone() + tup.asInstance(defn.TupleXXLType).select(nme.toArray) } case None => // No optimization, keep: diff --git a/library/src-3.x/scala/runtime/DynamicTuple.scala b/library/src-3.x/scala/runtime/DynamicTuple.scala index 3520c9be459a..1239c4f2a22d 100644 --- a/library/src-3.x/scala/runtime/DynamicTuple.scala +++ b/library/src-3.x/scala/runtime/DynamicTuple.scala @@ -175,12 +175,9 @@ object DynamicTuple { def dynamicToArray(self: Tuple): Array[Object] = (self: Any) match { - case self: Unit => - Array.emptyObjectArray - case self: TupleXXL => - self.elems - case self: Product => - productToArray(self) + case self: Unit => Array.emptyObjectArray + case self: TupleXXL => self.toArray + case self: Product => productToArray(self) } def productToArray(self: Product): Array[Object] = { @@ -222,7 +219,6 @@ object DynamicTuple { def dynamicSize[This <: Tuple](self: This): Size[This] = (self: Any) match { case self: Unit => 0.asInstanceOf[Size[This]] - case self: TupleXXL => self.elems.length.asInstanceOf[Size[This]] case self: Product => self.productArity.asInstanceOf[Size[This]] } diff --git a/library/src/scala/TupleXXL.scala b/library/src/scala/TupleXXL.scala index 88db06ea4d03..4f609683933d 100644 --- a/library/src/scala/TupleXXL.scala +++ b/library/src/scala/TupleXXL.scala @@ -19,6 +19,13 @@ final class TupleXXL private (es: Array[Object]) extends Product { case _ => false } def elems: Array[Object] = es + + def tailXXL: TupleXXL = { + assert(es.length > 23) + new TupleXXL(es.tail) + } + + def toArray: Array[Object] = es.clone } object TupleXXL { def fromIterator(elems: Iterator[Any]) = new TupleXXL(elems.map(_.asInstanceOf[Object]).toArray) From b3b3fb0d7d8f76fd58e87bdc99de114c729c4fab Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 28 May 2019 11:31:09 +0200 Subject: [PATCH 08/11] Add documentation --- library/src-3.x/scala/Tuple.scala | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/library/src-3.x/scala/Tuple.scala b/library/src-3.x/scala/Tuple.scala index a19e6c56a4c0..785b7d2cd0dd 100644 --- a/library/src-3.x/scala/Tuple.scala +++ b/library/src-3.x/scala/Tuple.scala @@ -5,18 +5,27 @@ import internal._ import scala.runtime.DynamicTuple +/** Tuple of arbitrary arity */ sealed trait Tuple extends Any { import Tuple._ + /** Create a copy this tuple as an Array */ inline def toArray: Array[Object] = DynamicTuple.dynamicToArray(this) + /** Return a new tuple by prepending the element to `this` tuple. + * This opteration is O(this.size) + */ inline def *: [H, This >: this.type <: Tuple] (x: H): H *: This = DynamicTuple.dynamicCons[This, H](this, x) + /** Return a new tuple by concatenating `this` tuple with `that` tuple. + * This opteration is O(this.size + that.size) + */ inline def ++ [This >: this.type <: Tuple](that: Tuple): Concat[This, that.type] = DynamicTuple.dynamicConcat[This, that.type](this, that) + /** Return the size (or arity) of the tuple */ inline def size[This >: this.type <: Tuple]: Size[This] = DynamicTuple.dynamicSize(this) @@ -24,19 +33,23 @@ sealed trait Tuple extends Any { object Tuple { + /** Type of the head of a tuple */ type Head[X <: NonEmptyTuple] = X match { case x *: _ => x } + /** Type of the tail of a tuple */ type Tail[X <: NonEmptyTuple] <: Tuple = X match { case _ *: xs => xs } + /** Type of the concatenation of two tuples */ type Concat[X <: Tuple, +Y <: Tuple] <: Tuple = X match { case Unit => Y case x1 *: xs1 => x1 *: Concat[xs1, Y] } + /** Type of the element a position N in the tuple X */ type Elem[X <: Tuple, N] = X match { case x *: xs => N match { @@ -45,11 +58,13 @@ object Tuple { } } + /** Literal constant Int size of a tuple */ type Size[X] <: Int = X match { case Unit => 0 case x *: xs => S[Size[xs]] } + /** Convert an array into a tuple of unknown arity and types */ def fromArray[T](xs: Array[T]): Tuple = { val xs2 = xs match { case xs: Array[Object] => xs @@ -58,20 +73,29 @@ object Tuple { DynamicTuple.dynamicFromArray[Tuple](xs2) } + /** Convert a Product into a tuple of unknown arity and types */ def fromProduct(product: Product): Tuple = runtime.DynamicTuple.dynamicFromProduct[Tuple](product) } +/** Tuple of arbitrary non-zero arity */ sealed trait NonEmptyTuple extends Tuple { import Tuple._ + /** Get the i-th element of this tuple. + * Equivalent to productElement but with a precise return type. + */ inline def apply[This >: this.type <: NonEmptyTuple](n: Int): Elem[This, n.type] = DynamicTuple.dynamicApply[This, n.type](this, n) + /** Get the head of this tuple */ inline def head[This >: this.type <: NonEmptyTuple]: Head[This] = DynamicTuple.dynamicApply[This, 0](this, 0) + /** Get the tail of this tuple. + * This opteration is O(this.size) + */ inline def tail[This >: this.type <: NonEmptyTuple]: Tail[This] = DynamicTuple.dynamicTail[This](this) From c4fb0aceecf7459560f45e237d278dd45afb9bd6 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 28 May 2019 11:34:28 +0200 Subject: [PATCH 09/11] Add missing bounds --- library/src-3.x/scala/Tuple.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/src-3.x/scala/Tuple.scala b/library/src-3.x/scala/Tuple.scala index 785b7d2cd0dd..495a6201dc96 100644 --- a/library/src-3.x/scala/Tuple.scala +++ b/library/src-3.x/scala/Tuple.scala @@ -50,7 +50,7 @@ object Tuple { } /** Type of the element a position N in the tuple X */ - type Elem[X <: Tuple, N] = X match { + type Elem[X <: Tuple, N <: Int] = X match { case x *: xs => N match { case 0 => x @@ -59,7 +59,7 @@ object Tuple { } /** Literal constant Int size of a tuple */ - type Size[X] <: Int = X match { + type Size[X <: Tuple] <: Int = X match { case Unit => 0 case x *: xs => S[Size[xs]] } From 950ead1e5079c2499eae3557e998861a7a4ada62 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 5 Jun 2019 19:16:49 +0200 Subject: [PATCH 10/11] Improve tuple specialization code --- .../dotty/tools/dotc/core/Definitions.scala | 1 + .../dotc/transform/TupleOptimizations.scala | 19 ++++++++++--------- library/src-3.x/scala/Tuple.scala | 2 +- .../src-3.x/scala/runtime/DynamicTuple.scala | 2 +- .../staged-tuples/StagedTuple.scala | 4 ++-- 5 files changed, 15 insertions(+), 13 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 369501daf27e..0d02b50cb259 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -814,6 +814,7 @@ class Definitions { def TupleXXL_fromIterator(implicit ctx: Context): Symbol = TupleXXLModule.requiredMethod("fromIterator") lazy val DynamicTupleModule: Symbol = ctx.requiredModule("scala.runtime.DynamicTuple") + lazy val DynamicTupleModuleClass: Symbol = DynamicTupleModule.moduleClass lazy val DynamicTuple_consIterator: Symbol = DynamicTupleModule.requiredMethod("consIterator") lazy val DynamicTuple_concatIterator: Symbol = DynamicTupleModule.requiredMethod("concatIterator") lazy val DynamicTuple_dynamicApply: Symbol = DynamicTupleModule.requiredMethod("dynamicApply") diff --git a/compiler/src/dotty/tools/dotc/transform/TupleOptimizations.scala b/compiler/src/dotty/tools/dotc/transform/TupleOptimizations.scala index 82fa30e1f809..e57aeca57253 100644 --- a/compiler/src/dotty/tools/dotc/transform/TupleOptimizations.scala +++ b/compiler/src/dotty/tools/dotc/transform/TupleOptimizations.scala @@ -24,7 +24,8 @@ class TupleOptimizations extends MiniPhase with IdentityDenotTransformer { def phaseName: String = "genericTuples" override def transformApply(tree: tpd.Apply)(implicit ctx: Context): tpd.Tree = { - if (tree.symbol == defn.DynamicTuple_dynamicCons) transformTupleCons(tree) + if (!tree.symbol.exists || tree.symbol.owner != defn.DynamicTupleModuleClass) super.transformApply(tree) + else if (tree.symbol == defn.DynamicTuple_dynamicCons) transformTupleCons(tree) else if (tree.symbol == defn.DynamicTuple_dynamicTail) transformTupleTail(tree) else if (tree.symbol == defn.DynamicTuple_dynamicSize) transformTupleSize(tree) else if (tree.symbol == defn.DynamicTuple_dynamicConcat) transformTupleConcat(tree) @@ -34,8 +35,7 @@ class TupleOptimizations extends MiniPhase with IdentityDenotTransformer { } private def transformTupleCons(tree: tpd.Apply)(implicit ctx: Context): Tree = { - val TypeApply(_, headType :: tailType :: Nil) = tree.fun - val tail :: head :: Nil = tree.args + val head :: tail :: Nil = tree.args tupleTypes(tree.tpe) match { case Some(tpes) => // Generate a the tuple directly with TupleN+1.apply @@ -44,7 +44,7 @@ class TupleOptimizations extends MiniPhase with IdentityDenotTransformer { // val t = tail // TupleN+1(head, t._1, ..., t._n) evalOnce(Typed(tail, TypeTree(defn.tupleType(tpes.tail)))) { tup => - val elements = head :: (0 until size - 1).map(i => tup.select(nme.selectorName(i))).toList + val elements = head :: tupleSelectors(tup, size - 1) knownTupleFromElements(tpes, elements) } } else { @@ -77,7 +77,7 @@ class TupleOptimizations extends MiniPhase with IdentityDenotTransformer { // val t = tup.asInstanceOf[TupleN[...]] // TupleN-1(t._2, ..., t._n) evalOnce(Typed(tup, TypeTree(defn.tupleType(tpes)))) { tup => - val elements = (1 until size).map(i => tup.select(nme.selectorName(i))).toList + val elements = tupleSelectors(tup, size).tail knownTupleFromElements(tpes.tail, elements) } } else if (size <= MaxTupleArity + 1) { @@ -125,10 +125,7 @@ class TupleOptimizations extends MiniPhase with IdentityDenotTransformer { evalOnce(Typed(self, TypeTree(defn.tupleType(tpes1)))) { self => evalOnce(Typed(that, TypeTree(defn.tupleType(tpes2)))) { that => val types = tpes1 ::: tpes2 - val elements = { - (0 until n).map(i => self.select(nme.selectorName(i))) ++ - (0 until m).map(i => that.select(nme.selectorName(i))) - }.toList + val elements = tupleSelectors(self, n) ::: tupleSelectors(that, m) knownTupleFromElements(types, elements) } } @@ -235,4 +232,8 @@ class TupleOptimizations extends MiniPhase with IdentityDenotTransformer { } rec(tp.stripTypeVar, Nil, bound) } + + private def tupleSelectors(tup: Tree, size: Int)(implicit ctx: Context): List[Tree] = + (0 until size).map(i => tup.select(nme.selectorName(i))).toList + } diff --git a/library/src-3.x/scala/Tuple.scala b/library/src-3.x/scala/Tuple.scala index 495a6201dc96..91fdac7f46bf 100644 --- a/library/src-3.x/scala/Tuple.scala +++ b/library/src-3.x/scala/Tuple.scala @@ -17,7 +17,7 @@ sealed trait Tuple extends Any { * This opteration is O(this.size) */ inline def *: [H, This >: this.type <: Tuple] (x: H): H *: This = - DynamicTuple.dynamicCons[This, H](this, x) + DynamicTuple.dynamicCons[H, This](x, this) /** Return a new tuple by concatenating `this` tuple with `that` tuple. * This opteration is O(this.size + that.size) diff --git a/library/src-3.x/scala/runtime/DynamicTuple.scala b/library/src-3.x/scala/runtime/DynamicTuple.scala index 1239c4f2a22d..b27b38d59f5d 100644 --- a/library/src-3.x/scala/runtime/DynamicTuple.scala +++ b/library/src-3.x/scala/runtime/DynamicTuple.scala @@ -186,7 +186,7 @@ object DynamicTuple { arr } - def dynamicCons[This <: Tuple, H] (self: Tuple, x: H): H *: This = { + def dynamicCons[H, This <: Tuple](x: H, self: Tuple): H *: This = { type Result = H *: This (self: Any) match { case () => diff --git a/tests/run-with-compiler/staged-tuples/StagedTuple.scala b/tests/run-with-compiler/staged-tuples/StagedTuple.scala index f8c75ccefa63..a8a5d57c5f48 100644 --- a/tests/run-with-compiler/staged-tuples/StagedTuple.scala +++ b/tests/run-with-compiler/staged-tuples/StagedTuple.scala @@ -184,7 +184,7 @@ object StagedTuple { } def consStaged[T <: Tuple & Singleton : Type, H : Type](self: Expr[T], x: Expr[H], tailSize: Option[Int]): Expr[H *: T] = - if (!specialize) '{dynamicCons[T, H]($self, $x)} + if (!specialize) '{dynamicCons[H, T]($x, $self)} else { val res = tailSize match { case Some(0) => @@ -200,7 +200,7 @@ object StagedTuple { case Some(n) => fromArrayStaged[H *: T]('{cons$Array($x, ${ toArrayStaged(self, tailSize) })}, Some(n + 1)) case _ => - '{dynamicCons[T, H]($self, $x)} + '{dynamicCons[H, T]($x, $self)} } res.as[H *: T] } From e5b880f751226d6a14b6cf883c9de6af6216ed9a Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 6 Jun 2019 09:10:12 +0200 Subject: [PATCH 11/11] Return tree directly --- .../src/dotty/tools/dotc/transform/TupleOptimizations.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/TupleOptimizations.scala b/compiler/src/dotty/tools/dotc/transform/TupleOptimizations.scala index e57aeca57253..062d3d49120d 100644 --- a/compiler/src/dotty/tools/dotc/transform/TupleOptimizations.scala +++ b/compiler/src/dotty/tools/dotc/transform/TupleOptimizations.scala @@ -24,14 +24,14 @@ class TupleOptimizations extends MiniPhase with IdentityDenotTransformer { def phaseName: String = "genericTuples" override def transformApply(tree: tpd.Apply)(implicit ctx: Context): tpd.Tree = { - if (!tree.symbol.exists || tree.symbol.owner != defn.DynamicTupleModuleClass) super.transformApply(tree) + if (!tree.symbol.exists || tree.symbol.owner != defn.DynamicTupleModuleClass) tree else if (tree.symbol == defn.DynamicTuple_dynamicCons) transformTupleCons(tree) else if (tree.symbol == defn.DynamicTuple_dynamicTail) transformTupleTail(tree) else if (tree.symbol == defn.DynamicTuple_dynamicSize) transformTupleSize(tree) else if (tree.symbol == defn.DynamicTuple_dynamicConcat) transformTupleConcat(tree) else if (tree.symbol == defn.DynamicTuple_dynamicApply) transformTupleApply(tree) else if (tree.symbol == defn.DynamicTuple_dynamicToArray) transformTupleToArray(tree) - else super.transformApply(tree) + else tree } private def transformTupleCons(tree: tpd.Apply)(implicit ctx: Context): Tree = {