Description
Compiler version
Scala version 3.2.1
Minimized code
inline def ownerWorks(in: Int): Any =
${ownerWorksImpl('in)}
transparent inline def ownerDoesNotWork(in: Int): Any =
${ownerWorksImpl('in)}
def ownerWorksImpl(in: Expr[Int])(using Quotes): Expr[String] =
import quotes.reflect.*
val position = Position.ofMacroExpansion
val file = position.sourceFile
val owner0 = Symbol.spliceOwner.maybeOwner
println("owner0 = " + owner0)
val ownerName = owner0.tree match {
case ValDef(name, _, _) =>
name
case DefDef(name, _, _, _) =>
name
case t => report.errorAndAbort(s"unexpected tree shape: ${t.show}")
}
val path = file.path
val line = position.startLine
val column = position.startColumn
val v = in.valueOrAbort
val out = Expr(s"val $ownerName $v: $file @ ${position.startLine}")
out
Used as follows:
val o1 = ownerWorks(1)
println(o1)
val o2 = ownerDoesNotWork(2)
println(o2)
val o3 = alternateDoesNotWork(3)
println(o3)
Output
At the owner0.tree
line, when ownerDoesNotWork
is used, I get the following exception:
[error] -- Error: /home/hmf/VSCodeProjects/sploty/meta/src/data/Data8.scala:124:29 -----
[error] 124 | val o2 = ownerDoesNotWork(2)
[error] | ^^^^^^^^^^^^^^^^^^^
[error] |Exception occurred while executing macro expansion.
[error] |dotty.tools.dotc.core.CyclicReference:
[error] | at dotty.tools.dotc.core.CyclicReference$.apply(TypeErrors.scala:157)
[error] | at dotty.tools.dotc.core.SymDenotations$SymDenotation.completeFrom(SymDenotations.scala:171)
[error] | at dotty.tools.dotc.core.Denotations$Denotation.completeInfo$1(Denotations.scala:187)
[error] | at dotty.tools.dotc.core.Denotations$Denotation.info(Denotations.scala:189)
[error] | at dotty.tools.dotc.ast.tpd$.ValDef(tpd.scala:207)
[error] | at dotty.tools.dotc.quoted.reflect.FromSymbol$.valDefFromSym(FromSymbol.scala:50)
[error] | at dotty.tools.dotc.quoted.reflect.FromSymbol$.definitionFromSym(FromSymbol.scala:22)
[error] | at scala.quoted.runtime.impl.QuotesImpl$reflect$SymbolMethods$.tree(QuotesImpl.scala:2528)
[error] | at scala.quoted.runtime.impl.QuotesImpl$reflect$SymbolMethods$.tree(QuotesImpl.scala:2528)
[error] | at data.Macros5$.ownerWorksImpl(Macros5.scala:154)
[error] |
[error] |---------------------------------------------------------------------------
This exception is also generated during other calls such as Symbol.children
, Symbol.flags
and Symbol.declaredFields
.
Expectation
The documentation found here states that " the tree for a symbol might not be defined". As per the linked best practices section, I have used the -Yretain-trees
thus:
override def scalacOptions = T{ Seq("-deprecation", "-feature", "-explain", "-Yretain-trees") }
So I assume that the tree should be available. Here the tree does seem to be available, but causes a loop. Seems like a possible bug.
However, the best practice says that we should "avoid Symbol.Tree
". This may mean that in some conditions, possibly such as this one, no tree may be available. In other words, its not a bug. In such a case, if I follow, the above guidelines. how can I check for and deconstruct a ValDef
(or any other element, for that matter)?
I have made many attempts and failed. In the example below I seem to only have access to a TermRef
. I can deconstruct that but am unable to cast it to a ValDef
, even when the reference states it is (or points to) one. I assume I have to de-reference it, but cannot see how.
transparent inline def alternateDoesNotWork(in: Int): Any =
${alternateWorksImpl('in)}
def alternateWorksImpl(in: Expr[Int])(using Quotes): Expr[String] =
import quotes.reflect.*
val position = Position.ofMacroExpansion
val file = position.sourceFile
val owner0 = Symbol.spliceOwner.maybeOwner
println("alternate owner0 = " + owner0)
val term = owner0.termRef
println(s"term = $term")
val symbol = term.termSymbol
println(s"term.termSymbol = ${symbol}")
println(s"symbol.isValDef = ${symbol.isValDef}")
println(s"symbol.isLocalDummy = ${symbol.isLocalDummy}")
println(s"symbol.name = ${symbol.name}")
// How should use the reflection API
// val tree: Term = x.asTerm
val ownerName = symbol.name
// val ownerName = owner0.tree match {
// case ValDef(name, _, _) =>
// name
// case DefDef(name, _, _, _) =>
// name
// case t => report.errorAndAbort(s"unexpected tree shape: ${t.show}")
// }
// Alternatives?
val TermRef(a,b) = term
println("TermRef.TypeRepr = " + a)
println("TermRef.name = " + b)
// val valDef = term.asInstanceOf[ValDef]
// println("valDef = " + valDef)
// println("valDef.name = " + valDef.name)
val path = file.path
val line = position.startLine
val column = position.startColumn
val v = in.valueOrAbort
val out = Expr(s"val $ownerName $v: $file @ ${position.startLine}")
out