Search for implicit method definitions in macro context

I understand the basic concept of macros in Scala, but currently I cannot complete this (simple?) Task:

  • Find all the implicit def s / val that are currently visible to the compiler to convert from the given type to another.

What I expect to get is a List of Method objects or something similar. I already played with enclosingImplicits , but I always get an empty list and don’t know where to look next.

What do I need to do to get the list I'm looking for?

+6
source share
1 answer

In the context, there can only be one implicit from type A to B (or you get an ambiguous 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 an 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 is false ( true by default), a TypecheckException will be TypecheckException in case of an output error. Therefore, you can analyze it to find a list of ambiguous implications.

PS If type B not found, there is no (documented) way to find all implications using macros: openImplicits / enclosingImplicits simply looking for opportunities that materialize in the context of macro decomposition - not for all that exist in the context. A compiler plugin may help, but it's not that simple.

If you really decide to try the "compiler-plugin method" - the logic for finding implicits is implemented here . Here you can find the Context compiler (not the same as the macro) and its implicitss field, which contains all the implications in the context (but it is not so trivial to get the corresponding context).


And I should not tell you, but there is a complex and unsafe hack from the Context macro to the compiler 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: 

In any case, you need to analyze the ImplicitInfo list to get the implications you are looking for, and this may not be trivial, as you can see from Analizer sources, but at least you can get an approximate result, which may be good for your needs. But then again, it’s better to do this very carefully , since the structures you work with are mutable and the methods are not clean. And, as @Eugene Burmako noted, this solution does not give you incompatibility on the part of the companion.

+2
source

Source: https://habr.com/ru/post/985822/


All Articles