Skip to content

Commit ac7a2b0

Browse files
Merge pull request #7047 from dotty-staging/liftable-derivation
toExprOfTuple method with precise types
2 parents 96b0854 + a581172 commit ac7a2b0

File tree

8 files changed

+87
-1
lines changed

8 files changed

+87
-1
lines changed

compiler/test/dotc/pos-test-pickling.blacklist

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,6 @@ i7087.scala
2525

2626
# Opaque type
2727
i5720.scala
28+
29+
# Tuples
30+
toexproftuple.scala

library/src-bootstrapped/scala/quoted/package.scala

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,12 @@ package object quoted {
9595
'{ Tuple.fromIArray(IArray(${seq.toExprOfSeq}: _*)) }
9696
}
9797
}
98-
}
9998

99+
/** Given a tuple of the form `(Expr[A1], ..., Expr[An])`, outputs a tuple `Expr[(A1, ..., An)]`. */
100+
def (tup: T) toExprOfTuple[T <: Tuple: Tuple.IsMappedBy[Expr]: Type] given (ctx: QuoteContext): Expr[Tuple.InverseMap[T, Expr]] = {
101+
import ctx.tasty._
102+
tup.asInstanceOf[Product].productIterator.toSeq.asInstanceOf[Seq[Expr[_]]].toExprOfTuple
103+
.cast[Tuple.InverseMap[T, Expr]]
104+
}
105+
}
100106
}

library/src/scala/Tuple.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,19 @@ object Tuple {
103103
case _ => Tuple
104104
}
105105

106+
/** Converts a tuple `(F[T1], ..., F[Tn])` to `(T1, ... Tn)` */
107+
type InverseMap[X <: Tuple, F[_]] <: Tuple = X match {
108+
case F[x] *: t => x *: InverseMap[t, F]
109+
case Unit => Unit
110+
}
111+
112+
/** Implicit evidence. IsMappedBy[F][X] is present in the implicit scope iff
113+
* X is a tuple for which each element's type is constructed via `F`. E.g.
114+
* (F[A1], ..., F[An]), but not `(F[A1], B2, ..., F[An])` where B2 does not
115+
* have the shape of `F[A]`.
116+
*/
117+
type IsMappedBy[F[_]] = [X <: Tuple] =>> X =:= Map[InverseMap[X, F], F]
118+
106119
/** Convert an array into a tuple of unknown arity and types */
107120
def fromArray[T](xs: Array[T]): Tuple = {
108121
val xs2 = xs match {

tests/neg/toexproftuple.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import scala.quoted._, scala.deriving._
2+
import given scala.quoted._
3+
4+
inline def mcr: Any = ${mcrImpl}
5+
def mcrImpl given (ctx: QuoteContext): Expr[Any] = {
6+
val tpl: (Expr[1], Expr[2], Expr[3]) = ('{1}, '{2}, '{3})
7+
'{val res: (1, 3, 3) = ${tpl.toExprOfTuple}; res} // error
8+
9+
val tpl2: (Expr[1], 2, Expr[3]) = ('{1}, 2, '{3})
10+
'{val res = ${tpl2.toExprOfTuple}; res} // error
11+
}

tests/neg/tuple-isMappedBy-2.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import scala.Tuple.IsMappedBy
2+
3+
object Test2 {
4+
def test0[F[_], T: IsMappedBy[F]]: Unit = () // error: Type argument T does not conform to upper bound Tuple
5+
}

tests/neg/tuple-isMappedBy.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import scala.Tuple.IsMappedBy
2+
3+
object Test1 {
4+
5+
def test[F[_], T <: Tuple: IsMappedBy[F]]: Unit = ()
6+
7+
test[List, (List[Int], Char)] // error
8+
test[List, (List[Int], Seq[Char])] // error
9+
test[Seq, (List[Int], Seq[Char])] // error
10+
test[List, Tuple] // error
11+
test[List, Nothing] // error
12+
13+
}

tests/pos/toexproftuple.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import scala.quoted._, scala.deriving._
2+
import given scala.quoted._
3+
4+
inline def mcr: Any = ${mcrImpl}
5+
def mcrImpl given (ctx: QuoteContext): Expr[Any] = {
6+
val tpl: (Expr[1], Expr[2], Expr[3]) = ('{1}, '{2}, '{3})
7+
'{val res: (1, 2, 3) = ${tpl.toExprOfTuple}; res}
8+
}

tests/pos/tuple-isMappedBy.scala

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
2+
import scala.Tuple.IsMappedBy
3+
4+
object Test {
5+
6+
def test[F[_], T <: Tuple: IsMappedBy[F]]: Unit = ()
7+
8+
test[[X] =>> X, Unit]
9+
test[[X] =>> X, (Int, Long)]
10+
11+
test[List, Unit]
12+
test[List, (List[Int], List[Long])]
13+
14+
trait A[+X]
15+
trait B[-X]
16+
trait C[X]
17+
18+
test[A, Unit]
19+
test[A, (A[Int], A[Long])]
20+
21+
test[B, Unit]
22+
test[B, (B[Int], B[Long])]
23+
24+
test[C, Unit]
25+
test[C, (C[Int], C[Long])]
26+
27+
}

0 commit comments

Comments
 (0)