Open nicolasstucki opened 3 years ago
Thanks @nicolasstucki for the info. I actually tried something like this myself trying add extensions methods on things inside of quotes.reflect like Term and Constant but it never worked.
@odersky It would be immensely useful for macro authors to be able to add extensions to things inside quotes.reflect, but this means that we need to be able to add extension methods to path-dependent types.
To bypass this issue I gathered all of this functionality inside this large file called TastyMatchers.scala that assigns quotes to a local val and then imports from it. That's a really clunky solution because then when I try to do something like this:
object MatchMac:
inline def apply(inline any: Any): Any = ${ matchMacImpl('any) }
def matchMacImpl(any: Expr[Any])(using Quotes): Expr[Any] =
import quotes.reflect._
val tmc = new TastyMatchersContext
import tmc._
val lastPart: Term = Untype(any.asTerm) match
case Block(parts, lastPart) => lastPart
case other => report.throwError("Error")
lastPart.asExpr
I get the following error:
[error] -- [E007] Type Mismatch Error: /Users/aleiof/git/dotty_test/src/main/scala/miniquill/parser/PrintMac.scala:35:36
[error] 35 | val lastPart: Term = Untype(any.asTerm) match
[error] | ^^^^^^^^^^
[error] | Found: x$2.reflect.Term
[error] | Required: tmc.qctx.reflect.Term
That is because the Untype
function in TastyMatchers uses its local reference to Quotes (**)... and it needs to do that since if it doesn't, the following error happens.
(** Additional Details: Untype does several internal things related to ignoring Typed(...)
nodes which I typically need to ignore)
[error] -- Error: /Users/aleiof/git/dotty_test/src/main/scala/miniquill/parser/TastyMatchers.scala:84:8
[error] 84 | def unapply(term: Term): Option[Term] = term match {
[error] | ^
[error] |non-private method unapply in object Untype refers to private value qctx
[error] |in its type signature (term: TastyMatchers.this.qctx.reflect.Term):
[error] | Option[TastyMatchers.this.qctx.reflect.Term]
The only current way to bypass this issue is like this:
object MatchMac:
inline def apply(inline any: Any): Any = ${ matchMacImpl('any) }
def matchMacImpl(any: Expr[Any])(using Quotes): Expr[Any] =
import quotes.reflect._
class Operations(using val qctx: Quotes) extends TastyMatchers:
import qctx.reflect._
def apply =
val lastPart: Term = Untype(any.asTerm) match
case Block(parts, lastPart) => lastPart
case other => report.throwError("Error")
lastPart.asExpr
new Operations().apply
That way, inside of the Operations class, Scala knows that the two instances of quotes are the same thing.
This is really clunky and impractical in many cases since instance of Term, Constant, and anything else coming from qctx cannot be returned from Operations
back into the outer context.
It would be really, really nice to add extension methods on things inside of quotes.reflect directly.
Minimized example
Output
Expectation
Should find extension method
toTerm
andtoExprOf
.