-
Notifications
You must be signed in to change notification settings - Fork 21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Abort in icode when compiling macro that reifies an anonymous function #5797
Comments
Imported From: https://issues.scala-lang.org/browse/SI-5797?orig=1 |
@retronym said: The "safe" way to write the macro is: c.universe.reify( () => c.Expr[A](c.resetLocalAttrs(x.tree)).splice ) Splicing code into the body of the closure means that any symbols defined in the splicee will now have the wrong owner, the hacky way around this is to 'reset' the symbols/types to null and let the definitions pick up branch new symbols when the expanded macro is typechecked. I'll leave this ticket open as admission that this situation is untenable, it is far to easy to write a macro that works in some circumstances but breaks in others. |
@retronym said: class OwnerRepair[C <: reflect.macros.Context with Singleton](val c: C) {
/**
* If macro arguments are spliced into underneath DefTree that introduces
* an entry into the symbol ownership chain, any symbols defined in the
* spliced tree will be ill-owned.
*
* This method detects this situation, and corrects the owners.
*/
def repairOwners[A](expr: c.Expr[A]): c.Expr[A] = {
val symtab = c.universe.asInstanceOf[reflect.internal.SymbolTable]
val utils = new Utils[symtab.type](symtab)
// Proactively typecheck the tree. This will assign symbols to
// DefTrees introduced by the macro.
val typed = c.typeCheck(expr.tree).asInstanceOf[symtab.Tree]
// The current owner at the call site. Symbols owned by this may need
// to be transplanted.
import scala.reflect.macros.runtime.{Context => MRContext}
val callsiteOwner =
c.asInstanceOf[MRContext]
.callsiteTyper.context.owner
.asInstanceOf[symtab.Symbol]
val repairedTree = utils.repairOwners(typed, callsiteOwner)
c.Expr[A](repairedTree.asInstanceOf[c.universe.Tree])
}
private class Utils[U <: reflect.internal.SymbolTable](val u: U) {
import u._
class ChangeOwnerAndModuleClassTraverser(oldowner: Symbol, newowner: Symbol)
extends ChangeOwnerTraverser(oldowner, newowner) {
override def traverse(tree: Tree) {
tree match {
case _: DefTree => change(tree.symbol.moduleClass)
case _ =>
}
super.traverse(tree)
}
}
def repairOwners(t: Tree, macroCallSiteOwner: Symbol): Tree = {
object repairer extends Transformer {
override def transform(t: Tree): Tree = {
// TODO see `fixerUpper` in the pattern matcher for a slightly simpler way to do this.
if (currentOwner.hasTransOwner(macroCallSiteOwner) && currentOwner.owner != macroCallSiteOwner)
new ChangeOwnerAndModuleClassTraverser(macroCallSiteOwner, currentOwner)(t)
else super.transform(t)
}
}
repairer transform t
}
}
} // end of the macro impl
val ownerRepair = new OwnerRepair[c.type](c)
val checked = ownerRepair.repairOwners(code).tree |
@xeno-by said: |
@retronym said: I'm working towards this for async, but its not trivial, as if we remove the resetAttrs calls, the rest of the macro actually breaks, because we splice untyped trees underneath typed trees, as well as the reverse. So we really need to drop down to lower-level mechanisms, like |
@retronym said: // This is needed to repair owner chain as encountered in the following issue:
// https://github.com/scalatest/scalatest/issues/276
class OwnerRepair[C <: reflect.macros.Context with Singleton](val c: C) {
/**
* If macro arguments are spliced into underneath DefTree that introduces
* an entry into the symbol ownership chain, any symbols defined in the
* spliced tree will be ill-owned.
*
* This method detects this situation, and corrects the owners.
*/
def repairOwners[A](expr: c.Expr[A]): c.Expr[A] = {
val symtab = c.universe.asInstanceOf[reflect.internal.SymbolTable]
val utils = new Utils[symtab.type](symtab)
// Proactively typecheck the tree. This will assign symbols to
// DefTrees introduced by the macro.
val typed = c.typeCheck(expr.tree).asInstanceOf[symtab.Tree]
// The current owner at the call site. Symbols owned by this may need
// to be transplanted.
import scala.reflect.macros.runtime.{Context => MRContext}
val callsiteOwner =
c.asInstanceOf[MRContext]
.callsiteTyper.context.owner
.asInstanceOf[symtab.Symbol]
val repairedTree = utils.repairOwners(typed, callsiteOwner)
c.Expr[A](repairedTree.asInstanceOf[c.universe.Tree])
}
private class Utils[U <: reflect.internal.SymbolTable](val u: U) {
import u._
class ChangeOwnerAndModuleClassTraverser(oldowner: Symbol, newowner: Symbol)
extends ChangeOwnerTraverser(oldowner, newowner) {
override def traverse(tree: Tree) {
tree match {
case _: DefTree => change(tree.symbol.moduleClass)
case _ =>
}
super.traverse(tree)
}
}
def repairOwners(t: Tree, macroCallSiteOwner: Symbol): Tree = {
object repairer extends Transformer {
override def transform(t: Tree): Tree = {
t match {
case (_: DefTree | _: Function | _: Import) if t.symbol.owner == macroCallSiteOwner && macroCallSiteOwner != currentOwner =>
new ChangeOwnerAndModuleClassTraverser(macroCallSiteOwner, currentOwner)(t)
case _ =>
super.transform(t)
}
}
}
repairer.atOwner(macroCallSiteOwner) {
repairer transform t
}
}
}
} |
closing stale backend tickets; comment/reopen if you have evidence this is still applicable |
Removing the backend label (that's just where the symptom appears, not the problem.) But I'll leave this close as the need to use |
The compiler aborts in icode when compiling a macro that reifies an anonymous function whose body is the macro argument. The crash happens when the macro argument contains a local variable use.
The following REPL dump shows the crash.
This does not happen when using
scala.reflect.mirror
directly:The text was updated successfully, but these errors were encountered: