0

I wrote a simple annotation macro to expand a type synonym and save the result in a string. It works fine in a block, but does not work when used to declare a class member. Why?

Here's all the code necessary to compile and run.

Client code:

object Client extends App {
  type Synonym = Int

  /** fails to compile due to TypeCheckException: not found: type Synonym
    * should expand to:
    *
    *   val classScope : String = "Int"
    */
  // @dealias val classScope : Synonym = ?

  /** this works */
  val blockScope = {
    @dealias val blockScope : Synonym = ?
    blockScope
  }

  println(s"Inside a block, `Synonym` dealiases to `$blockScope`.")
}

Macro implementation:

def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
  import c.universe._
  annottees.head.tree match {
    case q"val $lhs : $typeTree = $rhs" =>
      val tpe = c.typecheck(q"{ ??? : $typeTree }").tpe
      val dealiased = tpe.dealias
      c.Expr(q"val $lhs : String = ${dealiased.toString}")
  }
}

1 Answers1

0

This works for SeparatedClient:

c.Expr(q"val $lhs : String = reflect.runtime.universe.typeOf[$typeTree].dealias.toString")

I could imagine that the enclosing class is not available yet during expansion, but that an annotation on the class would work.

That's just a guess by someone who doesn't use this stuff.

scala> SeparatedClient.run
Inside a block, `Synonym` dealiases to `scala.Int`.
Inside a member, `Synonym` dealiases to `scala.Int`.
som-snytt
  • 39,429
  • 2
  • 47
  • 129
  • Thanks. I encountered the problem trying to generate code according to the type of some value. Dealiasing at runtime is not an option for this application. Also tried annotating the whole class with the synonym declared in a superclass; `Context.typecheck` continues to throw `TypecheckException`. [New code](https://github.com/yfcai/dealias/blob/master/client/AnnotatedClient.scala). – Yufei Cai Aug 25 '14 at 19:15