Search code examples
scalamacrosscala-macro-paradise

Scala Macros: Generated class fails to compile with unimplemented members


My problem is that my generated class' members are not getting recognised as implemented. This is being built for Scala 2.10/2.11/2.12 with 2.11 being the default. Using macroparadise 2.10.

Goal of this was to mark the some traits as freezable and generate an implementation class for each that takes scala.util.Try for each method that is freezable that will have its get method called when accessing the data. All unfreezable methods are filled in with throw new IllegalStateException. And a custom freeze method is created that creates an instance of the Frozen class filled in with the data needed using the methods defined by the trait.

I have set up an annotation macro to be placed on traits and generate a companion object with an inner class called Frozen like below:

{
  abstract trait JavaInfo extends scala.AnyRef {
    @new CanFreeze() def isJavaInfo: Boolean;
    @new CannotFreeze() def toJavaInfo: AnyRef
  };
  object JavaInfo extends scala.AnyRef {
    def <init>() = {
      super.<init>();
      ()
    };
    class Frozen extends JavaInfo with java.io.Serializable {
      <paramaccessor> private val $isJavaInfo: scala.util.Try[Boolean] = _;
      def <init>($isJavaInfo: scala.util.Try[Boolean]) = {
        super.<init>();
        ()
      };
      override def isJavaInfo: Boolean = this.$isJavaInfo.get;
      override def toJavaInfo: AnyRef = throw new IllegalStateException("Method not frozen!")
    };
    def freeze(valueToFreeze: JavaInfo): Frozen = new Frozen(scala.util.Try.apply(valueToFreeze.isJavaInfo))
  };
  ()
}

The following error occurs when I try to run my macro on the trait JavaInfo:

[error] /Users/senk/projects/scala-debugger/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/JavaInfo.scala:9: class Frozen needs to be abstract, since:
[error] it has 2 unimplemented members.
[error] /** As seen from class Frozen, the missing signatures are as follows.
[error]  *  For convenience, these are usable as stub implementations.
[error]  */
[error]   override def isJavaInfo: Boolean = ???
[error]   override def toJavaInfo: AnyRef = ???
[error] @Freezable trait JavaInfo {
[error]  ^
[error] one error found

The trait itself is as follows:

import org.scaladebugger.macros.freeze.{CanFreeze, CannotFreeze, Freezable}

@Freezable trait JavaInfo {
  @CanFreeze def isJavaInfo: Boolean
  @CannotFreeze def toJavaInfo: AnyRef
}

Get error that nothing has been implemented by the Frozen class, even though I see the implementation in the tree I'm building.

Code in question is here: https://github.com/chipsenkbeil/scala-debugger/blob/AddFreezableMacroSupport/scala-debugger-macros/src/main/scala/org/scaladebugger/macros/freeze/FreezableMacro.scala

You can test the failure by checking out that branch and running sbt scalaDebuggerApi/compile, which will compile the macros project for Scala 2.11 followed by the API itself.


Solution

  • Figured it out. I was creating the methods and implementing their bodies, but reusing the old modifiers of each method and just tacking on the Flag.OVERRIDE: FlagSet. Turns out unimplemented methods have a Flag.DEFERRED on them, so I was producing a Modifiers(OVERRIDE | DEFERRED), which didn't make sense considering it had a body. Seems that having the modifier of DEFERRED will cause the compiler to ignore the body when checking the method, even though the body will show up in the byte code itself.