diff --git a/library/src/scala/Tuple.scala b/library/src/scala/Tuple.scala index 78b2196217aa..a17e73c509be 100644 --- a/library/src/scala/Tuple.scala +++ b/library/src/scala/Tuple.scala @@ -143,6 +143,18 @@ object Tuple { } } + /** + * Use this type to widen a self-type to a tuple. E.g. + * ``` + * val x: (1, 3) = (1, 3) + * val y: Widen[x.type] = x + * ``` + */ + type Widen[Tup <: Tuple] <: Tuple = Tup match { + case EmptyTuple => EmptyTuple + case h *: t => h *: t + } + /** 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` diff --git a/library/src/scala/compiletime/package.scala b/library/src/scala/compiletime/package.scala index dbc60f3b9298..39cd810d30ac 100644 --- a/library/src/scala/compiletime/package.scala +++ b/library/src/scala/compiletime/package.scala @@ -4,6 +4,16 @@ import scala.quoted._ package object compiletime { + /** Use this method when you have a type, do not have a value for it but want to + * pattern match on it. For example, given a type `Tup <: Tuple`, one can + * pattern-match on it as follows: + * ``` + * erasedValue[Tup] match { + * case _: EmptyTuple => ... + * case _: h *: t => ... + * } + * ``` + */ erased def erasedValue[T]: T = ??? /** The error method is used to produce user-defined compile errors during inline expansion. @@ -38,10 +48,29 @@ package object compiletime { transparent inline def (inline self: StringContext) code (inline args: Any*): String = ${ dotty.internal.CompileTimeMacros.codeExpr('self, 'args) } + /** Same as `constValue` but returns a `None` if a constant value + * cannot be constructed from the provided type. Otherwise returns + * that value wrapped in `Some`. + */ inline def constValueOpt[T]: Option[T] = ??? + /** Given a constant, singleton type `T`, convert it to a value + * of the same singleton type. For example: `assert(constValue[1] == 1)`. + */ inline def constValue[T]: T = ??? + /** Given a tuple type `(X1, ..., Xn)`, returns a tuple value + * `(constValue[X1], ..., constValue[Xn])`. + */ + inline def constValueTuple[T <: Tuple]: Tuple.Widen[T]= + val res = + inline erasedValue[T] match + case _: EmptyTuple => EmptyTuple + case _: (t *: ts) => constValue[t] *: constValueTuple[ts] + end match + res.asInstanceOf[Tuple.Widen[T]] + end constValueTuple + /** Summons first given matching one of the listed cases. E.g. in * * given B { ... } @@ -68,6 +97,20 @@ package object compiletime { case t: T => t } + /** Given a tuple T, summons each of its member types and returns them in + * a Tuple. + * + * @tparam T the tuple containing the types of the values to be summoned + * @return the given values typed as elements of the tuple + */ + inline def summonAll[T <: Tuple]: Tuple.Widen[T] = + val res = + inline erasedValue[T] match + case _: EmptyTuple => EmptyTuple + case _: (t *: ts) => summonInline[t] *: summonAll[ts] + end match + res.asInstanceOf[Tuple.Widen[T]] + end summonAll /** Succesor of a natural number where zero is the type 0 and successors are reduced as if the definition was * diff --git a/tests/run/constValueTuple.check b/tests/run/constValueTuple.check new file mode 100644 index 000000000000..264d259343f4 --- /dev/null +++ b/tests/run/constValueTuple.check @@ -0,0 +1 @@ +(foo,bar,10,2.5) diff --git a/tests/run/constValueTuple.scala b/tests/run/constValueTuple.scala new file mode 100644 index 000000000000..ab9467adb714 --- /dev/null +++ b/tests/run/constValueTuple.scala @@ -0,0 +1,4 @@ +import compiletime.constValueTuple + +@main def Test = + println(constValueTuple["foo" *: "bar" *: 10 *: 2.5 *: EmptyTuple]) diff --git a/tests/run/summonAll.check b/tests/run/summonAll.check new file mode 100644 index 000000000000..c4bbadaa6ae8 --- /dev/null +++ b/tests/run/summonAll.check @@ -0,0 +1 @@ +(10,foo,1.2) diff --git a/tests/run/summonAll.scala b/tests/run/summonAll.scala new file mode 100644 index 000000000000..b4638d7e6ee6 --- /dev/null +++ b/tests/run/summonAll.scala @@ -0,0 +1,7 @@ +import compiletime.summonAll + +@main def Test = + given as Int = 10 + given as String = "foo" + given as Double = 1.2 + println(summonAll[Int *: String *: Double *: EmptyTuple])