title: Scala macro annotation - case class with type parameters
body: <p>I'm trying to write a simple macro annotation for case classes which adds a method to the companion object. The catch is that the new method must account for type parameters on the annotated case class.</p>
  
  <p>Here's the test that I need to pass</p>
  
  <pre><code>package my.macros
  
  import org.scalatest._
  
  class DefaultApplyTest extends FlatSpec with Matchers {
  
    @defaultApply case class Generic[A, B](a: A, b: B)
  
    it should "define defaultApply method in companion object" in {
      assert(Generic.defaultApply("str", 1) == Generic("str", 1))
    }
  }
  </code></pre>
  
  <p>Here's the code I've written to accomplish this</p>
  
  <pre><code>package my.macros
  
  import scala.reflect.macros._
  import scala.language.experimental.macros
  import scala.annotation.StaticAnnotation
  
  class defaultApply extends StaticAnnotation {
    def macroTransform(annottees: Any*): Any = macro DefaultApply.impl
  }
  
  object DefaultApply {
  
    def impl(c: blackbox.Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
      import c.universe._
  
      def defaultApplyCompanion(classDecl: ClassDef) = {
        val (name, typeParams, valueParams) = try {
          val q"case class ${name: TypeName}[..${typeParams: Seq[TypeDef]}](..${valueParams: Seq[ValDef]}) extends ..$bases { ..$body }" = classDecl
          (name, typeParams, valueParams)
        } catch {
          case e: MatchError =&gt;
            c.warning(c.enclosingPosition, e.toString)
            c.abort(c.enclosingPosition, "Annotation is only supported on case class")
        }
  
        val applyDef = q"""${name.toTermName}.apply[..$typeParams]"""
        c.warning(c.enclosingPosition, showRaw(applyDef))
  
        q"""
          object ${name.toTermName} {
            def defaultApply: (..${valueParams.map(_.tpt)}) =&gt; $name[..$typeParams] = $applyDef
          }
        """
      }
  
      def modifiedDeclaration(classDecl: ClassDef) = {
        val compDecl = defaultApplyCompanion(classDecl)
  
        c.Expr(q"""
          $classDecl
          $compDecl
        """)
      }
  
      annottees.map(_.tree) match {
        case (classDecl: ClassDef) :: Nil =&gt; modifiedDeclaration(classDecl)
        case _ =&gt; c.abort(c.enclosingPosition, "Invalid annottee")
      }
    }
  }
  </code></pre>
  
  <p>The problem as I understand it is that when I try to <a href="http://docs.scala-lang.org/overviews/quasiquotes/lifting" rel="nofollow" title="lift">lift</a> the list of type parameters into the resulting syntax tree, they are not recognized as the same type parameters as from the original tree.</p>
  
  <p>So what I'm focused on is that for this part of the macro</p>
  
  <pre><code>  val applyDef = q"""${name.toTermName}.apply[..$typeParams]"""
    c.warning(c.enclosingPosition, showRaw(applyDef))
  </code></pre>
  
  <p>The raw syntax tree is emitted as</p>
  
  <pre><code>TypeApply(Select(Ident(TermName("Generic")), TermName("apply")), List(TypeDef(Modifiers(PARAM), TypeName("A"), List(), TypeBoundsTree(EmptyTree, EmptyTree)), TypeDef(Modifiers(PARAM), TypeName("B"), List(), TypeBoundsTree(EmptyTree, EmptyTree))))
  </code></pre>
  
  <p>but the compiler isn't happy with this</p>
  
  <pre><code>type arguments [&lt;notype&gt;,&lt;notype&gt;] do not conform to method apply's type parameter bounds [A,B]
  </code></pre>
  
  <p>The ultimate use case is for generating instances of a cacheable type class which touches over 1k lines of code. The non-parameterized version already works, this is just the icing on the cake. There's something under the hood of scalac that I don't understand, but would like to. Your time spent reading this is greatly appreciated.</p>
  
  <p>I'm using Scala 2.11.8 with <a href="http://docs.scala-lang.org/overviews/macros/paradise.html" rel="nofollow" title="macro paradise 2.1.0">macro paradise 2.1.0</a></p>