Skip to content

Commit 071c720

Browse files
Merge pull request #12423 from dotty-staging/add-Type-valueOfTuple
Add quoted.Type.valueOfTuple
2 parents 1bedf7a + d359fc8 commit 071c720

File tree

7 files changed

+90
-7
lines changed

7 files changed

+90
-7
lines changed

library/src/scala/quoted/Type.scala

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package scala.quoted
22

3-
import scala.annotation.compileTimeOnly
3+
import scala.annotation.{compileTimeOnly, experimental}
44

55
/** Type (or type constructor) `T` needed contextually when using `T` in a quoted expression `'{... T ...}` */
66
abstract class Type[T <: AnyKind] private[scala]:
@@ -35,11 +35,50 @@ object Type:
3535
* @syntax markdown
3636
*/
3737
def valueOfConstant[T](using Type[T])(using Quotes): Option[T] =
38+
ValueOf.unapply(quotes.reflect.TypeRepr.of[T]).asInstanceOf[Option[T]]
39+
40+
/** Extracts the value of a tuple of singleton constant types.
41+
* Returns Some of the tuple type if it is a tuple singleton constant types.
42+
* Returns None if the type is not a tuple singleton constant types.
43+
*
44+
* Example usage:
45+
* ```scala
46+
* ... match
47+
* case '{ $mirrorExpr : Mirror.Sum { type MirroredElemLabels = label } } =>
48+
* Type.valueOfTuple[label] // Option[Tuple]
49+
* }
50+
* ```
51+
* @syntax markdown
52+
*/
53+
@experimental
54+
def valueOfTuple[T <: Tuple](using Type[T])(using Quotes): Option[T] =
55+
valueOfTuple(quotes.reflect.TypeRepr.of[T]).asInstanceOf[Option[T]]
56+
57+
private def valueOfTuple(using Quotes)(tpe: quotes.reflect.TypeRepr): Option[Tuple] =
3858
import quotes.reflect.*
39-
def valueOf(tpe: TypeRepr): Option[T] =
40-
tpe.dealias.widenTermRefByName match
41-
case ConstantType(const) => Some(const.value.asInstanceOf[T])
59+
val cons = Symbol.classSymbol("scala.*:")
60+
def rec(tpe: TypeRepr): Option[Tuple] =
61+
tpe.widenTermRefByName.dealias match
62+
case AppliedType(fn, tpes) if defn.isTupleClass(fn.typeSymbol) =>
63+
tpes.foldRight(Option[Tuple](EmptyTuple)) {
64+
case (_, None) => None
65+
case (ValueOf(v), Some(acc)) => Some(v *: acc)
66+
case _ => None
67+
}
68+
case AppliedType(tp, List(ValueOf(headValue), tail)) if tp.derivesFrom(cons) =>
69+
rec(tail) match
70+
case Some(tailValue) => Some(headValue *: tailValue)
71+
case None => None
72+
case tpe =>
73+
if tpe.derivesFrom(Symbol.classSymbol("scala.EmptyTuple")) then Some(EmptyTuple)
74+
else None
75+
rec(tpe)
76+
77+
private object ValueOf:
78+
def unapply(using Quotes)(tpe: quotes.reflect.TypeRepr): Option[Any] =
79+
import quotes.reflect.*
80+
tpe.widenTermRefByName.dealias match
81+
case ConstantType(const) => Some(const.value)
4282
case _ => None
43-
valueOf(TypeRepr.of[T])
4483

4584
end Type

project/MiMaFilters.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ object MiMaFilters {
1414
exclude[DirectMissingMethodProblem]("scala.quoted.Quotes#reflectModule#SymbolMethods.typeMember"),
1515
exclude[DirectMissingMethodProblem]("scala.quoted.Quotes#reflectModule#SymbolMethods.typeMembers"),
1616
exclude[DirectMissingMethodProblem]("scala.quoted.Quotes#reflectModule#TermParamClauseMethods.isErased"),
17-
exclude[DirectMissingMethodProblem]("scala.quoted.Quotes#reflectModule#TermParamClauseMethods.isErased"),
18-
exclude[MissingClassProblem]("scala.annotation.experimental"),
17+
exclude[DirectMissingMethodProblem]("scala.quoted.Type.valueOfTuple"),
1918
exclude[MissingClassProblem]("scala.annotation.experimental"),
2019
exclude[MissingClassProblem]("scala.annotation.internal.ErasedParam"),
2120
exclude[MissingClassProblem]("scala.annotation.internal.ErasedParam"),
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import scala.deriving.Mirror
2+
import scala.compiletime.{constValue, error}
3+
import scala.quoted.*
4+
5+
object TestMacro {
6+
inline def test1[CASE_CLASS <: Product](using m: Mirror.ProductOf[CASE_CLASS]): String =
7+
${ code('m) }
8+
9+
def code[CASE_CLASS <: Product: Type](m: Expr[Mirror.ProductOf[CASE_CLASS]])(using Quotes): Expr[String] =
10+
m match
11+
case '{ type t <: Tuple; $_ : Mirror { type MirroredElemLabels = `t` } } =>
12+
Expr(Type.valueOfTuple[t].toString)
13+
}

tests/run-macros/i12417/Test_2.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import scala.deriving.Mirror
2+
import scala.compiletime.{constValue, error}
3+
4+
object Test extends App {
5+
case class A(x: String, y: Int)
6+
assert(TestMacro.test1[A] == "Some((x,y))")
7+
}

tests/run-macros/i12417b.check

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Some(())
2+
Some((1))
3+
Some((1,2))
4+
Some((1,2,3))
5+
Some((1,2,3,4))
6+
Some((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,26))
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import scala.deriving.Mirror
2+
import scala.compiletime.{constValue, error}
3+
import scala.quoted.*
4+
5+
object TestMacro {
6+
inline def test1: Unit =
7+
${ code() }
8+
9+
def code()(using Quotes): Expr[Unit] =
10+
'{
11+
println(${Expr(Type.valueOfTuple[EmptyTuple].toString)})
12+
println(${Expr(Type.valueOfTuple[1 *: EmptyTuple].toString)})
13+
println(${Expr(Type.valueOfTuple[(1, 2)].toString)})
14+
println(${Expr(Type.valueOfTuple[(1, 2, 3)].toString)})
15+
println(${Expr(Type.valueOfTuple[(1, 2, 3, 4)].toString)})
16+
println(${Expr(Type.valueOfTuple[(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, 26)].toString)})
17+
}
18+
}

tests/run-macros/i12417b/Test_2.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@main def Test = TestMacro.test1

0 commit comments

Comments
 (0)