From 49d9901bcccee72515a196832bcb471c289b8229 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Fri, 14 Apr 2023 11:56:54 +0200 Subject: [PATCH 1/3] correctly type Expr.ofTupleFromSeq for arity > 22 --- library/src/scala/quoted/Expr.scala | 15 +++++++++++- tests/run-macros/i17257/a.scala | 38 +++++++++++++++++++++++++++++ tests/run-macros/i17257/b.scala | 21 ++++++++++++++++ 3 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 tests/run-macros/i17257/a.scala create mode 100644 tests/run-macros/i17257/b.scala diff --git a/library/src/scala/quoted/Expr.scala b/library/src/scala/quoted/Expr.scala index 996fe3ff8da2..72ceaa8ffadd 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,19 @@ 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) => + expr match + case '{ $e: t } => AppliedType(consRef, TypeRepr.of[t] :: 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..873e8a6e84a2 --- /dev/null +++ b/tests/run-macros/i17257/a.scala @@ -0,0 +1,38 @@ +package derivation +import scala.quoted.* + +import scala.annotation.tailrec + +object Helpers: + + // file a.scala + inline def summonAllOptimized[T <: Tuple]: T = + ${ summonAllOptimizedImpl[T] } + + 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] + } + + @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..b9159973fbc4 --- /dev/null +++ b/tests/run-macros/i17257/b.scala @@ -0,0 +1,21 @@ +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 + )] +} +@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) From faf70f8dea4d8cde2501530cc289432d5b6755e5 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Mon, 17 Apr 2023 13:12:20 +0200 Subject: [PATCH 2/3] test Expr.ofTuple --- tests/run-macros/i17257/a.scala | 15 +++++++++++++++ tests/run-macros/i17257/b.scala | 2 ++ 2 files changed, 17 insertions(+) diff --git a/tests/run-macros/i17257/a.scala b/tests/run-macros/i17257/a.scala index 873e8a6e84a2..4a5682327604 100644 --- a/tests/run-macros/i17257/a.scala +++ b/tests/run-macros/i17257/a.scala @@ -9,6 +9,9 @@ object Helpers: 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.* @@ -22,6 +25,18 @@ object Helpers: .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 diff --git a/tests/run-macros/i17257/b.scala b/tests/run-macros/i17257/b.scala index b9159973fbc4..65d66fb20bff 100644 --- a/tests/run-macros/i17257/b.scala +++ b/tests/run-macros/i17257/b.scala @@ -7,6 +7,7 @@ package derivation { 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 = @@ -19,3 +20,4 @@ package derivation { "a", "a", "a" )) assertions(derivation.test.toList) + assertions(derivation.test2.toList) From b3cfa95119f2041078e1e03f47f040cb0ed61a59 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Mon, 17 Apr 2023 13:50:37 +0200 Subject: [PATCH 3/3] small optimisation --- library/src/scala/quoted/Expr.scala | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/library/src/scala/quoted/Expr.scala b/library/src/scala/quoted/Expr.scala index 72ceaa8ffadd..8243e7dc4a4b 100644 --- a/library/src/scala/quoted/Expr.scala +++ b/library/src/scala/quoted/Expr.scala @@ -218,14 +218,13 @@ object Expr { val tupleTpe = tupleTypeFromSeq(seq) tupleTpe.asType match case '[tpe] => - '{ Tuple.fromIArray(IArray(${Varargs(seq)}: _*)).asInstanceOf[tpe & Tuple] } + '{ 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) => - expr match - case '{ $e: t } => AppliedType(consRef, TypeRepr.of[t] :: ts :: Nil) + AppliedType(consRef, expr.asTerm.tpe :: ts :: Nil) } /** Given a tuple of the form `(Expr[A1], ..., Expr[An])`, outputs a tuple `Expr[(A1, ..., An)]`. */