12

I have the following macro defining a class and returning an instance of that class (with Scala 2.10.2 and the macro plugin):

def test[T] = macro testImpl[T]

def testImpl[T : c.WeakTypeTag](c: Context): c.Expr[Any] = {
  import c.universe._
  val className = newTypeName("Test")

  c.Expr { q"""
    class $className  {
      def method = 1
    }
    new $className
  """}
}

When I call the macro:

case class Cat(name: String)

val t = test[Cat].method

I get the following error:

method method in class Test cannot be accessed in Test
val t = test[Cat].method
                   ^

My overall goal is to use vampire methods and to use quasi-quotes to describe the generated class. How can I solve this error?

Eric
  • 15,494
  • 38
  • 61
  • Can't your synthesized class extend a trait which defines `method` abstractly? – Miles Sabin Aug 28 '13 at 07:40
  • No because I need to generate those methods with specific names. And in trying to do that I realised that I could not even access non-generated methods. I suspect that I am not using quasi-quotes properly here. – Eric Aug 28 '13 at 07:45
  • I had no idea that there was now a compiler plugin for macro paradise (as opposed to being forced to use a forked compiler). If only for learning that, thanks a lot for your question. – Régis Jean-Gilles Aug 28 '13 at 09:15
  • @Eric, mind if I add the `scala-macros` tag to make this a little easier to find? – Travis Brown Aug 28 '13 at 12:28
  • Of course, I didn't really know which tag to use. scala-macros seemed a bit redundant to me – Eric Aug 28 '13 at 12:39

1 Answers1

11

In my post on vampire methods I mention this workaround for this bug. For some reason you currently aren't able to see an anonymous class's methods on the instance returned from the macro unless you create a wrapper class that extends the class with the methods and return an instance of that, instead.

You're seeing the same bug from a slightly different angle. You've named the class with the methods you want to see on the returned instance's structural type, but you still need a wrapper. The following will work:

  c.Expr { q"""
    class $className  {
      def method = 1
    }
    new $className {}
  """}

Note that all I've done is add a pair of brackets to the line creating the instance, so that I get an instance of an anonymous class extending $className instead of just a $className.

I have no idea what's behind this bug, and I'm not sure if Eugene knows more. I did recently confirm that it's still around in the latest build of 2.11.

Community
  • 1
  • 1
Travis Brown
  • 138,631
  • 12
  • 375
  • 680
  • 2
    I'll bring this up on the next reflection meeting. – Eugene Burmako Aug 28 '13 at 11:49
  • 1
    I knew you would step-in Travis. Thanks! Now I can prepare the next question :-) – Eric Aug 28 '13 at 12:07
  • 2
    Looks like it's by design. According to Martin, allowing immediate members of local classes to end up in structural types led to some weird problems back then, that's why now one has to go the extra mile to expose these members. – Eugene Burmako Sep 03 '13 at 12:38
  • @EugeneBurmako: Thanks for asking! Any idea why e.g. `new { def foo = 42 }` or `{ class X { def foo = 42 }; new X }` works as expected (i.e., I can see `foo` on the structural type of the instance) in non-macro contexts, though? – Travis Brown Sep 03 '13 at 12:46
  • 2
    The behavior `{ class X { def foo = 42 }; new X }` though is really surprising... – Eugene Burmako Sep 03 '13 at 15:04