From 763af55caad6e216a65137d3ae6e516c8e1a9ba3 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 15 Apr 2019 17:38:39 +0200 Subject: [PATCH 1/2] Remove staged tuples Until now tuples had two implementations, one with staging and one with plain inline and inliner constant folding. The second one was used in practice as the first one could not be used if tuples where used (macros expanded) in the library due to bootstrapping issues. These implementations were never intended to actually be used, they mostly showed the power of match types, inline and staging. Eventually, this logic will be special cased in the compiler for performance. --- library/src-bootstrapped/scala/Tuple.scala | 362 ++++++++---------- .../internal => tests/pos}/StagedTuple.scala | 0 2 files changed, 160 insertions(+), 202 deletions(-) rename {library/src-bootstrapped/scala/internal => tests/pos}/StagedTuple.scala (100%) diff --git a/library/src-bootstrapped/scala/Tuple.scala b/library/src-bootstrapped/scala/Tuple.scala index 622656ab8259..389a47adb4e1 100644 --- a/library/src-bootstrapped/scala/Tuple.scala +++ b/library/src-bootstrapped/scala/Tuple.scala @@ -7,8 +7,7 @@ sealed trait Tuple extends Any { import Tuple._ inline def toArray: Array[Object] = - if (stageIt) toArrayStaged - else inline constValueOpt[BoundedSize[this.type]] match { + inline constValueOpt[BoundedSize[this.type]] match { case Some(0) => scala.runtime.DynamicTuple.empty$Array case Some(1) => @@ -31,101 +30,82 @@ sealed trait Tuple extends Any { runtime.DynamicTuple.dynamicToArray(this) } - inline def toArrayStaged: Array[Object] = - ${ StagedTuple.toArrayStaged('this, constValueOpt[BoundedSize[this.type]]) } - - inline def *: [H, This >: this.type <: Tuple] (x: H): H *: This = - if (stageIt) consStaged[H](x) - else { - 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) => - fromArray[H *: this.type](cons$Array(x, toArray)) - case _ => - runtime.DynamicTuple.dynamic_*:[This, H](this, x) - } + 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) => + fromArray[H *: this.type](cons$Array(x, toArray)) + case _ => + runtime.DynamicTuple.dynamic_*:[This, H](this, x) } + } - inline def consStaged[H] (x: H): H *: this.type = - ${ StagedTuple.consStaged('this, 'x, constValueOpt[BoundedSize[this.type]]) } - - inline def ++ [This >: this.type <: Tuple](that: Tuple): Concat[This, that.type] = - if (stageIt) concatStaged(that).asInstanceOf - else { - 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 _ => - genericConcat[Result](this, that).asInstanceOf[Result] - } - 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 _ => - genericConcat[Result](this, that).asInstanceOf[Result] - } - case Some(_) => - if (constValue[BoundedSize[that.type]] == 0) this.asInstanceOf[Result] - else genericConcat[Result](this, that).asInstanceOf[Result] - case None => - runtime.DynamicTuple.dynamic_++[This, that.type](this, that) - } + 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 _ => + genericConcat[Result](this, that).asInstanceOf[Result] + } + 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 _ => + genericConcat[Result](this, that).asInstanceOf[Result] + } + case Some(_) => + if (constValue[BoundedSize[that.type]] == 0) this.asInstanceOf[Result] + else genericConcat[Result](this, that).asInstanceOf[Result] + case None => + runtime.DynamicTuple.dynamic_++[This, that.type](this, that) } - - inline def concatStaged(that: Tuple): Concat[this.type, that.type] = - ${ StagedTuple.concatStaged('this, constValueOpt[BoundedSize[this.type]], - 'that, constValueOpt[BoundedSize[that.type]]) } + } inline def genericConcat[T <: Tuple](xs: Tuple, ys: Tuple): Tuple = fromArray[T](xs.toArray ++ ys.toArray) - inline def size[This >: this.type <: Tuple]: Size[This] = - if (stageIt) sizeStaged.asInstanceOf - else { - type Result = Size[This] - inline constValueOpt[BoundedSize[this.type]] match { - case Some(n) => n.asInstanceOf[Result] - case _ => runtime.DynamicTuple.dynamicSize(this) - } + 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 _ => runtime.DynamicTuple.dynamicSize(this) } + } - inline def sizeStaged: Size[this.type] = - ${ StagedTuple.sizeStaged[Size[this.type]]('this, constValueOpt[BoundedSize[this.type]]) } } object Tuple { - final val stageIt = false type Head[X <: NonEmptyTuple] = X match { case x *: _ => x @@ -185,8 +165,7 @@ object Tuple { } inline def fromArray[T <: Tuple](xs: Array[Object]): T = - if (stageIt) fromArrayStaged[T](xs) - else inline constValue[BoundedSize[T]] match { + 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] @@ -212,73 +191,60 @@ object Tuple { 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] } - - inline def fromArrayStaged[T <: Tuple](xs: Array[Object]): T = - ${StagedTuple.fromArrayStaged[T]('xs, constValueOpt[BoundedSize[this.type]])} } sealed trait NonEmptyTuple extends Tuple { import Tuple._ - inline def head[This >: this.type <: NonEmptyTuple]: Head[This] = - if (stageIt) headStaged.asInstanceOf - else { - 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 <= scala.runtime.DynamicTuple.MaxSpecialized => - asInstanceOf[Product].productElement(0) - case Some(n) if n > scala.runtime.DynamicTuple.MaxSpecialized => - val t = asInstanceOf[TupleXXL] - t.elems(0) - case None => - scala.runtime.DynamicTuple.dynamicHead[this.type](this) - } - resVal.asInstanceOf[Result] + 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 <= scala.runtime.DynamicTuple.MaxSpecialized => + asInstanceOf[Product].productElement(0) + case Some(n) if n > scala.runtime.DynamicTuple.MaxSpecialized => + val t = asInstanceOf[TupleXXL] + t.elems(0) + case None => + scala.runtime.DynamicTuple.dynamicHead[this.type](this) } + resVal.asInstanceOf[Result] + } - inline def headStaged: Head[this.type] = - ${ StagedTuple.headStaged[this.type]('this, constValueOpt[BoundedSize[this.type]]) } - - inline def tail[This >: this.type <: NonEmptyTuple]: Tail[This] = - if (stageIt) tailStaged.asInstanceOf - else { - 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 => - fromArray[Result](toArray.tail) - case None => - runtime.DynamicTuple.dynamicTail[This](this) - } + 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 => + fromArray[Result](toArray.tail) + case None => + runtime.DynamicTuple.dynamicTail[This](this) } - - inline def tailStaged: Tail[this.type] = - ${ StagedTuple.tailStaged[this.type]('this, constValueOpt[BoundedSize[this.type]]) } + } inline def fallbackApply(n: Int) = inline constValueOpt[n.type] match { @@ -286,68 +252,60 @@ sealed trait NonEmptyTuple extends Tuple { case None => runtime.DynamicTuple.dynamicApply[this.type, n.type](this, n) } - inline def apply[This >: this.type <: NonEmptyTuple](n: Int): Elem[This, n.type] = - if (stageIt) applyStaged(n).asInstanceOf - else { - 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 <= scala.runtime.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 > scala.runtime.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 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 <= scala.runtime.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 > scala.runtime.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 applyStaged(n: Int): Elem[this.type, n.type] = - ${StagedTuple.applyStaged[this.type, n.type]( - 'this, constValueOpt[Size[this.type]], 'n, constValueOpt[n.type])} } @showAsInfix sealed class *:[+H, +T <: Tuple] extends NonEmptyTuple object *: { - inline def unapply[H, T <: Tuple](x: H *: T) = - // With stageIt on we cannot expand x.head in the same run and fails - if (Tuple.stageIt) (scala.runtime.DynamicTuple.dynamicHead(x), scala.runtime.DynamicTuple.dynamicTail(x)) - else (x.head, x.tail) + inline def unapply[H, T <: Tuple](x: H *: T) = (x.head, x.tail) } diff --git a/library/src-bootstrapped/scala/internal/StagedTuple.scala b/tests/pos/StagedTuple.scala similarity index 100% rename from library/src-bootstrapped/scala/internal/StagedTuple.scala rename to tests/pos/StagedTuple.scala From 75d9dda78c38df6fe2b6b19851bc330bfe5ccf72 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 16 Apr 2019 12:34:55 +0200 Subject: [PATCH 2/2] Add some runtime tests for StagedTuple --- .../staged-tuples}/StagedTuple.scala | 0 .../staged-tuples/Test.scala | 26 +++++++++++++++++++ 2 files changed, 26 insertions(+) rename tests/{pos => run-with-compiler/staged-tuples}/StagedTuple.scala (100%) create mode 100644 tests/run-with-compiler/staged-tuples/Test.scala diff --git a/tests/pos/StagedTuple.scala b/tests/run-with-compiler/staged-tuples/StagedTuple.scala similarity index 100% rename from tests/pos/StagedTuple.scala rename to tests/run-with-compiler/staged-tuples/StagedTuple.scala diff --git a/tests/run-with-compiler/staged-tuples/Test.scala b/tests/run-with-compiler/staged-tuples/Test.scala new file mode 100644 index 000000000000..c9b3f031628d --- /dev/null +++ b/tests/run-with-compiler/staged-tuples/Test.scala @@ -0,0 +1,26 @@ +import scala.internal.StagedTuple._ + +object Test { + + def main(args: Array[String]): Unit = { + implicit val toolbox: scala.quoted.Toolbox = scala.quoted.Toolbox.make(getClass.getClassLoader) + + assert(fromArrayStaged('{ Array.empty[Object] }, Some(0)).run.==(())) + assert(fromArrayStaged[Tuple1[String]]('{ Array[Object]("a") }, Some(1)).run == Tuple1("a")) + assert(fromArrayStaged[(String, String)]('{ Array[Object]("a", "b") }, Some(2)).run == ("a", "b")) + assert(fromArrayStaged[(String, String, String)]('{ Array[Object]("a", "b", "c") }, Some(3)).run == ("a", "b", "c")) + + assert(headStaged[Tuple1[String]]('{ Tuple1("a") }, Some(1)).run == "a") + assert(headStaged[(String, String)]('{ ("a", "b") }, Some(2)).run == "a") + assert(headStaged[(String, String, String)]('{ ("a", "b", "c") }, Some(3)).run == "a") + + assert(tailStaged[Tuple1[String]]('{ Tuple1("a") }, Some(1)).run == (())) + assert(tailStaged[(String, String)]('{ ("a", "b") }, Some(2)).run == Tuple1("b")) + assert(tailStaged[(String, String, String)]('{ ("a", "b", "c") }, Some(3)).run == ("b", "c")) + + assert(headStaged[(String, String, String)]('{ ("a", "b", "c") }, Some(3)).run == "a") + assert(headStaged[(String, String, String)]('{ ("a", "b", "c") }, Some(3)).run == "a") + assert(headStaged[(String, String, String)]('{ ("a", "b", "c") }, Some(3)).run == "a") + + } +}