diff --git a/library/src/scala/quoted/Expr.scala b/library/src/scala/quoted/Expr.scala index 996fe3ff8da2..8243e7dc4a4b 100644 --- a/library/src/scala/quoted/Expr.scala +++ b/library/src/scala/quoted/Expr.scala @@ -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) } } @@ -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]] = { diff --git a/tests/run-macros/i17257/a.scala b/tests/run-macros/i17257/a.scala new file mode 100644 index 000000000000..4a5682327604 --- /dev/null +++ b/tests/run-macros/i17257/a.scala @@ -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}") diff --git a/tests/run-macros/i17257/b.scala b/tests/run-macros/i17257/b.scala new file mode 100644 index 000000000000..65d66fb20bff --- /dev/null +++ b/tests/run-macros/i17257/b.scala @@ -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)