Skip to content

IllegalArgumentException: Could not find proxy for Lambda/ValDef combination in quotes #13922

Closed
@cchantep

Description

@cchantep

Compiler version

3.1.2-RC1-bin-20211022-f7abd32-NIGHTLY-git-f7abd32

Minimized code

import scala.quoted.*

  inline def optPrettyPrinter[T]: Option[T] => Option[T] =
    ${ optPrettyPrinterImpl[T] }

  private def optPrettyPrinterImpl[T: Type](
      using
      q: Quotes
    ): Expr[Option[T] => Option[T]] = {
    import q.reflect.*

    val tpe = TypeRepr.of[T]

    val fn = Lambda(
      Symbol.spliceOwner,
      MethodType(List("macroVal"))(
        _ => List(tpe),
        _ => tpe
      ),
      {
        case (_, List(arg: Term)) =>
          ValDef.let(Symbol.spliceOwner, "v", arg) { v =>
            '{
              val vv = ${ v.asExprOf[T] }
              println("v=" + vv.toString())
              vv
            }.asTerm
          }

        case _ =>
          report.errorAndAbort("Fails compile")
      }
    ).asExprOf[T => T]

    '{ (_: Option[T]).map(${ fn }) }
  }
optPrettyPrinter(Some("foo"))

Output

(run-main-2) java.lang.IllegalArgumentException: Could not find proxy for val vv: String in List(val vv, method res0, module class rs$line$1$, module class repl$, module class ), encl = package repl, owners = package repl, package ; enclosures = package repl, package java.lang.IllegalArgumentException: Could not find proxy for val vv: String in List(val vv, method res0, module class rs$line$1$, module class repl$, module class ), encl = package repl, owners = package repl, package ; enclosures = package repl, package
	at dotty.tools.dotc.transform.LambdaLift$Lifter.searchIn$1(LambdaLift.scala:148)
	at dotty.tools.dotc.transform.LambdaLift$Lifter.proxy(LambdaLift.scala:161)
	at dotty.tools.dotc.transform.LambdaLift$Lifter.proxyRef(LambdaLift.scala:179)
	at dotty.tools.dotc.transform.LambdaLift$Lifter.addFreeArgs$$anonfun$1(LambdaLift.scala:185)
	at scala.collection.immutable.List.map(List.scala:246)
	at dotty.tools.dotc.transform.LambdaLift$Lifter.addFreeArgs(LambdaLift.scala:185)
	at dotty.tools.dotc.transform.LambdaLift.transformApply(LambdaLift.scala:322)
	at dotty.tools.dotc.transform.LambdaLift.transformApply(LambdaLift.scala:321)
	at dotty.tools.dotc.transform.MegaPhase.goApply(MegaPhase.scala:644)
	at dotty.tools.dotc.transform.MegaPhase.transformUnnamed$1(MegaPhase.scala:281)
	at dotty.tools.dotc.transform.MegaPhase.transformTree(MegaPhase.scala:429)
	at dotty.tools.dotc.transform.MegaPhase.mapValDef$1(MegaPhase.scala:235)
	at dotty.tools.dotc.transform.MegaPhase.transformNamed$1(MegaPhase.scala:240)
	at dotty.tools.dotc.transform.MegaPhase.transformTree(MegaPhase.scala:427)
	at dotty.tools.dotc.transform.MegaPhase.transformStat$1(MegaPhase.scala:437)
	at dotty.tools.dotc.transform.MegaPhase.recur$1(MegaPhase.scala:442)
	at dotty.tools.dotc.transform.MegaPhase.transformStats(MegaPhase.scala:442)
	at dotty.tools.dotc.transform.MegaPhase.mapPackage$1(MegaPhase.scala:382)
	at dotty.tools.dotc.transform.MegaPhase.transformUnnamed$1(MegaPhase.scala:385)
	at dotty.tools.dotc.transform.MegaPhase.transformTree(MegaPhase.scala:429)
	at dotty.tools.dotc.transform.MegaPhase.transformUnit(MegaPhase.scala:448)
	at dotty.tools.dotc.transform.MegaPhase.run(MegaPhase.scala:460)
	at dotty.tools.dotc.core.Phases$Phase.runOn$$anonfun$1(Phases.scala:308)
	at scala.collection.immutable.List.map(List.scala:246)
	at dotty.tools.dotc.core.Phases$Phase.runOn(Phases.scala:309)
	at dotty.tools.dotc.Run.runPhases$1$$anonfun$1(Run.scala:261)
	at scala.runtime.function.JProcedure1.apply(JProcedure1.java:15)
	at scala.runtime.function.JProcedure1.apply(JProcedure1.java:10)
	at scala.collection.ArrayOps$.foreach$extension(ArrayOps.scala:1323)
	at dotty.tools.dotc.Run.runPhases$1(Run.scala:272)
	at dotty.tools.dotc.Run.compileUnits$$anonfun$1(Run.scala:280)
	at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.scala:18)
	at dotty.tools.dotc.util.Stats$.maybeMonitored(Stats.scala:68)
	at dotty.tools.dotc.Run.compileUnits(Run.scala:289)
	at dotty.tools.dotc.Run.compileUnits(Run.scala:228)
	at dotty.tools.repl.ReplCompiler.runCompilationUnit(ReplCompiler.scala:167)
	at dotty.tools.repl.ReplCompiler.compile(ReplCompiler.scala:178)
	at dotty.tools.repl.ReplDriver.compile(ReplDriver.scala:251)
	at dotty.tools.repl.ReplDriver.interpret(ReplDriver.scala:219)
	at dotty.tools.repl.ReplDriver.loop$1(ReplDriver.scala:153)
	at dotty.tools.repl.ReplDriver.runUntilQuit$$anonfun$1(ReplDriver.scala:156)
	at dotty.tools.repl.ReplDriver.withRedirectedOutput(ReplDriver.scala:175)
	at dotty.tools.repl.ReplDriver.runUntilQuit(ReplDriver.scala:156)
	at xsbt.ConsoleInterface.run(ConsoleInterface.java:52)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:564)
	at sbt.internal.inc.AnalyzingCompiler.invoke(AnalyzingCompiler.scala:329)
	at sbt.internal.inc.AnalyzingCompiler.console(AnalyzingCompiler.scala:233)
	at sbt.Console.console0$1(Console.scala:64)
	at sbt.Console.$anonfun$apply$5(Console.scala:74)
	at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:23)
	at sbt.util.InterfaceUtil$$anon$1.get(InterfaceUtil.scala:17)
	at sbt.TrapExit$App.run(TrapExit.scala:258)
	at java.base/java.lang.Thread.run(Thread.java:844)

