diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index 8cc83ab85fc9..545b391a3a43 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -449,7 +449,14 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler withDefaultPos(tpd.ref(tp).asInstanceOf[tpd.RefTree]) def apply(sym: Symbol): Ref = assert(sym.isTerm) - withDefaultPos(tpd.ref(sym).asInstanceOf[tpd.RefTree]) + val refTree = tpd.ref(sym) match + case t @ tpd.This(ident) => // not a RefTree, so we need to work around this - issue #19732 + // ident in `This` can be a TypeIdent of sym, so we manually prepare the ref here, + // knowing that the owner is actually `This`. + val term = Select(This(sym.owner), sym) + term.asInstanceOf[tpd.RefTree] + case other => other.asInstanceOf[tpd.RefTree] + withDefaultPos(refTree) end Ref type Ident = tpd.Ident diff --git a/tests/run-macros/i19732.check b/tests/run-macros/i19732.check new file mode 100644 index 000000000000..a5a57e7eb466 --- /dev/null +++ b/tests/run-macros/i19732.check @@ -0,0 +1 @@ +Map(b -> 23) diff --git a/tests/run-macros/i19732/Macro_1.scala b/tests/run-macros/i19732/Macro_1.scala new file mode 100644 index 000000000000..31564fdcf77f --- /dev/null +++ b/tests/run-macros/i19732/Macro_1.scala @@ -0,0 +1,42 @@ +// package dummy + +import scala.quoted.* + +trait Defaults[A]: + def defaults: Map[String, Any] + +object Defaults: + inline def derived[A <: Product]: Defaults[A] = ${ defaultsImpl[A] } + + def defaultsImpl[A <: Product: Type](using Quotes): Expr[Defaults[A]] = + '{ + new Defaults[A]: + def defaults: Map[String, Any] = ${ defaultParmasImpl[A] } + } + +def defaultParmasImpl[T](using quotes: Quotes, tpe: Type[T]): Expr[Map[String, Any]] = + import quotes.reflect.* + + TypeRepr.of[T].classSymbol match + case None => '{ Map.empty[String, Any] } + case Some(sym) => + val comp = sym.companionClass + val mod = Ref(sym.companionModule) + val names = + for p <- sym.caseFields if p.flags.is(Flags.HasDefault) + yield p.name + val namesExpr: Expr[List[String]] = + Expr.ofList(names.map(Expr(_))) + + val body = comp.tree.asInstanceOf[ClassDef].body + val idents: List[Ref] = + for + case deff @ DefDef(name, _, _, _) <- body + if name.startsWith("$lessinit$greater$default") + yield mod.select(deff.symbol) + val typeArgs = TypeRepr.of[T].typeArgs + val identsExpr: Expr[List[Any]] = + if typeArgs.isEmpty then Expr.ofList(idents.map(_.asExpr)) + else Expr.ofList(idents.map(_.appliedToTypes(typeArgs).asExpr)) + + '{ $namesExpr.zip($identsExpr).toMap } diff --git a/tests/run-macros/i19732/Test_2.scala b/tests/run-macros/i19732/Test_2.scala new file mode 100644 index 000000000000..04efd3a6015b --- /dev/null +++ b/tests/run-macros/i19732/Test_2.scala @@ -0,0 +1,7 @@ +class Outer: + case class Inner(a: String, b: Int = 23) derives Defaults + +object Test: + def main(args: Array[String]): Unit = + val outer = Outer() + println(summon[Defaults[outer.Inner]].defaults)