A bit of a funky scenario, best outlined below.
Suppose I have sealed trait Painting[T]
, and then a bunch of more specialised variations, such as trait ImpressionistPainting[T] extends Painting[T]
and so on, building a simple sealed type family.
I then have a class with a f bounded polymorphic type bound as such:
class Gallery[T <: Gallery[T]]()(implicit helper: GalleryHelper[T])
And a:
trait GalleryHelper[T <: Gallery[T]] {
def paintings: Set[Painting[_]]
}
object GalleryHelper {
implicit def materialize[T <: Gallery[T]]: GalleyHelper[T] = {
macro MyMacro.materialize[T]
}
def apply[T <: Gallery[T]]()(
implicit ev: GalleryHelper[T]
): GalleryHelper[T] = ev
}
So far this is very basic and straightforward foo bar code.
The whole goal of this setup is to macro materialise a helper list of items that I need to describe the gallery, or in this case arbitrarily typed "user content", as such:
class MyGallery extends Gallery[MyGallery] {
object `le-reve` extends CubistPainting[Picasso]
object starry_night extends PostImpressionistPainting[VanGogh]
// ..
}
Now with some macro compat fun, I want to macro materialise the helper in question and filter the members of T <: Gallery[T]
to extract those which are MemberType <:< Painting[_]
.
All very straightforward, the below is more complex than simply type.decls.filter(_.typeSignature <:< typeOf[Filter]
because it needs to return a list of all inherited members in the order they are written as well, given galleries extend other galleries for instance.
def findMembersWithBound[T : WeakTypeTag, Filter : TypeTag](
exclusions: Symbol => Option[Symbol] = { s: Symbol => Some(s) }
): Set[Symbol] = {
val tpe = weakTypeOf[T].typeSymbol.typeSignature
(
for {
baseClass <- tpe.baseClasses.reverse.flatMap(exclusions(_))
symbol <- baseClass.typeSignature.members.sorted
if symbol.typeSignature <:< typeOf[Filter]
} yield symbol
)(collection.breakOut)
}
So in an implicit macro, a basic traversal of module members for type T would need to filter by a sub type, in this case Painting[_]
, and then look at what specific type arguments where provided by the user when extending a variant of Painting
. The type family is sealed, so users extend a sub class of Painting
with object
, never Painting[_]
directly, if this is relevant in any way.
@macrocompat.bundle
class MyMacro(val c: blackbox.Context) {
import c.universe._
def materialize[T <: Gallery[T]]: Tree = {
val galleryTpe = weakTypeOf[T]
val fields = findMembersWithBound[T, Painting[_]](exclusions)
val colMembers = sourceMembers.map { member =>
val memberType = member.typeSignatureIn(galleryTpe)
memberType.baseClasses.find(colSymbol ==) match {
case Some(root) => {
// Herein lies the problem, typeArgs is Nil.
root.typeSignatureIn(memberType).typeArgs.headOption match {
case Some(colSignature) => colSignature
case None => c.abort(
c.enclosingPosition,
s"Could not find the artist for ${member.asModule.name}"
)
}
}
case None => c.abort(c.enclosingPosition, s"Could not find root painting type for ${member.asModule.name}")
}
}
}
The problem is that none of the original type arguments passed through to Painting
are visible anymore, even though the typeSignature
would apper to be evaluated in scope and so on, and I'm simply trying to make sure Van Gogh doesn't become the proverbial Waldo.
What is the correct API to dealias
or however else make those typeArgs
visible again? Currently an empty list.