diff --git a/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/Apply.scala b/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/Apply.scala index 2bcde1ac6992..d5fcd17a0e5c 100644 --- a/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/Apply.scala +++ b/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/Apply.scala @@ -21,7 +21,7 @@ class Apply { @Benchmark def tupleApply(): Any = { - runtime.Tuple.apply(tuple, index) + runtime.Tuples.apply(tuple, index) } @Benchmark diff --git a/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/Concat.scala b/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/Concat.scala index 4f8345909fe0..c631d6877c36 100644 --- a/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/Concat.scala +++ b/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/Concat.scala @@ -27,6 +27,6 @@ class Concat { @Benchmark def tupleConcat(): Tuple = { - runtime.Tuple.concat(tuple1, tuple2) + runtime.Tuples.concat(tuple1, tuple2) } } diff --git a/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/Cons.scala b/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/Cons.scala index e7f91bfc5c18..19bc3732524a 100644 --- a/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/Cons.scala +++ b/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/Cons.scala @@ -23,7 +23,7 @@ class Cons { @Benchmark def tupleCons(): Tuple = { - runtime.Tuple.cons("elem", tuple) + runtime.Tuples.cons("elem", tuple) } @Benchmark diff --git a/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/Conversions.scala b/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/Conversions.scala index e44c8a3072b4..8f353fae26a7 100644 --- a/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/Conversions.scala +++ b/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/Conversions.scala @@ -23,27 +23,27 @@ class Conversions { @Benchmark def tupleToArray(): Array[Object] = { - runtime.Tuple.toArray(tuple) + runtime.Tuples.toArray(tuple) } @Benchmark def tupleToIArray(): IArray[Object] = { - runtime.Tuple.toIArray(tuple) + runtime.Tuples.toIArray(tuple) } @Benchmark def tupleFromArray(): Tuple = { - runtime.Tuple.fromArray(array) + runtime.Tuples.fromArray(array) } @Benchmark def tupleFromIArray(): Tuple = { - runtime.Tuple.fromIArray(iarray) + runtime.Tuples.fromIArray(iarray) } @Benchmark def productToArray(): Array[Object] = { - runtime.Tuple.productToArray(tuple.asInstanceOf[Product]) + runtime.Tuples.productToArray(tuple.asInstanceOf[Product]) } @Benchmark diff --git a/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/Map.scala b/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/Map.scala index 41418e2bf48b..fbf040f2e31c 100644 --- a/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/Map.scala +++ b/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/Map.scala @@ -29,7 +29,7 @@ class Map { @Benchmark def tupleMap(): Tuple = { - runtime.Tuple.map[Id](tuple, [T] => (x:T) => x.asInstanceOf[String].updated(0, 'a').asInstanceOf[T]) + runtime.Tuples.map[Id](tuple, [T] => (x:T) => x.asInstanceOf[String].updated(0, 'a').asInstanceOf[T]) } @Benchmark diff --git a/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/Tail.scala b/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/Tail.scala index 4ce39c464bab..0a8314a074b5 100644 --- a/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/Tail.scala +++ b/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/Tail.scala @@ -21,7 +21,7 @@ class Tail { @Benchmark def tupleTail(): Tuple = { - runtime.Tuple.tail(tuple) + runtime.Tuples.tail(tuple) } @Benchmark diff --git a/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/Zip.scala b/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/Zip.scala index 1ad79ab42079..32f5b19da731 100644 --- a/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/Zip.scala +++ b/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/Zip.scala @@ -27,7 +27,7 @@ class Zip { @Benchmark def tupleZip(): Tuple = { - runtime.Tuple.zip(tuple1, tuple2) + runtime.Tuples.zip(tuple1, tuple2) } @Benchmark diff --git a/bench-run/src/main/scala/tuples/Drop.scala b/bench-run/src/main/scala/tuples/Drop.scala index f5fcfcdd803c..6f2b01055e0f 100644 --- a/bench-run/src/main/scala/tuples/Drop.scala +++ b/bench-run/src/main/scala/tuples/Drop.scala @@ -23,7 +23,7 @@ class Drop { @Benchmark def tupleDrop(): Tuple = { - runtime.Tuple.drop(tuple, half) + runtime.Tuples.drop(tuple, half) } @Benchmark diff --git a/bench-run/src/main/scala/tuples/Split.scala b/bench-run/src/main/scala/tuples/Split.scala index b3b182ec87a7..b279b654741a 100644 --- a/bench-run/src/main/scala/tuples/Split.scala +++ b/bench-run/src/main/scala/tuples/Split.scala @@ -23,7 +23,7 @@ class Split { @Benchmark def tupleSplit(): (Tuple, Tuple) = { - runtime.Tuple.splitAt(tuple, half) + runtime.Tuples.splitAt(tuple, half) } @Benchmark diff --git a/bench-run/src/main/scala/tuples/Take.scala b/bench-run/src/main/scala/tuples/Take.scala index 12205e21d71c..50fccbe5e7cd 100644 --- a/bench-run/src/main/scala/tuples/Take.scala +++ b/bench-run/src/main/scala/tuples/Take.scala @@ -23,7 +23,7 @@ class Take { @Benchmark def tupleTake(): Tuple = { - runtime.Tuple.take(tuple, half) + runtime.Tuples.take(tuple, half) } @Benchmark diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index cca01dd32a48..a94cc44bb2aa 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -867,20 +867,20 @@ class Definitions { def TupleXXL_fromIterator(using Context): Symbol = TupleXXLModule.requiredMethod("fromIterator") - @tu lazy val RuntimeTupleModule: Symbol = requiredModule("scala.runtime.Tuple") - @tu lazy val RuntimeTupleModuleClass: Symbol = RuntimeTupleModule.moduleClass - lazy val RuntimeTuple_consIterator: Symbol = RuntimeTupleModule.requiredMethod("consIterator") - lazy val RuntimeTuple_concatIterator: Symbol = RuntimeTupleModule.requiredMethod("concatIterator") - lazy val RuntimeTuple_apply: Symbol = RuntimeTupleModule.requiredMethod("apply") - lazy val RuntimeTuple_cons: Symbol = RuntimeTupleModule.requiredMethod("cons") - lazy val RuntimeTuple_size: Symbol = RuntimeTupleModule.requiredMethod("size") - lazy val RuntimeTuple_tail: Symbol = RuntimeTupleModule.requiredMethod("tail") - lazy val RuntimeTuple_concat: Symbol = RuntimeTupleModule.requiredMethod("concat") - lazy val RuntimeTuple_toArray: Symbol = RuntimeTupleModule.requiredMethod("toArray") - lazy val RuntimeTuple_productToArray: Symbol = RuntimeTupleModule.requiredMethod("productToArray") - lazy val RuntimeTuple_isInstanceOfTuple: Symbol = RuntimeTupleModule.requiredMethod("isInstanceOfTuple") - lazy val RuntimeTuple_isInstanceOfEmptyTuple: Symbol = RuntimeTupleModule.requiredMethod("isInstanceOfEmptyTuple") - lazy val RuntimeTuple_isInstanceOfNonEmptyTuple: Symbol = RuntimeTupleModule.requiredMethod("isInstanceOfNonEmptyTuple") + @tu lazy val RuntimeTuplesModule: Symbol = requiredModule("scala.runtime.Tuples") + @tu lazy val RuntimeTuplesModuleClass: Symbol = RuntimeTuplesModule.moduleClass + lazy val RuntimeTuples_consIterator: Symbol = RuntimeTuplesModule.requiredMethod("consIterator") + lazy val RuntimeTuples_concatIterator: Symbol = RuntimeTuplesModule.requiredMethod("concatIterator") + lazy val RuntimeTuples_apply: Symbol = RuntimeTuplesModule.requiredMethod("apply") + lazy val RuntimeTuples_cons: Symbol = RuntimeTuplesModule.requiredMethod("cons") + lazy val RuntimeTuples_size: Symbol = RuntimeTuplesModule.requiredMethod("size") + lazy val RuntimeTuples_tail: Symbol = RuntimeTuplesModule.requiredMethod("tail") + lazy val RuntimeTuples_concat: Symbol = RuntimeTuplesModule.requiredMethod("concat") + lazy val RuntimeTuples_toArray: Symbol = RuntimeTuplesModule.requiredMethod("toArray") + lazy val RuntimeTuples_productToArray: Symbol = RuntimeTuplesModule.requiredMethod("productToArray") + lazy val RuntimeTuples_isInstanceOfTuple: Symbol = RuntimeTuplesModule.requiredMethod("isInstanceOfTuple") + lazy val RuntimeTuples_isInstanceOfEmptyTuple: Symbol = RuntimeTuplesModule.requiredMethod("isInstanceOfEmptyTuple") + lazy val RuntimeTuples_isInstanceOfNonEmptyTuple: Symbol = RuntimeTuplesModule.requiredMethod("isInstanceOfNonEmptyTuple") // Annotation base classes @tu lazy val AnnotationClass: ClassSymbol = requiredClass("scala.annotation.Annotation") diff --git a/compiler/src/dotty/tools/dotc/transform/TupleOptimizations.scala b/compiler/src/dotty/tools/dotc/transform/TupleOptimizations.scala index 124581c1db6e..a5ea7c19e45c 100644 --- a/compiler/src/dotty/tools/dotc/transform/TupleOptimizations.scala +++ b/compiler/src/dotty/tools/dotc/transform/TupleOptimizations.scala @@ -24,13 +24,13 @@ class TupleOptimizations extends MiniPhase with IdentityDenotTransformer { def phaseName: String = "genericTuples" override def transformApply(tree: tpd.Apply)(using Context): tpd.Tree = - if (!tree.symbol.exists || tree.symbol.owner != defn.RuntimeTupleModuleClass) tree - else if (tree.symbol == defn.RuntimeTuple_cons) transformTupleCons(tree) - else if (tree.symbol == defn.RuntimeTuple_tail) transformTupleTail(tree) - else if (tree.symbol == defn.RuntimeTuple_size) transformTupleSize(tree) - else if (tree.symbol == defn.RuntimeTuple_concat) transformTupleConcat(tree) - else if (tree.symbol == defn.RuntimeTuple_apply) transformTupleApply(tree) - else if (tree.symbol == defn.RuntimeTuple_toArray) transformTupleToArray(tree) + if (!tree.symbol.exists || tree.symbol.owner != defn.RuntimeTuplesModuleClass) tree + else if (tree.symbol == defn.RuntimeTuples_cons) transformTupleCons(tree) + else if (tree.symbol == defn.RuntimeTuples_tail) transformTupleTail(tree) + else if (tree.symbol == defn.RuntimeTuples_size) transformTupleSize(tree) + else if (tree.symbol == defn.RuntimeTuples_concat) transformTupleConcat(tree) + else if (tree.symbol == defn.RuntimeTuples_apply) transformTupleApply(tree) + else if (tree.symbol == defn.RuntimeTuples_toArray) transformTupleToArray(tree) else tree private def transformTupleCons(tree: tpd.Apply)(using Context): Tree = { @@ -49,14 +49,14 @@ class TupleOptimizations extends MiniPhase with IdentityDenotTransformer { else { // val it = Iterator.single(head) ++ tail.asInstanceOf[Product].productIterator // TupleN+1(it.next(), ..., it.next()) - val fullIterator = ref(defn.RuntimeTuple_consIterator).appliedToArgs(head :: tail :: Nil) + val fullIterator = ref(defn.RuntimeTuples_consIterator).appliedToArgs(head :: tail :: Nil) evalOnce(fullIterator) { it => knownTupleFromIterator(tpes.length, it).asInstance(tree.tpe) } } case _ => // No optimization, keep: - // scala.runtime.Tuple.cons(tail, head) + // scala.runtime.Tuples.cons(tail, head) tree } } @@ -93,7 +93,7 @@ class TupleOptimizations extends MiniPhase with IdentityDenotTransformer { tup.asInstance(defn.TupleXXLClass.typeRef).select("tailXXL".toTermName) case None => // No optimization, keep: - // scala.runtime.Tuple.tail(tup) + // scala.runtime.Tuples.tail(tup) tree } } @@ -127,14 +127,14 @@ class TupleOptimizations extends MiniPhase with IdentityDenotTransformer { else { // val it = self.asInstanceOf[Product].productIterator ++ that.asInstanceOf[Product].productIterator // TupleN+M(it.next(), ..., it.next()) - val fullIterator = ref(defn.RuntimeTuple_concatIterator).appliedToArgs(tree.args) + val fullIterator = ref(defn.RuntimeTuples_concatIterator).appliedToArgs(tree.args) evalOnce(fullIterator) { it => knownTupleFromIterator(n + m, it).asInstance(tree.tpe) } } case _ => // No optimization, keep: - // scala.runtime.Tuple.cons(self, that) + // scala.runtime.Tuples.cons(self, that) tree } } @@ -161,7 +161,7 @@ class TupleOptimizations extends MiniPhase with IdentityDenotTransformer { tree case _ => // No optimization, keep: - // scala.runtime.Tuple.apply(tup, n) + // scala.runtime.Tuples.apply(tup, n) tree } } @@ -175,14 +175,14 @@ class TupleOptimizations extends MiniPhase with IdentityDenotTransformer { // Array.emptyObjectArray ref(defn.ArrayModule).select("emptyObjectArray".toTermName).ensureApplied else if (size <= MaxTupleArity) - // scala.runtime.Tuple.productToArray(tup.asInstanceOf[Product]) - ref(defn.RuntimeTuple_productToArray).appliedTo(tup.asInstance(defn.ProductClass.typeRef)) + // scala.runtime.Tuples.productToArray(tup.asInstanceOf[Product]) + ref(defn.RuntimeTuples_productToArray).appliedTo(tup.asInstance(defn.ProductClass.typeRef)) else // tup.asInstanceOf[TupleXXL].elems.clone() tup.asInstance(defn.TupleXXLClass.typeRef).select(nme.toArray) case None => // No optimization, keep: - // scala.runtime.Tuple.toArray(tup) + // scala.runtime.Tuples.toArray(tup) tree } } diff --git a/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala b/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala index d54d00006630..f299695e0eb6 100644 --- a/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala +++ b/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala @@ -311,17 +311,17 @@ object TypeTestsCasts { * * expr.isInstanceOf[A | B] ~~> expr.isInstanceOf[A] | expr.isInstanceOf[B] * expr.isInstanceOf[A & B] ~~> expr.isInstanceOf[A] & expr.isInstanceOf[B] - * expr.isInstanceOf[Tuple] ~~> scala.runtime.Tuple.isInstanceOfTuple(expr) - * expr.isInstanceOf[EmptyTuple] ~~> scala.runtime.Tuple.isInstanceOfEmptyTuple(expr) - * expr.isInstanceOf[NonEmptyTuple] ~~> scala.runtime.Tuple.isInstanceOfNonEmptyTuple(expr) - * expr.isInstanceOf[*:[_, _]] ~~> scala.runtime.Tuple.isInstanceOfNonEmptyTuple(expr) + * expr.isInstanceOf[Tuple] ~~> scala.runtime.Tuples.isInstanceOfTuple(expr) + * expr.isInstanceOf[EmptyTuple] ~~> scala.runtime.Tuples.isInstanceOfEmptyTuple(expr) + * expr.isInstanceOf[NonEmptyTuple] ~~> scala.runtime.Tuples.isInstanceOfNonEmptyTuple(expr) + * expr.isInstanceOf[*:[_, _]] ~~> scala.runtime.Tuples.isInstanceOfNonEmptyTuple(expr) * * The transform happens before erasure of `testType`, thus cannot be merged * with `transformIsInstanceOf`, which depends on erased type of `testType`. */ def transformTypeTest(expr: Tree, testType: Type, flagUnrelated: Boolean): Tree = testType.dealias match { case tref: TermRef if tref.symbol == defn.EmptyTupleModule => - ref(defn.RuntimeTuple_isInstanceOfEmptyTuple).appliedTo(expr) + ref(defn.RuntimeTuples_isInstanceOfEmptyTuple).appliedTo(expr) case _: SingletonType => expr.isInstance(testType).withSpan(tree.span) case OrType(tp1, tp2) => @@ -343,11 +343,11 @@ object TypeTestsCasts { .and(isArrayTest(e)) } case tref: TypeRef if tref.symbol == defn.TupleClass => - ref(defn.RuntimeTuple_isInstanceOfTuple).appliedTo(expr) + ref(defn.RuntimeTuples_isInstanceOfTuple).appliedTo(expr) case tref: TypeRef if tref.symbol == defn.NonEmptyTupleClass => - ref(defn.RuntimeTuple_isInstanceOfNonEmptyTuple).appliedTo(expr) + ref(defn.RuntimeTuples_isInstanceOfNonEmptyTuple).appliedTo(expr) case AppliedType(tref: TypeRef, _) if tref.symbol == defn.PairClass => - ref(defn.RuntimeTuple_isInstanceOfNonEmptyTuple).appliedTo(expr) + ref(defn.RuntimeTuples_isInstanceOfNonEmptyTuple).appliedTo(expr) case _ => val erasedTestType = erasure(testType) transformIsInstanceOf(expr, erasedTestType, erasedTestType, flagUnrelated) diff --git a/library/src-bootstrapped/scala/Tuple.scala b/library/src-bootstrapped/scala/Tuple.scala new file mode 100644 index 000000000000..87d6f26e0e6f --- /dev/null +++ b/library/src-bootstrapped/scala/Tuple.scala @@ -0,0 +1,278 @@ +package scala +import annotation.showAsInfix +import compiletime._ + +/** Tuple of arbitrary arity */ +sealed trait Tuple extends Product { + import Tuple._ + + /** Create a copy this tuple as an Array */ + inline def toArray: Array[Object] = + runtime.Tuples.toArray(this) + + /** Create a copy this tuple as a List */ + inline def toList: List[Union[this.type]] = + this.productIterator.toList + .asInstanceOf[List[Union[this.type]]] + + /** Create a copy this tuple as an IArray */ + inline def toIArray: IArray[Object] = + runtime.Tuples.toIArray(this) + + /** Return a new tuple by prepending the element to `this` tuple. + * This operation is O(this.size) + */ + inline def *: [H, This >: this.type <: Tuple] (x: H): H *: This = + runtime.Tuples.cons(x, this).asInstanceOf[H *: This] + + /** Return a new tuple by concatenating `this` tuple with `that` tuple. + * This operation is O(this.size + that.size) + */ + inline def ++ [This >: this.type <: Tuple](that: Tuple): Concat[This, that.type] = + runtime.Tuples.concat(this, that).asInstanceOf[Concat[This, that.type]] + + /** Return the size (or arity) of the tuple */ + inline def size[This >: this.type <: Tuple]: Size[This] = + runtime.Tuples.size(this).asInstanceOf[Size[This]] + + /** Given two tuples, `(a1, ..., an)` and `(a1, ..., an)`, returns a tuple + * `((a1, b1), ..., (an, bn))`. If the two tuples have different sizes, + * the extra elements of the larger tuple will be disregarded. + * The result is typed as `((A1, B1), ..., (An, Bn))` if at least one of the + * tuple types has a `EmptyTuple` tail. Otherwise the result type is + * `(A1, B1) *: ... *: (Ai, Bi) *: Tuple` + */ + inline def zip[This >: this.type <: Tuple, T2 <: Tuple](t2: T2): Zip[This, T2] = + runtime.Tuples.zip(this, t2).asInstanceOf[Zip[This, T2]] + + /** Called on a tuple `(a1, ..., an)`, returns a new tuple `(f(a1), ..., f(an))`. + * The result is typed as `(F[A1], ..., F[An])` if the tuple type is fully known. + * If the tuple is of the form `a1 *: ... *: Tuple` (that is, the tail is not known + * to be the cons type. + */ + inline def map[F[_]](f: [t] => t => F[t]): Map[this.type, F] = + runtime.Tuples.map(this, f).asInstanceOf[Map[this.type, F]] + + /** Given a tuple `(a1, ..., am)`, returns the tuple `(a1, ..., an)` consisting + * of its first n elements. + */ + inline def take[This >: this.type <: Tuple](n: Int): Take[This, n.type] = + runtime.Tuples.take(this, n).asInstanceOf[Take[This, n.type]] + + + /** Given a tuple `(a1, ..., am)`, returns the tuple `(an+1, ..., am)` consisting + * all its elements except the first n ones. + */ + inline def drop[This >: this.type <: Tuple](n: Int): Drop[This, n.type] = + runtime.Tuples.drop(this, n).asInstanceOf[Drop[This, n.type]] + + /** Given a tuple `(a1, ..., am)`, returns a pair of the tuple `(a1, ..., an)` + * consisting of the first n elements, and the tuple `(an+1, ..., am)` consisting + * of the remaining elements. + */ + inline def splitAt[This >: this.type <: Tuple](n: Int): Split[This, n.type] = + runtime.Tuples.splitAt(this, n).asInstanceOf[Split[This, n.type]] +} + +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 EmptyTuple => Y + case x1 *: xs1 => x1 *: Concat[xs1, Y] + } + + /** Type of the element a position N in the tuple X */ + type Elem[X <: Tuple, N <: Int] = X match { + case x *: xs => + N match { + case 0 => x + case S[n1] => Elem[xs, n1] + } + } + + /** Literal constant Int size of a tuple */ + type Size[X <: Tuple] <: Int = X match { + case EmptyTuple => 0 + case x *: xs => S[Size[xs]] + } + + /** Fold a tuple `(T1, ..., Tn)` into `F[T1, F[... F[Tn, Z]...]]]` */ + type Fold[T <: Tuple, Z, F[_, _]] = T match + case EmptyTuple => Z + case h *: t => F[h, Fold[t, Z, F]] + + /** Converts a tuple `(T1, ..., Tn)` to `(F[T1], ..., F[Tn])` */ + type Map[Tup <: Tuple, F[_]] <: Tuple = Tup match { + case EmptyTuple => EmptyTuple + case h *: t => F[h] *: Map[t, F] + } + + /** Converts a tuple `(T1, ..., Tn)` to a flattened `(..F[T1], ..., ..F[Tn])` */ + type FlatMap[Tup <: Tuple, F[_] <: Tuple] <: Tuple = Tup match { + case EmptyTuple => EmptyTuple + case h *: t => Concat[F[h], FlatMap[t, F]] + } + + /** Filters out those members of the tuple for which the predicate `P` returns `false`. + * A predicate `P[X]` is a type that can be either `true` or `false`. For example: + * ``` + * type IsString[x] = x match { + * case String => true + * case _ => false + * } + * Filter[(1, "foo", 2, "bar"), IsString] =:= ("foo", "bar") + * ``` + */ + type Filter[Tup <: Tuple, P[_] <: Boolean] <: Tuple = Tup match { + case EmptyTuple => EmptyTuple + case h *: t => P[h] match { + case true => h *: Filter[t, P] + case false => Filter[t, P] + } + } + + /** Given two tuples, `A1 *: ... *: An * At` and `B1 *: ... *: Bn *: Bt` + * where at least one of `At` or `Bt` is `EmptyTuple` or `Tuple`, + * returns the tuple type `(A1, B1) *: ... *: (An, Bn) *: Ct` + * where `Ct` is `EmptyTuple` if `At` or `Bt` is `EmptyTuple`, otherwise `Ct` is `Tuple`. + */ + type Zip[T1 <: Tuple, T2 <: Tuple] <: Tuple = (T1, T2) match { + case (h1 *: t1, h2 *: t2) => (h1, h2) *: Zip[t1, t2] + case (EmptyTuple, _) => EmptyTuple + case (_, EmptyTuple) => EmptyTuple + case _ => Tuple + } + + /** Converts a tuple `(F[T1], ..., F[Tn])` to `(T1, ... Tn)` */ + type InverseMap[X <: Tuple, F[_]] <: Tuple = X match { + case F[x] *: t => x *: InverseMap[t, F] + case EmptyTuple => EmptyTuple + } + + /** Implicit evidence. IsMappedBy[F][X] is present in the implicit scope iff + * X is a tuple for which each element's type is constructed via `F`. E.g. + * (F[A1], ..., F[An]), but not `(F[A1], B2, ..., F[An])` where B2 does not + * have the shape of `F[A]`. + */ + type IsMappedBy[F[_]] = [X <: Tuple] =>> X =:= Map[InverseMap[X, F], F] + + /** Transforms a tuple `(T1, ..., Tn)` into `(T1, ..., Ti)`. */ + type Take[T <: Tuple, N <: Int] <: Tuple = N match { + case 0 => EmptyTuple + case S[n1] => T match { + case EmptyTuple => EmptyTuple + case x *: xs => x *: Take[xs, n1] + } + } + + /** Transforms a tuple `(T1, ..., Tn)` into `(Ti+1, ..., Tn)`. */ + type Drop[T <: Tuple, N <: Int] <: Tuple = N match { + case 0 => T + case S[n1] => T match { + case EmptyTuple => EmptyTuple + case x *: xs => Drop[xs, n1] + } + } + + /** Splits a tuple (T1, ..., Tn) into a pair of two tuples `(T1, ..., Ti)` and + * `(Ti+1, ..., Tn)`. + */ + type Split[T <: Tuple, N <: Int] = (Take[T, N], Drop[T, N]) + + /** Given a tuple `(T1, ..., Tn)`, returns a union of its + * member types: `T1 | ... | Tn`. Returns `Nothing` if the tuple is empty. + */ + type Union[T <: Tuple] = Fold[T, Nothing, [x, y] =>> x | y] + + /** Empty tuple */ + def apply(): EmptyTuple = EmptyTuple + + /** Tuple with one element */ + def apply[T](x: T): T *: EmptyTuple = Tuple1(x) + + /** Matches an empty tuple. */ + def unapply(x: EmptyTuple): true = true + + /** 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 + case xs => xs.map(_.asInstanceOf[Object]) + } + runtime.Tuples.fromArray(xs2).asInstanceOf[Tuple] + } + + /** Convert an immutable array into a tuple of unknown arity and types */ + def fromIArray[T](xs: IArray[T]): Tuple = { + val xs2: IArray[Object] = xs match { + case xs: IArray[Object] @unchecked => xs + case xs => + // TODO support IArray.map + xs.asInstanceOf[Array[T]].map(_.asInstanceOf[Object]).asInstanceOf[IArray[Object]] + } + runtime.Tuples.fromIArray(xs2).asInstanceOf[Tuple] + } + + /** Convert a Product into a tuple of unknown arity and types */ + def fromProduct(product: Product): Tuple = + runtime.Tuples.fromProduct(product) + + def fromProductTyped[P <: Product](p: P)(using m: scala.deriving.Mirror.ProductOf[P]): m.MirroredElemTypes = + runtime.Tuples.fromProduct(p).asInstanceOf[m.MirroredElemTypes] +} + +/** A tuple of 0 elements */ +type EmptyTuple = EmptyTuple.type + +/** A tuple of 0 elements. */ +object EmptyTuple extends Tuple { + override def productArity: Int = 0 + + @throws(classOf[IndexOutOfBoundsException]) + override def productElement(n: Int): Any = + throw new IndexOutOfBoundsException(n.toString()) + + def canEqual(that: Any): Boolean = this == that + + override def toString(): String = "()" +} + +/** 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] = + runtime.Tuples.apply(this, n).asInstanceOf[Elem[This, n.type]] + + /** Get the head of this tuple */ + inline def head[This >: this.type <: NonEmptyTuple]: Head[This] = + runtime.Tuples.apply(this, 0).asInstanceOf[Head[This]] + + /** Get the tail of this tuple. + * This operation is O(this.size) + */ + inline def tail[This >: this.type <: NonEmptyTuple]: Tail[This] = + runtime.Tuples.tail(this).asInstanceOf[Tail[This]] + +} + +@showAsInfix +sealed abstract class *:[+H, +T <: Tuple] extends NonEmptyTuple + +object *: { + def unapply[H, T <: Tuple](x: H *: T): (H, T) = (x.head, x.tail) +} diff --git a/library/src/scala/Tuple.scala b/library/src-non-bootstrapped/scala/Tuple.scala similarity index 100% rename from library/src/scala/Tuple.scala rename to library/src-non-bootstrapped/scala/Tuple.scala diff --git a/library/src/scala/runtime/Tuple.scala b/library/src-non-bootstrapped/scala/runtime/Tuple.scala similarity index 100% rename from library/src/scala/runtime/Tuple.scala rename to library/src-non-bootstrapped/scala/runtime/Tuple.scala diff --git a/library/src/scala/runtime/Tuples.scala b/library/src/scala/runtime/Tuples.scala new file mode 100644 index 000000000000..57b404e79d5c --- /dev/null +++ b/library/src/scala/runtime/Tuples.scala @@ -0,0 +1,525 @@ +package scala.runtime + +object Tuples { + + inline val MaxSpecialized = 22 + + def toArray(self: Tuple): Array[Object] = (self: Any) match { + case EmptyTuple => Array.emptyObjectArray + case self: TupleXXL => self.toArray + case self: Product => productToArray(self) + } + + def toIArray(self: Tuple): IArray[Object] = (self: Any) match { + case EmptyTuple => Array.emptyObjectArray.asInstanceOf[IArray[Object]] + case self: TupleXXL => self.elems + case self: Product => productToArray(self).asInstanceOf[IArray[Object]] + } + + def productToArray(self: Product): Array[Object] = { + val arr = new Array[Object](self.productArity) + var i = 0 + while (i < arr.length) { + arr(i) = self.productElement(i).asInstanceOf[Object] + i += 1 + } + arr + } + + def fromArray(xs: Array[Object]): Tuple = xs.length match { + case 0 => EmptyTuple + case 1 => Tuple1(xs(0)) + case 2 => Tuple2(xs(0), xs(1)) + case 3 => Tuple3(xs(0), xs(1), xs(2)) + case 4 => Tuple4(xs(0), xs(1), xs(2), xs(3)) + case 5 => Tuple5(xs(0), xs(1), xs(2), xs(3), xs(4)) + case 6 => Tuple6(xs(0), xs(1), xs(2), xs(3), xs(4), xs(5)) + case 7 => Tuple7(xs(0), xs(1), xs(2), xs(3), xs(4), xs(5), xs(6)) + case 8 => Tuple8(xs(0), xs(1), xs(2), xs(3), xs(4), xs(5), xs(6), xs(7)) + case 9 => Tuple9(xs(0), xs(1), xs(2), xs(3), xs(4), xs(5), xs(6), xs(7), xs(8)) + case 10 => Tuple10(xs(0), xs(1), xs(2), xs(3), xs(4), xs(5), xs(6), xs(7), xs(8), xs(9)) + 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)) + 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)) + 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)) + 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)) + 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)) + 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)) + 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)) + 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)) + 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)) + 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)) + 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)) + 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)) + case _ => TupleXXL.fromIArray(xs.clone().asInstanceOf[IArray[Object]]).asInstanceOf[Tuple] + } + + def fromIArray(xs: IArray[Object]): Tuple = + if (xs.length <= 22) fromArray(xs.asInstanceOf[Array[Object]]) + else TupleXXL.fromIArray(xs).asInstanceOf[Tuple] + + def fromProduct(xs: Product): Tuple = (xs.productArity match { + case 1 => + xs match { + case xs: Tuple1[_] => xs + case xs => Tuple1(xs.productElement(0)) + } + case 2 => + xs match { + case xs: Tuple2[_, _] => xs + case xs => Tuple2(xs.productElement(0), xs.productElement(1)) + } + case 3 => + xs match { + case xs: Tuple3[_, _, _] => xs + case xs => Tuple3(xs.productElement(0), xs.productElement(1), xs.productElement(2)) + } + case 4 => + xs match { + case xs: Tuple4[_, _, _, _] => xs + case xs => Tuple4(xs.productElement(0), xs.productElement(1), xs.productElement(2), xs.productElement(3)) + } + case 5 => + xs match { + case xs: Tuple5[_, _, _, _, _] => xs + case xs => Tuple5(xs.productElement(0), xs.productElement(1), xs.productElement(2), xs.productElement(3), xs.productElement(4)) + } + case 6 => + xs match { + case xs: Tuple6[_, _, _, _, _, _] => xs + case xs => Tuple6(xs.productElement(0), xs.productElement(1), xs.productElement(2), xs.productElement(3), xs.productElement(4), xs.productElement(5)) + } + case 7 => + xs match { + case xs: Tuple7[_, _, _, _, _, _, _] => xs + case xs => Tuple7(xs.productElement(0), xs.productElement(1), xs.productElement(2), xs.productElement(3), xs.productElement(4), xs.productElement(5), xs.productElement(6)) + } + case 8 => + xs match { + case xs: Tuple8[_, _, _, _, _, _, _, _] => xs + case xs => Tuple8(xs.productElement(0), xs.productElement(1), xs.productElement(2), xs.productElement(3), xs.productElement(4), xs.productElement(5), xs.productElement(6), xs.productElement(7)) + } + case 9 => + xs match { + case xs: Tuple9[_, _, _, _, _, _, _, _, _] => xs + case xs => Tuple9(xs.productElement(0), xs.productElement(1), xs.productElement(2), xs.productElement(3), xs.productElement(4), xs.productElement(5), xs.productElement(6), xs.productElement(7), xs.productElement(8)) + } + case 10 => + xs match { + case xs: Tuple10[_, _, _, _, _, _, _, _, _, _] => xs + case xs => Tuple10(xs.productElement(0), xs.productElement(1), xs.productElement(2), xs.productElement(3), xs.productElement(4), xs.productElement(5), xs.productElement(6), xs.productElement(7), xs.productElement(8), xs.productElement(9)) + } + case 11 => + xs match { + case xs: Tuple11[_, _, _, _, _, _, _, _, _, _, _] => xs + case xs => Tuple11(xs.productElement(0), xs.productElement(1), xs.productElement(2), xs.productElement(3), xs.productElement(4), xs.productElement(5), xs.productElement(6), xs.productElement(7), xs.productElement(8), xs.productElement(9), xs.productElement(10)) + } + case 12 => + xs match { + case xs: Tuple12[_, _, _, _, _, _, _, _, _, _, _, _] => xs + case xs => Tuple12(xs.productElement(0), xs.productElement(1), xs.productElement(2), xs.productElement(3), xs.productElement(4), xs.productElement(5), xs.productElement(6), xs.productElement(7), xs.productElement(8), xs.productElement(9), xs.productElement(10), xs.productElement(11)) + } + case 13 => + xs match { + case xs: Tuple13[_, _, _, _, _, _, _, _, _, _, _, _, _] => xs + case xs => Tuple13(xs.productElement(0), xs.productElement(1), xs.productElement(2), xs.productElement(3), xs.productElement(4), xs.productElement(5), xs.productElement(6), xs.productElement(7), xs.productElement(8), xs.productElement(9), xs.productElement(10), xs.productElement(11), xs.productElement(12)) + } + case 14 => + xs match { + case xs: Tuple14[_, _, _, _, _, _, _, _, _, _, _, _, _, _] => xs + case xs => Tuple14(xs.productElement(0), xs.productElement(1), xs.productElement(2), xs.productElement(3), xs.productElement(4), xs.productElement(5), xs.productElement(6), xs.productElement(7), xs.productElement(8), xs.productElement(9), xs.productElement(10), xs.productElement(11), xs.productElement(12), xs.productElement(13)) + } + case 15 => + xs match { + case xs: Tuple15[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => xs + case xs => Tuple15(xs.productElement(0), xs.productElement(1), xs.productElement(2), xs.productElement(3), xs.productElement(4), xs.productElement(5), xs.productElement(6), xs.productElement(7), xs.productElement(8), xs.productElement(9), xs.productElement(10), xs.productElement(11), xs.productElement(12), xs.productElement(13), xs.productElement(14)) + } + case 16 => + xs match { + case xs: Tuple16[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => xs + case xs => Tuple16(xs.productElement(0), xs.productElement(1), xs.productElement(2), xs.productElement(3), xs.productElement(4), xs.productElement(5), xs.productElement(6), xs.productElement(7), xs.productElement(8), xs.productElement(9), xs.productElement(10), xs.productElement(11), xs.productElement(12), xs.productElement(13), xs.productElement(14), xs.productElement(15)) + } + case 17 => + xs match { + case xs: Tuple17[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => xs + case xs => Tuple17(xs.productElement(0), xs.productElement(1), xs.productElement(2), xs.productElement(3), xs.productElement(4), xs.productElement(5), xs.productElement(6), xs.productElement(7), xs.productElement(8), xs.productElement(9), xs.productElement(10), xs.productElement(11), xs.productElement(12), xs.productElement(13), xs.productElement(14), xs.productElement(15), xs.productElement(16)) + } + case 18 => + xs match { + case xs: Tuple18[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => xs + case xs => Tuple18(xs.productElement(0), xs.productElement(1), xs.productElement(2), xs.productElement(3), xs.productElement(4), xs.productElement(5), xs.productElement(6), xs.productElement(7), xs.productElement(8), xs.productElement(9), xs.productElement(10), xs.productElement(11), xs.productElement(12), xs.productElement(13), xs.productElement(14), xs.productElement(15), xs.productElement(16), xs.productElement(17)) + } + case 19 => + xs match { + case xs: Tuple19[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => xs + case xs => Tuple19(xs.productElement(0), xs.productElement(1), xs.productElement(2), xs.productElement(3), xs.productElement(4), xs.productElement(5), xs.productElement(6), xs.productElement(7), xs.productElement(8), xs.productElement(9), xs.productElement(10), xs.productElement(11), xs.productElement(12), xs.productElement(13), xs.productElement(14), xs.productElement(15), xs.productElement(16), xs.productElement(17), xs.productElement(18)) + } + case 20 => + xs match { + case xs: Tuple20[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => xs + case xs => Tuple20(xs.productElement(0), xs.productElement(1), xs.productElement(2), xs.productElement(3), xs.productElement(4), xs.productElement(5), xs.productElement(6), xs.productElement(7), xs.productElement(8), xs.productElement(9), xs.productElement(10), xs.productElement(11), xs.productElement(12), xs.productElement(13), xs.productElement(14), xs.productElement(15), xs.productElement(16), xs.productElement(17), xs.productElement(18), xs.productElement(19)) + } + case 21 => + xs match { + case xs: Tuple21[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => xs + case xs => Tuple21(xs.productElement(0), xs.productElement(1), xs.productElement(2), xs.productElement(3), xs.productElement(4), xs.productElement(5), xs.productElement(6), xs.productElement(7), xs.productElement(8), xs.productElement(9), xs.productElement(10), xs.productElement(11), xs.productElement(12), xs.productElement(13), xs.productElement(14), xs.productElement(15), xs.productElement(16), xs.productElement(17), xs.productElement(18), xs.productElement(19), xs.productElement(20)) + } + case 22 => + xs match { + case xs: Tuple22[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => xs + case xs => Tuple22(xs.productElement(0), xs.productElement(1), xs.productElement(2), xs.productElement(3), xs.productElement(4), xs.productElement(5), xs.productElement(6), xs.productElement(7), xs.productElement(8), xs.productElement(9), xs.productElement(10), xs.productElement(11), xs.productElement(12), xs.productElement(13), xs.productElement(14), xs.productElement(15), xs.productElement(16), xs.productElement(17), xs.productElement(18), xs.productElement(19), xs.productElement(20), xs.productElement(21)) + } + case _ => + (xs match { + case xs: TupleXXL => xs + case xs => TupleXXL.fromIArray(xs.productIterator.map(_.asInstanceOf[Object]).toArray.asInstanceOf[IArray[Object]]) // TODO use Iterator.toIArray + }).asInstanceOf[Tuple] + }) + + // Cons for Tuple1 to Tuple22 + private def specialCaseCons(x: Any, self: Tuple): Tuple = { + (self: Any) match { + case EmptyTuple => + Tuple1(x) + case self: Tuple1[_] => + Tuple2(x, self._1) + case self: Tuple2[_, _] => + Tuple3(x, self._1, self._2) + case self: Tuple3[_, _, _] => + Tuple4(x, self._1, self._2, self._3) + case self: Tuple4[_, _, _, _] => + Tuple5(x, self._1, self._2, self._3, self._4) + case self: Tuple5[_, _, _, _, _] => + Tuple6(x, self._1, self._2, self._3, self._4, self._5) + case self: Tuple6[_, _, _, _, _, _] => + Tuple7(x, self._1, self._2, self._3, self._4, self._5, self._6) + case self: Tuple7[_, _, _, _, _, _, _] => + Tuple8(x, self._1, self._2, self._3, self._4, self._5, self._6, self._7) + case self: Tuple8[_, _, _, _, _, _, _, _] => + Tuple9(x, self._1, self._2, self._3, self._4, self._5, self._6, self._7, self._8) + case self: Tuple9[_, _, _, _, _, _, _, _, _] => + Tuple10(x, self._1, self._2, self._3, self._4, self._5, self._6, self._7, self._8, self._9) + case self: Tuple10[_, _, _, _, _, _, _, _, _, _] => + Tuple11(x, self._1, self._2, self._3, self._4, self._5, self._6, self._7, self._8, self._9, self._10) + case self: Tuple11[_, _, _, _, _, _, _, _, _, _, _] => + Tuple12(x, self._1, self._2, self._3, self._4, self._5, self._6, self._7, self._8, self._9, self._10, self._11) + case self: Tuple12[_, _, _, _, _, _, _, _, _, _, _, _] => + Tuple13(x, self._1, self._2, self._3, self._4, self._5, self._6, self._7, self._8, self._9, self._10, self._11, self._12) + case self: Tuple13[_, _, _, _, _, _, _, _, _, _, _, _, _] => + Tuple14(x, self._1, self._2, self._3, self._4, self._5, self._6, self._7, self._8, self._9, self._10, self._11, self._12, self._13) + case self: Tuple14[_, _, _, _, _, _, _, _, _, _, _, _, _, _] => + Tuple15(x, self._1, self._2, self._3, self._4, self._5, self._6, self._7, self._8, self._9, self._10, self._11, self._12, self._13, self._14) + case self: Tuple15[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => + Tuple16(x, self._1, self._2, self._3, self._4, self._5, self._6, self._7, self._8, self._9, self._10, self._11, self._12, self._13, self._14, self._15) + case self: Tuple16[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => + Tuple17(x, self._1, self._2, self._3, self._4, self._5, self._6, self._7, self._8, self._9, self._10, self._11, self._12, self._13, self._14, self._15, self._16) + case self: Tuple17[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => + Tuple18(x, self._1, self._2, self._3, self._4, self._5, self._6, self._7, self._8, self._9, self._10, self._11, self._12, self._13, self._14, self._15, self._16, self._17) + case self: Tuple18[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => + Tuple19(x, self._1, self._2, self._3, self._4, self._5, self._6, self._7, self._8, self._9, self._10, self._11, self._12, self._13, self._14, self._15, self._16, self._17, self._18) + case self: Tuple19[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => + Tuple20(x, self._1, self._2, self._3, self._4, self._5, self._6, self._7, self._8, self._9, self._10, self._11, self._12, self._13, self._14, self._15, self._16, self._17, self._18, self._19) + case self: Tuple20[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => + Tuple21(x, self._1, self._2, self._3, self._4, self._5, self._6, self._7, self._8, self._9, self._10, self._11, self._12, self._13, self._14, self._15, self._16, self._17, self._18, self._19, self._20) + case self: Tuple21[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => + Tuple22(x, self._1, self._2, self._3, self._4, self._5, self._6, self._7, self._8, self._9, self._10, self._11, self._12, self._13, self._14, self._15, self._16, self._17, self._18, self._19, self._20, self._21) + case self: Tuple22[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => + val arr: Array[Object] = Array( + x.asInstanceOf[Object], self._1.asInstanceOf[Object], self._2.asInstanceOf[Object], + self._3.asInstanceOf[Object], self._4.asInstanceOf[Object], self._5.asInstanceOf[Object], + self._6.asInstanceOf[Object], self._7.asInstanceOf[Object], self._8.asInstanceOf[Object], + self._9.asInstanceOf[Object], self._10.asInstanceOf[Object], self._11.asInstanceOf[Object], + self._12.asInstanceOf[Object], self._13.asInstanceOf[Object], self._14.asInstanceOf[Object], + self._15.asInstanceOf[Object], self._16.asInstanceOf[Object], self._17.asInstanceOf[Object], + self._18.asInstanceOf[Object], self._19.asInstanceOf[Object], self._20.asInstanceOf[Object], + self._21.asInstanceOf[Object], self._22.asInstanceOf[Object], + ) + TupleXXL.fromIArray(arr.asInstanceOf[IArray[Object]]).asInstanceOf[Tuple] + } + } + + // Cons for TupleXXL + private def xxlCons(x: Any, xxl: TupleXXL): TupleXXL = { + val arr = new Array[Object](xxl.productArity + 1) + arr(0) = x.asInstanceOf[Object] + System.arraycopy(xxl.elems, 0, arr, 1, xxl.productArity) + TupleXXL.fromIArray(arr.asInstanceOf[IArray[Object]]) + } + + def cons(x: Any, self: Tuple): Tuple = (self: Any) match { + case xxl: TupleXXL => xxlCons(x, xxl).asInstanceOf[Tuple] + case _ => specialCaseCons(x, self) + } + + def concat[This <: Tuple, That <: Tuple](self: This, that: That): Tuple = { + val selfSize: Int = self.size + // If one of the tuples is empty, we can leave early + if selfSize == 0 then + return that + + val thatSize: Int = that.size + if thatSize == 0 then + return self + + val arr = new Array[Object](selfSize + thatSize) + + // Copies the tuple to an array, at the given offset + inline def copyToArray[T <: Tuple](tuple: T, size: Int, array: Array[Object], offset: Int): Unit = (tuple: Any) match { + case xxl: TupleXXL => + System.arraycopy(xxl.elems, 0, array, offset, size) + case _ => + tuple.productIterator.asInstanceOf[Iterator[Object]] + .copyToArray(array, offset, size) + } + + // In the general case, we copy the two tuples to an array, and convert it back to a tuple + copyToArray(self, selfSize, arr, 0) + copyToArray(that, thatSize, arr, selfSize) + fromIArray(arr.asInstanceOf[IArray[Object]]) + } + + def size(self: Tuple): Int = (self: Any) match { + case EmptyTuple => 0 + case self: Product => self.productArity + } + + // Tail for Tuple1 to Tuple22 + private def specialCaseTail(self: Tuple): Tuple = { + (self: Any) match { + case self: Tuple1[_] => + EmptyTuple + case self: Tuple2[_, _] => + Tuple1(self._2) + case self: Tuple3[_, _, _] => + Tuple2(self._2, self._3) + case self: Tuple4[_, _, _, _] => + Tuple3(self._2, self._3, self._4) + case self: Tuple5[_, _, _, _, _] => + Tuple4(self._2, self._3, self._4, self._5) + case self: Tuple6[_, _, _, _, _, _] => + Tuple5(self._2, self._3, self._4, self._5, self._6) + case self: Tuple7[_, _, _, _, _, _, _] => + Tuple6(self._2, self._3, self._4, self._5, self._6, self._7) + case self: Tuple8[_, _, _, _, _, _, _, _] => + Tuple7(self._2, self._3, self._4, self._5, self._6, self._7, self._8) + case self: Tuple9[_, _, _, _, _, _, _, _, _] => + Tuple8(self._2, self._3, self._4, self._5, self._6, self._7, self._8, self._9) + case self: Tuple10[_, _, _, _, _, _, _, _, _, _] => + Tuple9(self._2, self._3, self._4, self._5, self._6, self._7, self._8, self._9, self._10) + case self: Tuple11[_, _, _, _, _, _, _, _, _, _, _] => + Tuple10(self._2, self._3, self._4, self._5, self._6, self._7, self._8, self._9, self._10, self._11) + case self: Tuple12[_, _, _, _, _, _, _, _, _, _, _, _] => + Tuple11(self._2, self._3, self._4, self._5, self._6, self._7, self._8, self._9, self._10, self._11, self._12) + case self: Tuple13[_, _, _, _, _, _, _, _, _, _, _, _, _] => + Tuple12(self._2, self._3, self._4, self._5, self._6, self._7, self._8, self._9, self._10, self._11, self._12, self._13) + case self: Tuple14[_, _, _, _, _, _, _, _, _, _, _, _, _, _] => + Tuple13(self._2, self._3, self._4, self._5, self._6, self._7, self._8, self._9, self._10, self._11, self._12, self._13, self._14) + case self: Tuple15[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => + Tuple14(self._2, self._3, self._4, self._5, self._6, self._7, self._8, self._9, self._10, self._11, self._12, self._13, self._14, self._15) + case self: Tuple16[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => + Tuple15(self._2, self._3, self._4, self._5, self._6, self._7, self._8, self._9, self._10, self._11, self._12, self._13, self._14, self._15, self._16) + case self: Tuple17[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => + Tuple16(self._2, self._3, self._4, self._5, self._6, self._7, self._8, self._9, self._10, self._11, self._12, self._13, self._14, self._15, self._16, self._17) + case self: Tuple18[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => + Tuple17(self._2, self._3, self._4, self._5, self._6, self._7, self._8, self._9, self._10, self._11, self._12, self._13, self._14, self._15, self._16, self._17, self._18) + case self: Tuple19[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => + Tuple18(self._2, self._3, self._4, self._5, self._6, self._7, self._8, self._9, self._10, self._11, self._12, self._13, self._14, self._15, self._16, self._17, self._18, self._19) + case self: Tuple20[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => + Tuple19(self._2, self._3, self._4, self._5, self._6, self._7, self._8, self._9, self._10, self._11, self._12, self._13, self._14, self._15, self._16, self._17, self._18, self._19, self._20) + case self: Tuple21[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => + Tuple20(self._2, self._3, self._4, self._5, self._6, self._7, self._8, self._9, self._10, self._11, self._12, self._13, self._14, self._15, self._16, self._17, self._18, self._19, self._20, self._21) + case self: Tuple22[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => + Tuple21(self._2, self._3, self._4, self._5, self._6, self._7, self._8, self._9, self._10, self._11, self._12, self._13, self._14, self._15, self._16, self._17, self._18, self._19, self._20, self._21, self._22) + } + } + + // Tail for TupleXXL + private def xxlTail(xxl: TupleXXL): Tuple = { + if (xxl.productArity == 23) { + val elems = xxl.elems + Tuple22( + elems(1), elems(2), elems(3), elems(4), elems(5), elems(6), elems(7), + elems(8), elems(9), elems(10), elems(11), elems(12), elems(13), elems(14), + elems(15), elems(16), elems(17), elems(18), elems(19), elems(20), + elems(21), elems(22) + ) + } else { + val arr = new Array[Object](xxl.elems.length - 1) + System.arraycopy(xxl.elems, 1, arr, 0, xxl.elems.length - 1) + TupleXXL.fromIArray(arr.asInstanceOf[IArray[Object]]).asInstanceOf[Tuple] + } + } + + def tail(self: NonEmptyTuple): Tuple = (self: Any) match { + case xxl: TupleXXL => xxlTail(xxl) + case _ => specialCaseTail(self) + } + + def apply(self: NonEmptyTuple, n: Int): Any = + self.productElement(n) + + // Benchmarks showed that this is faster than doing (it1 zip it2).copyToArray(...) + private def zipIterators(it1: Iterator[Any], it2: Iterator[Any], size: Int): IArray[Object] = { + val arr = new Array[Object](size) + var i = 0 + while (i < size) { + arr(i) = (it1.next(), it2.next()) + i += 1 + } + arr.asInstanceOf[IArray[Object]] + } + + def zip(t1: Tuple, t2: Tuple): Tuple = { + val t1Size: Int = t1.size + val t2Size: Int = t2.size + val size = Math.min(t1Size, t2Size) + if size == 0 then EmptyTuple + else Tuple.fromIArray( + zipIterators( + t1.productIterator, + t2.productIterator, + size + ) + ) + } + + def map[F[_]](self: Tuple, f: [t] => t => F[t]): Tuple = self match { + case EmptyTuple => self + case _ => fromIArray(self.productIterator.map(f(_).asInstanceOf[Object]).toArray.asInstanceOf[IArray[Object]]) // TODO use toIArray + } + + def take(self: Tuple, n: Int): Tuple = { + if (n < 0) throw new IndexOutOfBoundsException(n.toString) + val selfSize: Int = self.size + val actualN = Math.min(n, selfSize) + + if (actualN == 0) EmptyTuple + else { + val arr = (self: Any) match { + case xxl: TupleXXL => + xxl.elems.asInstanceOf[Array[Object]].take(actualN) + case _ => + val arr = new Array[Object](actualN) + self.productIterator.asInstanceOf[Iterator[Object]] + .copyToArray(arr, 0, actualN) + arr + } + + fromIArray(arr.asInstanceOf[IArray[Object]]) + } + } + + def drop(self: Tuple, n: Int): Tuple = { + if (n < 0) throw new IndexOutOfBoundsException(n.toString) + val size = self.size + val actualN = Math.min(n, size) + val rem = size - actualN + + if (rem == 0) EmptyTuple + else { + val arr = (self: Any) match { + case xxl: TupleXXL => + xxl.elems.asInstanceOf[Array[Object]].drop(actualN) + case _ => + val arr = new Array[Object](rem) + self.productIterator.asInstanceOf[Iterator[Object]] + .drop(actualN).copyToArray(arr, 0, rem) + arr + } + + fromIArray(arr.asInstanceOf[IArray[Object]]) + } + } + + def splitAt(self: Tuple, n: Int): (Tuple, Tuple) = { + if (n < 0) throw new IndexOutOfBoundsException(n.toString) + val size = self.size + val actualN = Math.min(n, size) + val (arr1, arr2) = (self: Any) match { + case EmptyTuple => (Array.empty[Object], Array.empty[Object]) + case xxl: TupleXXL => + xxl.elems.asInstanceOf[Array[Object]].splitAt(actualN) + case _ => + val arr1 = new Array[Object](actualN) + val arr2 = new Array[Object](size - actualN) + val it = self.productIterator.asInstanceOf[Iterator[Object]] + it.copyToArray(arr1, 0, actualN) + it.copyToArray(arr2, 0, size - actualN) + (arr1, arr2) + } + + ( + fromIArray(arr1.asInstanceOf[IArray[Object]]), + fromIArray(arr2.asInstanceOf[IArray[Object]]) + ) + } + + def consIterator(head: Any, tail: Tuple): Iterator[Any] = + Iterator.single(head) ++ tail.productIterator + + def concatIterator(tup1: Tuple, tup2: Tuple): Iterator[Any] = + tup1.productIterator ++ tup2.productIterator + + def isInstanceOfTuple(x: Any): Boolean = + x match + case x: Product => + x.productArity match + case 0 => x == EmptyTuple + case 1 => x.isInstanceOf[Tuple1[_]] + case 2 => x.isInstanceOf[Tuple2[_, _]] + case 3 => x.isInstanceOf[Tuple3[_, _, _]] + case 4 => x.isInstanceOf[Tuple4[_, _, _, _]] + case 5 => x.isInstanceOf[Tuple5[_, _, _, _, _]] + case 6 => x.isInstanceOf[Tuple6[_, _, _, _, _, _]] + case 7 => x.isInstanceOf[Tuple7[_, _, _, _, _, _, _]] + case 8 => x.isInstanceOf[Tuple8[_, _, _, _, _, _, _, _]] + case 9 => x.isInstanceOf[Tuple9[_, _, _, _, _, _, _, _, _]] + case 10 => x.isInstanceOf[Tuple10[_, _, _, _, _, _, _, _, _, _]] + case 11 => x.isInstanceOf[Tuple11[_, _, _, _, _, _, _, _, _, _, _]] + case 12 => x.isInstanceOf[Tuple12[_, _, _, _, _, _, _, _, _, _, _, _]] + case 13 => x.isInstanceOf[Tuple13[_, _, _, _, _, _, _, _, _, _, _, _, _]] + case 14 => x.isInstanceOf[Tuple14[_, _, _, _, _, _, _, _, _, _, _, _, _, _]] + case 15 => x.isInstanceOf[Tuple15[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _]] + case 16 => x.isInstanceOf[Tuple16[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]] + case 17 => x.isInstanceOf[Tuple17[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]] + case 18 => x.isInstanceOf[Tuple18[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]] + case 19 => x.isInstanceOf[Tuple19[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]] + case 20 => x.isInstanceOf[Tuple20[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]] + case 21 => x.isInstanceOf[Tuple21[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]] + case 22 => x.isInstanceOf[Tuple22[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]] + case _ => x.isInstanceOf[TupleXXL] + case _ => + false + + def isInstanceOfEmptyTuple(x: Any): Boolean = x == EmptyTuple + + def isInstanceOfNonEmptyTuple(x: Any): Boolean = + x match + case x: Product => + x.productArity match + case 1 => x.isInstanceOf[Tuple1[_]] + case 2 => x.isInstanceOf[Tuple2[_, _]] + case 3 => x.isInstanceOf[Tuple3[_, _, _]] + case 4 => x.isInstanceOf[Tuple4[_, _, _, _]] + case 5 => x.isInstanceOf[Tuple5[_, _, _, _, _]] + case 6 => x.isInstanceOf[Tuple6[_, _, _, _, _, _]] + case 7 => x.isInstanceOf[Tuple7[_, _, _, _, _, _, _]] + case 8 => x.isInstanceOf[Tuple8[_, _, _, _, _, _, _, _]] + case 9 => x.isInstanceOf[Tuple9[_, _, _, _, _, _, _, _, _]] + case 10 => x.isInstanceOf[Tuple10[_, _, _, _, _, _, _, _, _, _]] + case 11 => x.isInstanceOf[Tuple11[_, _, _, _, _, _, _, _, _, _, _]] + case 12 => x.isInstanceOf[Tuple12[_, _, _, _, _, _, _, _, _, _, _, _]] + case 13 => x.isInstanceOf[Tuple13[_, _, _, _, _, _, _, _, _, _, _, _, _]] + case 14 => x.isInstanceOf[Tuple14[_, _, _, _, _, _, _, _, _, _, _, _, _, _]] + case 15 => x.isInstanceOf[Tuple15[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _]] + case 16 => x.isInstanceOf[Tuple16[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]] + case 17 => x.isInstanceOf[Tuple17[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]] + case 18 => x.isInstanceOf[Tuple18[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]] + case 19 => x.isInstanceOf[Tuple19[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]] + case 20 => x.isInstanceOf[Tuple20[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]] + case 21 => x.isInstanceOf[Tuple21[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]] + case 22 => x.isInstanceOf[Tuple22[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]] + case _ => x.isInstanceOf[TupleXXL] + case _ => + false + +} diff --git a/tests/run-staging/staged-tuples/StagedTuple.scala b/tests/run-staging/staged-tuples/StagedTuple.scala index 74933f6ace69..1a1f10b47eea 100644 --- a/tests/run-staging/staged-tuples/StagedTuple.scala +++ b/tests/run-staging/staged-tuples/StagedTuple.scala @@ -4,7 +4,7 @@ import scala.quoted._ import scala.runtime.TupleXXL -import scala.runtime.Tuple.MaxSpecialized +import scala.runtime.Tuples.MaxSpecialized object StagedTuple { import Tuple.Concat @@ -16,7 +16,7 @@ object StagedTuple { private final val specialize = true def toArrayStaged(tup: Expr[Tuple], size: Option[Int])(using Quotes): Expr[Array[Object]] = { - if (!specialize) '{scala.runtime.Tuple.toArray($tup)} + if (!specialize) '{scala.runtime.Tuples.toArray($tup)} else size match { case Some(0) => '{Array.emptyObjectArray} @@ -31,12 +31,12 @@ object StagedTuple { case Some(n) => '{ ${tup.as[TupleXXL]}.toArray } case None => - '{scala.runtime.Tuple.toArray($tup)} + '{scala.runtime.Tuples.toArray($tup)} } } def fromArrayStaged[T <: Tuple : Type](xs: Expr[Array[Object]], size: Option[Int])(using Quotes): Expr[T] = { - if (!specialize) '{scala.runtime.Tuple.fromArray($xs)}.as[T] + if (!specialize) '{scala.runtime.Tuples.fromArray($xs)}.as[T] else xs.bind { xs => val tup: Expr[Any] = size match { case Some(0) => '{Tuple()} @@ -63,7 +63,7 @@ object StagedTuple { case Some(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))} case Some(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))} case Some(_) => '{TupleXXL($xs)} - case None => '{scala.runtime.Tuple.fromArray($xs)} + case None => '{scala.runtime.Tuples.fromArray($xs)} } tup.as[T] } @@ -71,16 +71,16 @@ object StagedTuple { def sizeStaged[Res <: Int : Type](tup: Expr[Tuple], size: Option[Int])(using Quotes): Expr[Res] = { val res = - if (!specialize) '{scala.runtime.Tuple.size($tup)} + if (!specialize) '{scala.runtime.Tuples.size($tup)} else size match { case Some(n) => Expr(n) - case None => '{scala.runtime.Tuple.size($tup)} + case None => '{scala.runtime.Tuples.size($tup)} } res.as[Res] } def headStaged[Tup <: NonEmptyTuple : Type](tup: Expr[Tup], size: Option[Int])(using Quotes): Expr[Head[Tup]] = { - if (!specialize) '{scala.runtime.Tuple.apply($tup, 0)}.as[Head[Tup]] + if (!specialize) '{scala.runtime.Tuples.apply($tup, 0)}.as[Head[Tup]] else { val resVal = size match { case Some(1) => @@ -96,14 +96,14 @@ object StagedTuple { case Some(n) if n > MaxSpecialized => '{${tup.as[TupleXXL] }.elems(0)} case None => - '{scala.runtime.Tuple.apply($tup, 0)} + '{scala.runtime.Tuples.apply($tup, 0)} } resVal.as[Head[Tup]] } } def tailStaged[Tup <: NonEmptyTuple : Type](tup: Expr[Tup], size: Option[Int])(using Quotes): Expr[Tail[Tup]] = { - if (!specialize) '{scala.runtime.Tuple.tail($tup)}.as[Tail[Tup]] + if (!specialize) '{scala.runtime.Tuples.tail($tup)}.as[Tail[Tup]] else { val res = size match { case Some(1) => @@ -120,7 +120,7 @@ object StagedTuple { val arr = toArrayStaged(tup, size) fromArrayStaged[Tail[Tup]]('{ $arr.tail }, Some(n - 1)) case None => - '{scala.runtime.Tuple.tail($tup)} + '{scala.runtime.Tuples.tail($tup)} } res.as[Tail[Tup]] } @@ -128,13 +128,13 @@ object StagedTuple { def applyStaged[Tup <: NonEmptyTuple : Type, N <: Int : Type](tup: Expr[Tup], size: Option[Int], n: Expr[N], nValue: Option[Int])(using Quotes): Expr[Elem[Tup, N]] = { - if (!specialize) '{scala.runtime.Tuple.apply($tup, $n)}.as[Elem[Tup, N]] + if (!specialize) '{scala.runtime.Tuples.apply($tup, $n)}.as[Elem[Tup, N]] else { def fallbackApply(): Expr[Elem[Tup, N]] = nValue match { case Some(n) => quotes.reflect.report.error("index out of bounds: " + n, tup) '{ throw new IndexOutOfBoundsException(${Expr(n.toString)}) } - case None => '{scala.runtime.Tuple.apply($tup, $n)}.as[Elem[Tup, N]] + case None => '{scala.runtime.Tuples.apply($tup, $n)}.as[Elem[Tup, N]] } val res = size match { case Some(1) => @@ -186,7 +186,7 @@ object StagedTuple { } def consStaged[T <: Tuple & Singleton : Type, H : Type](self: Expr[T], x: Expr[H], tailSize: Option[Int])(using Quotes): Expr[H *: T] = - if (!specialize) '{scala.runtime.Tuple.cons($x, $self)}.as[H *: T] + if (!specialize) '{scala.runtime.Tuples.cons($x, $self)}.as[H *: T] else { val res = tailSize match { case Some(0) => @@ -200,13 +200,13 @@ object StagedTuple { case Some(4) => self.as[Tuple4[_, _, _, _]].bind(t => '{Tuple5($x, $t._1, $t._2, $t._3, $t._4)}) case _ => - '{scala.runtime.Tuple.cons($x, $self)} + '{scala.runtime.Tuples.cons($x, $self)} } 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])(using Quotes): Expr[Concat[Self, That]] = { - if (!specialize) '{scala.runtime.Tuple.concat($self, $that)}.as[Concat[Self, That]] + if (!specialize) '{scala.runtime.Tuples.concat($self, $that)}.as[Concat[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 => - '{scala.runtime.Tuple.concat($self, $that)} + '{scala.runtime.Tuples.concat($self, $that)} } res.as[Concat[Self, That]] }