Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Instantiating a forwarder trait #16366

Unanswered
kubukoz asked this question in Metaprogramming
Nov 17, 2022 · 1 comments · 3 replies
Discussion options

Hi! I'm trying to build a macro that builds a proxy for arbitrary traits. I'm having trouble using the parameters the method is called with.

Here's what the macro looks like:

//> using scala "3.2.1"
//> using option "-Ycheck:all"
//> using option "-Xcheck-macros"
////> using option "-Xprint:inlining"
//> using option "-Xprint-types"
import scala.quoted._
import scala.annotation.experimental
object macros {
 trait Proxxy[Alg] {
 def proxify(fa: Alg): Alg
 }
 inline def mkProxy[Alg]: Proxxy[Alg] = ${ mkProxyImpl[Alg] }
 @experimental
 def mkProxyImpl[Alg: Type](using Quotes): Expr[Proxxy[Alg]] = {
 val Typ = Type.of[Alg]
 import quotes.reflect._
 import quotes.reflect.given
 def instance(
 fExpr: Expr[Alg]
 )(
 using Quotes
 ): Expr[Alg] = {
 val par = TypeRepr.of[Alg]
 def methodSymbols(
 owner: Symbol
 ): List[Symbol] = par.typeSymbol.declaredMethods.map { baseMethod =>
 val deff = baseMethod.tree.asInstanceOf[DefDef]
 val paramVals = deff.paramss.flatMap(_.params.asInstanceOf[List[ValDef]])
 Symbol.newMethod(
 parent = owner,
 baseMethod.name,
 TypeRepr.of[Alg].memberType(baseMethod),
 )
 }
 val cl = Symbol.newClass(
 Symbol.spliceOwner,
 "$anon",
 parents = List(
 TypeRepr.of[Object],
 par,
 ),
 decls = methodSymbols(_),
 selfType = None,
 )
 val defs = methodSymbols(cl).map { m =>
 DefDef(
 m,
 args =>
 Some {
 fExpr
 .asTerm
 .select(cl.declaredMethod(m.name).head)
 .appliedToArgss(
 args.map(
 _.map { a =>
 // the problematic part, apparently
 a.asExpr.asTerm
 }
 )
 )
 // should I do this? doesn't seem to have an effect
 // .changeOwner(m)
 },
 )
 }
 Block(
 List(
 ClassDef(cl, Nil, defs)
 ),
 New(TypeTree.ref(cl))
 .select(cl.primaryConstructor)
 .appliedToArgs(Nil),
 ).asExprOf[Alg]
 }
 '{
 new Proxxy[Alg] {
 def proxify(fa: Alg): Alg = ${ instance('fa) }
 }
 }
 }
}

I'm trying it with this trait:

trait MyAlg {
 def hello(s: String): Int
}

and the error I'm getting is:

Error: Unexpected error when compiling project_466232d218: 'Could not find proxy for s: String in [parameter s, method hello, anonymous class Object with demo.MyAlg {...}, value macro, getter derivedProxy, object demo, package <empty>, package <root>], encl = package <empty>, owners = package <empty>, package <root>; enclosures = package <empty>, package <root>'

I've tried dozens of combinations of moving things around, changing owners, different ways to refer to the parameter, and I keep running into the same issue. I would appreciate any and all help and resources on this.

As you can see, I'm using -Xcheck-macros and -Xcheck:all, but they don't offer any help.

Related: #13571

You must be logged in to vote

Replies: 1 comment 3 replies

Comment options

I don't think you should be calling methodSymbols twice, that will create two sets of independent symbols

You must be logged in to vote
3 replies
Comment options

hmm, I guess there's a way I didn't consider before... lemme try that.

Comment options

yeah, that does the trick! Is there something we can do about the error message to make the problem easier to understand?

Comment options

also, are the Symbol.new*** the only impure parts of the reflect API or are there more?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet

AltStyle によって変換されたページ (->オリジナル) /