diff --git a/library/src/scala/Tuple.scala b/library/src/scala/Tuple.scala index fa72e320b560..2d9aaea2be08 100644 --- a/library/src/scala/Tuple.scala +++ b/library/src/scala/Tuple.scala @@ -1,6 +1,6 @@ package scala -import annotation.showAsInfix +import annotation.{experimental, showAsInfix} import compiletime._ import compiletime.ops.int._ @@ -78,6 +78,13 @@ sealed trait Tuple extends Product { */ inline def splitAt[This >: this.type <: Tuple](n: Int): Split[This, n.type] = runtime.Tuples.splitAt(this, n).asInstanceOf[Split[This, n.type]] + + /** Given a tuple `(a1, ..., am)`, returns the reversed tuple `(am, ..., a1)` + * consisting all its elements. + */ + @experimental + inline def reverse[This >: this.type <: Tuple]: Reverse[This] = + runtime.Tuples.reverse(this).asInstanceOf[Reverse[This]] } object Tuple { @@ -193,6 +200,19 @@ object Tuple { */ type IsMappedBy[F[_]] = [X <: Tuple] =>> X =:= Map[InverseMap[X, F], F] + /** Type of the reversed tuple */ + @experimental + type Reverse[X <: Tuple] = Helpers.ReverseImpl[EmptyTuple, X] + + @experimental + object Helpers: + + /** Type of the reversed tuple */ + @experimental + type ReverseImpl[Acc <: Tuple, X <: Tuple] <: Tuple = X match + case x *: xs => ReverseImpl[x *: Acc, xs] + case EmptyTuple => Acc + /** Transforms a tuple `(T1, ..., Tn)` into `(T1, ..., Ti)`. */ type Take[T <: Tuple, N <: Int] <: Tuple = N match { case 0 => EmptyTuple @@ -297,7 +317,6 @@ sealed trait NonEmptyTuple extends Tuple { */ inline def tail[This >: this.type <: NonEmptyTuple]: Tail[This] = runtime.Tuples.tail(this).asInstanceOf[Tail[This]] - } @showAsInfix diff --git a/library/src/scala/runtime/Tuples.scala b/library/src/scala/runtime/Tuples.scala index c16d8c2a201a..87682ed86ce5 100644 --- a/library/src/scala/runtime/Tuples.scala +++ b/library/src/scala/runtime/Tuples.scala @@ -1,5 +1,7 @@ package scala.runtime +import scala.annotation.experimental + object Tuples { inline val MaxSpecialized = 22 @@ -447,6 +449,68 @@ object Tuples { } } + // Reverse for TupleXXL + private def xxlReverse(xxl: TupleXXL): Tuple = + TupleXXL.fromIArray(xxl.elems.reverse.asInstanceOf[IArray[Object]]).asInstanceOf[Tuple] + + // Reverse for Tuple0 to Tuple22 + private def specialCaseReverse(self: Tuple): Tuple = { + (self: Any) match { + case EmptyTuple => + EmptyTuple + case self: Tuple1[_] => + self + case self: Tuple2[_, _] => + Tuple2(self._2, self._1) + case self: Tuple3[_, _, _] => + Tuple3(self._3, self._2, self._1) + case self: Tuple4[_, _, _, _] => + Tuple4(self._4, self._3, self._2, self._1) + case self: Tuple5[_, _, _, _, _] => + Tuple5(self._5, self._4, self._3, self._2, self._1) + case self: Tuple6[_, _, _, _, _, _] => + Tuple6(self._6, self._5, self._4, self._3, self._2, self._1) + case self: Tuple7[_, _, _, _, _, _, _] => + Tuple7(self._7, self._6, self._5, self._4, self._3, self._2, self._1) + case self: Tuple8[_, _, _, _, _, _, _, _] => + Tuple8(self._8, self._7, self._6, self._5, self._4, self._3, self._2, self._1) + case self: Tuple9[_, _, _, _, _, _, _, _, _] => + Tuple9(self._9, self._8, self._7, self._6, self._5, self._4, self._3, self._2, self._1) + case self: Tuple10[_, _, _, _, _, _, _, _, _, _] => + Tuple10(self._10, self._9, self._8, self._7, self._6, self._5, self._4, self._3, self._2, self._1) + case self: Tuple11[_, _, _, _, _, _, _, _, _, _, _] => + Tuple11(self._11, self._10, self._9, self._8, self._7, self._6, self._5, self._4, self._3, self._2, self._1) + case self: Tuple12[_, _, _, _, _, _, _, _, _, _, _, _] => + Tuple12(self._12, self._11, self._10, self._9, self._8, self._7, self._6, self._5, self._4, self._3, self._2, self._1) + case self: Tuple13[_, _, _, _, _, _, _, _, _, _, _, _, _] => + Tuple13(self._13, self._12, self._11, self._10, self._9, self._8, self._7, self._6, self._5, self._4, self._3, self._2, self._1) + case self: Tuple14[_, _, _, _, _, _, _, _, _, _, _, _, _, _] => + Tuple14(self._14, self._13, self._12, self._11, self._10, self._9, self._8, self._7, self._6, self._5, self._4, self._3, self._2, self._1) + case self: Tuple15[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => + Tuple15(self._15, self._14, self._13, self._12, self._11, self._10, self._9, self._8, self._7, self._6, self._5, self._4, self._3, self._2, self._1) + case self: Tuple16[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => + Tuple16(self._16, self._15, self._14, self._13, self._12, self._11, self._10, self._9, self._8, self._7, self._6, self._5, self._4, self._3, self._2, self._1) + case self: Tuple17[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => + Tuple17(self._17, self._16, self._15, self._14, self._13, self._12, self._11, self._10, self._9, self._8, self._7, self._6, self._5, self._4, self._3, self._2, self._1) + case self: Tuple18[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => + Tuple18(self._18, self._17, self._16, self._15, self._14, self._13, self._12, self._11, self._10, self._9, self._8, self._7, self._6, self._5, self._4, self._3, self._2, self._1) + case self: Tuple19[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => + Tuple19(self._19, self._18, self._17, self._16, self._15, self._14, self._13, self._12, self._11, self._10, self._9, self._8, self._7, self._6, self._5, self._4, self._3, self._2, self._1) + case self: Tuple20[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => + Tuple20(self._20, self._19, self._18, self._17, self._16, self._15, self._14, self._13, self._12, self._11, self._10, self._9, self._8, self._7, self._6, self._5, self._4, self._3, self._2, self._1) + case self: Tuple21[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => + Tuple21(self._21, self._20, self._19, self._18, self._17, self._16, self._15, self._14, self._13, self._12, self._11, self._10, self._9, self._8, self._7, self._6, self._5, self._4, self._3, self._2, self._1) + case self: Tuple22[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => + Tuple22(self._22, self._21, self._20, self._19, self._18, self._17, self._16, self._15, self._14, self._13, self._12, self._11, self._10, self._9, self._8, self._7, self._6, self._5, self._4, self._3, self._2, self._1) + } + } + + @experimental + def reverse(self: Tuple): Tuple = (self: Any) match { + case xxl: TupleXXL => xxlReverse(xxl) + case _ => specialCaseReverse(self) + } + // Init for Tuple1 to Tuple22 private def specialCaseInit(self: Tuple): Tuple = { (self: Any) match { diff --git a/project/MiMaFilters.scala b/project/MiMaFilters.scala index a7d449c48ebc..ae135528a834 100644 --- a/project/MiMaFilters.scala +++ b/project/MiMaFilters.scala @@ -13,6 +13,8 @@ object MiMaFilters { ProblemFilters.exclude[MissingClassProblem]("scala.runtime.stdLibPatches.language$experimental$clauseInterleaving$"), ProblemFilters.exclude[MissingFieldProblem]("scala.runtime.stdLibPatches.language#experimental.relaxedExtensionImports"), ProblemFilters.exclude[MissingClassProblem]("scala.runtime.stdLibPatches.language$experimental$relaxedExtensionImports$"), + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.Tuples.reverse"), + ProblemFilters.exclude[MissingFieldProblem]("scala.Tuple.Helpers"), // end of New experimental features in 3.3.X ) val TastyCore: Seq[ProblemFilter] = Seq( diff --git a/tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala b/tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala index 4860d19dbc8b..e2499ef48883 100644 --- a/tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala +++ b/tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala @@ -99,7 +99,15 @@ val experimentalDefinitionInLibrary = Set( "scala.quoted.Quotes.reflectModule.MethodTypeMethods.hasErasedParams", "scala.quoted.Quotes.reflectModule.TermParamClauseMethods.erasedArgs", "scala.quoted.Quotes.reflectModule.TermParamClauseMethods.hasErasedArgs", - "scala.quoted.Quotes.reflectModule.defnModule.ErasedFunctionClass" + "scala.quoted.Quotes.reflectModule.defnModule.ErasedFunctionClass", + + // New feature: reverse method on Tuple + "scala.Tuple.reverse", + "scala.Tuple$.Helpers", + "scala.Tuple$.Helpers$", + "scala.Tuple$.Helpers$.ReverseImpl", + "scala.Tuple$.Reverse", + "scala.runtime.Tuples$.reverse" ) diff --git a/tests/run-deep-subtype/Tuple-reverse.check b/tests/run-deep-subtype/Tuple-reverse.check new file mode 100644 index 000000000000..e889132e88a5 --- /dev/null +++ b/tests/run-deep-subtype/Tuple-reverse.check @@ -0,0 +1,78 @@ +(0) +(0,0) +(1,0,0) +(2,1,0,0) +(3,2,1,0,0) +(4,3,2,1,0,0) +(5,4,3,2,1,0,0) +(6,5,4,3,2,1,0,0) +(7,6,5,4,3,2,1,0,0) +(8,7,6,5,4,3,2,1,0,0) +(9,8,7,6,5,4,3,2,1,0,0) +(10,9,8,7,6,5,4,3,2,1,0,0) +(11,10,9,8,7,6,5,4,3,2,1,0,0) +(12,11,10,9,8,7,6,5,4,3,2,1,0,0) +(13,12,11,10,9,8,7,6,5,4,3,2,1,0,0) +(14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,0) +(15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,0) +(16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,0) +(17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,0) +(18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,0) +(19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,0) +(20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,0) +(21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,0) +(22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,0) +(23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,0) +(24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,0) +() +(1) +(2,1) +(3,2,1) +(4,3,2,1) +(5,4,3,2,1) +(6,5,4,3,2,1) +(7,6,5,4,3,2,1) +(8,7,6,5,4,3,2,1) +(9,8,7,6,5,4,3,2,1) +(10,9,8,7,6,5,4,3,2,1) +(11,10,9,8,7,6,5,4,3,2,1) +(12,11,10,9,8,7,6,5,4,3,2,1) +(13,12,11,10,9,8,7,6,5,4,3,2,1) +(14,13,12,11,10,9,8,7,6,5,4,3,2,1) +(15,14,13,12,11,10,9,8,7,6,5,4,3,2,1) +(16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1) +(17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1) +(18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1) +(19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1) +(20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1) +(21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1) +(22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1) +(23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1) +(24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1) +(25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1) +() +(1) +(2,1) +(3,2,1) +(4,3,2,1) +(5,4,3,2,1) +(6,5,4,3,2,1) +(7,6,5,4,3,2,1) +(8,7,6,5,4,3,2,1) +(9,8,7,6,5,4,3,2,1) +(10,9,8,7,6,5,4,3,2,1) +(11,10,9,8,7,6,5,4,3,2,1) +(12,11,10,9,8,7,6,5,4,3,2,1) +(13,12,11,10,9,8,7,6,5,4,3,2,1) +(14,13,12,11,10,9,8,7,6,5,4,3,2,1) +(15,14,13,12,11,10,9,8,7,6,5,4,3,2,1) +(16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1) +(17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1) +(18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1) +(19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1) +(20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1) +(21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1) +(22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1) +(23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1) +(24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1) +(25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1) diff --git a/tests/run-deep-subtype/Tuple-reverse.scala b/tests/run-deep-subtype/Tuple-reverse.scala new file mode 100644 index 000000000000..b0461d167fab --- /dev/null +++ b/tests/run-deep-subtype/Tuple-reverse.scala @@ -0,0 +1,73 @@ +import scala.reflect.ClassTag + +object Test { + def main(args: Array[String]): Unit = { + + def testArray[T: ClassTag](n: Int, elem: Int => T): Unit = { + val t: Int *: Tuple = 0 *: Tuple.fromArray(Array.tabulate(n)(elem)) + println(t.reverse) + } + + for (i <- 0 to 25) + testArray(i, j => j) + + val tuple: Tuple3[Int, Boolean, String] = (1, true, "hello") + val reversedTuple: Tuple3[String, Boolean, Int] = tuple.reverse + + assert(reversedTuple == ("hello", true, 1)) + + println(EmptyTuple.reverse) + println(Tuple1(1).reverse) + println((1, 2).reverse) + println((1, 2, 3).reverse) + println((1, 2, 3, 4).reverse) + println((1, 2, 3, 4, 5).reverse) + println((1, 2, 3, 4, 5, 6).reverse) + println((1, 2, 3, 4, 5, 6, 7).reverse) + println((1, 2, 3, 4, 5, 6, 7, 8).reverse) + println((1, 2, 3, 4, 5, 6, 7, 8, 9).reverse) + println((1, 2, 3, 4, 5, 6, 7, 8, 9, 10).reverse) + println((1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11).reverse) + println((1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12).reverse) + println((1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13).reverse) + println((1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14).reverse) + println((1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15).reverse) + println((1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16).reverse) + println((1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17).reverse) + println((1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18).reverse) + println((1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19).reverse) + println((1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20).reverse) + println((1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21).reverse) + println((1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22).reverse) + println((1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23).reverse) + 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).reverse) + 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).reverse) + + println(Tuple().reverse) + println((1 *: Tuple()).reverse) + println((1 *: 2 *: Tuple()).reverse) + println((1 *: 2 *: 3 *: Tuple()).reverse) + println((1 *: 2 *: 3 *: 4 *: Tuple()).reverse) + println((1 *: 2 *: 3 *: 4 *: 5 *: Tuple()).reverse) + println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: Tuple()).reverse) + println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: Tuple()).reverse) + println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: Tuple()).reverse) + println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: Tuple()).reverse) + println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: Tuple()).reverse) + println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: Tuple()).reverse) + println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: Tuple()).reverse) + println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: Tuple()).reverse) + println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: Tuple()).reverse) + println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: Tuple()).reverse) + println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: Tuple()).reverse) + println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: Tuple()).reverse) + println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: Tuple()).reverse) + println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: Tuple()).reverse) + println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: Tuple()).reverse) + println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: Tuple()).reverse) + println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: 22 *: Tuple()).reverse) + println((1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: 22 *: 23 *: Tuple()).reverse) + 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 *: Tuple()).reverse) + 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 *: Tuple()).reverse) + } +}