Expectation

val res0: Option[String] = Some(foo)

Details

The following is working fine, not mixing '{ ... } with tree constructed using Lambda/ValDef.

  inline def prettyPrinter[T]: T => T = ${ prettyPrinterImpl[T] }

  private def prettyPrinterImpl[T: Type](using q: Quotes): Expr[T => T] = {
    import q.reflect.*

    val tpe = TypeRepr.of[T]

    Lambda(
      Symbol.spliceOwner,
      MethodType(List("macroVal"))(
        _ => List(tpe),
        _ => tpe
      ),
      {
        case (_, List(arg: Term)) =>
          ValDef.let(Symbol.spliceOwner, "v", arg) { v =>
            '{
              val vv = ${ v.asExprOf[T] }
              println("v=" + vv.toString())
              vv
            }.asTerm
          }

        case _ =>
          report.errorAndAbort("Fails compile")
      }
    ).asExprOf[T => T]
  }

Also coming back to macro optPrettyPrinter it can be fixed using changeOwner.

  inline def optPrettyPrinter[T]: Option[T] => Option[T] =
    ${ optPrettyPrinterImpl[T] }

  private def optPrettyPrinterImpl[T: Type](
      using
      q: Quotes
    ): Expr[Option[T] => Option[T]] = {
    import q.reflect.*

    val tpe = TypeRepr.of[T]

    val fn = Lambda(
      Symbol.spliceOwner,
      MethodType(List("macroVal"))(
        _ => List(tpe),
        _ => tpe
      ),
      {
        case (m, List(arg: Term)) =>
          ValDef
            .let(Symbol.spliceOwner, "v", arg) { v =>
              '{
                val vv = ${ v.asExprOf[T] }
                println("v=" + vv.toString())
                vv
              }.asTerm
            }
            .changeOwner(m) // <---- HERE

        case _ =>
          report.errorAndAbort("Fails compile")
      }
    ).asExprOf[T => T]

    '{ (_: Option[T]).map(${ fn }) }
  }

The importance of changeOwner should at least be documented on the Lambda factories, maybe always enforced.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions