lena_punkt
lena_punkt

Reputation: 399

Finding implicit method definitions in macro context

I understand the basic concept of macros in Scala, but currently fail to do this (simple?) job:

What I'd expect to get is a List of Method objects or something similar. I already played around with enclosingImplicits but always get an empty list and do not really know where to look next.

What do I need to do to get the list I am looking for?

Upvotes: 7

Views: 933

Answers (1)

dk14
dk14

Reputation: 22374

There can be only one implicit from type A to B in the context (or you get ambigious implicit), so if you want to find it:

import reflect.macros.Context, scala.language.experimental.macros

def fImpl(c: Context): c.Expr[Unit] = {
  import c.mirror._         
  println(c.inferImplicitValue(typeOf[Int]))
  c.universe.reify( () )
}
def f = macro fImpl

scala> f
<empty>

scala> implicit val a = 5
a: Int = 5

scala> f
$line24.$read.$iw.$iw.$iw.$iw.a

scala> implicit val b = 5
b: Int = 5

scala> f //result will be empty, but error printed to the log
error: ambiguous implicit values:
 both value a of type => Int
 and value b of type => Int
 match expected type Int
<empty>

To find implicit method:

def fImpl(c: Context): c.Expr[Unit] = {
      import c.mirror._       
      println(c.inferImplicitValue(typeOf[String => Int]))        
      c.universe.reify( () )
 }
def f = macro fImpl

scala> f
<empty>

scala> implicit def aaa(a: String) = 5
warning: there was one feature warning; re-run with -feature for details
aaa: (a: String)Int

scala> "A" : Int
res10: Int = 5

scala> f
{
  ((a: String) => $line47.$read.$iw.$iw.$iw.$iw.$iw.$iw.aaa(a))
}

If silent parameter is false (true by default), TypecheckException will be thrown in case of an inference error. So you could analize it to find list of ambigious implicits.

P.S. If type B is uncknown - there is no (documented) way to find all implicits using macroses: openImplicits/enclosingImplicits just looking for implicits being materialized in context of macro-expansion - not for all, that exists in the context. Compiler-plugin may help, but it's not so easy then.

If you really decide to try "compiler-plugin" way - the logic of finding implicits is implemented here. Here you can find compiler's Context (not same as macro's) and its implicitss field, which contains all implicits in the context (but it's not so trivial to obtain appropriate context).


And I shouldn't tell you but there is a tricky and unsafe hack to lift from macro Context to compiler's level and do what you want:

 scala>  def fImpl(c: Context): c.Expr[Unit] = {
 |       val cc = c.asInstanceOf[reflect.macros.contexts.Context]
 |       println(cc.callsiteTyper.context.implicitss.flatten)
 |       c.universe.reify( () )
 |  }
fImpl: (c: reflect.macros.Context)c.Expr[Unit]

scala> def f = macro fImpl

scala> f //I've defined aaaaaaaa etc. implicits while playing with that
List(aaaaaaaa: ?, lllllllllllllllllllllzzzz: ?, lllllllllllllllllllll: ?, lllllllllllllllllllll: ?, aaa: ?, aaa: ?, aaa: ?, aaa: ?, aaa: ?, aaa: ?, b: ?, a: ?, macros: ?, RuntimeClassTag:

Anyway, you have to analize a list of ImplicitInfo to obtain implicits you're looking for and it may not be trivial, as you can see from Analizer's sources, but at least it's possible to get approximate result, which may be fine for your needs. But again, it's better to do that very very very careful, as structures you working with are mutable and methods are not pure. And, as @Eugene Burmako noticed, this solution doesn't give you implicits from companion object.

Upvotes: 4

Related Questions