Skip to content

Commit 6980b2e

Browse files
authored
correctly type Expr.ofTupleFromSeq for arity > 22 (#17261)
identified that for all `Seq` with arity <= 22, the result had the most precise type possible, for > 22 then it was only `Tuple` fixes #17257
2 parents ad5eb6c + b3cfa95 commit 6980b2e

File tree

3 files changed

+89
-1
lines changed

3 files changed

+89
-1
lines changed

library/src/scala/quoted/Expr.scala

+13-1
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ object Expr {
103103
case 20 => ofTupleFromSeq20(seq)
104104
case 21 => ofTupleFromSeq21(seq)
105105
case 22 => ofTupleFromSeq22(seq)
106-
case _ => '{ Tuple.fromIArray(IArray(${Varargs(seq)}: _*)) }
106+
case _ => ofTupleFromSeqXXL(seq)
107107
}
108108
}
109109

@@ -214,6 +214,18 @@ object Expr {
214214
case Seq('{ $x1: t1 }, '{ $x2: t2 }, '{ $x3: t3 }, '{ $x4: t4 }, '{ $x5: t5 }, '{ $x6: t6 }, '{ $x7: t7 }, '{ $x8: t8 }, '{ $x9: t9 }, '{ $x10: t10 }, '{ $x11: t11 }, '{ $x12: t12 }, '{ $x13: t13 }, '{ $x14: t14 }, '{ $x15: t15 }, '{ $x16: t16 }, '{ $x17: t17 }, '{ $x18: t18 }, '{ $x19: t19 }, '{ $x20: t20 }, '{ $x21: t21 }, '{ $x22: t22 }) =>
215215
'{ Tuple22($x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11, $x12, $x13, $x14, $x15, $x16, $x17, $x18, $x19, $x20, $x21, $x22) }
216216

217+
private def ofTupleFromSeqXXL(seq: Seq[Expr[Any]])(using Quotes): Expr[Tuple] =
218+
val tupleTpe = tupleTypeFromSeq(seq)
219+
tupleTpe.asType match
220+
case '[tpe] =>
221+
'{ Tuple.fromIArray(IArray(${Varargs(seq)}*)).asInstanceOf[tpe & Tuple] }
222+
223+
private def tupleTypeFromSeq(seq: Seq[Expr[Any]])(using Quotes): quotes.reflect.TypeRepr =
224+
import quotes.reflect.*
225+
val consRef = Symbol.classSymbol("scala.*:").typeRef
226+
seq.foldLeft(TypeRepr.of[EmptyTuple]) { (ts, expr) =>
227+
AppliedType(consRef, expr.asTerm.tpe :: ts :: Nil)
228+
}
217229

218230
/** Given a tuple of the form `(Expr[A1], ..., Expr[An])`, outputs a tuple `Expr[(A1, ..., An)]`. */
219231
def ofTuple[T <: Tuple: Tuple.IsMappedBy[Expr]: Type](tup: T)(using Quotes): Expr[Tuple.InverseMap[T, Expr]] = {

tests/run-macros/i17257/a.scala

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package derivation
2+
import scala.quoted.*
3+
4+
import scala.annotation.tailrec
5+
6+
object Helpers:
7+
8+
// file a.scala
9+
inline def summonAllOptimized[T <: Tuple]: T =
10+
${ summonAllOptimizedImpl[T] }
11+
12+
inline def summon23[E]: (E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E) =
13+
${ summon23Impl[E] }
14+
15+
private def summonAllOptimizedImpl[T <: Tuple: Type](using q: Quotes): Expr[T] = {
16+
import q.reflect.*
17+
18+
Expr
19+
.ofTupleFromSeq(typesOfTuple(TypeRepr.of[T], Nil).map { tpe =>
20+
tpe.asType match {
21+
case '[t] =>
22+
Expr.summon[t].getOrElse(report.errorAndAbort(s"Unable to to find implicit instance for ${tpe.show}"))
23+
}
24+
})
25+
.asExprOf[T]
26+
}
27+
28+
private def summon23Impl[E: Type](using q: Quotes): Expr[(E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E)] = {
29+
import q.reflect.*
30+
31+
val e = Expr.summon[E].getOrElse(report.errorAndAbort(s"Unable to to find implicit instance for ${TypeRepr.of[E].show}"))
32+
33+
val tuple = (e, e, e, e, e, e, e, e, e, e, e, e, e, e, e, e, e, e, e, e, e, e, e)
34+
35+
assert(tuple.size == 23)
36+
37+
Expr.ofTuple(tuple)
38+
}
39+
40+
@tailrec
41+
private[derivation] def typesOfTuple(
42+
using q: Quotes
43+
)(tpe: q.reflect.TypeRepr, acc: List[q.reflect.TypeRepr]): List[q.reflect.TypeRepr] =
44+
import q.reflect.*
45+
val cons = Symbol.classSymbol("scala.*:")
46+
tpe.widenTermRefByName.dealias match
47+
case AppliedType(fn, tpes) if defn.isTupleClass(fn.typeSymbol) =>
48+
tpes.reverse_:::(acc)
49+
case AppliedType(tp, List(headType, tailType)) if tp.derivesFrom(cons) =>
50+
typesOfTuple(tailType, headType :: acc)
51+
case tpe =>
52+
if tpe.derivesFrom(Symbol.classSymbol("scala.EmptyTuple")) then acc.reverse
53+
else report.errorAndAbort(s"Unknown type encountered in tuple ${tpe.show}")

tests/run-macros/i17257/b.scala

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package derivation {
2+
//file b.scala
3+
val test = Helpers.summonAllOptimized[(
4+
ValueOf["a"], ValueOf["a"], ValueOf["a"], ValueOf["a"], ValueOf["a"],
5+
ValueOf["a"], ValueOf["a"], ValueOf["a"], ValueOf["a"], ValueOf["a"],
6+
ValueOf["a"], ValueOf["a"], ValueOf["a"], ValueOf["a"], ValueOf["a"],
7+
ValueOf["a"], ValueOf["a"], ValueOf["a"], ValueOf["a"], ValueOf["a"],
8+
ValueOf["a"], ValueOf["a"], ValueOf["a"] //Commenting out the last one here fixes the compile error
9+
)]
10+
val test2 = Helpers.summon23[ValueOf["a"]]
11+
}
12+
@main def Test =
13+
def assertions(list: List[ValueOf["a"]]): Unit =
14+
assert(list.size == 23)
15+
assert(list.map(_.value) == List(
16+
"a", "a", "a", "a", "a",
17+
"a", "a", "a", "a", "a",
18+
"a", "a", "a", "a", "a",
19+
"a", "a", "a", "a", "a",
20+
"a", "a", "a"
21+
))
22+
assertions(derivation.test.toList)
23+
assertions(derivation.test2.toList)

0 commit comments

Comments
 (0)