Closed
Description
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.