Consider the following traits:
sealed trait Test
//Test's branches
trait Base {
type Action = Test
}
Now I need to get a ClassSymbol
of Test
referring to it as Base#Action
.
Here is my attempt:
def macroImpl[B <: Base: c.WeakTypeTag](c: blackbox.Context): c.Tree = {
import c.universe._
val baseType = weakTypeOf[B].typeSymbol.asType
val actionType = c.typecheck(tq"$baseType#Action", mode = c.TYPEMode)
println(tq"$actionType") //prints pack.age.Base#Action
println(tq"$actionType".symbol) //prints type Action
println(tq"$actionType".symbol.asClass) //raises scala.ScalaReflectionException: type Action is not a class
}
In my case Base#Action = Test
which is definitely a class.
Is there a way to refer to its ClassSymbol
from the macro implementation via type projection?
Try firstly take the type of a tree
tq"$actionType".tpe.typeSymbol.asClass
I guess val baseType = weakTypeOf[B]
will be shorter than val baseType = weakTypeOf[B].typeSymbol.asType
. It's not clear why to go from type to symbol and then back to type.
couldn't you please give a bit of explanation? It's not really obvious
The symbol of type member Action
of trait Base
and the symbol of trait Test
are different. The type member Action
is a type alias for Test
but definitions of trait Test
and type Action
are different (symbols represent definitions).
The type member Action
is not a class (trait) itself, it's a type. It's an alias for the type of a class (trait), but not a class itself.
Equal types can have different symbols.
I thought that we need to dealias in tq"$actionType".tpe.dealias
(transforming the type Base#Action
into Test
) but it turns out that here dealiasing is aggressive and automatical so tq"$actionType".tpe
is enough (the type Test
already). Its symbol tq"$actionType".tpe.typeSymbol
is the symbol of class (trait) Test
and we can check that it's a ClassSymbol
(and cast to it) with .asClass
.