Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1926,7 +1926,19 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
else
member.info

memberInfo.asSeenFrom(self, member.owner) match
val memberInfoSubstituted =
if member.owner.isConstructor then
self match
case dotc.core.Types.AppliedType(_, args) =>
val ctorTypeParams = member.owner.paramSymss.flatten.filter(_.isType)
if ctorTypeParams.length == args.length then
memberInfo.subst(ctorTypeParams, args)
else
memberInfo
case _ => memberInfo
else memberInfo

memberInfoSubstituted.asSeenFrom(self, member.owner) match
case dotc.core.Types.ClassInfo(prefix, sym, _, _, _) =>
// We do not want to expose ClassInfo in the reflect API, instead we change it to a TypeRef,
// see issue #22395
Expand Down
70 changes: 70 additions & 0 deletions tests/pos-macros/i25565/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import scala.quoted.*

object Repro:
inline def singletonValues[A]: List[A] = ${ singletonValuesImpl[A] }

private def singletonValuesImpl[A: Type](using Quotes): Expr[List[A]] =
import quotes.reflect.*

def collectValues[T: Type]: List[Expr[T]] =
val tpe = TypeRepr.of[T]
tpe.dealias match
case o: OrType => unionValues[T](o)
case _ if tpe.isSingleton => singletonValue[T](tpe) :: Nil
case _ =>
val sym = tpe.typeSymbol
if sym.flags.is(Flags.Sealed) then sumValues[T](sym)
else if sym.flags.is(Flags.Case) && sym.caseFields.nonEmpty then productValues[T](tpe, sym)
else
report.errorAndAbort(
s"Cannot derive values for ${tpe.show}. Supported types: singleton types, " +
"enums, sealed traits/classes, union types, and case classes/tuples whose fields are all enumerable."
)

def singletonValue[T: Type](tpe: TypeRepr): Expr[T] =
tpe.asType match
case '[t] =>
Expr.summon[ValueOf[t]] match
case Some(vo) => '{ $vo.value }.asExprOf[T]
case None => report.errorAndAbort(s"Cannot determine value for singleton type: ${tpe.show}")

def unionValues[T: Type](orType: OrType): List[Expr[T]] =
def extract(tpe: TypeRepr): List[Expr[T]] =
tpe.dealias match
case o: OrType => extract(o.left) ++ extract(o.right)
case s if s.isSingleton => singletonValue[T](s) :: Nil
case other => report.errorAndAbort(s"Unsupported type in union: ${other.show}.")
extract(orType)

def sumValues[T: Type](sym: Symbol): List[Expr[T]] =
sym.children.flatMap { child =>
if child.isTerm then
child.termRef.asType match
case '[t] =>
Expr.summon[ValueOf[t]] match
case Some(vo) => '{ $vo.value }.asExprOf[T] :: Nil
case None => report.errorAndAbort(s"Cannot get value for: ${child.name}")
else
child.typeRef.asType match
case '[c] => collectValues[c].map(_.asExprOf[T])
}

def productValues[T: Type](tpe: TypeRepr, sym: Symbol): List[Expr[T]] =
val constructorParams = sym.primaryConstructor.paramSymss.flatten.filter(_.isTerm)
val fieldTypes = constructorParams.map(f => tpe.memberType(f).widen.dealias)
val fieldValueExprs: List[List[Term]] = fieldTypes.map { ft =>
ft.asType match
case '[f] => collectValues[f].map(_.asTerm)
}
val cartesian = fieldValueExprs.foldRight(List(List.empty[Term])) { (vals, acc) =>
for v <- vals; rest <- acc yield v :: rest
}
cartesian.map { args =>
val companion = Ref(sym.companionModule)
val applyMethod = sym.companionModule.methodMember("apply").head
val typeParams = applyMethod.paramSymss.flatten.filter(_.isTypeParam)
if typeParams.nonEmpty then Select.overloaded(companion, "apply", fieldTypes, args).asExprOf[T]
else Select.overloaded(companion, "apply", Nil, args).asExprOf[T]
}

Expr.ofList(collectValues[A])
27 changes: 27 additions & 0 deletions tests/pos-macros/i25565/Test_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
object TestCase1:
enum Foo:
case A
case B
enum Bar:
case A
case B

val values = Repro.singletonValues[(Foo, Bar)]

def TestCase2 =
type Foo = "a" | "b"
type Bar = "x" | "y"

val values = Repro.singletonValues[(Foo, Bar)]

case class MyPair[A, B](first: A, second: B)

object TestCase3:
enum X:
case P
case Q
enum Y:
case R
case S

val values = Repro.singletonValues[MyPair[X, Y]]
Loading