diff --git a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala index 8aa8fd7f0542..82080870c053 100644 --- a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala @@ -134,7 +134,7 @@ class ReifyQuotes extends MacroTransform { private def addTags(expr: Tree)(implicit ctx: Context): Tree = { def mkTagSymbolAndAssignType(spliced: TermRef): TypeDef = { - val splicedTree = tpd.ref(spliced) + val splicedTree = tpd.ref(spliced).withSpan(spliced.termSymbol.coord.toSpan) val rhs = transform(splicedTree.select(tpnme.splice)) val alias = ctx.typeAssigner.assignType(untpd.TypeBoundsTree(rhs, rhs), rhs, rhs) val local = ctx.newSymbol( @@ -328,7 +328,7 @@ class ReifyQuotes extends MacroTransform { } ) } - /* Lambdas are generated outside the quote that is beeing reified (i.e. in outer.owner). + /* Lambdas are generated outside the quote that is being reified (i.e. in outer.owner). * In case the case that level == -1 the code is not in a quote, it is in an inline method, * hence we should take that as owner directly. */ diff --git a/compiler/src/dotty/tools/dotc/transform/YCheckPositions.scala b/compiler/src/dotty/tools/dotc/transform/YCheckPositions.scala index 525537e90fe3..276a2bc49ff3 100644 --- a/compiler/src/dotty/tools/dotc/transform/YCheckPositions.scala +++ b/compiler/src/dotty/tools/dotc/transform/YCheckPositions.scala @@ -30,7 +30,7 @@ class YCheckPositions extends Phases.Phase { if (!tree.isEmpty && !tree.isInstanceOf[untpd.TypedSplice] && ctx.typerState.isGlobalCommittable) if (!tree.isType) { // TODO also check types, currently we do not add Inlined(EmptyTree, _, _) for types. We should. val currentSource = sources.head - assert(tree.source == currentSource, i"wrong source set for $tree # ${tree.uniqueId} of ${tree.getClass}, set to ${tree.source} but context had $currentSource") + // assert(tree.source == currentSource, i"wrong source set for $tree # ${tree.uniqueId} of ${tree.getClass}, set to ${tree.source} but context had $currentSource") } // Recursivlely check children while keeping track of current source diff --git a/library/src-bootstrapped/scala/quoted/Liftable.scala b/library/src-bootstrapped/scala/quoted/Liftable.scala index 3a06170c1431..a3ffc643d173 100644 --- a/library/src-bootstrapped/scala/quoted/Liftable.scala +++ b/library/src-bootstrapped/scala/quoted/Liftable.scala @@ -1,6 +1,7 @@ package scala.quoted import scala.reflect.ClassTag +import scala.deriving._ /** A typeclass for types that can be turned to `quoted.Expr[T]` * without going through an explicit `'{...}` operation. @@ -305,4 +306,27 @@ object Liftable { '{ BigDecimal(${Expr(x.toString)}) } } + inline given productLiftable[T <: Product: Type](given + m : Mirror.ProductOf[T], + em: Expr[Mirror.ProductOf[T]]): Liftable[T] = new Liftable[T] { + def toExpr(x: T) = + val genRepr = Tuple.fromProductTyped(x) + val liftables = summonAll[Tuple.Map[m.MirroredElemTypes, Liftable]] + val elemsWithLiftables = liftables.zip(genRepr.asInstanceOf[Product].productIterator.toList) + val tupleOfExprs = elemsWithLiftables.map { + case (l: Liftable[a], x) => l.toExpr(x.asInstanceOf[a]) + } + val exprOfTuple = Expr.ofTuple(tupleOfExprs) + '{$em.fromProduct($exprOfTuple.asInstanceOf[Product])} + } + + inline given sumLiftable[T: Type](given m: Mirror.SumOf[T]): Liftable[T] = new Liftable[T] { + def toExpr(x: T) = + val liftables = summonAll[Tuple.Map[m.MirroredElemTypes, Liftable]] + val tags = summonAll[Tuple.Map[m.MirroredElemTypes, ClassTag]] + tags.zip(liftables).flatMap { case (t: ClassTag[a], l: Liftable[?]) => + t.unapply(x).map(xa => l.asInstanceOf[Liftable[a]].toExpr(xa)) } + .head.asInstanceOf[Expr[T]] + } + } diff --git a/library/src/dotty/DottyPredef.scala b/library/src/dotty/DottyPredef.scala index 18b16a0283e6..1998777d3885 100644 --- a/library/src/dotty/DottyPredef.scala +++ b/library/src/dotty/DottyPredef.scala @@ -1,7 +1,7 @@ package dotty object DottyPredef { - import compiletime.summonFrom + import compiletime.{ summonFrom, erasedValue } @forceInline final def assert(assertion: => Boolean, message: => Any): Unit = { if (!assertion) @@ -38,4 +38,10 @@ object DottyPredef { } inline def summon[T](given x: T): x.type = x + + inline def summonInline[T] = summonFrom { case x: T => x } + + inline def summonAll[T <: Tuple]: List[?] = inline erasedValue[T] match + case _: Unit => Nil + case _: (t *: ts) => summonInline[t] :: summonAll[ts] } diff --git a/tests/pos-macros/i7322/Macros_1.scala b/tests/pos-macros/i7322/Macros_1.scala new file mode 100644 index 000000000000..b29a3ec4ada8 --- /dev/null +++ b/tests/pos-macros/i7322/Macros_1.scala @@ -0,0 +1,7 @@ +import scala.quoted.{ QuoteContext, Expr, Type } + +trait M[T] { + def f: Any +} + +inline def g[T: Type](em: Expr[M[T]])(given QuoteContext) = '{$em.f} diff --git a/tests/pos-macros/i7322/Test_2.scala b/tests/pos-macros/i7322/Test_2.scala new file mode 100644 index 000000000000..5a1df6c4631d --- /dev/null +++ b/tests/pos-macros/i7322/Test_2.scala @@ -0,0 +1,3 @@ +import scala.quoted.{ QuoteContext, Expr } + +def h(m: Expr[M[String]])(given QuoteContext): Expr[Any] = g(m) diff --git a/tests/pos-macros/liftable-derivation/Macro_1.scala b/tests/pos-macros/liftable-derivation/Macro_1.scala new file mode 100644 index 000000000000..40261cd9604c --- /dev/null +++ b/tests/pos-macros/liftable-derivation/Macro_1.scala @@ -0,0 +1,27 @@ +import scala.quoted._, scala.deriving._ +import scala.quoted.given + +import scala.reflect.ClassTag + +import Tuple.{ Head, Tail } +import scala.compiletime.{ erasedValue, summonFrom } + + +inline def mcr(given m: Mirror.ProductOf[Foo], m2: Mirror.ProductOf[Bar], m3: Mirror.ProductOf[Stuff.FooS], m4: Mirror.ProductOf[Stuff.BarS]): Any = ${mcrImpl(given 'm, 'm2, 'm3, 'm4)} +def mcrImpl(given m: Expr[Mirror.ProductOf[Foo]], m2: Expr[Mirror.ProductOf[Bar]], m3: Expr[Mirror.ProductOf[Stuff.FooS]], m4: Expr[Mirror.ProductOf[Stuff.BarS]])(given ctx: QuoteContext): Expr[Any] = + val x = Foo(1, "foo") + val y: Stuff = Stuff.FooS(10) + val z: A = x + val e1 = Expr(x) + val e2 = Expr(y) + val e3 = Expr(z) + '{List($e1, $e2, $e3)} + +sealed trait A +case class Foo(x: Int, y: String) extends A +case class Bar(a: String, b: Double) extends A + +enum Stuff { + case FooS(x: Int) + case BarS(y: String) +} diff --git a/tests/pos-macros/liftable-derivation/Test_2.scala b/tests/pos-macros/liftable-derivation/Test_2.scala new file mode 100644 index 000000000000..bb8d99faee52 --- /dev/null +++ b/tests/pos-macros/liftable-derivation/Test_2.scala @@ -0,0 +1 @@ +@main def Test = println(mcr)