Search code examples
scalaimplicitscala-macros

How to remove all inferred implicits in a macro?


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.

Symbols 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...


Solution

  • 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