scalametaprogrammingscala-macrostype-projection

Resolve type projection in scala macro


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?


Solution

  • 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.