`Symbol.asQuotes` not working for quote that creates a context function
Compiler version
3.3.1-RC1-bin-20230216-2507577-NIGHTLY
Minimized code
Consider the following macro definition, which replaces the context function body of a def with another context function:
import scala.quoted.*
inline def test(inline v: Unit) = ${ testImpl('v) }
def testImpl(using Quotes)(v: Expr[Unit]) =
import quotes.reflect.*
val block @ Block(List(defdef @ DefDef(name, paramss, tpt, Some(Lambda(_, body)))), expr) = v.asTerm.underlyingArgument: @unchecked
val rhs =
given Quotes = defdef.symbol.asQuotes
'{ (_: String) ?=> ??? }.asTerm
Block.copy(block)(List(DefDef.copy(defdef)(name, paramss, tpt, Some(rhs))), expr).asExprOf[Unit]
Macro call site:
def method: Unit = test {
def innerMethod = { (_: String) ?=> ??? }
}
Output
The compiler crashes.
Compiler Error and Stack Trace
[info] compiling 4 Scala sources
exception while typing @ContextResultCount(1) def innerMethod: (String) ?=> Nothing =
{
def $anonfun(using _$1: String): Nothing = ???
closure($anonfun)
} of class class dotty.tools.dotc.ast.Trees$DefDef # -1
exception while typing {
@ContextResultCount(1) def innerMethod: (String) ?=> Nothing =
{
def $anonfun(using _$1: String): Nothing = ???
closure($anonfun)
}
()
} of class class dotty.tools.dotc.ast.Trees$Block # -1
exception while typing {
@ContextResultCount(1) def innerMethod: ((String) ?=> Nothing) =
{
def $anonfun(using _$1: String): Nothing = ???
closure($anonfun)
}
()
}:Unit of class class dotty.tools.dotc.ast.Trees$Typed # -1
exception while typing {
@ContextResultCount(1) def innerMethod: ((String) ?=> Nothing) =
{
def $anonfun(using _$1: String): Nothing = ???
closure($anonfun)
}
()
}:Unit of class class dotty.tools.dotc.ast.Trees$Inlined # -1
exception while typing def method: Unit =
{
@ContextResultCount(1) def innerMethod: ((String) ?=> Nothing) =
{
def $anonfun(using _$1: String): Nothing = ???
closure($anonfun)
}
()
}:Unit of class class dotty.tools.dotc.ast.Trees$DefDef # -1
exception while typing @SourceFile("src/main/scala/Test.scala") final module class Test$package()
extends Object() {
private def writeReplace(): AnyRef =
new scala.runtime.ModuleSerializationProxy(classOf[Test$package.type])
def method: Unit =
{
@ContextResultCount(1) def innerMethod: ((String) ?=> Nothing) =
{
def $anonfun(using _$1: String): Nothing = ???
closure($anonfun)
}
()
}:Unit
} of class class dotty.tools.dotc.ast.Trees$TypeDef # -1
exception while typing [cannot display due to ImportType(Select(Ident(scala),quoted)) (of class dotty.tools.dotc.core.Types$ImportType), raw string = PackageDef(Ident(<empty>),List(Import(Select(Ident(scala),quoted),List(ImportSelector(Ident(_),EmptyTree,EmptyTree))), ValDef(Test$package,TypeTree[TypeRef(ThisType(TypeRef(NoPrefix,module class <empty>)),module class Test$package$)],Apply(Select(New(TypeTree[TypeRef(ThisType(TypeRef(NoPrefix,module class <empty>)),module class Test$package$)]),<init>),List())), TypeDef(Test$package$,Template(DefDef(<init>,List(List()),TypeTree[TypeRef(ThisType(TypeRef(NoPrefix,module class scala)),class Unit)],EmptyTree),List(Apply(Select(New(TypeTree[TypeRef(ThisType(TypeRef(NoPrefix,module class lang)),class Object)]),<init>),List())),ValDef(_,EmptyTree,EmptyTree),List(DefDef(writeReplace,List(List()),TypeTree[TypeRef(ThisType(TypeRef(NoPrefix,module class scala)),type AnyRef)],Apply(Select(New(TypeTree[TypeRef(ThisType(TypeRef(NoPrefix,module class runtime)),class ModuleSerializationProxy)]),<init>),List(Literal(Constant(TermRef(ThisType(TypeRef(NoPrefix,module class <empty>)),object Test$package)))))), DefDef(method,List(),TypeTree[TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class <root>)),object scala),class Unit)],Inlined(Apply(Ident(test),List(Block(List(DefDef(innerMethod,List(),TypeTree[AppliedType(TypeRef(ThisType(TypeRef(NoPrefix,module class scala)),trait ContextFunction1),List(TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class scala)),object Predef),type String), TypeRef(ThisType(TypeRef(NoPrefix,module class scala)),class Nothing)))],Block(List(),Block(List(DefDef($anonfun,List(List(ValDef(_$2,Ident(String),EmptyTree))),TypeTree[TypeRef(ThisType(TypeRef(NoPrefix,module class scala)),class Nothing)],Block(List(),Ident(???)))),Closure(List(),Ident($anonfun),EmptyTree))))),Literal(Constant(()))))),List(),Typed(Block(List(DefDef(innerMethod,List(),TypeTree[AppliedType(TypeRef(ThisType(TypeRef(NoPrefix,module class scala)),trait ContextFunction1),List(TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class scala)),object Predef),type String), TypeRef(ThisType(TypeRef(NoPrefix,module class scala)),class Nothing)))],Inlined(Ident(TestMacro$package$),List(),Block(List(DefDef($anonfun,List(List(ValDef(_$1,TypeTree[TypeRef(TermRef(TermRef(ThisType(TypeRef(NoPrefix,module class <root>)),object scala),Predef),String)],EmptyTree))),TypeTree[TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class <root>)),object scala),Nothing)],Ident(???))),Closure(List(),Ident($anonfun),EmptyTree))))),Literal(Constant(()))),TypeTree[TypeRef(ThisType(TypeRef(NoPrefix,module class scala)),class Unit)]))))))))] of class class dotty.tools.dotc.ast.Trees$PackageDef # -1
scala.MatchError: Inlined(Ident(TestMacro$package$),List(),Block(List(DefDef($anonfun,List(List(ValDef(_$1,TypeTree[TypeRef(TermRef(TermRef(ThisType(TypeRef(NoPrefix,module class <root>)),object scala),Predef),String)],EmptyTree))),TypeTree[TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class <root>)),object scala),Nothing)],Ident(???))),Closure(List(),Ident($anonfun),EmptyTree))) (of class dotty.tools.dotc.ast.Trees$Inlined) while running erasure
scala.MatchError: Inlined(Ident(TestMacro$package$),List(),Block(List(DefDef($anonfun,List(List(ValDef(_$1,TypeTree[TypeRef(TermRef(TermRef(ThisType(TypeRef(NoPrefix,module class <root>)),object scala),Predef),String)],EmptyTree))),TypeTree[TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class <root>)),object scala),Nothing)],Ident(???))),Closure(List(),Ident($anonfun),EmptyTree))) (of class dotty.tools.dotc.ast.Trees$Inlined)
[error] ## Exception when compiling 4 sources
[error] scala.MatchError: Inlined(Ident(TestMacro$package$),List(),Block(List(DefDef($anonfun,List(List(ValDef(_$1,TypeTree[TypeRef(TermRef(TermRef(ThisType(TypeRef(NoPrefix,module class <root>)),object scala),Predef),String)],EmptyTree))),TypeTree[TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class <root>)),object scala),Nothing)],Ident(???))),Closure(List(),Ident($anonfun),EmptyTree))) (of class dotty.tools.dotc.ast.Trees$Inlined)
[error] dotty.tools.dotc.transform.Erasure$Typer.skipContextClosures$1(Erasure.scala:928)
[error] dotty.tools.dotc.transform.Erasure$Typer.typedDefDef(Erasure.scala:930)
[error] dotty.tools.dotc.typer.Typer.typedNamed$1(Typer.scala:2923)
[error] dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:3009)
[error] dotty.tools.dotc.typer.ReTyper.typedUnadapted(ReTyper.scala:126)
[error] dotty.tools.dotc.typer.Typer.typed(Typer.scala:3077)
[error] dotty.tools.dotc.typer.Typer.typed(Typer.scala:3081)
[error] dotty.tools.dotc.typer.Typer.traverse$1(Typer.scala:3103)
[error] dotty.tools.dotc.typer.Typer.typedStats(Typer.scala:3149)
[error] dotty.tools.dotc.transform.Erasure$Typer.typedStats(Erasure.scala:1047)
[error] dotty.tools.dotc.typer.Typer.typedBlockStats(Typer.scala:1099)
[error] dotty.tools.dotc.typer.Typer.typedBlock(Typer.scala:1103)
[error] dotty.tools.dotc.typer.Typer.typedUnnamed$1(Typer.scala:2955)
[error] dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:3010)
[error] dotty.tools.dotc.typer.ReTyper.typedUnadapted(ReTyper.scala:126)
[error] dotty.tools.dotc.typer.Typer.typed(Typer.scala:3077)
[error] dotty.tools.dotc.typer.Typer.typed(Typer.scala:3074)
[error] dotty.tools.dotc.typer.Typer.typed(Typer.scala:3081)
[error] dotty.tools.dotc.transform.Erasure$Typer.typedTyped(Erasure.scala:625)
[error] dotty.tools.dotc.typer.Typer.typedUnnamed$1(Typer.scala:2952)
[error] dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:3010)
[error] dotty.tools.dotc.typer.ReTyper.typedUnadapted(ReTyper.scala:126)
[error] dotty.tools.dotc.typer.Typer.typed(Typer.scala:3077)
[error] dotty.tools.dotc.typer.Typer.typed(Typer.scala:3074)
[error] dotty.tools.dotc.typer.Typer.typed(Typer.scala:3081)
[error] dotty.tools.dotc.typer.Typer.typedInlined(Typer.scala:1974)
[error] dotty.tools.dotc.transform.Erasure$Typer.typedInlined(Erasure.scala:892)
[error] dotty.tools.dotc.typer.Typer.typedUnnamed$1(Typer.scala:2969)
[error] dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:3010)
[error] dotty.tools.dotc.typer.ReTyper.typedUnadapted(ReTyper.scala:126)
[error] dotty.tools.dotc.typer.Typer.typed(Typer.scala:3077)
[error] dotty.tools.dotc.typer.Typer.typed(Typer.scala:3081)
[error] dotty.tools.dotc.typer.Typer.typedExpr(Typer.scala:3193)
[error] dotty.tools.dotc.typer.Typer.$anonfun$50(Typer.scala:2401)
[error] dotty.tools.dotc.inlines.PrepareInlineable$.dropInlineIfError(PrepareInlineable.scala:249)
[error] dotty.tools.dotc.typer.Typer.typedDefDef(Typer.scala:2401)
[error] dotty.tools.dotc.transform.Erasure$Typer.typedDefDef(Erasure.scala:948)
[error] dotty.tools.dotc.typer.Typer.typedNamed$1(Typer.scala:2923)
[error] dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:3009)
[error] dotty.tools.dotc.typer.ReTyper.typedUnadapted(ReTyper.scala:126)
[error] dotty.tools.dotc.typer.Typer.typed(Typer.scala:3077)
[error] dotty.tools.dotc.typer.Typer.typed(Typer.scala:3081)
[error] dotty.tools.dotc.typer.Typer.traverse$1(Typer.scala:3103)
[error] dotty.tools.dotc.typer.Typer.typedStats(Typer.scala:3149)
[error] dotty.tools.dotc.transform.Erasure$Typer.typedStats(Erasure.scala:1047)
[error] dotty.tools.dotc.typer.Typer.typedClassDef(Typer.scala:2581)
[error] dotty.tools.dotc.transform.Erasure$Typer.typedClassDef(Erasure.scala:1036)
[error] dotty.tools.dotc.typer.Typer.typedTypeOrClassDef$1(Typer.scala:2935)
[error] dotty.tools.dotc.typer.Typer.typedNamed$1(Typer.scala:2939)
[error] dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:3009)
[error] dotty.tools.dotc.typer.ReTyper.typedUnadapted(ReTyper.scala:126)
[error] dotty.tools.dotc.typer.Typer.typed(Typer.scala:3077)
[error] dotty.tools.dotc.typer.Typer.typed(Typer.scala:3081)
[error] dotty.tools.dotc.typer.Typer.traverse$1(Typer.scala:3103)
[error] dotty.tools.dotc.typer.Typer.typedStats(Typer.scala:3149)
[error] dotty.tools.dotc.transform.Erasure$Typer.typedStats(Erasure.scala:1047)
[error] dotty.tools.dotc.typer.Typer.typedPackageDef(Typer.scala:2711)
[error] dotty.tools.dotc.typer.Typer.typedUnnamed$1(Typer.scala:2980)
[error] dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:3010)
[error] dotty.tools.dotc.typer.ReTyper.typedUnadapted(ReTyper.scala:126)
[error] dotty.tools.dotc.typer.Typer.typed(Typer.scala:3077)
[error] dotty.tools.dotc.typer.Typer.typed(Typer.scala:3081)
[error] dotty.tools.dotc.typer.Typer.typedExpr(Typer.scala:3193)
[error] dotty.tools.dotc.transform.Erasure.run(Erasure.scala:144)
[error] dotty.tools.dotc.core.Phases$Phase.runOn$$anonfun$1(Phases.scala:324)
[error] scala.collection.immutable.List.map(List.scala:246)
[error] dotty.tools.dotc.core.Phases$Phase.runOn(Phases.scala:328)
[error] dotty.tools.dotc.Run.runPhases$1$$anonfun$1(Run.scala:247)
[error] scala.runtime.function.JProcedure1.apply(JProcedure1.java:15)
[error] scala.runtime.function.JProcedure1.apply(JProcedure1.java:10)
[error] scala.collection.ArrayOps$.foreach$extension(ArrayOps.scala:1321)
[error] dotty.tools.dotc.Run.runPhases$1(Run.scala:263)
[error] dotty.tools.dotc.Run.compileUnits$$anonfun$1(Run.scala:271)
[error] dotty.tools.dotc.Run.compileUnits$$anonfun$adapted$1(Run.scala:280)
[error] dotty.tools.dotc.util.Stats$.maybeMonitored(Stats.scala:67)
[error] dotty.tools.dotc.Run.compileUnits(Run.scala:280)
[error] dotty.tools.dotc.Run.compileUnits(Run.scala:201)
[error] dotty.tools.dotc.Driver.finish(Driver.scala:56)
[error] dotty.tools.dotc.Driver.doCompile(Driver.scala:36)
[error] dotty.tools.xsbt.CompilerBridgeDriver.run(CompilerBridgeDriver.java:88)
[error] dotty.tools.xsbt.CompilerBridge.run(CompilerBridge.java:22)
[error] sbt.internal.inc.AnalyzingCompiler.compile(AnalyzingCompiler.scala:91)
[error] sbt.internal.inc.MixedAnalyzingCompiler.$anonfun$compile$7(MixedAnalyzingCompiler.scala:193)
[error] scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:23)
[error] sbt.internal.inc.MixedAnalyzingCompiler.timed(MixedAnalyzingCompiler.scala:248)
[error] sbt.internal.inc.MixedAnalyzingCompiler.$anonfun$compile$4(MixedAnalyzingCompiler.scala:183)
[error] sbt.internal.inc.MixedAnalyzingCompiler.$anonfun$compile$4$adapted(MixedAnalyzingCompiler.scala:163)
[error] sbt.internal.inc.JarUtils$.withPreviousJar(JarUtils.scala:239)
[error] sbt.internal.inc.MixedAnalyzingCompiler.compileScala$1(MixedAnalyzingCompiler.scala:163)
[error] sbt.internal.inc.MixedAnalyzingCompiler.compile(MixedAnalyzingCompiler.scala:211)
[error] sbt.internal.inc.IncrementalCompilerImpl.$anonfun$compileInternal$1(IncrementalCompilerImpl.scala:534)
[error] sbt.internal.inc.IncrementalCompilerImpl.$anonfun$compileInternal$1$adapted(IncrementalCompilerImpl.scala:534)
[error] sbt.internal.inc.Incremental$.$anonfun$apply$5(Incremental.scala:179)
[error] sbt.internal.inc.Incremental$.$anonfun$apply$5$adapted(Incremental.scala:177)
[error] sbt.internal.inc.Incremental$$anon$2.run(Incremental.scala:463)
[error] sbt.internal.inc.IncrementalCommon$CycleState.next(IncrementalCommon.scala:116)
[error] sbt.internal.inc.IncrementalCommon$$anon$1.next(IncrementalCommon.scala:56)
[error] sbt.internal.inc.IncrementalCommon$$anon$1.next(IncrementalCommon.scala:52)
[error] sbt.internal.inc.IncrementalCommon.cycle(IncrementalCommon.scala:263)
[error] sbt.internal.inc.Incremental$.$anonfun$incrementalCompile$8(Incremental.scala:418)
[error] sbt.internal.inc.Incremental$.withClassfileManager(Incremental.scala:506)
[error] sbt.internal.inc.Incremental$.incrementalCompile(Incremental.scala:405)
[error] sbt.internal.inc.Incremental$.apply(Incremental.scala:171)
[error] sbt.internal.inc.IncrementalCompilerImpl.compileInternal(IncrementalCompilerImpl.scala:534)
[error] sbt.internal.inc.IncrementalCompilerImpl.$anonfun$compileIncrementally$1(IncrementalCompilerImpl.scala:488)
[error] sbt.internal.inc.IncrementalCompilerImpl.handleCompilationError(IncrementalCompilerImpl.scala:332)
[error] sbt.internal.inc.IncrementalCompilerImpl.compileIncrementally(IncrementalCompilerImpl.scala:425)
[error] sbt.internal.inc.IncrementalCompilerImpl.compile(IncrementalCompilerImpl.scala:137)
[error] sbt.Defaults$.compileIncrementalTaskImpl(Defaults.scala:2363)
[error] sbt.Defaults$.$anonfun$compileIncrementalTask$2(Defaults.scala:2313)
[error] sbt.internal.server.BspCompileTask$.$anonfun$compute$1(BspCompileTask.scala:30)
[error] sbt.internal.io.Retry$.apply(Retry.scala:46)
[error] sbt.internal.io.Retry$.apply(Retry.scala:28)
[error] sbt.internal.io.Retry$.apply(Retry.scala:23)
[error] sbt.internal.server.BspCompileTask$.compute(BspCompileTask.scala:30)
[error] sbt.Defaults$.$anonfun$compileIncrementalTask$1(Defaults.scala:2311)
[error] scala.Function1.$anonfun$compose$1(Function1.scala:49)
[error] sbt.internal.util.$tilde$greater.$anonfun$$u2219$1(TypeFunctions.scala:62)
[error] sbt.std.Transform$$anon$4.work(Transform.scala:68)
[error] sbt.Execute.$anonfun$submit$2(Execute.scala:282)
[error] sbt.internal.util.ErrorHandling$.wideConvert(ErrorHandling.scala:23)
[error] sbt.Execute.work(Execute.scala:291)
[error] sbt.Execute.$anonfun$submit$1(Execute.scala:282)
[error] sbt.ConcurrentRestrictions$$anon$4.$anonfun$submitValid$1(ConcurrentRestrictions.scala:265)
[error] sbt.CompletionService$$anon$2.call(CompletionService.scala:64)
[error] java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
[error] java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539)
[error] java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
[error] java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
[error] java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
[error] java.base/java.lang.Thread.run(Thread.java:833)
[error]
[error] stack trace is suppressed; run last Compile / compileIncremental for the full output
[error] (Compile / compileIncremental) scala.MatchError: Inlined(Ident(TestMacro$package$),List(),Block(List(DefDef($anonfun,List(List(ValDef(_$1,TypeTree[TypeRef(TermRef(TermRef(ThisType(TypeRef(NoPrefix,module class <root>)),object scala),Predef),String)],EmptyTree))),TypeTree[TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class <root>)),object scala),Nothing)],Ident(???))),Closure(List(),Ident($anonfun),EmptyTree))) (of class dotty.tools.dotc.ast.Trees$Inlined)
Not that the code used underlyingArgument to get a tree that was not in scope. This tree should not be used in the generated code. This might be the cause of the crash.
You are right, in general you should not rely on underlyingArgument working in such situations. But I do not think this is an issue in this specific example as v has the form Inlined(EmptyTree, List(), ``block``), i.e., the bindings part is empty. So the code is not cutting out definitions that may be referenced in the body and the code should be safe; is that correct?
Further observation: Adding yet another underlyingArgument to '{ (_: String) ?=> ??? }.asTerm.underlyingArgument makes the code compile, which does not make much sense to me.
Side note: The code compiles in Scala 3.2.2.
Minimized
import scala.quoted.*
inline def myMacro = ${ myMacroExpr }
def myMacroExpr(using Quotes) =
import quotes.reflect.*
'{ def innerMethod = (_: String) ?=> ???; () }.asTerm match
case block @ Inlined(_, _, Block(List(defdef: DefDef), _)) =>
val rhs =
given Quotes = defdef.symbol.asQuotes
'{ (x: String) ?=> ??? }.asTerm
Block(List(DefDef(defdef.symbol, _ => Some(rhs))), '{}.asTerm).asExprOf[Unit]
def method: Unit = myMacro
According to bisect, it was broken by https://github.com/lampepfl/dotty/pull/16377 (in 229fdaaeaede5edd4489fd67c0c383bb8ffa3255)