Question

I have coded a macro which uses the resetLocalAttrs method of the (macro) context. After the macro has expanded, I get a weird error and I can't figure out what is happening. Firstly, I will introduce the problem. We have two primitives: the state (which I'll represent as a simple Int) and the action. The state represents the current state of the system, while the actions are responsible for modifying the state. The actions can be atomic or composed of atomic actions.

trait Action {
  def andNext(action: PartialFunction[Int, Int => Action]): AndNext = AndNext(this, action)
}
case class ActionId extends Action
case class AndNext(action: Action, and: PartialFunction[Int, Int => Action]) extends Action

As one can see in the code, the AndNext receives a partial function, because it could launch one action or another depending on the current state in that moment. An atomic action which is inside a composed action could require the preceding state as left by the preceding atomic action in the sequence, so I need to protect it with an Int => Action.

Let's test the code, using a dummy function to force the test to require a Int => Action parameter:

def f(lifted: Int => Action) = ???
f(implicit state => ActionId() andNext { case _ => implicit state => ActionId() })

It's OK, but I am working on a DSL, and it is too much verbose to write all those redundant implicit state, they must be hidden to make this code nice for DSL usage. Trying to reach the following cute invocation...

f(ActionId() andNext { case _ => ActionId() })

I have implemented a macro which turns the actions into protected actions (remember Int => Action):

implicit def lift[T](expr: T): Int => T = macro liftImpl[T]

def liftImpl[T: c.WeakTypeTag](c: Context)(expr: c.Expr[T]): c.Expr[Int => T] = {
  import c.mirror._
  import c.universe._

  //reify(implicit state => expr.splice)
  reify(implicit state => c.Expr[T](c.resetLocalAttrs(expr.tree)).splice)
}

(*) Notice the implicit in the macro declaration, which converts the method in a view.

The former (and commented) reify works fine, but because of some limitations I have to use the reify which calls resetLocalAttrs() (you don't want to know, this question is becoming too long :). This reify fails with a weird error:

class Any is abstract; cannot be instantiated

Where am I trying to instance an Any? Why is the reset affecting the AST so much? I have used it many times and it was resetting the types and symbols nicely.

What's the better approach to find the problem? I have been working with showRaw enabling types and ids flags and it becomes quite hard.

Was it helpful?

Solution

Partial functions give rise to synthetic anonymous classes, and anonymous classes have known issues with typecheck-reset-retypechecking: https://issues.scala-lang.org/browse/SI-6187

17:04 ~/Projects/Kepler_macrosnippet00/sandbox (topic/macrosnippet00)$ scalac -Ymacro-debug-lite Test.scala 
performing macro expansion Macros.lift[ActionId](ActionId.apply()) at source-/Users/xeno_by/Projects/Kepler_macrosnippet00/sandbox/Test.scala,line-11,offset=449
((implicit state) => ActionId.apply())
Function(List(ValDef(Modifiers(IMPLICIT | PARAM), newTermName("state"), TypeTree(), EmptyTree)), Apply(Select(Ident(ActionId), newTermName("apply")), List()))
performing macro expansion Macros.lift[AndNext](ActionId.apply().andNext(({
  @SerialVersionUID(0) final <synthetic> class $anonfun extends scala.runtime.AbstractPartialFunction[Int,Int => Action] with Serializable {
    def <init>(): anonymous class $anonfun = {
      $anonfun.super.<init>();
      ()
    };
    final override def applyOrElse[A1 >: Nothing <: Int, B1 >: Int => Action <: Any](x$1: A1, default: A1 => B1): B1 = (x$1: A1 @unchecked) match {
      case _ => ((implicit state: Int) => ActionId.apply())
    };
    final def isDefinedAt(x$1: Int): Boolean = (x$1: Int @unchecked) match {
      case _ => true
    }
  };
  new anonymous class $anonfun() // an anonymous class created for a partial function
}: PartialFunction[Int,Int => Action]))) at source-/Users/xeno_by/Projects/Kepler_macrosnippet00/sandbox/Test.scala,line-11,offset=421
((implicit state) => ActionId.apply().andNext(({
  final <synthetic> class $anonfun extends scala.runtime.AbstractPartialFunction[Int,Int => Action] with Serializable {
    def <init>() = {
      super.<init>();
      ()
    };
    final override def applyOrElse[A1 <: Int, B1 >: Int => Action](x$1, default) = (x$1: <type ?>) match {
      case _ => ((implicit state: Int) => ActionId.apply())
    };
    final def isDefinedAt(x$1: Int): Boolean = (x$1: Int @unchecked) match {
      case _ => true
    }
  };
  new <type ?>() // <= the problem is here: the type has been irreversibly erased by resetLocalAttrs
}: PartialFunction[Int,Int => Action])))
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top