Question

I have a simple test macro that uses reify. It causes a StackOverflowError during macro expansion.

def test() = macro testimpl

def testimpl(c:Context)():c.Expr[Any] = {
  import c.universe._
  val o = reify { // StackOverflowError here
    object O
    O
  }
  o
}

Why does this happen? Can it somehow be avoided?

EDIT: This is what happens with M6. I just tried it with M7 and now it says

implementation restriction: cannot reify type Object{def (): O.type} (ClassInfoType)

So that answers the why question, but the question still remains whether there is a way to work around this.

Was it helpful?

Solution

Currently the reifier doesn't know how to reify types that reference things defined inside the block being reified. Hence the error.

But what does it have to do with your example? Here's how it works.

To reify your block of code, the compiler uses def apply[T: AbsTypeTag](mirror: MirrorOf[self.type], treec: TreeCreator): Expr[T] (upd. in 2.10.0-RC1 AbsTypeTag has been renamed to WeakTypeTag) to create an object of type Expr that reifies the expression. However implicit in the contract of Expr is that it also captures the type of the reifee, and that gives rise to the problem.

Therefore you need a workaround. The simplest one would be to cast O in the last line of the snippet to something reifiable, e.g. to write O.asInstanceOf[Object]. Then you can manually strip off the asInstanceOf part from the result.

scala> reify { object O; O }
<console>:26: error: implementation restriction: cannot reify type Object{def <init>(): O.type} (ClassInfoType)
              reify { object O; O }
                    ^

scala> reify { object O; O.asInstanceOf[Object] }
res1 @ 2d059fd6: reflect.runtime.universe.Expr[Object] =
Expr[java.lang.Object]({
  object O extends AnyRef {
    def <init>() = {
      super.<init>();
      ()
    }
  };
  O.asInstanceOf[Object]
})

OTHER TIPS

I bumped into the same issue lately. But I couldn't afford casting the object type, as I used the singleton type within a other macro to distinguish between (compile time) "variables". So if you really need to reify an object, you can do the following within a macro so reify return the object instead of a Unit value.

def mkObject(c: Context) = {
  import c.universe._

  val objectO = reify { object O }
  c.Expr(objectO.tree match {
    case Block(stats, expr) => Block(stats, Ident(newTermName("O")))
  })
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top