Inline summon generated by macro doesn't consider expanded macro scope
See original GitHub issueCompiler version
3.0.0-RC3
Attempt 1: summonInline
Minimized code
a:scala
:
import scala.quoted.*
case class F[A]()
object F:
implicit def option[A: F]: F[Option[A]] = F()
inline def test[A]: F[Option[A]] =
${ _test[A] }
def _test[A](using Quotes, Type[A]): Expr[F[Option[A]]] =
import quotes.reflect.*
// Dynamically created `implicit val`
val faSym = Symbol.newVal(Symbol.spliceOwner, "fa", TypeRepr.of[F[A]], Flags.Implicit, Symbol.noSymbol)
val faBody = '{ F[Int]() }.asTerm
val faDef = ValDef(faSym, Some(faBody))
val summonInline = '{ scala.compiletime.summonInline[F[Option[A]]] }.asTerm
val result = Block(List(faDef), summonInline).asExprOf[F[Option[A]]]
println(s"\n${result.show}\n")
result
b:scala
:
object Test:
val ko = F.test[Int]
// Copy-paste of the .show output of the generated Expr
val ok =
implicit val fa: F[scala.Int] = F.apply[scala.Int]()
scala.compiletime.package$package.summonInline[F[scala.Option[scala.Int]]]
Output
{
implicit val fa: F[scala.Int] = F.apply[scala.Int]()
scala.compiletime.package$package.summonInline[F[scala.Option[scala.Int]]]
}
[error] -- Error: b.scala:3:17 ----------------
[error] 3 | val ko = F.test[Int]
[error] | ^^^^^^^^^^^
[error] | cannot reduce summonFrom with
[error] | patterns : case given t @ _:F[Option[Int]]
[error] | This location contains code that was inlined from package.scala:140
[error] | This location contains code that was inlined from a.scala:19
[error] | This location contains code that was inlined from a.scala:19
[error] one error found
Attempt 2: inline
call to Implicits.search
Minimized code
a:scala
:
import scala.quoted.*
object Util:
def summonLater[A: Type](using Quotes): Expr[A] =
'{ inlineSummon[A] }
inline def inlineSummon[A]: A =
${ _inlineSummon[A] }
def _inlineSummon[A: Type](using Quotes): Expr[A] =
summonOrError[A]
def summonOrError[A](using Type[A])(using Quotes): Expr[A] =
import quotes.reflect.*
Implicits.search(TypeRepr.of[A]) match
case iss: ImplicitSearchSuccess => iss.tree.asExpr.asInstanceOf[Expr[A]]
case isf: ImplicitSearchFailure => report.throwError(isf.explanation)
case class F[A]()
object F:
implicit def option[A: F]: F[Option[A]] = F()
inline def test[A]: F[Option[A]] =
${ _test[A] }
def _test[A](using Quotes, Type[A]): Expr[F[Option[A]]] =
import quotes.reflect.*
// Dynamically created `implicit val`
val faSym = Symbol.newVal(Symbol.spliceOwner, "fa", TypeRepr.of[F[A]], Flags.Implicit, Symbol.noSymbol)
val faBody = '{ F[Int]() }.asTerm
val faDef = ValDef(faSym, Some(faBody))
val summonInline = Util.summonLater[F[Option[A]]].asTerm
val result = Block(List(faDef), summonInline).asExprOf[F[Option[A]]]
println(s"\n${result.show}\n")
result
b:scala
:
object Test:
val ko = F.test[Int]
// Copy-paste of the .show output of the generated Expr
val ok =
implicit val fa: F[scala.Int] = F.apply[scala.Int]()
Util.inlineSummon[F[scala.Option[scala.Int]]]
Output
{
implicit val fa: F[scala.Int] = F.apply[scala.Int]()
Util.inlineSummon[F[scala.Option[scala.Int]]]
}
[error] -- Error: b.scala:3:17 ----------------
[error] 3 | val ko = F.test[Int]
[error] | ^^^^^^^^^^^
[error] | no implicit values were found that match type F[Int]
[error] | This location contains code that was inlined from b.scala:3
[error] | This location contains code that was inlined from a.scala:5
[error] | This location contains code that was inlined from a.scala:5
[error] one error found
Expectation
Neither macro compiles, however in both cases if I copy-paste the output of .show
, the code compiles ok.
Implicit search should consider sibling implicits in local scope, just like normal code.
Issue Analytics
- State:
- Created 2 years ago
- Comments:15 (13 by maintainers)
Top Results From Across the Web
Macros - Scala 3
Here, we need a tweak of the typing rules. An inline function such as assert that contains a splice operation outside an enclosing...
Read more >Within a function passed to a scala macro, I cannot reference ...
Isn't the class recompiled with the macro expanded? If recompiled with inline expansion, no compilation error should occur. object Generation { ...
Read more >Macros: Essential Information - World of Warcraft Forums
Most often, macros are created to use items or abilities. However, macros can also be used to send chat messages, equip gear, change...
Read more >Scala Compile-time Operations | Macros in Scala 3
It is possible to emit error messages when inlining code. ... If error is called outside an inline method, the error will be...
Read more >Pathfinder Community Sheet - Roll20 Wiki
The Pathfinder's Community Sheet Guide was created for the Pathfinder ... Q: I edited a macro with [[1d6]] and it doesn't work, why?...
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
In attempt 1, if it is
inline def summonInline
:If it is
transparent inline def summonInline
then it just works.Then I quit while I was ahead.
If you know exactly what you need, then yes 100% this is a bad pattern, I agree with you. The problem is that there are cases where you not only do you know what what’s required, but the effort to work out if something is required and then to try to recursively populate it with it’s required implicits, especially when you’re generating implicits that can (according to user-space rules and not the rules of the macro) have mutual/recursive implicit dependencies, trying to calculate all of that effort 100% statically is equivalent to just implementing the implicit search algorithm. Using this pattern I can just generate all of the expected implicits and if the user has done everything right on their side, then everything should work out at expansion time. If they haven’t, then they’ll get a nice implicit-not-found error after my macro expands which is perfect for both users, and me as the macro author.