I need to really untypecheck the trees that are emitted by my macro. That means not only removing inferred types and all that, but also removing the implicit arguments that are inferred by the compiler. Not even resetAllAttrs
seems to take care of that.
Symbol
s appear to have a method isSynthetic
that indicates whether code was generated by the compiler, but apparently that flag is only set for things like autogenerated getters and setters, not for implicit values that the compiler inserts.
I could of course manually look for all implicit argument lists and remove them, but then I will also remove the ones that were explicitly provided by the user of my macro.
So ideally for the following code
scala> def foo(a: Int)(implicit e: DummyImplicit) = a
foo: (a: Int)(implicit e: DummyImplicit)Int
scala> myMacro{ foo(4); foo(2)(DummyImplicit.dummyImplicit) }
myMacro
would emit the tree
{
foo(4);
foo(2)(Predef.this.DummyImplicit.dummyImplicit)
}
that is then typechecked again and compiled.
But I'm afraid this can't be done...
They have internal API to detect the implicitly supplied args, but it is commented as unstable. (They'll use a tree attachment instead of a subtype of Apply.)
$ scala
Welcome to Scala 2.12.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_111).
Type in expressions for evaluation. Or try :help.
scala> import language.experimental.macros
import language.experimental.macros
scala> import reflect.macros.blackbox.Context
import reflect.macros.blackbox.Context
scala> def fImpl(c: Context)(block: c.Expr[Any]): c.Expr[Boolean] = c.Expr { import c._, universe._
| val trees = universe.asInstanceOf[reflect.internal.Trees]
| block.tree match {
| case t@Apply(_,_) => Literal(Constant(t.isInstanceOf[trees.ApplyToImplicitArgs]))
| case _ => Literal(Constant(false)) }}
fImpl: (c: scala.reflect.macros.blackbox.Context)(block: c.Expr[Any])c.Expr[Boolean]
scala> def f(block: Any): Boolean = macro fImpl
defined term macro f: (block: Any)Boolean
scala> def g(implicit d: DummyImplicit) = 42
g: (implicit d: DummyImplicit)Int
scala> f(println(""))
res0: Boolean = false
scala> f(g)
res1: Boolean = true
scala> f(g(new DummyImplicit))
res2: Boolean = false