Skip to content

Commit

Permalink
correctly type Expr.ofTupleFromSeq for arity > 22 (#17261)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
bishabosha authored Apr 17, 2023
2 parents ad5eb6c + b3cfa95 commit 6980b2e
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 1 deletion.
14 changes: 13 additions & 1 deletion library/src/scala/quoted/Expr.scala
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ object Expr {
case 20 => ofTupleFromSeq20(seq)
case 21 => ofTupleFromSeq21(seq)
case 22 => ofTupleFromSeq22(seq)
case _ => '{ Tuple.fromIArray(IArray(${Varargs(seq)}: _*)) }
case _ => ofTupleFromSeqXXL(seq)
}
}

Expand Down Expand Up @@ -214,6 +214,18 @@ object Expr {
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 }) =>
'{ Tuple22($x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11, $x12, $x13, $x14, $x15, $x16, $x17, $x18, $x19, $x20, $x21, $x22) }

private def ofTupleFromSeqXXL(seq: Seq[Expr[Any]])(using Quotes): Expr[Tuple] =
val tupleTpe = tupleTypeFromSeq(seq)
tupleTpe.asType match
case '[tpe] =>
'{ Tuple.fromIArray(IArray(${Varargs(seq)}*)).asInstanceOf[tpe & Tuple] }

private def tupleTypeFromSeq(seq: Seq[Expr[Any]])(using Quotes): quotes.reflect.TypeRepr =
import quotes.reflect.*
val consRef = Symbol.classSymbol("scala.*:").typeRef
seq.foldLeft(TypeRepr.of[EmptyTuple]) { (ts, expr) =>
AppliedType(consRef, expr.asTerm.tpe :: ts :: Nil)
}

/** Given a tuple of the form `(Expr[A1], ..., Expr[An])`, outputs a tuple `Expr[(A1, ..., An)]`. */
def ofTuple[T <: Tuple: Tuple.IsMappedBy[Expr]: Type](tup: T)(using Quotes): Expr[Tuple.InverseMap[T, Expr]] = {
Expand Down
53 changes: 53 additions & 0 deletions tests/run-macros/i17257/a.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package derivation
import scala.quoted.*

import scala.annotation.tailrec

object Helpers:

// file a.scala
inline def summonAllOptimized[T <: Tuple]: T =
${ summonAllOptimizedImpl[T] }

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) =
${ summon23Impl[E] }

private def summonAllOptimizedImpl[T <: Tuple: Type](using q: Quotes): Expr[T] = {
import q.reflect.*

Expr
.ofTupleFromSeq(typesOfTuple(TypeRepr.of[T], Nil).map { tpe =>
tpe.asType match {
case '[t] =>
Expr.summon[t].getOrElse(report.errorAndAbort(s"Unable to to find implicit instance for ${tpe.show}"))
}
})
.asExprOf[T]
}

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)] = {
import q.reflect.*

val e = Expr.summon[E].getOrElse(report.errorAndAbort(s"Unable to to find implicit instance for ${TypeRepr.of[E].show}"))

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)

assert(tuple.size == 23)

Expr.ofTuple(tuple)
}

@tailrec
private[derivation] def typesOfTuple(
using q: Quotes
)(tpe: q.reflect.TypeRepr, acc: List[q.reflect.TypeRepr]): List[q.reflect.TypeRepr] =
import q.reflect.*
val cons = Symbol.classSymbol("scala.*:")
tpe.widenTermRefByName.dealias match
case AppliedType(fn, tpes) if defn.isTupleClass(fn.typeSymbol) =>
tpes.reverse_:::(acc)
case AppliedType(tp, List(headType, tailType)) if tp.derivesFrom(cons) =>
typesOfTuple(tailType, headType :: acc)
case tpe =>
if tpe.derivesFrom(Symbol.classSymbol("scala.EmptyTuple")) then acc.reverse
else report.errorAndAbort(s"Unknown type encountered in tuple ${tpe.show}")
23 changes: 23 additions & 0 deletions tests/run-macros/i17257/b.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package derivation {
//file b.scala
val test = Helpers.summonAllOptimized[(
ValueOf["a"], ValueOf["a"], ValueOf["a"], ValueOf["a"], ValueOf["a"],
ValueOf["a"], ValueOf["a"], ValueOf["a"], ValueOf["a"], ValueOf["a"],
ValueOf["a"], ValueOf["a"], ValueOf["a"], ValueOf["a"], ValueOf["a"],
ValueOf["a"], ValueOf["a"], ValueOf["a"], ValueOf["a"], ValueOf["a"],
ValueOf["a"], ValueOf["a"], ValueOf["a"] //Commenting out the last one here fixes the compile error
)]
val test2 = Helpers.summon23[ValueOf["a"]]
}
@main def Test =
def assertions(list: List[ValueOf["a"]]): Unit =
assert(list.size == 23)
assert(list.map(_.value) == List(
"a", "a", "a", "a", "a",
"a", "a", "a", "a", "a",
"a", "a", "a", "a", "a",
"a", "a", "a", "a", "a",
"a", "a", "a"
))
assertions(derivation.test.toList)
assertions(derivation.test2.toList)

0 comments on commit 6980b2e

Please sign in to comment.