diff --git a/build.sbt b/build.sbt index 4331173..25a47b1 100644 --- a/build.sbt +++ b/build.sbt @@ -45,6 +45,15 @@ lazy val core = crossProject(JVMPlatform, JSPlatform, NativePlatform) ) lazy val coreJVM = core.jvm + .settings( + libraryDependencies ++= + (if (scalaVersion.value.startsWith("2.")) + Seq( + "org.scala-lang" % "scala-reflect" % scalaVersion.value % Test, + "org.scala-lang" % "scala-compiler" % scalaVersion.value % Test + ) + else Nil) + ) lazy val coreJS = core.js .disablePlugins(DoctestPlugin) diff --git a/core/jvm/src/test/scala-2/org/typelevel/twiddles/test/CompilationPerformanceSpec.scala b/core/jvm/src/test/scala-2/org/typelevel/twiddles/test/CompilationPerformanceSpec.scala new file mode 100644 index 0000000..2fbd97b --- /dev/null +++ b/core/jvm/src/test/scala-2/org/typelevel/twiddles/test/CompilationPerformanceSpec.scala @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2023, Typelevel + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.typelevel.twiddles.test + +import munit.FunSuite +import org.typelevel.twiddles.Tuple + +import scala.concurrent._ +import scala.concurrent.duration._ +import scala.reflect.runtime.currentMirror +import scala.reflect.runtime.universe._ +import scala.tools.reflect.ToolBox + +class CompilationPerformanceSpec extends FunSuite { + private implicit val ec: ExecutionContext = scala.concurrent.ExecutionContext.global + override val munitTimeout: Duration = 30.seconds + + test("should compile long twiddles in a reasonable amount of time") { + val compiled = compileWithin( + q"""import org.typelevel.twiddles._ + 1 *: 1 *: 1 *: 1 *: 1 *: 1 *: 1 *: 1 *: + 1 *: 1 *: 1 *: 1 *: 1 *: 1 *: 1 *: 1 *: + 1 *: 1 *: 1 *: 1 *: 1 *: 1 *: 1 *: 1 *: + 1 *: 1 *: 1 *: 1 *: 1 *: 1 *: 1 *: 1 *: + 1 *: 1 *: 1 *: 1 *: 1 *: 1 *: 1 *: 1 *: + 1 *: 1 *: 1 *: 1 *: 1 *: 1 *: 1 *: 1 *: + 1 *: 1 *: 1 *: 1 *: 1 *: 1 *: 1 *: 1 *: + 1 *: 1 *: 1 *: 1 *: 1 *: 1 *: 1 *: 1 *: + EmptyTuple""", + 10.seconds + ) + + assert(compiled.isInstanceOf[Tuple]) + } + + private val toolbox = { + val toolbox = currentMirror.mkToolBox() + // warmup + toolbox.compile(q"") + toolbox + } + + private def compileWithin(t: Tree, atMost: Duration)(implicit ec: ExecutionContext) = + Await.result(Future(toolbox.compile(t)()), atMost) +} diff --git a/core/shared/src/main/scala-2/org/typelevel/twiddles/TwiddleCompat.scala b/core/shared/src/main/scala-2/org/typelevel/twiddles/TwiddleCompat.scala index f93d462..4c84c52 100644 --- a/core/shared/src/main/scala-2/org/typelevel/twiddles/TwiddleCompat.scala +++ b/core/shared/src/main/scala-2/org/typelevel/twiddles/TwiddleCompat.scala @@ -31,6 +31,7 @@ package org.typelevel.twiddles import shapeless.{::, HList, HNil} +import shapeless.ops.hlist.Tupler /** Mix-in trait that provides source compatibility between Scala 2 tuples and Twiddles. */ trait TwiddleCompat { @@ -115,7 +116,80 @@ trait TwiddleCompat { ): A *: B *: C *: D *: E *: F *: G *: H *: I *: J *: K *: L *: M *: N *: O *: P *: Q *: R *: S *: T *: U *: V *: EmptyTuple = t.productElements - implicit def hlistToTuple[L <: Tuple, T](l: L)(implicit + // format: off + implicit def hlistToTuple0[Z <: Tuple](z: Z)(implicit + tupler: shapeless.ops.hlist.Tupler.Aux[Z, Unit] + ): Unit = tupler(z) + implicit def hlistToTuple1[Z <: Tuple, A](z: Z)(implicit + tupler: shapeless.ops.hlist.Tupler.Aux[Z, Tuple1[A]] + ): Tuple1[A] = tupler(z) + implicit def hlistToTuple2[Z <: Tuple, A, B](z: Z)(implicit + tupler: Tupler.Aux[Z, (A, B)] + ): (A, B) = tupler(z) + implicit def hlistToTuple3[Z <: Tuple, A, B, C](z: Z)(implicit + tupler: Tupler.Aux[Z, (A, B, C)] + ): (A, B, C) = tupler(z) + implicit def hlistToTuple4[Z <: Tuple, A, B, C, D](z: Z)(implicit + tupler: Tupler.Aux[Z, (A, B, C, D)] + ): (A, B, C, D) = tupler(z) + implicit def hlistToTuple5[Z <: Tuple, A, B, C, D, E](z: Z)(implicit + tupler: Tupler.Aux[Z, (A, B, C, D, E)] + ): (A, B, C, D, E) = tupler(z) + implicit def hlistToTuple6[Z <: Tuple, A, B, C, D, E, F](z: Z)(implicit + tupler: Tupler.Aux[Z, (A, B, C, D, E, F)] + ): (A, B, C, D, E, F) = tupler(z) + implicit def hlistToTuple7[Z <: Tuple, A, B, C, D, E, F, G](z: Z)(implicit + tupler: Tupler.Aux[Z, (A, B, C, D, E, F, G)] + ): (A, B, C, D, E, F, G) = tupler(z) + implicit def hlistToTuple8[Z <: Tuple, A, B, C, D, E, F, G, H](z: Z)(implicit + tupler: Tupler.Aux[Z, (A, B, C, D, E, F, G, H)] + ): (A, B, C, D, E, F, G, H) = tupler(z) + implicit def hlistToTuple9[Z <: Tuple, A, B, C, D, E, F, G, H, I](z: Z)(implicit + tupler: Tupler.Aux[Z, (A, B, C, D, E, F, G, H, I)] + ): (A, B, C, D, E, F, G, H, I) = tupler(z) + implicit def hlistToTuple10[Z <: Tuple, A, B, C, D, E, F, G, H, I, J](z: Z)(implicit + tupler: Tupler.Aux[Z, (A, B, C, D, E, F, G, H, I, J)] + ): (A, B, C, D, E, F, G, H, I, J) = tupler(z) + implicit def hlistToTuple11[Z <: Tuple, A, B, C, D, E, F, G, H, I, J, K](z: Z)(implicit + tupler: Tupler.Aux[Z, (A, B, C, D, E, F, G, H, I, J, K)] + ): (A, B, C, D, E, F, G, H, I, J, K) = tupler(z) + implicit def hlistToTuple12[Z <: Tuple, A, B, C, D, E, F, G, H, I, J, K, L](z: Z)(implicit + tupler: Tupler.Aux[Z, (A, B, C, D, E, F, G, H, I, J, K, L)] + ): (A, B, C, D, E, F, G, H, I, J, K, L) = tupler(z) + implicit def hlistToTuple13[Z <: Tuple, A, B, C, D, E, F, G, H, I, J, K, L, M](z: Z)(implicit + tupler: Tupler.Aux[Z, (A, B, C, D, E, F, G, H, I, J, K, L, M)] + ): (A, B, C, D, E, F, G, H, I, J, K, L, M) = tupler(z) + implicit def hlistToTuple14[Z <: Tuple, A, B, C, D, E, F, G, H, I, J, K, L, M, N](z: Z)(implicit + tupler: Tupler.Aux[Z, (A, B, C, D, E, F, G, H, I, J, K, L, M, N)] + ): (A, B, C, D, E, F, G, H, I, J, K, L, M, N) = tupler(z) + implicit def hlistToTuple15[Z <: Tuple, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O](z: Z)(implicit + tupler: Tupler.Aux[Z, (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O)] + ): (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O) = tupler(z) + implicit def hlistToTuple16[Z <: Tuple, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P](z: Z)(implicit + tupler: Tupler.Aux[Z, (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P)] + ): (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P) = tupler(z) + implicit def hlistToTuple17[Z <: Tuple, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q](z: Z)(implicit + tupler: Tupler.Aux[Z, (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q)] + ): (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q) = tupler(z) + implicit def hlistToTuple18[Z <: Tuple, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R](z: Z)(implicit + tupler: Tupler.Aux[Z, (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R)] + ): (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R) = tupler(z) + implicit def hlistToTuple19[Z <: Tuple, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S](z: Z)(implicit + tupler: Tupler.Aux[Z, (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S)] + ): (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S) = tupler(z) + implicit def hlistToTuple20[Z <: Tuple, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T](z: Z)(implicit + tupler: Tupler.Aux[Z, (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T)] + ): (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T) = tupler(z) + implicit def hlistToTuple21[Z <: Tuple, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U](z: Z)(implicit + tupler: Tupler.Aux[Z, (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U)] + ): (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U) = tupler(z) + implicit def hlistToTuple22[Z <: Tuple, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V](z: Z)(implicit + tupler: Tupler.Aux[Z, (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V)] + ): (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V) = tupler(z) + // format: on + + // Unused but kept for binary compatibility + def hlistToTuple[L <: Tuple, T](l: L)(implicit tupler: shapeless.ops.hlist.Tupler.Aux[L, T] ): T = l.tupled }